初始化

This commit is contained in:
qsh
2024-07-15 11:19:37 +08:00
commit beb3c73b2d
420 changed files with 46525 additions and 0 deletions

54
src/App.vue Normal file
View File

@@ -0,0 +1,54 @@
<script lang="ts" name="APP" setup>
import { isDark } from '@/utils/is'
import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import routerSearch from '@/components/RouterSearch/index.vue'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('app')
const appStore = useAppStore()
const currentSize = computed(() => appStore.getCurrentSize)
const greyMode = computed(() => appStore.getGreyMode)
const { wsCache } = useCache()
// 根据浏览器当前主题设置系统主题色
const setDefaultTheme = () => {
let isDarkTheme = wsCache.get(CACHE_KEY.IS_DARK)
if (isDarkTheme === null) {
isDarkTheme = isDark()
}
appStore.setIsDark(isDarkTheme)
}
setDefaultTheme()
</script>
<template>
<ConfigGlobal :size="currentSize">
<RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" />
<routerSearch />
</ConfigGlobal>
</template>
<style lang="scss">
$prefix-cls: #{$namespace}-app;
.size {
width: 100%;
height: 100%;
}
html,
body {
padding: 0 !important;
margin: 0;
overflow: hidden;
@extend .size;
#app {
@extend .size;
}
}
.#{$prefix-cls}-grey-mode {
filter: grayscale(100%);
}
</style>

13
src/api/call/index.js Normal file
View File

@@ -0,0 +1,13 @@
import request from '@/config/axios'
export default {
callLogin(data) {
return request.post({ url: '/call-api/openapi/V2.0.4/agentLogin', data })
},
callUserStatus(data) {
return request.post({ url: '/call-api/openapi/V2.0.4/getAgentStatus', data })
},
callNumber(data) {
return request.post({ url: '/call-api/openapi/V2.0.4/callNumber', data })
}
}

39
src/api/clue/afterSale.js Normal file
View File

@@ -0,0 +1,39 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getAfterSaleList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/list', params })
}
// 查询(精简)列表
export const getAfterSalePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/page', params })
}
// 新增
export const createAfterSale = async (data) => {
return await request.post({
url: '/admin-api/crm/sign-after-sale/create',
data,
isSubmitForm: true
})
}
// 审核
export const auditAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/check', data })
}
export const batchAuditAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/batch/check', data })
}
// 撤销
export const cancelApplyAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/revoke', params: data })
}
// 查询详情
export const getAfterSaleDetail = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/get', params })
}

11
src/api/clue/clueCache.js Normal file
View File

@@ -0,0 +1,11 @@
import request from '@/config/axios'
// 查询用户配置
export const getClueCache = async (params) => {
return await request.get({ url: '/admin-api/crm/param-user-setting/get-by-user', params })
}
// 保存用户配置
export const setClueCache = async (data) => {
return await request.post({ url: '/admin-api/crm/param-user-setting/save', data })
}

40
src/api/clue/clueField.js Normal file
View File

@@ -0,0 +1,40 @@
import request from '@/config/axios'
// 创建
export const createField = (data) => {
return request.post({ url: '/admin-api/crm/param/create', data: { ...data, module: 1 } })
}
// 更新
export const updateField = (data) => {
return request.put({ url: '/admin-api/crm/param/update', data })
}
// 删除
export const deleteField = (id) => {
return request.delete({ url: `/admin-api/crm/param/delete?id=${id}` })
}
// 获得
export const getField = (id) => {
return request.get({ url: `/admin-api/crm/param/get?id=${id}` })
}
// 获得精简信息列表
export const getSimpleFieldList = () => {
return request.get({ url: '/admin-api/crm/param/simple-list', params: { module: 1 } })
}
// 获取自定义字段
export const getDiyFieldList = () => {
return request.get({ url: '/admin-api/crm/param/get-diy-param', params: { module: 1 } })
}
// 状态修改
export const updateFieldStatus = (signParamId, status) => {
const data = {
signParamId,
status
}
return request.put({ url: '/admin-api/crm/param/status/update', data: data })
}

View File

@@ -0,0 +1,26 @@
import request from '@/config/axios'
// 线索获取规则
export const getClueGainRuleList = () => {
return request.get({ url: '/admin-api/crm/sch-clue-gain-rule/list' })
}
// 删除
export const deleteClueGainRule = (id) => {
return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
}
// 线索获取规则保存
export const saveClueGainRule = (data) => {
return request.put({ url: '/admin-api/crm/sch-clue-gain-rule/batchUpdate', data })
}
// 线索规则
export const getClueDistributeRuleByUser = (params) => {
return request.get({ url: '/admin-api/crm/sch-clue-distribution-rule/queryByUserId', params })
}
// 删除
export const deleteClueDistributeRule = (id) => {
return request.delete({ url: `/admin-api/crm/sch-clue-gain-rule/delete?id=${id}` })
}

21
src/api/clue/delivery.js Normal file
View File

@@ -0,0 +1,21 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getDeliveryList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-send/list', params })
}
// 查询分页列表
export const getDeliveryPage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-send/page', params })
}
// 新增
export const createDelivery = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-send/create', data, isSubmitForm: true })
}
// 查询详情
export const getDeliveryDetail = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-send/get', params })
}

View File

@@ -0,0 +1,20 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getFollowList = async (params) => {
return await request.get({ url: '/admin-api/crm/clue-follow-record/list', params })
}
// 新增
export const createFollow = async (data) => {
return await request.post({
url: '/admin-api/crm/clue-follow-record/create',
data: data,
isSubmitForm: true
})
}
// 删除
export const deleteFollow = async (id) => {
return await request.delete({ url: '/admin-api/crm/clue-follow-record/delete?id=' + id })
}

60
src/api/clue/index.js Normal file
View File

@@ -0,0 +1,60 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getSimpleClueList = async () => {
return await request.get({ url: '/admin-api/crm/sch-clue/list-all-simple' })
}
// 查询列表
export const getCluePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-clue/page', params })
}
// 查询详情
export const getClue = async (id) => {
return await request.get({ url: '/admin-api/crm/sch-clue/get?id=' + id })
}
// 新增
export const createClue = async (data) => {
return await request.post({
url: '/admin-api/crm/sch-clue/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updateClue = async (params) => {
return await request.put({ url: '/admin-api/crm/sch-clue/update', data: params })
}
// 删除
export const deleteClue = async (id) => {
return await request.delete({ url: '/admin-api/crm/sch-clue/delete?id=' + id })
}
// 释放
export const releaseClue = async (data) => {
return await request.put({ url: '/admin-api/crm/sch-clue/public/save', data })
}
// 通用查询数量
export const getClueCount = async () => {
return await request.get({ url: '/admin-api/crm/sch-clue/get-clue-num' })
}
// 获取操作记录
export const getOpearateRecord = async (params) => {
return await request.get({ url: '/admin-api/crm/clue-operate-record/list', params })
}
// 获取公海线索
export const getPublicClue = async (data) => {
return await request.put({ url: '/admin-api/crm/sch-clue/public/save', data })
}
// 获得线索跟进用户信息
export const getFollowUserList = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-clue/get-follow-user', params })
}

View File

@@ -0,0 +1,40 @@
import request from '@/config/axios'
// 创建
export const createField = (data) => {
return request.post({ url: '/admin-api/crm/param/create', data: { ...data, module: 2 } })
}
// 更新
export const updateField = (data) => {
return request.put({ url: '/admin-api/crm/param/update', data })
}
// 删除
export const deleteField = (id) => {
return request.delete({ url: `/admin-api/crm/param/delete?id=${id}` })
}
// 获得
export const getField = (id) => {
return request.get({ url: `/admin-api/crm/param/get?id=${id}` })
}
// 获得精简信息列表
export const getSimpleFieldList = () => {
return request.get({ url: '/admin-api/crm/param/simple-list', params: { module: 2 } })
}
// 获取自定义字段
export const getDiyFieldList = () => {
return request.get({ url: '/admin-api/crm/param/get-diy-param', params: { module: 2 } })
}
// 状态修改
export const updateFieldStatus = (signParamId, status) => {
const data = {
signParamId,
status
}
return request.put({ url: '/admin-api/crm/param/status/update', data: data })
}

39
src/api/clue/payment.js Normal file
View File

@@ -0,0 +1,39 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getPaymentList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/list', params })
}
// 查询(精简)列表
export const getPaymentPage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/page', params })
}
// 新增
export const createPayment = async (data) => {
return await request.post({
url: '/admin-api/crm/sign-pay-record/create',
data,
isSubmitForm: true
})
}
// 审核
export const auditPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/check', data })
}
export const batchAuditPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/batch/check', data })
}
// 撤销
export const cancelApplyPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/revoke', params: data })
}
// 查询详情
export const getPaymentDetail = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/get', params })
}

16
src/api/clue/settle.js Normal file
View File

@@ -0,0 +1,16 @@
import request from '@/config/axios'
// 查询列表
export const getSettlePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-settlement/page', params })
}
// 查询详情
export const getSettle = async (id) => {
return await request.get({ url: '/admin-api/crm/sign-settlement/get?id=' + id })
}
// 批量结算
export const batchSaveSettle = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-settlement/batch/save', data })
}

31
src/api/clue/sign.js Normal file
View File

@@ -0,0 +1,31 @@
import request from '@/config/axios'
// 查询列表
export const getSignPage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign/page', params })
}
// 查询详情
export const getSign = async (id) => {
return await request.get({ url: '/admin-api/crm/sign/get?id=' + id })
}
// 新增
export const createSign = async (data) => {
return await request.post({ url: '/admin-api/crm/sign/create', data: data, isSubmitForm: true })
}
// 取消登记
export const cancelDeal = async (id) => {
return await request.delete({ url: '/admin-api/crm/sign/delete?id=' + id })
}
// 新增支出
export const signAddPay = async (data) => {
return await request.post({ url: '/admin-api/crm/sign/extra-pay/save', data: data })
}
// 查询支出
export const getSignExtraPayList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign/extra-pay/get', params })
}

26
src/api/clue/skill.js Normal file
View File

@@ -0,0 +1,26 @@
import request from '@/config/axios'
// 获得列表
export const getSkillPage = (params) => {
return request.get({ url: '/admin-api/crm/skill/page', params })
}
// 创建
export const createSkill = (data) => {
return request.post({ url: '/admin-api/crm/skill/create', data, isSubmitForm: true })
}
// 更新
export const updateSkill = (data) => {
return request.put({ url: '/admin-api/crm/skill/update', data })
}
// 删除
export const deleteSkill = (id) => {
return request.delete({ url: `/admin-api/crm/skill/delete?id=${id}` })
}
// 获得
export const getSkill = (id) => {
return request.get({ url: `/admin-api/crm/skill/get?id=${id}` })
}

31
src/api/clue/source.js Normal file
View File

