工作流表单+模型代码提交
This commit is contained in:
19
package.json
19
package.json
@@ -11,16 +11,24 @@
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@form-create/designer": "^3.1.3",
|
||||
"@form-create/element-ui": "^3.1.24",
|
||||
"qs": "^6.12.0",
|
||||
"vue-i18n": "9.10.2",
|
||||
"web-storage-cache": "^1.1.1",
|
||||
"vue-types": "^5.1.1",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||
"@vueuse/core": "^10.7.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.6.2",
|
||||
"bpmn-js": "^7.3.1",
|
||||
"bpmn-js-properties-panel": "^0.37.2",
|
||||
"bpmn-js-token-simulation": "0.10.0",
|
||||
"bpmn-moddle": "^6.0.0",
|
||||
"camunda-bpmn-moddle": "^4.5.0",
|
||||
"bpmn-js-token-simulation": "^0.10.0",
|
||||
"camunda-bpmn-moddle": "^7.0.1",
|
||||
"bpmn-js": "8.9.0",
|
||||
"bpmn-js-properties-panel": "0.46.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"diagram-js": "^11.4.1",
|
||||
"diagram-js-minimap": "^2.0.4",
|
||||
@@ -46,7 +54,6 @@
|
||||
"splitpanes": "^3.1.5",
|
||||
"steady-xml": "0.1.0",
|
||||
"use-element-plus-theme": "^0.0.5",
|
||||
"vform3-builds": "^3.0.10",
|
||||
"vue": "^3.3.11",
|
||||
"vue-baidu-map-3x": "^1.0.35",
|
||||
"vue-baidu-map-offline": "^1.0.7",
|
||||
@@ -59,7 +66,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"@types/node": "^20.10.5",
|
||||
"@purge-icons/generated": "^0.9.0",
|
||||
"@types/splitpanes": "^2.2.6",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
|
||||
5509
pnpm-lock.yaml
generated
5509
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
73
src/api/bpm-boot/category.ts
Normal file
73
src/api/bpm-boot/category.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
import { BPM_BOOT } from '@/utils/constantRequest'
|
||||
|
||||
const MAPPING_PATH = BPM_BOOT + '/bpm/category'
|
||||
|
||||
/**
|
||||
* 查询流程分类数据
|
||||
*/
|
||||
export const listCategory = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/list',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有流程分类
|
||||
*/
|
||||
export const getCategorySimpleList = () => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/simpleList',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询分类详细信息
|
||||
*/
|
||||
export const getById = (id: string) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/getById?id=' + id,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增流程分类
|
||||
*/
|
||||
export const addCategory = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/add',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新流程分类
|
||||
*/
|
||||
export const updateCategory = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/update',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除流程分类
|
||||
*/
|
||||
export const deleteCategory = (data: any) => {
|
||||
let ids = [data]
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/delete',
|
||||
method: 'POST',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
73
src/api/bpm-boot/form.ts
Normal file
73
src/api/bpm-boot/form.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
import { BPM_BOOT } from '@/utils/constantRequest'
|
||||
|
||||
const MAPPING_PATH = BPM_BOOT + '/bpm/form'
|
||||
|
||||
/**
|
||||
* 查询流程表单数据
|
||||
*/
|
||||
export const listForm = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/list',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有流程表单
|
||||
*/
|
||||
export const getFormSimpleList = () => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/simpleList',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询表单详细信息
|
||||
*/
|
||||
export const getById = (id: string) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/getById?id=' + id,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增流程表单
|
||||
*/
|
||||
export const addForm = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/add',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新流程表单
|
||||
*/
|
||||
export const updateForm = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/update',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除流程表单
|
||||
*/
|
||||
export const deleteForm = (data: any) => {
|
||||
let ids = [data]
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/delete',
|
||||
method: 'POST',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
83
src/api/bpm-boot/model.ts
Normal file
83
src/api/bpm-boot/model.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import createAxios from '@/utils/request'
|
||||
|
||||
import { BPM_BOOT } from '@/utils/constantRequest'
|
||||
|
||||
const MAPPING_PATH = BPM_BOOT + '/bpm/model'
|
||||
|
||||
/**
|
||||
* 查询流程模型数据
|
||||
*/
|
||||
export const listModel = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/list',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有流程模型
|
||||
*/
|
||||
export const getModelSimpleList = () => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/simpleList',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询模型详细信息
|
||||
*/
|
||||
export const getById = (id: string) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/getById?id=' + id,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id部署模型
|
||||
*/
|
||||
export const deployModel = (id: string) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/deploy?id=' + id,
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增流程模型
|
||||
*/
|
||||
export const addModel = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/add',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新流程模型
|
||||
*/
|
||||
export const updateModel = (data: any) => {
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/update',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除流程模型
|
||||
*/
|
||||
export const deleteModel = (data: any) => {
|
||||
let ids = [data]
|
||||
return createAxios({
|
||||
url: MAPPING_PATH + '/delete',
|
||||
method: 'POST',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
42
src/api/bpm/processExpression/index.ts
Normal file
42
src/api/bpm/processExpression/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// BPM 流程表达式 VO
|
||||
export interface ProcessExpressionVO {
|
||||
id: number // 编号
|
||||
name: string // 表达式名字
|
||||
status: number // 表达式状态
|
||||
expression: string // 表达式
|
||||
}
|
||||
|
||||
// BPM 流程表达式 API
|
||||
export const ProcessExpressionApi = {
|
||||
// 查询BPM 流程表达式分页
|
||||
getProcessExpressionPage: async (params: any) => {
|
||||
return await request.get({ url: `/bpm/process-expression/page`, params })
|
||||
},
|
||||
|
||||
// 查询BPM 流程表达式详情
|
||||
getProcessExpression: async (id: number) => {
|
||||
return await request.get({ url: `/bpm/process-expression/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增BPM 流程表达式
|
||||
createProcessExpression: async (data: ProcessExpressionVO) => {
|
||||
return await request.post({ url: `/bpm/process-expression/create`, data })
|
||||
},
|
||||
|
||||
// 修改BPM 流程表达式
|
||||
updateProcessExpression: async (data: ProcessExpressionVO) => {
|
||||
return await request.put({ url: `/bpm/process-expression/update`, data })
|
||||
},
|
||||
|
||||
// 删除BPM 流程表达式
|
||||
deleteProcessExpression: async (id: number) => {
|
||||
return await request.delete({ url: `/bpm/process-expression/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出BPM 流程表达式 Excel
|
||||
exportProcessExpression: async (params) => {
|
||||
return await request.download({ url: `/bpm/process-expression/export-excel`, params })
|
||||
}
|
||||
}
|
||||
40
src/api/bpm/processListener/index.ts
Normal file
40
src/api/bpm/processListener/index.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// BPM 流程监听器 VO
|
||||
export interface ProcessListenerVO {
|
||||
id: number // 编号
|
||||
name: string // 监听器名字
|
||||
type: string // 监听器类型
|
||||
status: number // 监听器状态
|
||||
event: string // 监听事件
|
||||
valueType: string // 监听器值类型
|
||||
value: string // 监听器值
|
||||
}
|
||||
|
||||
// BPM 流程监听器 API
|
||||
export const ProcessListenerApi = {
|
||||
// 查询流程监听器分页
|
||||
getProcessListenerPage: async (params: any) => {
|
||||
return await request.get({ url: `/bpm/process-listener/page`, params })
|
||||
},
|
||||
|
||||
// 查询流程监听器详情
|
||||
getProcessListener: async (id: number) => {
|
||||
return await request.get({ url: `/bpm/process-listener/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增流程监听器
|
||||
createProcessListener: async (data: ProcessListenerVO) => {
|
||||
return await request.post({ url: `/bpm/process-listener/create`, data })
|
||||
},
|
||||
|
||||
// 修改流程监听器
|
||||
updateProcessListener: async (data: ProcessListenerVO) => {
|
||||
return await request.put({ url: `/bpm/process-listener/update`, data })
|
||||
},
|
||||
|
||||
// 删除流程监听器
|
||||
deleteProcessListener: async (id: number) => {
|
||||
return await request.delete({ url: `/bpm/process-listener/delete?id=` + id })
|
||||
}
|
||||
}
|
||||
47
src/api/bpm/userGroup/index.ts
Normal file
47
src/api/bpm/userGroup/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export type UserGroupVO = {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
userIds: number[]
|
||||
status: number
|
||||
remark: string
|
||||
createTime: string
|
||||
}
|
||||
|
||||
// 创建用户组
|
||||
export const createUserGroup = async (data: UserGroupVO) => {
|
||||
return await request.post({
|
||||
url: '/bpm/user-group/create',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新用户组
|
||||
export const updateUserGroup = async (data: UserGroupVO) => {
|
||||
return await request.put({
|
||||
url: '/bpm/user-group/update',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户组
|
||||
export const deleteUserGroup = async (id: number) => {
|
||||
return await request.delete({ url: '/bpm/user-group/delete?id=' + id })
|
||||
}
|
||||
|
||||
// 获得用户组
|
||||
export const getUserGroup = async (id: number) => {
|
||||
return await request.get({ url: '/bpm/user-group/get?id=' + id })
|
||||
}
|
||||
|
||||
// 获得用户组分页
|
||||
export const getUserGroupPage = async (params) => {
|
||||
return await request.get({ url: '/bpm/user-group/page', params })
|
||||
}
|
||||
|
||||
// 获取用户组精简信息列表
|
||||
export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
|
||||
return await request.get({ url: '/bpm/user-group/simple-list' })
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/tenapp/dbs/${url}`, ...arg)
|
||||
/**
|
||||
* 多租户
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取数据源分页
|
||||
dbsPage(data) {
|
||||
return request('storage/page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'storage/edit' : 'storage/add', data)
|
||||
},
|
||||
// 删除数据源
|
||||
dbsDelete(data) {
|
||||
return request('storage/delete', data)
|
||||
},
|
||||
// 获取数据源详情
|
||||
dbsDetail(data) {
|
||||
return request('storage/detail', data, 'get')
|
||||
},
|
||||
// 获取数据库中所有表
|
||||
dbsTables(data) {
|
||||
return request('tables', data, 'get')
|
||||
},
|
||||
// 获取数据库表中所有字段
|
||||
dbsTableColumns(data) {
|
||||
return request('tableColumns', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/model/${url}`, ...arg)
|
||||
/**
|
||||
* 模型
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取模型分页
|
||||
modelPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 获取所有模型列表
|
||||
modelAllList(data) {
|
||||
return request('allList', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除模型
|
||||
modelDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 部署模型
|
||||
modelDeploy(data) {
|
||||
return request('deploy', data)
|
||||
},
|
||||
// 获取模型详情
|
||||
modelDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 停用模型
|
||||
modelDisable(data) {
|
||||
return request('disableModel', data)
|
||||
},
|
||||
// 启用模型
|
||||
modelEnable(data) {
|
||||
return request('enableModel', data)
|
||||
},
|
||||
// 模型降版
|
||||
modelDownVersion(data) {
|
||||
return request('downVersion', data)
|
||||
},
|
||||
// 获取组织树选择器
|
||||
modelOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取组织列表选择器
|
||||
modelOrgListSelector(data) {
|
||||
return request('orgListSelector', data, 'get')
|
||||
},
|
||||
// 获取职位选择器
|
||||
modelPositionSelector(data) {
|
||||
return request('positionSelector', data, 'get')
|
||||
},
|
||||
// 获取角色选择器
|
||||
modelRoleSelector(data) {
|
||||
return request('roleSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
modelUserSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
},
|
||||
// 获取执行监听器选择器
|
||||
modelExecutionListenerSelector(data) {
|
||||
return request('executionListenerSelector', data, 'get')
|
||||
},
|
||||
// 获取自定义事件执行监听器选择器
|
||||
modelExecutionListenerSelectorForCustomEvent(data) {
|
||||
return request('executionListenerSelectorForCustomEvent', data, 'get')
|
||||
},
|
||||
// 获取任务监听器选择器
|
||||
modelTaskListenerSelector(data) {
|
||||
return request('taskListenerSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/process/monitor/${url}`, ...arg)
|
||||
/**
|
||||
* 流程
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取所有流程分页
|
||||
processMonitorPage(data) {
|
||||
return request('monitorPage', data, 'get')
|
||||
},
|
||||
// 删除流程
|
||||
processDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 终止流程
|
||||
processEnd(data) {
|
||||
return request('end', data)
|
||||
},
|
||||
// 撤回流程
|
||||
processRevoke(data) {
|
||||
return request('revoke', data)
|
||||
},
|
||||
// 挂起流程
|
||||
processSuspend(data) {
|
||||
return request('suspend', data)
|
||||
},
|
||||
// 激活流程
|
||||
processActive(data) {
|
||||
return request('active', data)
|
||||
},
|
||||
// 转办流程
|
||||
processTurn(data) {
|
||||
return request('turn', data)
|
||||
},
|
||||
// 跳转流程
|
||||
processJump(data) {
|
||||
return request('jump', data)
|
||||
},
|
||||
// 复活流程
|
||||
processRestart(data) {
|
||||
return request('restart', data)
|
||||
},
|
||||
// 迁移流程
|
||||
processMigrate(data) {
|
||||
return request('migrate', data)
|
||||
},
|
||||
// 获取流程变量分页
|
||||
processVariablePage(data) {
|
||||
return request('variablePage', data, 'get')
|
||||
},
|
||||
// 批量编辑流程变量
|
||||
processVariableUpdateBatch(data) {
|
||||
return request('variableUpdateBatch', data)
|
||||
},
|
||||
// 获取流程详情
|
||||
processDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取可跳转节点列表
|
||||
processGetCanJumpNodeInfoList(data) {
|
||||
return request('getCanJumpNodeInfoList', data, 'get')
|
||||
},
|
||||
// 获取可复活到节点列表
|
||||
processGetCanRestartNodeInfoList(data) {
|
||||
return request('getCanRestartNodeInfoList', data, 'get')
|
||||
},
|
||||
// 获取可迁移到节点列表
|
||||
processGetCanMigrateNodeInfoList(data) {
|
||||
return request('getCanMigrateNodeInfoList', data, 'get')
|
||||
},
|
||||
// 获取组织树选择器
|
||||
processOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
processUserSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/process/${url}`, ...arg)
|
||||
/**
|
||||
* 我的流程
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取我可以发起的流程模型列表
|
||||
processMyModelList(data) {
|
||||
return request('myModelList', data, 'get')
|
||||
},
|
||||
// 保存草稿
|
||||
processSaveDraft(data) {
|
||||
return request('saveDraft', data)
|
||||
},
|
||||
// 发起流程
|
||||
processStart(data) {
|
||||
return request('start', data)
|
||||
},
|
||||
// 获取我的草稿分页
|
||||
processMyDraftPage(data) {
|
||||
return request('myDraftPage', data, 'get')
|
||||
},
|
||||
// 获取草稿详情
|
||||
processDraftDetail(data) {
|
||||
return request('draftDetail', data, 'get')
|
||||
},
|
||||
// 删除草稿
|
||||
processDeleteDraft(data) {
|
||||
return request('deleteDraft', data)
|
||||
},
|
||||
// 获取我发起的流程分页
|
||||
processMyPage(data) {
|
||||
return request('myPage', data, 'get')
|
||||
},
|
||||
// 获取我的待阅流程分页
|
||||
processMyCopyUnreadPage(data) {
|
||||
return request('myCopyUnreadPage', data, 'get')
|
||||
},
|
||||
// 设置待阅流程为已阅
|
||||
processReadMyCopyProcess(data) {
|
||||
return request('readMyCopyProcess', data)
|
||||
},
|
||||
// 获取我的已阅流程分页
|
||||
processMyCopyHasReadPage(data) {
|
||||
return request('myCopyHasReadPage', data, 'get')
|
||||
},
|
||||
// 删除我的已阅流程
|
||||
processDeleteMyHasReadProcess(data) {
|
||||
return request('deleteMyHasReadProcess', data)
|
||||
},
|
||||
// 撤回流程
|
||||
processRevoke(data) {
|
||||
return request('revoke', data)
|
||||
},
|
||||
// 获取流程详情
|
||||
processDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/task/${url}`, ...arg)
|
||||
/**
|
||||
* 待办任务
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取待办任务分页
|
||||
taskTodoPage(data) {
|
||||
return request('todoPage', data, 'get')
|
||||
},
|
||||
// 获取已办任务分页
|
||||
taskDonePage(data) {
|
||||
return request('donePage', data, 'get')
|
||||
},
|
||||
// 调整申请
|
||||
taskAdjust(data) {
|
||||
return request('adjust', data)
|
||||
},
|
||||
// 审批保存
|
||||
taskSave(data) {
|
||||
return request('save', data)
|
||||
},
|
||||
// 审批同意
|
||||
taskPass(data) {
|
||||
return request('pass', data)
|
||||
},
|
||||
// 审批拒绝
|
||||
taskReject(data) {
|
||||
return request('reject', data)
|
||||
},
|
||||
// 审批退回
|
||||
taskBack(data) {
|
||||
return request('back', data)
|
||||
},
|
||||
// 任务转办
|
||||
taskTurn(data) {
|
||||
return request('turn', data)
|
||||
},
|
||||
// 审批跳转
|
||||
taskJump(data) {
|
||||
return request('jump', data)
|
||||
},
|
||||
// 任务加签
|
||||
taskAddSign(data) {
|
||||
return request('addSign', data)
|
||||
},
|
||||
// 任务详情
|
||||
taskDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取可驳回节点列表
|
||||
taskGetCanBackNodeInfoList(data) {
|
||||
return request('getCanBackNodeInfoList', data, 'get')
|
||||
},
|
||||
// 获取可跳转节点列表
|
||||
taskGetCanJumpNodeInfoList(data) {
|
||||
return request('getCanJumpNodeInfoList', data, 'get')
|
||||
},
|
||||
// 获取组织树选择器
|
||||
taskOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
taskUserSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/templatePrint/${url}`, ...arg)
|
||||
/**
|
||||
* 打印模板
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取打印模板分页
|
||||
templatePrintPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
templatePrintSubmitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除打印模板
|
||||
templatePrintDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取打印模板详情
|
||||
templatePrintDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取打印模板选择列表
|
||||
templateFlwTemplatePrintSelector(data) {
|
||||
return request('flwTemplatePrintSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/flwapp/flw/templateSn/${url}`, ...arg)
|
||||
/**
|
||||
* 流水号
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取流水号模板分页
|
||||
templateSnPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
templateSnSubmitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除流水号模板
|
||||
templateSnDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取流水号模板详情
|
||||
templateSnDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取序列号模板选择列表
|
||||
templateFlwTemplateSnSelector(data) {
|
||||
return request('flwTemplateSnSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/index/${url}`, ...arg)
|
||||
/**
|
||||
* 系统首页控制器
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 添加当前用户日程
|
||||
indexScheduleAdd(data) {
|
||||
return request('schedule/add', data)
|
||||
},
|
||||
// 删除日程
|
||||
indexScheduleDeleteSchedule(data) {
|
||||
return request('schedule/deleteSchedule', data)
|
||||
},
|
||||
// 获取当前用户日程列表
|
||||
indexScheduleList(data) {
|
||||
return request('schedule/list', data, 'get')
|
||||
},
|
||||
// 获取当前用户站内信列表
|
||||
indexMessageList(data) {
|
||||
return request('message/list', data, 'get')
|
||||
},
|
||||
// 获取站内信详情
|
||||
indexMessageDetail(data) {
|
||||
return request('message/detail', data, 'get')
|
||||
},
|
||||
//站内信全部标记已读
|
||||
indexMessageAllMarkRead(data) {
|
||||
return request('message/allMessageMarkRead', data)
|
||||
},
|
||||
// 获取当前用户访问日志列表
|
||||
indexVisLogList(data) {
|
||||
return request('visLog/list', data, 'get')
|
||||
},
|
||||
// 获取当前用户操作日志列表
|
||||
indexOpLogList(data) {
|
||||
return request('opLog/list', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/org/${url}`, ...arg)
|
||||
/**
|
||||
* 机构
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取组织分页
|
||||
orgPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 获取组织列表
|
||||
orgList(data) {
|
||||
return request('list', data, 'get')
|
||||
},
|
||||
// 获取组织树
|
||||
orgTree(data) {
|
||||
return request('tree', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除组织
|
||||
orgDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取组织详情
|
||||
orgDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取组织树选择器
|
||||
orgOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
orgUserSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/position/${url}`, ...arg)
|
||||
/**
|
||||
* 职位
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取职位分页
|
||||
positionPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 获取职位列表
|
||||
positionList(data) {
|
||||
return request('list', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除职位
|
||||
positionDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取职位详情
|
||||
positionDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取组织树选择器
|
||||
positionOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取职位选择器
|
||||
positionPositionSelector(data) {
|
||||
return request('positionSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/button/${url}`, ...arg)
|
||||
/**
|
||||
* 按钮
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取按钮分页
|
||||
buttonPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除按钮
|
||||
buttonDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取按钮详情
|
||||
buttonDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/field/${url}`, ...arg)
|
||||
/**
|
||||
* 字段
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取字段分页
|
||||
fieldPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 获取字段树
|
||||
fieldTree(data) {
|
||||
return request('tree', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除字段
|
||||
fieldDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取字段详情
|
||||
fieldDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取菜单树选择器
|
||||
fieldMenuTreeSelector(data) {
|
||||
return request('MenuTreeSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/menu/${url}`, ...arg)
|
||||
/**
|
||||
* 菜单
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取菜单树
|
||||
menuTree(data) {
|
||||
return request('tree', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 更改菜单所属模块
|
||||
menuChangeModule(data) {
|
||||
return request('changeModule', data)
|
||||
},
|
||||
// 删除菜单
|
||||
menuDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取菜单详情
|
||||
menuDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取模块选择器
|
||||
menuModuleSelector(data) {
|
||||
return request('moduleSelector', data, 'get')
|
||||
},
|
||||
// 获取菜单树选择器
|
||||
menuTreeSelector(data) {
|
||||
return request('menuTreeSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/module/${url}`, ...arg)
|
||||
/**
|
||||
* 模块
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取模块分页
|
||||
modulePage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除模块
|
||||
moduleDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取模块详情
|
||||
moduleDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/role/${url}`, ...arg)
|
||||
/**
|
||||
* 角色
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取角色分页
|
||||
rolePage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 获取角色列表
|
||||
roleList(data) {
|
||||
return request('list', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除角色
|
||||
roleDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取角色详情
|
||||
roleDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 获取角色拥有资源
|
||||
roleOwnResource(data) {
|
||||
return request('ownResource', data, 'get')
|
||||
},
|
||||
// 给角色授权资源
|
||||
roleGrantResource(data) {
|
||||
return request('grantResource', data)
|
||||
},
|
||||
// 获取角色拥有移动端菜单
|
||||
roleOwnMobileMenu(data) {
|
||||
return request('ownMobileMenu', data, 'get')
|
||||
},
|
||||
// 给角色授权移动端菜单
|
||||
roleGrantMobileMenu(data) {
|
||||
return request('grantMobileMenu', data)
|
||||
},
|
||||
// 获取角色拥有权限
|
||||
roleOwnPermission(data) {
|
||||
return request('ownPermission', data, 'get')
|
||||
},
|
||||
// 给角色授权权限
|
||||
roleGrantPermission(data) {
|
||||
return request('grantPermission', data)
|
||||
},
|
||||
// 获取角色下的用户
|
||||
roleOwnUser(data) {
|
||||
return request('ownUser', data, 'get')
|
||||
},
|
||||
// 给角色授权用户
|
||||
roleGrantUser(data) {
|
||||
return request('grantUser', data)
|
||||
},
|
||||
// 获取机构树
|
||||
roleOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取资源授权树
|
||||
roleResourceTreeSelector(data) {
|
||||
return request('resourceTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取移动端菜单授权树
|
||||
roleMobileMenuTreeSelector(data) {
|
||||
return request('mobileMenuTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取权限授权树
|
||||
rolePermissionTreeSelector(data) {
|
||||
return request('permissionTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取角色选择器
|
||||
roleRoleSelector(data) {
|
||||
return request('roleSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
roleUserSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/user/${url}`, ...arg)
|
||||
/**
|
||||
* 用户接口api
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取用户分页
|
||||
userPage(data) {
|
||||
return request('page', data, 'get')
|
||||
},
|
||||
// 提交表单 edit为true时为编辑,默认为新增
|
||||
submitForm(data, edit = false) {
|
||||
return request(edit ? 'edit' : 'add', data)
|
||||
},
|
||||
// 删除用户
|
||||
userDelete(data) {
|
||||
return request('delete', data)
|
||||
},
|
||||
// 获取用户详情
|
||||
userDetail(data) {
|
||||
return request('detail', data, 'get')
|
||||
},
|
||||
// 禁用用户
|
||||
userDisableUser(data) {
|
||||
return request('disableUser', data)
|
||||
},
|
||||
// 启用用户
|
||||
userEnableUser(data) {
|
||||
return request('enableUser', data)
|
||||
},
|
||||
// 重置用户密码
|
||||
userResetPassword(data) {
|
||||
return request('resetPassword', data)
|
||||
},
|
||||
// 获取组织选择器
|
||||
userOrgTreeSelector(data) {
|
||||
return request('orgTreeSelector', data, 'get')
|
||||
},
|
||||
// 获取职位选择器
|
||||
userPositionSelector(data) {
|
||||
return request('positionSelector', data, 'get')
|
||||
},
|
||||
// 获取角色选择器
|
||||
userRoleSelector(data) {
|
||||
return request('roleSelector', data, 'get')
|
||||
},
|
||||
// 获取用户选择器
|
||||
userSelector(data) {
|
||||
return request('userSelector', data, 'get')
|
||||
},
|
||||
// 用户拥有角色
|
||||
userOwnRole(data) {
|
||||
return request('ownRole', data, 'get')
|
||||
},
|
||||
// 给用户授权角色
|
||||
grantRole(data) {
|
||||
return request('grantRole', data)
|
||||
},
|
||||
// 获取用户拥有资源
|
||||
userOwnResource(data) {
|
||||
return request('ownResource', data, 'get')
|
||||
},
|
||||
// 给用户授权资源
|
||||
userGrantResource(data) {
|
||||
return request('grantResource', data)
|
||||
},
|
||||
// 获取用户拥有权限
|
||||
userOwnPermission(data) {
|
||||
return request('ownPermission', data, 'get')
|
||||
},
|
||||
// 给用户授权权限
|
||||
userGrantPermission(data) {
|
||||
return request('grantPermission', data)
|
||||
},
|
||||
// 下载用户导入模板
|
||||
userDownloadImportUserTemplate(data) {
|
||||
return request('downloadImportUserTemplate', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 用户导入
|
||||
userImport(data) {
|
||||
return request('import', data)
|
||||
},
|
||||
// 用户导出
|
||||
userExport(data) {
|
||||
return request('export', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
},
|
||||
// 导出用户个人信息
|
||||
userExportUserInfo(data) {
|
||||
return request('exportUserInfo', data, 'get', {
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import { baseRequest } from '@/utils/request'
|
||||
|
||||
const request = (url, ...arg) => baseRequest(`/api/webapp/sys/userCenter/${url}`, ...arg)
|
||||
/**
|
||||
* 用户个人控制器
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2022-09-22 22:33:20
|
||||
*/
|
||||
export default {
|
||||
// 获取图片验证码
|
||||
userGetPicCaptcha(data) {
|
||||
return request('getPicCaptcha', data, 'get')
|
||||
},
|
||||
// 找回密码获取手机验证码
|
||||
userFindPasswordGetPhoneValidCode(data) {
|
||||
return request('findPasswordGetPhoneValidCode', data, 'get')
|
||||
},
|
||||
// 找回密码获取邮箱验证码
|
||||
userFindPasswordGetEmailValidCode(data) {
|
||||
return request('findPasswordGetEmailValidCode', data, 'get')
|
||||
},
|
||||
// 通过手机号找回用户密码
|
||||
userFindPasswordByPhone(data) {
|
||||
return request('findPasswordByPhone', data)
|
||||
},
|
||||
// 通过邮箱找回用户密码
|
||||
userFindPasswordByEmail(data) {
|
||||
return request('findPasswordByEmail', data)
|
||||
},
|
||||
// 修改用户密码
|
||||
userUpdatePassword(data) {
|
||||
return request('updatePassword', data)
|
||||
},
|
||||
// 修改用户头像
|
||||
userUpdateAvatar(data) {
|
||||
return request('updateAvatar', data)
|
||||
},
|
||||
// 修改用户签名图片
|
||||
userUpdateSignature(data) {
|
||||
return request('updateSignature', data)
|
||||
},
|
||||
// 获取登录用户的菜单
|
||||
userLoginMenu(data) {
|
||||
return request('loginMenu', data, 'get')
|
||||
},
|
||||
// 获取登录用户组织树
|
||||
userLoginOrgTree(data) {
|
||||
return request('loginOrgTree', data, 'get')
|
||||
},
|
||||
// 获取登录用户的职位信息
|
||||
userLoginPositionInfo(data) {
|
||||
return request('loginPositionInfo', data, 'get')
|
||||
},
|
||||
// 编辑个人信息
|
||||
userUpdateUserInfo(data) {
|
||||
return request('updateUserInfo', data)
|
||||
},
|
||||
// 编辑个人工作台
|
||||
userUpdateUserWorkbench(data) {
|
||||
return request('updateUserWorkbench', data)
|
||||
},
|
||||
// 获取登录用户的工作台
|
||||
userLoginWorkbench(data) {
|
||||
return request('loginWorkbench', data, 'get')
|
||||
},
|
||||
// 获取登录用户的站内信分页
|
||||
userLoginUnreadMessagePage(data) {
|
||||
return request('loginUnreadMessagePage', data, 'get')
|
||||
},
|
||||
// 读取登录用户站内信详情
|
||||
userLoginUnreadMessageDetail(data) {
|
||||
return request('loginUnreadMessageDetail', data, 'get')
|
||||
},
|
||||
// 根据id集合获取组织集合
|
||||
userCenterGetOrgListByIdList(data) {
|
||||
return request('getOrgListByIdList', data)
|
||||
},
|
||||
// 根据id集合获取用户集合
|
||||
userCenterGetUserListByIdList(data) {
|
||||
return request('getUserListByIdList', data)
|
||||
},
|
||||
// 根据id集合获取职位集合
|
||||
userCenterGetPositionListByIdList(data) {
|
||||
return request('getPositionListByIdList', data)
|
||||
},
|
||||
// 根据id集合获取角色集合
|
||||
userCenterGetRoleListByIdList(data) {
|
||||
return request('getRoleListByIdList', data)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,15 @@ export function dictTypeList(data: any) {
|
||||
})
|
||||
}
|
||||
|
||||
export function dictTypeListAll() {
|
||||
return request({
|
||||
url: '/system-boot/dictType/listAll',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function dictTypeUpdate(data: any) {
|
||||
return request({
|
||||
url: '/system-boot/dictType/update',
|
||||
|
||||
49
src/api/system/dict/dict.data.ts
Normal file
49
src/api/system/dict/dict.data.ts
Normal 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 getSimpleDictDataList = () => {
|
||||
return request.get({ url: '/system/dict-data/simple-list' })
|
||||
}
|
||||
|
||||
// 查询字典数据列表
|
||||
export const getDictDataPage = (params: PageParam) => {
|
||||
return request.get({ url: '/system/dict-data/page', params })
|
||||
}
|
||||
|
||||
// 查询字典数据详情
|
||||
export const getDictData = (id: number) => {
|
||||
return request.get({ url: '/system/dict-data/get?id=' + id })
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export const createDictData = (data: DictDataVO) => {
|
||||
return request.post({ url: '/system/dict-data/create', data })
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export const updateDictData = (data: DictDataVO) => {
|
||||
return request.put({ url: '/system/dict-data/update', data })
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export const deleteDictData = (id: number) => {
|
||||
return request.delete({ url: '/system/dict-data/delete?id=' + id })
|
||||
}
|
||||
|
||||
// 导出字典类型数据
|
||||
export const exportDictData = (params) => {
|
||||
return request.download({ url: '/system/dict-data/export', params })
|
||||
}
|
||||
44
src/api/system/dict/dict.type.ts
Normal file
44
src/api/system/dict/dict.type.ts
Normal 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: '/system/dict-type/list-all-simple' })
|
||||
}
|
||||
|
||||
// 查询字典列表
|
||||
export const getDictTypePage = (params: PageParam) => {
|
||||
return request.get({ url: '/system/dict-type/page', params })
|
||||
}
|
||||
|
||||
// 查询字典详情
|
||||
export const getDictType = (id: number) => {
|
||||
return request.get({ url: '/system/dict-type/get?id=' + id })
|
||||
}
|
||||
|
||||
// 新增字典
|
||||
export const createDictType = (data: DictTypeVO) => {
|
||||
return request.post({ url: '/system/dict-type/create', data })
|
||||
}
|
||||
|
||||
// 修改字典
|
||||
export const updateDictType = (data: DictTypeVO) => {
|
||||
return request.put({ url: '/system/dict-type/update', data })
|
||||
}
|
||||
|
||||
// 删除字典
|
||||
export const deleteDictType = (id: number) => {
|
||||
return request.delete({ url: '/system/dict-type/delete?id=' + id })
|
||||
}
|
||||
// 导出字典类型
|
||||
export const exportDictType = (params) => {
|
||||
return request.download({ url: '/system/dict-type/export', params })
|
||||
}
|
||||
49
src/api/system/notify/message/index.ts
Normal file
49
src/api/system/notify/message/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
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
|
||||
createTime: Date
|
||||
}
|
||||
|
||||
// 查询站内信消息列表
|
||||
export const getNotifyMessagePage = async (params: PageParam) => {
|
||||
return await request.get({ url: '/system/notify-message/page', params })
|
||||
}
|
||||
|
||||
// 获得我的站内信分页
|
||||
export const getMyNotifyMessagePage = async (params: PageParam) => {
|
||||
return await request.get({ url: '/system/notify-message/my-page', params })
|
||||
}
|
||||
|
||||
// 批量标记已读
|
||||
export const updateNotifyMessageRead = async (ids) => {
|
||||
return await request.put({
|
||||
url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false })
|
||||
})
|
||||
}
|
||||
|
||||
// 标记所有站内信为已读
|
||||
export const updateAllNotifyMessageRead = async () => {
|
||||
return await request.put({ url: '/system/notify-message/update-all-read' })
|
||||
}
|
||||
|
||||
// 获取当前用户的最新站内信列表
|
||||
export const getUnreadNotifyMessageList = async () => {
|
||||
return await request.get({ url: '/system/notify-message/get-unread-list' })
|
||||
}
|
||||
|
||||
// 获得当前用户的未读站内信数量
|
||||
export const getUnreadNotifyMessageCount = async () => {
|
||||
return await request.get({ url: '/system/notify-message/get-unread-count' })
|
||||
}
|
||||
49
src/api/system/notify/template/index.ts
Normal file
49
src/api/system/notify/template/index.ts
Normal 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 })
|
||||
}
|
||||
@@ -51,4 +51,14 @@ export function getRoleListByIds(data:any) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*/
|
||||
export const getRoleSimpleList = () => {
|
||||
return createAxios({
|
||||
url: '/user-boot/role/simpleList',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -171,5 +171,16 @@ export function getUserListByIds(data:any) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有用户
|
||||
*/
|
||||
export const getUserSimpleList = () => {
|
||||
return request({
|
||||
url: '/user-boot/user/simpleList',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "snowy"; /* Project id 3880534 */
|
||||
src: url('iconfont.ttf?t=1675528061732') format('truetype');
|
||||
}
|
||||
|
||||
.snowy {
|
||||
font-family: "snowy" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.arrow-up-filling:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.arrow-down-filling:before {
|
||||
content: "\e689";
|
||||
}
|
||||
|
||||
.arrow-left-filling:before {
|
||||
content: "\e68a";
|
||||
}
|
||||
|
||||
.arrow-right-filling:before {
|
||||
content: "\e68b";
|
||||
}
|
||||
|
||||
.caps-unlock-filling:before {
|
||||
content: "\e68c";
|
||||
}
|
||||
|
||||
.comment-filling:before {
|
||||
content: "\e68d";
|
||||
}
|
||||
|
||||
.check-item-filling:before {
|
||||
content: "\e68e";
|
||||
}
|
||||
|
||||
.clock-filling:before {
|
||||
content: "\e68f";
|
||||
}
|
||||
|
||||
.delete-filling:before {
|
||||
content: "\e690";
|
||||
}
|
||||
|
||||
.decline-filling:before {
|
||||
content: "\e691";
|
||||
}
|
||||
|
||||
.dynamic-filling:before {
|
||||
content: "\e692";
|
||||
}
|
||||
|
||||
.intermediate-filling:before {
|
||||
content: "\e693";
|
||||
}
|
||||
|
||||
.favorite-filling:before {
|
||||
content: "\e694";
|
||||
}
|
||||
|
||||
.layout-filling:before {
|
||||
content: "\e695";
|
||||
}
|
||||
|
||||
.help-filling:before {
|
||||
content: "\e696";
|
||||
}
|
||||
|
||||
.history-filling:before {
|
||||
content: "\e697";
|
||||
}
|
||||
|
||||
.filter-filling:before {
|
||||
content: "\e698";
|
||||
}
|
||||
|
||||
.file-common-filling:before {
|
||||
content: "\e699";
|
||||
}
|
||||
|
||||
.news-filling:before {
|
||||
content: "\e69a";
|
||||
}
|
||||
|
||||
.edit-filling:before {
|
||||
content: "\e69b";
|
||||
}
|
||||
|
||||
.fullscreen-expand-filling:before {
|
||||
content: "\e69c";
|
||||
}
|
||||
|
||||
.smile-filling:before {
|
||||
content: "\e69d";
|
||||
}
|
||||
|
||||
.rise-filling:before {
|
||||
content: "\e69e";
|
||||
}
|
||||
|
||||
.picture-filling:before {
|
||||
content: "\e69f";
|
||||
}
|
||||
|
||||
.notification-filling:before {
|
||||
content: "\e6a0";
|
||||
}
|
||||
|
||||
.user-filling:before {
|
||||
content: "\e6a1";
|
||||
}
|
||||
|
||||
.setting-filling:before {
|
||||
content: "\e6a2";
|
||||
}
|
||||
|
||||
.switch-filling:before {
|
||||
content: "\e6a3";
|
||||
}
|
||||
|
||||
.work-filling:before {
|
||||
content: "\e6a4";
|
||||
}
|
||||
|
||||
.task-filling:before {
|
||||
content: "\e6a5";
|
||||
}
|
||||
|
||||
.success-filling:before {
|
||||
content: "\e6a6";
|
||||
}
|
||||
|
||||
.warning-filling:before {
|
||||
content: "\e6a7";
|
||||
}
|
||||
|
||||
.folder-filling:before {
|
||||
content: "\e6a8";
|
||||
}
|
||||
|
||||
.map-filling:before {
|
||||
content: "\e6a9";
|
||||
}
|
||||
|
||||
.prompt-filling:before {
|
||||
content: "\e6aa";
|
||||
}
|
||||
|
||||
.meh-filling:before {
|
||||
content: "\e6ab";
|
||||
}
|
||||
|
||||
.cry-filling:before {
|
||||
content: "\e6ac";
|
||||
}
|
||||
|
||||
.top-filling:before {
|
||||
content: "\e6ad";
|
||||
}
|
||||
|
||||
.home-filling:before {
|
||||
content: "\e6ae";
|
||||
}
|
||||
|
||||
.sorting:before {
|
||||
content: "\e6af";
|
||||
}
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
{
|
||||
"id": "3880534",
|
||||
"name": "snowy-app-filled",
|
||||
"font_family": "snowy",
|
||||
"css_prefix_text": "",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "15838581",
|
||||
"name": "arrow-up-filling",
|
||||
"font_class": "arrow-up-filling",
|
||||
"unicode": "e688",
|
||||
"unicode_decimal": 59016
|
||||
},
|
||||
{
|
||||
"icon_id": "15838582",
|
||||
"name": "arrow-down-filling",
|
||||
"font_class": "arrow-down-filling",
|
||||
"unicode": "e689",
|
||||
"unicode_decimal": 59017
|
||||
},
|
||||
{
|
||||
"icon_id": "15838583",
|
||||
"name": "arrow-left-filling",
|
||||
"font_class": "arrow-left-filling",
|
||||
"unicode": "e68a",
|
||||
"unicode_decimal": 59018
|
||||
},
|
||||
{
|
||||
"icon_id": "15838584",
|
||||
"name": "arrow-right-filling",
|
||||
"font_class": "arrow-right-filling",
|
||||
"unicode": "e68b",
|
||||
"unicode_decimal": 59019
|
||||
},
|
||||
{
|
||||
"icon_id": "15838585",
|
||||
"name": "caps-unlock-filling",
|
||||
"font_class": "caps-unlock-filling",
|
||||
"unicode": "e68c",
|
||||
"unicode_decimal": 59020
|
||||
},
|
||||
{
|
||||
"icon_id": "15838586",
|
||||
"name": "comment-filling",
|
||||
"font_class": "comment-filling",
|
||||
"unicode": "e68d",
|
||||
"unicode_decimal": 59021
|
||||
},
|
||||
{
|
||||
"icon_id": "15838587",
|
||||
"name": "check-item-filling",
|
||||
"font_class": "check-item-filling",
|
||||
"unicode": "e68e",
|
||||
"unicode_decimal": 59022
|
||||
},
|
||||
{
|
||||
"icon_id": "15838588",
|
||||
"name": "clock-filling",
|
||||
"font_class": "clock-filling",
|
||||
"unicode": "e68f",
|
||||
"unicode_decimal": 59023
|
||||
},
|
||||
{
|
||||
"icon_id": "15838589",
|
||||
"name": "delete-filling",
|
||||
"font_class": "delete-filling",
|
||||
"unicode": "e690",
|
||||
"unicode_decimal": 59024
|
||||
},
|
||||
{
|
||||
"icon_id": "15838590",
|
||||
"name": "decline-filling",
|
||||
"font_class": "decline-filling",
|
||||
"unicode": "e691",
|
||||
"unicode_decimal": 59025
|
||||
},
|
||||
{
|
||||
"icon_id": "15838591",
|
||||
"name": "dynamic-filling",
|
||||
"font_class": "dynamic-filling",
|
||||
"unicode": "e692",
|
||||
"unicode_decimal": 59026
|
||||
},
|
||||
{
|
||||
"icon_id": "15838592",
|
||||
"name": "intermediate-filling",
|
||||
"font_class": "intermediate-filling",
|
||||
"unicode": "e693",
|
||||
"unicode_decimal": 59027
|
||||
},
|
||||
{
|
||||
"icon_id": "15838593",
|
||||
"name": "favorite-filling",
|
||||
"font_class": "favorite-filling",
|
||||
"unicode": "e694",
|
||||
"unicode_decimal": 59028
|
||||
},
|
||||
{
|
||||
"icon_id": "15838594",
|
||||
"name": "layout-filling",
|
||||
"font_class": "layout-filling",
|
||||
"unicode": "e695",
|
||||
"unicode_decimal": 59029
|
||||
},
|
||||
{
|
||||
"icon_id": "15838595",
|
||||
"name": "help-filling",
|
||||
"font_class": "help-filling",
|
||||
"unicode": "e696",
|
||||
"unicode_decimal": 59030
|
||||
},
|
||||
{
|
||||
"icon_id": "15838596",
|
||||
"name": "history-filling",
|
||||
"font_class": "history-filling",
|
||||
"unicode": "e697",
|
||||
"unicode_decimal": 59031
|
||||
},
|
||||
{
|
||||
"icon_id": "15838597",
|
||||
"name": "filter-filling",
|
||||
"font_class": "filter-filling",
|
||||
"unicode": "e698",
|
||||
"unicode_decimal": 59032
|
||||
},
|
||||
{
|
||||
"icon_id": "15838598",
|
||||
"name": "file-common-filling",
|
||||
"font_class": "file-common-filling",
|
||||
"unicode": "e699",
|
||||
"unicode_decimal": 59033
|
||||
},
|
||||
{
|
||||
"icon_id": "15838599",
|
||||
"name": "news-filling",
|
||||
"font_class": "news-filling",
|
||||
"unicode": "e69a",
|
||||
"unicode_decimal": 59034
|
||||
},
|
||||
{
|
||||
"icon_id": "15838600",
|
||||
"name": "edit-filling",
|
||||
"font_class": "edit-filling",
|
||||
"unicode": "e69b",
|
||||
"unicode_decimal": 59035
|
||||
},
|
||||
{
|
||||
"icon_id": "15838601",
|
||||
"name": "fullscreen-expand-filling",
|
||||
"font_class": "fullscreen-expand-filling",
|
||||
"unicode": "e69c",
|
||||
"unicode_decimal": 59036
|
||||
},
|
||||
{
|
||||
"icon_id": "15838602",
|
||||
"name": "smile-filling",
|
||||
"font_class": "smile-filling",
|
||||
"unicode": "e69d",
|
||||
"unicode_decimal": 59037
|
||||
},
|
||||
{
|
||||
"icon_id": "15838603",
|
||||
"name": "rise-filling",
|
||||
"font_class": "rise-filling",
|
||||
"unicode": "e69e",
|
||||
"unicode_decimal": 59038
|
||||
},
|
||||
{
|
||||
"icon_id": "15838604",
|
||||
"name": "picture-filling",
|
||||
"font_class": "picture-filling",
|
||||
"unicode": "e69f",
|
||||
"unicode_decimal": 59039
|
||||
},
|
||||
{
|
||||
"icon_id": "15838605",
|
||||
"name": "notification-filling",
|
||||
"font_class": "notification-filling",
|
||||
"unicode": "e6a0",
|
||||
"unicode_decimal": 59040
|
||||
},
|
||||
{
|
||||
"icon_id": "15838606",
|
||||
"name": "user-filling",
|
||||
"font_class": "user-filling",
|
||||
"unicode": "e6a1",
|
||||
"unicode_decimal": 59041
|
||||
},
|
||||
{
|
||||
"icon_id": "15838607",
|
||||
"name": "setting-filling",
|
||||
"font_class": "setting-filling",
|
||||
"unicode": "e6a2",
|
||||
"unicode_decimal": 59042
|
||||
},
|
||||
{
|
||||
"icon_id": "15838608",
|
||||
"name": "switch-filling",
|
||||
"font_class": "switch-filling",
|
||||
"unicode": "e6a3",
|
||||
"unicode_decimal": 59043
|
||||
},
|
||||
{
|
||||
"icon_id": "15838609",
|
||||
"name": "work-filling",
|
||||
"font_class": "work-filling",
|
||||
"unicode": "e6a4",
|
||||
"unicode_decimal": 59044
|
||||
},
|
||||
{
|
||||
"icon_id": "15838610",
|
||||
"name": "task-filling",
|
||||
"font_class": "task-filling",
|
||||
"unicode": "e6a5",
|
||||
"unicode_decimal": 59045
|
||||
},
|
||||
{
|
||||
"icon_id": "15838611",
|
||||
"name": "success-filling",
|
||||
"font_class": "success-filling",
|
||||
"unicode": "e6a6",
|
||||
"unicode_decimal": 59046
|
||||
},
|
||||
{
|
||||
"icon_id": "15838612",
|
||||
"name": "warning-filling",
|
||||
"font_class": "warning-filling",
|
||||
"unicode": "e6a7",
|
||||
"unicode_decimal": 59047
|
||||
},
|
||||
{
|
||||
"icon_id": "15838613",
|
||||
"name": "folder-filling",
|
||||
"font_class": "folder-filling",
|
||||
"unicode": "e6a8",
|
||||
"unicode_decimal": 59048
|
||||
},
|
||||
{
|
||||
"icon_id": "15838614",
|
||||
"name": "map-filling",
|
||||
"font_class": "map-filling",
|
||||
"unicode": "e6a9",
|
||||
"unicode_decimal": 59049
|
||||
},
|
||||
{
|
||||
"icon_id": "15838615",
|
||||
"name": "prompt-filling",
|
||||
"font_class": "prompt-filling",
|
||||
"unicode": "e6aa",
|
||||
"unicode_decimal": 59050
|
||||
},
|
||||
{
|
||||
"icon_id": "15838616",
|
||||
"name": "meh-filling",
|
||||
"font_class": "meh-filling",
|
||||
"unicode": "e6ab",
|
||||
"unicode_decimal": 59051
|
||||
},
|
||||
{
|
||||
"icon_id": "15838617",
|
||||
"name": "cry-filling",
|
||||
"font_class": "cry-filling",
|
||||
"unicode": "e6ac",
|
||||
"unicode_decimal": 59052
|
||||
},
|
||||
{
|
||||
"icon_id": "15838618",
|
||||
"name": "top-filling",
|
||||
"font_class": "top-filling",
|
||||
"unicode": "e6ad",
|
||||
"unicode_decimal": 59053
|
||||
},
|
||||
{
|
||||
"icon_id": "15838619",
|
||||
"name": "home-filling",
|
||||
"font_class": "home-filling",
|
||||
"unicode": "e6ae",
|
||||
"unicode_decimal": 59054
|
||||
},
|
||||
{
|
||||
"icon_id": "15838620",
|
||||
"name": "sorting",
|
||||
"font_class": "sorting",
|
||||
"unicode": "e6af",
|
||||
"unicode_decimal": 59055
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
import './line/iconfont.css'
|
||||
import lineJsonData from './line/iconfont.json'
|
||||
|
||||
import './filled/iconfont.css'
|
||||
import filledJsonData from './filled/iconfont.json'
|
||||
|
||||
export default {
|
||||
icons: [
|
||||
{
|
||||
name: '基础',
|
||||
key: 'default',
|
||||
iconItem: [
|
||||
{
|
||||
name: '线框风格',
|
||||
key: 'default',
|
||||
item: lineJsonData.glyphs
|
||||
},
|
||||
{
|
||||
name: '实底风格',
|
||||
key: 'filled',
|
||||
item: filledJsonData.glyphs
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,825 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "snowy"; /* Project id 3791763 */
|
||||
src: url('iconfont.ttf?t=1675526220710') format('truetype');
|
||||
}
|
||||
|
||||
.snowy {
|
||||
font-family: "snowy" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.export-outlined:before {
|
||||
content: "\e792";
|
||||
}
|
||||
|
||||
.experiment-outlined:before {
|
||||
content: "\e7c9";
|
||||
}
|
||||
|
||||
.expand-outlined:before {
|
||||
content: "\e915";
|
||||
}
|
||||
|
||||
.expand-alt-outlined:before {
|
||||
content: "\e7e9";
|
||||
}
|
||||
|
||||
.exception-outlined:before {
|
||||
content: "\e7bb";
|
||||
}
|
||||
|
||||
.euro-outlined:before {
|
||||
content: "\e78f";
|
||||
}
|
||||
|
||||
.euro-circle-outlined:before {
|
||||
content: "\eb62";
|
||||
}
|
||||
|
||||
.environment-outlined:before {
|
||||
content: "\e790";
|
||||
}
|
||||
|
||||
.ellipsis-outlined:before {
|
||||
content: "\e815";
|
||||
}
|
||||
|
||||
.download-outlined:before {
|
||||
content: "\e814";
|
||||
}
|
||||
|
||||
.dollar-outlined:before {
|
||||
content: "\e78d";
|
||||
}
|
||||
|
||||
.dollar-circle-outlined:before {
|
||||
content: "\eb61";
|
||||
}
|
||||
|
||||
.dislike-outlined:before {
|
||||
content: "\e7c8";
|
||||
}
|
||||
|
||||
.disconnect-outlined:before {
|
||||
content: "\e7e8";
|
||||
}
|
||||
|
||||
.dingtalk-outlined:before {
|
||||
content: "\e881";
|
||||
}
|
||||
|
||||
.desktop-outlined:before {
|
||||
content: "\e845";
|
||||
}
|
||||
|
||||
.deployment-unit-outlined:before {
|
||||
content: "\e7d2";
|
||||
}
|
||||
|
||||
.delivered-procedure-outlined:before {
|
||||
content: "\e911";
|
||||
}
|
||||
|
||||
.delete-column-outlined:before {
|
||||
content: "\e901";
|
||||
}
|
||||
|
||||
.delete-row-outlined:before {
|
||||
content: "\e902";
|
||||
}
|
||||
|
||||
.database-outlined:before {
|
||||
content: "\e7b9";
|
||||
}
|
||||
|
||||
.dashboard-outlined:before {
|
||||
content: "\e78b";
|
||||
}
|
||||
|
||||
.customer-service-outlined:before {
|
||||
content: "\e7ca";
|
||||
}
|
||||
|
||||
.crown-outlined:before {
|
||||
content: "\e844";
|
||||
}
|
||||
|
||||
.credit-card-outlined:before {
|
||||
content: "\e7e5";
|
||||
}
|
||||
|
||||
.copyright-outlined:before {
|
||||
content: "\e789";
|
||||
}
|
||||
|
||||
.copyright-circle-outlined:before {
|
||||
content: "\eb60";
|
||||
}
|
||||
|
||||
.control-outlined:before {
|
||||
content: "\e79c";
|
||||
}
|
||||
|
||||
.container-outlined:before {
|
||||
content: "\e7b8";
|
||||
}
|
||||
|
||||
.contacts-outlined:before {
|
||||
content: "\e7e4";
|
||||
}
|
||||
|
||||
.console-sql-outlined:before {
|
||||
content: "\e910";
|
||||
}
|
||||
|
||||
.compress-outlined:before {
|
||||
content: "\e914";
|
||||
}
|
||||
|
||||
.compass-outlined:before {
|
||||
content: "\e786";
|
||||
}
|
||||
|
||||
.comment-outlined:before {
|
||||
content: "\e8ea";
|
||||
}
|
||||
|
||||
.coffee-outlined:before {
|
||||
content: "\e6b5";
|
||||
}
|
||||
|
||||
.code-outlined:before {
|
||||
content: "\e79b";
|
||||
}
|
||||
|
||||
.cloud-server-outlined:before {
|
||||
content: "\e7db";
|
||||
}
|
||||
|
||||
.cloud-upload-outlined:before {
|
||||
content: "\e7dc";
|
||||
}
|
||||
|
||||
.cloud-outlined:before {
|
||||
content: "\e7dd";
|
||||
}
|
||||
|
||||
.cloud-download-outlined:before {
|
||||
content: "\e7de";
|
||||
}
|
||||
|
||||
.cloud-sync-outlined:before {
|
||||
content: "\e7e0";
|
||||
}
|
||||
|
||||
.clear-outlined:before {
|
||||
content: "\e900";
|
||||
}
|
||||
|
||||
.ci-circle-outlined:before {
|
||||
content: "\e77f";
|
||||
}
|
||||
|
||||
.carry-out-outlined:before {
|
||||
content: "\e7d6";
|
||||
}
|
||||
|
||||
.car-outlined:before {
|
||||
content: "\e7da";
|
||||
}
|
||||
|
||||
.ci-outlined:before {
|
||||
content: "\eb5f";
|
||||
}
|
||||
|
||||
.camera-outlined:before {
|
||||
content: "\e7d9";
|
||||
}
|
||||
|
||||
.calendar-outlined:before {
|
||||
content: "\e7d4";
|
||||
}
|
||||
|
||||
.calculator-outlined:before {
|
||||
content: "\e79a";
|
||||
}
|
||||
|
||||
.bulb-outlined:before {
|
||||
content: "\e7c7";
|
||||
}
|
||||
|
||||
.build-outlined:before {
|
||||
content: "\e7d5";
|
||||
}
|
||||
|
||||
.bug-outlined:before {
|
||||
content: "\e8e9";
|
||||
}
|
||||
|
||||
.branches-outlined:before {
|
||||
content: "\e7e7";
|
||||
}
|
||||
|
||||
.borderless-table-outlined:before {
|
||||
content: "\e813";
|
||||
}
|
||||
|
||||
.border-outlined:before {
|
||||
content: "\e7b7";
|
||||
}
|
||||
|
||||
.book-outlined:before {
|
||||
content: "\e7b6";
|
||||
}
|
||||
|
||||
.block-outlined:before {
|
||||
content: "\e7df";
|
||||
}
|
||||
|
||||
.bell-outlined:before {
|
||||
content: "\e7c5";
|
||||
}
|
||||
|
||||
.bars-outlined:before {
|
||||
content: "\e71a";
|
||||
}
|
||||
|
||||
.barcode-outlined:before {
|
||||
content: "\e7d8";
|
||||
}
|
||||
|
||||
.bank-outlined:before {
|
||||
content: "\e7c6";
|
||||
}
|
||||
|
||||
.audit-outlined:before {
|
||||
content: "\e7c0";
|
||||
}
|
||||
|
||||
.audio-outlined:before {
|
||||
content: "\e89b";
|
||||
}
|
||||
|
||||
.audio-muted-outlined:before {
|
||||
content: "\e8e8";
|
||||
}
|
||||
|
||||
.api-outlined:before {
|
||||
content: "\e7e3";
|
||||
}
|
||||
|
||||
.apartment-outlined:before {
|
||||
content: "\e89a";
|
||||
}
|
||||
|
||||
.alert-outlined:before {
|
||||
content: "\e7c4";
|
||||
}
|
||||
|
||||
.aim-outlined:before {
|
||||
content: "\e913";
|
||||
}
|
||||
|
||||
.account-book-outlined:before {
|
||||
content: "\e7d3";
|
||||
}
|
||||
|
||||
.column-height-outlined:before {
|
||||
content: "\e811";
|
||||
}
|
||||
|
||||
.column-width-outlined:before {
|
||||
content: "\e812";
|
||||
}
|
||||
|
||||
.radius-setting-outlined:before {
|
||||
content: "\e7b5";
|
||||
}
|
||||
|
||||
.unordered-list-outlined:before {
|
||||
content: "\e80f";
|
||||
}
|
||||
|
||||
.ordered-list-outlined:before {
|
||||
content: "\e810";
|
||||
}
|
||||
|
||||
.drag-outlined:before {
|
||||
content: "\e843";
|
||||
}
|
||||
|
||||
.sort-descending-outlined:before {
|
||||
content: "\e80d";
|
||||
}
|
||||
|
||||
.sort-ascending-outlined:before {
|
||||
content: "\e80e";
|
||||
}
|
||||
|
||||
.font-colors-outlined:before {
|
||||
content: "\e808";
|
||||
}
|
||||
|
||||
.font-size-outlined:before {
|
||||
content: "\e809";
|
||||
}
|
||||
|
||||
.line-height-outlined:before {
|
||||
content: "\e80a";
|
||||
}
|
||||
|
||||
.dash-outlined:before {
|
||||
content: "\e80b";
|
||||
}
|
||||
|
||||
.small-dash-outlined:before {
|
||||
content: "\e80c";
|
||||
}
|
||||
|
||||
.zoom-out-outlined:before {
|
||||
content: "\e898";
|
||||
}
|
||||
|
||||
.zoom-in-outlined:before {
|
||||
content: "\e899";
|
||||
}
|
||||
|
||||
.undo-outlined:before {
|
||||
content: "\e787";
|
||||
}
|
||||
|
||||
.redo-outlined:before {
|
||||
content: "\e788";
|
||||
}
|
||||
|
||||
.bold-outlined:before {
|
||||
content: "\e804";
|
||||
}
|
||||
|
||||
.strikethrough-outlined:before {
|
||||
content: "\e805";
|
||||
}
|
||||
|
||||
.underline-outlined:before {
|
||||
content: "\e806";
|
||||
}
|
||||
|
||||
.italic-outlined:before {
|
||||
content: "\e807";
|
||||
}
|
||||
|
||||
.bg-colors-outlined:before {
|
||||
content: "\e803";
|
||||
}
|
||||
|
||||
.align-right-outlined:before {
|
||||
content: "\e7fb";
|
||||
}
|
||||
|
||||
.align-left-outlined:before {
|
||||
content: "\e802";
|
||||
}
|
||||
|
||||
.align-center-outlined:before {
|
||||
content: "\e7f5";
|
||||
}
|
||||
|
||||
.highlight-outlined:before {
|
||||
content: "\e7e2";
|
||||
}
|
||||
|
||||
.diff-outlined:before {
|
||||
content: "\e7bf";
|
||||
}
|
||||
|
||||
.snippets-outlined:before {
|
||||
content: "\e7bd";
|
||||
}
|
||||
|
||||
.delete-outlined:before {
|
||||
content: "\e7c3";
|
||||
}
|
||||
|
||||
.scissor-outlined:before {
|
||||
content: "\e7e6";
|
||||
}
|
||||
|
||||
.copy-outlined:before {
|
||||
content: "\e7bc";
|
||||
}
|
||||
|
||||
.form-outlined:before {
|
||||
content: "\e791";
|
||||
}
|
||||
|
||||
.edit-outlined:before {
|
||||
content: "\e7e1";
|
||||
}
|
||||
|
||||
.stop-outlined:before {
|
||||
content: "\e842";
|
||||
}
|
||||
|
||||
.issues-close-outlined:before {
|
||||
content: "\e68e";
|
||||
}
|
||||
|
||||
.warning-outlined:before {
|
||||
content: "\e682";
|
||||
}
|
||||
|
||||
.clock-circle-outlined:before {
|
||||
content: "\e784";
|
||||
}
|
||||
|
||||
.check-circle-outlined:before {
|
||||
content: "\e77d";
|
||||
}
|
||||
|
||||
.check-square-outlined:before {
|
||||
content: "\e794";
|
||||
}
|
||||
|
||||
.check-outlined:before {
|
||||
content: "\e7fc";
|
||||
}
|
||||
|
||||
.exclamation-circle-outlined:before {
|
||||
content: "\e785";
|
||||
}
|
||||
|
||||
.exclamation-outlined:before {
|
||||
content: "\e7fa";
|
||||
}
|
||||
|
||||
.info-circle-outlined:before {
|
||||
content: "\e77e";
|
||||
}
|
||||
|
||||
.info-outlined:before {
|
||||
content: "\e7f9";
|
||||
}
|
||||
|
||||
.minus-square-outlined:before {
|
||||
content: "\e796";
|
||||
}
|
||||
|
||||
.plus-square-outlined:before {
|
||||
content: "\e797";
|
||||
}
|
||||
|
||||
.minus-circle-outlined:before {
|
||||
content: "\e780";
|
||||
}
|
||||
|
||||
.minus-outlined:before {
|
||||
content: "\e801";
|
||||
}
|
||||
|
||||
.pause-circle-outlined:before {
|
||||
content: "\e783";
|
||||
}
|
||||
|
||||
.pause-outlined:before {
|
||||
content: "\e800";
|
||||
}
|
||||
|
||||
.plus-circle-outlined:before {
|
||||
content: "\e781";
|
||||
}
|
||||
|
||||
.plus-outlined:before {
|
||||
content: "\e8fe";
|
||||
}
|
||||
|
||||
.question-circle-outlined:before {
|
||||
content: "\e782";
|
||||
}
|
||||
|
||||
.question-outlined:before {
|
||||
content: "\e7ff";
|
||||
}
|
||||
|
||||
.fullscreen-outlined:before {
|
||||
content: "\e7ec";
|
||||
}
|
||||
|
||||
.fullscreen-exit-outlined:before {
|
||||
content: "\e7ed";
|
||||
}
|
||||
|
||||
.radius-bottomleft-outlined:before {
|
||||
content: "\e7b1";
|
||||
}
|
||||
|
||||
.radius-bottomright-outlined:before {
|
||||
content: "\e7b2";
|
||||
}
|
||||
|
||||
.radius-upleft-outlined:before {
|
||||
content: "\e7b3";
|
||||
}
|
||||
|
||||
.radius-upright-outlined:before {
|
||||
content: "\e7b4";
|
||||
}
|
||||
|
||||
.pic-center-outlined:before {
|
||||
content: "\e7f6";
|
||||
}
|
||||
|
||||
.pic-right-outlined:before {
|
||||
content: "\e7f7";
|
||||
}
|
||||
|
||||
.pic-left-outlined:before {
|
||||
content: "\e7f8";
|
||||
}
|
||||
|
||||
.border-outer-outlined:before {
|
||||
content: "\e7a9";
|
||||
}
|
||||
|
||||
.border-top-outlined:before {
|
||||
content: "\e7aa";
|
||||
}
|
||||
|
||||
.border-bottom-outlined:before {
|
||||
content: "\e7ab";
|
||||
}
|
||||
|
||||
.border-left-outlined:before {
|
||||
content: "\e7ac";
|
||||
}
|
||||
|
||||
.border-right-outlined:before {
|
||||
content: "\e7ad";
|
||||
}
|
||||
|
||||
.border-inner-outlined:before {
|
||||
content: "\e7ae";
|
||||
}
|
||||
|
||||
.border-verticle-outlined:before {
|
||||
content: "\e7af";
|
||||
}
|
||||
|
||||
.border-horizontal-outlined:before {
|
||||
content: "\e7b0";
|
||||
}
|
||||
|
||||
.menu-unfold-outlined:before {
|
||||
content: "\e7f3";
|
||||
}
|
||||
|
||||
.menu-fold-outlined:before {
|
||||
content: "\e7f4";
|
||||
}
|
||||
|
||||
.logout-outlined:before {
|
||||
content: "\e78c";
|
||||
}
|
||||
|
||||
.login-outlined:before {
|
||||
content: "\e8f4";
|
||||
}
|
||||
|
||||
.cluster-outlined:before {
|
||||
content: "\e7d7";
|
||||
}
|
||||
|
||||
.down-square-outlined:before {
|
||||
content: "\e793";
|
||||
}
|
||||
|
||||
.left-square-outlined:before {
|
||||
content: "\e795";
|
||||
}
|
||||
|
||||
.right-square-outlined:before {
|
||||
content: "\e798";
|
||||
}
|
||||
|
||||
.up-Square-outlined:before {
|
||||
content: "\e799";
|
||||
}
|
||||
|
||||
.play-circle-outlined:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.arrow-down-outlined:before {
|
||||
content: "\e66d";
|
||||
}
|
||||
|
||||
.arrow-right-outlined:before {
|
||||
content: "\e66e";
|
||||
}
|
||||
|
||||
.arrow-up-outlined:before {
|
||||
content: "\e66f";
|
||||
}
|
||||
|
||||
.arrow-left-outlined:before {
|
||||
content: "\e670";
|
||||
}
|
||||
|
||||
.swap-outlined:before {
|
||||
content: "\e7f2";
|
||||
}
|
||||
|
||||
.swap-right-outlined:before {
|
||||
content: "\e8f2";
|
||||
}
|
||||
|
||||
.swap-left-outlined:before {
|
||||
content: "\e8f3";
|
||||
}
|
||||
|
||||
.enter-outlined:before {
|
||||
content: "\e7fd";
|
||||
}
|
||||
|
||||
.rollback-outlined:before {
|
||||
content: "\e7fe";
|
||||
}
|
||||
|
||||
.retweet-outlined:before {
|
||||
content: "\e8f1";
|
||||
}
|
||||
|
||||
.fast-backward-outlined:before {
|
||||
content: "\e8ed";
|
||||
}
|
||||
|
||||
.fast-forward-outlined:before {
|
||||
content: "\e8ee";
|
||||
}
|
||||
|
||||
.vertical-align-bottom-outlined:before {
|
||||
content: "\e7ef";
|
||||
}
|
||||
|
||||
.vertical-align-middle-outlined:before {
|
||||
content: "\e7f0";
|
||||
}
|
||||
|
||||
.vertical-align-top-outlined:before {
|
||||
content: "\e7f1";
|
||||
}
|
||||
|
||||
.vertical-right-outlined:before {
|
||||
content: "\e7ea";
|
||||
}
|
||||
|
||||
.vertical-left-outlined:before {
|
||||
content: "\e7eb";
|
||||
}
|
||||
|
||||
.double-left-outlined:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.double-right-outlined:before {
|
||||
content: "\e66c";
|
||||
}
|
||||
|
||||
.up-circle-outlined:before {
|
||||
content: "\e666";
|
||||
}
|
||||
|
||||
.right-circle-outlined:before {
|
||||
content: "\e667";
|
||||
}
|
||||
|
||||
.left-circle-outlined:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.down-circle-outlined:before {
|
||||
content: "\eb5e";
|
||||
}
|
||||
|
||||
.caret-up-outlined:before {
|
||||
content: "\e689";
|
||||
}
|
||||
|
||||
.caret-down-outlined:before {
|
||||
content: "\e68a";
|
||||
}
|
||||
|
||||
.caret-left-outlined:before {
|
||||
content: "\e68b";
|
||||
}
|
||||
|
||||
.caret-right-outlined:before {
|
||||
content: "\e68c";
|
||||
}
|
||||
|
||||
.left-outlined:before {
|
||||
content: "\e685";
|
||||
}
|
||||
|
||||
.up-outlined:before {
|
||||
content: "\e686";
|
||||
}
|
||||
|
||||
.down-outlined:before {
|
||||
content: "\e687";
|
||||
}
|
||||
|
||||
.right-outlined:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.arrows-alt-outlined:before {
|
||||
content: "\e665";
|
||||
}
|
||||
|
||||
.shrink-outlined:before {
|
||||
content: "\e68d";
|
||||
}
|
||||
|
||||
.step-backward-outlined:before {
|
||||
content: "\e8ef";
|
||||
}
|
||||
|
||||
.step-forward-outlined:before {
|
||||
content: "\e8f0";
|
||||
}
|
||||
|
||||
.robot-outlined:before {
|
||||
content: "\e897";
|
||||
}
|
||||
|
||||
.file-word-outlined:before {
|
||||
content: "\e7ba";
|
||||
}
|
||||
|
||||
.usergroup-delete-outlined:before {
|
||||
content: "\e760";
|
||||
}
|
||||
|
||||
.field-time-outlined:before {
|
||||
content: "\eb5d";
|
||||
}
|
||||
|
||||
.setting-outlined:before {
|
||||
content: "\e78e";
|
||||
}
|
||||
|
||||
.file-search-outlined:before {
|
||||
content: "\e730";
|
||||
}
|
||||
|
||||
.team-outlined:before {
|
||||
content: "\e67d";
|
||||
}
|
||||
|
||||
.message-outlined:before {
|
||||
content: "\e78a";
|
||||
}
|
||||
|
||||
.mail-outlined:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.send-outlined:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.appstore-add-outlined:before {
|
||||
content: "\e8eb";
|
||||
}
|
||||
|
||||
.user-outlined:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.project-outlined:before {
|
||||
content: "\e746";
|
||||
}
|
||||
|
||||
.hdd-outlined:before {
|
||||
content: "\e734";
|
||||
}
|
||||
|
||||
.tool-outlined:before {
|
||||
content: "\e75b";
|
||||
}
|
||||
|
||||
.user-switch-outlined:before {
|
||||
content: "\ea3d";
|
||||
}
|
||||
|
||||
.appstore-outlined:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.home-outlined:before {
|
||||
content: "\e965";
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<div class="snowy-color-picker" @click="forceResize">
|
||||
<color-picker v-bind="$attrs" format="hex" :pureColor="props.value" @update:pureColor="update" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ColorPicker } from 'vue3-colorpicker'
|
||||
import 'vue3-colorpicker/style.css'
|
||||
|
||||
const emit = defineEmits(['update:value'])
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: '#1890ff'
|
||||
}
|
||||
})
|
||||
|
||||
const forceResize = () => {
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
}
|
||||
|
||||
const update = (val) => {
|
||||
const currentColor = document.querySelector('.current-color')
|
||||
if (currentColor) {
|
||||
currentColor.textContent = val
|
||||
}
|
||||
emit('update:value', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.snowy-color-picker {
|
||||
.vc-color-wrap {
|
||||
width: auto;
|
||||
}
|
||||
.current-color {
|
||||
color: #fff;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
src/components/ContentWrap/index.ts
Normal file
3
src/components/ContentWrap/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import ContentWrap from './src/ContentWrap.vue'
|
||||
|
||||
export { ContentWrap }
|
||||
38
src/components/ContentWrap/src/ContentWrap.vue
Normal file
38
src/components/ContentWrap/src/ContentWrap.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang='ts' setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { QuestionFilled } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({ name: 'ContentWrap' })
|
||||
|
||||
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>
|
||||
<QuestionFilled :size='14' class='ml-5px' />
|
||||
</ElTooltip>
|
||||
<div class='flex flex-grow pl-20px'>
|
||||
<slot name='header'></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
3
src/components/Dialog/index.ts
Normal file
3
src/components/Dialog/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Dialog from './src/Dialog.vue'
|
||||
|
||||
export { Dialog }
|
||||
140
src/components/Dialog/src/Dialog.vue
Normal file
140
src/components/Dialog/src/Dialog.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { isNumber } from '@/utils/is'
|
||||
defineOptions({ name: 'Dialog' })
|
||||
|
||||
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('400px')
|
||||
})
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['fullscreen', 'title', 'maxHeight', 'appendToBody']
|
||||
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)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
v-bind="getBindValue"
|
||||
:close-on-click-modal="true"
|
||||
:fullscreen="isFullscreen"
|
||||
:width="width"
|
||||
destroy-on-close
|
||||
lock-scroll
|
||||
draggable
|
||||
class="com-dialog"
|
||||
:show-close="false"
|
||||
>
|
||||
<template #header="{ close }">
|
||||
<div class="relative h-54px flex items-center justify-between pl-15px pr-15px">
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
<div
|
||||
class="absolute right-15px top-[50%] h-54px flex translate-y-[-50%] items-center justify-between"
|
||||
>
|
||||
<Icon
|
||||
v-if="fullscreen"
|
||||
class="is-hover mr-10px cursor-pointer"
|
||||
:icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'"
|
||||
color="var(--el-color-info)"
|
||||
hover-color="var(--el-color-primary)"
|
||||
@click="toggleFull"
|
||||
/>
|
||||
<Icon
|
||||
class="is-hover cursor-pointer"
|
||||
icon="ep:close"
|
||||
hover-color="var(--el-color-primary)"
|
||||
color="var(--el-color-info)"
|
||||
@click="close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElScrollbar v-if="scroll" :style="dialogStyle">
|
||||
<slot></slot>
|
||||
</ElScrollbar>
|
||||
<slot v-else></slot>
|
||||
<template v-if="slots.footer" #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.com-dialog {
|
||||
.el-overlay-dialog {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
margin: 0 !important;
|
||||
|
||||
&__header {
|
||||
height: 54px;
|
||||
padding: 0;
|
||||
margin-right: 0 !important;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
&__body {
|
||||
padding: 15px !important;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
border-top: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
||||
&__headerbtn {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
src/components/DictSelect/index.ts
Normal file
3
src/components/DictSelect/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import DictSelect from './src/DictSelect.vue'
|
||||
|
||||
export { DictSelect }
|
||||
46
src/components/DictSelect/src/DictSelect.vue
Normal file
46
src/components/DictSelect/src/DictSelect.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<!-- 数据字典 Select 选择器 -->
|
||||
<template>
|
||||
<el-select class="w-1/1" v-bind="attrs">
|
||||
<template v-if="valueType === 'int'">
|
||||
<el-option
|
||||
v-for="(dict, index) in getIntDictOptions(dictType)"
|
||||
:key="index"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="valueType === 'str'">
|
||||
<el-option
|
||||
v-for="(dict, index) in getStrDictOptions(dictType)"
|
||||
:key="index"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="valueType === 'bool'">
|
||||
<el-option
|
||||
v-for="(dict, index) in getBoolDictOptions(dictType)"
|
||||
:key="index"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</template>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
||||
|
||||
// 接受父组件参数
|
||||
interface Props {
|
||||
dictType: string // 字典类型
|
||||
valueType: string // 字典值类型
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
dictType: '',
|
||||
valueType: 'str'
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
defineOptions({ name: 'DictSelect' })
|
||||
</script>
|
||||
3
src/components/DictTag/index.ts
Normal file
3
src/components/DictTag/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import DictTag from './src/DictTag.vue'
|
||||
|
||||
export { DictTag }
|
||||
60
src/components/DictTag/src/DictTag.vue
Normal file
60
src/components/DictTag/src/DictTag.vue
Normal 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>
|
||||
8
src/components/Editor/index.ts
Normal file
8
src/components/Editor/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import Editor from './src/Editor.vue'
|
||||
import { IDomEditor } from '@wangeditor/editor'
|
||||
|
||||
export interface EditorExpose {
|
||||
getEditorRef: () => Promise<IDomEditor>
|
||||
}
|
||||
|
||||
export { Editor }
|
||||
202
src/components/Editor/src/Editor.vue
Normal file
202
src/components/Editor/src/Editor.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor/editor'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { isNumber } from '@/utils/is'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useLocaleStore } from '@/stores/modules/locale'
|
||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||
|
||||
defineOptions({ name: 'Editor' })
|
||||
|
||||
type InsertFnType = (url: string, alt: string, href: string) => void
|
||||
|
||||
const localeStore = useLocaleStore()
|
||||
|
||||
const currentLocale = computed(() => localeStore.getCurrentLocale)
|
||||
|
||||
i18nChangeLanguage(unref(currentLocale).lang)
|
||||
|
||||
const props = defineProps({
|
||||
editorId: propTypes.string.def('wangeEditor-1'),
|
||||
height: propTypes.oneOfType([Number, String]).def('500px'),
|
||||
editorConfig: {
|
||||
type: Object as PropType<Partial<IEditorConfig>>,
|
||||
default: () => undefined
|
||||
},
|
||||
readonly: propTypes.bool.def(false),
|
||||
modelValue: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef<IDomEditor>()
|
||||
|
||||
const valueHtml = ref('')
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string) => {
|
||||
if (val === unref(valueHtml)) return
|
||||
valueHtml.value = val
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
// 监听
|
||||
watch(
|
||||
() => valueHtml.value,
|
||||
(val: string) => {
|
||||
emit('update:modelValue', val)
|
||||
}
|
||||
)
|
||||
|
||||
const handleCreated = (editor: IDomEditor) => {
|
||||
editorRef.value = editor
|
||||
}
|
||||
|
||||
// 编辑器配置
|
||||
const editorConfig = computed((): IEditorConfig => {
|
||||
return Object.assign(
|
||||
{
|
||||
placeholder: '请输入内容...',
|
||||
readOnly: props.readonly,
|
||||
customAlert: (s: string, t: string) => {
|
||||
switch (t) {
|
||||
case 'success':
|
||||
ElMessage.success(s)
|
||||
break
|
||||
case 'info':
|
||||
ElMessage.info(s)
|
||||
break
|
||||
case 'warning':
|
||||
ElMessage.warning(s)
|
||||
break
|
||||
case 'error':
|
||||
ElMessage.error(s)
|
||||
break
|
||||
default:
|
||||
ElMessage.info(s)
|
||||
break
|
||||
}
|
||||
},
|
||||
autoFocus: false,
|
||||
scroll: true,
|
||||
MENU_CONF: {
|
||||
['uploadImage']: {
|
||||
server: import.meta.env.VITE_UPLOAD_URL,
|
||||
// 单个文件的最大体积限制,默认为 2M
|
||||
maxFileSize: 5 * 1024 * 1024,
|
||||
// 最多可上传几个文件,默认为 100
|
||||
maxNumberOfFiles: 10,
|
||||
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ['image/*'],
|
||||
|
||||
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
|
||||
meta: { updateSupport: 0 },
|
||||
// 将 meta 拼接到 url 参数中,默认 false
|
||||
metaWithUrl: true,
|
||||
|
||||
// 自定义增加 http header
|
||||
headers: {
|
||||
Accept: '*',
|
||||
Authorization: 'Bearer ' + getAccessToken(),
|
||||
'tenant-id': getTenantId()
|
||||
},
|
||||
|
||||
// 跨域是否传递 cookie ,默认为 false
|
||||
withCredentials: true,
|
||||
|
||||
// 超时时间,默认为 10 秒
|
||||
timeout: 5 * 1000, // 5 秒
|
||||
|
||||
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
|
||||
fieldName: 'file',
|
||||
|
||||
// 上传之前触发
|
||||
onBeforeUpload(file: File) {
|
||||
console.log(file)
|
||||
return file
|
||||
},
|
||||
// 上传进度的回调函数
|
||||
onProgress(progress: number) {
|
||||
// progress 是 0-100 的数字
|
||||
console.log('progress', progress)
|
||||
},
|
||||
onSuccess(file: File, res: any) {
|
||||
console.log('onSuccess', file, res)
|
||||
},
|
||||
onFailed(file: File, res: any) {
|
||||
alert(res.message)
|
||||
console.log('onFailed', file, res)
|
||||
},
|
||||
onError(file: File, err: any, res: any) {
|
||||
alert(err.message)
|
||||
console.error('onError', file, err, res)
|
||||
},
|
||||
// 自定义插入图片
|
||||
customInsert(res: any, insertFn: InsertFnType) {
|
||||
insertFn(res.data, 'image', res.data)
|
||||
}
|
||||
}
|
||||
},
|
||||
uploadImgShowBase64: true
|
||||
},
|
||||
props.editorConfig || {}
|
||||
)
|
||||
})
|
||||
|
||||
const editorStyle = computed(() => {
|
||||
return {
|
||||
height: isNumber(props.height) ? `${props.height}px` : props.height
|
||||
}
|
||||
})
|
||||
|
||||
// 回调函数
|
||||
const handleChange = (editor: IDomEditor) => {
|
||||
emit('change', editor)
|
||||
}
|
||||
|
||||
// 组件销毁时,及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = unref(editorRef.value)
|
||||
|
||||
// 销毁,并移除 editor
|
||||
editor?.destroy()
|
||||
})
|
||||
|
||||
const getEditorRef = async (): Promise<IDomEditor> => {
|
||||
await nextTick()
|
||||
return unref(editorRef.value) as IDomEditor
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getEditorRef
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-1 border-solid border-[var(--tags-view-border-color)] z-10">
|
||||
<!-- 工具栏 -->
|
||||
<Toolbar
|
||||
:editor="editorRef"
|
||||
:editorId="editorId"
|
||||
class="border-0 b-b-1 border-solid border-[var(--tags-view-border-color)]"
|
||||
/>
|
||||
<!-- 编辑器 -->
|
||||
<Editor
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:editorId="editorId"
|
||||
:style="editorStyle"
|
||||
@on-change="handleChange"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style src="@wangeditor/editor/dist/css/style.css"></style>
|
||||
3
src/components/FormCreate/index.ts
Normal file
3
src/components/FormCreate/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { useFormCreateDesigner } from './src/useFormCreateDesigner'
|
||||
|
||||
export { useFormCreateDesigner }
|
||||
15
src/components/FormCreate/src/config/index.ts
Normal file
15
src/components/FormCreate/src/config/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useUploadFileRule } from './useUploadFileRule'
|
||||
import { useUploadImgRule } from './useUploadImgRule'
|
||||
import { useUploadImgsRule } from './useUploadImgsRule'
|
||||
import { useDictSelectRule } from './useDictSelectRule'
|
||||
import { useUserSelectRule } from './useUserSelectRule'
|
||||
import { useEditorRule } from './useEditorRule'
|
||||
|
||||
export {
|
||||
useUploadFileRule,
|
||||
useUploadImgRule,
|
||||
useUploadImgsRule,
|
||||
useDictSelectRule,
|
||||
useUserSelectRule,
|
||||
useEditorRule
|
||||
}
|
||||
71
src/components/FormCreate/src/config/selectRule.ts
Normal file
71
src/components/FormCreate/src/config/selectRule.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
const selectRule = [
|
||||
{ type: 'switch', field: 'multiple', title: '是否多选' },
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'disabled',
|
||||
title: '是否禁用'
|
||||
},
|
||||
{ type: 'switch', field: 'clearable', title: '是否可以清空选项' },
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'collapseTags',
|
||||
title: '多选时是否将选中值按文字的形式展示'
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'multipleLimit',
|
||||
title: '多选时用户最多可以选择的项目数,为 0 则不限制',
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'autocomplete',
|
||||
title: 'autocomplete 属性'
|
||||
},
|
||||
{ type: 'input', field: 'placeholder', title: '占位符' },
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'filterable',
|
||||
title: '是否可搜索'
|
||||
},
|
||||
{ type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
|
||||
{
|
||||
type: 'input',
|
||||
field: 'noMatchText',
|
||||
title: '搜索条件无匹配时显示的文字'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'remote',
|
||||
title: '其中的选项是否从服务器远程加载'
|
||||
},
|
||||
{
|
||||
type: 'Struct',
|
||||
field: 'remoteMethod',
|
||||
title: '自定义远程搜索方法'
|
||||
},
|
||||
{ type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'reserveKeyword',
|
||||
title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'defaultFirstOption',
|
||||
title: '在输入框按下回车,选择第一个匹配项'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'popperAppendToBody',
|
||||
title: '是否将弹出框插入至 body 元素',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'automaticDropdown',
|
||||
title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单'
|
||||
}
|
||||
]
|
||||
|
||||
export default selectRule
|
||||
64
src/components/FormCreate/src/config/useDictSelectRule.ts
Normal file
64
src/components/FormCreate/src/config/useDictSelectRule.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
import selectRule from '@/components/FormCreate/src/config/selectRule'
|
||||
import { dictTypeListAll } from '@/api/system-boot/dictType'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
|
||||
export const useDictSelectRule = () => {
|
||||
const label = '字典选择器'
|
||||
const name = 'DictSelect'
|
||||
const dictOptions = ref<{ label: string; value: string }[]>([]) // 字典类型下拉数据
|
||||
onMounted(async () => {
|
||||
let data: any = []
|
||||
await dictTypeListAll().then(res => {
|
||||
data = res.data
|
||||
})
|
||||
if (data.length === 0) {
|
||||
return
|
||||
}
|
||||
dictOptions.value =
|
||||
data.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
})) ?? []
|
||||
})
|
||||
return {
|
||||
icon: 'icon-select',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [
|
||||
makeRequiredRule(),
|
||||
{
|
||||
type: 'select',
|
||||
field: 'dictType',
|
||||
title: '字典类型',
|
||||
value: '',
|
||||
options: dictOptions.value
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
field: 'valueType',
|
||||
title: '字典值类型',
|
||||
value: 'str',
|
||||
options: [
|
||||
{ label: '数字', value: 'int' },
|
||||
{ label: '字符串', value: 'str' },
|
||||
{ label: '布尔值', value: 'bool' }
|
||||
]
|
||||
},
|
||||
...selectRule
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/components/FormCreate/src/config/useEditorRule.ts
Normal file
32
src/components/FormCreate/src/config/useEditorRule.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
|
||||
export const useEditorRule = () => {
|
||||
const label = '富文本'
|
||||
const name = 'Editor'
|
||||
return {
|
||||
icon: 'icon-editor',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [
|
||||
makeRequiredRule(),
|
||||
{
|
||||
type: 'input',
|
||||
field: 'height',
|
||||
title: '高度'
|
||||
},
|
||||
{ type: 'switch', field: 'readonly', title: '是否只读' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/components/FormCreate/src/config/useUploadFileRule.ts
Normal file
80
src/components/FormCreate/src/config/useUploadFileRule.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
|
||||
export const useUploadFileRule = () => {
|
||||
const label = '文件上传'
|
||||
const name = 'UploadFile'
|
||||
return {
|
||||
icon: 'icon-upload',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [
|
||||
makeRequiredRule(),
|
||||
{
|
||||
type: 'select',
|
||||
field: 'fileType',
|
||||
title: '文件类型',
|
||||
value: ['doc', 'xls', 'ppt', 'txt', 'pdf'],
|
||||
options: [
|
||||
{ label: 'doc', value: 'doc' },
|
||||
{ label: 'xls', value: 'xls' },
|
||||
{ label: 'ppt', value: 'ppt' },
|
||||
{ label: 'txt', value: 'txt' },
|
||||
{ label: 'pdf', value: 'pdf' }
|
||||
],
|
||||
props: {
|
||||
multiple: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'autoUpload',
|
||||
title: '是否在选取文件后立即进行上传',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'drag',
|
||||
title: '拖拽上传',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'isShowTip',
|
||||
title: '是否显示提示',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'fileSize',
|
||||
title: '大小限制(MB)',
|
||||
value: 5,
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'limit',
|
||||
title: '数量限制',
|
||||
value: 5,
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'disabled',
|
||||
title: '是否禁用',
|
||||
value: false
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
89
src/components/FormCreate/src/config/useUploadImgRule.ts
Normal file
89
src/components/FormCreate/src/config/useUploadImgRule.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
|
||||
export const useUploadImgRule = () => {
|
||||
const label = '单图上传'
|
||||
const name = 'UploadImg'
|
||||
return {
|
||||
icon: 'icon-upload',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [
|
||||
makeRequiredRule(),
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'drag',
|
||||
title: '拖拽上传',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
field: 'fileType',
|
||||
title: '图片类型限制',
|
||||
value: ['image/jpeg', 'image/png', 'image/gif'],
|
||||
options: [
|
||||
{ label: 'image/apng', value: 'image/apng' },
|
||||
{ label: 'image/bmp', value: 'image/bmp' },
|
||||
{ label: 'image/gif', value: 'image/gif' },
|
||||
{ label: 'image/jpeg', value: 'image/jpeg' },
|
||||
{ label: 'image/pjpeg', value: 'image/pjpeg' },
|
||||
{ label: 'image/svg+xml', value: 'image/svg+xml' },
|
||||
{ label: 'image/tiff', value: 'image/tiff' },
|
||||
{ label: 'image/webp', value: 'image/webp' },
|
||||
{ label: 'image/x-icon', value: 'image/x-icon' }
|
||||
],
|
||||
props: {
|
||||
multiple: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'fileSize',
|
||||
title: '大小限制(MB)',
|
||||
value: 5,
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'height',
|
||||
title: '组件高度',
|
||||
value: '150px'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'width',
|
||||
title: '组件宽度',
|
||||
value: '150px'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'borderradius',
|
||||
title: '组件边框圆角',
|
||||
value: '8px'
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'disabled',
|
||||
title: '是否显示删除按钮',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'showBtnText',
|
||||
title: '是否显示按钮文字',
|
||||
value: true
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/components/FormCreate/src/config/useUploadImgsRule.ts
Normal file
84
src/components/FormCreate/src/config/useUploadImgsRule.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
|
||||
export const useUploadImgsRule = () => {
|
||||
const label = '多图上传'
|
||||
const name = 'UploadImgs'
|
||||
return {
|
||||
icon: 'icon-upload',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [
|
||||
makeRequiredRule(),
|
||||
{
|
||||
type: 'switch',
|
||||
field: 'drag',
|
||||
title: '拖拽上传',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
field: 'fileType',
|
||||
title: '图片类型限制',
|
||||
value: ['image/jpeg', 'image/png', 'image/gif'],
|
||||
options: [
|
||||
{ label: 'image/apng', value: 'image/apng' },
|
||||
{ label: 'image/bmp', value: 'image/bmp' },
|
||||
{ label: 'image/gif', value: 'image/gif' },
|
||||
{ label: 'image/jpeg', value: 'image/jpeg' },
|
||||
{ label: 'image/pjpeg', value: 'image/pjpeg' },
|
||||
{ label: 'image/svg+xml', value: 'image/svg+xml' },
|
||||
{ label: 'image/tiff', value: 'image/tiff' },
|
||||
{ label: 'image/webp', value: 'image/webp' },
|
||||
{ label: 'image/x-icon', value: 'image/x-icon' }
|
||||
],
|
||||
props: {
|
||||
multiple: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'fileSize',
|
||||
title: '大小限制(MB)',
|
||||
value: 5,
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'inputNumber',
|
||||
field: 'limit',
|
||||
title: '数量限制',
|
||||
value: 5,
|
||||
props: { min: 0 }
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'height',
|
||||
title: '组件高度',
|
||||
value: '150px'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'width',
|
||||
title: '组件宽度',
|
||||
value: '150px'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
field: 'borderradius',
|
||||
title: '组件边框圆角',
|
||||
value: '8px'
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/components/FormCreate/src/config/useUserSelectRule.ts
Normal file
25
src/components/FormCreate/src/config/useUserSelectRule.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { generateUUID } from '@/utils'
|
||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||
import selectRule from '@/components/FormCreate/src/config/selectRule'
|
||||
|
||||
export const useUserSelectRule = () => {
|
||||
const label = '用户选择器'
|
||||
const name = 'UserSelect'
|
||||
return {
|
||||
icon: 'icon-select',
|
||||
label,
|
||||
name,
|
||||
rule() {
|
||||
return {
|
||||
type: name,
|
||||
field: generateUUID(),
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false
|
||||
}
|
||||
},
|
||||
props(_, { t }) {
|
||||
return localeProps(t, name + '.props', [makeRequiredRule(), ...selectRule])
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/components/FormCreate/src/useFormCreateDesigner.ts
Normal file
53
src/components/FormCreate/src/useFormCreateDesigner.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
useDictSelectRule,
|
||||
useEditorRule,
|
||||
useUploadFileRule,
|
||||
useUploadImgRule,
|
||||
useUploadImgsRule,
|
||||
useUserSelectRule
|
||||
} from './config'
|
||||
import { Ref } from 'vue'
|
||||
|
||||
/**
|
||||
* 表单设计器增强 hook
|
||||
* 新增
|
||||
* - 文件上传
|
||||
* - 单图上传
|
||||
* - 多图上传
|
||||
* - 字典选择器
|
||||
* - 系统用户选择器
|
||||
* - 富文本
|
||||
*/
|
||||
export const useFormCreateDesigner = (designer: Ref) => {
|
||||
const editorRule = useEditorRule()
|
||||
const uploadFileRule = useUploadFileRule()
|
||||
const uploadImgRule = useUploadImgRule()
|
||||
const uploadImgsRule = useUploadImgsRule()
|
||||
const dictSelectRule = useDictSelectRule()
|
||||
const userSelectRule = useUserSelectRule()
|
||||
|
||||
onMounted(() => {
|
||||
// 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
|
||||
designer.value?.removeMenuItem('upload')
|
||||
// 移除自带的富文本组件规则,使用 editorRule 替代
|
||||
designer.value?.removeMenuItem('fc-editor')
|
||||
const components = [
|
||||
editorRule,
|
||||
uploadFileRule,
|
||||
uploadImgRule,
|
||||
uploadImgsRule,
|
||||
dictSelectRule,
|
||||
userSelectRule
|
||||
]
|
||||
components.forEach((component) => {
|
||||
// 插入组件规则
|
||||
designer.value?.addComponent(component)
|
||||
// 插入拖拽按钮到 `main` 分类下
|
||||
designer.value?.appendMenuItem('main', {
|
||||
icon: component.icon,
|
||||
name: component.name,
|
||||
label: component.label
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
79
src/components/FormCreate/src/utils/index.ts
Normal file
79
src/components/FormCreate/src/utils/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
// TODO puhui999: 借鉴一下 form-create-designer utils 方法 🤣 (导入不了只能先 copy 过来用下)
|
||||
export function makeRequiredRule() {
|
||||
return {
|
||||
type: 'Required',
|
||||
field: 'formCreate$required',
|
||||
title: '是否必填'
|
||||
}
|
||||
}
|
||||
|
||||
export const localeProps = (t, prefix, rules) => {
|
||||
return rules.map((rule) => {
|
||||
if (rule.field === 'formCreate$required') {
|
||||
rule.title = t('props.required') || rule.title
|
||||
} else if (rule.field && rule.field !== '_optionType') {
|
||||
rule.title = t('components.' + prefix + '.' + rule.field) || rule.title
|
||||
}
|
||||
return rule
|
||||
})
|
||||
}
|
||||
|
||||
export function upper(str) {
|
||||
return str.replace(str[0], str[0].toLocaleUpperCase())
|
||||
}
|
||||
|
||||
export function makeOptionsRule(t, to, userOptions) {
|
||||
console.log(userOptions[0])
|
||||
const options = [
|
||||
{ label: t('props.optionsType.struct'), value: 0 },
|
||||
{ label: t('props.optionsType.json'), value: 1 },
|
||||
{ label: '用户数据', value: 2 }
|
||||
]
|
||||
|
||||
const control = [
|
||||
{
|
||||
value: 0,
|
||||
rule: [
|
||||
{
|
||||
type: 'TableOptions',
|
||||
field: 'formCreate' + upper(to).replace('.', '>'),
|
||||
props: { defaultValue: [] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
rule: [
|
||||
{
|
||||
type: 'Struct',
|
||||
field: 'formCreate' + upper(to).replace('.', '>'),
|
||||
props: { defaultValue: [] }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
rule: [
|
||||
{
|
||||
type: 'TableOptions',
|
||||
field: 'formCreate' + upper(to).replace('.', '>'),
|
||||
props: { modelValue: [] }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
options.splice(0, 0)
|
||||
control.push()
|
||||
|
||||
return {
|
||||
type: 'radio',
|
||||
title: t('props.options'),
|
||||
field: '_optionType',
|
||||
value: 0,
|
||||
options,
|
||||
props: {
|
||||
type: 'button'
|
||||
},
|
||||
control
|
||||
}
|
||||
}
|
||||
33
src/components/ImageViewer/index.ts
Normal file
33
src/components/ImageViewer/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import ImageViewer from './src/ImageViewer.vue'
|
||||
import { isClient } from '@/utils/is'
|
||||
import { createVNode, render, VNode } from 'vue'
|
||||
import { ImageViewerProps } from './src/types'
|
||||
|
||||
let instance: Nullable<VNode> = null
|
||||
|
||||
export function createImageViewer(options: ImageViewerProps) {
|
||||
if (!isClient) return
|
||||
const {
|
||||
urlList,
|
||||
initialIndex = 0,
|
||||
infinite = true,
|
||||
hideOnClickModal = false,
|
||||
teleported = false,
|
||||
zIndex = 2000,
|
||||
show = true
|
||||
} = options
|
||||
|
||||
const propsData: Partial<ImageViewerProps> = {}
|
||||
const container = document.createElement('div')
|
||||
propsData.urlList = urlList
|
||||
propsData.initialIndex = initialIndex
|
||||
propsData.infinite = infinite
|
||||
propsData.hideOnClickModal = hideOnClickModal
|
||||
propsData.teleported = teleported
|
||||
propsData.zIndex = zIndex
|
||||
propsData.show = show
|
||||
|
||||
document.body.appendChild(container)
|
||||
instance = createVNode(ImageViewer, propsData)
|
||||
render(instance, container)
|
||||
}
|
||||
35
src/components/ImageViewer/src/ImageViewer.vue
Normal file
35
src/components/ImageViewer/src/ImageViewer.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
defineOptions({ name: 'ImageViewer' })
|
||||
|
||||
const props = defineProps({
|
||||
urlList: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => []
|
||||
},
|
||||
zIndex: propTypes.number.def(200),
|
||||
initialIndex: propTypes.number.def(0),
|
||||
infinite: propTypes.bool.def(true),
|
||||
hideOnClickModal: propTypes.bool.def(false),
|
||||
teleported: propTypes.bool.def(false),
|
||||
show: propTypes.bool.def(false)
|
||||
})
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const propsData: Recordable = { ...props }
|
||||
delete propsData.show
|
||||
return propsData
|
||||
})
|
||||
|
||||
const show = ref(props.show)
|
||||
|
||||
const close = () => {
|
||||
show.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElImageViewer v-if="show" v-bind="getBindValue" @close="close" />
|
||||
</template>
|
||||
9
src/components/ImageViewer/src/types.ts
Normal file
9
src/components/ImageViewer/src/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface ImageViewerProps {
|
||||
urlList?: string[]
|
||||
zIndex?: number
|
||||
initialIndex?: number
|
||||
infinite?: boolean
|
||||
hideOnClickModal?: boolean
|
||||
teleported?: boolean
|
||||
show?: boolean
|
||||
}
|
||||
111
src/components/RouterSearch/index.vue
Normal file
111
src/components/RouterSearch/index.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<ElDialog v-if="isModal" v-model="showSearch" :show-close="false" title="菜单搜索">
|
||||
<el-select
|
||||
filterable
|
||||
:reserve-keyword="false"
|
||||
remote
|
||||
placeholder="请输入菜单内容"
|
||||
:remote-method="remoteMethod"
|
||||
style="width: 100%"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</ElDialog>
|
||||
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
|
||||
<Icon icon="ep:search" />
|
||||
<el-select
|
||||
filterable
|
||||
:reserve-keyword="false"
|
||||
remote
|
||||
placeholder="请输入菜单内容"
|
||||
:remote-method="remoteMethod"
|
||||
class="overflow-hidden transition-all-600"
|
||||
:class="showTopSearch ? '!w-220px ml2' : '!w-0'"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
isModal: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const router = useRouter() // 路由对象
|
||||
const showSearch = ref(false) // 是否显示弹框
|
||||
const showTopSearch = ref(false) // 是否显示顶部搜索框
|
||||
const value: Ref = ref('') // 用户输入的值
|
||||
|
||||
const routers = router.getRoutes() // 路由对象
|
||||
const options = computed(() => {
|
||||
// 提示选项
|
||||
if (!value.value) {
|
||||
return []
|
||||
}
|
||||
const list = routers.filter((item: any) => {
|
||||
if (item.meta.title?.indexOf(value.value) > -1 || item.path.indexOf(value.value) > -1) {
|
||||
return true
|
||||
}
|
||||
})
|
||||
return list.map((item) => {
|
||||
return {
|
||||
label: `${item.meta.title}${item.path}`,
|
||||
value: item.path
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
function remoteMethod(data) {
|
||||
// 这里可以执行相应的操作(例如打开搜索框等)
|
||||
value.value = data
|
||||
}
|
||||
|
||||
function handleChange(path) {
|
||||
router.push({ path })
|
||||
hiddenTopSearch()
|
||||
}
|
||||
|
||||
function hiddenTopSearch() {
|
||||
showTopSearch.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', listenKey)
|
||||
window.addEventListener('click', hiddenTopSearch)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', listenKey)
|
||||
window.removeEventListener('click', hiddenTopSearch)
|
||||
})
|
||||
|
||||
// 监听 ctrl + k
|
||||
function listenKey(event) {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
||||
showSearch.value = !showSearch.value
|
||||
// 这里可以执行相应的操作(例如打开搜索框等)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openSearch: () => {
|
||||
showSearch.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,142 +0,0 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="移动端图标选择"
|
||||
:mask-closable="false"
|
||||
:width="800"
|
||||
:destroy-on-close="true"
|
||||
:footer="null"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="paneChange">
|
||||
<a-tab-pane v-for="item in iconData" :key="item.key" :tab="item.name">
|
||||
<div v-if="item.iconItem.length > 1" class="xn-icon-select-radio">
|
||||
<a-radio-group v-model:value="iconItemDefault" @change="radioGroupChange">
|
||||
<a-radio-button v-for="iconItem in item.iconItem" :key="iconItem.key" :value="iconItem.key">{{
|
||||
iconItem.name
|
||||
}}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<div :key="iconItemIns" v-for="iconItemIns in item.iconItem">
|
||||
<div v-if="iconItemIns.key === iconItemDefault" class="xn-icon-select-list">
|
||||
<ul>
|
||||
<li
|
||||
v-for="icon in iconItemIns.item"
|
||||
:key="icon"
|
||||
:class="icon === modelValue ? 'active' : ''"
|
||||
@click="selectIcon(icon.font_class)"
|
||||
>
|
||||
<span class="snowy xn-icons" :class="icon.font_class"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
import config from '@/assets/icons/mobile'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
iconData: [],
|
||||
modelValue: '',
|
||||
activeKey: 'default',
|
||||
iconItemDefault: 'default'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.iconData.push(...config.icons)
|
||||
},
|
||||
methods: {
|
||||
// 打开
|
||||
showIconModal(value) {
|
||||
this.visible = true
|
||||
this.defaultSetting(value)
|
||||
},
|
||||
// 默认配置
|
||||
defaultSetting(value) {
|
||||
if (value) {
|
||||
this.modelValue = value
|
||||
// 判断展开哪个
|
||||
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
|
||||
this.activeKey = 'default'
|
||||
if (value.indexOf('-two-tone') > -1) {
|
||||
this.iconItemDefault = 'twotone'
|
||||
} else if (value.indexOf('-filled') > -1) {
|
||||
this.iconItemDefault = 'filled'
|
||||
}
|
||||
} else if (value.indexOf('-extend') > -1) {
|
||||
// 扩展列表
|
||||
this.activeKey = 'extend'
|
||||
// 如扩展其他顶部单选的情况,默认选中在这里配置,同时这里需要做判断
|
||||
// this.iconItemDefault = '您的json中配置的'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 切换标签页,如果是切换到了没用额外的标签页的地方,我们将其置为默认
|
||||
paneChange(e) {
|
||||
if (e.indexOf('default') === -1) {
|
||||
this.iconItemDefault = 'default'
|
||||
}
|
||||
},
|
||||
// 切换icon风格
|
||||
radioGroupChange(e) {
|
||||
this.iconItemDefault = e.target.value
|
||||
},
|
||||
// 选择图标后关闭并返回
|
||||
selectIcon(value) {
|
||||
this.defaultValue = value
|
||||
this.visible = false
|
||||
// eslint-disable-next-line vue/require-explicit-emits
|
||||
this.$emit('iconCallBack', this.defaultValue)
|
||||
},
|
||||
onCancel() {
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.xn-icon-select-radio {
|
||||
padding-left: 5px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.xn-icons {
|
||||
font-size: 26px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.xn-icon-select-list {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
}
|
||||
.xn-icon-select-list ul {
|
||||
li {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
padding: 18px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
vertical-align: top;
|
||||
box-shadow: 0 0 0 1px var(--border-color-split);
|
||||
transition: all 0.1s;
|
||||
position: relative;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
cursor: pointer;
|
||||
color: #ffffff;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,143 +0,0 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="图标选择"
|
||||
:mask-closable="false"
|
||||
:width="800"
|
||||
:destroy-on-close="true"
|
||||
:footer="null"
|
||||
@cancel="onCancel"
|
||||
>
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left" size="small" @change="paneChange">
|
||||
<a-tab-pane v-for="item in iconData" :key="item.key" :tab="item.name">
|
||||
<div v-if="item.iconItem.length > 1" class="xn-icon-select-radio">
|
||||
<a-radio-group v-model:value="iconItemDefault" @change="radioGroupChange">
|
||||
<a-radio-button v-for="iconItem in item.iconItem" :key="iconItem.key" :value="iconItem.key">{{
|
||||
iconItem.name
|
||||
}}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
<div :key="iconItemIns" v-for="iconItemIns in item.iconItem">
|
||||
<div v-if="iconItemIns.key === iconItemDefault" class="xn-icon-select-list">
|
||||
<ul>
|
||||
<li
|
||||
v-for="icon in iconItemIns.item"
|
||||
:key="icon"
|
||||
:class="icon === modelValue ? 'active' : ''"
|
||||
@click="selectIcon(icon)"
|
||||
>
|
||||
<component :is="icon" class="xn-icons" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config from '@/config/iconSelect'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
iconData: [],
|
||||
modelValue: '',
|
||||
activeKey: 'default',
|
||||
iconItemDefault: 'default'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.iconData.push(...config.icons)
|
||||
},
|
||||
methods: {
|
||||
// 打开
|
||||
showIconModal(value) {
|
||||
this.visible = true
|
||||
this.defaultSetting(value)
|
||||
},
|
||||
// 默认配置
|
||||
defaultSetting(value) {
|
||||
if (value) {
|
||||
this.modelValue = value
|
||||
// 判断展开哪个
|
||||
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
|
||||
this.activeKey = 'default'
|
||||
if (value.indexOf('-two-tone') > -1) {
|
||||
this.iconItemDefault = 'twotone'
|
||||
} else if (value.indexOf('-filled') > -1) {
|
||||
this.iconItemDefault = 'filled'
|
||||
}
|
||||
} else if (value.indexOf('-extend') > -1) {
|
||||
// 扩展列表
|
||||
this.activeKey = 'extend'
|
||||
// 如扩展其他顶部单选的情况,默认选中在这里配置,同时这里需要做判断
|
||||
// this.iconItemDefault = '您的json中配置的'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 切换标签页,如果是切换到了没用额外的标签页的地方,我们将其置为默认
|
||||
paneChange(e) {
|
||||
if (e.indexOf('default') === -1) {
|
||||
this.iconItemDefault = 'default'
|
||||
}
|
||||
},
|
||||
// 切换icon风格
|
||||
radioGroupChange(e) {
|
||||
this.iconItemDefault = e.target.value
|
||||
},
|
||||
// 选择图标后关闭并返回
|
||||
selectIcon(value) {
|
||||
this.defaultValue = value
|
||||
this.visible = false
|
||||
// eslint-disable-next-line vue/require-explicit-emits
|
||||
this.$emit('iconCallBack', this.defaultValue)
|
||||
},
|
||||
onCancel() {
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.xn-icon-select-radio {
|
||||
padding-left: 5px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.xn-icons {
|
||||
font-size: 26px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.xn-icon-select-list {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
}
|
||||
.xn-icon-select-list ul {
|
||||
li {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
padding: 18px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
vertical-align: top;
|
||||
box-shadow: 0 0 0 1px var(--border-color-split);
|
||||
transition: all 0.1s;
|
||||
position: relative;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
cursor: pointer;
|
||||
color: #ffffff;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,496 +0,0 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="机构选择" :align-center="true" width="90%" style="height: 600px">
|
||||
<!-- :mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleClose" -->
|
||||
<div class="drawer_body">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="7">
|
||||
<el-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||
<el-tree
|
||||
v-if="treeData"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:props="defaultProps"
|
||||
accordion
|
||||
:default-expand-all="true"
|
||||
@node-click="treeSelect"
|
||||
></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<div class="table-operator" style="margin-bottom: 10px">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
name="advanced_search"
|
||||
class="ant-advanced-search-form"
|
||||
:model="searchFormState"
|
||||
>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item name="searchKey">
|
||||
<el-input
|
||||
v-model="searchFormState.searchKey"
|
||||
placeholder="请输入机构名"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" class="primarySele" @click="loadData()">查询</el-button>
|
||||
<el-button class="snowy-buttom-left" @click="() => reset()">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="org-table">
|
||||
<div class="org_table_head">
|
||||
<p>待选择列表</p>
|
||||
<div class="org_table_head_add">
|
||||
添加当前数据
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="table"
|
||||
size="small"
|
||||
:columns="commons"
|
||||
:data="tableData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="pageLoading"
|
||||
bordered
|
||||
:pagination="false"
|
||||
border
|
||||
stripe
|
||||
height="330"
|
||||
>
|
||||
<template #title>
|
||||
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" size="small" @click="addAllPageRecord">
|
||||
添加当前数据
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'category'">
|
||||
{{ $TOOL.dictTypeData('ORG_CATEGORY', record.category) }}
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<el-table-column prop="action" label="操作" width="80" align="center">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="机构名" />
|
||||
<el-table-column prop="type" label="分类"></el-table-column>
|
||||
</el-table>
|
||||
<div class="mt-2">
|
||||
<el-pagination
|
||||
v-if="!isEmpty(tableData)"
|
||||
v-model:current="current"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
size="small"
|
||||
showSizeChanger
|
||||
@change="paginationChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="org-table">
|
||||
<div class="org_table_heads">
|
||||
<p>待选择列表</p>
|
||||
<div class="org_table_head_delete">
|
||||
全部移除
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="selectedTable"
|
||||
size="small"
|
||||
:columns="selectedCommons"
|
||||
:data="selectedData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="selectedTableListLoading"
|
||||
bordered
|
||||
border
|
||||
>
|
||||
<template #title>
|
||||
<span>已选择: {{ selectedData.length }}</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" danger size="small" @click="delAllRecord">
|
||||
全部移除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" danger size="small" @click="delRecord(record)">
|
||||
移除
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<el-table-column prop="action" label="操作" width="80" align="center">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="机构名" />
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="visible = false">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="orgSelectorPlus">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { remove, isEmpty } from 'lodash-es'
|
||||
import { ref } from 'vue'
|
||||
// 弹窗是否打开
|
||||
const visible = ref(false)
|
||||
// 主表格common
|
||||
const commons = [
|
||||
{
|
||||
label: '操作',
|
||||
prop: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: '机构名',
|
||||
prop: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
label: '分类',
|
||||
prop: 'category'
|
||||
}
|
||||
]
|
||||
// 选中表格的表格common
|
||||
const selectedCommons = [
|
||||
{
|
||||
label: '操作',
|
||||
prop: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: '机构名',
|
||||
prop: 'name',
|
||||
ellipsis: true
|
||||
}
|
||||
]
|
||||
|
||||
// 主表格的ref 名称
|
||||
const table = ref()
|
||||
// 选中表格的ref 名称
|
||||
const selectedTable = ref()
|
||||
const tableRecordNum = ref()
|
||||
const searchFormState = ref({})
|
||||
const searchFormRef = ref()
|
||||
const cardLoading = ref(true)
|
||||
const pageLoading = ref(false)
|
||||
const selectedTableListLoading = ref(false)
|
||||
//树状图数据配置
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
// 获取机构树数据
|
||||
const treeData = ref()
|
||||
// 默认展开二级树的节点id
|
||||
const defaultExpandedKeys = ref([])
|
||||
const emit = defineEmits({ onBack: null })
|
||||
const tableData = ref([])
|
||||
const selectedData = ref([])
|
||||
const recordIds = ref()
|
||||
const props = defineProps(['orgPageApi', 'orgTreeApi', 'radioModel', 'dataIsConverterFlw', 'checkedOrgListApi'])
|
||||
// 是否是单选
|
||||
const radioModel = props.radioModel || false
|
||||
// 数据是否转换成工作流格式
|
||||
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||
// 分页相关
|
||||
const current = ref(0) // 当前页数
|
||||
const pageSize = ref(20) // 每页条数
|
||||
const total = ref(0) // 数据总数
|
||||
|
||||
// 打开弹框
|
||||
const showOrgPlusModal = ids => {
|
||||
visible.value = true
|
||||
if (dataIsConverterFlw) {
|
||||
ids = goDataConverter(ids)
|
||||
}
|
||||
recordIds.value = ids
|
||||
if (props.orgTreeApi) {
|
||||
// 获取机构树
|
||||
props.orgTreeApi().then(data => {
|
||||
cardLoading.value = false
|
||||
if (data !== null) {
|
||||
console.log(data, 'data,++++++++++++')
|
||||
treeData.value = data.data
|
||||
// 默认展开2级
|
||||
treeData.value.forEach(item => {
|
||||
// 因为0的顶级
|
||||
if (item.parentId === '0') {
|
||||
defaultExpandedKeys.value.push(item.id)
|
||||
// 取到下级ID
|
||||
if (item.children) {
|
||||
item.children.forEach(items => {
|
||||
defaultExpandedKeys.value.push(items.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
searchFormState.value.size = pageSize.value
|
||||
loadData()
|
||||
if (props.checkedOrgListApi) {
|
||||
if (isEmpty(recordIds.value)) {
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
idList: recordIds.value
|
||||
}
|
||||
selectedTableListLoading.value = true
|
||||
props
|
||||
.checkedOrgListApi(param)
|
||||
.then(data => {
|
||||
selectedData.value = data
|
||||
})
|
||||
.finally(() => {
|
||||
selectedTableListLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// 查询主表格数据
|
||||
const loadData = () => {
|
||||
pageLoading.value = true
|
||||
props
|
||||
.orgPageApi(searchFormState.value)
|
||||
.then(data => {
|
||||
current.value = data.current
|
||||
total.value = data.total
|
||||
// 重置、赋值
|
||||
tableData.value = []
|
||||
tableRecordNum.value = 0
|
||||
console.log(data, '999999999999')
|
||||
tableData.value = data.data.records
|
||||
if (data.records) {
|
||||
tableRecordNum.value = data.data.records.length
|
||||
} else {
|
||||
tableRecordNum.value = 0
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
}
|
||||
// pageSize改变回调分页事件
|
||||
const paginationChange = (page, pageSize) => {
|
||||
searchFormState.value.current = page
|
||||
searchFormState.value.size = pageSize
|
||||
loadData()
|
||||
}
|
||||
const judge = () => {
|
||||
if (radioModel && selectedData.value.length > 0) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 添加记录
|
||||
const addRecord = record => {
|
||||
if (!judge()) {
|
||||
message.warning('只可选择一条')
|
||||
return
|
||||
}
|
||||
const selectedRecord = selectedData.value.filter(item => item.id === record.id)
|
||||
if (selectedRecord.length === 0) {
|
||||
selectedData.value.push(record)
|
||||
} else {
|
||||
message.warning('该记录已存在')
|
||||
}
|
||||
}
|
||||
// 添加全部
|
||||
const addAllPageRecord = () => {
|
||||
let newArray = selectedData.value.concat(tableData.value)
|
||||
let list = []
|
||||
for (let item1 of newArray) {
|
||||
let flag = true
|
||||
for (let item2 of list) {
|
||||
if (item1.id === item2.id) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
list.push(item1)
|
||||
}
|
||||
}
|
||||
selectedData.value = list
|
||||
}
|
||||
// 删减记录
|
||||
const delRecord = record => {
|
||||
remove(selectedData.value, item => item.id === record.id)
|
||||
}
|
||||
// 删减记录
|
||||
const delAllRecord = () => {
|
||||
selectedData.value = []
|
||||
}
|
||||
// 点击树查询
|
||||
const treeSelect = selectedKeys => {
|
||||
console.log(selectedKeys.id, '111112222333')
|
||||
searchFormState.value.current = 0
|
||||
if (selectedKeys.id) {
|
||||
searchFormState.value.orgId = selectedKeys.id.toString()
|
||||
} else {
|
||||
// delete searchFormState.value.orgId
|
||||
delete searchFormState.value.orgId
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
// 确定
|
||||
const handleOk = () => {
|
||||
const value = []
|
||||
selectedData.value.forEach(item => {
|
||||
const obj = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
category: item.category
|
||||
}
|
||||
value.push(obj)
|
||||
})
|
||||
// 判断是否做数据的转换为工作流需要的
|
||||
if (dataIsConverterFlw) {
|
||||
emit('onBack', outDataConverter(value))
|
||||
} else {
|
||||
emit('onBack', value)
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
delete searchFormState.value.searchKey
|
||||
loadData()
|
||||
}
|
||||
const handleClose = () => {
|
||||
searchFormState.value = {}
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = []
|
||||
current.value = 0
|
||||
pageSize.value = 20
|
||||
total.value = 0
|
||||
selectedData.value = []
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 数据进入后转换
|
||||
const goDataConverter = data => {
|
||||
const resultData = []
|
||||
if (data.length > 0) {
|
||||
const values = data[0].value.split(',')
|
||||
if (JSON.stringify(values) !== '[""]') {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
resultData.push(values[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultData
|
||||
}
|
||||
// 数据出口转换器
|
||||
const outDataConverter = data => {
|
||||
const obj = {}
|
||||
let label = ''
|
||||
let value = ''
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data.length === i + 1) {
|
||||
label = label + data[i].name
|
||||
value = value + data[i].id
|
||||
} else {
|
||||
label = label + data[i].name + ','
|
||||
value = value + data[i].id + ','
|
||||
}
|
||||
}
|
||||
obj.key = 'ORG'
|
||||
obj.label = label
|
||||
obj.value = value
|
||||
obj.extJson = ''
|
||||
return obj
|
||||
}
|
||||
defineExpose({
|
||||
showOrgPlusModal
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.selectorTreeDiv {
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
.cardTag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.primarySele {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.org-table {
|
||||
overflow: auto;
|
||||
max-height: 450px;
|
||||
}
|
||||
.drawer_body {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
}
|
||||
.dialog-footer{
|
||||
position:absolute;
|
||||
right:10px;
|
||||
bottom:10px;
|
||||
z-index:2005;
|
||||
}
|
||||
.org_table_head,.org_table_heads{
|
||||
width:100%;
|
||||
height: 40px;
|
||||
border: 1px solid #F0F0F0;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
padding:0 10px;
|
||||
p{
|
||||
font-size: 12px;
|
||||
}
|
||||
.org_table_head_add,.org_table_head_delete{
|
||||
width:100px;
|
||||
height:24px;
|
||||
font-size: 12px;
|
||||
border:2px dashed #F0F0F0;
|
||||
border-radius:2px;
|
||||
cursor:pointer;
|
||||
display:flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.org_table_head_add:hover{
|
||||
border:2px dashed #3FA9FF;
|
||||
color:#3FA9FF;
|
||||
}
|
||||
.org_table_head_delete{
|
||||
width:72px;
|
||||
height:24px;
|
||||
border:2px dashed #FF4D4F;
|
||||
color:#FF4D4F;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,399 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="职位选择"
|
||||
:width="1000"
|
||||
:mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleClose"
|
||||
>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="7">
|
||||
<el-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||
<el-tree
|
||||
v-if="treeData"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
@node-click="treeSelect"
|
||||
>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<div class="table-operator" style="margin-bottom: 10px">
|
||||
<el-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item name="searchKey">
|
||||
<el-input v-model:value="searchFormState.searchKey" placeholder="请输入职位名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" class="primarySele" @click="loadData()"> 查询 </el-button>
|
||||
<el-button class="snowy-buttom-left" @click="() => reset()"> 重置 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="pos-table">
|
||||
<el-table
|
||||
ref="table"
|
||||
size="small"
|
||||
:columns="commons"
|
||||
:datel="tableData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="pageLoading"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #title>
|
||||
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'category'">
|
||||
{{ $TOOL.dictTypeData('POSITION_CATEGORY', record.category) }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="mt-2">
|
||||
<el-pagination
|
||||
v-if="!isEmpty(tableData)"
|
||||
v-model:current="current"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
size="small"
|
||||
showSizeChanger
|
||||
@change="paginationChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="pos-table">
|
||||
<el-table
|
||||
ref="selectedTable"
|
||||
size="small"
|
||||
:columns="selectedCommons"
|
||||
:datel-source="selectedData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="selectedTableListLoading"
|
||||
bordered
|
||||
>
|
||||
<template #title>
|
||||
<span>已选择: {{ selectedData.length }}</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" danger size="small" @click="delAllRecord">全部移除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" danger size="small" @click="delRecord(record)">移除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="posSelectorPlus">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { remove, isEmpty } from 'lodash-es'
|
||||
import { ref } from 'vue'
|
||||
// 弹窗是否打开
|
||||
const visible = ref(false)
|
||||
// 主表格common
|
||||
const commons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '职位名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'category'
|
||||
}
|
||||
]
|
||||
// 选中表格的表格common
|
||||
const selectedCommons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '职位名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
}
|
||||
]
|
||||
// 主表格的ref 名称
|
||||
const table = ref()
|
||||
// 选中表格的ref 名称
|
||||
const selectedTable = ref()
|
||||
const tableRecordNum = ref()
|
||||
const searchFormState = ref({})
|
||||
const searchFormRef = ref()
|
||||
const cardLoading = ref(true)
|
||||
const pageLoading = ref(false)
|
||||
const selectedTableListLoading = ref(false)
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
// 获取机构树数据
|
||||
const treeData = ref()
|
||||
// 默认展开二级树的节点id
|
||||
const defaultExpandedKeys = ref([])
|
||||
const emit = defineEmits({ onBack: null })
|
||||
const tableData = ref([])
|
||||
const selectedData = ref([])
|
||||
const recordIds = ref()
|
||||
const props = defineProps(['posPageApi', 'orgTreeApi', 'radioModel', 'dataIsConverterFlw', 'checkedPosListApi'])
|
||||
// 是否是单选
|
||||
const radioModel = props.radioModel || false
|
||||
// 数据是否转换成工作流格式
|
||||
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||
// 分页相关
|
||||
const current = ref(0) // 当前页数
|
||||
const pageSize = ref(20) // 每页条数
|
||||
const total = ref(0) // 数据总数
|
||||
|
||||
// 打开弹框
|
||||
const showPosPlusModal = (ids) => {
|
||||
visible.value = true
|
||||
if (dataIsConverterFlw) {
|
||||
ids = goDataConverter(ids)
|
||||
}
|
||||
recordIds.value = ids
|
||||
// 获取机构树
|
||||
if (props.orgTreeApi) {
|
||||
// 获取机构树
|
||||
props.orgTreeApi().then((data) => {
|
||||
cardLoading.value = false
|
||||
if (data !== null) {
|
||||
treeData.value = data
|
||||
// 默认展开2级
|
||||
treeData.value.forEach((item) => {
|
||||
// 因为0的顶级
|
||||
if (item.parentId === '0') {
|
||||
defaultExpandedKeys.value.push(item.id)
|
||||
// 取到下级ID
|
||||
if (item.children) {
|
||||
item.children.forEach((items) => {
|
||||
defaultExpandedKeys.value.push(items.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
searchFormState.value.size = pageSize.value
|
||||
loadData()
|
||||
if (props.checkedPosListApi) {
|
||||
if (isEmpty(recordIds.value)) {
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
idList: recordIds.value
|
||||
}
|
||||
selectedTableListLoading.value = true
|
||||
props
|
||||
.checkedPosListApi(param)
|
||||
.then((data) => {
|
||||
selectedData.value = data
|
||||
})
|
||||
.finally(() => {
|
||||
selectedTableListLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// 查询主表格数据
|
||||
const loadData = () => {
|
||||
pageLoading.value = true
|
||||
props
|
||||
.posPageApi(searchFormState.value)
|
||||
.then((data) => {
|
||||
current.value = data.current
|
||||
total.value = data.total
|
||||
// 重置、赋值
|
||||
tableData.value = []
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = data.records
|
||||
if (data.records) {
|
||||
tableRecordNum.value = data.records.length
|
||||
} else {
|
||||
tableRecordNum.value = 0
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
}
|
||||
// pageSize改变回调分页事件
|
||||
const paginationChange = (page, pageSize) => {
|
||||
searchFormState.value.current = page
|
||||
searchFormState.value.size = pageSize
|
||||
loadData()
|
||||
}
|
||||
const judge = () => {
|
||||
if (radioModel && selectedData.value.length > 0) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 添加记录
|
||||
const addRecord = (record) => {
|
||||
if (!judge()) {
|
||||
message.warning('只可选择一条')
|
||||
return
|
||||
}
|
||||
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||
if (selectedRecord.length === 0) {
|
||||
selectedData.value.push(record)
|
||||
} else {
|
||||
message.warning('该记录已存在')
|
||||
}
|
||||
}
|
||||
// 添加全部
|
||||
const addAllPageRecord = () => {
|
||||
let newArray = selectedData.value.concat(tableData.value)
|
||||
let list = []
|
||||
for (let item1 of newArray) {
|
||||
let flag = true
|
||||
for (let item2 of list) {
|
||||
if (item1.id === item2.id) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
list.push(item1)
|
||||
}
|
||||
}
|
||||
selectedData.value = list
|
||||
}
|
||||
// 删减记录
|
||||
const delRecord = (record) => {
|
||||
remove(selectedData.value, (item) => item.id === record.id)
|
||||
}
|
||||
// 删减记录
|
||||
const delAllRecord = () => {
|
||||
selectedData.value = []
|
||||
}
|
||||
// 点击树查询
|
||||
const treeSelect = (selectedKeys) => {
|
||||
searchFormState.value.current = 0
|
||||
if (selectedKeys.length > 0) {
|
||||
searchFormState.value.orgId = selectedKeys.toString()
|
||||
} else {
|
||||
delete searchFormState.value.orgId
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
// 确定
|
||||
const handleOk = () => {
|
||||
const value = []
|
||||
selectedData.value.forEach((item) => {
|
||||
const obj = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
account: item.account
|
||||
}
|
||||
value.push(obj)
|
||||
})
|
||||
// 判断是否做数据的转换为工作流需要的
|
||||
if (dataIsConverterFlw) {
|
||||
emit('onBack', outDataConverter(value))
|
||||
} else {
|
||||
emit('onBack', value)
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
delete searchFormState.value.searchKey
|
||||
loadData()
|
||||
}
|
||||
const handleClose = () => {
|
||||
searchFormState.value = {}
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = []
|
||||
current.value = 0
|
||||
pageSize.value = 20
|
||||
total.value = 0
|
||||
selectedData.value = []
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 数据进入后转换
|
||||
const goDataConverter = (data) => {
|
||||
const resultData = []
|
||||
if (data.length > 0) {
|
||||
const values = data[0].value.split(',')
|
||||
if (JSON.stringify(values) !== '[""]') {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
resultData.push(values[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultData
|
||||
}
|
||||
// 数据出口转换器
|
||||
const outDataConverter = (data) => {
|
||||
const obj = {}
|
||||
let label = ''
|
||||
let value = ''
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data.length === i + 1) {
|
||||
label = label + data[i].name
|
||||
value = value + data[i].id
|
||||
} else {
|
||||
label = label + data[i].name + ','
|
||||
value = value + data[i].id + ','
|
||||
}
|
||||
}
|
||||
obj.key = 'POSITION'
|
||||
obj.label = label
|
||||
obj.value = value
|
||||
obj.extJson = ''
|
||||
return obj
|
||||
}
|
||||
defineExpose({
|
||||
showPosPlusModal
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.selectorTreeDiv {
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
.cardTag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.primarySele {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.pos-table {
|
||||
overflow: auto;
|
||||
max-height: 450px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,449 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="角色选择"
|
||||
:width="1000"
|
||||
:mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleClose"
|
||||
>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="7">
|
||||
<el-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||
<el-tree
|
||||
v-if="treeData"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
@select="treeSelect"
|
||||
>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<div class="table-operator" style="margin-bottom: 10px">
|
||||
<el-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item name="searchKey">
|
||||
<el-input v-model:value="searchFormState.searchKey" placeholder="请输入角色名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" class="primarySele" @click="loadData()"> 查询 </el-button>
|
||||
<el-button class="snowy-buttom-left" @click="() => reset()"> 重置 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="role-table">
|
||||
<el-table
|
||||
ref="table"
|
||||
size="small"
|
||||
:columns="commons"
|
||||
:data="tableData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="pageLoading"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #title>
|
||||
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'category'">
|
||||
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="mt-2">
|
||||
<el-pagination
|
||||
v-if="!isEmpty(tableData)"
|
||||
v-model:current="current"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
size="small"
|
||||
showSizeChanger
|
||||
@change="paginationChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="role-table">
|
||||
<el-table
|
||||
ref="selectedTable"
|
||||
size="small"
|
||||
:columns="selectedCommons"
|
||||
:data-source="selectedData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="selectedTableListLoading"
|
||||
bordered
|
||||
>
|
||||
<template #title>
|
||||
<span>已选择: {{ selectedData.length }}</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" danger size="small" @click="delAllRecord">全部移除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" danger size="small" @click="delRecord(record)">移除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="roleSelectorPlus">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { remove, isEmpty } from 'lodash-es'
|
||||
import { ref } from 'vue'
|
||||
// 弹窗是否打开
|
||||
const visible = ref(false)
|
||||
// 主表格common
|
||||
const commons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '角色名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'category'
|
||||
}
|
||||
]
|
||||
// 选中表格的表格common
|
||||
const selectedCommons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '角色名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
}
|
||||
]
|
||||
// 主表格的ref 名称
|
||||
const table = ref()
|
||||
// 选中表格的ref 名称
|
||||
const selectedTable = ref()
|
||||
const tableRecordNum = ref()
|
||||
const searchFormState = ref({})
|
||||
const searchFormRef = ref()
|
||||
const cardLoading = ref(true)
|
||||
const pageLoading = ref(false)
|
||||
const selectedTableListLoading = ref(false)
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
// 获取机构树数据
|
||||
const treeData = ref()
|
||||
// 默认展开二级树的节点id
|
||||
const defaultExpandedKeys = ref([])
|
||||
const emit = defineEmits({ onBack: null })
|
||||
const tableData = ref([])
|
||||
const selectedData = ref([])
|
||||
const recordIds = ref()
|
||||
const props = defineProps({
|
||||
rolePageApi: {
|
||||
type: Function
|
||||
},
|
||||
orgTreeApi: {
|
||||
type: Function
|
||||
},
|
||||
// 是否是单选
|
||||
radioModel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 数据是否转换成工作流格式
|
||||
dataIsConverterFlw: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 是否展示‘全局’这个节点
|
||||
roleGlobal: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
checkedRoleListApi: {
|
||||
type: Function
|
||||
}
|
||||
})
|
||||
// 是否是单选
|
||||
const radioModel = props.radioModel
|
||||
// 数据是否转换成工作流格式
|
||||
const dataIsConverterFlw = props.dataIsConverterFlw
|
||||
// 是否展示‘全局’这个节点
|
||||
const roleGlobal = props.roleGlobal
|
||||
// 分页相关
|
||||
const current = ref(0) // 当前页数
|
||||
const pageSize = ref(20) // 每页条数
|
||||
const total = ref(0) // 数据总数
|
||||
|
||||
// 打开弹框
|
||||
const showRolePlusModal = (ids) => {
|
||||
visible.value = true
|
||||
if (dataIsConverterFlw) {
|
||||
ids = goDataConverter(ids)
|
||||
}
|
||||
recordIds.value = ids
|
||||
if (props.orgTreeApi) {
|
||||
// 获取机构树
|
||||
props.orgTreeApi().then((data) => {
|
||||
cardLoading.value = false
|
||||
if (data !== null) {
|
||||
treeData.value = data
|
||||
// 树中插入全局角色类型
|
||||
if (roleGlobal) {
|
||||
const globalRoleType = [
|
||||
{
|
||||
id: 'GLOBAL',
|
||||
parentId: '-1',
|
||||
name: '全局'
|
||||
}
|
||||
]
|
||||
treeData.value = globalRoleType.concat(data)
|
||||
}
|
||||
// 默认展开2级
|
||||
treeData.value.forEach((item) => {
|
||||
// 因为0的顶级
|
||||
if (item.parentId === '0') {
|
||||
defaultExpandedKeys.value.push(item.id)
|
||||
// 取到下级ID
|
||||
if (item.children) {
|
||||
item.children.forEach((items) => {
|
||||
defaultExpandedKeys.value.push(items.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
searchFormState.value.size = pageSize.value
|
||||
loadData()
|
||||
if (props.checkedRoleListApi) {
|
||||
if (isEmpty(recordIds.value)) {
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
idList: recordIds.value
|
||||
}
|
||||
selectedTableListLoading.value = true
|
||||
props
|
||||
.checkedRoleListApi(param)
|
||||
.then((data) => {
|
||||
selectedData.value = data
|
||||
})
|
||||
.finally(() => {
|
||||
selectedTableListLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// 查询主表格数据
|
||||
const loadData = () => {
|
||||
// 如果不是用全局的,我们每次查询加上这个条件
|
||||
if (!roleGlobal) {
|
||||
searchFormState.value.category = 'ORG'
|
||||
}
|
||||
pageLoading.value = true
|
||||
props
|
||||
.rolePageApi(searchFormState.value)
|
||||
.then((data) => {
|
||||
current.value = data.current
|
||||
total.value = data.total
|
||||
// 重置、赋值
|
||||
tableData.value = []
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = data.records
|
||||
if (data.records) {
|
||||
tableRecordNum.value = data.records.length
|
||||
} else {
|
||||
tableRecordNum.value = 0
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
}
|
||||
// pageSize改变回调分页事件
|
||||
const paginationChange = (page, pageSize) => {
|
||||
searchFormState.value.current = page
|
||||
searchFormState.value.size = pageSize
|
||||
loadData()
|
||||
}
|
||||
const judge = () => {
|
||||
if (radioModel && selectedData.value.length > 0) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 添加记录
|
||||
const addRecord = (record) => {
|
||||
if (!judge()) {
|
||||
message.warning('只可选择一条')
|
||||
return
|
||||
}
|
||||
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||
if (selectedRecord.length === 0) {
|
||||
selectedData.value.push(record)
|
||||
} else {
|
||||
message.warning('该记录已存在')
|
||||
}
|
||||
}
|
||||
// 添加全部
|
||||
const addAllPageRecord = () => {
|
||||
let newArray = selectedData.value.concat(tableData.value)
|
||||
let list = []
|
||||
for (let item1 of newArray) {
|
||||
let flag = true
|
||||
for (let item2 of list) {
|
||||
if (item1.id === item2.id) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
list.push(item1)
|
||||
}
|
||||
}
|
||||
selectedData.value = list
|
||||
}
|
||||
// 删减记录
|
||||
const delRecord = (record) => {
|
||||
remove(selectedData.value, (item) => item.id === record.id)
|
||||
}
|
||||
// 删减记录
|
||||
const delAllRecord = () => {
|
||||
selectedData.value = []
|
||||
}
|
||||
// 点击树查询
|
||||
const treeSelect = (selectedKeys) => {
|
||||
searchFormState.value.current = 0
|
||||
if (selectedKeys.length > 0) {
|
||||
if (selectedKeys[0] === 'GLOBAL') {
|
||||
searchFormState.value.category = selectedKeys[0]
|
||||
delete searchFormState.value.orgId
|
||||
} else {
|
||||
searchFormState.value.orgId = selectedKeys.toString()
|
||||
delete searchFormState.value.category
|
||||
}
|
||||
} else {
|
||||
delete searchFormState.value.category
|
||||
delete searchFormState.value.orgId
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
// 确定
|
||||
const handleOk = () => {
|
||||
const value = []
|
||||
selectedData.value.forEach((item) => {
|
||||
const obj = {
|
||||
id: item.id,
|
||||
name: item.name
|
||||
}
|
||||
value.push(obj)
|
||||
})
|
||||
// 判断是否做数据的转换为工作流需要的
|
||||
if (dataIsConverterFlw) {
|
||||
emit('onBack', outDataConverter(value))
|
||||
} else {
|
||||
emit('onBack', value)
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
delete searchFormState.value.searchKey
|
||||
loadData()
|
||||
}
|
||||
const handleClose = () => {
|
||||
searchFormState.value = {}
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = []
|
||||
current.value = 0
|
||||
pageSize.value = 20
|
||||
total.value = 0
|
||||
selectedData.value = []
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 数据进入后转换
|
||||
const goDataConverter = (data) => {
|
||||
const resultData = []
|
||||
if (data.length > 0) {
|
||||
const values = data[0].value.split(',')
|
||||
if (JSON.stringify(values) !== '[""]') {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
resultData.push(values[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultData
|
||||
}
|
||||
// 数据出口转换器
|
||||
const outDataConverter = (data) => {
|
||||
const obj = {}
|
||||
let label = ''
|
||||
let value = ''
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data.length === i + 1) {
|
||||
label = label + data[i].name
|
||||
value = value + data[i].id
|
||||
} else {
|
||||
label = label + data[i].name + ','
|
||||
value = value + data[i].id + ','
|
||||
}
|
||||
}
|
||||
obj.key = 'ROLE'
|
||||
obj.label = label
|
||||
obj.value = value
|
||||
obj.extJson = ''
|
||||
return obj
|
||||
}
|
||||
defineExpose({
|
||||
showRolePlusModal
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.selectorTreeDiv {
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
.cardTag {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.primarySele {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.role-table {
|
||||
overflow: auto;
|
||||
max-height: 450px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,401 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="用户选择"
|
||||
:width="1000"
|
||||
:mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="handleClose"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="7">
|
||||
<el-card size="small" :loading="cardLoading" class="selectorTreeDiv">
|
||||
<el-tree
|
||||
v-if="treeData"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
@node-click="treeSelect"
|
||||
>
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<div class="table-operator" style="margin-bottom: 10px">
|
||||
<el-form ref="searchFormRef" name="advanced_search" class="ant-advanced-search-form" :model="searchFormState">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item name="searchKey">
|
||||
<el-input v-model:value="searchFormState.searchKey" placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" class="primarySele" @click="loadData()"> 查询 </el-button>
|
||||
<el-button class="snowy-buttom-left" @click="reset()"> 重置 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="user-table">
|
||||
<el-table
|
||||
ref="table"
|
||||
size="small"
|
||||
:columns="commons"
|
||||
:data-source="tableData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="pageLoading"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #title>
|
||||
<span>待选择列表 {{ tableRecordNum }} 条</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" size="small" @click="addAllPageRecord">添加当前数据</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" size="small" @click="addRecord(record)">添加</el-button>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'category'">
|
||||
{{ $TOOL.dictTypeData('ROLE_CATEGORY', record.category) }}
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="mt-2">
|
||||
<el-pagination
|
||||
v-if="!isEmpty(tableData)"
|
||||
v-model:current="current"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
size="small"
|
||||
showSizeChanger
|
||||
@change="paginationChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="user-table">
|
||||
<el-table
|
||||
ref="selectedTable"
|
||||
size="small"
|
||||
:columns="selectedCommons"
|
||||
:data-source="selectedData"
|
||||
:expand-row-by-click="true"
|
||||
:loading="selectedTableListLoading"
|
||||
bordered
|
||||
>
|
||||
<template #title>
|
||||
<span>已选择: {{ selectedData.length }}</span>
|
||||
<div v-if="!radioModel" style="float: right">
|
||||
<el-button type="dashed" danger size="small" @click="delAllRecord">全部移除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'action'">
|
||||
<el-button type="dashed" danger size="small" @click="delRecord(record)">移除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="userSelectorPlus">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { remove, isEmpty } from 'lodash-es'
|
||||
import { ref } from 'vue'
|
||||
// 弹窗是否打开
|
||||
const visible = ref(false)
|
||||
// 主表格common
|
||||
const commons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
dataIndex: 'account'
|
||||
}
|
||||
]
|
||||
// 选中表格的表格common
|
||||
const selectedCommons = [
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
align: 'center',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true
|
||||
}
|
||||
]
|
||||
// 主表格的ref 名称
|
||||
const table = ref()
|
||||
// 选中表格的ref 名称
|
||||
const selectedTable = ref()
|
||||
const tableRecordNum = ref()
|
||||
const searchFormState = ref({})
|
||||
const searchFormRef = ref()
|
||||
const cardLoading = ref(true)
|
||||
const pageLoading = ref(false)
|
||||
const selectedTableListLoading = ref(false)
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
// 获取机构树数据
|
||||
const treeData = ref()
|
||||
// 默认展开二级树的节点id
|
||||
const defaultExpandedKeys = ref([])
|
||||
const emit = defineEmits({ onBack: null })
|
||||
const tableData = ref([])
|
||||
const selectedData = ref([])
|
||||
const recordIds = ref()
|
||||
const props = defineProps(['radioModel', 'dataIsConverterFlw', 'orgTreeApi', 'userPageApi', 'checkedUserListApi'])
|
||||
// 是否是单选
|
||||
const radioModel = props.radioModel || false
|
||||
// 数据是否转换成工作流格式
|
||||
const dataIsConverterFlw = props.dataIsConverterFlw || false
|
||||
// 分页相关
|
||||
const current = ref(0) // 当前页数
|
||||
const pageSize = ref(20) // 每页条数
|
||||
const total = ref(0) // 数据总数
|
||||
|
||||
// 打开弹框
|
||||
const showUserPlusModal = (ids = []) => {
|
||||
visible.value = true
|
||||
if (dataIsConverterFlw) {
|
||||
ids = goDataConverter(ids)
|
||||
}
|
||||
recordIds.value = ids
|
||||
// 加载机构树
|
||||
if (props.orgTreeApi) {
|
||||
// 获取机构树
|
||||
props.orgTreeApi().then((data) => {
|
||||
cardLoading.value = false
|
||||
if (data !== null) {
|
||||
treeData.value = data.data
|
||||
// 默认展开2级
|
||||
treeData.value.forEach((item) => {
|
||||
// 因为0的顶级
|
||||
if (item.parentId === '0') {
|
||||
defaultExpandedKeys.value.push(item.id)
|
||||
// 取到下级ID
|
||||
if (item.children) {
|
||||
item.children.forEach((items) => {
|
||||
defaultExpandedKeys.value.push(items.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
searchFormState.value.size = pageSize.value
|
||||
loadData()
|
||||
if (props.checkedUserListApi) {
|
||||
if (isEmpty(recordIds.value)) {
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
idList: recordIds.value
|
||||
}
|
||||
selectedTableListLoading.value = true
|
||||
props
|
||||
.checkedUserListApi(param)
|
||||
.then((data) => {
|
||||
selectedData.value = data
|
||||
})
|
||||
.finally(() => {
|
||||
selectedTableListLoading.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
// 查询主表格数据
|
||||
const loadData = () => {
|
||||
pageLoading.value = true
|
||||
props
|
||||
.userPageApi(searchFormState.value)
|
||||
.then((data) => {
|
||||
current.value = data.current
|
||||
// pageSize.value = data.size
|
||||
total.value = data.total
|
||||
// 重置、赋值
|
||||
tableData.value = []
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = data.records
|
||||
if (data.records) {
|
||||
tableRecordNum.value = data.records.length
|
||||
} else {
|
||||
tableRecordNum.value = 0
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
pageLoading.value = false
|
||||
})
|
||||
}
|
||||
// pageSize改变回调分页事件
|
||||
const paginationChange = (page, pageSize) => {
|
||||
searchFormState.value.current = page
|
||||
searchFormState.value.size = pageSize
|
||||
loadData()
|
||||
}
|
||||
const judge = () => {
|
||||
if (radioModel && selectedData.value.length > 0) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 添加记录
|
||||
const addRecord = (record) => {
|
||||
if (!judge()) {
|
||||
message.warning('只可选择一条')
|
||||
return
|
||||
}
|
||||
const selectedRecord = selectedData.value.filter((item) => item.id === record.id)
|
||||
if (selectedRecord.length === 0) {
|
||||
selectedData.value.push(record)
|
||||
} else {
|
||||
message.warning('该记录已存在')
|
||||
}
|
||||
}
|
||||
// 添加全部
|
||||
const addAllPageRecord = () => {
|
||||
let newArray = selectedData.value.concat(tableData.value)
|
||||
let list = []
|
||||
for (let item1 of newArray) {
|
||||
let flag = true
|
||||
for (let item2 of list) {
|
||||
if (item1.id === item2.id) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
list.push(item1)
|
||||
}
|
||||
}
|
||||
selectedData.value = list
|
||||
}
|
||||
// 删减记录
|
||||
const delRecord = (record) => {
|
||||
remove(selectedData.value, (item) => item.id === record.id)
|
||||
}
|
||||
// 删减记录
|
||||
const delAllRecord = () => {
|
||||
selectedData.value = []
|
||||
}
|
||||
// 点击树查询
|
||||
const treeSelect = (selectedKeys) => {
|
||||
searchFormState.value.current = 0
|
||||
if (selectedKeys.length > 0) {
|
||||
searchFormState.value.orgId = selectedKeys.toString()
|
||||
} else {
|
||||
delete searchFormState.value.orgId
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
// 确定
|
||||
const handleOk = () => {
|
||||
const value = []
|
||||
selectedData.value.forEach((item) => {
|
||||
const obj = {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
account: item.account
|
||||
}
|
||||
value.push(obj)
|
||||
})
|
||||
// 判断是否做数据的转换为工作流需要的
|
||||
if (dataIsConverterFlw) {
|
||||
emit('onBack', outDataConverter(value))
|
||||
} else {
|
||||
emit('onBack', value)
|
||||
}
|
||||
handleClose()
|
||||
}
|
||||
// 重置
|
||||
const reset = () => {
|
||||
delete searchFormState.value.searchKey
|
||||
loadData()
|
||||
}
|
||||
const handleClose = () => {
|
||||
searchFormState.value = {}
|
||||
tableRecordNum.value = 0
|
||||
tableData.value = []
|
||||
current.value = 0
|
||||
pageSize.value = 20
|
||||
total.value = 0
|
||||
selectedData.value = []
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 数据进入后转换
|
||||
const goDataConverter = (data) => {
|
||||
const resultData = []
|
||||
if (data.length > 0) {
|
||||
const values = data[0].value.split(',')
|
||||
if (JSON.stringify(values) !== '[""]') {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
resultData.push(values[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultData
|
||||
}
|
||||
// 数据出口转换器
|
||||
const outDataConverter = (data) => {
|
||||
const obj = {}
|
||||
let label = ''
|
||||
let value = ''
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data.length === i + 1) {
|
||||
label = label + data[i].name
|
||||
value = value + data[i].id
|
||||
} else {
|
||||
label = label + data[i].name + ','
|
||||
value = value + data[i].id + ','
|
||||
}
|
||||
}
|
||||
obj.key = 'USER'
|
||||
obj.label = label
|
||||
obj.value = value
|
||||
obj.extJson = ''
|
||||
return obj
|
||||
}
|
||||
defineExpose({
|
||||
showUserPlusModal
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.selectorTreeDiv {
|
||||
max-height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
.cardTag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.primarySele {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.ant-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.user-table {
|
||||
overflow: auto;
|
||||
max-height: 450px;
|
||||
}
|
||||
</style>
|
||||
5
src/components/UploadFile/index.ts
Normal file
5
src/components/UploadFile/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import UploadImg from './src/UploadImg.vue'
|
||||
import UploadImgs from './src/UploadImgs.vue'
|
||||
import UploadFile from './src/UploadFile.vue'
|
||||
|
||||
export { UploadImg, UploadImgs, UploadFile }
|
||||
214
src/components/UploadFile/src/UploadFile.vue
Normal file
214
src/components/UploadFile/src/UploadFile.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div class='upload-file'>
|
||||
<el-upload
|
||||
ref='uploadRef'
|
||||
v-model:file-list='fileList'
|
||||
:action='uploadUrl'
|
||||
:auto-upload='autoUpload'
|
||||
:before-upload='beforeUpload'
|
||||
:disabled='disabled'
|
||||
:drag='drag'
|
||||
:http-request='httpRequest'
|
||||
:limit='props.limit'
|
||||
:multiple='props.limit > 1'
|
||||
:on-error='excelUploadError'
|
||||
:on-exceed='handleExceed'
|
||||
:on-preview='handlePreview'
|
||||
:on-remove='handleRemove'
|
||||
:on-success='handleFileSuccess'
|
||||
:show-file-list='true'
|
||||
class='upload-file-uploader'
|
||||
name='file'
|
||||
>
|
||||
<el-button v-if='!disabled' type='primary'>
|
||||
<UploadFilled />
|
||||
选取文件
|
||||
</el-button>
|
||||
<template v-if='isShowTip && !disabled' #tip>
|
||||
<div style='font-size: 8px'>
|
||||
大小不超过 <b style='color: #f56c6c'>{{ fileSize }}MB</b>
|
||||
</div>
|
||||
<div style='font-size: 8px'>
|
||||
格式为 <b style='color: #f56c6c'>{{ fileType.join('/') }}</b> 的文件
|
||||
</div>
|
||||
</template>
|
||||
<template #file='row'>
|
||||
<div class='flex items-center'>
|
||||
<span>{{ row.file.name }}</span>
|
||||
<div class='ml-10px'>
|
||||
<el-link
|
||||
:href='row.file.url'
|
||||
:underline='false'
|
||||
download
|
||||
target='_blank'
|
||||
type='primary'
|
||||
>
|
||||
下载
|
||||
</el-link>
|
||||
</div>
|
||||
<div class='ml-10px'>
|
||||
<el-button link type='danger' @click='handleRemove(row.file)'> 删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script lang='ts' setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
|
||||
import { isString } from '@/utils/is'
|
||||
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||
import { UploadFile } from 'element-plus/es/components/upload/src/upload'
|
||||
|
||||
defineOptions({ name: 'UploadFile' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
||||
limit: propTypes.number.def(5), // 数量限制
|
||||
autoUpload: propTypes.bool.def(true), // 自动上传
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
isShowTip: propTypes.bool.def(true), // 是否显示提示
|
||||
disabled: propTypes.bool.def(false) // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
})
|
||||
|
||||
// ========== 上传相关 ==========
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const uploadList = ref<UploadUserFile[]>([])
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
|
||||
// 文件上传之前判断
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
if (fileList.value.length >= props.limit) {
|
||||
message.error(`上传文件数量不能超过${props.limit}个!`)
|
||||
return false
|
||||
}
|
||||
let fileExtension = ''
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
const isImg = props.fileType.some((type: string) => {
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
return !!(fileExtension && fileExtension.indexOf(type) > -1)
|
||||
})
|
||||
const isLimit = file.size < props.fileSize * 1024 * 1024
|
||||
if (!isImg) {
|
||||
message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
|
||||
return false
|
||||
}
|
||||
if (!isLimit) {
|
||||
message.error(`上传文件大小不能超过${props.fileSize}MB!`)
|
||||
return false
|
||||
}
|
||||
message.success('正在上传文件,请稍候...')
|
||||
uploadNumber.value++
|
||||
}
|
||||
// 处理上传的文件发生变化
|
||||
// const handleFileChange = (uploadFile: UploadFile): void => {
|
||||
// uploadRef.value.data.path = uploadFile.name
|
||||
// }
|
||||
// 文件上传成功
|
||||
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
message.success('上传成功')
|
||||
// 删除自身
|
||||
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
|
||||
fileList.value.splice(index, 1)
|
||||
uploadList.value.push({ name: res.data, url: res.data })
|
||||
if (uploadList.value.length == uploadNumber.value) {
|
||||
fileList.value.push(...uploadList.value)
|
||||
uploadList.value = []
|
||||
uploadNumber.value = 0
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
}
|
||||
// 文件数超出提示
|
||||
const handleExceed: UploadProps['onExceed'] = (): void => {
|
||||
message.error(`上传文件数量不能超过${props.limit}个!`)
|
||||
}
|
||||
// 上传错误提示
|
||||
const excelUploadError: UploadProps['onError'] = (): void => {
|
||||
message.error('导入数据失败,请您重新上传!')
|
||||
}
|
||||
// 删除上传文件
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
const index = fileList.value.map((f) => f.name).indexOf(file.name)
|
||||
if (index > -1) {
|
||||
fileList.value.splice(index, 1)
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
}
|
||||
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
console.log(uploadFile)
|
||||
}
|
||||
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
|
||||
fileList.value = [] // 保障数据为空
|
||||
// 情况1:字符串
|
||||
if (isString(val)) {
|
||||
fileList.value.push(
|
||||
...val.split(',').map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
||||
)
|
||||
return
|
||||
}
|
||||
// 情况2:数组
|
||||
fileList.value.push(
|
||||
...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
||||
)
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
// 发送文件链接列表更新
|
||||
const emitUpdateModelValue = () => {
|
||||
// 情况1:数组结果
|
||||
let result: string | string[] = fileList.value.map((file) => file.url!)
|
||||
// 情况2:逗号分隔的字符串
|
||||
if (props.limit === 1 || isString(props.modelValue)) {
|
||||
result = result.join(',')
|
||||
}
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .el-upload-list__item) {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
line-height: 2;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
:deep(.el-upload-list__item-file-name) {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .ele-upload-list__item-content) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:deep(.ele-upload-list__item-content-action .el-link) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
273
src/components/UploadFile/src/UploadImg.vue
Normal file
273
src/components/UploadFile/src/UploadImg.vue
Normal file
@@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<div class='upload-box'>
|
||||
<el-upload
|
||||
:id='uuid'
|
||||
:accept="fileType.join(',')"
|
||||
:action='uploadUrl'
|
||||
:before-upload='beforeUpload'
|
||||
:class="['upload', drag ? 'no-border' : '']"
|
||||
:disabled='disabled'
|
||||
:drag='drag'
|
||||
:http-request='httpRequest'
|
||||
:multiple='false'
|
||||
:on-error='uploadError'
|
||||
:on-success='uploadSuccess'
|
||||
:show-file-list='false'
|
||||
>
|
||||
<template v-if='modelValue'>
|
||||
<img :src='modelValue' class='upload-image' />
|
||||
<div class='upload-handle' @click.stop>
|
||||
<div v-if='!disabled' class='handle-icon' @click='editImg'>
|
||||
<Edit />
|
||||
<span v-if='showBtnText'>{{ t('action.edit') }}</span>
|
||||
</div>
|
||||
<div class='handle-icon' @click='imagePreview(modelValue)'>
|
||||
<ZoomIn />
|
||||
<span v-if='showBtnText'>{{ t('action.detail') }}</span>
|
||||
</div>
|
||||
<div v-if='showDelete && !disabled' class='handle-icon' @click='deleteImg'>
|
||||
<Delete />
|
||||
<span v-if='showBtnText'>{{ t('action.del') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class='upload-empty'>
|
||||
<slot name='empty'>
|
||||
<Plus />
|
||||
<!-- <span>请上传图片</span> -->
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<div class='el-upload__tip'>
|
||||
<slot name='tip'></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { Plus,Edit,ZoomIn,Delete} from '@element-plus/icons-vue'
|
||||
import { generateUUID } from '@/utils'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
|
||||
defineOptions({ name: 'UploadImg' })
|
||||
|
||||
type FileTypes =
|
||||
| 'image/apng'
|
||||
| 'image/bmp'
|
||||
| 'image/gif'
|
||||
| 'image/jpeg'
|
||||
| 'image/pjpeg'
|
||||
| 'image/png'
|
||||
| 'image/svg+xml'
|
||||
| 'image/tiff'
|
||||
| 'image/webp'
|
||||
| 'image/x-icon'
|
||||
|
||||
// 接受父组件参数
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.string.def(''),
|
||||
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
|
||||
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
||||
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
||||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
showDelete: propTypes.bool.def(true), // 是否显示删除按钮
|
||||
showBtnText: propTypes.bool.def(true) // 是否显示按钮文字
|
||||
})
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
// 生成组件唯一id
|
||||
const uuid = ref('id-' + generateUUID())
|
||||
// 查看图片
|
||||
const imagePreview = (imgUrl: string) => {
|
||||
createImageViewer({
|
||||
zIndex: 9999999,
|
||||
urlList: [imgUrl]
|
||||
})
|
||||
}
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const deleteImg = () => {
|
||||
emit('update:modelValue', '')
|
||||
}
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
|
||||
const editImg = () => {
|
||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
||||
dom && dom.dispatchEvent(new MouseEvent('click'))
|
||||
}
|
||||
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
|
||||
const imgType = props.fileType
|
||||
if (!imgType.includes(rawFile.type as FileTypes))
|
||||
message.notifyWarning('上传图片不符合所需的格式!')
|
||||
if (!imgSize) message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`)
|
||||
return imgType.includes(rawFile.type as FileTypes) && imgSize
|
||||
}
|
||||
|
||||
// 图片上传成功提示
|
||||
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
message.success('上传成功')
|
||||
emit('update:modelValue', res.data)
|
||||
}
|
||||
|
||||
// 图片上传错误提示
|
||||
const uploadError = () => {
|
||||
message.notifyError('图片上传失败,请您重新上传!')
|
||||
}
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
.is-error {
|
||||
.upload {
|
||||
:deep(.el-upload),
|
||||
:deep(.el-upload-dragger) {
|
||||
border: 1px dashed var(--el-color-danger) !important;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.disabled) {
|
||||
.el-upload,
|
||||
.el-upload-dragger {
|
||||
cursor: not-allowed !important;
|
||||
background: var(--el-disabled-bg-color);
|
||||
border: 1px dashed var(--el-border-color-darker) !important;
|
||||
|
||||
&:hover {
|
||||
border: 1px dashed var(--el-border-color-darker) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
.no-border {
|
||||
:deep(.el-upload) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.upload) {
|
||||
.el-upload {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: v-bind(width);
|
||||
height: v-bind(height);
|
||||
overflow: hidden;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: v-bind(borderradius);
|
||||
transition: var(--el-transition-duration-fast);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
|
||||
.upload-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload-dragger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: v-bind(borderradius);
|
||||
|
||||
&:hover {
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload-dragger.is-dragover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 2px dashed var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.upload-empty {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
color: var(--el-color-info);
|
||||
|
||||
.el-icon {
|
||||
font-size: 28px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
opacity: 0;
|
||||
box-sizing: border-box;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.handle-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6%;
|
||||
color: aliceblue;
|
||||
|
||||
.el-icon {
|
||||
margin-bottom: 40%;
|
||||
font-size: 130%;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 85%;
|
||||
line-height: 85%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
324
src/components/UploadFile/src/UploadImgs.vue
Normal file
324
src/components/UploadFile/src/UploadImgs.vue
Normal file
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<div class='upload-box'>
|
||||
<el-upload
|
||||
v-model:file-list='fileList'
|
||||
:accept="fileType.join(',')"
|
||||
:action='uploadUrl'
|
||||
:before-upload='beforeUpload'
|
||||
:class="['upload', drag ? 'no-border' : '']"
|
||||
:disabled='disabled'
|
||||
:drag='drag'
|
||||
:http-request='httpRequest'
|
||||
:limit='limit'
|
||||
:multiple='true'
|
||||
:on-error='uploadError'
|
||||
:on-exceed='handleExceed'
|
||||
:on-success='uploadSuccess'
|
||||
list-type='picture-card'
|
||||
>
|
||||
<div class='upload-empty'>
|
||||
<slot name='empty'>
|
||||
<Plus />
|
||||
<!-- <span>请上传图片</span> -->
|
||||
</slot>
|
||||
</div>
|
||||
<template #file='{ file }'>
|
||||
<img :src='file.url' class='upload-image' />
|
||||
<div class='upload-handle' @click.stop>
|
||||
<div class='handle-icon' @click='handlePictureCardPreview(file)'>
|
||||
<ZoomIn />
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div v-if='!disabled' class='handle-icon' @click='handleRemove(file)'>
|
||||
<Delete />
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<div class='el-upload__tip'>
|
||||
<slot name='tip'></slot>
|
||||
</div>
|
||||
<el-image-viewer
|
||||
v-if='imgViewVisible'
|
||||
:url-list='[viewImageUrl]'
|
||||
@close='imgViewVisible = false'
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang='ts' setup>
|
||||
import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
|
||||
import { ElNotification } from 'element-plus'
|
||||
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useUpload } from '@/components/UploadFile/src/useUpload'
|
||||
import { Plus, Edit, ZoomIn, Delete } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({ name: 'UploadImgs' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
type FileTypes =
|
||||
| 'image/apng'
|
||||
| 'image/bmp'
|
||||
| 'image/gif'
|
||||
| 'image/jpeg'
|
||||
| 'image/pjpeg'
|
||||
| 'image/png'
|
||||
| 'image/svg+xml'
|
||||
| 'image/tiff'
|
||||
| 'image/webp'
|
||||
| 'image/x-icon'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
limit: propTypes.number.def(5), // 最大图片上传数 ==> 非必传(默认为 5张)
|
||||
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
|
||||
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
||||
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
||||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||
borderradius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
})
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
const uploadList = ref<UploadUserFile[]>([])
|
||||
/**
|
||||
* @description 文件上传之前判断
|
||||
* @param rawFile 上传的文件
|
||||
* */
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
|
||||
const imgType = props.fileType
|
||||
if (!imgType.includes(rawFile.type as FileTypes))
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: '上传图片不符合所需的格式!',
|
||||
type: 'warning'
|
||||
})
|
||||
if (!imgSize)
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: `上传图片大小不能超过 ${props.fileSize}M!`,
|
||||
type: 'warning'
|
||||
})
|
||||
uploadNumber.value++
|
||||
return imgType.includes(rawFile.type as FileTypes) && imgSize
|
||||
}
|
||||
|
||||
// 图片上传成功
|
||||
interface UploadEmits {
|
||||
(e: 'update:modelValue', value: string[]): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<UploadEmits>()
|
||||
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
message.success('上传成功')
|
||||
// 删除自身
|
||||
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
|
||||
fileList.value.splice(index, 1)
|
||||
uploadList.value.push({ name: res.data, url: res.data })
|
||||
if (uploadList.value.length == uploadNumber.value) {
|
||||
fileList.value.push(...uploadList.value)
|
||||
uploadList.value = []
|
||||
uploadNumber.value = 0
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
}
|
||||
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
|
||||
fileList.value = [] // 保障数据为空
|
||||
fileList.value.push(
|
||||
...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
|
||||
)
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
// 发送图片链接列表更新
|
||||
const emitUpdateModelValue = () => {
|
||||
let result: string[] = fileList.value.map((file) => file.url!)
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
// 删除图片
|
||||
const handleRemove = (uploadFile: UploadFile) => {
|
||||
fileList.value = fileList.value.filter(
|
||||
(item) => item.url !== uploadFile.url || item.name !== uploadFile.name
|
||||
)
|
||||
emit(
|
||||
'update:modelValue',
|
||||
fileList.value.map((file) => file.url!)
|
||||
)
|
||||
}
|
||||
|
||||
// 图片上传错误提示
|
||||
const uploadError = () => {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: '图片上传失败,请您重新上传!',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
|
||||
// 文件数超出提示
|
||||
const handleExceed = () => {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: `当前最多只能上传 ${props.limit} 张图片,请移除后上传!`,
|
||||
type: 'warning'
|
||||
})
|
||||
}
|
||||
|
||||
// 图片预览
|
||||
const viewImageUrl = ref('')
|
||||
const imgViewVisible = ref(false)
|
||||
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
viewImageUrl.value = uploadFile.url!
|
||||
imgViewVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.is-error {
|
||||
.upload {
|
||||
:deep(.el-upload--picture-card),
|
||||
:deep(.el-upload-dragger) {
|
||||
border: 1px dashed var(--el-color-danger) !important;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.disabled) {
|
||||
.el-upload--picture-card,
|
||||
.el-upload-dragger {
|
||||
cursor: not-allowed;
|
||||
background: var(--el-disabled-bg-color) !important;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-border-color-darker) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-box {
|
||||
.no-border {
|
||||
:deep(.el-upload--picture-card) {
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.upload) {
|
||||
.el-upload-dragger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: v-bind(borderradius);
|
||||
|
||||
&:hover {
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload-dragger.is-dragover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 2px dashed var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
.el-upload-list__item,
|
||||
.el-upload--picture-card {
|
||||
width: v-bind(width);
|
||||
height: v-bind(height);
|
||||
background-color: transparent;
|
||||
border-radius: v-bind(borderradius);
|
||||
}
|
||||
|
||||
.upload-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.upload-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
opacity: 0;
|
||||
box-sizing: border-box;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.handle-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6%;
|
||||
color: aliceblue;
|
||||
|
||||
.el-icon {
|
||||
margin-bottom: 15%;
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload-list__item {
|
||||
&:hover {
|
||||
.upload-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
line-height: 30px;
|
||||
color: var(--el-color-info);
|
||||
|
||||
.el-icon {
|
||||
font-size: 28px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload__tip {
|
||||
line-height: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
99
src/components/UploadFile/src/useUpload.ts
Normal file
99
src/components/UploadFile/src/useUpload.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// import * as FileApi from '@/api/infra/file'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||
import axios from 'axios'
|
||||
import { uploadFile } from '@/api/system-boot/file'
|
||||
|
||||
export const useUpload = () => {
|
||||
// 后端上传地址
|
||||
const uploadUrl = 'import.meta.env.VITE_UPLOAD_URL'
|
||||
// // 是否使用前端直连上传
|
||||
// const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
|
||||
const isClientUpload = false
|
||||
// 重写ElUpload上传方法
|
||||
const httpRequest = async (options: UploadRequestOptions) => {
|
||||
// 模式一:前端上传
|
||||
// if (isClientUpload) {
|
||||
// // 1.1 生成文件名称
|
||||
// const fileName = await generateFileName(options.file)
|
||||
// // 1.2 获取文件预签名地址
|
||||
// const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
|
||||
// // 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
|
||||
// return axios.put(presignedInfo.uploadUrl, options.file, {
|
||||
// headers: {
|
||||
// 'Content-Type': options.file.type,
|
||||
// }
|
||||
// }).then(() => {
|
||||
// // 1.4. 记录文件信息到后端(异步)
|
||||
// createFile(presignedInfo, fileName, options.file)
|
||||
// // 通知成功,数据格式保持与后端上传的返回结果一致
|
||||
// return { data: presignedInfo.url }
|
||||
// })
|
||||
// } else {
|
||||
// 模式二:后端上传
|
||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||
return new Promise((resolve, reject) => {
|
||||
uploadFile({ file: options.file, path: 'backUpFile' })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
// }
|
||||
}
|
||||
|
||||
return {
|
||||
uploadUrl,
|
||||
httpRequest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件信息
|
||||
* @param vo 文件预签名信息
|
||||
* @param name 文件名称
|
||||
* @param file 文件
|
||||
*/
|
||||
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
|
||||
const fileVo = {
|
||||
configId: vo.configId,
|
||||
url: vo.url,
|
||||
path: name,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size
|
||||
}
|
||||
FileApi.createFile(fileVo)
|
||||
return fileVo
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名称(使用算法SHA256)
|
||||
* @param file 要上传的文件
|
||||
*/
|
||||
async function generateFileName(file: UploadRawFile) {
|
||||
// 读取文件内容
|
||||
const data = await file.arrayBuffer()
|
||||
const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||
// 计算SHA256
|
||||
const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||
// 拼接后缀
|
||||
const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||
return `${sha256}${ext}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传类型
|
||||
*/
|
||||
enum UPLOAD_TYPE {
|
||||
// 客户端直接上传(只支持S3服务)
|
||||
CLIENT = 'client',
|
||||
// 客户端发送到后端上传
|
||||
SERVER = 'server'
|
||||
}
|
||||
4
src/components/XButton/index.ts
Normal file
4
src/components/XButton/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import XButton from './src/XButton.vue'
|
||||
import XTextButton from './src/XTextButton.vue'
|
||||
|
||||
export { XButton, XTextButton }
|
||||
50
src/components/XButton/src/XButton.vue
Normal file
50
src/components/XButton/src/XButton.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script lang="ts" setup>
|
||||
import { PropType,computed,useAttrs } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
defineOptions({ name: 'XButton' })
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
loading: propTypes.bool.def(false),
|
||||
preIcon: propTypes.string.def(''),
|
||||
postIcon: propTypes.string.def(''),
|
||||
title: propTypes.string.def(''),
|
||||
type: propTypes.oneOf(['', 'primary', 'success', 'warning', 'danger', 'info']).def(''),
|
||||
link: propTypes.bool.def(false),
|
||||
circle: propTypes.bool.def(false),
|
||||
round: propTypes.bool.def(false),
|
||||
plain: propTypes.bool.def(false),
|
||||
onClick: { type: Function as PropType<(...args) => any>, default: null }
|
||||
})
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['title', 'preIcon', 'postIcon', 'onClick']
|
||||
const attrs = useAttrs()
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-button v-bind="getBindValue" @click="onClick">
|
||||
<Icon v-if="preIcon" :icon="preIcon" class="mr-1px" />
|
||||
{{ title ? title : '' }}
|
||||
<Icon v-if="postIcon" :icon="postIcon" class="mr-1px" />
|
||||
</el-button>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-button.is-text) {
|
||||
padding: 8px 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:deep(.el-button.is-link) {
|
||||
padding: 8px 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
49
src/components/XButton/src/XTextButton.vue
Normal file
49
src/components/XButton/src/XTextButton.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { PropType } from 'vue'
|
||||
|
||||
defineOptions({ name: 'XTextButton' })
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.bool.def(false),
|
||||
loading: propTypes.bool.def(false),
|
||||
preIcon: propTypes.string.def(''),
|
||||
postIcon: propTypes.string.def(''),
|
||||
title: propTypes.string.def(''),
|
||||
type: propTypes.oneOf(['', 'primary', 'success', 'warning', 'danger', 'info']).def('primary'),
|
||||
circle: propTypes.bool.def(false),
|
||||
round: propTypes.bool.def(false),
|
||||
plain: propTypes.bool.def(false),
|
||||
onClick: { type: Function as PropType<(...args) => any>, default: null }
|
||||
})
|
||||
const getBindValue = computed(() => {
|
||||
const delArr: string[] = ['title', 'preIcon', 'postIcon', 'onClick']
|
||||
const attrs = useAttrs()
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-button link v-bind="getBindValue" @click="onClick">
|
||||
<Icon v-if="preIcon" :icon="preIcon" class="mr-1px" />
|
||||
{{ title ? title : '' }}
|
||||
<Icon v-if="postIcon" :icon="postIcon" class="mr-1px" />
|
||||
</el-button>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-button.is-text) {
|
||||
padding: 8px 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
:deep(.el-button.is-link) {
|
||||
padding: 8px 4px;
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<template>
|
||||
<start-events v-if="childNode.type === 'startEvent'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||
<start-tasks v-if="childNode.type === 'startTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||
<user-tasks v-if="childNode.type === 'userTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||
<service-tasks v-if="childNode.type === 'serviceTask'" v-model="childNode" :currentActivityId="currentActivityId" />
|
||||
<exclusive-gateways v-if="childNode.type === 'exclusiveGateway'" v-model="childNode" :currentActivityId="currentActivityId">
|
||||
<template #default="slot">
|
||||
<c-node-wrap v-if="slot.node" v-model="slot.node.childNode" :currentActivityId="currentActivityId" />
|
||||
</template>
|
||||
</exclusive-gateways>
|
||||
<parallel-gateways v-if="childNode.type === 'parallelGateway'" v-model="childNode" :currentActivityId="currentActivityId">
|
||||
<template #default="slot">
|
||||
<c-node-wrap v-if="slot.node" v-model="slot.node.childNode" :currentActivityId="currentActivityId" />
|
||||
</template>
|
||||
</parallel-gateways>
|
||||
<c-node-wrap v-if="childNode.childNode" v-model="childNode.childNode" :currentActivityId="currentActivityId" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import startEvents from './nodes/cStartEvent.vue'
|
||||
import startTasks from './nodes/cStartTask.vue'
|
||||
import userTasks from './nodes/cUserTask.vue'
|
||||
import exclusiveGateways from './nodes/cExclusiveGateway.vue'
|
||||
import parallelGateways from './nodes/cParallelGateway.vue'
|
||||
import serviceTasks from './nodes/cServiceTask.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
startEvents,
|
||||
startTasks,
|
||||
userTasks,
|
||||
exclusiveGateways,
|
||||
parallelGateways,
|
||||
serviceTasks
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.childNode = val
|
||||
},
|
||||
childNode(val) {
|
||||
this.$emit('update:modelValue', val)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div class="workflow-design">
|
||||
<div class="workflow-design-btns">
|
||||
<a-space>
|
||||
<a-tooltip>
|
||||
<template #title>放大</template>
|
||||
<a-button @click="handleZoom(true)" :disabled="zoom > 2">
|
||||
<template #icon><plus-outlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template #title>缩小</template>
|
||||
<a-button @click="handleZoom(false)" :disabled="zoom < 1">
|
||||
<template #icon><minus-outlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
<div id="div1">
|
||||
<div class="box-scale" :style="style">
|
||||
<node-wrap-chart v-if="childNode" v-model="childNode.childNode" :currentActivityId="currentActivityId" />
|
||||
<div class="end-node">
|
||||
<div class="end-node-circle"></div>
|
||||
<div class="end-node-text">流程结束</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import nodeWrapChart from '@/components/XnWorkflow/chart/cNodeWrap.vue'
|
||||
import FullscreenPreviewHelper from './zoom_helper'
|
||||
|
||||
const FullScreenRightSpace = 20
|
||||
export default {
|
||||
components: {
|
||||
nodeWrapChart
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: this.modelValue,
|
||||
style: {},
|
||||
zoom: 1
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
modelValue(val) {
|
||||
this.childNode = val
|
||||
},
|
||||
childNode(val) {
|
||||
this.$emit('update:modelValue', val)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleGetStyle(zoom) {
|
||||
return FullscreenPreviewHelper.getZoomStyles(zoom, 1, 0, FullScreenRightSpace)
|
||||
},
|
||||
handleZoom(zoomIn) {
|
||||
const zoom = this.zoom
|
||||
const zoomData = FullscreenPreviewHelper.getZoomData(zoomIn, zoom)
|
||||
const style = this.handleGetStyle(zoomData.zoom)
|
||||
this.style = style
|
||||
this.zoom = zoomData.zoom
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '../flowIndex.less';
|
||||
.workflow-design-btns {
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div class="add-node-btn-box">
|
||||
<div class="add-node-btn"></div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,141 +0,0 @@
|
||||
<template>
|
||||
<div class="branch-wrap">
|
||||
<div class="branch-box-wrap">
|
||||
<div class="branch-box" style="margin-top: 0px">
|
||||
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||
<div class="condition-node">
|
||||
<div class="condition-node-box">
|
||||
<div class="auto-judge">
|
||||
<div class="title">
|
||||
<span class="node-title">{{ item.title }}</span>
|
||||
<span class="priority-title">优先级{{ item.properties.configInfo.priorityLevel }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span v-if="toText(childNode, index)">{{ toText(childNode, index) }}</span>
|
||||
<span v-else class="placeholder">请设置条件</span>
|
||||
</div>
|
||||
</div>
|
||||
<add-nodes v-model="item.childNode" />
|
||||
</div>
|
||||
</div>
|
||||
<slot v-if="item.childNode" :node="item"></slot>
|
||||
<div v-if="index == 0" class="top-left-cover-line"></div>
|
||||
<div v-if="index == 0" class="bottom-left-cover-line"></div>
|
||||
<div v-if="index == childNode.conditionNodeList.length - 1" class="top-right-cover-line"></div>
|
||||
<div v-if="index == childNode.conditionNodeList.length - 1" class="bottom-right-cover-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<add-nodes v-model="childNode.childNode" :parent-data="childNode" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNodes from './cAddNode.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addNodes
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {},
|
||||
index: 0,
|
||||
operatorList: [
|
||||
{
|
||||
label: '等于',
|
||||
value: '=='
|
||||
},
|
||||
{
|
||||
label: '不等于',
|
||||
value: '!='
|
||||
},
|
||||
{
|
||||
label: '大于',
|
||||
value: '>'
|
||||
},
|
||||
{
|
||||
label: '大于等于',
|
||||
value: '>='
|
||||
},
|
||||
{
|
||||
label: '小于',
|
||||
value: '<'
|
||||
},
|
||||
{
|
||||
label: '小于等于',
|
||||
value: '<='
|
||||
},
|
||||
{
|
||||
label: '包含',
|
||||
value: 'include'
|
||||
},
|
||||
{
|
||||
label: '不包含',
|
||||
value: 'notInclude'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
toText(childNode, index) {
|
||||
const conditionList = childNode.conditionNodeList[index].properties.conditionInfo
|
||||
const priorityLevel = childNode.conditionNodeList[index].properties.configInfo.priorityLevel
|
||||
const len = this.childNode.conditionNodeList.length
|
||||
const priorityLevelMax = this.childNode.conditionNodeList[len - 1].properties.configInfo.priorityLevel
|
||||
|
||||
if (JSON.stringify(conditionList) !== undefined && conditionList.length > 0) {
|
||||
let text = ''
|
||||
for (let i = 0; i < conditionList.length; i++) {
|
||||
for (let j = 0; j < conditionList[i].length; j++) {
|
||||
if (j + 1 !== conditionList[i].length) {
|
||||
text =
|
||||
text +
|
||||
conditionList[i][j].label +
|
||||
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||
conditionList[i][j].value +
|
||||
' 且 '
|
||||
} else {
|
||||
text =
|
||||
text +
|
||||
conditionList[i][j].label +
|
||||
this.getOperatorLabel(conditionList[i][j].operator) +
|
||||
conditionList[i][j].value
|
||||
}
|
||||
}
|
||||
if (i + 1 !== conditionList.length) {
|
||||
text = text + ' 或 '
|
||||
}
|
||||
}
|
||||
return text
|
||||
} else if (conditionList.length === 0 && priorityLevel < priorityLevelMax) {
|
||||
return false
|
||||
} else {
|
||||
return '其他条件进入此流程'
|
||||
}
|
||||
},
|
||||
// 通过value 获取界面显示的label汉字
|
||||
getOperatorLabel(value) {
|
||||
return this.operatorList.find((item) => item.value === value).label
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped type="less">
|
||||
.workflow-design .condition-node {
|
||||
min-height: 200px !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<div class="branch-wrap">
|
||||
<div class="branch-box-wrap">
|
||||
<div class="branch-box">
|
||||
<div v-for="(item, index) in childNode.conditionNodeList" :key="index" class="col-box">
|
||||
<div class="condition-node">
|
||||
<div class="condition-node-box">
|
||||
<user-tasks v-model="childNode.conditionNodeList[index]" :currentActivityId="currentActivityId" />
|
||||
</div>
|
||||
</div>
|
||||
<slot v-if="item.childNode" :node="item"></slot>
|
||||
<div v-if="index == 0" class="top-left-cover-line"></div>
|
||||
<div v-if="index == 0" class="bottom-left-cover-line"></div>
|
||||
<div v-if="index == childNode.conditionNodeList.length - 1" class="top-right-cover-line"></div>
|
||||
<div v-if="index == childNode.conditionNodeList.length - 1" class="bottom-right-cover-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<add-nodes v-model="childNode.childNode" :parent-data="childNode" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNodes from './cAddNode.vue'
|
||||
import userTasks from './cUserTask.vue'
|
||||
export default {
|
||||
components: {
|
||||
addNodes,
|
||||
userTasks
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {},
|
||||
index: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div class="node-wrap">
|
||||
<a-tooltip placement="left">
|
||||
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||
{{ comment.userName }}于{{ comment.approveTime }}
|
||||
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||
>,意见:{{ comment.comment }}
|
||||
</div>
|
||||
</template>
|
||||
<div :class="childNode.properties && childNode.properties.commentList.length > 0 ? 'node-state-label' : ''">
|
||||
<div class="node-wrap-box">
|
||||
<div class="title" style="background: #3296fa">
|
||||
<send-outlined class="icon" />
|
||||
<span>{{ childNode.title }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||
<span v-else class="placeholder">未选择人员</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<add-nodes v-model="childNode.childNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNodes from './cAddNode.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addNodes
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
toText(childNode) {
|
||||
if (JSON.stringify(childNode) !== '{}') {
|
||||
const participateInfo = childNode.properties.participateInfo
|
||||
if (participateInfo.length > 0) {
|
||||
let resultArray = []
|
||||
if (participateInfo[0].label.indexOf(',') !== -1) {
|
||||
resultArray = participateInfo[0].label.split(',')
|
||||
} else {
|
||||
resultArray.push(participateInfo[0].label)
|
||||
}
|
||||
return resultArray.toString()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.node-state-label {
|
||||
padding: 1px;
|
||||
border: 3px solid rgb(24, 144, 255);
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,24 +0,0 @@
|
||||
<template>
|
||||
<div class="node-wrap"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<div class="node-wrap">
|
||||
<a-tooltip placement="left">
|
||||
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||
{{ comment.userName }}于{{ comment.approveTime }}
|
||||
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||
>,意见:{{ comment.comment }}
|
||||
</div>
|
||||
</template>
|
||||
<div :class="getClassName()">
|
||||
<div class="node-wrap-box start-node">
|
||||
<div class="title" style="background: #576a95">
|
||||
<user-outlined class="icon" />
|
||||
<span>{{ childNode.title }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span>{{ toText(childNode) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<add-nodes v-model="childNode.childNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNodes from './cAddNode.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addNodes
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
toText() {
|
||||
return '系统自动配置参与人'
|
||||
},
|
||||
getClassName() {
|
||||
if (this.childNode.id === this.currentActivityId) {
|
||||
return 'node-state-label-activity'
|
||||
} else {
|
||||
return this.childNode.properties && this.childNode.properties.commentList.length > 0 ? 'node-state-label' : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.node-state-label {
|
||||
padding: 1px;
|
||||
border: 3px solid rgb(24, 144, 255);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-activity {
|
||||
padding: 1px;
|
||||
border: 3px dashed #00e97c;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<div class="node-wrap">
|
||||
<a-tooltip placement="left">
|
||||
<template #title v-if="childNode.properties && childNode.properties.commentList.length > 0">
|
||||
<div v-for="comment in childNode.properties.commentList" :key="comment.id">
|
||||
{{ comment.userName }}于{{ comment.approveTime }}
|
||||
<a-tag color="rgb(212, 212, 212)">{{ comment.operateText }}</a-tag
|
||||
>,意见:{{ comment.comment }}
|
||||
</div>
|
||||
</template>
|
||||
<div :class="getClassName()">
|
||||
<div class="node-wrap-box">
|
||||
<div class="title" style="background: #ff943e">
|
||||
<user-outlined class="icon" />
|
||||
<span>{{ childNode.title }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span v-if="toText(childNode)">{{ toText(childNode) }}</span>
|
||||
<span v-else class="placeholder">未选择审批人</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<add-nodes v-model="childNode.childNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import addNodes from './cAddNode.vue'
|
||||
export default {
|
||||
components: {
|
||||
addNodes
|
||||
},
|
||||
props: {
|
||||
modelValue: { type: Object, default: () => {} },
|
||||
currentActivityId: { type: String, default: () => '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
childNode: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
this.childNode = this.modelValue
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.childNode = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
toText(childNode) {
|
||||
if (JSON.stringify(childNode) !== '{}') {
|
||||
const strArray = this.toTag(childNode.properties.participateInfo[0])
|
||||
if (strArray.length > 0) {
|
||||
let value = ''
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < strArray.length; i++) {
|
||||
if (strArray.length === i + 1) {
|
||||
value = value + strArray[i]
|
||||
} else {
|
||||
value = value + strArray[i] + ','
|
||||
}
|
||||
}
|
||||
return value
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
toTag(participateInfo) {
|
||||
// eslint-disable-next-line no-undefined
|
||||
if (participateInfo === undefined) {
|
||||
return []
|
||||
}
|
||||
if (participateInfo.label === '') {
|
||||
return []
|
||||
} else {
|
||||
let resultArray = []
|
||||
if (participateInfo.label.indexOf(',') !== -1) {
|
||||
resultArray = participateInfo.label.split(',')
|
||||
} else {
|
||||
resultArray.push(participateInfo.label)
|
||||
}
|
||||
return resultArray
|
||||
}
|
||||
},
|
||||
getClassName() {
|
||||
if (this.childNode.properties && this.childNode.properties.commentList.length > 0) {
|
||||
if (this.childNode.properties.commentList.length === 1) {
|
||||
if (this.childNode.properties.commentList[0].operateType === 'PASS') {
|
||||
return 'node-state-label-pass'
|
||||
}
|
||||
if (this.childNode.properties.commentList[0].operateType === 'REJECT') {
|
||||
return 'node-state-label-reject'
|
||||
}
|
||||
if (this.childNode.properties.commentList[0].operateType === 'JUMP') {
|
||||
return 'node-state-label-jump'
|
||||
}
|
||||
if (this.childNode.properties.commentList[0].operateType === 'BACK') {
|
||||
return 'node-state-label-back'
|
||||
}
|
||||
}
|
||||
return 'node-state-label'
|
||||
} else {
|
||||
if (this.childNode.id === this.currentActivityId) {
|
||||
return 'node-state-label-activity'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.node-state-label {
|
||||
padding: 1px;
|
||||
border: 3px solid #1890ff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-pass {
|
||||
padding: 1px;
|
||||
border: 3px solid #52c41a;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-reject {
|
||||
padding: 1px;
|
||||
border: 3px solid #ff4d4f;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-jump {
|
||||
padding: 1px;
|
||||
border: 3px solid #bfbfbf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-back {
|
||||
padding: 1px;
|
||||
border: 3px solid #bfbfbf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.node-state-label-activity {
|
||||
padding: 1px;
|
||||
border: 3px dashed #00e97c;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,39 +0,0 @@
|
||||
const defaultZoomMargin = 360
|
||||
const perZoom = 0.25 // 每次放大缩小0.25倍
|
||||
|
||||
const ZoomHelper = {
|
||||
// 处理全屏放大的样式
|
||||
getZoomStyles(zoom, MinZoom = 1, ZoomMargin = defaultZoomMargin, rightSpace = 0) {
|
||||
const width = document.querySelector('.workflow-design').clientWidth
|
||||
let style = {}
|
||||
// 兼容Firefox浏览器的放大缩小
|
||||
if (zoom !== MinZoom) {
|
||||
style = {
|
||||
transform: `scale(${zoom})`,
|
||||
'transform-origin': '0 0',
|
||||
width: (width - ZoomMargin - rightSpace) / zoom + 'px'
|
||||
}
|
||||
}
|
||||
return style
|
||||
},
|
||||
// 获取最大的放大倍数
|
||||
getMaxZoom() {
|
||||
const width = window.innerWidth
|
||||
const mediumWidth = 1600
|
||||
const smallScreenScale = 2.5 // 小屏幕下附件放大3倍会有样式问题, 所以取2.5
|
||||
const bigScreenScale = 3 // 大于1600的最大倍数为3
|
||||
const maxZoom = width > mediumWidth ? bigScreenScale : smallScreenScale
|
||||
return maxZoom
|
||||
},
|
||||
// 获取点击放大缩小之后生成的最终的放大倍数和样式
|
||||
getZoomData(zoomIn, zoom) {
|
||||
const zoomResult = zoomIn ? zoom + perZoom : zoom - perZoom // 放大倍数加一次或者减少一次
|
||||
const zoomData = {
|
||||
style: this.getZoomStyles(zoomResult),
|
||||
zoom: zoomResult
|
||||
}
|
||||
return zoomData
|
||||
}
|
||||
}
|
||||
|
||||
export default ZoomHelper
|
||||
@@ -1,9 +0,0 @@
|
||||
审批中 蓝色 #1890FF
|
||||
已挂起 黄色 #FCC02E
|
||||
已完成 绿色 #52C41A
|
||||
已终止 黄色 #FF5A5A
|
||||
已撤回 灰色 #BFBFBF
|
||||
已拒绝 红色 #FF4D4F
|
||||
|
||||
同意 绿色 #52C41A
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogFormVisible" title="新增" width="500" :append-to-body="true">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px" label-position="top">
|
||||
<el-form-item label="监听类型:" prop="listenerType">
|
||||
<el-select v-model="formData.listenerType" placeholder="请选择类型">
|
||||
<el-option
|
||||
v-for="item in listenerTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="JAVA监听器:" prop="javaClass">
|
||||
<el-select v-model="formData.javaClass" placeholder="请选择JAVA监听器">
|
||||
<el-option
|
||||
v-for="item in listenerValueArray"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleAdd">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, defineProps, defineEmits, watch } from 'vue'
|
||||
const dialogFormVisible = ref(true)
|
||||
const formLabelWidth = '140px'
|
||||
const props = defineProps({
|
||||
addFlag: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['close', 'addWacth'])
|
||||
watch(
|
||||
() => props.addFlag,
|
||||
(val, oldVal) => {
|
||||
console.log(val, oldVal)
|
||||
if (val) {
|
||||
dialogFormVisible.value = val
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
//必填规则
|
||||
const formRules = {
|
||||
listenerType: [{ required: true, message: '请选择监听类型', trigger: 'change' }],
|
||||
javaClass: [{ required: true, message: '请选择JAVA监听器', trigger: 'change' }]
|
||||
}
|
||||
//form
|
||||
const formData = reactive({
|
||||
listenerType: '',
|
||||
javaClass: ''
|
||||
})
|
||||
//监听类型数组
|
||||
const listenerTypeOptions = reactive([
|
||||
{
|
||||
label: '开始',
|
||||
value: '开始'
|
||||
},
|
||||
{
|
||||
label: '完成',
|
||||
value: '完成'
|
||||
},
|
||||
{
|
||||
label: '拒绝',
|
||||
value: '拒绝'
|
||||
},
|
||||
{
|
||||
label: '终止',
|
||||
value: '终止'
|
||||
},
|
||||
{
|
||||
label: '撤回',
|
||||
value: '撤回'
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
value: '删除'
|
||||
}
|
||||
])
|
||||
//java监听器数组
|
||||
const listenerValueArray = reactive([
|
||||
{
|
||||
label: 'vip.xiaonuo.flw.core.listener.FlwTestExecutionListener',
|
||||
value: 'vip.xiaonuo.flw.core.listener.FlwTestExecutionListener'
|
||||
}
|
||||
])
|
||||
//关闭
|
||||
async function handleClose() {
|
||||
emit('close')
|
||||
}
|
||||
//提交
|
||||
const formRef = ref(null)
|
||||
async function handleAdd() {
|
||||
formRef.value.validate(valid => {
|
||||
if (valid) {
|
||||
console.log(valid)
|
||||
emit("addWatch",formData);
|
||||
emit("close");
|
||||
} else {
|
||||
console.log('error submit!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
console.log()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dialog-footer {
|
||||
padding: 20px 15px !important;
|
||||
}
|
||||
.el-form {
|
||||
width: 96%;
|
||||
margin: 0 auto;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<div class="drawers">
|
||||
<el-drawer
|
||||
v-model="drawerVisibile"
|
||||
title="全局属性"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
:before-close="handleClose"
|
||||
@closed="handleClose"
|
||||
>
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="人员配置" name="0">
|
||||
<user v-if="activeName == 0"></user>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="基础配置" name="1">
|
||||
<basic v-if="activeName == 1"></basic>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="通知配置" name="2">
|
||||
<notice v-if="activeName == 2"></notice>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="表单预设" name="3">
|
||||
<formPreset v-if="activeName == 3"></formPreset>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="执行监听" name="4">
|
||||
<watchs v-if="activeName == 4"></watchs>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<div style="flex: auto" class="drawer_footer">
|
||||
<el-button type="primary" @click="handleConfirm">保存</el-button>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import user from '../globalAttribute/user.vue'
|
||||
import basic from '../globalAttribute/basic.vue'
|
||||
import notice from '../globalAttribute/notice.vue'
|
||||
import formPreset from '../globalAttribute/formPreset.vue'
|
||||
import watchs from '../globalAttribute/watchs.vue'
|
||||
export default {
|
||||
components: {
|
||||
user,
|
||||
basic,
|
||||
notice,
|
||||
formPreset,
|
||||
watchs
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawerVisibile: true,
|
||||
activeName: '0'
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit('updateDrawer')
|
||||
},
|
||||
handleClick(val) {
|
||||
console.log(val)
|
||||
},
|
||||
handleConfirm(){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.drawers ::v-deep(.el-drawer__header) {
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.drawers ::v-deep(.el-drawer__body){
|
||||
position:fixed;
|
||||
top:40px;
|
||||
left:50%;
|
||||
width:50%;
|
||||
}
|
||||
.drawer_footer{
|
||||
display: flex;
|
||||
width: 100px;
|
||||
justify-content: space-between;
|
||||
float: right;
|
||||
position:absolute;
|
||||
bottom: 20px;
|
||||
right:50px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<div class="nav_title" :style="{ borderLeft: `4px solid ${borderColor}` }">
|
||||
<p><slot name="nav_name"></slot></p>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useConfig } from '@/stores/config'
|
||||
import { ref } from 'vue'
|
||||
const configStore = useConfig()
|
||||
const borderColor =configStore.getColorVal('elementUiPrimary')
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.nav_title {
|
||||
padding-left: 8px;
|
||||
margin-top: 10px;
|
||||
p {
|
||||
font-size: 15px;
|
||||
color: #212121;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<a-result status="404" title="未找到表单" sub-title="对不起,该节点配置的自定义表单不存在,请联系管理员。" />
|
||||
</template>
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* 自定义表单导入
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2023-05-11 00:12:22
|
||||
*/
|
||||
const modules = import.meta.glob('/src/views/flw/customform/**/**.vue')
|
||||
const notFound = () => import(/* @vite-ignore */ `/src/components/XnWorkflow/customForm/404.vue`)
|
||||
|
||||
// 直接渲染组件
|
||||
export const loadComponent = (component) => {
|
||||
if (component) {
|
||||
const link = modules[`/src/views/flw/customform/${component}.vue`]
|
||||
return markRaw(defineAsyncComponent(link ? link : notFound))
|
||||
} else {
|
||||
return markRaw(defineAsyncComponent(notFound))
|
||||
}
|
||||
}
|
||||
|
||||
// 给出判断,如果使用的地方取到是404,那么他的下一步就不走了
|
||||
export const verdictComponent = (component) => {
|
||||
if (component) {
|
||||
const link = modules[`/src/views/flw/customform/${component}.vue`]
|
||||
return !!link
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
.workflow-design {
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
}
|
||||
.workflow-design .box-scale {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 55px;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
min-width: min-content;
|
||||
}
|
||||
.nodeLegal {
|
||||
border: 2px solid red;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.workflow-design {
|
||||
.node-wrap {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
flex-flow: column wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 0px 50px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.node-wrap-box {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
width: 220px;
|
||||
min-height: 72px;
|
||||
flex-shrink: 0;
|
||||
background: var(--node-wrap-box-color);
|
||||
border-radius: 1px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.node-wrap-box::before {
|
||||
background: var(--auto-judge-before-color);
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0px;
|
||||
border-style: solid;
|
||||
border-width: 8px 6px 4px;
|
||||
border-color: #cacaca transparent transparent;
|
||||
}
|
||||
.node-wrap-box.start-node:before {
|
||||
content: none;
|
||||
}
|
||||
.node-wrap-box .title {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
color: #fff;
|
||||
padding-left: 16px;
|
||||
padding-right: 30px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.node-wrap-box .title .icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.node-wrap-box .title .close {
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
display: none;
|
||||
}
|
||||
.node-wrap-box .title .success {
|
||||
font-size: 20px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: -25px;
|
||||
display: block;
|
||||
color: #00bb00;
|
||||
}
|
||||
.node-wrap-box .content {
|
||||
position: relative;
|
||||
padding: 15px;
|
||||
}
|
||||
.node-wrap-box .content .placeholder {
|
||||
color: red;
|
||||
}
|
||||
.node-wrap-box:hover .close {
|
||||
display: block;
|
||||
}
|
||||
.add-node-btn-box {
|
||||
width: 240px;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.add-node-btn-box:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: -1;
|
||||
margin: auto;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background-color: rgb(202, 202, 202);
|
||||
}
|
||||
.add-node-btn {
|
||||
user-select: none;
|
||||
width: 240px;
|
||||
padding: 20px 0px 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.add-node-btn span {
|
||||
}
|
||||
.add-branch {
|
||||
justify-content: center;
|
||||
padding: 0px 10px;
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transform-origin: center center;
|
||||
z-index: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.branch-wrap {
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
}
|
||||
.branch-box-wrap {
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
align-items: center;
|
||||
min-height: 270px;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.col-box {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
background: var(--component-background);
|
||||
}
|
||||
// 分支 上面横线
|
||||
.branch-box {
|
||||
display: flex;
|
||||
overflow: visible;
|
||||
min-height: 180px;
|
||||
height: auto;
|
||||
border-bottom: 2px solid #ccc;
|
||||
border-top: 2px solid #ccc;
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.branch-box .col-box::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 0;
|
||||
margin: auto;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background-color: rgb(202, 202, 202);
|
||||
}
|
||||
.condition-node {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
min-height: 220px;
|
||||
}
|
||||
.condition-node-box {
|
||||
padding-top: 30px;
|
||||
padding-right: 50px;
|
||||
padding-left: 50px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.auto-judge {
|
||||
position: relative;
|
||||
width: 220px;
|
||||
min-height: 72px;
|
||||
background: var(--node-wrap-box-color);
|
||||
border-radius: 2px;
|
||||
padding: 15px 15px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
// 箭头框框
|
||||
.auto-judge::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0px;
|
||||
border-style: solid;
|
||||
border-width: 8px 6px 4px;
|
||||
border-color: #cacaca transparent transparent;
|
||||
background: var(--auto-judge-before-color);
|
||||
}
|
||||
.auto-judge .title {
|
||||
line-height: 16px;
|
||||
}
|
||||
.auto-judge .title .node-title {
|
||||
color: #15bc83;
|
||||
}
|
||||
.auto-judge .title .close {
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
color: #999;
|
||||
display: none;
|
||||
}
|
||||
.auto-judge .title .success {
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
color: #999;
|
||||
display: block;
|
||||
}
|
||||
.auto-judge .title .priority-title {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
color: #999;
|
||||
}
|
||||
.auto-judge .content {
|
||||
position: relative;
|
||||
padding-top: 15px;
|
||||
}
|
||||
.auto-judge .content .placeholder {
|
||||
color: red;
|
||||
}
|
||||
.auto-judge:hover {
|
||||
.close {
|
||||
display: block;
|
||||
}
|
||||
.priority-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.top-left-cover-line,
|
||||
.top-right-cover-line {
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
width: 50%;
|
||||
background-color: var(--component-background);
|
||||
top: -2px;
|
||||
}
|
||||
.bottom-left-cover-line,
|
||||
.bottom-right-cover-line {
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
width: 50%;
|
||||
background-color: var(--component-background);
|
||||
bottom: -2px;
|
||||
}
|
||||
.top-left-cover-line {
|
||||
left: -1px;
|
||||
}
|
||||
.top-right-cover-line {
|
||||
right: -1px;
|
||||
}
|
||||
.bottom-left-cover-line {
|
||||
left: -1px;
|
||||
}
|
||||
.bottom-right-cover-line {
|
||||
right: -1px;
|
||||
}
|
||||
.end-node {
|
||||
border-radius: 50%;
|
||||
font-size: 14px;
|
||||
color: rgba(25, 31, 37, 0.4);
|
||||
text-align: left;
|
||||
}
|
||||
// 结束的小点点
|
||||
.end-node-circle {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: auto;
|
||||
border-radius: 50%;
|
||||
background: #dbdcdc;
|
||||
}
|
||||
.end-node-text {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.auto-judge:hover {
|
||||
.sort-left {
|
||||
display: flex;
|
||||
}
|
||||
.sort-right {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.auto-judge .sort-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.auto-judge .sort-right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.auto-judge .sort-left:hover,
|
||||
.auto-judge .sort-right:hover {
|
||||
background: var(--auto-judge-before-color);
|
||||
}
|
||||
.auto-judge:after {
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
border-radius: 2px;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.auto-judge:hover:after {
|
||||
border: 1px solid #3296fa;
|
||||
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
|
||||
}
|
||||
.node-wrap-box:after {
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
border-radius: 2px;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.node-wrap-box:hover:after {
|
||||
border: 1px solid #3296fa;
|
||||
box-shadow: 0 0 6px 0 rgba(50, 150, 250, 0.3);
|
||||
}
|
||||
}
|
||||
.tags-list {
|
||||
margin-top: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
.add-node-popover-body {
|
||||
height: 81px;
|
||||
}
|
||||
.add-node-popover-body li {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.add-node-popover-body li i {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 38px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.add-node-popover-body li i:hover {
|
||||
border: 1px solid #3296fa;
|
||||
background: #3296fa;
|
||||
color: #fff !important;
|
||||
}
|
||||
.add-node-popover-body li p {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.node-wrap-drawer__title {
|
||||
padding-right: 40px;
|
||||
}
|
||||
.node-wrap-drawer__title label {
|
||||
cursor: pointer;
|
||||
}
|
||||
.node-wrap-drawer__title label:hover {
|
||||
border-bottom: 1px dashed #409eff;
|
||||
}
|
||||
.node-wrap-drawer__title .node-wrap-drawer-title-edit {
|
||||
color: #409eff;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
<!-- 全局属性基础配置页面 -->
|
||||
<template>
|
||||
<div class="home">
|
||||
<navTitle>
|
||||
<template #nav_name>流程基础全局配置</template>
|
||||
</navTitle>
|
||||
<div class="basic_form">
|
||||
<el-form :model="form" label-width="auto" label-position="top">
|
||||
<div class="col">
|
||||
<el-form-item label="流水号">
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
style="width: 300px !important"
|
||||
v-model="form.region"
|
||||
placeholder="please select your zone"
|
||||
>
|
||||
<el-option label="Zone one" value="shanghai" />
|
||||
<el-option label="Zone two" value="beijing" />
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item label="打印模板">
|
||||
<el-col :span="12">
|
||||
<p style="width: 300px">自定义表单内提供打印方法</p>
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item label="标题模板">
|
||||
<div class="form_position_item">
|
||||
<el-input v-model="form.desc" type="textarea" />
|
||||
<el-button type="primary" size="small">
|
||||
置入字段
|
||||
<el-icon class="el-icon--right"><ArrowDownBold /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="摘要模板">
|
||||
<div class="form_position_item">
|
||||
<el-input v-model="form.desc" type="textarea" />
|
||||
<el-button type="primary" size="small">
|
||||
置入字段
|
||||
<el-icon class="el-icon--right"><ArrowDownBold /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开启自动去重">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<el-radio-group v-model="form.resource" class="radioGroup">
|
||||
<p><el-radio value="0">当审批人和发起人是同一个人,审批自动通过</el-radio></p>
|
||||
<p><el-radio value="1">当同一审批人在流程中连续多次出现时,自动去重</el-radio></p>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div class="col">
|
||||
<el-form-item label="开启审批撤销">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启意见必填">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import navTitle from '../components/navTitle.vue'
|
||||
import { ArrowDownBold } from '@element-plus/icons-vue'
|
||||
import { ref } from 'vue'
|
||||
const form = ref({})
|
||||
form.value = {
|
||||
name: '',
|
||||
region: '',
|
||||
date1: '',
|
||||
date2: '',
|
||||
delivery: false,
|
||||
type: [],
|
||||
resource: '',
|
||||
desc: ''
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
width: 100%;
|
||||
height: calc(100vh - 170px);
|
||||
padding-top: 20px;
|
||||
overflow: auto;
|
||||
.basic_form {
|
||||
width:96%;
|
||||
padding-top: 10px;
|
||||
margin: 0 auto;
|
||||
.col {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.el-form-item {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
.form_position_item {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
.el-input {
|
||||
height: 100%;
|
||||
}
|
||||
.el-button {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
.radioGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,122 +0,0 @@
|
||||
<!-- 全局属性表单预设页面 -->
|
||||
<template>
|
||||
<div class="home">
|
||||
<navTitle>
|
||||
<template #nav_name>预设全局需要的表单</template>
|
||||
</navTitle>
|
||||
<div class="node_form">
|
||||
<el-form :model="form" label-width="auto" label-position="top">
|
||||
<el-form-item
|
||||
label="开始节点表单"
|
||||
name="processStartTaskFormUrl"
|
||||
:rules="[{ required: true, message: '请输入开始节点表单' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.properties.configInfo.processStartTaskFormUrl"
|
||||
placeholder="请输入开始节点表单组件"
|
||||
clearable
|
||||
>
|
||||
<template #prepend>src/views/flw/customform/</template>
|
||||
<template #append>.vue</template>
|
||||
<template #suffix>
|
||||
<el-button
|
||||
v-if="form.properties.configInfo.processStartTaskFormUrl"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="
|
||||
$refs.previewCustomFormRef.onOpen(
|
||||
form.properties.configInfo.processStartTaskFormUrl
|
||||
)
|
||||
"
|
||||
>
|
||||
预览
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="移动端开始节点表单"
|
||||
name="processStartTaskMobileFormUrl"
|
||||
:rules="[{ required: true, message: '请输入移动端开始节点表单', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.properties.configInfo.processStartTaskMobileFormUrl"
|
||||
placeholder="请输入移动端开始节点表单组件"
|
||||
clearable
|
||||
>
|
||||
<template #prepend>pages/flw/customform/</template>
|
||||
<template #append>.vue</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="人员节点表单"
|
||||
name="processUserTaskFormUrl"
|
||||
:rules="[{ required: true, message: '请输入人员节点表单' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.properties.configInfo.processUserTaskFormUrl"
|
||||
placeholder="请输入人员节点表单组件"
|
||||
clearable
|
||||
>
|
||||
<template #prepend>src/views/flw/customform/</template>
|
||||
<template #append>.vue</template>
|
||||
<template #suffix>
|
||||
<el-button
|
||||
v-if="form.properties.configInfo.processUserTaskFormUrl"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="
|
||||
$refs.previewCustomFormRef.onOpen(form.properties.configInfo.processUserTaskFormUrl)
|
||||
"
|
||||
>
|
||||
预览
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="移动端人员节点表单"
|
||||
name="processUserTaskMobileFormUrl"
|
||||
:rules="[{ required: true, message: '请输入移动端人员节点表单' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.properties.configInfo.processUserTaskMobileFormUrl"
|
||||
placeholder="请输入移动端人员节点表单组件"
|
||||
clearable
|
||||
>
|
||||
<template #prepend>pages/flw/customform/</template>
|
||||
<template #append>.vue</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import navTitle from '../components/navTitle.vue'
|
||||
import { ref ,reactive} from 'vue'
|
||||
const form = ref({})
|
||||
form.value= {
|
||||
properties: {
|
||||
configInfo: {
|
||||
processStartTaskFormUrl: '', //开始节点表单
|
||||
processStartTaskMobileFormUrl: '', //移动端开始节点表单
|
||||
processUserTaskFormUrl: '', //人员节点表单
|
||||
processUserTaskMobileFormUrl: '' //移动端人员节点表单
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
width: 100%;
|
||||
height: calc(100vh - 170px);
|
||||
padding-top: 20px;
|
||||
overflow: auto;
|
||||
.node_form{
|
||||
width:96%;
|
||||
padding-top: 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,47 +0,0 @@
|
||||
<!-- 全局属性通知配置页面 -->
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="info">
|
||||
<navTitle>
|
||||
<template #nav_name>配置通知事项</template>
|
||||
</navTitle>
|
||||
</div>
|
||||
<div class="basic_form">
|
||||
<el-form :model="form" label-width="auto" label-position="top">
|
||||
<el-form-item label="开启退回通知">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启待办通知">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启抄送通知">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启完成通知">
|
||||
<el-switch v-model="form.delivery" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import navTitle from '../components/navTitle.vue';
|
||||
import { ref } from 'vue'
|
||||
const form = ref({})
|
||||
form.value = {
|
||||
delivery: false,
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.home {
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
padding-top: 20px;
|
||||
overflow: auto;
|
||||
.basic_form {
|
||||
width:96%;
|
||||
padding-top: 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,105 +0,0 @@
|
||||
<!-- 全局属性人员配置页面 -->
|
||||
<template>
|
||||
<div class="home">
|
||||
<!-- 未选择任何类型的人员 -->
|
||||
<div class="nobody_info" v-if="checkUserList.length == 0">
|
||||
<p>未选择任何类型的人员配置,默认所有人均可参与此流程</p>
|
||||
</div>
|
||||
<navTitle>
|
||||
<template #nav_name>配置使用该流程的人员</template>
|
||||
</navTitle>
|
||||
<div class="info">
|
||||
<div class="info_item">
|
||||
<div class="info_item_top">
|
||||
<el-button type="primary">+ 选择机构</el-button>
|
||||
</div>
|
||||
<div class="info_item_bot">
|
||||
<div class="checked" v-for="(item, index) in 22">工会办公室{{ index+1 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info_item">
|
||||
<div class="info_item_top">
|
||||
<el-button type="primary">+ 选择角色</el-button>
|
||||
</div>
|
||||
<div class="info_item_bot">
|
||||
<div class="checked" v-for="(item, index) in 30">小诺科技有限公司{{ index+1 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info_item">
|
||||
<div class="info_item_top">
|
||||
<el-button type="primary">+ 选择用户</el-button>
|
||||
</div>
|
||||
<div class="info_item_bot">
|
||||
<div class="checked" v-for="(item, index) in 40">党群工作部{{ index+1 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { checkUser } from '@/api/user-boot/user'
|
||||
import navTitle from '../components/navTitle.vue'
|
||||
export default {
|
||||
components: {
|
||||
navTitle
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
checkUserList: []
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.home {
|
||||
width: 100%;
|
||||
height: calc(100vh - 170px);
|
||||
padding-top: 20px;
|
||||
overflow: auto;
|
||||
.nobody_info {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background-color: #fffbe6;
|
||||
padding: 8px 15px;
|
||||
border-radius: 4px;
|
||||
border: 2px solid #ffe58f;
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
.info_item {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
.info_item_bot {
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.checked {
|
||||
margin: 3px;
|
||||
padding: 0 7px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
background: #FAFAFA;
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 2px;
|
||||
opacity: 1;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user