@@ -0,0 +1,31 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getSimpleSourceList = async () => {
return await request.get({ url: '/admin-api/crm/source/list' })
}
// 查询列表
export const getSourcePage = async (params) => {
return await request.get({ url: '/admin-api/crm/source/list', params })
}
// 查询详情
export const getSource = async (id) => {
return await request.get({ url: '/admin-api/crm/source/get?id=' + id })
}
// 新增
export const createSource = async (data) => {
return await request.post({ url: '/admin-api/crm/source/create', data: data, isSubmitForm: true })
}
// 修改
export const updateSource = async (params) => {
return await request.put({ url: '/admin-api/crm/source/update', data: params })
}
// 删除
export const deleteSource = async (id) => {
return await request.delete({ url: '/admin-api/crm/source/delete?id=' + id })
}

20
src/api/home/index.js Normal file
View File

@@ -0,0 +1,20 @@
import request from '@/config/axios'
// 线索情况
export const getUserClueCount = async (params) => {
return await request.get({ url: '/admin-api/crm/home/get-user-count', params })
}
// 成交率
export const getClueSignSignRate = async (params) => {
return await request.get({ url: '/admin-api/crm/home/get-sign-rate', params })
}
// 成交率
export const getClueSignSignRank = async (params) => {
return await request.get({ url: '/admin-api/crm/home/get-sign-top10', params })
}
// 成交率
export const getClueFollowRank = async (params) => {
return await request.get({ url: '/admin-api/crm/home/get-follow-top10', params })
}

View File

@@ -0,0 +1,48 @@
import request from '@/config/axios'
export interface ConfigVO {
id: number | undefined
category: string
name: string
key: string
value: string
type: number
visible: boolean
remark: string
createTime: Date
}
// 查询参数列表
export const getConfigPage = (params: PageParam) => {
return request.get({ url: '/admin-api/infra/config/page', params })
}
// 查询参数详情
export const getConfig = (id: number) => {
return request.get({ url: '/admin-api/infra/config/get?id=' + id })
}
// 根据参数键名查询参数值
export const getConfigKey = (configKey: string) => {
return request.get({ url: '/admin-api/infra/config/get-value-by-key?key=' + configKey })
}
// 新增参数
export const createConfig = (data: ConfigVO) => {
return request.post({ url: '/admin-api/infra/config/create', data })
}
// 修改参数
export const updateConfig = (data: ConfigVO) => {
return request.put({ url: '/admin-api/infra/config/update', data })
}
// 删除参数
export const deleteConfig = (id: number) => {
return request.delete({ url: '/admin-api/infra/config/delete?id=' + id })
}
// 导出参数
export const exportConfig = (params) => {
return request.download({ url: '/admin-api/infra/config/export', params })
}

View File

@@ -0,0 +1,45 @@
import request from '@/config/axios'
export interface FilePageReqVO extends PageParam {
path?: string
type?: string
createTime?: Date[]
}
// 文件预签名地址 Response VO
export interface FilePresignedUrlRespVO {
// 文件配置编号
configId: number
// 文件上传 URL
uploadUrl: string
// 文件 URL
url: string
}
// 查询文件列表
export const getFilePage = (params: FilePageReqVO) => {
return request.get({ url: '/infra/file/page', params })
}
// 删除文件
export const deleteFile = (id: number) => {
return request.delete({ url: '/infra/file/delete?id=' + id })
}
// 获取文件预签名地址
export const getFilePresignedUrl = (path: string) => {
return request.get<FilePresignedUrlRespVO>({
url: '/infra/file/presigned-url',
params: { path }
})
}
// 创建文件
export const createFile = (data: any) => {
return request.post({ url: '/infra/file/create', data })
}
// 上传文件
export const updateFile = (data: any) => {
return request.upload({ url: '/admin-api/system/file/upload', data })
}

73
src/api/login/index.ts Normal file
View File

@@ -0,0 +1,73 @@
import request from '@/config/axios'
import { getRefreshToken } from '@/utils/auth'
import type { UserLoginVO } from './types'
export interface SmsCodeVO {
mobile: string
scene: number
}
export interface SmsLoginVO {
mobile: string
code: string
}
// 登录
export const login = (data: UserLoginVO) => {
return request.post({ url: '/admin-api/system/auth/login', data })
}
// 刷新访问令牌
export const refreshToken = () => {
return request.post({
url: '/admin-api/system/auth/refresh-token?refreshToken=' + getRefreshToken()
})
}
// 使用租户名,获得租户编号
export const getTenantIdByName = (name: string) => {
return request.get({ url: '/admin-api/system/tenant/get-id-by-name?name=' + name })
}
// 登出
export const loginOut = () => {
return request.post({ url: '/admin-api/system/auth/logout' })
}
// 获取用户权限信息
export const getInfo = (params) => {
return request.get({ url: '/admin-api/system/auth/get-permission-info', params })
}
//获取登录验证码
export const sendSmsCode = (data: SmsCodeVO) => {
return request.post({ url: '/admin-api/system/auth/send-sms-code', data })
}
// 短信验证码登录
export const smsLogin = (data: SmsLoginVO) => {
return request.post({ url: '/admin-api/system/auth/sms-login', data })
}
// 社交授权的跳转
export const socialAuthRedirect = (type: number, redirectUri: string) => {
return request.get({
url: '/admin-api/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri
})
}
// 获取验证图片以及 token
export const getCode = (data) => {
return request.postOriginal({ url: '/admin-api/system/captcha/get', data })
}
// 滑动或者点选验证
export const reqCheck = (data) => {
return request.postOriginal({ url: '/admin-api/system/captcha/check', data })
}
// 获取应用信息
export const getAppInfo = (instanceId: number) => {
return request.get({
url: '/admin-api/system/serviceInstance/getInstanceInfo?instanceId=' + instanceId
})
}

View File

@@ -0,0 +1,41 @@
import request from '@/config/axios'
// 获得授权信息
export const getAuthorize = (clientId: string) => {
return request.get({ url: '/admin-api/system/oauth2/authorize?clientId=' + clientId })
}
// 发起授权
export const authorize = (
responseType: string,
clientId: string,
redirectUri: string,
state: string,
autoApprove: boolean,
checkedScopes: string[],
uncheckedScopes: string[]
) => {
// 构建 scopes
const scopes = {}
for (const scope of checkedScopes) {
scopes[scope] = true
}
for (const scope of uncheckedScopes) {
scopes[scope] = false
}
// 发起请求
return request.post({
url: '/admin-api/system/oauth2/authorize',
headers: {
'Content-type': 'application/x-www-form-urlencoded'
},
params: {
response_type: responseType,
client_id: clientId,
redirect_uri: redirectUri,
state: state,
auto_approve: autoApprove,
scope: JSON.stringify(scopes)
}
})
}

28
src/api/login/types.ts Normal file
View File

@@ -0,0 +1,28 @@
export type UserLoginVO = {
username: string
password: string
captchaVerification: string
}
export type TokenType = {
id: number // 编号
accessToken: string // 访问令牌
refreshToken: string // 刷新令牌
userId: number // 用户编号
userType: number //用户类型
clientId: string //客户端编号
expiresTime: number //过期时间
}
export type UserVO = {
id: number
username: string
nickname: string
deptId: number
email: string
mobile: string
sex: number
avatar: string
loginIp: string
loginDate: string
}

View File

@@ -0,0 +1,61 @@
import request from '@/config/axios'
/**
* 商品品牌
*/
export interface BrandVO {
/**
* 品牌编号
*/
id?: number
/**
* 品牌名称
*/
name: string
/**
* 品牌图片
*/
picUrl: string
/**
* 品牌排序
*/
sort?: number
/**
* 品牌描述
*/
description?: string
/**
* 开启状态
*/
status: number
}
// 创建商品品牌
export const createBrand = (data: BrandVO) => {
return request.post({ url: '/admin-api/crm/erp-product-brand/create', data, isSubmitForm: true })
}
// 更新商品品牌
export const updateBrand = (data: BrandVO) => {
return request.put({ url: '/admin-api/crm/erp-product-brand/update', data })
}
// 删除商品品牌
export const deleteBrand = (id: number) => {
return request.delete({ url: `/admin-api/crm/erp-product-brand/delete?id=${id}` })
}
// 获得商品品牌
export const getBrand = (id: number) => {
return request.get({ url: `/admin-api/crm/erp-product-brand/get?id=${id}` })
}
// 获得商品品牌列表
export const getBrandParam = (params: PageParam) => {
return request.get({ url: '/admin-api/crm/erp-product-brand/page', params })
}
// 获得商品品牌精简信息列表
export const getSimpleBrandList = () => {
return request.get({ url: '/admin-api/crm/erp-product-brand/simple-list' })
}

View File

@@ -0,0 +1,69 @@
import request from '@/config/axios'
/**
* 产品分类
*/
export interface CategoryVO {
/**
* 分类编号
*/
id?: number
/**
* 父分类编号
*/
parentId?: number
/**
* 分类名称
*/
name: string
/**
* 移动端分类图
*/
picUrl: string
/**
* PC 端分类图
*/
bigPicUrl?: string
/**
* 分类排序
*/
sort: number
/**
* 开启状态
*/
status: number
}
// 创建商品分类
export const createCategory = (data: CategoryVO) => {
return request.post({
url: '/admin-api/crm/erp-product-category/create',
data,
isSubmitForm: true
})
}
// 更新商品分类
export const updateCategory = (data: CategoryVO) => {
return request.put({ url: '/admin-api/crm/erp-product-category/update', data })
}
// 删除商品分类
export const deleteCategory = (id: number) => {
return request.delete({ url: `/admin-api/crm/erp-product-category/delete?id=${id}` })
}
// 获得商品分类
export const getCategory = (id: number) => {
return request.get({ url: `/admin-api/crm/erp-product-category/get?id=${id}` })
}
// 获得商品分类列表
export const getCategoryList = (params: any) => {
return request.get({ url: '/admin-api/crm/erp-product-category/list', params })
}
// 获得商品分类列表
export const getCategorySimpleList = (params: any) => {
return request.get({ url: '/admin-api/crm/erp-product-category/simple-list', params })
}

View File

@@ -0,0 +1,33 @@
import request from '@/config/axios'
// 查询列表
export const getProductPage = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-product/page', params })
}
// 查询详情
export const getProduct = async (id) => {
return await request.get({ url: '/admin-api/crm/erp-product/get?id=' + id })
}
// 新增
export const createProduct = async (data) => {
return await request.post({
url: '/admin-api/crm/erp-product/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updateProduct = async (params) => {
return await request.put({ url: '/admin-api/crm/erp-product/update', data: params })
}
// 删除
export const deleteProduct = async (id) => {
return await request.delete({ url: '/admin-api/crm/erp-product/delete?id=' + id })
}
export const getSimpleProductList = async () => {
return await request.get({ url: '/admin-api/crm/erp-product/simple-list' })
}

View File

@@ -0,0 +1,44 @@
import request from '@/config/axios'
// 创建
export const createField = (data) => {
return request.post({
url: '/admin-api/crm/param/create',
data: { ...data, module: 3 },
isSubmitForm: true
})
}
// 更新
export const updateField = (data) => {
return request.put({ url: '/admin-api/crm/param/update', data })
}
// 删除
export const deleteField = (id) => {
return request.delete({ url: `/admin-api/crm/param/delete?id=${id}` })
}
// 获得
export const getField = (id) => {
return request.get({ url: `/admin-api/crm/param/get?id=${id}` })
}
// 获得精简信息列表
export const getSimpleFieldList = () => {
return request.get({ url: '/admin-api/crm/param/simple-list', params: { module: 3 } })
}
// 获取自定义字段
export const getDiyFieldList = () => {
return request.get({ url: '/admin-api/crm/param/get-diy-param', params: { module: 3 } })
}
// 状态修改
export const updateFieldStatus = (signParamId, status) => {
const data = {
signParamId,
status
}
return request.put({ url: '/admin-api/crm/param/status/update', data: data })
}

View File

@@ -0,0 +1,107 @@
import request from '@/config/axios'
/**
* 商品属性
*/
export interface PropertyVO {
id?: number
/** 名称 */
name: string
/** 备注 */
remark?: string
}
/**
* 属性值
*/
export interface PropertyValueVO {
id?: number
/** 属性项的编号 */
propertyId?: number
/** 名称 */
name: string
/** 备注 */
remark?: string
}
/**
* 商品属性值的明细
*/
export interface PropertyValueDetailVO {
/** 属性项的编号 */
propertyId: number // 属性的编号
/** 属性的名称 */
propertyName: string
/** 属性值的编号 */
valueId: number
/** 属性值的名称 */
valueName: string
}
// ------------------------ 属性项 -------------------
// 创建属性项
export const createProperty = (data: PropertyVO) => {
return request.post({
url: '/admin-api/crm/erp-product-property/create',
data,
isSubmitForm: true
})
}
// 更新属性项
export const updateProperty = (data: PropertyVO) => {
return request.put({ url: '/product/property/update', data })
}
// 删除属性项
export const deleteProperty = (id: number) => {
return request.delete({ url: `/product/property/delete?id=${id}` })
}
// 获得属性项
export const getProperty = (id: number): Promise<PropertyVO> => {
return request.get({ url: `/product/property/get?id=${id}` })
}
// 获得属性项分页
export const getPropertyPage = (params: PageParam) => {
return request.get({ url: '/product/property/page', params })
}
// 获得属性项列表
export const getPropertyList = (params: any) => {
return request.get({ url: '/product/property/list', params })
}
// 获得属性项列表
export const getPropertyListAndValue = (data: any) => {
return request.post({ url: '/product/property/get-value-list', data })
}
// ------------------------ 属性值 -------------------
// 获得属性值分页
export const getPropertyValuePage = (params: PageParam & any) => {
return request.get({ url: '/product/property/value/page', params })
}
// 获得属性值
export const getPropertyValue = (id: number): Promise<PropertyValueVO> => {
return request.get({ url: `/product/property/value/get?id=${id}` })
}
// 创建属性值
export const createPropertyValue = (data: PropertyValueVO) => {
return request.post({ url: '/admin-api/crm/erp-product-property-value/create', data })
}
// 更新属性值
export const updatePropertyValue = (data: PropertyValueVO) => {
return request.put({ url: '/product/property/value/update', data })
}
// 删除属性值
export const deletePropertyValue = (id: number) => {
return request.delete({ url: `/product/property/value/delete?id=${id}` })
}

View File

@@ -0,0 +1,29 @@
import request from '@/config/axios'
// 查询列表
export const getPurchasePage = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-purchase/page', params })
}
// 新增
export const createPurchase = async (data) => {
return await request.post({
url: '/admin-api/crm/erp-purchase/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updatePurchase = async (params) => {
return await request.put({ url: '/admin-api/crm/erp-purchase/update', data: params })
}
// 删除
export const deletePurchase = async (id) => {
return await request.delete({ url: '/admin-api/crm/erp-purchase/delete?id=' + id })
}
// 审核
export const auditPurchase = async (data) => {
return await request.post({ url: '/admin-api/crm/erp-purchase/audit', data })
}

View File

@@ -0,0 +1,40 @@
import request from '@/config/axios'
export interface DeliveryExpressVO {
id: number
code: string
name: string
logo: string
sort: number
status: number
}
// 查询快递公司列表
export const getDeliveryExpressPage = async (params: PageParam) => {
return await request.get({ url: '/trade/delivery/express/page', params })
}
// 查询快递公司详情
export const getDeliveryExpress = async (id: number) => {
return await request.get({ url: '/trade/delivery/express/get?id=' + id })
}
// 新增快递公司
export const createDeliveryExpress = async (data: DeliveryExpressVO) => {
return await request.post({ url: '/trade/delivery/express/create', data })
}
// 修改快递公司
export const updateDeliveryExpress = async (data: DeliveryExpressVO) => {
return await request.put({ url: '/trade/delivery/express/update', data })
}
// 删除快递公司
export const deleteDeliveryExpress = async (id: number) => {
return await request.delete({ url: '/trade/delivery/express/delete?id=' + id })
}
// 导出快递公司 Excel
export const exportDeliveryExpressApi = async (params) => {
return await request.download({ url: '/trade/delivery/express/export-excel', params })
}

View File

@@ -0,0 +1,54 @@
import request from '@/config/axios'
export interface DeliveryExpressTemplateVO {
id: number
name: string
chargeMode: number
sort: number
templateCharge: ExpressTemplateChargeVO[]
templateFree: ExpressTemplateFreeVO[]
}
export declare type ExpressTemplateChargeVO = {
areaIds: number[]
startCount: number
startPrice: number
extraCount: number
extraPrice: number
}
export declare type ExpressTemplateFreeVO = {
areaIds: number[]
freeCount: number
freePrice: number
}
// 查询快递运费模板列表
export const getDeliveryExpressTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/trade/delivery/express-template/page', params })
}
// 查询快递运费模板详情
export const getDeliveryExpressTemplate = async (id: number) => {
return await request.get({ url: '/trade/delivery/express-template/get?id=' + id })
}
// 新增快递运费模板
export const createDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => {
return await request.post({ url: '/trade/delivery/express-template/create', data })
}
// 修改快递运费模板
export const updateDeliveryExpressTemplate = async (data: DeliveryExpressTemplateVO) => {
return await request.put({ url: '/trade/delivery/express-template/update', data })
}
// 删除快递运费模板
export const deleteDeliveryExpressTemplate = async (id: number) => {
return await request.delete({ url: '/trade/delivery/express-template/delete?id=' + id })
}
// 导出快递运费模板 Excel
export const exportDeliveryExpressTemplateApi = async (params) => {
return await request.download({ url: '/trade/delivery/express-template/export-excel', params })
}

View File

@@ -0,0 +1,49 @@
import request from '@/config/axios'
// 查询列表
export const getWarehousePage = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-warehouse/page', params })
}
// 新增
export const createWarehouse = async (data) => {
return await request.post({
url: '/admin-api/crm/erp-warehouse/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updateWarehouse = async (params) => {
return await request.put({ url: '/admin-api/crm/erp-warehouse/update', data: params })
}
// 删除
export const deleteWarehouse = async (id) => {
return await request.delete({ url: '/admin-api/crm/erp-warehouse/delete?id=' + id })
}
// 获取仓库
export const getWarehouse = async (id) => {
return await request.get({ url: '/admin-api/crm/erp-warehouse/get?id=' + id })
}
// 获取仓库列表
export const getSimpleWarehouseList = async () => {
return await request.get({ url: '/admin-api/crm/erp-warehouse/simple-list' })
}
// 获取库存
export const getInventoryList = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-inventory/page', params })
}
// 获取库存变动记录
export const getInventoryRecord = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-inventory-record/page', params })
}
// 获取剩余库存
export const getRemainInventoryList = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-inventory-detail/list', params })
}

View File

@@ -0,0 +1,54 @@
import request from '@/config/axios'
// 查询列表-分页
export const getClassTypePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-class-type/page', params })
}
export const getClassTypeList = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-class-type/list', params })
}
// 查询详情
export const getClassType = async (id) => {
return await request.get({ url: '/admin-api/crm/sch-class-type/get?id=' + id })
}
// 新增
export const createClassType = async (data) => {
return await request.post({
url: '/admin-api/crm/sch-class-type/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updateClassType = async (params) => {
return await request.put({ url: '/admin-api/crm/sch-class-type/update', data: params })
}
// 删除
export const deleteClassType = async (id) => {
return await request.delete({ url: '/admin-api/crm/sch-class-type/delete?id=' + id })
}
// 修改状态
export const updateClassTypeStatus = async (data) => {
return request.put({ url: '/admin-api/crm/sch-class-type/status/update', data })
}
// 批量修改状态
export const batchUpdateClassTypeStatus = async (data) => {
return request.put({ url: '/admin-api/crm/sch-class-type/status/batch-update', data })
}
// 批量删除
export const batchDeleteClassType = async (ids) => {
return await request.delete({ url: '/admin-api/crm/sch-class-type/batch-delete?ids=' + ids })
}
// 获取提成结算参数
export const getCommissionParams = async () => {
return await request.get({ url: '/admin-api/crm/sch-class-type/get-percentage-param' })
}

View File

@@ -0,0 +1,43 @@
import request from '@/config/axios'
// 查询列表-分页
export const getPlacePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-place/page', params })
}
// 查询列表-不分页
export const getPlaceList = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-place/list', params })
}
// 查询详情
export const getPlace = async (id) => {
return await request.get({ url: '/admin-api/crm/sch-place/get?id=' + id })
}
// 新增
export const createPlace = async (data) => {
return await request.post({
url: '/admin-api/crm/sch-place/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updatePlace = async (params) => {
return await request.put({ url: '/admin-api/crm/sch-place/update', data: params })
}
// 删除
export const deletePlace = async (id) => {
return await request.delete({ url: '/admin-api/crm/sch-place/delete?id=' + id })
}
export const updatePlaceStatus = async (placeId, status) => {
const data = {
placeId,
status
}
return request.put({ url: '/admin-api/crm/sch-place/status/update', data: data })
}

View File

@@ -0,0 +1,37 @@
import request from '@/config/axios'
// 查询列表
export const getSchoolPage = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-school/page', params })
}
// 查询详情
export const getSchool = async (id) => {
return await request.get({ url: '/admin-api/crm/sch-school/get?id=' + id })
}
// 新增
export const createSchool = async (data) => {
return await request.post({
url: '/admin-api/crm/sch-school/create',
data: data,
isSubmitForm: true
})
}
// 修改
export const updateSchool = async (params) => {
return await request.put({ url: '/admin-api/crm/sch-school/update', data: params })
}
// 删除
export const deleteSchool = async (id) => {
return await request.delete({ url: '/admin-api/crm/sch-school/delete?id=' + id })
}
export const updateSchoolStatus = async (schoolId, status) => {
const data = {
schoolId,
status
}
return request.put({ url: '/admin-api/crm/sch-school/status/update', data: data })
}

View File

@@ -0,0 +1,43 @@
import request from '@/config/axios'
export interface DeptVO {
id?: number
name: string
parentId: number
status: number
sort: number
leaderUserId: number
phone: string
email: string
createTime: Date
}
// 查询部门(精简)列表
export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
return await request.get({ url: '/admin-api/system/dept/list-all-simple' })
}
// 查询部门列表
export const getDeptPage = async (params) => {
return await request.get({ url: '/admin-api/system/dept/list', params })
}
// 查询部门详情
export const getDept = async (id: number) => {
return await request.get({ url: '/admin-api/system/dept/get?id=' + id })
}
// 新增部门
export const createDept = async (data: DeptVO) => {
return await request.post({ url: '/admin-api/system/dept/create', data: data })
}
// 修改部门
export const updateDept = async (params: DeptVO) => {
return await request.put({ url: '/admin-api/system/dept/update', data: params })
}
// 删除部门
export const deleteDept = async (id: number) => {
return await request.delete({ url: '/admin-api/system/dept/delete?id=' + id })
}

View File

@@ -0,0 +1,49 @@
import request from '@/config/axios'
export type DictDataVO = {
id: number | undefined
sort: number | undefined
label: string
value: string
dictType: string
status: number
colorType: string
cssClass: string
remark: string
createTime: Date
}
// 查询字典数据(精简)列表
export const listSimpleDictData = () => {
return request.get({ url: '/admin-api/oa/dict-data/simple-list' })
}
// 查询字典数据列表
export const getDictDataPage = (params: PageParam) => {
return request.get({ url: '/admin-api/oa/dict-data/page', params })
}
// 查询字典数据详情
export const getDictData = (id: number) => {
return request.get({ url: '/admin-api/oa/dict-data/get?id=' + id })
}
// 新增字典数据
export const createDictData = (data: DictDataVO) => {
return request.post({ url: '/admin-api/oa/dict-data/create', data })
}
// 修改字典数据
export const updateDictData = (data: DictDataVO) => {
return request.put({ url: '/admin-api/oa/dict-data/update', data })
}
// 删除字典数据
export const deleteDictData = (id: number) => {
return request.delete({ url: '/admin-api/oa/dict-data/delete?id=' + id })
}
// 导出字典类型数据
export const exportDictData = (params) => {
return request.get({ url: '/admin-api/oa/dict-data/export', params })
}

View File

@@ -0,0 +1,44 @@
import request from '@/config/axios'
export type DictTypeVO = {
id: number | undefined
name: string
type: string
status: number
remark: string
createTime: Date
}
// 查询字典(精简)列表
export const getSimpleDictTypeList = () => {
return request.get({ url: '/admin-api/oa/dict-type/list-all-simple' })
}
// 查询字典列表
export const getDictTypePage = (params: PageParam) => {
return request.get({ url: '/admin-api/oa/dict-type/page', params })
}
// 查询字典详情
export const getDictType = (id: number) => {
return request.get({ url: '/admin-api/oa/dict-type/get?id=' + id })
}
// 新增字典
export const createDictType = (data: DictTypeVO) => {
return request.post({ url: '/admin-api/oa/dict-type/create', data })
}
// 修改字典
export const updateDictType = (data: DictTypeVO) => {
return request.put({ url: '/admin-api/oa/dict-type/update', data })
}
// 删除字典
export const deleteDictType = (id: number) => {
return request.delete({ url: '/admin-api/oa/dict-type/delete?id=' + id })
}
// 导出字典类型
export const exportDictType = (params) => {
return request.get({ url: '/admin-api/oa/dict-type/export', params })
}

View File

@@ -0,0 +1,26 @@
import request from '@/config/axios'
// 查询知识库列表
export const getLibraryPage = (params) => {
return request.get({ url: '/admin-api/crm/knowledge-lib/page', params })
}
// 查询知识库详情
export const getLibrary = (id) => {
return request.get({ url: '/admin-api/crm/knowledge-lib/get?id=' + id })
}
// 新增知识库
export const createLibrary = (data) => {
return request.post({ url: '/admin-api/crm/knowledge-lib/create', data })
}
// 修改知识库
export const updateLibrary = (data) => {
return request.put({ url: '/admin-api/crm/knowledge-lib/update', data })
}
// 删除知识库
export const deleteLibrary = (id) => {
return request.delete({ url: '/admin-api/crm/knowledge-lib/delete?id=' + id })
}

View File

@@ -0,0 +1,26 @@
import request from '@/config/axios'
// 查询资源列表
export const getResourcePage = (params) => {
return request.get({ url: '/admin-api/crm/knowledge-lib-info/page', params })
}
// 查询资源详情
export const getResource = (id) => {
return request.get({ url: '/admin-api/crm/knowledge-lib-info/get?id=' + id })
}
// 新增资源
export const createResource = (data) => {
return request.post({ url: '/admin-api/crm/knowledge-lib-info/create', data })
}
// 修改资源
export const updateResource = (data) => {
return request.put({ url: '/admin-api/crm/knowledge-lib-info/update', data })
}
// 删除资源
export const deleteResource = (id) => {
return request.delete({ url: '/admin-api/crm/knowledge-lib-info/delete?id=' + id })
}

View File

@@ -0,0 +1,54 @@
import request from '@/config/axios'
export interface MenuVO {
id: number
name: string
permission: string
type: number
sort: number
parentId: number
path: string
icon: string
component: string
componentName?: string
status: number
visible: boolean
keepAlive: boolean
alwaysShow?: boolean
createTime: Date
}
// 获取服务列表
export const getServiceAppList = () => {
return request.get({ url: '/admin-api/system/service/list' })
}
// 查询菜单(精简)列表
export const getSimpleMenusList = () => {
return request.get({ url: '/admin-api/system/menu/list-all-simple' })
}
// 查询菜单列表
export const getMenuList = (params) => {
return request.get({ url: '/admin-api/system/menu/list', params })
}
// 获取菜单详情
export const getMenu = (id: number) => {
return request.get({ url: '/admin-api/system/menu/get?id=' + id })
}
// 新增菜单
export const createMenu = (data: MenuVO) => {
return request.post({ url: '/admin-api/system/menu/create', data })
}
// 修改菜单
export const updateMenu = (data: MenuVO) => {
return request.put({ url: '/admin-api/system/menu/update', data })
}
// 删除菜单
export const deleteMenu = (id: number) => {
return request.delete({ url: '/admin-api/system/menu/delete?id=' + id })
}

View File

@@ -0,0 +1,50 @@
import request from '@/config/axios'
import qs from 'qs'
export interface NotifyMessageVO {
id: number
userId: number
userType: number
templateId: number
templateCode: string
templateNickname: string
templateContent: string
templateType: number
templateParams: string
readStatus: boolean
readTime: Date
}
// 查询站内信消息列表
export const getNotifyMessagePage = async (params: PageParam) => {
return await request.get({ url: '/admin-api/system/notify-message/page', params })
}
// 获得我的站内信分页
export const getMyNotifyMessagePage = async (params: PageParam) => {
return await request.get({ url: '/admin-api/system/notify-message/my-page', params })
}
// 批量标记已读
export const updateNotifyMessageRead = async (ids) => {
return await request.put({
url:
'/admin-api/system/notify-message/update-read?' +
qs.stringify({ ids: ids }, { indices: false })
})
}
// 标记所有站内信为已读
export const updateAllNotifyMessageRead = async () => {
return await request.put({ url: '/admin-api/system/notify-message/update-all-read' })
}
// 获取当前用户的最新站内信列表
export const getUnreadNotifyMessageList = async () => {
return await request.get({ url: '/admin-api/system/notify-message/get-unread-list' })
}
// 获得当前用户的未读站内信数量
export const getUnreadNotifyMessageCount = async () => {
return await request.get({ url: '/admin-api/system/notify-message/get-unread-count' })
}

View File

@@ -0,0 +1,49 @@
import request from '@/config/axios'
export interface NotifyTemplateVO {
id?: number
name: string
nickname: string
code: string
content: string
type: number
params: string
status: number
remark: string
}
export interface NotifySendReqVO {
userId: number | null
templateCode: string
templateParams: Map<String, Object>
}
// 查询站内信模板列表
export const getNotifyTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/system/notify-template/page', params })
}
// 查询站内信模板详情
export const getNotifyTemplate = async (id: number) => {
return await request.get({ url: '/system/notify-template/get?id=' + id })
}
// 新增站内信模板
export const createNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.post({ url: '/system/notify-template/create', data })
}
// 修改站内信模板
export const updateNotifyTemplate = async (data: NotifyTemplateVO) => {
return await request.put({ url: '/system/notify-template/update', data })
}
// 删除站内信模板
export const deleteNotifyTemplate = async (id: number) => {
return await request.delete({ url: '/system/notify-template/delete?id=' + id })
}
// 发送站内信
export const sendNotify = (data: NotifySendReqVO) => {
return request.post({ url: '/system/notify-template/send-notify', data })
}

View File

@@ -0,0 +1,42 @@
import request from '@/config/axios'
export interface PermissionAssignUserRoleReqVO {
userId: number
roleIds: number[]
}
export interface PermissionAssignRoleMenuReqVO {
roleId: number
menuIds: number[]
}
export interface PermissionAssignRoleDataScopeReqVO {
roleId: number
dataScope: number
dataScopeDeptIds: number[]
}
// 查询角色拥有的菜单权限
export const getRoleMenuList = async (roleId: number) => {
return await request.get({ url: '/admin-api/system/permission/list-role-menus?roleId=' + roleId })
}
// 赋予角色菜单权限
export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => {
return await request.post({ url: '/admin-api/system/permission/assign-role-menu', data })
}
// 赋予角色数据权限
export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => {
return await request.post({ url: '/admin-api/system/permission/assign-role-data-scope', data })
}
// 查询用户拥有的角色数组
export const getUserRoleList = async (userId: number) => {
return await request.get({ url: '/admin-api/system/permission/list-user-roles?userId=' + userId })
}
// 赋予用户角色
export const assignUserRole = async (data: PermissionAssignUserRoleReqVO) => {
return await request.post({ url: '/admin-api/system/permission/assign-user-role', data })
}

View File

@@ -0,0 +1,53 @@
import request from '@/config/axios'
export interface RoleVO {
id: number
name: string
code: string
sort: number
status: number
type: number
dataScope: number
dataScopeDeptIds: number[]
createTime: Date
}
export interface UpdateStatusReqVO {
id: number
status: number
}
// 查询角色列表
export const getRolePage = async (params: PageParam) => {
return await request.get({ url: '/admin-api/system/role/page', params })
}
// 查询角色(精简)列表
export const getSimpleRoleList = async () => {
return await request.get({ url: '/admin-api/system/role/list-all-simple' })
}
// 查询角色详情
export const getRole = async (id: number) => {
return await request.get({ url: '/admin-api/system/role/get?id=' + id })
}
// 新增角色
export const createRole = async (data: RoleVO) => {
return await request.post({ url: '/admin-api/system/role/create', data })
}
// 修改角色
export const updateRole = async (data: RoleVO) => {
return await request.put({ url: '/admin-api/system/role/update', data })
}
// 删除角色
export const deleteRole = async (id: number) => {
return await request.delete({ url: '/admin-api/system/role/delete?id=' + id })
}
// 角色用户
export const getRoleUsers = async (params) => {
return await request.get({ url: '/admin-api/system/role/getUserByRole', params })
}

View File

@@ -0,0 +1,16 @@
import request from '@/config/axios'
// 通过key查询内容
export const getConfigByConfigKey = (params) => {
return request.get({ url: '/admin-api/crm/config/getConfigByConfigKey', params })
}
// 保存配置项
export const updateConfig = (data) => {
return request.put({ url: '/admin-api/crm/config/batchUpdateConfigValue', data })
}
// 根据模块获取配置列表
export const getConfigList = (params) => {
return request.get({ url: '/admin-api/crm/config/query', params })
}

View File

@@ -0,0 +1,76 @@
import request from '@/config/axios'
export interface UserVO {
id: number
username: string
nickname: string
deptId: number
postIds: string[]
email: string
mobile: string
sex: number
avatar: string
loginIp: string
status: number
remark: string
loginDate: Date
createTime: Date
}
// 查询用户管理列表
export const getUserPage = (params: PageParam) => {
return request.get({ url: '/admin-api/system/user/page', params })
}
// 查询用户详情
export const getUser = (id: number) => {
return request.get({ url: '/admin-api/system/user/get?id=' + id })
}
// 新增用户
export const createUser = (data: UserVO) => {
return request.post({ url: '/admin-api/system/user/create', data })
}
// 修改用户
export const updateUser = (data: UserVO) => {
return request.put({ url: '/admin-api/system/user/update', data })
}
// 删除用户
export const deleteUser = (id: number) => {
return request.delete({ url: '/admin-api/system/user/delete?id=' + id })
}
// 导出用户
export const exportUser = (params) => {
return request.download({ url: '/admin-api/system/user/export', params })
}
// 下载用户导入模板
export const importUserTemplate = () => {
return request.download({ url: '/admin-api/system/user/get-import-template' })
}
// 用户密码重置
export const resetUserPwd = (id: number, password: string) => {
const data = {
id,
password
}
return request.put({ url: '/admin-api/system/user/update-password', data: data })
}
// 用户状态修改
export const updateUserStatus = (id: number, status: number) => {
const data = {
id,
status
}
return request.put({ url: '/admin-api/system/user/update-status', data: data })
}
// 获取用户精简信息列表
export const getSimpleUserList = (): Promise<UserVO[]> => {
return request.get({ url: '/admin-api/system/user/list-all-simple' })
}

View File

@@ -0,0 +1,77 @@
import request from '@/config/axios'
export interface ProfileDept {
id: number
name: string
}
export interface ProfileRole {
id: number
name: string
}
export interface ProfilePost {
id: number
name: string
}
export interface SocialUser {
id: number
type: number
openid: string
token: string
rawTokenInfo: string
nickname: string
avatar: string
rawUserInfo: string
code: string
state: string
}
export interface ProfileVO {
id: number
username: string
nickname: string
dept: ProfileDept
roles: ProfileRole[]
posts: ProfilePost[]
socialUsers: SocialUser[]
email: string
mobile: string
sex: number
avatar: string
status: number
remark: string
loginIp: string
loginDate: Date
createTime: Date
}
export interface UserProfileUpdateReqVO {
nickname: string
email: string
mobile: string
sex: number
}
// 查询用户个人信息
export const getUserProfile = () => {
return request.get({ url: '/admin-api/system/user/profile/get' })
}
// 修改用户个人信息
export const updateUserProfile = (data: UserProfileUpdateReqVO) => {
return request.put({ url: '/admin-api/system/user/profile/update', data })
}
// 用户密码重置
export const updateUserPassword = (oldPassword: string, newPassword: string) => {
return request.put({
url: '/admin-api/system/user/profile/update-password',
data: {
oldPassword: oldPassword,
newPassword: newPassword
}
})
}
// 用户头像上传
export const uploadAvatar = (data) => {
return request.upload({ url: '/admin-api/system/user/profile/update-avatar', data: data })
}

View File

@@ -0,0 +1,31 @@
import request from '@/config/axios'
// 社交绑定,使用 code 授权码
export const socialBind = (type, code, state) => {
return request.post({
url: '/admin-api/system/social-user/bind',
data: {
type,
code,
state
}
})
}
// 取消社交绑定
export const socialUnbind = (type, openid) => {
return request.delete({
url: '/admin-api/system/social-user/unbind',
data: {
type,
openid
}
})
}
// 社交授权的跳转
export const socialAuthRedirect = (type, redirectUri) => {
return request.get({
url: '/admin-api/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri
})
}

Binary file not shown.

BIN
src/assets/imgs/avatar.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
src/assets/imgs/avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

BIN
src/assets/imgs/profile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
src/assets/imgs/wechat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

1
src/assets/svgs/403.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

1
src/assets/svgs/404.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

1
src/assets/svgs/500.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

1
src/assets/svgs/icon.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.147.062a13 13 0 014.94.945c1.55.63 2.907 1.526 4.069 2.688a13.148 13.148 0 012.761 4.069c.678 1.55 1.017 3.245 1.017 5.086v102.3c0 3.681-1.187 6.733-3.56 9.155-2.373 2.422-5.352 3.633-8.937 3.633H12.992c-3.875 0-7-1.26-9.373-3.779-2.373-2.518-3.56-5.667-3.56-9.445V12.704c0-3.39 1.163-6.345 3.488-8.863C5.872 1.32 8.972.062 12.847.062h102.3zM81.434 109.047c1.744 0 3.003-.412 3.778-1.235.775-.824 1.163-1.914 1.163-3.27 0-1.26-.388-2.325-1.163-3.197-.775-.872-2.034-1.307-3.778-1.307H72.57c.097-.194.145-.485.145-.872V27.09h9.01c1.743 0 2.954-.436 3.633-1.308.678-.872 1.017-1.938 1.017-3.197 0-1.26-.34-2.325-1.017-3.197-.679-.872-1.89-1.308-3.633-1.308H46.268c-1.743 0-2.954.436-3.632 1.308-.678.872-1.018 1.938-1.018 3.197 0 1.26.34 2.325 1.018 3.197.678.872 1.889 1.308 3.632 1.308h8.138v72.075c0 .193.024.339.073.436.048.096.072.242.072.436H46.56c-1.744 0-3.003.435-3.778 1.307-.775.872-1.163 1.938-1.163 3.197 0 1.356.388 2.446 1.163 3.27.775.823 2.034 1.235 3.778 1.235h34.875z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="5760" height="3040"><image width="5760" height="3040" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAFoAAAAvgAQMAAAC1QKagAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABlBMVEUsNEr///91v/yPAAAA AWJLR0QB/wIt3gAAAAd0SU1FB+YBBQYyN1c3BnEAAAhjSURBVHja7cExAQAAAMKg9U9tDB+gAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAACAtwFzzwABY3VrRQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMi0wMS0wNVQwNjo1 MDo1MyswMDowMCfNlVoAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjItMDEtMDVUMDY6NTA6NTQrMDA6 MDCTNxNoAAAAAElFTkSuQmCC"/></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 20.967v59.59c0 11.59 8.537 20.966 19.075 20.966h28.613l1 26.477L76.8 101.523h32.125c10.538 0 19.075-9.377 19.075-20.966v-59.59C128 9.377 119.463 0 108.925 0h-89.85C8.538 0 0 9.377 0 20.967zm82.325 33.1c0-5.524 4.013-9.935 9.037-9.935 5.026 0 9.038 4.41 9.038 9.934 0 5.524-4.025 9.934-9.038 9.934-5.024 0-9.037-4.41-9.037-9.934zm-27.613 0c0-5.524 4.013-9.935 9.038-9.935s9.037 4.41 9.037 9.934c0 5.524-4.025 9.934-9.037 9.934-5.025 0-9.038-4.41-9.038-9.934zm-27.1 0c0-5.524 4.013-9.935 9.038-9.935s9.038 4.41 9.038 9.934c0 5.524-4.026 9.934-9.05 9.934-5.013 0-9.025-4.41-9.025-9.934z"/></svg>

After

Width:  |  Height:  |  Size: 669 B

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M54.122 127.892v-28.68H7.513V87.274h46.609v-12.4H7.513v-12.86h38.003L.099 0h22.6l32.556 45.07c3.617 5.144 6.44 9.611 8.487 13.385 1.788-3.05 4.89-7.779 9.301-14.186L103.93 0h24.01L82.385 62.013h38.34v12.862h-46.41v12.4h46.41v11.937h-46.41v28.68H54.123z"/></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M42.913 101.36c1.642 0 3.198.332 4.667.996a12.28 12.28 0 013.89 2.772c1.123 1.184 1.987 2.582 2.592 4.193.605 1.612.908 3.318.908 5.118 0 1.8-.303 3.507-.908 5.118-.605 1.611-1.469 3.01-2.593 4.194a13.3 13.3 0 01-3.889 2.843 10.582 10.582 0 01-4.667 1.066c-1.729 0-3.306-.355-4.732-1.066a13.604 13.604 0 01-3.825-2.843c-1.123-1.185-1.988-2.583-2.593-4.194a14.437 14.437 0 01-.907-5.118c0-1.8.302-3.506.907-5.118.605-1.61 1.47-3.009 2.593-4.193a12.515 12.515 0 013.825-2.772c1.426-.664 3.003-.996 4.732-.996zm53.932.285c1.643 0 3.22.331 4.733.995a11.386 11.386 0 013.889 2.772c1.08 1.185 1.945 2.583 2.593 4.194.648 1.61.972 3.317.972 5.118 0 1.8-.324 3.506-.972 5.117-.648 1.611-1.513 3.01-2.593 4.194a12.253 12.253 0 01-3.89 2.843 11 11 0 01-4.732 1.066 10.58 10.58 0 01-4.667-1.066 12.478 12.478 0 01-3.824-2.843c-1.08-1.185-1.945-2.583-2.593-4.194a13.581 13.581 0 01-.973-5.117c0-1.801.325-3.507.973-5.118.648-1.611 1.512-3.01 2.593-4.194a11.559 11.559 0 013.824-2.772 11.212 11.212 0 014.667-.995zm21.781-80.747c2.42 0 4.3.355 5.64 1.066 1.34.71 2.29 1.587 2.852 2.63a6.427 6.427 0 01.778 3.34c-.044 1.185-.195 2.204-.454 3.057-.26.853-.8 2.606-1.62 5.26a589.268 589.268 0 01-2.788 8.743 1236.373 1236.373 0 00-3.047 9.453c-.994 3.128-1.75 5.592-2.269 7.393-1.123 3.79-2.55 6.42-4.278 7.89-1.728 1.469-3.846 2.203-6.352 2.203H39.023l1.945 12.795h65.342c4.148 0 6.223 1.943 6.223 5.828 0 1.896-.41 3.53-1.232 4.905-.821 1.374-2.442 2.061-4.862 2.061H38.505c-1.729 0-3.176-.426-4.343-1.28-1.167-.852-2.14-1.966-2.917-3.34a21.277 21.277 0 01-1.88-4.478 44.128 44.128 0 01-1.102-4.55c-.087-.568-.324-1.942-.713-4.122-.39-2.18-.865-4.904-1.426-8.174l-1.88-10.947c-.692-4.027-1.383-8.079-2.075-12.154-1.642-9.572-3.5-20.234-5.574-31.986H6.87c-1.296 0-2.377-.356-3.24-1.067a9.024 9.024 0 01-2.14-2.558 10.416 10.416 0 01-1.167-3.2C.108 8.53 0 7.488 0 6.54c0-1.896.583-3.46 1.75-4.69C2.917.615 4.494 0 6.482 0h13.095c1.728 0 3.111.284 4.148.853 1.037.569 1.858 1.28 2.463 2.132a8.548 8.548 0 011.297 2.701c.26.948.475 1.754.648 2.417.173.758.346 1.825.519 3.199.173 1.374.345 2.772.518 4.193.26 1.706.519 3.507.778 5.403h88.678z"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,3 @@
import Backtop from './src/Backtop.vue'
export { Backtop }

View File

@@ -0,0 +1,15 @@
<script lang="ts" name="BackTop" setup>
import { ElBacktop } from 'element-plus'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls, variables } = useDesign()
const prefixCls = getPrefixCls('backtop')
</script>
<template>
<ElBacktop
:class="`${prefixCls}-backtop`"
:target="`.${variables.namespace}-layout-content-scrollbar .${variables.elNamespace}-scrollbar__wrap`"
/>
</template>

View File

@@ -0,0 +1,3 @@
import ConfigGlobal from './src/ConfigGlobal.vue'
export { ConfigGlobal }

View File

@@ -0,0 +1,61 @@
<script lang="ts" name="ConfigGlobal" setup>
import { propTypes } from '@/utils/propTypes'
import { useLocaleStore } from '@/store/modules/locale'
import { useAppStore } from '@/store/modules/app'
import { setCssVar } from '@/utils'
import { useDesign } from '@/hooks/web/useDesign'
import { ElementPlusSize } from '@/types/elementPlus'
import { useWindowSize } from '@vueuse/core'
const { variables } = useDesign()
const appStore = useAppStore()
const props = defineProps({
size: propTypes.oneOf<ElementPlusSize>(['default', 'small', 'large']).def('default')
})
provide('configGlobal', props)
// 初始化所有主题色
onMounted(() => {
appStore.setCssVarTheme()
})
const { width } = useWindowSize()
// 监听窗口变化
watch(
() => width.value,
(width: number) => {
if (width < 768) {
!appStore.getMobile ? appStore.setMobile(true) : undefined
setCssVar('--left-menu-min-width', '0')
appStore.setCollapse(true)
appStore.getLayout !== 'classic' ? appStore.setLayout('classic') : undefined
} else {
appStore.getMobile ? appStore.setMobile(false) : undefined
setCssVar('--left-menu-min-width', '64px')
}
},
{
immediate: true
}
)
// 多语言相关
const localeStore = useLocaleStore()
const currentLocale = computed(() => localeStore.currentLocale)
</script>
<template>
<ElConfigProvider
:locale="currentLocale.elLocale"
:message="{ max: 1 }"
:namespace="variables.elNamespace"
:size="size"
>
<slot></slot>
</ElConfigProvider>
</template>

View File

@@ -0,0 +1,3 @@
import ContentDetailWrap from './src/ContentDetailWrap.vue'
export { ContentDetailWrap }

View File

@@ -0,0 +1,56 @@
<script lang="ts" name="ContentDetailWrap" setup>
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
const { t } = useI18n()
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('content-detail-wrap')
defineProps({
title: propTypes.string.def(''),
message: propTypes.string.def('')
})
const emit = defineEmits(['back'])
const offset = ref(85)
const contentDetailWrap = ref()
onMounted(() => {
offset.value = contentDetailWrap.value.getBoundingClientRect().top
})
</script>
<template>
<div ref="contentDetailWrap" :class="[`${prefixCls}-container`]">
<Sticky :offset="offset">
<div
:class="[
`${prefixCls}-header`,
'flex border-bottom-1 h-50px items-center text-center pr-10px'
]"
>
<div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']">
<ElButton @click="emit('back')">
<Icon class="mr-5px" icon="ep:arrow-left" />
{{ t('common.back') }}
</ElButton>
</div>
<div :class="[`${prefixCls}-header__title`, 'flex flex-1 justify-center']">
<slot name="title">
<label class="text-16px font-700">{{ title }}</label>
</slot>
</div>
<div :class="[`${prefixCls}-header__right`, 'flex pl-10px pr-10px']">
<slot name="right"></slot>
</div>
</div>
</Sticky>
<div style="padding: var(--app-content-padding)">
<ElCard :class="[`${prefixCls}-body`, 'mb-20px']" shadow="never">
<div>
<slot></slot>
</div>
</ElCard>
</div>
</div>
</template>

View File

@@ -0,0 +1,3 @@
import ContentWrap from './src/ContentWrap.vue'
export { ContentWrap }

View File

@@ -0,0 +1,32 @@
<script lang="ts" name="ContentWrap" setup>
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('content-wrap')
defineProps({
title: propTypes.string.def(''),
message: propTypes.string.def('')
})
</script>
<template>
<ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
<template v-if="title" #header>
<div class="flex items-center">
<span class="text-16px font-700">{{ title }}</span>
<ElTooltip v-if="message" effect="dark" placement="right">
<template #content>
<div class="max-w-200px">{{ message }}</div>
</template>
<Icon :size="14" class="ml-5px" icon="ep:question-filled" />
</ElTooltip>
</div>
</template>
<div>
<slot></slot>
</div>
</ElCard>
</template>

View File

@@ -0,0 +1,3 @@
import CountTo from './src/CountTo.vue'
export { CountTo }

View File

@@ -0,0 +1,180 @@
<script lang="ts" name="CountTo" setup>
import { PropType } from 'vue'
import { isNumber } from '@/utils/is'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('count-to')
const props = defineProps({
startVal: propTypes.number.def(0),
endVal: propTypes.number.def(2021),
duration: propTypes.number.def(3000),
autoplay: propTypes.bool.def(true),
decimals: propTypes.number.validate((value: number) => value >= 0).def(0),
decimal: propTypes.string.def('.'),
separator: propTypes.string.def(','),
prefix: propTypes.string.def(''),
suffix: propTypes.string.def(''),
useEasing: propTypes.bool.def(true),
easingFn: {
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
default(t: number, b: number, c: number, d: number) {
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b
}
}
})
const emit = defineEmits(['mounted', 'callback'])
const formatNumber = (num: number | string) => {
const { decimals, decimal, separator, suffix, prefix } = props
num = Number(num).toFixed(decimals)
num += ''
const x = num.split('.')
let x1 = x[0]
const x2 = x.length > 1 ? decimal + x[1] : ''
const rgx = /(\d+)(\d{3})/
if (separator && !isNumber(separator)) {
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + separator + '$2')
}
}
return prefix + x1 + x2 + suffix
}
const state = reactive<{
localStartVal: number
printVal: number | null
displayValue: string
paused: boolean
localDuration: number | null
startTime: number | null
timestamp: number | null
rAF: any
remaining: number | null
}>({
localStartVal: props.startVal,
displayValue: formatNumber(props.startVal),
printVal: null,
paused: false,
localDuration: props.duration,
startTime: null,
timestamp: null,
remaining: null,
rAF: null
})
const displayValue = toRef(state, 'displayValue')
onMounted(() => {
if (props.autoplay) {
start()
}
emit('mounted')
})
const getCountDown = computed(() => {
return props.startVal > props.endVal
})
watch([() => props.startVal, () => props.endVal], () => {
if (props.autoplay) {
start()
}
})
const start = () => {
const { startVal, duration } = props
state.localStartVal = startVal
state.startTime = null
state.localDuration = duration
state.paused = false
state.rAF = requestAnimationFrame(count)
}
const pauseResume = () => {
if (state.paused) {
resume()
state.paused = false
} else {
pause()
state.paused = true
}
}
const pause = () => {
cancelAnimationFrame(state.rAF)
}
const resume = () => {
state.startTime = null
state.localDuration = +(state.remaining as number)
state.localStartVal = +(state.printVal as number)
requestAnimationFrame(count)
}
const reset = () => {
state.startTime = null
cancelAnimationFrame(state.rAF)
state.displayValue = formatNumber(props.startVal)
}
const count = (timestamp: number) => {
const { useEasing, easingFn, endVal } = props
if (!state.startTime) state.startTime = timestamp
state.timestamp = timestamp
const progress = timestamp - state.startTime
state.remaining = (state.localDuration as number) - progress
if (useEasing) {
if (unref(getCountDown)) {
state.printVal =
state.localStartVal -
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number)
} else {
state.printVal = easingFn(
progress,
state.localStartVal,
endVal - state.localStartVal,
state.localDuration as number
)
}
} else {
if (unref(getCountDown)) {
state.printVal =
state.localStartVal -
(state.localStartVal - endVal) * (progress / (state.localDuration as number))
} else {
state.printVal =
state.localStartVal +
(endVal - state.localStartVal) * (progress / (state.localDuration as number))
}
}
if (unref(getCountDown)) {
state.printVal = state.printVal < endVal ? endVal : state.printVal
} else {
state.printVal = state.printVal > endVal ? endVal : state.printVal
}
state.displayValue = formatNumber(state.printVal!)
if (progress < (state.localDuration as number)) {
state.rAF = requestAnimationFrame(count)
} else {
emit('callback')
}
}
defineExpose({
pauseResume,
reset,
start,
pause
})
</script>
<template>
<span :class="prefixCls">
{{ displayValue }}
</span>
</template>

View File

@@ -0,0 +1,2 @@
import Crontab from './src/Crontab.vue'
export { Crontab }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
import CropperImage from './src/Cropper.vue'
import CropperAvatar from './src/CropperAvatar.vue'
export { CropperImage, CropperAvatar }

View File

@@ -0,0 +1,257 @@
<template>
<div>
<Dialog
v-model="dialogVisible"
:canFullscreen="false"
:title="t('cropper.modalTitle')"
maxHeight="380px"
width="800px"
>
<div :class="prefixCls">
<div :class="`${prefixCls}-left`">
<div :class="`${prefixCls}-cropper`">
<CropperImage
v-if="src"
:circled="circled"
:src="src"
height="300px"
@cropend="handleCropend"
@ready="handleReady"
/>
</div>
<div :class="`${prefixCls}-toolbar`">
<el-upload :beforeUpload="handleBeforeUpload" :fileList="[]" accept="image/*">
<el-tooltip :content="t('cropper.selectImage')" placement="bottom">
<XButton preIcon="ant-design:upload-outlined" type="primary" />
</el-tooltip>
</el-upload>
<el-space>
<el-tooltip :content="t('cropper.btn_reset')" placement="bottom">
<XButton
:disabled="!src"
preIcon="ant-design:reload-outlined"
size="small"
type="primary"
@click="handlerToolbar('reset')"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_rotate_left')" placement="bottom">
<XButton
:disabled="!src"
preIcon="ant-design:rotate-left-outlined"
size="small"
type="primary"
@click="handlerToolbar('rotate', -45)"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_rotate_right')" placement="bottom">
<XButton
:disabled="!src"
preIcon="ant-design:rotate-right-outlined"
size="small"
type="primary"
@click="handlerToolbar('rotate', 45)"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_scale_x')" placement="bottom">
<XButton
:disabled="!src"
preIcon="vaadin:arrows-long-h"
size="small"
type="primary"
@click="handlerToolbar('scaleX')"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_scale_y')" placement="bottom">
<XButton
:disabled="!src"
preIcon="vaadin:arrows-long-v"
size="small"
type="primary"
@click="handlerToolbar('scaleY')"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_zoom_in')" placement="bottom">
<XButton
:disabled="!src"
preIcon="ant-design:zoom-in-outlined"
size="small"
type="primary"
@click="handlerToolbar('zoom', 0.1)"
/>
</el-tooltip>
<el-tooltip :content="t('cropper.btn_zoom_out')" placement="bottom">
<XButton
:disabled="!src"
preIcon="ant-design:zoom-out-outlined"
size="small"
type="primary"
@click="handlerToolbar('zoom', -0.1)"
/>
</el-tooltip>
</el-space>
</div>
</div>
<div :class="`${prefixCls}-right`">
<div :class="`${prefixCls}-preview`">
<img v-if="previewSource" :alt="t('cropper.preview')" :src="previewSource" />
</div>
<template v-if="previewSource">
<div :class="`${prefixCls}-group`">
<el-avatar :src="previewSource" size="large" />
<el-avatar :size="48" :src="previewSource" />
<el-avatar :size="64" :src="previewSource" />
<el-avatar :size="80" :src="previewSource" />
</div>
</template>
</div>
</div>
<template #footer>
<el-button type="primary" @click="handleOk">{{ t('cropper.okText') }}</el-button>
</template>
</Dialog>
</div>
</template>
<script lang="ts" name="CopperModal" setup>
import { useDesign } from '@/hooks/web/useDesign'
import { dataURLtoBlob } from '@/utils/filt'
import { useI18n } from 'vue-i18n'
import type { CropendResult, Cropper } from './types'
import { propTypes } from '@/utils/propTypes'
import { CropperImage } from '@/components/Cropper'
const props = defineProps({
srcValue: propTypes.string.def(''),
circled: propTypes.bool.def(true)
})
const emit = defineEmits(['uploadSuccess'])
const { t } = useI18n()
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('cropper-am')
const src = ref(props.srcValue)
const previewSource = ref('')
const cropper = ref<Cropper>()
const dialogVisible = ref(false)
let filename = ''
let scaleX = 1
let scaleY = 1
// Block upload
function handleBeforeUpload(file: File) {
const reader = new FileReader()
reader.readAsDataURL(file)
src.value = ''
previewSource.value = ''
reader.onload = function (e) {
src.value = (e.target?.result as string) ?? ''
filename = file.name
}
return false
}
function handleCropend({ imgBase64 }: CropendResult) {
previewSource.value = imgBase64
}
function handleReady(cropperInstance: Cropper) {
cropper.value = cropperInstance
}
function handlerToolbar(event: string, arg?: number) {
if (event === 'scaleX') {
scaleX = arg = scaleX === -1 ? 1 : -1
}
if (event === 'scaleY') {
scaleY = arg = scaleY === -1 ? 1 : -1
}
cropper?.value?.[event]?.(arg)
}
async function handleOk() {
const blob = dataURLtoBlob(previewSource.value)
emit('uploadSuccess', { source: previewSource.value, data: blob, filename: filename })
}
function openModal() {
dialogVisible.value = true
}
function closeModal() {
dialogVisible.value = false
}
defineExpose({ openModal, closeModal })
</script>
<style lang="scss">
$prefix-cls: #{$namespace}-cropper-am;
.#{$prefix-cls} {
display: flex;
&-left,
&-right {
height: 340px;
}
&-left {
width: 55%;
}
&-right {
width: 45%;
}
&-cropper {
height: 300px;
background: #eee;
background-image: linear-gradient(
45deg,
rgb(0 0 0 / 25%) 25%,
transparent 0,
transparent 75%,
rgb(0 0 0 / 25%) 0
),
linear-gradient(
45deg,
rgb(0 0 0 / 25%) 25%,
transparent 0,
transparent 75%,
rgb(0 0 0 / 25%) 0
);
background-position: 0 0, 12px 12px;
background-size: 24px 24px;
}
&-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
&-preview {
width: 220px;
height: 220px;
margin: 0 auto;
overflow: hidden;
border: 1px solid;
border-radius: 50%;
img {
width: 100%;
height: 100%;
}
}
&-group {
display: flex;
padding-top: 8px;
margin-top: 8px;
border-top: 1px solid;
justify-content: space-around;
align-items: center;
}
}
</style>

View File

@@ -0,0 +1,181 @@
<template>
<div :class="getClass" :style="getWrapperStyle">
<img
v-show="isReady"
ref="imgElRef"
:alt="alt"
:crossorigin="crossorigin"
:src="src"
:style="getImageStyle"
/>
</div>
</template>
<script lang="ts" name="Cropper" setup>
import { CSSProperties, PropType } from 'vue'
import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes'
import { useDebounceFn } from '@vueuse/core'
type Options = Cropper.Options
const defaultOptions: Options = {
aspectRatio: 1,
zoomable: true,
zoomOnTouch: true,
zoomOnWheel: true,
cropBoxMovable: true,
cropBoxResizable: true,
toggleDragModeOnDblclick: true,
autoCrop: true,
background: true,
highlight: true,
center: true,
responsive: true,
restore: true,
checkCrossOrigin: true,
checkOrientation: true,
scalable: true,
modal: true,
guides: true,
movable: true,
rotatable: true
}
const props = defineProps({
src: propTypes.string.def(''),
alt: propTypes.string.def(''),
circled: propTypes.bool.def(false),
realTimePreview: propTypes.bool.def(true),
height: propTypes.string.def('360px'),
crossorigin: {
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
default: undefined
},
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
options: { type: Object as PropType<Options>, default: () => ({}) }
})
const emit = defineEmits(['cropend', 'ready', 'cropendError'])
const attrs = useAttrs()
const imgElRef = ref<ElRef<HTMLImageElement>>()
const cropper = ref<Nullable<Cropper>>()
const isReady = ref(false)
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('cropper-image')
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80)
const getImageStyle = computed((): CSSProperties => {
return {
height: props.height,
maxWidth: '100%',
...props.imageStyle
}
})
const getClass = computed(() => {
return [
prefixCls,
attrs.class,
{
[`${prefixCls}--circled`]: props.circled
}
]
})
const getWrapperStyle = computed((): CSSProperties => {
return { height: `${props.height}`.replace(/px/, '') + 'px' }
})
onMounted(init)
onUnmounted(() => {
cropper.value?.destroy()
})
async function init() {
const imgEl = unref(imgElRef)
if (!imgEl) {
return
}
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true
realTimeCroppered()
emit('ready', cropper.value)
},
crop() {
debounceRealTimeCroppered()
},
zoom() {
debounceRealTimeCroppered()
},
cropmove() {
debounceRealTimeCroppered()
},
...props.options
})
}
// Real-time display preview
function realTimeCroppered() {
props.realTimePreview && croppered()
}
// event: return base64 and width and height information after cropping
function croppered() {
if (!cropper.value) {
return
}
let imgInfo = cropper.value.getData()
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas()
canvas.toBlob((blob) => {
if (!blob) {
return
}
let fileReader: FileReader = new FileReader()
fileReader.readAsDataURL(blob)
fileReader.onloadend = (e) => {
emit('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo
})
}
fileReader.onerror = () => {
emit('cropendError')
}
}, 'image/png')
}
// Get a circular picture canvas
function getRoundedCanvas() {
const sourceCanvas = cropper.value!.getCroppedCanvas()
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')!
const width = sourceCanvas.width
const height = sourceCanvas.height
canvas.width = width
canvas.height = height
context.imageSmoothingEnabled = true
context.drawImage(sourceCanvas, 0, 0, width, height)
context.globalCompositeOperation = 'destination-in'
context.beginPath()
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true)
context.fill()
return canvas
}
</script>
<style lang="scss">
$prefix-cls: #{$namespace}-cropper-image;
.#{$prefix-cls} {
&--circled {
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
}
}
</style>

View File

@@ -0,0 +1,141 @@
<template>
<div class="user-info-head" @click="open()">
<img v-if="sourceValue" :src="sourceValue" alt="avatar" class="img-circle img-lg" />
<el-button v-if="showBtn" :class="`${prefixCls}-upload-btn`" @click="open()">
{{ btnText ? btnText : t('cropper.selectImage') }}
</el-button>
<CopperModal
ref="cropperModelRef"
:srcValue="sourceValue"
@upload-success="handleUploadSuccess"
/>
</div>
</template>
<script lang="ts" name="CropperAvatar" setup>
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes'
import { useI18n } from 'vue-i18n'
import CopperModal from './CopperModal.vue'
const props = defineProps({
width: propTypes.string.def('200px'),
value: propTypes.string.def(''),
showBtn: propTypes.bool.def(true),
btnText: propTypes.string.def('')
})
const emit = defineEmits(['update:value', 'change'])
const sourceValue = ref(props.value)
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('cropper-avatar')
const message = useMessage()
const { t } = useI18n()
const cropperModelRef = ref()
watchEffect(() => {
sourceValue.value = props.value
})
watch(
() => sourceValue.value,
(v: string) => {
emit('update:value', v)
}
)
function handleUploadSuccess({ source, data, filename }) {
sourceValue.value = source
emit('change', { source, data, filename })
message.success(t('cropper.uploadSuccess'))
}
function open() {
cropperModelRef.value.openModal()
}
function close() {
cropperModelRef.value.closeModal()
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
$prefix-cls: #{$namespace}--cropper-avatar;
.#{$prefix-cls} {
display: inline-block;
text-align: center;
&-image-wrapper {
overflow: hidden;
cursor: pointer;
border: 1px solid;
border-radius: 50%;
img {
width: 100%;
}
}
&-image-mask {
opacity: 0%;
position: absolute;
width: inherit;
height: inherit;
border-radius: inherit;
border: inherit;
background: rgb(0 0 0 / 40%);
cursor: pointer;
transition: opacity 0.4s;
::v-deep(svg) {
margin: auto;
}
}
&-image-mask:hover {
opacity: 4000%;
}
&-upload-btn {
margin: 10px auto;
}
}
.user-info-head {
position: relative;
display: inline-block;
}
.img-circle {
border-radius: 50%;
}
.img-lg {
width: 120px;
height: 120px;
}
.user-info-head:hover:after {
content: '+';
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
color: #eee;
background: rgba(0, 0, 0, 0.5);
font-size: 24px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
line-height: 110px;
border-radius: 50%;
}
</style>

View File

@@ -0,0 +1,8 @@
import type Cropper from 'cropperjs'
export interface CropendResult {
imgBase64: string
imgInfo: Cropper.Data
}
export type { Cropper }

View File

@@ -0,0 +1,3 @@
import Descriptions from './src/Descriptions.vue'
export { Descriptions }

View File

@@ -0,0 +1,172 @@
<script lang="ts" name="Descriptions" setup>
import { PropType } from 'vue'
import dayjs from 'dayjs'
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes'
import { useAppStore } from '@/store/modules/app'
import { DescriptionsSchema } from '@/types/descriptions'
const appStore = useAppStore()
const mobile = computed(() => appStore.getMobile)
const attrs = useAttrs()
const slots = useSlots()
const props = defineProps({
title: propTypes.string.def(''),
message: propTypes.string.def(''),
collapse: propTypes.bool.def(true),
columns: propTypes.number.def(1),
labelWidth: propTypes.string.def('100px'),
schema: {
type: Array as PropType<DescriptionsSchema[]>,
default: () => []
},
data: {
type: Object as PropType<any>,
default: () => ({})
},
defaultShow: propTypes.bool.def(true)
})
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('descriptions')
const getBindValue = computed(() => {
const delArr: string[] = ['title', 'message', 'collapse', 'schema', 'data', 'class']
const obj = { ...attrs, ...props }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
})
const getBindItemValue = (item: DescriptionsSchema) => {
const delArr: string[] = ['field']
const obj = { ...item }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
}
// 折叠
const show = ref(props.defaultShow)
const toggleClick = () => {
if (props.collapse) {
show.value = !unref(show)
}
}
</script>
<template>
<div
:class="[
prefixCls,
'bg-[var(--el-color-white)] dark:(bg-[var(--el-bg-color)] border-[var(--el-border-color)] border-1px)'
]"
>
<div
v-if="title"
:class="[
`${prefixCls}-header`,
'h-40px flex justify-between items-center border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
]"
@click="toggleClick"
>
<div :class="[`${prefixCls}-header__title`, 'relative font-18px font-bold ml-10px']">
<div class="flex items-center">
{{ title }}
<ElTooltip v-if="message" :content="message" placement="right">
<Icon class="ml-5px" icon="ep:warning" />
</ElTooltip>
</div>
</div>
<Icon v-if="collapse" :icon="show ? 'ep:arrow-down' : 'ep:arrow-up'" />
</div>
<ElCollapseTransition>
<div v-show="show" :class="[`${prefixCls}-content`, 'p-10px']">
<ElDescriptions
:column="props.columns"
:direction="mobile ? 'vertical' : 'horizontal'"
border
v-bind="getBindValue"
>
<template v-if="slots['extra']" #extra>
<slot name="extra"></slot>
</template>
<ElDescriptionsItem
v-for="item in schema"
:key="item.field"
min-width="80"
:span="item.span"
label-class-name="desc-label"
v-bind="getBindItemValue(item)"
>
<template #label>
<slot
:name="`${item.field}-label`"
:row="{
label: item.label
}"
>{{ item.label }}
</slot>
</template>
<template #default>
<slot v-if="item.dateFormat">
{{
data[item.field] !== null ? dayjs(data[item.field]).format(item.dateFormat) : ''
}}
</slot>
<slot v-else-if="item.dictType">
<DictTag :type="item.dictType" :value="data[item.field] + ''" />
</slot>
<slot v-else-if="item.isEditor">
<div v-if="data[item.field]" v-dompurify-html="data[item.field]"></div>
</slot>
<slot v-else :name="item.field" :row="data">{{ data[item.field] }}</slot>
</template>
</ElDescriptionsItem>
</ElDescriptions>
</div>
</ElCollapseTransition>
</div>
</template>
<style lang="scss" scoped>
$prefix-cls: #{$namespace}-descriptions;
.#{$prefix-cls}-header {
&__title {
&::after {
position: absolute;
top: 3px;
left: -10px;
width: 4px;
height: 70%;
background: var(--el-color-primary);
content: '';
}
}
}
.#{$prefix-cls}-content {
// :deep(.#{$elNamespace}-descriptions__cell) {
// width: 0;
// }
:deep(.desc-label) {
width: v-bind('labelWidth');
}
}
</style>

View File

@@ -0,0 +1,3 @@
import Dialog from './src/Dialog.vue'
export { Dialog }

View File

@@ -0,0 +1,126 @@
<script lang="ts" name="Dialog" setup>
import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is'
const slots = useSlots()
const props = defineProps({
modelValue: propTypes.bool.def(false),
title: propTypes.string.def('Dialog'),
fullscreen: propTypes.bool.def(true),
width: propTypes.oneOfType([String, Number]).def('40%'),
scroll: propTypes.bool.def(false), // 是否开启滚动条。如果是的话,按照 maxHeight 设置最大高度
maxHeight: propTypes.oneOfType([String, Number]).def('300px')
})
const getBindValue = computed(() => {
const delArr: string[] = ['fullscreen', 'title', 'maxHeight']
const attrs = useAttrs()
const obj = { ...attrs, ...props }
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
})
const isFullscreen = ref(false)
const toggleFull = () => {
isFullscreen.value = !unref(isFullscreen)
}
const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight)
watch(
() => isFullscreen.value,
async (val: boolean) => {
// 计算最大高度
await nextTick()
if (val) {
const windowHeight = document.documentElement.offsetHeight
dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px`
} else {
dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight
}
},
{
immediate: true
}
)
const dialogStyle = computed(() => {
return {
height: unref(dialogHeight)
}
})
const emit = defineEmits(['close'])
</script>
<template>
<ElDialog
:close-on-click-modal="false"
:fullscreen="isFullscreen"
:width="width"
destroy-on-close
draggable
lock-scroll
v-bind="getBindValue"
@close="emit('close')"
>
<template #header>
<div class="flex justify-between">
<slot name="title">
{{ title }}
</slot>
<Icon
v-if="fullscreen"
:icon="isFullscreen ? 'zmdi:fullscreen-exit' : 'zmdi:fullscreen'"
class="mr-22px cursor-pointer is-hover mt-2px z-10"
color="var(--el-color-info)"
@click="toggleFull"
/>
</div>
</template>
<!-- 情况一如果 scroll true说明开启滚动条 -->
<ElScrollbar v-if="scroll" :style="dialogStyle">
<slot></slot>
</ElScrollbar>
<!-- 情况二如果 scroll false说明关闭滚动条滚动条 -->
<slot v-else></slot>
<template v-if="slots.footer" #footer>
<slot name="footer"></slot>
</template>
</ElDialog>
</template>
<style lang="scss">
.#{$elNamespace}-dialog__header {
margin-right: 0 !important;
border-bottom: 1px solid var(--tags-view-border-color);
}
.#{$elNamespace}-dialog__footer {
border-top: 1px solid var(--tags-view-border-color);
}
.is-hover {
&:hover {
color: var(--el-color-primary) !important;
}
}
.dark {
.#{$elNamespace}-dialog__header {
border-bottom: 1px solid var(--el-border-color);
}
.#{$elNamespace}-dialog__footer {
border-top: 1px solid var(--el-border-color);
}
}
</style>

View File

@@ -0,0 +1,3 @@
import DictTag from './src/DictTag.vue'
export { DictTag }

View File

@@ -0,0 +1,60 @@
<script lang="tsx">
import { defineComponent, PropType, ref } from 'vue'
import { isHexColor } from '@/utils/color'
import { ElTag } from 'element-plus'
import { DictDataType, getDictOptions } from '@/utils/dict'
export default defineComponent({
name: 'DictTag',
props: {
type: {
type: String as PropType<string>,
required: true
},
value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>,
required: true
}
},
setup(props) {
const dictData = ref<DictDataType>()
const getDictObj = (dictType: string, value: string) => {
const dictOptions = getDictOptions(dictType)
dictOptions.forEach((dict: DictDataType) => {
if (dict.value === value) {
if (dict.colorType + '' === 'primary' || dict.colorType + '' === 'default') {
dict.colorType = ''
}
dictData.value = dict
}
})
}
const rederDictTag = () => {
if (!props.type) {
return null
}
// 解决自定义字典标签值为零时标签不渲染的问题
if (props.value === undefined || props.value === null) {
return null
}
getDictObj(props.type, props.value.toString())
// 添加标签的文字颜色为白色,解决自定义背景颜色时标签文字看不清的问题
return (
<ElTag
style={dictData.value?.cssClass ? 'color: #fff' : ''}
type={dictData.value?.colorType}
color={
dictData.value?.cssClass && isHexColor(dictData.value?.cssClass)
? dictData.value?.cssClass
: ''
}
disableTransitions={true}
>
{dictData.value?.label}
</ElTag>
)
}
return () => rederDictTag()
}
})
</script>

View File

@@ -0,0 +1,32 @@
<template>
<el-alert v-if="getEnable()" type="success" show-icon>
<template #title>
<div @click="goToUrl">{{ '' + title + '文档地址' + url }}</div>
</template>
</el-alert>
</template>
<script setup lang="tsx" name="DocAlert">
import { propTypes } from '@/utils/propTypes'
const props = defineProps({
title: propTypes.string,
url: propTypes.string
})
/** 跳转 URL 链接 */
const goToUrl = () => {
window.open(props.url)
}
/** 是否开启 */
const getEnable = () => {
return import.meta.env.VITE_APP_TENANT_ENABLE === 'true'
}
</script>
<style scoped>
.el-alert--success.is-light {
border: 1px solid green;
margin-bottom: 10px;
cursor: pointer;
}
</style>

View File

@@ -0,0 +1,3 @@
import Echart from './src/Echart.vue'
export { Echart }

Some files were not shown because too many files have changed in this diff Show More