Merge branch 'dev' of gitee.com:yudaocode/yudao-ui-admin-vue3 into hotfix-role
Signed-off-by: AhJindeg <ahjindeg@gmail.com>pull/414/head
						commit
						b5fa188805
					
				| 
						 | 
				
			
			@ -68,6 +68,8 @@ module.exports = defineConfig({
 | 
			
		|||
    ],
 | 
			
		||||
    'vue/multi-word-component-names': 'off',
 | 
			
		||||
    'vue/no-v-html': 'off',
 | 
			
		||||
    'prettier/prettier': 'off' // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件
 | 
			
		||||
    'prettier/prettier': 'off', // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件
 | 
			
		||||
    '@unocss/order': 'off', // 芋艿:禁用 unocss 【css】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
 | 
			
		||||
    '@unocss/order-attributify': 'off' // 芋艿:禁用 unocss 【属性】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 16 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 16 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 13 KiB  | 
| 
						 | 
				
			
			@ -117,6 +117,8 @@
 | 
			
		|||
| 🚀  | 应用管理  | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
 | 
			
		||||
| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息,支持 IP 对应城市      |
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 工作流程
 | 
			
		||||
 | 
			
		||||
|     | 功能    | 描述                                     |
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +131,8 @@
 | 
			
		|||
| 🚀  | 已办任务  | 查看自己【已】审批的工作任务,未来会支持回退操作               |
 | 
			
		||||
| 🚀  | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 支付系统
 | 
			
		||||
 | 
			
		||||
|     | 功能   | 描述                        |
 | 
			
		||||
| 
						 | 
				
			
			@ -164,6 +168,8 @@ ps:核心功能已经实现,正在对接微信小程序中...
 | 
			
		|||
| 🚀  | 日志服务     | 轻量级日志中心,查看远程服务器的日志                           |
 | 
			
		||||
| 🚀  | 单元测试     | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等    |
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### 数据报表
 | 
			
		||||
 | 
			
		||||
|     | 功能    | 描述                 |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										87
									
								
								package.json
								
								
								
								
							
							
						
						
									
										87
									
								
								package.json
								
								
								
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "yudao-ui-admin-vue3",
 | 
			
		||||
  "version": "2.0.0-snapshot",
 | 
			
		||||
  "version": "2.0.1-snapshot",
 | 
			
		||||
  "description": "基于vue3、vite4、element-plus、typesScript",
 | 
			
		||||
  "author": "xingyu",
 | 
			
		||||
  "private": false,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,12 +30,12 @@
 | 
			
		|||
    "@form-create/element-ui": "^3.1.24",
 | 
			
		||||
    "@iconify/iconify": "^3.1.1",
 | 
			
		||||
    "@videojs-player/vue": "^1.0.0",
 | 
			
		||||
    "@vueuse/core": "^10.6.1",
 | 
			
		||||
    "@vueuse/core": "^10.9.0",
 | 
			
		||||
    "@wangeditor/editor": "^5.1.23",
 | 
			
		||||
    "@wangeditor/editor-for-vue": "^5.1.10",
 | 
			
		||||
    "@zxcvbn-ts/core": "^3.0.4",
 | 
			
		||||
    "animate.css": "^4.1.1",
 | 
			
		||||
    "axios": "^1.6.1",
 | 
			
		||||
    "axios": "^1.6.7",
 | 
			
		||||
    "benz-amr-recorder": "^1.1.5",
 | 
			
		||||
    "bpmn-js-token-simulation": "^0.10.0",
 | 
			
		||||
    "camunda-bpmn-moddle": "^7.0.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +44,9 @@
 | 
			
		|||
    "dayjs": "^1.11.10",
 | 
			
		||||
    "diagram-js": "^12.8.0",
 | 
			
		||||
    "driver.js": "^1.3.1",
 | 
			
		||||
    "echarts": "^5.4.3",
 | 
			
		||||
    "echarts": "^5.5.0",
 | 
			
		||||
    "echarts-wordcloud": "^2.1.0",
 | 
			
		||||
    "element-plus": "2.4.2",
 | 
			
		||||
    "element-plus": "2.5.3",
 | 
			
		||||
    "fast-xml-parser": "^4.3.2",
 | 
			
		||||
    "highlight.js": "^11.9.0",
 | 
			
		||||
    "jsencrypt": "^3.3.2",
 | 
			
		||||
| 
						 | 
				
			
			@ -55,77 +55,78 @@
 | 
			
		|||
    "mitt": "^3.0.1",
 | 
			
		||||
    "nprogress": "^0.2.0",
 | 
			
		||||
    "pinia": "^2.1.7",
 | 
			
		||||
    "pinia-plugin-persistedstate": "^3.2.0",
 | 
			
		||||
    "qrcode": "^1.5.3",
 | 
			
		||||
    "qs": "^6.11.2",
 | 
			
		||||
    "steady-xml": "^0.1.0",
 | 
			
		||||
    "url": "^0.11.3",
 | 
			
		||||
    "video.js": "^7.21.5",
 | 
			
		||||
    "vue": "^3.3.8",
 | 
			
		||||
    "vue": "3.4.20",
 | 
			
		||||
    "vue-dompurify-html": "^4.1.4",
 | 
			
		||||
    "vue-i18n": "^9.6.5",
 | 
			
		||||
    "vue-router": "^4.2.5",
 | 
			
		||||
    "vue-i18n": "9.9.1",
 | 
			
		||||
    "vue-router": "^4.3.0",
 | 
			
		||||
    "vue-types": "^5.1.1",
 | 
			
		||||
    "vuedraggable": "^4.1.0",
 | 
			
		||||
    "web-storage-cache": "^1.1.1",
 | 
			
		||||
    "xml-js": "^1.6.11"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@commitlint/cli": "^18.4.1",
 | 
			
		||||
    "@commitlint/config-conventional": "^18.4.0",
 | 
			
		||||
    "@iconify/json": "^2.2.142",
 | 
			
		||||
    "@intlify/unplugin-vue-i18n": "^1.5.0",
 | 
			
		||||
    "@commitlint/cli": "^19.0.1",
 | 
			
		||||
    "@commitlint/config-conventional": "^19.0.0",
 | 
			
		||||
    "@iconify/json": "^2.2.187",
 | 
			
		||||
    "@intlify/unplugin-vue-i18n": "^2.0.0",
 | 
			
		||||
    "@purge-icons/generated": "^0.9.0",
 | 
			
		||||
    "@types/lodash-es": "^4.17.11",
 | 
			
		||||
    "@types/node": "^20.9.0",
 | 
			
		||||
    "@types/lodash-es": "^4.17.12",
 | 
			
		||||
    "@types/node": "^20.11.21",
 | 
			
		||||
    "@types/nprogress": "^0.2.3",
 | 
			
		||||
    "@types/qrcode": "^1.5.5",
 | 
			
		||||
    "@types/qs": "^6.9.10",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.11.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.11.0",
 | 
			
		||||
    "@unocss/transformer-variant-group": "^0.57.4",
 | 
			
		||||
    "@types/qs": "^6.9.12",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^7.1.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^7.1.0",
 | 
			
		||||
    "@unocss/transformer-variant-group": "^0.58.5",
 | 
			
		||||
    "@unocss/eslint-config": "^0.57.4",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^4.1.1",
 | 
			
		||||
    "@vitejs/plugin-vue": "^4.4.1",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^3.0.2",
 | 
			
		||||
    "autoprefixer": "^10.4.16",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^5.3.1",
 | 
			
		||||
    "@vitejs/plugin-vue": "^5.0.4",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^3.1.0",
 | 
			
		||||
    "autoprefixer": "^10.4.17",
 | 
			
		||||
    "bpmn-js": "8.9.0",
 | 
			
		||||
    "bpmn-js-properties-panel": "0.46.0",
 | 
			
		||||
    "consola": "^3.2.3",
 | 
			
		||||
    "eslint": "^8.53.0",
 | 
			
		||||
    "eslint-config-prettier": "^9.0.0",
 | 
			
		||||
    "eslint-define-config": "^1.24.1",
 | 
			
		||||
    "eslint-plugin-prettier": "^5.0.1",
 | 
			
		||||
    "eslint-plugin-vue": "^9.18.1",
 | 
			
		||||
    "lint-staged": "^15.1.0",
 | 
			
		||||
    "postcss": "^8.4.31",
 | 
			
		||||
    "postcss-html": "^1.5.0",
 | 
			
		||||
    "eslint": "^8.57.0",
 | 
			
		||||
    "eslint-config-prettier": "^9.1.0",
 | 
			
		||||
    "eslint-define-config": "^2.1.0",
 | 
			
		||||
    "eslint-plugin-prettier": "^5.1.3",
 | 
			
		||||
    "eslint-plugin-vue": "^9.22.0",
 | 
			
		||||
    "lint-staged": "^15.2.2",
 | 
			
		||||
    "postcss": "^8.4.35",
 | 
			
		||||
    "postcss-html": "^1.6.0",
 | 
			
		||||
    "postcss-scss": "^4.0.9",
 | 
			
		||||
    "prettier": "^3.1.0",
 | 
			
		||||
    "prettier": "^3.2.5",
 | 
			
		||||
    "prettier-eslint": "^16.3.0",
 | 
			
		||||
    "rimraf": "^5.0.5",
 | 
			
		||||
    "rollup": "^4.4.1",
 | 
			
		||||
    "rollup": "^4.12.0",
 | 
			
		||||
    "sass": "^1.69.5",
 | 
			
		||||
    "stylelint": "^15.11.0",
 | 
			
		||||
    "stylelint": "^16.2.1",
 | 
			
		||||
    "stylelint-config-html": "^1.1.0",
 | 
			
		||||
    "stylelint-config-recommended": "^13.0.0",
 | 
			
		||||
    "stylelint-config-standard": "^34.0.0",
 | 
			
		||||
    "stylelint-order": "^6.0.3",
 | 
			
		||||
    "terser": "^5.24.0",
 | 
			
		||||
    "typescript": "5.2.2",
 | 
			
		||||
    "unocss": "^0.57.4",
 | 
			
		||||
    "stylelint-config-recommended": "^14.0.0",
 | 
			
		||||
    "stylelint-config-standard": "^36.0.0",
 | 
			
		||||
    "stylelint-order": "^6.0.4",
 | 
			
		||||
    "terser": "^5.28.1",
 | 
			
		||||
    "typescript": "5.3.3",
 | 
			
		||||
    "unocss": "^0.58.5",
 | 
			
		||||
    "unplugin-auto-import": "^0.16.7",
 | 
			
		||||
    "unplugin-element-plus": "^0.8.0",
 | 
			
		||||
    "unplugin-vue-components": "^0.25.2",
 | 
			
		||||
    "vite": "4.5.0",
 | 
			
		||||
    "vite": "5.1.4",
 | 
			
		||||
    "vite-plugin-compression": "^0.5.1",
 | 
			
		||||
    "vite-plugin-ejs": "^1.6.4",
 | 
			
		||||
    "vite-plugin-ejs": "^1.7.0",
 | 
			
		||||
    "vite-plugin-eslint": "^1.8.1",
 | 
			
		||||
    "vite-plugin-progress": "^0.0.7",
 | 
			
		||||
    "vite-plugin-purge-icons": "^0.9.2",
 | 
			
		||||
    "vite-plugin-purge-icons": "^0.10.0",
 | 
			
		||||
    "vite-plugin-svg-icons": "^2.0.1",
 | 
			
		||||
    "vite-plugin-top-level-await": "^1.3.1",
 | 
			
		||||
    "vue-eslint-parser": "^9.3.2",
 | 
			
		||||
    "vue-tsc": "^1.8.22"
 | 
			
		||||
    "vue-tsc": "^1.8.27"
 | 
			
		||||
  },
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "repository": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
// BPM 流程分类 VO
 | 
			
		||||
export interface CategoryVO {
 | 
			
		||||
  id: number // 分类编号
 | 
			
		||||
  name: string // 分类名
 | 
			
		||||
  code: string // 分类标志
 | 
			
		||||
  status: number // 分类状态
 | 
			
		||||
  sort: number // 分类排序
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BPM 流程分类 API
 | 
			
		||||
export const CategoryApi = {
 | 
			
		||||
  // 查询流程分类分页
 | 
			
		||||
  getCategoryPage: async (params: any) => {
 | 
			
		||||
    return await request.get({ url: `/bpm/category/page`, params })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // 查询流程分类列表
 | 
			
		||||
  getCategorySimpleList: async () => {
 | 
			
		||||
    return await request.get({ url: `/bpm/category/simple-list` })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // 查询流程分类详情
 | 
			
		||||
  getCategory: async (id: number) => {
 | 
			
		||||
    return await request.get({ url: `/bpm/category/get?id=` + id })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // 新增流程分类
 | 
			
		||||
  createCategory: async (data: CategoryVO) => {
 | 
			
		||||
    return await request.post({ url: `/bpm/category/create`, data })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // 修改流程分类
 | 
			
		||||
  updateCategory: async (data: CategoryVO) => {
 | 
			
		||||
    return await request.put({ url: `/bpm/category/update`, data })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // 删除流程分类
 | 
			
		||||
  deleteCategory: async (id: number) => {
 | 
			
		||||
    return await request.delete({ url: `/bpm/category/delete?id=` + id })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
export const getProcessDefinitionBpmnXML = async (id: number) => {
 | 
			
		||||
export const getProcessDefinition = async (id: number, key: string) => {
 | 
			
		||||
  return await request.get({
 | 
			
		||||
    url: '/bpm/process-definition/get-bpmn-xml?id=' + id
 | 
			
		||||
    url: '/bpm/process-definition/get',
 | 
			
		||||
    params: { id, key }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,8 +49,8 @@ export const getFormPage = async (params) => {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// 获得动态表单的精简列表
 | 
			
		||||
export const getSimpleFormList = async () => {
 | 
			
		||||
export const getFormSimpleList = async () => {
 | 
			
		||||
  return await request.get({
 | 
			
		||||
    url: '/bpm/form/list-all-simple'
 | 
			
		||||
    url: '/bpm/form/simple-list'
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import request from '@/config/axios'
 | 
			
		|||
 | 
			
		||||
export type LeaveVO = {
 | 
			
		||||
  id: number
 | 
			
		||||
  result: number
 | 
			
		||||
  status: number
 | 
			
		||||
  type: number
 | 
			
		||||
  reason: string
 | 
			
		||||
  processInstanceId: string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -20,51 +20,49 @@ export type ProcessInstanceVO = {
 | 
			
		|||
  endTime: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type ProcessInstanceCCVO = {
 | 
			
		||||
  type: number,
 | 
			
		||||
  taskName: string,
 | 
			
		||||
  taskKey: string,
 | 
			
		||||
  processInstanceName: string,
 | 
			
		||||
  processInstanceKey: string,
 | 
			
		||||
  startUserId: string,
 | 
			
		||||
  options:string [],
 | 
			
		||||
export type ProcessInstanceCopyVO = {
 | 
			
		||||
  type: number
 | 
			
		||||
  taskName: string
 | 
			
		||||
  taskKey: string
 | 
			
		||||
  processInstanceName: string
 | 
			
		||||
  processInstanceKey: string
 | 
			
		||||
  startUserId: string
 | 
			
		||||
  options: string[]
 | 
			
		||||
  reason: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getMyProcessInstancePage = async (params) => {
 | 
			
		||||
export const getProcessInstanceMyPage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/process-instance/my-page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getProcessInstanceManagerPage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/process-instance/manager-page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const createProcessInstance = async (data) => {
 | 
			
		||||
  return await request.post({ url: '/bpm/process-instance/create', data: data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const cancelProcessInstance = async (id: number, reason: string) => {
 | 
			
		||||
export const cancelProcessInstanceByStartUser = async (id: number, reason: string) => {
 | 
			
		||||
  const data = {
 | 
			
		||||
    id: id,
 | 
			
		||||
    reason: reason
 | 
			
		||||
  }
 | 
			
		||||
  return await request.delete({ url: '/bpm/process-instance/cancel', data: data })
 | 
			
		||||
  return await request.delete({ url: '/bpm/process-instance/cancel-by-start-user', data: data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getProcessInstance = async (id: number) => {
 | 
			
		||||
export const cancelProcessInstanceByAdmin = async (id: number, reason: string) => {
 | 
			
		||||
  const data = {
 | 
			
		||||
    id: id,
 | 
			
		||||
    reason: reason
 | 
			
		||||
  }
 | 
			
		||||
  return await request.delete({ url: '/bpm/process-instance/cancel-by-admin', data: data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getProcessInstance = async (id: string) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/process-instance/get?id=' + id })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 抄送
 | 
			
		||||
 * @param data 抄送数据
 | 
			
		||||
 * @returns 是否抄送成功
 | 
			
		||||
 */
 | 
			
		||||
export const createProcessInstanceCC = async (data) => {
 | 
			
		||||
  return await request.post({ url: '/bpm/process-instance/cc/create', data: data })
 | 
			
		||||
export const getProcessInstanceCopyPage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/process-instance/copy/page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 抄送列表
 | 
			
		||||
 * @param params 
 | 
			
		||||
 * @returns 
 | 
			
		||||
 */
 | 
			
		||||
export const getProcessInstanceCCPage = async (params) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/process-instance/cc/my-page', params })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,78 +4,63 @@ export type TaskVO = {
 | 
			
		|||
  id: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTodoTaskPage = async (params) => {
 | 
			
		||||
export const getTaskTodoPage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/todo-page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getDoneTaskPage = async (params) => {
 | 
			
		||||
export const getTaskDonePage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/done-page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const completeTask = async (data) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/complete', data })
 | 
			
		||||
export const getTaskManagerPage = async (params: any) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/manager-page', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const approveTask = async (data) => {
 | 
			
		||||
export const approveTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/approve', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const rejectTask = async (data) => {
 | 
			
		||||
export const rejectTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/reject', data })
 | 
			
		||||
}
 | 
			
		||||
export const backTask = async (data) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/back', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const updateTaskAssignee = async (data) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/update-assignee', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTaskListByProcessInstanceId = async (processInstanceId) => {
 | 
			
		||||
export const getTaskListByProcessInstanceId = async (processInstanceId: string) => {
 | 
			
		||||
  return await request.get({
 | 
			
		||||
    url: '/bpm/task/list-by-process-instance-id?processInstanceId=' + processInstanceId
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 导出任务
 | 
			
		||||
export const exportTask = async (params) => {
 | 
			
		||||
  return await request.download({ url: '/bpm/task/export', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取所有可回退的节点
 | 
			
		||||
export const getReturnList = async (params) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/return-list', params })
 | 
			
		||||
export const getTaskListByReturn = async (id: string) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/list-by-return', params: { id } })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 回退
 | 
			
		||||
export const returnTask = async (data) => {
 | 
			
		||||
export const returnTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/return', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 委派
 | 
			
		||||
 */
 | 
			
		||||
export const delegateTask = async (data) => {
 | 
			
		||||
// 委派
 | 
			
		||||
export const delegateTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/delegate', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 加签
 | 
			
		||||
 */
 | 
			
		||||
export const taskAddSign = async (data) => {
 | 
			
		||||
// 转派
 | 
			
		||||
export const transferTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/transfer', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 加签
 | 
			
		||||
export const signCreateTask = async (data: any) => {
 | 
			
		||||
  return await request.put({ url: '/bpm/task/create-sign', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取减签任务列表
 | 
			
		||||
 */
 | 
			
		||||
export const getChildrenTaskList = async (id: string) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/children-list?taskId=' + id })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 减签
 | 
			
		||||
 */
 | 
			
		||||
export const taskSubSign = async (data) => {
 | 
			
		||||
// 减签
 | 
			
		||||
export const signDeleteTask = async (data: any) => {
 | 
			
		||||
  return await request.delete({ url: '/bpm/task/delete-sign', data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取减签任务列表
 | 
			
		||||
export const getChildrenTaskList = async (id: string) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task/list-by-parent-task-id?parentTaskId=' + id })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
export type TaskAssignVO = {
 | 
			
		||||
  id: number
 | 
			
		||||
  modelId: string
 | 
			
		||||
  processDefinitionId: string
 | 
			
		||||
  taskDefinitionKey: string
 | 
			
		||||
  taskDefinitionName: string
 | 
			
		||||
  options: string[]
 | 
			
		||||
  type: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTaskAssignRuleList = async (params) => {
 | 
			
		||||
  return await request.get({ url: '/bpm/task-assign-rule/list', params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const createTaskAssignRule = async (data: TaskAssignVO) => {
 | 
			
		||||
  return await request.post({
 | 
			
		||||
    url: '/bpm/task-assign-rule/create',
 | 
			
		||||
    data: data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const updateTaskAssignRule = async (data: TaskAssignVO) => {
 | 
			
		||||
  return await request.put({
 | 
			
		||||
    url: '/bpm/task-assign-rule/update',
 | 
			
		||||
    data: data
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ export type UserGroupVO = {
 | 
			
		|||
  id: number
 | 
			
		||||
  name: string
 | 
			
		||||
  description: string
 | 
			
		||||
  memberUserIds: number[]
 | 
			
		||||
  userIds: number[]
 | 
			
		||||
  status: number
 | 
			
		||||
  remark: string
 | 
			
		||||
  createTime: string
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +42,6 @@ export const getUserGroupPage = async (params) => {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// 获取用户组精简信息列表
 | 
			
		||||
export const getSimpleUserGroupList = async (): Promise<UserGroupVO[]> => {
 | 
			
		||||
  return await request.get({ url: '/bpm/user-group/list-all-simple' })
 | 
			
		||||
export const getUserGroupSimpleList = async (): Promise<UserGroupVO[]> => {
 | 
			
		||||
  return await request.get({ url: '/bpm/user-group/simple-list' })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,21 +14,21 @@ export interface CrmStatisticsCustomerSummaryByUserRespVO {
 | 
			
		|||
  receivablePrice: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticsFollowupSummaryByDateRespVO {
 | 
			
		||||
export interface CrmStatisticsFollowUpSummaryByDateRespVO {
 | 
			
		||||
  time: string
 | 
			
		||||
  followupRecordCount: number
 | 
			
		||||
  followupCustomerCount: number
 | 
			
		||||
  followUpRecordCount: number
 | 
			
		||||
  followUpCustomerCount: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticsFollowupSummaryByUserRespVO {
 | 
			
		||||
export interface CrmStatisticsFollowUpSummaryByUserRespVO {
 | 
			
		||||
  ownerUserName: string
 | 
			
		||||
  followupRecordCount: number
 | 
			
		||||
  followupCustomerCount: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticsFollowupSummaryByTypeRespVO {
 | 
			
		||||
  followupType: string
 | 
			
		||||
  followupRecordCount: number
 | 
			
		||||
export interface CrmStatisticsFollowUpSummaryByTypeRespVO {
 | 
			
		||||
  followUpType: string
 | 
			
		||||
  followUpRecordCount: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticsCustomerContractSummaryRespVO {
 | 
			
		||||
| 
						 | 
				
			
			@ -72,23 +72,23 @@ export const StatisticsCustomerApi = {
 | 
			
		|||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 2.1 客户跟进次数分析(按日期)
 | 
			
		||||
  getFollowupSummaryByDate: (params: any) => {
 | 
			
		||||
  getFollowUpSummaryByDate: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-customer/get-followup-summary-by-date',
 | 
			
		||||
      url: '/crm/statistics-customer/get-follow-up-summary-by-date',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 2.2 客户跟进次数分析(按用户)
 | 
			
		||||
  getFollowupSummaryByUser: (params: any) => {
 | 
			
		||||
  getFollowUpSummaryByUser: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-customer/get-followup-summary-by-user',
 | 
			
		||||
      url: '/crm/statistics-customer/get-follow-up-summary-by-user',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 3.1 获取客户跟进方式统计数
 | 
			
		||||
  getFollowupSummaryByType: (params: any) => {
 | 
			
		||||
  getFollowUpSummaryByType: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-customer/get-followup-summary-by-type',
 | 
			
		||||
      url: '/crm/statistics-customer/get-follow-up-summary-by-type',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
export interface StatisticsPerformanceRespVO {
 | 
			
		||||
  time: string
 | 
			
		||||
  currentMonthCount: number
 | 
			
		||||
  lastMonthCount: number
 | 
			
		||||
  lastYearCount: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 排行 API
 | 
			
		||||
export const StatisticsPerformanceApi = {
 | 
			
		||||
  // 员工获得合同金额统计
 | 
			
		||||
  getContractPricePerformance: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-performance/get-contract-price-performance',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 员工获得回款统计
 | 
			
		||||
  getReceivablePricePerformance: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-performance/get-receivable-price-performance',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  //员工获得签约合同数量统计
 | 
			
		||||
  getContractCountPerformance: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-performance/get-contract-count-performance',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticCustomerBaseRespVO {
 | 
			
		||||
  customerCount: number
 | 
			
		||||
  dealCount: number
 | 
			
		||||
  dealPortion: string | number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticCustomerIndustryRespVO extends CrmStatisticCustomerBaseRespVO {
 | 
			
		||||
  industryId: number
 | 
			
		||||
  industryPortion: string | number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticCustomerSourceRespVO extends CrmStatisticCustomerBaseRespVO {
 | 
			
		||||
  source: number
 | 
			
		||||
  sourcePortion: string | number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticCustomerLevelRespVO extends CrmStatisticCustomerBaseRespVO {
 | 
			
		||||
  level: number
 | 
			
		||||
  levelPortion: string | number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CrmStatisticCustomerAreaRespVO extends CrmStatisticCustomerBaseRespVO {
 | 
			
		||||
  areaId: number
 | 
			
		||||
  areaName: string
 | 
			
		||||
  areaPortion: string | number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 客户分析 API
 | 
			
		||||
export const StatisticsPortraitApi = {
 | 
			
		||||
  // 1. 获取客户行业统计数据
 | 
			
		||||
  getCustomerIndustry: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-portrait/get-customer-industry-summary',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 2. 获取客户来源统计数据
 | 
			
		||||
  getCustomerSource: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-portrait/get-customer-source-summary',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 3. 获取客户级别统计数据
 | 
			
		||||
  getCustomerLevel: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-portrait/get-customer-level-summary',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  // 4. 获取客户地区统计数据
 | 
			
		||||
  getCustomerArea: (params: any) => {
 | 
			
		||||
    return request.get({
 | 
			
		||||
      url: '/crm/statistics-portrait/get-customer-area-summary',
 | 
			
		||||
      params
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,11 +8,15 @@ export interface ApiAccessLogVO {
 | 
			
		|||
  applicationName: string
 | 
			
		||||
  requestMethod: string
 | 
			
		||||
  requestParams: string
 | 
			
		||||
  responseBody: string
 | 
			
		||||
  requestUrl: string
 | 
			
		||||
  userIp: string
 | 
			
		||||
  userAgent: string
 | 
			
		||||
  operateModule: string
 | 
			
		||||
  operateName: string
 | 
			
		||||
  operateType: number
 | 
			
		||||
  beginTime: Date
 | 
			
		||||
  endTIme: Date
 | 
			
		||||
  endTime: Date
 | 
			
		||||
  duration: number
 | 
			
		||||
  resultCode: number
 | 
			
		||||
  resultMsg: string
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import { formatDate } from '@/utils/formatTime'
 | 
			
		|||
 | 
			
		||||
/** 会员分析 Request VO */
 | 
			
		||||
export interface MemberAnalyseReqVO {
 | 
			
		||||
  times: [dayjs.ConfigType, dayjs.ConfigType]
 | 
			
		||||
  times: dayjs.ConfigType[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 会员分析 Response VO */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,39 +0,0 @@
 | 
			
		|||
import request from '@/config/axios'
 | 
			
		||||
 | 
			
		||||
export interface UReportDataVO {
 | 
			
		||||
  id: number
 | 
			
		||||
  name: string
 | 
			
		||||
  status: number
 | 
			
		||||
  content: string
 | 
			
		||||
  remark: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询Ureport2报表分页
 | 
			
		||||
export const getUReportDataPage = async (params) => {
 | 
			
		||||
  return await request.get({ url: `/report/ureport-data/page`, params })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询Ureport2报表详情
 | 
			
		||||
export const getUReportData = async (id: number) => {
 | 
			
		||||
  return await request.get({ url: `/report/ureport-data/get?id=` + id })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 新增Ureport2报表
 | 
			
		||||
export const createUReportData = async (data: UReportDataVO) => {
 | 
			
		||||
  return await request.post({ url: `/report/ureport-data/create`, data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 修改Ureport2报表
 | 
			
		||||
export const updateUReportData = async (data: UReportDataVO) => {
 | 
			
		||||
  return await request.put({ url: `/report/ureport-data/update`, data })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 删除Ureport2报表
 | 
			
		||||
export const deleteUReportData = async (id: number) => {
 | 
			
		||||
  return await request.delete({ url: `/report/ureport-data/delete?id=` + id })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 导出Ureport2报表 Excel
 | 
			
		||||
export const exportUReportData = async (params) => {
 | 
			
		||||
  return await request.download({ url: `/report/ureport-data/export-excel`, params })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,30 +2,6 @@ import request from '@/config/axios'
 | 
			
		|||
 | 
			
		||||
export type OperateLogVO = {
 | 
			
		||||
  id: number
 | 
			
		||||
  userNickname: string
 | 
			
		||||
  traceId: string
 | 
			
		||||
  userId: number
 | 
			
		||||
  module: string
 | 
			
		||||
  name: string
 | 
			
		||||
  type: number
 | 
			
		||||
  content: string
 | 
			
		||||
  exts: Map<String, Object>
 | 
			
		||||
  requestMethod: string
 | 
			
		||||
  requestUrl: string
 | 
			
		||||
  userIp: string
 | 
			
		||||
  userAgent: string
 | 
			
		||||
  javaMethod: string
 | 
			
		||||
  javaMethodArgs: string
 | 
			
		||||
  startTime: Date
 | 
			
		||||
  duration: number
 | 
			
		||||
  resultCode: number
 | 
			
		||||
  resultMsg: string
 | 
			
		||||
  resultData: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type OperateLogV2VO = {
 | 
			
		||||
  id: number
 | 
			
		||||
  userNickname: string
 | 
			
		||||
  traceId: string
 | 
			
		||||
  userType: number
 | 
			
		||||
  userId: number
 | 
			
		||||
| 
						 | 
				
			
			@ -42,11 +18,6 @@ export type OperateLogV2VO = {
 | 
			
		|||
  creator: string
 | 
			
		||||
  creatorName: string
 | 
			
		||||
  createTime: Date
 | 
			
		||||
  // 数据扩展,渲染时使用
 | 
			
		||||
  title: string // 操作标题(如果为空则取 name 值)
 | 
			
		||||
  colSize: number // 变更记录行数
 | 
			
		||||
  contentStrList: string[]
 | 
			
		||||
  tagsContentList: string[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 查询操作日志列表
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +25,6 @@ export const getOperateLogPage = (params: PageParam) => {
 | 
			
		|||
  return request.get({ url: '/system/operate-log/page', params })
 | 
			
		||||
}
 | 
			
		||||
// 导出操作日志
 | 
			
		||||
export const exportOperateLog = (params) => {
 | 
			
		||||
export const exportOperateLog = (params: any) => {
 | 
			
		||||
  return request.download({ url: '/system/operate-log/export', params })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6.1 KiB  | 
| 
						 | 
				
			
			@ -25,6 +25,9 @@ defineProps({
 | 
			
		|||
          </template>
 | 
			
		||||
          <Icon :size="14" class="ml-5px" icon="ep:question-filled" />
 | 
			
		||||
        </ElTooltip>
 | 
			
		||||
        <div class="flex flex-grow pl-20px">
 | 
			
		||||
          <slot name="header"></slot>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
    <div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -503,9 +503,13 @@ const submit = () => {
 | 
			
		|||
  emit('update:modelValue', defaultValue.value)
 | 
			
		||||
  dialogVisible.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const inputChange = () => {
 | 
			
		||||
  emit('update:modelValue', defaultValue.value)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<template>
 | 
			
		||||
  <el-input v-model="defaultValue" class="input-with-select" v-bind="$attrs">
 | 
			
		||||
  <el-input v-model="defaultValue" class="input-with-select" v-bind="$attrs" @input="inputChange">
 | 
			
		||||
    <template #append>
 | 
			
		||||
      <el-select v-model="select" placeholder="生成器" style="width: 115px">
 | 
			
		||||
        <el-option label="每分钟" value="0 * * * * ?" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
import DictSelect from './src/DictSelect.vue'
 | 
			
		||||
 | 
			
		||||
export { DictSelect }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
<!-- 数据字典 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 {
 | 
			
		||||
  modelValue?: any // 值
 | 
			
		||||
  dictType: string // 字典类型
 | 
			
		||||
  valueType: string // 字典值类型
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
  dictType: '',
 | 
			
		||||
  valueType: 'str'
 | 
			
		||||
})
 | 
			
		||||
const attrs = useAttrs()
 | 
			
		||||
defineOptions({ name: 'DictSelect' })
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -180,12 +180,12 @@ defineExpose({
 | 
			
		|||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="z-99 border-1 border-[var(--el-border-color)] border-solid">
 | 
			
		||||
  <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-[var(--el-border-color)] border-solid"
 | 
			
		||||
      class="border-0 b-b-1 border-solid border-[var(--tags-view-border-color)]"
 | 
			
		||||
    />
 | 
			
		||||
    <!-- 编辑器 -->
 | 
			
		||||
    <Editor
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
import MyFormCreateDesigner from './src/MyFormCreateDesigner.vue'
 | 
			
		||||
import { useFormCreateDesigner } from './src/useFormCreateDesigner'
 | 
			
		||||
 | 
			
		||||
export { MyFormCreateDesigner, useFormCreateDesigner }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
<!-- TODO puhui999: 没啥问题的话准备移除 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <FcDesigner ref="designer" height="780px" />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { useUploadFileRule, useUploadImgRule, useUploadImgsRule } from './config'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'MyFormCreateDesigner' })
 | 
			
		||||
 | 
			
		||||
const designer = ref() // 表单设计器
 | 
			
		||||
const uploadFileRule = useUploadFileRule()
 | 
			
		||||
const uploadImgRule = useUploadImgRule()
 | 
			
		||||
const uploadImgsRule = useUploadImgsRule()
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  // 移除自带的上传组件规则
 | 
			
		||||
  designer.value?.removeMenuItem('upload')
 | 
			
		||||
  const components = [uploadFileRule, uploadImgRule, uploadImgsRule]
 | 
			
		||||
  components.forEach((component) => {
 | 
			
		||||
    //插入组件规则
 | 
			
		||||
    designer.value?.addComponent(component)
 | 
			
		||||
    //插入拖拽按钮到`main`分类下
 | 
			
		||||
    designer.value?.appendMenuItem('main', {
 | 
			
		||||
      icon: component.icon,
 | 
			
		||||
      name: component.name,
 | 
			
		||||
      label: component.label
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped></style>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import { useUploadFileRule } from './useUploadFileRule'
 | 
			
		||||
import { useUploadImgRule } from './useUploadImgRule'
 | 
			
		||||
import { useUploadImgsRule } from './useUploadImgsRule'
 | 
			
		||||
import { useDictSelectRule } from './useDictSelectRule'
 | 
			
		||||
import { useUserSelectRule } from './useUserSelectRule'
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  useUploadFileRule,
 | 
			
		||||
  useUploadImgRule,
 | 
			
		||||
  useUploadImgsRule,
 | 
			
		||||
  useDictSelectRule,
 | 
			
		||||
  useUserSelectRule
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
import { generateUUID } from '@/utils'
 | 
			
		||||
import * as DictDataApi from '@/api/system/dict/dict.type'
 | 
			
		||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
 | 
			
		||||
 | 
			
		||||
export const useDictSelectRule = () => {
 | 
			
		||||
  const label = '字典选择器'
 | 
			
		||||
  const name = 'DictSelect'
 | 
			
		||||
  const dictOptions = ref<{ label: string; value: string }[]>([]) // 字典类型下拉数据
 | 
			
		||||
  onMounted(async () => {
 | 
			
		||||
    const data = await DictDataApi.getSimpleDictTypeList()
 | 
			
		||||
    if (!data || data.length === 0) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    dictOptions.value =
 | 
			
		||||
      data?.map((item: DictDataApi.DictTypeVO) => ({
 | 
			
		||||
        label: item.name,
 | 
			
		||||
        value: item.type
 | 
			
		||||
      })) ?? []
 | 
			
		||||
  })
 | 
			
		||||
  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' }
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        { 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,是否在输入框获得焦点后自动弹出选项菜单'
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
import { generateUUID } from '@/utils'
 | 
			
		||||
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
 | 
			
		||||
 | 
			
		||||
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(),
 | 
			
		||||
        { 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,是否在输入框获得焦点后自动弹出选项菜单'
 | 
			
		||||
        }
 | 
			
		||||
      ])
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
import {
 | 
			
		||||
  useDictSelectRule,
 | 
			
		||||
  useUploadFileRule,
 | 
			
		||||
  useUploadImgRule,
 | 
			
		||||
  useUploadImgsRule,
 | 
			
		||||
  useUserSelectRule
 | 
			
		||||
} from './config'
 | 
			
		||||
import { Ref } from 'vue'
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 表单设计器增强 hook
 | 
			
		||||
 * 新增
 | 
			
		||||
 * - 文件上传
 | 
			
		||||
 * - 单图上传
 | 
			
		||||
 * - 多图上传
 | 
			
		||||
 */
 | 
			
		||||
export const useFormCreateDesigner = (designer: Ref) => {
 | 
			
		||||
  const uploadFileRule = useUploadFileRule()
 | 
			
		||||
  const uploadImgRule = useUploadImgRule()
 | 
			
		||||
  const uploadImgsRule = useUploadImgsRule()
 | 
			
		||||
  const dictSelectRule = useDictSelectRule()
 | 
			
		||||
  const userSelectRule = useUserSelectRule()
 | 
			
		||||
 | 
			
		||||
  onMounted(() => {
 | 
			
		||||
    // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
 | 
			
		||||
    designer.value?.removeMenuItem('upload')
 | 
			
		||||
    const components = [
 | 
			
		||||
      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
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ export function createImageViewer(options: ImageViewerProps) {
 | 
			
		|||
    initialIndex = 0,
 | 
			
		||||
    infinite = true,
 | 
			
		||||
    hideOnClickModal = false,
 | 
			
		||||
    appendToBody = false,
 | 
			
		||||
    teleported = false,
 | 
			
		||||
    zIndex = 2000,
 | 
			
		||||
    show = true
 | 
			
		||||
  } = options
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export function createImageViewer(options: ImageViewerProps) {
 | 
			
		|||
  propsData.initialIndex = initialIndex
 | 
			
		||||
  propsData.infinite = infinite
 | 
			
		||||
  propsData.hideOnClickModal = hideOnClickModal
 | 
			
		||||
  propsData.appendToBody = appendToBody
 | 
			
		||||
  propsData.teleported = teleported
 | 
			
		||||
  propsData.zIndex = zIndex
 | 
			
		||||
  propsData.show = show
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ const props = defineProps({
 | 
			
		|||
  initialIndex: propTypes.number.def(0),
 | 
			
		||||
  infinite: propTypes.bool.def(true),
 | 
			
		||||
  hideOnClickModal: propTypes.bool.def(false),
 | 
			
		||||
  appendToBody: propTypes.bool.def(false),
 | 
			
		||||
  teleported: propTypes.bool.def(false),
 | 
			
		||||
  show: propTypes.bool.def(false)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,6 @@ export interface ImageViewerProps {
 | 
			
		|||
  initialIndex?: number
 | 
			
		||||
  infinite?: boolean
 | 
			
		||||
  hideOnClickModal?: boolean
 | 
			
		||||
  appendToBody?: boolean
 | 
			
		||||
  teleported?: boolean
 | 
			
		||||
  show?: boolean
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { OperateLogV2VO } from '@/api/system/operatelog'
 | 
			
		||||
import { OperateLogVO } from '@/api/system/operatelog'
 | 
			
		||||
import { formatDate } from '@/utils/formatTime'
 | 
			
		||||
import { DICT_TYPE, getDictLabel, getDictObj } from '@/utils/dict'
 | 
			
		||||
import { ElTag } from 'element-plus'
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ import { ElTag } from 'element-plus'
 | 
			
		|||
defineOptions({ name: 'OperateLogV2' })
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  logList: OperateLogV2VO[] // 操作日志列表
 | 
			
		||||
  logList: OperateLogVO[] // 操作日志列表
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
withDefaults(defineProps<Props>(), {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ const props = defineProps({
 | 
			
		|||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:page', 'update:limit', 'pagination', 'pagination'])
 | 
			
		||||
const emit = defineEmits(['update:page', 'update:limit', 'pagination'])
 | 
			
		||||
const currentPage = computed({
 | 
			
		||||
  get() {
 | 
			
		||||
    return props.page
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@
 | 
			
		|||
      placeholder="请输入菜单内容"
 | 
			
		||||
      :remote-method="remoteMethod"
 | 
			
		||||
      class="overflow-hidden transition-all-600"
 | 
			
		||||
      :class="showTopSearch ? 'w-220px ml2' : 'w-0'"
 | 
			
		||||
      :class="showTopSearch ? '!w-220px ml2' : '!w-0'"
 | 
			
		||||
      @change="handleChange"
 | 
			
		||||
    >
 | 
			
		||||
      <el-option
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,237 @@
 | 
			
		|||
/* stylelint-disable order/properties-order */
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="add-node-btn-box">
 | 
			
		||||
    <div class="add-node-btn">
 | 
			
		||||
      <el-popover placement="right-start" v-model="visible" width="auto">
 | 
			
		||||
        <div class="add-node-popover-body">
 | 
			
		||||
          <a class="add-node-popover-item approver" @click="addType(1)">
 | 
			
		||||
            <div class="item-wrapper">
 | 
			
		||||
              <span class="iconfont"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p>审批人</p>
 | 
			
		||||
          </a>
 | 
			
		||||
          <a class="add-node-popover-item notifier" @click="addType(2)">
 | 
			
		||||
            <div class="item-wrapper">
 | 
			
		||||
              <span class="iconfont"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p>抄送人</p>
 | 
			
		||||
          </a>
 | 
			
		||||
          <a class="add-node-popover-item condition" @click="addType(4)">
 | 
			
		||||
            <div class="item-wrapper">
 | 
			
		||||
              <span class="iconfont"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p>条件分支</p>
 | 
			
		||||
          </a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <template #reference>
 | 
			
		||||
          <button class="btn" type="button">
 | 
			
		||||
            <span class="iconfont"></span>
 | 
			
		||||
          </button>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-popover>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup>
 | 
			
		||||
import { ref } from 'vue'
 | 
			
		||||
let props = defineProps({
 | 
			
		||||
  childNodeP: {
 | 
			
		||||
    type: Object,
 | 
			
		||||
    default: () => ({})
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
let emits = defineEmits(['update:childNodeP'])
 | 
			
		||||
let visible = ref(false)
 | 
			
		||||
const addType = (type) => {
 | 
			
		||||
  visible.value = false
 | 
			
		||||
  if (type != 4) {
 | 
			
		||||
    var data
 | 
			
		||||
    if (type == 1) {
 | 
			
		||||
      data = {
 | 
			
		||||
        nodeName: '审核人',
 | 
			
		||||
        error: true,
 | 
			
		||||
        type: 1,
 | 
			
		||||
        settype: 1,
 | 
			
		||||
        selectMode: 0,
 | 
			
		||||
        selectRange: 0,
 | 
			
		||||
        directorLevel: 1,
 | 
			
		||||
        examineMode: 1,
 | 
			
		||||
        noHanderAction: 1,
 | 
			
		||||
        examineEndDirectorLevel: 0,
 | 
			
		||||
        childNode: props.childNodeP,
 | 
			
		||||
        nodeUserList: []
 | 
			
		||||
      }
 | 
			
		||||
    } else if (type == 2) {
 | 
			
		||||
      data = {
 | 
			
		||||
        nodeName: '抄送人',
 | 
			
		||||
        type: 2,
 | 
			
		||||
        ccSelfSelectFlag: 1,
 | 
			
		||||
        childNode: props.childNodeP,
 | 
			
		||||
        nodeUserList: []
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    emits('update:childNodeP', data)
 | 
			
		||||
  } else {
 | 
			
		||||
    emits('update:childNodeP', {
 | 
			
		||||
      nodeName: '路由',
 | 
			
		||||
      type: 4,
 | 
			
		||||
      childNode: null,
 | 
			
		||||
      conditionNodes: [
 | 
			
		||||
        {
 | 
			
		||||
          nodeName: '条件1',
 | 
			
		||||
          error: true,
 | 
			
		||||
          type: 3,
 | 
			
		||||
          priorityLevel: 1,
 | 
			
		||||
          conditionList: [],
 | 
			
		||||
          nodeUserList: [],
 | 
			
		||||
          childNode: props.childNodeP
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          nodeName: '条件2',
 | 
			
		||||
          type: 3,
 | 
			
		||||
          priorityLevel: 2,
 | 
			
		||||
          conditionList: [],
 | 
			
		||||
          nodeUserList: [],
 | 
			
		||||
          childNode: null
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
.add-node-btn-box {
 | 
			
		||||
  width: 240px;
 | 
			
		||||
  display: inline-flex;
 | 
			
		||||
  -ms-flex-negative: 0;
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
  -webkit-box-flex: 1;
 | 
			
		||||
  -ms-flex-positive: 1;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  &:before {
 | 
			
		||||
    content: '';
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    z-index: -1;
 | 
			
		||||
    margin: auto;
 | 
			
		||||
    width: 2px;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    background-color: #cacaca;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .add-node-btn {
 | 
			
		||||
    user-select: none;
 | 
			
		||||
    width: 240px;
 | 
			
		||||
    padding: 20px 0 32px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    -webkit-box-pack: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
    -webkit-box-flex: 1;
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
 | 
			
		||||
    .btn {
 | 
			
		||||
      outline: none;
 | 
			
		||||
      box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
 | 
			
		||||
      width: 30px;
 | 
			
		||||
      height: 30px;
 | 
			
		||||
      background: #3296fa;
 | 
			
		||||
      border-radius: 50%;
 | 
			
		||||
      position: relative;
 | 
			
		||||
      border: none;
 | 
			
		||||
      line-height: 30px;
 | 
			
		||||
      -webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
 | 
			
		||||
      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
 | 
			
		||||
 | 
			
		||||
      .iconfont {
 | 
			
		||||
        color: #fff;
 | 
			
		||||
        font-size: 16px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:hover {
 | 
			
		||||
        transform: scale(1.3);
 | 
			
		||||
        box-shadow: 0 13px 27px 0 rgba(0, 0, 0, 0.1);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &:active {
 | 
			
		||||
        transform: none;
 | 
			
		||||
        background: #1e83e9;
 | 
			
		||||
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.add-node-popover-body {
 | 
			
		||||
  display: flex;
 | 
			
		||||
 | 
			
		||||
  .add-node-popover-item {
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    color: #191f25 !important;
 | 
			
		||||
 | 
			
		||||
    .item-wrapper {
 | 
			
		||||
      user-select: none;
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      width: 80px;
 | 
			
		||||
      height: 80px;
 | 
			
		||||
      margin-bottom: 5px;
 | 
			
		||||
      background: #fff;
 | 
			
		||||
      border: 1px solid #e2e2e2;
 | 
			
		||||
      border-radius: 50%;
 | 
			
		||||
      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
 | 
			
		||||
 | 
			
		||||
      .iconfont {
 | 
			
		||||
        font-size: 35px;
 | 
			
		||||
        line-height: 80px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.approver {
 | 
			
		||||
      .item-wrapper {
 | 
			
		||||
        color: #ff943e;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.notifier {
 | 
			
		||||
      .item-wrapper {
 | 
			
		||||
        color: #3296fa;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.condition {
 | 
			
		||||
      .item-wrapper {
 | 
			
		||||
        color: #15bc83;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:hover {
 | 
			
		||||
      .item-wrapper {
 | 
			
		||||
        background: #3296fa;
 | 
			
		||||
        box-shadow: 0 10px 20px 0 rgba(50, 150, 250, 0.4);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .iconfont {
 | 
			
		||||
        color: #fff;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:active {
 | 
			
		||||
      .item-wrapper {
 | 
			
		||||
        box-shadow: none;
 | 
			
		||||
        background: #eaeaea;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .iconfont {
 | 
			
		||||
        color: inherit;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,297 @@
 | 
			
		|||
<!-- eslint-disable vue/no-mutating-props -->
 | 
			
		||||
<!--
 | 
			
		||||
 * @Date: 2022-09-21 14:41:53
 | 
			
		||||
 * @LastEditors: StavinLi 495727881@qq.com
 | 
			
		||||
 * @LastEditTime: 2023-05-24 15:20:24
 | 
			
		||||
 * @FilePath: /Workflow-Vue3/src/components/nodeWrap.vue
 | 
			
		||||
-->
 | 
			
		||||
<template>
 | 
			
		||||
     <div class="node-wrap" v-if="nodeConfig.type < 3">
 | 
			
		||||
      <div class="node-wrap-box" :class="(nodeConfig.type == 0 ? 'start-node ' : '') +(isTried && nodeConfig.error ? 'active error' : '')">
 | 
			
		||||
          <div class="title" :style="`background: rgb(${bgColors[nodeConfig.type]});`">
 | 
			
		||||
            <span v-if="nodeConfig.type == 0">{{ nodeConfig.nodeName }}</span>
 | 
			
		||||
            <template v-else>
 | 
			
		||||
              <span class="iconfont">{{nodeConfig.type == 1?'':''}}</span>
 | 
			
		||||
              <input
 | 
			
		||||
                v-if="isInput"
 | 
			
		||||
                type="text"
 | 
			
		||||
                class="ant-input editable-title-input"
 | 
			
		||||
                @blur="blurEvent()"
 | 
			
		||||
                @focus="$event.currentTarget.select()"
 | 
			
		||||
                v-focus
 | 
			
		||||
                v-model="nodeConfig.nodeName"
 | 
			
		||||
                :placeholder="defaultText"
 | 
			
		||||
              />
 | 
			
		||||
              <span v-else class="editable-title" @click="clickEvent()">{{ nodeConfig.nodeName }}</span>
 | 
			
		||||
              <i class="anticon anticon-close close" @click="delNode"></i>
 | 
			
		||||
            </template>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="content" @click="setPerson">
 | 
			
		||||
            <div class="text">
 | 
			
		||||
                <span class="placeholder" v-if="!showText">请选择{{defaultText}}</span>
 | 
			
		||||
                {{showText}}
 | 
			
		||||
            </div>
 | 
			
		||||
            <i class="anticon anticon-right arrow"></i>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="error_tip" v-if="isTried && nodeConfig.error">
 | 
			
		||||
            <i class="anticon anticon-exclamation-circle"></i>
 | 
			
		||||
          </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <addNode v-model:childNodeP="nodeConfig.childNode" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="branch-wrap" v-if="nodeConfig.type == 4">
 | 
			
		||||
    <div class="branch-box-wrap">
 | 
			
		||||
      <div class="branch-box">
 | 
			
		||||
        <button class="add-branch" @click="addTerm">添加条件</button>
 | 
			
		||||
        <div class="col-box" v-for="(item, index) in nodeConfig.conditionNodes" :key="index">
 | 
			
		||||
          <div class="condition-node">
 | 
			
		||||
            <div class="condition-node-box">
 | 
			
		||||
              <div class="auto-judge" :class="isTried && item.error ? 'error active' : ''">
 | 
			
		||||
                <div class="sort-left" v-if="index != 0" @click="arrTransfer(index, -1)"><</div>
 | 
			
		||||
                <div class="title-wrapper">
 | 
			
		||||
                  <input
 | 
			
		||||
                    v-if="isInputList[index]"
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    class="ant-input editable-title-input"
 | 
			
		||||
                    @blur="blurEvent(index)"
 | 
			
		||||
                    @focus="$event.currentTarget.select()"
 | 
			
		||||
                    v-model="item.nodeName"
 | 
			
		||||
                  />
 | 
			
		||||
                  <span v-else class="editable-title" @click="clickEvent(index)">{{ item.nodeName }}</span>
 | 
			
		||||
                  <span class="priority-title" @click="setPerson(item.priorityLevel)">优先级{{ item.priorityLevel }}</span>
 | 
			
		||||
                  <i class="anticon anticon-close close" @click="delTerm(index)"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="sort-right" v-if="index != nodeConfig.conditionNodes.length - 1" @click="arrTransfer(index)">></div>
 | 
			
		||||
                <div class="content" @click="setPerson(item.priorityLevel)">{{ conditionStr(nodeConfig, index) }}</div>
 | 
			
		||||
                <div class="error_tip" v-if="isTried && item.error">
 | 
			
		||||
                    <i class="anticon anticon-exclamation-circle"></i>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              <addNode v-model:childNodeP="item.childNode" />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <nodeWrap v-if="item.childNode" v-model:nodeConfig="item.childNode" />
 | 
			
		||||
          <template v-if="index == 0">
 | 
			
		||||
            <div class="top-left-cover-line"></div>
 | 
			
		||||
            <div class="bottom-left-cover-line"></div>
 | 
			
		||||
          </template>
 | 
			
		||||
          <template v-if="index == nodeConfig.conditionNodes.length - 1">
 | 
			
		||||
            <div class="top-right-cover-line"></div>
 | 
			
		||||
            <div class="bottom-right-cover-line"></div>
 | 
			
		||||
          </template>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <addNode v-model:childNodeP="nodeConfig.childNode" />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
    <nodeWrap v-if="nodeConfig.childNode" v-model:nodeConfig="nodeConfig.childNode" />
 | 
			
		||||
</template>
 | 
			
		||||
<script  setup>
 | 
			
		||||
import addNode from './addNode.vue'
 | 
			
		||||
import { onMounted, ref, watch, getCurrentInstance, computed } from 'vue'
 | 
			
		||||
import {
 | 
			
		||||
  arrToStr,
 | 
			
		||||
  conditionStr,
 | 
			
		||||
  setApproverStr,
 | 
			
		||||
  copyerStr,
 | 
			
		||||
  bgColors,
 | 
			
		||||
  placeholderList
 | 
			
		||||
} from './util'
 | 
			
		||||
import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
 | 
			
		||||
let _uid = getCurrentInstance().uid
 | 
			
		||||
 | 
			
		||||
let props = defineProps({
 | 
			
		||||
  nodeConfig: {
 | 
			
		||||
    type: Object,
 | 
			
		||||
    default: () => ({})
 | 
			
		||||
  },
 | 
			
		||||
  flowPermission: {
 | 
			
		||||
    type: Object,
 | 
			
		||||
    // eslint-disable-next-line vue/require-valid-default-prop
 | 
			
		||||
    default: () => []
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
let defaultText = computed(() => {
 | 
			
		||||
  return placeholderList[props.nodeConfig.type]
 | 
			
		||||
})
 | 
			
		||||
let showText = computed(() => {
 | 
			
		||||
  if (props.nodeConfig.type == 0) return arrToStr(props.flowPermission) || '所有人'
 | 
			
		||||
  if (props.nodeConfig.type == 1) return setApproverStr(props.nodeConfig)
 | 
			
		||||
  return copyerStr(props.nodeConfig)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
let isInputList = ref([])
 | 
			
		||||
let isInput = ref(false)
 | 
			
		||||
const resetConditionNodesErr = () => {
 | 
			
		||||
  for (var i = 0; i < props.nodeConfig.conditionNodes.length; i++) {
 | 
			
		||||
    // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
    props.nodeConfig.conditionNodes[i].error =
 | 
			
		||||
      conditionStr(props.nodeConfig, i) == '请设置条件' &&
 | 
			
		||||
      i != props.nodeConfig.conditionNodes.length - 1
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  if (props.nodeConfig.type == 1) {
 | 
			
		||||
    // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
    props.nodeConfig.error = !setApproverStr(props.nodeConfig)
 | 
			
		||||
  } else if (props.nodeConfig.type == 2) {
 | 
			
		||||
    // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
    props.nodeConfig.error = !copyerStr(props.nodeConfig)
 | 
			
		||||
  } else if (props.nodeConfig.type == 4) {
 | 
			
		||||
    resetConditionNodesErr()
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
let emits = defineEmits(['update:flowPermission', 'update:nodeConfig'])
 | 
			
		||||
let store = useWorkFlowStoreWithOut()
 | 
			
		||||
let {
 | 
			
		||||
  setPromoter,
 | 
			
		||||
  setApprover,
 | 
			
		||||
  setCopyer,
 | 
			
		||||
  setCondition,
 | 
			
		||||
  setFlowPermission,
 | 
			
		||||
  setApproverConfig,
 | 
			
		||||
  setCopyerConfig,
 | 
			
		||||
  setConditionsConfig
 | 
			
		||||
} = store
 | 
			
		||||
let isTried = computed(() => store.isTried)
 | 
			
		||||
let flowPermission1 = computed(() => store.flowPermission1)
 | 
			
		||||
let approverConfig1 = computed(() => store.approverConfig1)
 | 
			
		||||
let copyerConfig1 = computed(() => store.copyerConfig1)
 | 
			
		||||
let conditionsConfig1 = computed(() => store.conditionsConfig1)
 | 
			
		||||
watch(flowPermission1, (flow) => {
 | 
			
		||||
  if (flow.flag && flow.id === _uid) {
 | 
			
		||||
    emits('update:flowPermission', flow.value)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
watch(approverConfig1, (approver) => {
 | 
			
		||||
  if (approver.flag && approver.id === _uid) {
 | 
			
		||||
    emits('update:nodeConfig', approver.value)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
watch(copyerConfig1, (copyer) => {
 | 
			
		||||
  if (copyer.flag && copyer.id === _uid) {
 | 
			
		||||
    emits('update:nodeConfig', copyer.value)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
watch(conditionsConfig1, (condition) => {
 | 
			
		||||
  if (condition.flag && condition.id === _uid) {
 | 
			
		||||
    emits('update:nodeConfig', condition.value)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const clickEvent = (index) => {
 | 
			
		||||
  if (index || index === 0) {
 | 
			
		||||
    isInputList.value[index] = true
 | 
			
		||||
  } else {
 | 
			
		||||
    isInput.value = true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const blurEvent = (index) => {
 | 
			
		||||
  if (index || index === 0) {
 | 
			
		||||
    isInputList.value[index] = false
 | 
			
		||||
    // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
    props.nodeConfig.conditionNodes[index].nodeName =
 | 
			
		||||
      props.nodeConfig.conditionNodes[index].nodeName || '条件'
 | 
			
		||||
  } else {
 | 
			
		||||
    isInput.value = false
 | 
			
		||||
    // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
    props.nodeConfig.nodeName = props.nodeConfig.nodeName || defaultText
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const delNode = () => {
 | 
			
		||||
  emits('update:nodeConfig', props.nodeConfig.childNode)
 | 
			
		||||
}
 | 
			
		||||
const addTerm = () => {
 | 
			
		||||
  let len = props.nodeConfig.conditionNodes.length + 1
 | 
			
		||||
  // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
  props.nodeConfig.conditionNodes.push({
 | 
			
		||||
    nodeName: '条件' + len,
 | 
			
		||||
    type: 3,
 | 
			
		||||
    priorityLevel: len,
 | 
			
		||||
    conditionList: [],
 | 
			
		||||
    nodeUserList: [],
 | 
			
		||||
    childNode: null
 | 
			
		||||
  })
 | 
			
		||||
  resetConditionNodesErr()
 | 
			
		||||
  emits('update:nodeConfig', props.nodeConfig)
 | 
			
		||||
}
 | 
			
		||||
const delTerm = (index) => {
 | 
			
		||||
  // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
  props.nodeConfig.conditionNodes.splice(index, 1)
 | 
			
		||||
  props.nodeConfig.conditionNodes.map((item, index) => {
 | 
			
		||||
    item.priorityLevel = index + 1
 | 
			
		||||
    item.nodeName = `条件${index + 1}`
 | 
			
		||||
  })
 | 
			
		||||
  resetConditionNodesErr()
 | 
			
		||||
  emits('update:nodeConfig', props.nodeConfig)
 | 
			
		||||
  if (props.nodeConfig.conditionNodes.length == 1) {
 | 
			
		||||
    if (props.nodeConfig.childNode) {
 | 
			
		||||
      if (props.nodeConfig.conditionNodes[0].childNode) {
 | 
			
		||||
        reData(props.nodeConfig.conditionNodes[0].childNode, props.nodeConfig.childNode)
 | 
			
		||||
      } else {
 | 
			
		||||
        // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
        props.nodeConfig.conditionNodes[0].childNode = props.nodeConfig.childNode
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    emits('update:nodeConfig', props.nodeConfig.conditionNodes[0].childNode)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const reData = (data, addData) => {
 | 
			
		||||
  if (!data.childNode) {
 | 
			
		||||
    data.childNode = addData
 | 
			
		||||
  } else {
 | 
			
		||||
    reData(data.childNode, addData)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const setPerson = (priorityLevel) => {
 | 
			
		||||
  var { type } = props.nodeConfig
 | 
			
		||||
  if (type == 0) {
 | 
			
		||||
    setPromoter(true)
 | 
			
		||||
    setFlowPermission({
 | 
			
		||||
      value: props.flowPermission,
 | 
			
		||||
      flag: false,
 | 
			
		||||
      id: _uid
 | 
			
		||||
    })
 | 
			
		||||
  } else if (type == 1) {
 | 
			
		||||
    setApprover(true)
 | 
			
		||||
    setApproverConfig({
 | 
			
		||||
      value: {
 | 
			
		||||
        ...JSON.parse(JSON.stringify(props.nodeConfig)),
 | 
			
		||||
        ...{ settype: props.nodeConfig.settype ? props.nodeConfig.settype : 1 }
 | 
			
		||||
      },
 | 
			
		||||
      flag: false,
 | 
			
		||||
      id: _uid
 | 
			
		||||
    })
 | 
			
		||||
  } else if (type == 2) {
 | 
			
		||||
    setCopyer(true)
 | 
			
		||||
    setCopyerConfig({
 | 
			
		||||
      value: JSON.parse(JSON.stringify(props.nodeConfig)),
 | 
			
		||||
      flag: false,
 | 
			
		||||
      id: _uid
 | 
			
		||||
    })
 | 
			
		||||
  } else {
 | 
			
		||||
    setCondition(true)
 | 
			
		||||
    setConditionsConfig({
 | 
			
		||||
      value: JSON.parse(JSON.stringify(props.nodeConfig)),
 | 
			
		||||
      priorityLevel,
 | 
			
		||||
      flag: false,
 | 
			
		||||
      id: _uid
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const arrTransfer = (index, type = 1) => {
 | 
			
		||||
  //向左-1,向右1
 | 
			
		||||
  // eslint-disable-next-line vue/no-mutating-props
 | 
			
		||||
  props.nodeConfig.conditionNodes[index] = props.nodeConfig.conditionNodes.splice(
 | 
			
		||||
    index + type,
 | 
			
		||||
    1,
 | 
			
		||||
    props.nodeConfig.conditionNodes[index]
 | 
			
		||||
  )[0]
 | 
			
		||||
  props.nodeConfig.conditionNodes.map((item, index) => {
 | 
			
		||||
    item.priorityLevel = index + 1
 | 
			
		||||
  })
 | 
			
		||||
  resetConditionNodesErr()
 | 
			
		||||
  emits('update:nodeConfig', props.nodeConfig)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,165 @@
 | 
			
		|||
/**
 | 
			
		||||
 * todo
 | 
			
		||||
 */
 | 
			
		||||
export const arrToStr = (arr?: [{ name: string }]) => {
 | 
			
		||||
  if (arr) {
 | 
			
		||||
    return arr
 | 
			
		||||
      .map((item) => {
 | 
			
		||||
        return item.name
 | 
			
		||||
      })
 | 
			
		||||
      .toString()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setApproverStr = (nodeConfig: any) => {
 | 
			
		||||
  if (nodeConfig.settype == 1) {
 | 
			
		||||
    if (nodeConfig.nodeUserList.length == 1) {
 | 
			
		||||
      return nodeConfig.nodeUserList[0].name
 | 
			
		||||
    } else if (nodeConfig.nodeUserList.length > 1) {
 | 
			
		||||
      if (nodeConfig.examineMode == 1) {
 | 
			
		||||
        return arrToStr(nodeConfig.nodeUserList)
 | 
			
		||||
      } else if (nodeConfig.examineMode == 2) {
 | 
			
		||||
        return nodeConfig.nodeUserList.length + '人会签'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if (nodeConfig.settype == 2) {
 | 
			
		||||
    const level =
 | 
			
		||||
      nodeConfig.directorLevel == 1 ? '直接主管' : '第' + nodeConfig.directorLevel + '级主管'
 | 
			
		||||
    if (nodeConfig.examineMode == 1) {
 | 
			
		||||
      return level
 | 
			
		||||
    } else if (nodeConfig.examineMode == 2) {
 | 
			
		||||
      return level + '会签'
 | 
			
		||||
    }
 | 
			
		||||
  } else if (nodeConfig.settype == 4) {
 | 
			
		||||
    if (nodeConfig.selectRange == 1) {
 | 
			
		||||
      return '发起人自选'
 | 
			
		||||
    } else {
 | 
			
		||||
      if (nodeConfig.nodeUserList.length > 0) {
 | 
			
		||||
        if (nodeConfig.selectRange == 2) {
 | 
			
		||||
          return '发起人自选'
 | 
			
		||||
        } else {
 | 
			
		||||
          return '发起人从' + nodeConfig.nodeUserList[0].name + '中自选'
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return ''
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if (nodeConfig.settype == 5) {
 | 
			
		||||
    return '发起人自己'
 | 
			
		||||
  } else if (nodeConfig.settype == 7) {
 | 
			
		||||
    return '从直接主管到通讯录中级别最高的第' + nodeConfig.examineEndDirectorLevel + '个层级主管'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const copyerStr = (nodeConfig: any) => {
 | 
			
		||||
  if (nodeConfig.nodeUserList.length != 0) {
 | 
			
		||||
    return arrToStr(nodeConfig.nodeUserList)
 | 
			
		||||
  } else {
 | 
			
		||||
    if (nodeConfig.ccSelfSelectFlag == 1) {
 | 
			
		||||
      return '发起人自选'
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
export const conditionStr = (nodeConfig, index) => {
 | 
			
		||||
  const { conditionList, nodeUserList } = nodeConfig.conditionNodes[index]
 | 
			
		||||
  if (conditionList.length == 0) {
 | 
			
		||||
    return index == nodeConfig.conditionNodes.length - 1 &&
 | 
			
		||||
      nodeConfig.conditionNodes[0].conditionList.length != 0
 | 
			
		||||
      ? '其他条件进入此流程'
 | 
			
		||||
      : '请设置条件'
 | 
			
		||||
  } else {
 | 
			
		||||
    let str = ''
 | 
			
		||||
    for (let i = 0; i < conditionList.length; i++) {
 | 
			
		||||
      const {
 | 
			
		||||
        columnId,
 | 
			
		||||
        columnType,
 | 
			
		||||
        showType,
 | 
			
		||||
        showName,
 | 
			
		||||
        optType,
 | 
			
		||||
        zdy1,
 | 
			
		||||
        opt1,
 | 
			
		||||
        zdy2,
 | 
			
		||||
        opt2,
 | 
			
		||||
        fixedDownBoxValue
 | 
			
		||||
      } = conditionList[i]
 | 
			
		||||
      if (columnId == 0) {
 | 
			
		||||
        if (nodeUserList.length != 0) {
 | 
			
		||||
          str += '发起人属于:'
 | 
			
		||||
          str +=
 | 
			
		||||
            nodeUserList
 | 
			
		||||
              .map((item) => {
 | 
			
		||||
                return item.name
 | 
			
		||||
              })
 | 
			
		||||
              .join('或') + ' 并且 '
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (columnType == 'String' && showType == '3') {
 | 
			
		||||
        if (zdy1) {
 | 
			
		||||
          str += showName + '属于:' + dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + ' 并且 '
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (columnType == 'Double') {
 | 
			
		||||
        if (optType != 6 && zdy1) {
 | 
			
		||||
          const optTypeStr = ['', '<', '>', '≤', '=', '≥'][optType]
 | 
			
		||||
          str += `${showName} ${optTypeStr} ${zdy1} 并且 `
 | 
			
		||||
        } else if (optType == 6 && zdy1 && zdy2) {
 | 
			
		||||
          str += `${zdy1} ${opt1} ${showName} ${opt2} ${zdy2} 并且 `
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return str ? str.substring(0, str.length - 4) : '请设置条件'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const dealStr = (str: string, obj) => {
 | 
			
		||||
  const arr = []
 | 
			
		||||
  const list = str.split(',')
 | 
			
		||||
  for (const elem in obj) {
 | 
			
		||||
    list.map((item) => {
 | 
			
		||||
      if (item == elem) {
 | 
			
		||||
        arr.push(obj[elem].value)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
  return arr.join('或')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeEle = (arr, elem, key = 'id') => {
 | 
			
		||||
  let includesIndex
 | 
			
		||||
  arr.map((item, index) => {
 | 
			
		||||
    if (item[key] == elem[key]) {
 | 
			
		||||
      includesIndex = index
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  arr.splice(includesIndex, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const bgColors = ['87, 106, 149', '255, 148, 62', '50, 150, 250']
 | 
			
		||||
export const placeholderList = ['发起人', '审核人', '抄送人']
 | 
			
		||||
export const setTypes = [
 | 
			
		||||
  { value: 1, label: '指定成员' },
 | 
			
		||||
  { value: 2, label: '主管' },
 | 
			
		||||
  { value: 4, label: '发起人自选' },
 | 
			
		||||
  { value: 5, label: '发起人自己' },
 | 
			
		||||
  { value: 7, label: '连续多级主管' }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export const selectModes = [
 | 
			
		||||
  { value: 1, label: '选一个人' },
 | 
			
		||||
  { value: 2, label: '选多个人' }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export const selectRanges = [
 | 
			
		||||
  { value: 1, label: '全公司' },
 | 
			
		||||
  { value: 2, label: '指定成员' },
 | 
			
		||||
  { value: 3, label: '指定角色' }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export const optTypes = [
 | 
			
		||||
  { value: '1', label: '小于' },
 | 
			
		||||
  { value: '2', label: '大于' },
 | 
			
		||||
  { value: '3', label: '小于等于' },
 | 
			
		||||
  { value: '4', label: '等于' },
 | 
			
		||||
  { value: '5', label: '大于等于' },
 | 
			
		||||
  { value: '6', label: '介于两个数之间' }
 | 
			
		||||
]
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -6,7 +6,9 @@
 | 
			
		|||
      :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"
 | 
			
		||||
| 
						 | 
				
			
			@ -15,15 +17,14 @@
 | 
			
		|||
      :on-remove="handleRemove"
 | 
			
		||||
      :on-success="handleFileSuccess"
 | 
			
		||||
      :show-file-list="true"
 | 
			
		||||
      :http-request="httpRequest"
 | 
			
		||||
      class="upload-file-uploader"
 | 
			
		||||
      name="file"
 | 
			
		||||
    >
 | 
			
		||||
      <el-button type="primary">
 | 
			
		||||
      <el-button v-if="!disabled" type="primary">
 | 
			
		||||
        <Icon icon="ep:upload-filled" />
 | 
			
		||||
        选取文件
 | 
			
		||||
      </el-button>
 | 
			
		||||
      <template v-if="isShowTip" #tip>
 | 
			
		||||
      <template v-if="isShowTip && !disabled" #tip>
 | 
			
		||||
        <div style="font-size: 8px">
 | 
			
		||||
          大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -48,13 +49,13 @@ const emit = defineEmits(['update:modelValue'])
 | 
			
		|||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
 | 
			
		||||
  title: propTypes.string.def('文件上传'),
 | 
			
		||||
  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) // 是否显示提示
 | 
			
		||||
  isShowTip: propTypes.bool.def(true), // 是否显示提示
 | 
			
		||||
  disabled: propTypes.bool.def(false) // 是否禁用上传组件 ==> 非必传(默认为 false)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// ========== 上传相关 ==========
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,17 +6,18 @@
 | 
			
		|||
      :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"
 | 
			
		||||
      :http-request="httpRequest"
 | 
			
		||||
    >
 | 
			
		||||
      <template v-if="modelValue">
 | 
			
		||||
        <img :src="modelValue" class="upload-image" />
 | 
			
		||||
        <div class="upload-handle" @click.stop>
 | 
			
		||||
          <div class="handle-icon" @click="editImg" v-if="!disabled">
 | 
			
		||||
          <div v-if="!disabled" class="handle-icon" @click="editImg">
 | 
			
		||||
            <Icon icon="ep:edit" />
 | 
			
		||||
            <span v-if="showBtnText">{{ t('action.edit') }}</span>
 | 
			
		||||
          </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -77,10 +78,8 @@ const props = defineProps({
 | 
			
		|||
  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)
 | 
			
		||||
  showDelete: propTypes.bool.def(true), // 是否显示删除按钮
 | 
			
		||||
  showBtnText: propTypes.bool.def(true) // 是否显示按钮文字
 | 
			
		||||
})
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,13 +6,14 @@
 | 
			
		|||
      :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"
 | 
			
		||||
      :http-request="httpRequest"
 | 
			
		||||
      list-type="picture-card"
 | 
			
		||||
    >
 | 
			
		||||
      <div class="upload-empty">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -436,7 +436,7 @@ const initBpmnModeler = () => {
 | 
			
		|||
 | 
			
		||||
  // bpmnModeler.createDiagram()
 | 
			
		||||
 | 
			
		||||
  console.log(bpmnModeler, 'bpmnModeler111111')
 | 
			
		||||
  // console.log(bpmnModeler, 'bpmnModeler111111')
 | 
			
		||||
  emit('init-finished', bpmnModeler)
 | 
			
		||||
  initModelListeners()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -666,10 +666,10 @@ const previewProcessJson = () => {
 | 
			
		|||
}
 | 
			
		||||
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
 | 
			
		||||
const processSave = async () => {
 | 
			
		||||
  console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler')
 | 
			
		||||
  // console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler')
 | 
			
		||||
  const { err, xml } = await bpmnModeler.saveXML()
 | 
			
		||||
  console.log(err, 'errerrerrerrerr')
 | 
			
		||||
  console.log(xml, 'xmlxmlxmlxmlxml')
 | 
			
		||||
  // console.log(err, 'errerrerrerrerr')
 | 
			
		||||
  // console.log(xml, 'xmlxmlxmlxmlxml')
 | 
			
		||||
  // 读取异常时抛出异常
 | 
			
		||||
  if (err) {
 | 
			
		||||
    // this.$modal.msgError('保存模型失败,请重试!')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,19 +115,19 @@ const highlightDiagram = async () => {
 | 
			
		|||
      if (!task) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      //进行中的任务已经高亮过了,则不高亮后面的任务了
 | 
			
		||||
      // 进行中的任务已经高亮过了,则不高亮后面的任务了
 | 
			
		||||
      if (findProcessTask) {
 | 
			
		||||
        removeTaskDefinitionKeyList.push(n.id)
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      // 高亮任务
 | 
			
		||||
      canvas.addMarker(n.id, getResultCss(task.result))
 | 
			
		||||
      canvas.addMarker(n.id, getResultCss(task.status))
 | 
			
		||||
      //标记是否高亮了进行中任务
 | 
			
		||||
      if (task.result === 1) {
 | 
			
		||||
      if (task.status === 1) {
 | 
			
		||||
        findProcessTask = true
 | 
			
		||||
      }
 | 
			
		||||
      // 如果非通过,就不走后面的线条了
 | 
			
		||||
      if (task.result !== 2) {
 | 
			
		||||
      if (task.status !== 2) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      // 处理 outgoing 出线
 | 
			
		||||
| 
						 | 
				
			
			@ -194,6 +194,7 @@ const highlightDiagram = async () => {
 | 
			
		|||
      })
 | 
			
		||||
    } else if (n.$type === 'bpmn:StartEvent') {
 | 
			
		||||
      // 开始节点
 | 
			
		||||
      canvas.addMarker(n.id, 'highlight')
 | 
			
		||||
      n.outgoing?.forEach((nn) => {
 | 
			
		||||
        // outgoing 例如说【bpmn:SequenceFlow】连线
 | 
			
		||||
        // 获得连线是否有指向目标。如果有,则进行高亮
 | 
			
		||||
| 
						 | 
				
			
			@ -205,10 +206,10 @@ const highlightDiagram = async () => {
 | 
			
		|||
      })
 | 
			
		||||
    } else if (n.$type === 'bpmn:EndEvent') {
 | 
			
		||||
      // 结束节点
 | 
			
		||||
      if (!processInstance.value || processInstance.value.result === 1) {
 | 
			
		||||
      if (!processInstance.value || processInstance.value.status === 1) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      canvas.addMarker(n.id, getResultCss(processInstance.value.result))
 | 
			
		||||
      canvas.addMarker(n.id, getResultCss(processInstance.value.status))
 | 
			
		||||
    } else if (n.$type === 'bpmn:ServiceTask') {
 | 
			
		||||
      //服务任务
 | 
			
		||||
      if (activity.startTime > 0 && activity.endTime === 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -223,39 +224,49 @@ const highlightDiagram = async () => {
 | 
			
		|||
          canvas.addMarker(out.id, getResultCss(2))
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    } else if (n.$type === 'bpmn:SequenceFlow') {
 | 
			
		||||
      let targetActivity = activityList.find((m: any) => m.key === n.targetRef.id)
 | 
			
		||||
      if (targetActivity) {
 | 
			
		||||
        canvas.addMarker(n.id, getActivityHighlightCss(targetActivity))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  if (!isEmpty(removeTaskDefinitionKeyList)) {
 | 
			
		||||
    taskList.value = taskList.value.filter(
 | 
			
		||||
      (item) => !removeTaskDefinitionKeyList.includes(item.definitionKey)
 | 
			
		||||
      (item) => !removeTaskDefinitionKeyList.includes(item.taskDefinitionKey)
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getActivityHighlightCss = (activity) => {
 | 
			
		||||
  return activity.endTime ? 'highlight' : 'highlight-todo'
 | 
			
		||||
}
 | 
			
		||||
const getResultCss = (result) => {
 | 
			
		||||
  if (result === 1) {
 | 
			
		||||
 | 
			
		||||
const getResultCss = (status) => {
 | 
			
		||||
  if (status === 1) {
 | 
			
		||||
    // 审批中
 | 
			
		||||
    return 'highlight-todo'
 | 
			
		||||
  } else if (result === 2) {
 | 
			
		||||
  } else if (status === 2) {
 | 
			
		||||
    // 已通过
 | 
			
		||||
    return 'highlight'
 | 
			
		||||
  } else if (result === 3) {
 | 
			
		||||
  } else if (status === 3) {
 | 
			
		||||
    // 不通过
 | 
			
		||||
    return 'highlight-reject'
 | 
			
		||||
  } else if (result === 4) {
 | 
			
		||||
  } else if (status === 4) {
 | 
			
		||||
    // 已取消
 | 
			
		||||
    return 'highlight-cancel'
 | 
			
		||||
  } else if (result === 5) {
 | 
			
		||||
  } else if (status === 5) {
 | 
			
		||||
    // 退回
 | 
			
		||||
    return 'highlight-return'
 | 
			
		||||
  } else if (result === 6) {
 | 
			
		||||
  } else if (status === 6) {
 | 
			
		||||
    // 委派
 | 
			
		||||
    return 'highlight-return'
 | 
			
		||||
  } else if (result === 7 || result === 8 || result === 9) {
 | 
			
		||||
    // 待后加签任务完成/待前加签任务完成/待前置任务完成
 | 
			
		||||
    return 'highlight-return'
 | 
			
		||||
    return 'highlight-todo'
 | 
			
		||||
  } else if (status === 7) {
 | 
			
		||||
    // 审批通过中
 | 
			
		||||
    return 'highlight-todo'
 | 
			
		||||
  } else if (status === 0) {
 | 
			
		||||
    // 待审批
 | 
			
		||||
    return 'highlight-todo'
 | 
			
		||||
  }
 | 
			
		||||
  return ''
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -296,10 +307,10 @@ const elementHover = (element) => {
 | 
			
		|||
  !elementOverlayIds.value && (elementOverlayIds.value = {})
 | 
			
		||||
  !overlays.value && (overlays.value = bpmnModeler.get('overlays'))
 | 
			
		||||
  // 展示信息
 | 
			
		||||
  console.log(activityLists.value, 'activityLists.value')
 | 
			
		||||
  console.log(element.value, 'element.value')
 | 
			
		||||
  // console.log(activityLists.value, 'activityLists.value')
 | 
			
		||||
  // console.log(element.value, 'element.value')
 | 
			
		||||
  const activity = activityLists.value.find((m) => m.key === element.value.id)
 | 
			
		||||
  console.log(activity, 'activityactivityactivityactivity')
 | 
			
		||||
  // console.log(activity, 'activityactivityactivityactivity')
 | 
			
		||||
  if (!activity) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -313,15 +324,14 @@ const elementHover = (element) => {
 | 
			
		|||
                  <p>部门:${processInstance.value.startUser.deptName}</p>
 | 
			
		||||
                  <p>创建时间:${formatDate(processInstance.value.createTime)}`
 | 
			
		||||
    } else if (element.value.type === 'bpmn:UserTask') {
 | 
			
		||||
      // debugger
 | 
			
		||||
      let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId
 | 
			
		||||
      if (!task) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)
 | 
			
		||||
      let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
 | 
			
		||||
      let dataResult = ''
 | 
			
		||||
      optionData.forEach((element) => {
 | 
			
		||||
        if (element.value == task.result) {
 | 
			
		||||
        if (element.value == task.status) {
 | 
			
		||||
          dataResult = element.label
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
| 
						 | 
				
			
			@ -333,7 +343,7 @@ const elementHover = (element) => {
 | 
			
		|||
      //             <p>部门:${task.assigneeUser.deptName}</p>
 | 
			
		||||
      //             <p>结果:${getIntDictOptions(
 | 
			
		||||
      //               DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
 | 
			
		||||
      //               task.result
 | 
			
		||||
      //               task.status
 | 
			
		||||
      //             )}</p>
 | 
			
		||||
      //             <p>创建时间:${formatDate(task.createTime)}</p>`
 | 
			
		||||
      if (task.endTime) {
 | 
			
		||||
| 
						 | 
				
			
			@ -351,29 +361,30 @@ const elementHover = (element) => {
 | 
			
		|||
      }
 | 
			
		||||
      console.log(html)
 | 
			
		||||
    } else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) {
 | 
			
		||||
      let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)
 | 
			
		||||
      let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
 | 
			
		||||
      let dataResult = ''
 | 
			
		||||
      optionData.forEach((element) => {
 | 
			
		||||
        if (element.value == processInstance.value.result) {
 | 
			
		||||
        if (element.value == processInstance.value.status) {
 | 
			
		||||
          dataResult = element.label
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      html = `<p>结果:${dataResult}</p>`
 | 
			
		||||
      // html = `<p>结果:${getIntDictOptions(
 | 
			
		||||
      //   DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
 | 
			
		||||
      //   processInstance.value.result
 | 
			
		||||
      //   processInstance.value.status
 | 
			
		||||
      // )}</p>`
 | 
			
		||||
      if (processInstance.value.endTime) {
 | 
			
		||||
        html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>`
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    console.log(html, 'html111111111111111')
 | 
			
		||||
    // console.log(html, 'html111111111111111')
 | 
			
		||||
    elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, {
 | 
			
		||||
      position: { left: 0, bottom: 0 },
 | 
			
		||||
      html: `<div class="element-overlays">${html}</div>`
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 流程图的元素被 out
 | 
			
		||||
const elementOut = (element) => {
 | 
			
		||||
  toRaw(overlays.value).remove({ element })
 | 
			
		||||
| 
						 | 
				
			
			@ -389,6 +400,7 @@ onMounted(() => {
 | 
			
		|||
  // 初始模型的监听器
 | 
			
		||||
  initModelListeners()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
  // this.$once('hook:beforeDestroy', () => {
 | 
			
		||||
  // })
 | 
			
		||||
| 
						 | 
				
			
			@ -427,7 +439,7 @@ watch(
 | 
			
		|||
)
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
/** 处理中 */
 | 
			
		||||
.highlight-todo.djs-connection > .djs-visual > path {
 | 
			
		||||
  stroke: #1890ff !important;
 | 
			
		||||
| 
						 | 
				
			
			@ -501,6 +513,10 @@ watch(
 | 
			
		|||
  stroke: green !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.djs-element.highlight > .djs-visual > path {
 | 
			
		||||
  stroke: green !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 不通过 */
 | 
			
		||||
.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
 | 
			
		||||
  fill: red !important;
 | 
			
		||||
| 
						 | 
				
			
			@ -520,6 +536,7 @@ watch(
 | 
			
		|||
 | 
			
		||||
.highlight-reject.djs-connection > .djs-visual > path {
 | 
			
		||||
  stroke: red !important;
 | 
			
		||||
  marker-end: url(#sequenceflow-end-white-success) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -332,6 +332,16 @@
 | 
			
		|||
          "name": "multiinstance_condition",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateStrategy",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateParam",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -319,6 +319,16 @@
 | 
			
		|||
          "name": "priority",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateStrategy",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateParam",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -319,6 +319,16 @@
 | 
			
		|||
          "name": "priority",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateStrategy",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "candidateParam",
 | 
			
		||||
          "isAttr": true,
 | 
			
		||||
          "type": "String"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,15 +24,10 @@
 | 
			
		|||
      </el-collapse-item>
 | 
			
		||||
      <el-collapse-item name="condition" v-if="formVisible" key="form">
 | 
			
		||||
        <template #title><Icon icon="ep:list" />表单</template>
 | 
			
		||||
        <!-- <element-form :id="elementId" :type="elementType" /> -->
 | 
			
		||||
        友情提示:使用
 | 
			
		||||
        <router-link :to="{ path: '/bpm/manager/form' }"
 | 
			
		||||
          ><el-link type="danger">流程表单</el-link>
 | 
			
		||||
        </router-link>
 | 
			
		||||
        替代,提供更好的表单设计功能
 | 
			
		||||
        <element-form :id="elementId" :type="elementType" />
 | 
			
		||||
      </el-collapse-item>
 | 
			
		||||
      <el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
 | 
			
		||||
        <template #title><Icon icon="ep:checked" />任务</template>
 | 
			
		||||
        <template #title><Icon icon="ep:checked" />任务(审批人)</template>
 | 
			
		||||
        <element-task :id="elementId" :type="elementType" />
 | 
			
		||||
      </el-collapse-item>
 | 
			
		||||
      <el-collapse-item
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +35,7 @@
 | 
			
		|||
        v-if="elementType.indexOf('Task') !== -1"
 | 
			
		||||
        key="multiInstance"
 | 
			
		||||
      >
 | 
			
		||||
        <template #title><Icon icon="ep:help-filled" />多实例</template>
 | 
			
		||||
        <template #title><Icon icon="ep:help-filled" />多实例(会签配置)</template>
 | 
			
		||||
        <element-multi-instance :business-object="elementBusinessObject" :type="elementType" />
 | 
			
		||||
      </el-collapse-item>
 | 
			
		||||
      <el-collapse-item name="listeners" key="listeners">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,13 +3,6 @@
 | 
			
		|||
    <el-form label-width="90px" :model="needProps" :rules="rules">
 | 
			
		||||
      <div v-if="needProps.type == 'bpmn:Process'">
 | 
			
		||||
        <!-- 如果是 Process 信息的时候,使用自定义表单 -->
 | 
			
		||||
        <el-link
 | 
			
		||||
          href="https://doc.iocoder.cn/bpm/#_3-%E6%B5%81%E7%A8%8B%E5%9B%BE%E7%A4%BA%E4%BE%8B"
 | 
			
		||||
          type="danger"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
        >
 | 
			
		||||
          如何实现实现会签、或签?
 | 
			
		||||
        </el-link>
 | 
			
		||||
        <el-form-item label="流程标识" prop="id">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="needProps.id"
 | 
			
		||||
| 
						 | 
				
			
			@ -68,13 +61,13 @@ const resetBaseInfo = () => {
 | 
			
		|||
  console.log(bpmnElement.value, 'bpmnElement')
 | 
			
		||||
 | 
			
		||||
  bpmnElement.value = bpmnInstances()?.bpmnElement
 | 
			
		||||
  console.log(bpmnElement.value, 'resetBaseInfo11111111111')
 | 
			
		||||
  // console.log(bpmnElement.value, 'resetBaseInfo11111111111')
 | 
			
		||||
  elementBaseInfo.value = bpmnElement.value.businessObject
 | 
			
		||||
  needProps.value['type'] = bpmnElement.value.businessObject.$type
 | 
			
		||||
  // elementBaseInfo.value['typess'] = bpmnElement.value.businessObject.$type
 | 
			
		||||
 | 
			
		||||
  // elementBaseInfo.value = JSON.parse(JSON.stringify(bpmnElement.value.businessObject))
 | 
			
		||||
  console.log(elementBaseInfo.value, 'elementBaseInfo22222222222')
 | 
			
		||||
  // console.log(elementBaseInfo.value, 'elementBaseInfo22222222222')
 | 
			
		||||
}
 | 
			
		||||
const handleKeyUpdate = (value) => {
 | 
			
		||||
  // 校验 value 的值,只有 XML NCName 通过的情况下,才进行赋值。否则,会导致流程图报错,无法绘制的问题
 | 
			
		||||
| 
						 | 
				
			
			@ -121,11 +114,11 @@ const updateBaseInfo = (key) => {
 | 
			
		|||
  //   id: elementBaseInfo.value[key]
 | 
			
		||||
  //   // di: { id: `${elementBaseInfo.value[key]}_di` }
 | 
			
		||||
  // }
 | 
			
		||||
  console.log(elementBaseInfo, 'elementBaseInfo11111111111')
 | 
			
		||||
  // console.log(elementBaseInfo, 'elementBaseInfo11111111111')
 | 
			
		||||
  needProps.value = { ...elementBaseInfo.value, ...needProps.value }
 | 
			
		||||
 | 
			
		||||
  if (key === 'id') {
 | 
			
		||||
    console.log('jinru')
 | 
			
		||||
    // console.log('jinru')
 | 
			
		||||
    console.log(window, 'window')
 | 
			
		||||
    console.log(bpmnElement.value, 'bpmnElement')
 | 
			
		||||
    console.log(toRaw(bpmnElement.value), 'bpmnElement')
 | 
			
		||||
| 
						 | 
				
			
			@ -138,20 +131,11 @@ const updateBaseInfo = (key) => {
 | 
			
		|||
    bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), attrObj)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  // 针对上传的 bpmn 流程图时,需要延迟 1 秒的时间,保证 key 和 name 的更新
 | 
			
		||||
  setTimeout(() => {
 | 
			
		||||
    console.log(props.model, 'props.model')
 | 
			
		||||
    handleKeyUpdate(props.model.key)
 | 
			
		||||
    handleNameUpdate(props.model.name)
 | 
			
		||||
    console.log(props, 'propsssssssssssssssssssss')
 | 
			
		||||
  }, 1000)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.businessObject,
 | 
			
		||||
  (val) => {
 | 
			
		||||
    console.log(val, 'val11111111111111111111')
 | 
			
		||||
    // console.log(val, 'val11111111111111111111')
 | 
			
		||||
    if (val) {
 | 
			
		||||
      // nextTick(() => {
 | 
			
		||||
      resetBaseInfo()
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +143,18 @@ watch(
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.model?.key,
 | 
			
		||||
  (val) => {
 | 
			
		||||
    // 针对上传的 bpmn 流程图时,保证 key 和 name 的更新
 | 
			
		||||
    if (val) {
 | 
			
		||||
      handleKeyUpdate(props.model.key)
 | 
			
		||||
      handleNameUpdate(props.model.name)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// watch(
 | 
			
		||||
//   () => ({ ...props }),
 | 
			
		||||
//   (oldVal, newVal) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,228 +1,233 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="panel-tab__content">
 | 
			
		||||
    <el-form label-width="80px">
 | 
			
		||||
      <el-form-item label="表单标识">
 | 
			
		||||
        <el-input v-model="formKey" clearable @change="updateElementFormKey" />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="业务标识">
 | 
			
		||||
        <el-select v-model="businessKey" @change="updateElementBusinessKey">
 | 
			
		||||
          <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />
 | 
			
		||||
          <el-option label="无" value="" />
 | 
			
		||||
      <el-form-item label="流程表单">
 | 
			
		||||
        <!--        <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
 | 
			
		||||
        <el-select v-model="formKey" clearable @change="updateElementFormKey">
 | 
			
		||||
          <el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
 | 
			
		||||
        </el-select>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <!--      <el-form-item label="业务标识">-->
 | 
			
		||||
      <!--        <el-select v-model="businessKey" @change="updateElementBusinessKey">-->
 | 
			
		||||
      <!--          <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />-->
 | 
			
		||||
      <!--          <el-option label="无" value="" />-->
 | 
			
		||||
      <!--        </el-select>-->
 | 
			
		||||
      <!--      </el-form-item>-->
 | 
			
		||||
    </el-form>
 | 
			
		||||
 | 
			
		||||
    <!--字段列表-->
 | 
			
		||||
    <div class="element-property list-property">
 | 
			
		||||
      <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>
 | 
			
		||||
      <el-table :data="fieldList" max-height="240" fit border>
 | 
			
		||||
        <el-table-column label="序号" type="index" width="50px" />
 | 
			
		||||
        <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />
 | 
			
		||||
        <el-table-column
 | 
			
		||||
          label="字段类型"
 | 
			
		||||
          prop="type"
 | 
			
		||||
          min-width="80px"
 | 
			
		||||
          :formatter="(row) => fieldType[row.type] || row.type"
 | 
			
		||||
          show-overflow-tooltip
 | 
			
		||||
        />
 | 
			
		||||
        <el-table-column
 | 
			
		||||
          label="默认值"
 | 
			
		||||
          prop="defaultValue"
 | 
			
		||||
          min-width="80px"
 | 
			
		||||
          show-overflow-tooltip
 | 
			
		||||
        />
 | 
			
		||||
        <el-table-column label="操作" width="90px">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"
 | 
			
		||||
              >编辑</el-button
 | 
			
		||||
            >
 | 
			
		||||
            <el-divider direction="vertical" />
 | 
			
		||||
            <el-button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              link
 | 
			
		||||
              style="color: #ff4d4f"
 | 
			
		||||
              @click="removeField(scope, scope.$index)"
 | 
			
		||||
              >移除</el-button
 | 
			
		||||
            >
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="element-drawer__button">
 | 
			
		||||
      <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--    <div class="element-property list-property">-->
 | 
			
		||||
    <!--      <el-divider><Icon icon="ep:coin" /> 表单字段</el-divider>-->
 | 
			
		||||
    <!--      <el-table :data="fieldList" max-height="240" fit border>-->
 | 
			
		||||
    <!--        <el-table-column label="序号" type="index" width="50px" />-->
 | 
			
		||||
    <!--        <el-table-column label="字段名称" prop="label" min-width="80px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--        <el-table-column-->
 | 
			
		||||
    <!--          label="字段类型"-->
 | 
			
		||||
    <!--          prop="type"-->
 | 
			
		||||
    <!--          min-width="80px"-->
 | 
			
		||||
    <!--          :formatter="(row) => fieldType[row.type] || row.type"-->
 | 
			
		||||
    <!--          show-overflow-tooltip-->
 | 
			
		||||
    <!--        />-->
 | 
			
		||||
    <!--        <el-table-column-->
 | 
			
		||||
    <!--          label="默认值"-->
 | 
			
		||||
    <!--          prop="defaultValue"-->
 | 
			
		||||
    <!--          min-width="80px"-->
 | 
			
		||||
    <!--          show-overflow-tooltip-->
 | 
			
		||||
    <!--        />-->
 | 
			
		||||
    <!--        <el-table-column label="操作" width="90px">-->
 | 
			
		||||
    <!--          <template #default="scope">-->
 | 
			
		||||
    <!--            <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"-->
 | 
			
		||||
    <!--              >编辑</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--            <el-divider direction="vertical" />-->
 | 
			
		||||
    <!--            <el-button-->
 | 
			
		||||
    <!--              type="primary"-->
 | 
			
		||||
    <!--              link-->
 | 
			
		||||
    <!--              style="color: #ff4d4f"-->
 | 
			
		||||
    <!--              @click="removeField(scope, scope.$index)"-->
 | 
			
		||||
    <!--              >移除</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--          </template>-->
 | 
			
		||||
    <!--        </el-table-column>-->
 | 
			
		||||
    <!--      </el-table>-->
 | 
			
		||||
    <!--    </div>-->
 | 
			
		||||
    <!--    <div class="element-drawer__button">-->
 | 
			
		||||
    <!--      <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />-->
 | 
			
		||||
    <!--    </div>-->
 | 
			
		||||
 | 
			
		||||
    <!--字段配置侧边栏-->
 | 
			
		||||
    <el-drawer
 | 
			
		||||
      v-model="fieldModelVisible"
 | 
			
		||||
      title="字段配置"
 | 
			
		||||
      :size="`${width}px`"
 | 
			
		||||
      append-to-body
 | 
			
		||||
      destroy-on-close
 | 
			
		||||
    >
 | 
			
		||||
      <el-form :model="formFieldForm" label-width="90px">
 | 
			
		||||
        <el-form-item label="字段ID">
 | 
			
		||||
          <el-input v-model="formFieldForm.id" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="类型">
 | 
			
		||||
          <el-select
 | 
			
		||||
            v-model="formFieldForm.typeType"
 | 
			
		||||
            placeholder="请选择字段类型"
 | 
			
		||||
            clearable
 | 
			
		||||
            @change="changeFieldTypeType"
 | 
			
		||||
          >
 | 
			
		||||
            <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />
 | 
			
		||||
          </el-select>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">
 | 
			
		||||
          <el-input v-model="formFieldForm.type" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="名称">
 | 
			
		||||
          <el-input v-model="formFieldForm.label" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">
 | 
			
		||||
          <el-input v-model="formFieldForm.datePattern" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="默认值">
 | 
			
		||||
          <el-input v-model="formFieldForm.defaultValue" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-form>
 | 
			
		||||
    <!--    <el-drawer-->
 | 
			
		||||
    <!--      v-model="fieldModelVisible"-->
 | 
			
		||||
    <!--      title="字段配置"-->
 | 
			
		||||
    <!--      :size="`${width}px`"-->
 | 
			
		||||
    <!--      append-to-body-->
 | 
			
		||||
    <!--      destroy-on-close-->
 | 
			
		||||
    <!--    >-->
 | 
			
		||||
    <!--      <el-form :model="formFieldForm" label-width="90px">-->
 | 
			
		||||
    <!--        <el-form-item label="字段ID">-->
 | 
			
		||||
    <!--          <el-input v-model="formFieldForm.id" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="类型">-->
 | 
			
		||||
    <!--          <el-select-->
 | 
			
		||||
    <!--            v-model="formFieldForm.typeType"-->
 | 
			
		||||
    <!--            placeholder="请选择字段类型"-->
 | 
			
		||||
    <!--            clearable-->
 | 
			
		||||
    <!--            @change="changeFieldTypeType"-->
 | 
			
		||||
    <!--          >-->
 | 
			
		||||
    <!--            <el-option v-for="(value, key) of fieldType" :label="value" :value="key" :key="key" />-->
 | 
			
		||||
    <!--          </el-select>-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
 | 
			
		||||
    <!--          <el-input v-model="formFieldForm.type" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="名称">-->
 | 
			
		||||
    <!--          <el-input v-model="formFieldForm.label" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
 | 
			
		||||
    <!--          <el-input v-model="formFieldForm.datePattern" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="默认值">-->
 | 
			
		||||
    <!--          <el-input v-model="formFieldForm.defaultValue" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--      </el-form>-->
 | 
			
		||||
 | 
			
		||||
      <!-- 枚举值设置 -->
 | 
			
		||||
      <template v-if="formFieldForm.type === 'enum'">
 | 
			
		||||
        <el-divider key="enum-divider" />
 | 
			
		||||
        <p class="listener-filed__title" key="enum-title">
 | 
			
		||||
          <span><Icon icon="ep:menu" />枚举值列表:</span>
 | 
			
		||||
          <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"
 | 
			
		||||
            >添加枚举值</el-button
 | 
			
		||||
          >
 | 
			
		||||
        </p>
 | 
			
		||||
        <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>
 | 
			
		||||
          <el-table-column label="序号" width="50px" type="index" />
 | 
			
		||||
          <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
          <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
          <el-table-column label="操作" width="90px">
 | 
			
		||||
            <template #default="scope">
 | 
			
		||||
              <el-button
 | 
			
		||||
                type="primary"
 | 
			
		||||
                link
 | 
			
		||||
                @click="openFieldOptionForm(scope, scope.$index, 'enum')"
 | 
			
		||||
                >编辑</el-button
 | 
			
		||||
              >
 | 
			
		||||
              <el-divider direction="vertical" />
 | 
			
		||||
              <el-button
 | 
			
		||||
                type="primary"
 | 
			
		||||
                link
 | 
			
		||||
                style="color: #ff4d4f"
 | 
			
		||||
                @click="removeFieldOptionItem(scope, scope.$index, 'enum')"
 | 
			
		||||
                >移除</el-button
 | 
			
		||||
              >
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-table-column>
 | 
			
		||||
        </el-table>
 | 
			
		||||
      </template>
 | 
			
		||||
    <!--      <!– 枚举值设置 –>-->
 | 
			
		||||
    <!--      <template v-if="formFieldForm.type === 'enum'">-->
 | 
			
		||||
    <!--        <el-divider key="enum-divider" />-->
 | 
			
		||||
    <!--        <p class="listener-filed__title" key="enum-title">-->
 | 
			
		||||
    <!--          <span><Icon icon="ep:menu" />枚举值列表:</span>-->
 | 
			
		||||
    <!--          <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
 | 
			
		||||
    <!--            >添加枚举值</el-button-->
 | 
			
		||||
    <!--          >-->
 | 
			
		||||
    <!--        </p>-->
 | 
			
		||||
    <!--        <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>-->
 | 
			
		||||
    <!--          <el-table-column label="序号" width="50px" type="index" />-->
 | 
			
		||||
    <!--          <el-table-column label="枚举值编号" prop="id" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--          <el-table-column label="枚举值名称" prop="name" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--          <el-table-column label="操作" width="90px">-->
 | 
			
		||||
    <!--            <template #default="scope">-->
 | 
			
		||||
    <!--              <el-button-->
 | 
			
		||||
    <!--                type="primary"-->
 | 
			
		||||
    <!--                link-->
 | 
			
		||||
    <!--                @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
 | 
			
		||||
    <!--                >编辑</el-button-->
 | 
			
		||||
    <!--              >-->
 | 
			
		||||
    <!--              <el-divider direction="vertical" />-->
 | 
			
		||||
    <!--              <el-button-->
 | 
			
		||||
    <!--                type="primary"-->
 | 
			
		||||
    <!--                link-->
 | 
			
		||||
    <!--                style="color: #ff4d4f"-->
 | 
			
		||||
    <!--                @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
 | 
			
		||||
    <!--                >移除</el-button-->
 | 
			
		||||
    <!--              >-->
 | 
			
		||||
    <!--            </template>-->
 | 
			
		||||
    <!--          </el-table-column>-->
 | 
			
		||||
    <!--        </el-table>-->
 | 
			
		||||
    <!--      </template>-->
 | 
			
		||||
 | 
			
		||||
      <!-- 校验规则 -->
 | 
			
		||||
      <el-divider key="validation-divider" />
 | 
			
		||||
      <p class="listener-filed__title" key="validation-title">
 | 
			
		||||
        <span><Icon icon="ep:menu" />约束条件列表:</span>
 | 
			
		||||
        <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"
 | 
			
		||||
          >添加约束</el-button
 | 
			
		||||
        >
 | 
			
		||||
      </p>
 | 
			
		||||
      <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>
 | 
			
		||||
        <el-table-column label="序号" width="50px" type="index" />
 | 
			
		||||
        <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
        <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
        <el-table-column label="操作" width="90px">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <el-button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              link
 | 
			
		||||
              @click="openFieldOptionForm(scope, scope.$index, 'constraint')"
 | 
			
		||||
              >编辑</el-button
 | 
			
		||||
            >
 | 
			
		||||
            <el-divider direction="vertical" />
 | 
			
		||||
            <el-button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              link
 | 
			
		||||
              style="color: #ff4d4f"
 | 
			
		||||
              @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"
 | 
			
		||||
              >移除</el-button
 | 
			
		||||
            >
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
    <!--      <!– 校验规则 –>-->
 | 
			
		||||
    <!--      <el-divider key="validation-divider" />-->
 | 
			
		||||
    <!--      <p class="listener-filed__title" key="validation-title">-->
 | 
			
		||||
    <!--        <span><Icon icon="ep:menu" />约束条件列表:</span>-->
 | 
			
		||||
    <!--        <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
 | 
			
		||||
    <!--          >添加约束</el-button-->
 | 
			
		||||
    <!--        >-->
 | 
			
		||||
    <!--      </p>-->
 | 
			
		||||
    <!--      <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>-->
 | 
			
		||||
    <!--        <el-table-column label="序号" width="50px" type="index" />-->
 | 
			
		||||
    <!--        <el-table-column label="约束名称" prop="name" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--        <el-table-column label="约束配置" prop="config" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--        <el-table-column label="操作" width="90px">-->
 | 
			
		||||
    <!--          <template #default="scope">-->
 | 
			
		||||
    <!--            <el-button-->
 | 
			
		||||
    <!--              type="primary"-->
 | 
			
		||||
    <!--              link-->
 | 
			
		||||
    <!--              @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
 | 
			
		||||
    <!--              >编辑</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--            <el-divider direction="vertical" />-->
 | 
			
		||||
    <!--            <el-button-->
 | 
			
		||||
    <!--              type="primary"-->
 | 
			
		||||
    <!--              link-->
 | 
			
		||||
    <!--              style="color: #ff4d4f"-->
 | 
			
		||||
    <!--              @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
 | 
			
		||||
    <!--              >移除</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--          </template>-->
 | 
			
		||||
    <!--        </el-table-column>-->
 | 
			
		||||
    <!--      </el-table>-->
 | 
			
		||||
 | 
			
		||||
      <!-- 表单属性 -->
 | 
			
		||||
      <el-divider key="property-divider" />
 | 
			
		||||
      <p class="listener-filed__title" key="property-title">
 | 
			
		||||
        <span><Icon icon="ep:menu" />字段属性列表:</span>
 | 
			
		||||
        <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"
 | 
			
		||||
          >添加属性</el-button
 | 
			
		||||
        >
 | 
			
		||||
      </p>
 | 
			
		||||
      <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>
 | 
			
		||||
        <el-table-column label="序号" width="50px" type="index" />
 | 
			
		||||
        <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
        <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />
 | 
			
		||||
        <el-table-column label="操作" width="90px">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <el-button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              link
 | 
			
		||||
              @click="openFieldOptionForm(scope, scope.$index, 'property')"
 | 
			
		||||
              >编辑</el-button
 | 
			
		||||
            >
 | 
			
		||||
            <el-divider direction="vertical" />
 | 
			
		||||
            <el-button
 | 
			
		||||
              type="primary"
 | 
			
		||||
              link
 | 
			
		||||
              style="color: #ff4d4f"
 | 
			
		||||
              @click="removeFieldOptionItem(scope, scope.$index, 'property')"
 | 
			
		||||
              >移除</el-button
 | 
			
		||||
            >
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
    <!--      <!– 表单属性 –>-->
 | 
			
		||||
    <!--      <el-divider key="property-divider" />-->
 | 
			
		||||
    <!--      <p class="listener-filed__title" key="property-title">-->
 | 
			
		||||
    <!--        <span><Icon icon="ep:menu" />字段属性列表:</span>-->
 | 
			
		||||
    <!--        <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
 | 
			
		||||
    <!--          >添加属性</el-button-->
 | 
			
		||||
    <!--        >-->
 | 
			
		||||
    <!--      </p>-->
 | 
			
		||||
    <!--      <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>-->
 | 
			
		||||
    <!--        <el-table-column label="序号" width="50px" type="index" />-->
 | 
			
		||||
    <!--        <el-table-column label="属性编号" prop="id" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--        <el-table-column label="属性值" prop="value" min-width="100px" show-overflow-tooltip />-->
 | 
			
		||||
    <!--        <el-table-column label="操作" width="90px">-->
 | 
			
		||||
    <!--          <template #default="scope">-->
 | 
			
		||||
    <!--            <el-button-->
 | 
			
		||||
    <!--              type="primary"-->
 | 
			
		||||
    <!--              link-->
 | 
			
		||||
    <!--              @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
 | 
			
		||||
    <!--              >编辑</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--            <el-divider direction="vertical" />-->
 | 
			
		||||
    <!--            <el-button-->
 | 
			
		||||
    <!--              type="primary"-->
 | 
			
		||||
    <!--              link-->
 | 
			
		||||
    <!--              style="color: #ff4d4f"-->
 | 
			
		||||
    <!--              @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
 | 
			
		||||
    <!--              >移除</el-button-->
 | 
			
		||||
    <!--            >-->
 | 
			
		||||
    <!--          </template>-->
 | 
			
		||||
    <!--        </el-table-column>-->
 | 
			
		||||
    <!--      </el-table>-->
 | 
			
		||||
 | 
			
		||||
      <!-- 底部按钮 -->
 | 
			
		||||
      <div class="element-drawer__button">
 | 
			
		||||
        <el-button>取 消</el-button>
 | 
			
		||||
        <el-button type="primary" @click="saveField">保 存</el-button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-drawer>
 | 
			
		||||
    <!--      <!– 底部按钮 –>-->
 | 
			
		||||
    <!--      <div class="element-drawer__button">-->
 | 
			
		||||
    <!--        <el-button>取 消</el-button>-->
 | 
			
		||||
    <!--        <el-button type="primary" @click="saveField">保 存</el-button>-->
 | 
			
		||||
    <!--      </div>-->
 | 
			
		||||
    <!--    </el-drawer>-->
 | 
			
		||||
 | 
			
		||||
    <el-dialog
 | 
			
		||||
      v-model="fieldOptionModelVisible"
 | 
			
		||||
      :title="optionModelTitle"
 | 
			
		||||
      width="600px"
 | 
			
		||||
      append-to-body
 | 
			
		||||
      destroy-on-close
 | 
			
		||||
    >
 | 
			
		||||
      <el-form :model="fieldOptionForm" label-width="96px">
 | 
			
		||||
        <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">
 | 
			
		||||
          <el-input v-model="fieldOptionForm.id" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">
 | 
			
		||||
          <el-input v-model="fieldOptionForm.name" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">
 | 
			
		||||
          <el-input v-model="fieldOptionForm.config" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">
 | 
			
		||||
          <el-input v-model="fieldOptionForm.value" clearable />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
      </el-form>
 | 
			
		||||
      <template #footer>
 | 
			
		||||
        <el-button @click="fieldOptionModelVisible = false">取 消</el-button>
 | 
			
		||||
        <el-button type="primary" @click="saveFieldOption">确 定</el-button>
 | 
			
		||||
      </template>
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
    <!--    <el-dialog-->
 | 
			
		||||
    <!--      v-model="fieldOptionModelVisible"-->
 | 
			
		||||
    <!--      :title="optionModelTitle"-->
 | 
			
		||||
    <!--      width="600px"-->
 | 
			
		||||
    <!--      append-to-body-->
 | 
			
		||||
    <!--      destroy-on-close-->
 | 
			
		||||
    <!--    >-->
 | 
			
		||||
    <!--      <el-form :model="fieldOptionForm" label-width="96px">-->
 | 
			
		||||
    <!--        <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
 | 
			
		||||
    <!--          <el-input v-model="fieldOptionForm.id" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
 | 
			
		||||
    <!--          <el-input v-model="fieldOptionForm.name" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
 | 
			
		||||
    <!--          <el-input v-model="fieldOptionForm.config" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--        <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
 | 
			
		||||
    <!--          <el-input v-model="fieldOptionForm.value" clearable />-->
 | 
			
		||||
    <!--        </el-form-item>-->
 | 
			
		||||
    <!--      </el-form>-->
 | 
			
		||||
    <!--      <template #footer>-->
 | 
			
		||||
    <!--        <el-button @click="fieldOptionModelVisible = false">取 消</el-button>-->
 | 
			
		||||
    <!--        <el-button type="primary" @click="saveFieldOption">确 定</el-button>-->
 | 
			
		||||
    <!--      </template>-->
 | 
			
		||||
    <!--    </el-dialog>-->
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import * as FormApi from '@/api/bpm/form'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'ElementForm' })
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
| 
						 | 
				
			
			@ -263,6 +268,9 @@ const bpmnInstances = () => (window as any)?.bpmnInstances
 | 
			
		|||
const resetFormList = () => {
 | 
			
		||||
  bpmnELement.value = bpmnInstances().bpmnElement
 | 
			
		||||
  formKey.value = bpmnELement.value.businessObject.formKey
 | 
			
		||||
  if (formKey.value?.length > 0) {
 | 
			
		||||
    formKey.value = parseInt(formKey.value)
 | 
			
		||||
  }
 | 
			
		||||
  // 获取元素扩展属性 或者 创建扩展属性
 | 
			
		||||
  elExtensionElements.value =
 | 
			
		||||
    bpmnELement.value.businessObject.get('extensionElements') ||
 | 
			
		||||
| 
						 | 
				
			
			@ -421,7 +429,7 @@ const saveField = () => {
 | 
			
		|||
 | 
			
		||||
// 移除某个 字段的 配置项
 | 
			
		||||
const removeFieldOptionItem = (option, index, type) => {
 | 
			
		||||
  console.log(option, 'option')
 | 
			
		||||
  // console.log(option, 'option')
 | 
			
		||||
  if (type === 'property') {
 | 
			
		||||
    fieldPropertiesList.value.splice(index, 1)
 | 
			
		||||
    return
 | 
			
		||||
| 
						 | 
				
			
			@ -451,6 +459,11 @@ const updateElementExtensions = () => {
 | 
			
		|||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const formList = ref([]) // 流程表单的下拉框的数据
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  formList.value = await FormApi.getFormSimpleList()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.id,
 | 
			
		||||
  (val) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,8 +26,16 @@
 | 
			
		|||
        type="primary"
 | 
			
		||||
        preIcon="ep:plus"
 | 
			
		||||
        title="添加监听器"
 | 
			
		||||
        size="small"
 | 
			
		||||
        @click="openListenerForm(null)"
 | 
			
		||||
      />
 | 
			
		||||
      <XButton
 | 
			
		||||
        type="success"
 | 
			
		||||
        preIcon="ep:select"
 | 
			
		||||
        title="选择监听器"
 | 
			
		||||
        size="small"
 | 
			
		||||
        @click="openProcessListenerDialog"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- 监听器 编辑/创建 部分 -->
 | 
			
		||||
| 
						 | 
				
			
			@ -240,11 +248,21 @@
 | 
			
		|||
      </template>
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <!-- 选择弹窗 -->
 | 
			
		||||
  <ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" />
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ElMessageBox } from 'element-plus'
 | 
			
		||||
import { createListenerObject, updateElementExtensions } from '../../utils'
 | 
			
		||||
import { initListenerType, initListenerForm, listenerType, fieldType } from './utilSelf'
 | 
			
		||||
import {
 | 
			
		||||
  initListenerType,
 | 
			
		||||
  initListenerForm,
 | 
			
		||||
  listenerType,
 | 
			
		||||
  fieldType,
 | 
			
		||||
  initListenerForm2
 | 
			
		||||
} from './utilSelf'
 | 
			
		||||
import ProcessListenerDialog from './ProcessListenerDialog.vue'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'ElementListeners' })
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +302,7 @@ const resetListenersList = () => {
 | 
			
		|||
}
 | 
			
		||||
// 打开 监听器详情 侧边栏
 | 
			
		||||
const openListenerForm = (listener, index?) => {
 | 
			
		||||
  // debugger
 | 
			
		||||
  if (listener) {
 | 
			
		||||
    listenerForm.value = initListenerForm(listener)
 | 
			
		||||
    editingListenerIndex.value = index
 | 
			
		||||
| 
						 | 
				
			
			@ -321,6 +340,7 @@ const openListenerFieldForm = (field, index?) => {
 | 
			
		|||
}
 | 
			
		||||
// 保存监听器注入字段
 | 
			
		||||
const saveListenerFiled = async () => {
 | 
			
		||||
  // debugger
 | 
			
		||||
  let validateStatus = await listenerFieldFormRef.value.validate()
 | 
			
		||||
  if (!validateStatus) return // 验证不通过直接返回
 | 
			
		||||
  if (editingListenerFieldIndex.value === -1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -337,6 +357,7 @@ const saveListenerFiled = async () => {
 | 
			
		|||
}
 | 
			
		||||
// 移除监听器字段
 | 
			
		||||
const removeListenerField = (index) => {
 | 
			
		||||
  // debugger
 | 
			
		||||
  ElMessageBox.confirm('确认移除该字段吗?', '提示', {
 | 
			
		||||
    confirmButtonText: '确 认',
 | 
			
		||||
    cancelButtonText: '取 消'
 | 
			
		||||
| 
						 | 
				
			
			@ -349,6 +370,7 @@ const removeListenerField = (index) => {
 | 
			
		|||
}
 | 
			
		||||
// 移除监听器
 | 
			
		||||
const removeListener = (index) => {
 | 
			
		||||
  debugger
 | 
			
		||||
  ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
 | 
			
		||||
    confirmButtonText: '确 认',
 | 
			
		||||
    cancelButtonText: '取 消'
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +387,7 @@ const removeListener = (index) => {
 | 
			
		|||
}
 | 
			
		||||
// 保存监听器配置
 | 
			
		||||
const saveListenerConfig = async () => {
 | 
			
		||||
  // debugger
 | 
			
		||||
  let validateStatus = await listenerFormRef.value.validate()
 | 
			
		||||
  if (!validateStatus) return // 验证不通过直接返回
 | 
			
		||||
  const listenerObject = createListenerObject(listenerForm.value, false, prefix)
 | 
			
		||||
| 
						 | 
				
			
			@ -389,6 +412,28 @@ const saveListenerConfig = async () => {
 | 
			
		|||
  listenerForm.value = {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 打开监听器弹窗
 | 
			
		||||
const processListenerDialogRef = ref()
 | 
			
		||||
const openProcessListenerDialog = async () => {
 | 
			
		||||
  processListenerDialogRef.value.open('execution')
 | 
			
		||||
}
 | 
			
		||||
const selectProcessListener = (listener) => {
 | 
			
		||||
  const listenerForm = initListenerForm2(listener)
 | 
			
		||||
  const listenerObject = createListenerObject(listenerForm, false, prefix)
 | 
			
		||||
  bpmnElementListeners.value.push(listenerObject)
 | 
			
		||||
  elementListenersList.value.push(listenerForm)
 | 
			
		||||
 | 
			
		||||
  // 保存其他配置
 | 
			
		||||
  otherExtensionList.value =
 | 
			
		||||
    bpmnElement.value.businessObject?.extensionElements?.values?.filter(
 | 
			
		||||
      (ex) => ex.$type !== `${prefix}:ExecutionListener`
 | 
			
		||||
    ) ?? []
 | 
			
		||||
  updateElementExtensions(
 | 
			
		||||
    bpmnElement.value,
 | 
			
		||||
    otherExtensionList.value.concat(bpmnElementListeners.value)
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.id,
 | 
			
		||||
  (val) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
<!-- 执行器选择 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <Dialog title="请选择监听器" v-model="dialogVisible" width="1024px">
 | 
			
		||||
    <ContentWrap>
 | 
			
		||||
      <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
 | 
			
		||||
        <el-table-column label="名字" align="center" prop="name" />
 | 
			
		||||
        <el-table-column label="类型" align="center" prop="type">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <dict-tag :type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE" :value="scope.row.type" />
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
        <el-table-column label="事件" align="center" prop="event" />
 | 
			
		||||
        <el-table-column label="值类型" align="center" prop="valueType">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <dict-tag
 | 
			
		||||
              :type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
 | 
			
		||||
              :value="scope.row.valueType"
 | 
			
		||||
            />
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
        <el-table-column label="值" align="center" prop="value" />
 | 
			
		||||
        <el-table-column label="操作" align="center">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <el-button link type="primary" @click="select(scope.row)"> 选择 </el-button>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
      <!-- 分页 -->
 | 
			
		||||
      <Pagination
 | 
			
		||||
        :total="total"
 | 
			
		||||
        v-model:page="queryParams.pageNo"
 | 
			
		||||
        v-model:limit="queryParams.pageSize"
 | 
			
		||||
        @pagination="getList"
 | 
			
		||||
      />
 | 
			
		||||
    </ContentWrap>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ProcessListenerApi, ProcessListenerVO } from '@/api/bpm/processListener'
 | 
			
		||||
import { DICT_TYPE } from '@/utils/dict'
 | 
			
		||||
import { CommonStatusEnum } from '@/utils/constants'
 | 
			
		||||
 | 
			
		||||
/** BPM 流程 表单 */
 | 
			
		||||
defineOptions({ name: 'ProcessListenerDialog' })
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
 | 
			
		||||
const dialogVisible = ref(false) // 弹窗的是否展示
 | 
			
		||||
const loading = ref(true) // 列表的加载中
 | 
			
		||||
const list = ref<ProcessListenerVO[]>([]) // 列表的数据
 | 
			
		||||
const total = ref(0) // 列表的总页数
 | 
			
		||||
const queryParams = reactive({
 | 
			
		||||
  pageNo: 1,
 | 
			
		||||
  pageSize: 10,
 | 
			
		||||
  type: undefined,
 | 
			
		||||
  status: CommonStatusEnum.ENABLE
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/** 打开弹窗 */
 | 
			
		||||
const open = async (type: string) => {
 | 
			
		||||
  dialogVisible.value = true
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    queryParams.pageNo = 1
 | 
			
		||||
    queryParams.type = type
 | 
			
		||||
    const data = await ProcessListenerApi.getProcessListenerPage(queryParams)
 | 
			
		||||
    list.value = data.list
 | 
			
		||||
    total.value = data.total
 | 
			
		||||
  } finally {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 | 
			
		||||
 | 
			
		||||
/** 提交表单 */
 | 
			
		||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 | 
			
		||||
const select = async (row) => {
 | 
			
		||||
  dialogVisible.value = false
 | 
			
		||||
  // 发送操作成功的事件
 | 
			
		||||
  emit('select', row)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +39,13 @@
 | 
			
		|||
        title="添加监听器"
 | 
			
		||||
        @click="openListenerForm(null)"
 | 
			
		||||
      />
 | 
			
		||||
      <XButton
 | 
			
		||||
        type="success"
 | 
			
		||||
        preIcon="ep:select"
 | 
			
		||||
        title="选择监听器"
 | 
			
		||||
        size="small"
 | 
			
		||||
        @click="openProcessListenerDialog"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <!-- 监听器 编辑/创建 部分 -->
 | 
			
		||||
| 
						 | 
				
			
			@ -286,11 +293,22 @@
 | 
			
		|||
      </template>
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <!-- 选择弹窗 -->
 | 
			
		||||
  <ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" />
 | 
			
		||||
</template>
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ElMessageBox } from 'element-plus'
 | 
			
		||||
import { createListenerObject, updateElementExtensions } from '../../utils'
 | 
			
		||||
import { initListenerForm, initListenerType, eventType, listenerType, fieldType } from './utilSelf'
 | 
			
		||||
import {
 | 
			
		||||
  initListenerForm,
 | 
			
		||||
  initListenerType,
 | 
			
		||||
  eventType,
 | 
			
		||||
  listenerType,
 | 
			
		||||
  fieldType,
 | 
			
		||||
  initListenerForm2
 | 
			
		||||
} from './utilSelf'
 | 
			
		||||
import ProcessListenerDialog from '@/components/bpmnProcessDesigner/package/penal/listeners/ProcessListenerDialog.vue'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'UserTaskListeners' })
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +455,28 @@ const removeListenerField = (field, index) => {
 | 
			
		|||
    .catch(() => console.info('操作取消'))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 打开监听器弹窗
 | 
			
		||||
const processListenerDialogRef = ref()
 | 
			
		||||
const openProcessListenerDialog = async () => {
 | 
			
		||||
  processListenerDialogRef.value.open('task')
 | 
			
		||||
}
 | 
			
		||||
const selectProcessListener = (listener) => {
 | 
			
		||||
  const listenerForm = initListenerForm2(listener)
 | 
			
		||||
  const listenerObject = createListenerObject(listenerForm, true, prefix)
 | 
			
		||||
  bpmnElementListeners.value.push(listenerObject)
 | 
			
		||||
  elementListenersList.value.push(listenerForm)
 | 
			
		||||
 | 
			
		||||
  // 保存其他配置
 | 
			
		||||
  otherExtensionList.value =
 | 
			
		||||
    bpmnElement.value.businessObject?.extensionElements?.values?.filter(
 | 
			
		||||
      (ex) => ex.$type !== `${prefix}:TaskListener`
 | 
			
		||||
    ) ?? []
 | 
			
		||||
  updateElementExtensions(
 | 
			
		||||
    bpmnElement.value,
 | 
			
		||||
    otherExtensionList.value.concat(bpmnElementListeners.value)
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => props.id,
 | 
			
		||||
  (val) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,33 @@ export function initListenerType(listener) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 将 ProcessListenerDO 转换成 initListenerForm 想同的 Form 对象 */
 | 
			
		||||
export function initListenerForm2(processListener) {
 | 
			
		||||
  if (processListener.valueType === 'class') {
 | 
			
		||||
    return {
 | 
			
		||||
      listenerType: 'classListener',
 | 
			
		||||
      class: processListener.value,
 | 
			
		||||
      event: processListener.event,
 | 
			
		||||
      fields: []
 | 
			
		||||
    }
 | 
			
		||||
  } else if (processListener.valueType === 'expression') {
 | 
			
		||||
    return {
 | 
			
		||||
      listenerType: 'expressionListener',
 | 
			
		||||
      expression: processListener.value,
 | 
			
		||||
      event: processListener.event,
 | 
			
		||||
      fields: []
 | 
			
		||||
    }
 | 
			
		||||
  } else if (processListener.valueType === 'delegateExpression') {
 | 
			
		||||
    return {
 | 
			
		||||
      listenerType: 'delegateExpressionListener',
 | 
			
		||||
      delegateExpression: processListener.value,
 | 
			
		||||
      event: processListener.event,
 | 
			
		||||
      fields: []
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  throw new Error('未知的监听器类型')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const listenerType = {
 | 
			
		||||
  classListener: 'Java 类',
 | 
			
		||||
  expressionListener: '表达式',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,15 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="panel-tab__content">
 | 
			
		||||
    <el-form label-width="90px">
 | 
			
		||||
      <el-form-item label="回路特性">
 | 
			
		||||
      <el-form-item label="快捷配置">
 | 
			
		||||
        <el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button>
 | 
			
		||||
        <el-button size="small" @click="changeConfig('会签')">会签</el-button>
 | 
			
		||||
        <el-button size="small" @click="changeConfig('或签')">或签</el-button>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="会签类型">
 | 
			
		||||
        <el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType">
 | 
			
		||||
          <el-option label="并行多重事件" value="ParallelMultiInstance" />
 | 
			
		||||
          <el-option label="时序多重事件" value="SequentialMultiInstance" />
 | 
			
		||||
          <el-option label="循环事件" value="StandardLoop" />
 | 
			
		||||
          <el-option label="无" value="Null" />
 | 
			
		||||
        </el-select>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +19,7 @@
 | 
			
		|||
          loopCharacteristics === 'SequentialMultiInstance'
 | 
			
		||||
        "
 | 
			
		||||
      >
 | 
			
		||||
        <el-form-item label="循环基数" key="loopCardinality">
 | 
			
		||||
        <el-form-item label="循环数量" key="loopCardinality">
 | 
			
		||||
          <el-input
 | 
			
		||||
            v-model="loopInstanceForm.loopCardinality"
 | 
			
		||||
            clearable
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +29,8 @@
 | 
			
		|||
        <el-form-item label="集合" key="collection" v-show="false">
 | 
			
		||||
          <el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="元素变量" key="elementVariable">
 | 
			
		||||
        <!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
 | 
			
		||||
        <el-form-item label="元素变量" key="elementVariable" style="display: none">
 | 
			
		||||
          <el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="完成条件" key="completionCondition">
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +40,8 @@
 | 
			
		|||
            @change="updateLoopCondition"
 | 
			
		||||
          />
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item label="异步状态" key="async">
 | 
			
		||||
        <!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
 | 
			
		||||
        <el-form-item label="异步状态" key="async" style="display: none">
 | 
			
		||||
          <el-checkbox
 | 
			
		||||
            v-model="loopInstanceForm.asyncBefore"
 | 
			
		||||
            label="异步前"
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +130,7 @@ const getElementLoop = (businessObject) => {
 | 
			
		|||
      businessObject.loopCharacteristics.extensionElements.values[0].body
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const changeLoopCharacteristicsType = (type) => {
 | 
			
		||||
  // this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置
 | 
			
		||||
  // 取消多实例配置
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +167,7 @@ const changeLoopCharacteristicsType = (type) => {
 | 
			
		|||
    loopCharacteristics: toRaw(multiLoopInstance.value)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 循环基数
 | 
			
		||||
const updateLoopCardinality = (cardinality) => {
 | 
			
		||||
  let loopCardinality = null
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +184,7 @@ const updateLoopCardinality = (cardinality) => {
 | 
			
		|||
    }
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 完成条件
 | 
			
		||||
const updateLoopCondition = (condition) => {
 | 
			
		||||
  let completionCondition = null
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +201,7 @@ const updateLoopCondition = (condition) => {
 | 
			
		|||
    }
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 重试周期
 | 
			
		||||
const updateLoopTimeCycle = (timeCycle) => {
 | 
			
		||||
  const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
 | 
			
		||||
| 
						 | 
				
			
			@ -209,6 +219,7 @@ const updateLoopTimeCycle = (timeCycle) => {
 | 
			
		|||
    }
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 直接更新的基础信息
 | 
			
		||||
const updateLoopBase = () => {
 | 
			
		||||
  bpmnInstances().modeling.updateModdleProperties(
 | 
			
		||||
| 
						 | 
				
			
			@ -220,6 +231,7 @@ const updateLoopBase = () => {
 | 
			
		|||
    }
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 各异步状态
 | 
			
		||||
const updateLoopAsync = (key) => {
 | 
			
		||||
  const { asyncBefore, asyncAfter } = loopInstanceForm.value
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +250,20 @@ const updateLoopAsync = (key) => {
 | 
			
		|||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const changeConfig = (config) => {
 | 
			
		||||
  if (config === '依次审批') {
 | 
			
		||||
    changeLoopCharacteristicsType('SequentialMultiInstance')
 | 
			
		||||
    updateLoopCardinality('1')
 | 
			
		||||
    updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
 | 
			
		||||
  } else if (config === '会签') {
 | 
			
		||||
    changeLoopCharacteristicsType('ParallelMultiInstance')
 | 
			
		||||
    updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
 | 
			
		||||
  } else if (config === '或签') {
 | 
			
		||||
    changeLoopCharacteristicsType('ParallelMultiInstance')
 | 
			
		||||
    updateLoopCondition('${ nrOfCompletedInstances > 0 }')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
  multiLoopInstance.value = null
 | 
			
		||||
  bpmnElement.value = null
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div class="panel-tab__content">
 | 
			
		||||
    <el-form size="small" label-width="90px">
 | 
			
		||||
      <el-form-item label="异步延续">
 | 
			
		||||
      <!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
 | 
			
		||||
      <el-form-item label="异步延续" style="display: none">
 | 
			
		||||
        <el-checkbox
 | 
			
		||||
          v-model="taskConfigForm.asyncBefore"
 | 
			
		||||
          label="异步前"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
<!-- 表达式选择 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <Dialog title="请选择表达式" v-model="dialogVisible" width="1024px">
 | 
			
		||||
    <ContentWrap>
 | 
			
		||||
      <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
 | 
			
		||||
        <el-table-column label="名字" align="center" prop="name" />
 | 
			
		||||
        <el-table-column label="表达式" align="center" prop="expression" />
 | 
			
		||||
        <el-table-column label="操作" align="center">
 | 
			
		||||
          <template #default="scope">
 | 
			
		||||
            <el-button link type="primary" @click="select(scope.row)"> 选择 </el-button>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-table-column>
 | 
			
		||||
      </el-table>
 | 
			
		||||
      <!-- 分页 -->
 | 
			
		||||
      <Pagination
 | 
			
		||||
        :total="total"
 | 
			
		||||
        v-model:page="queryParams.pageNo"
 | 
			
		||||
        v-model:limit="queryParams.pageSize"
 | 
			
		||||
        @pagination="getList"
 | 
			
		||||
      />
 | 
			
		||||
    </ContentWrap>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { CommonStatusEnum } from '@/utils/constants'
 | 
			
		||||
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
 | 
			
		||||
 | 
			
		||||
/** BPM 流程 表单 */
 | 
			
		||||
defineOptions({ name: 'ProcessExpressionDialog' })
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
 | 
			
		||||
const dialogVisible = ref(false) // 弹窗的是否展示
 | 
			
		||||
const loading = ref(true) // 列表的加载中
 | 
			
		||||
const list = ref<ProcessExpressionVO[]>([]) // 列表的数据
 | 
			
		||||
const total = ref(0) // 列表的总页数
 | 
			
		||||
const queryParams = reactive({
 | 
			
		||||
  pageNo: 1,
 | 
			
		||||
  pageSize: 10,
 | 
			
		||||
  type: undefined,
 | 
			
		||||
  status: CommonStatusEnum.ENABLE
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/** 打开弹窗 */
 | 
			
		||||
const open = async (type: string) => {
 | 
			
		||||
  dialogVisible.value = true
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    queryParams.pageNo = 1
 | 
			
		||||
    queryParams.type = type
 | 
			
		||||
    const data = await ProcessExpressionApi.getProcessExpressionPage(queryParams)
 | 
			
		||||
    list.value = data.list
 | 
			
		||||
    total.value = data.total
 | 
			
		||||
  } finally {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 | 
			
		||||
 | 
			
		||||
/** 提交表单 */
 | 
			
		||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 | 
			
		||||
const select = async (row) => {
 | 
			
		||||
  dialogVisible.value = false
 | 
			
		||||
  // 发送操作成功的事件
 | 
			
		||||
  emit('select', row)
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,85 +1,204 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div style="margin-top: 16px">
 | 
			
		||||
    <!--    <el-form-item label="处理用户">-->
 | 
			
		||||
    <!--      <el-select v-model="userTaskForm.assignee" @change="updateElementTask('assignee')">-->
 | 
			
		||||
    <!--        <el-option v-for="ak in mockData" :key="'ass-' + ak" :label="`用户${ak}`" :value="`user${ak}`" />-->
 | 
			
		||||
    <!--      </el-select>-->
 | 
			
		||||
    <!--    </el-form-item>-->
 | 
			
		||||
    <!--    <el-form-item label="候选用户">-->
 | 
			
		||||
    <!--      <el-select v-model="userTaskForm.candidateUsers" multiple collapse-tags @change="updateElementTask('candidateUsers')">-->
 | 
			
		||||
    <!--        <el-option v-for="uk in mockData" :key="'user-' + uk" :label="`用户${uk}`" :value="`user${uk}`" />-->
 | 
			
		||||
    <!--      </el-select>-->
 | 
			
		||||
    <!--    </el-form-item>-->
 | 
			
		||||
    <!--    <el-form-item label="候选分组">-->
 | 
			
		||||
    <!--      <el-select v-model="userTaskForm.candidateGroups" multiple collapse-tags @change="updateElementTask('candidateGroups')">-->
 | 
			
		||||
    <!--        <el-option v-for="gk in mockData" :key="'ass-' + gk" :label="`分组${gk}`" :value="`group${gk}`" />-->
 | 
			
		||||
    <!--      </el-select>-->
 | 
			
		||||
    <!--    </el-form-item>-->
 | 
			
		||||
    <el-form-item label="到期时间">
 | 
			
		||||
      <el-input v-model="userTaskForm.dueDate" clearable @change="updateElementTask('dueDate')" />
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item label="跟踪时间">
 | 
			
		||||
      <el-input
 | 
			
		||||
        v-model="userTaskForm.followUpDate"
 | 
			
		||||
  <el-form label-width="100px">
 | 
			
		||||
    <el-form-item label="规则类型" prop="candidateStrategy">
 | 
			
		||||
      <el-select
 | 
			
		||||
        v-model="userTaskForm.candidateStrategy"
 | 
			
		||||
        clearable
 | 
			
		||||
        @change="updateElementTask('followUpDate')"
 | 
			
		||||
        style="width: 100%"
 | 
			
		||||
        @change="changeCandidateStrategy"
 | 
			
		||||
      >
 | 
			
		||||
        <el-option
 | 
			
		||||
          v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)"
 | 
			
		||||
          :key="dict.value"
 | 
			
		||||
          :label="dict.label"
 | 
			
		||||
          :value="dict.value"
 | 
			
		||||
        />
 | 
			
		||||
      </el-select>
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy == 10"
 | 
			
		||||
      label="指定角色"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
    >
 | 
			
		||||
      <el-select
 | 
			
		||||
        v-model="userTaskForm.candidateParam"
 | 
			
		||||
        clearable
 | 
			
		||||
        multiple
 | 
			
		||||
        style="width: 100%"
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      >
 | 
			
		||||
        <el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
 | 
			
		||||
      </el-select>
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy == 20 || userTaskForm.candidateStrategy == 21"
 | 
			
		||||
      label="指定部门"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
      span="24"
 | 
			
		||||
    >
 | 
			
		||||
      <el-tree-select
 | 
			
		||||
        ref="treeRef"
 | 
			
		||||
        v-model="userTaskForm.candidateParam"
 | 
			
		||||
        :data="deptTreeOptions"
 | 
			
		||||
        :props="defaultProps"
 | 
			
		||||
        empty-text="加载中,请稍后"
 | 
			
		||||
        multiple
 | 
			
		||||
        node-key="id"
 | 
			
		||||
        show-checkbox
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      />
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item label="优先级">
 | 
			
		||||
      <el-input v-model="userTaskForm.priority" clearable @change="updateElementTask('priority')" />
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy == 22"
 | 
			
		||||
      label="指定岗位"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
      span="24"
 | 
			
		||||
    >
 | 
			
		||||
      <el-select
 | 
			
		||||
        v-model="userTaskForm.candidateParam"
 | 
			
		||||
        clearable
 | 
			
		||||
        multiple
 | 
			
		||||
        style="width: 100%"
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      >
 | 
			
		||||
        <el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
 | 
			
		||||
      </el-select>
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    友情提示:任务的分配规则,使用
 | 
			
		||||
    <router-link target="_blank" :to="{ path: '/bpm/manager/model' }"
 | 
			
		||||
      ><el-link type="danger">流程模型</el-link>
 | 
			
		||||
    </router-link>
 | 
			
		||||
    下的【分配规则】替代,提供指定角色、部门负责人、部门成员、岗位、工作组、自定义脚本等 7
 | 
			
		||||
    种维护的任务分配维度,更加灵活!
 | 
			
		||||
  </div>
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy == 30"
 | 
			
		||||
      label="指定用户"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
      span="24"
 | 
			
		||||
    >
 | 
			
		||||
      <el-select
 | 
			
		||||
        v-model="userTaskForm.candidateParam"
 | 
			
		||||
        clearable
 | 
			
		||||
        multiple
 | 
			
		||||
        style="width: 100%"
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      >
 | 
			
		||||
        <el-option
 | 
			
		||||
          v-for="item in userOptions"
 | 
			
		||||
          :key="item.id"
 | 
			
		||||
          :label="item.nickname"
 | 
			
		||||
          :value="item.id"
 | 
			
		||||
        />
 | 
			
		||||
      </el-select>
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy === 40"
 | 
			
		||||
      label="指定用户组"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
    >
 | 
			
		||||
      <el-select
 | 
			
		||||
        v-model="userTaskForm.candidateParam"
 | 
			
		||||
        clearable
 | 
			
		||||
        multiple
 | 
			
		||||
        style="width: 100%"
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      >
 | 
			
		||||
        <el-option
 | 
			
		||||
          v-for="item in userGroupOptions"
 | 
			
		||||
          :key="item.id"
 | 
			
		||||
          :label="item.name"
 | 
			
		||||
          :value="item.id"
 | 
			
		||||
        />
 | 
			
		||||
      </el-select>
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
    <el-form-item
 | 
			
		||||
      v-if="userTaskForm.candidateStrategy === 60"
 | 
			
		||||
      label="流程表达式"
 | 
			
		||||
      prop="candidateParam"
 | 
			
		||||
    >
 | 
			
		||||
      <el-input
 | 
			
		||||
        type="textarea"
 | 
			
		||||
        v-model="userTaskForm.candidateParam[0]"
 | 
			
		||||
        clearable
 | 
			
		||||
        style="width: 72%"
 | 
			
		||||
        @change="updateElementTask"
 | 
			
		||||
      />
 | 
			
		||||
      <el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
 | 
			
		||||
        >选择表达式</el-button
 | 
			
		||||
      >
 | 
			
		||||
      <!-- 选择弹窗 -->
 | 
			
		||||
      <ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
 | 
			
		||||
    </el-form-item>
 | 
			
		||||
  </el-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 | 
			
		||||
import { defaultProps, handleTree } from '@/utils/tree'
 | 
			
		||||
import * as RoleApi from '@/api/system/role'
 | 
			
		||||
import * as DeptApi from '@/api/system/dept'
 | 
			
		||||
import * as PostApi from '@/api/system/post'
 | 
			
		||||
import * as UserApi from '@/api/system/user'
 | 
			
		||||
import * as UserGroupApi from '@/api/bpm/userGroup'
 | 
			
		||||
import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'UserTask' })
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  id: String,
 | 
			
		||||
  type: String
 | 
			
		||||
})
 | 
			
		||||
const defaultTaskForm = ref({
 | 
			
		||||
  assignee: '',
 | 
			
		||||
  candidateUsers: [],
 | 
			
		||||
  candidateGroups: [],
 | 
			
		||||
  dueDate: '',
 | 
			
		||||
  followUpDate: '',
 | 
			
		||||
  priority: ''
 | 
			
		||||
const userTaskForm = ref({
 | 
			
		||||
  candidateStrategy: undefined, // 分配规则
 | 
			
		||||
  candidateParam: [] // 分配选项
 | 
			
		||||
})
 | 
			
		||||
const userTaskForm = ref<any>({})
 | 
			
		||||
// const mockData=ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
 | 
			
		||||
const bpmnElement = ref()
 | 
			
		||||
const bpmnInstances = () => (window as any)?.bpmnInstances
 | 
			
		||||
 | 
			
		||||
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
 | 
			
		||||
const deptTreeOptions = ref() // 部门树
 | 
			
		||||
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
 | 
			
		||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
 | 
			
		||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
 | 
			
		||||
 | 
			
		||||
const resetTaskForm = () => {
 | 
			
		||||
  for (let key in defaultTaskForm.value) {
 | 
			
		||||
    let value
 | 
			
		||||
    if (key === 'candidateUsers' || key === 'candidateGroups') {
 | 
			
		||||
      value = bpmnElement.value?.businessObject[key]
 | 
			
		||||
        ? bpmnElement.value.businessObject[key].split(',')
 | 
			
		||||
        : []
 | 
			
		||||
  const businessObject = bpmnElement.value.businessObject
 | 
			
		||||
  if (!businessObject) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  if (businessObject.candidateStrategy != undefined) {
 | 
			
		||||
    userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
 | 
			
		||||
  } else {
 | 
			
		||||
    userTaskForm.value.candidateStrategy = undefined
 | 
			
		||||
  }
 | 
			
		||||
  if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
 | 
			
		||||
    if (userTaskForm.value.candidateStrategy === 60) {
 | 
			
		||||
      // 特殊:流程表达式,只有一个 input 输入框
 | 
			
		||||
      userTaskForm.value.candidateParam = [businessObject.candidateParam]
 | 
			
		||||
    } else {
 | 
			
		||||
      value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key]
 | 
			
		||||
      userTaskForm.value.candidateParam = businessObject.candidateParam
 | 
			
		||||
        .split(',')
 | 
			
		||||
        .map((item) => +item)
 | 
			
		||||
    }
 | 
			
		||||
    userTaskForm.value[key] = value
 | 
			
		||||
  } else {
 | 
			
		||||
    userTaskForm.value.candidateParam = []
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const updateElementTask = (key) => {
 | 
			
		||||
  const taskAttr = Object.create(null)
 | 
			
		||||
  if (key === 'candidateUsers' || key === 'candidateGroups') {
 | 
			
		||||
    taskAttr[key] =
 | 
			
		||||
      userTaskForm.value[key] && userTaskForm.value[key].length
 | 
			
		||||
        ? userTaskForm.value[key].join()
 | 
			
		||||
        : null
 | 
			
		||||
  } else {
 | 
			
		||||
    taskAttr[key] = userTaskForm.value[key] || null
 | 
			
		||||
  }
 | 
			
		||||
  bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
 | 
			
		||||
 | 
			
		||||
/** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
 | 
			
		||||
const changeCandidateStrategy = () => {
 | 
			
		||||
  userTaskForm.value.candidateParam = []
 | 
			
		||||
  updateElementTask()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 选中某个 options 时候,更新 bpmn 图  */
 | 
			
		||||
const updateElementTask = () => {
 | 
			
		||||
  bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
 | 
			
		||||
    candidateStrategy: userTaskForm.value.candidateStrategy,
 | 
			
		||||
    candidateParam: userTaskForm.value.candidateParam.join(',')
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 打开监听器弹窗
 | 
			
		||||
const processExpressionDialogRef = ref()
 | 
			
		||||
const openProcessExpressionDialog = async () => {
 | 
			
		||||
  processExpressionDialogRef.value.open()
 | 
			
		||||
}
 | 
			
		||||
const selectProcessExpression = (expression) => {
 | 
			
		||||
  userTaskForm.value.candidateParam = [expression.expression]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +211,21 @@ watch(
 | 
			
		|||
  },
 | 
			
		||||
  { immediate: true }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
onMounted(async () => {
 | 
			
		||||
  // 获得角色列表
 | 
			
		||||
  roleOptions.value = await RoleApi.getSimpleRoleList()
 | 
			
		||||
  // 获得部门列表
 | 
			
		||||
  const deptOptions = await DeptApi.getSimpleDeptList()
 | 
			
		||||
  deptTreeOptions.value = handleTree(deptOptions, 'id')
 | 
			
		||||
  // 获得岗位列表
 | 
			
		||||
  postOptions.value = await PostApi.getSimplePostList()
 | 
			
		||||
  // 获得用户列表
 | 
			
		||||
  userOptions.value = await UserApi.getSimpleUserList()
 | 
			
		||||
  // 获得用户组列表
 | 
			
		||||
  userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
onBeforeUnmount(() => {
 | 
			
		||||
  bpmnElement.value = null
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import { toRaw } from 'vue'
 | 
			
		|||
const bpmnInstances = () => (window as any)?.bpmnInstances
 | 
			
		||||
// 创建监听器实例
 | 
			
		||||
export function createListenerObject(options, isTask, prefix) {
 | 
			
		||||
  debugger
 | 
			
		||||
  const listenerObj = Object.create(null)
 | 
			
		||||
  listenerObj.event = options.event
 | 
			
		||||
  isTask && (listenerObj.id = options.id) // 任务监听器特有的 id 字段
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } f
 | 
			
		|||
import errorCode from './errorCode'
 | 
			
		||||
 | 
			
		||||
import { resetRouter } from '@/router'
 | 
			
		||||
import { useCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { deleteUserCache } from '@/hooks/web/useCache'
 | 
			
		||||
 | 
			
		||||
const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
 | 
			
		||||
const { result_code, base_url, request_timeout } = config
 | 
			
		||||
| 
						 | 
				
			
			@ -217,9 +217,8 @@ const handleAuthorized = () => {
 | 
			
		|||
      confirmButtonText: t('login.relogin'),
 | 
			
		||||
      type: 'warning'
 | 
			
		||||
    }).then(() => {
 | 
			
		||||
      const { wsCache } = useCache()
 | 
			
		||||
      resetRouter() // 重置静态路由表
 | 
			
		||||
      wsCache.clear()
 | 
			
		||||
      deleteUserCache() // 删除用户缓存
 | 
			
		||||
      removeToken()
 | 
			
		||||
      isRelogin.show = false
 | 
			
		||||
      // 干掉token后再走一次路由让它过router.beforeEach的校验
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,13 +7,18 @@ import WebStorageCache from 'web-storage-cache'
 | 
			
		|||
type CacheType = 'localStorage' | 'sessionStorage'
 | 
			
		||||
 | 
			
		||||
export const CACHE_KEY = {
 | 
			
		||||
  IS_DARK: 'isDark',
 | 
			
		||||
  // 用户相关
 | 
			
		||||
  ROLE_ROUTERS: 'roleRouters',
 | 
			
		||||
  USER: 'user',
 | 
			
		||||
  // 系统设置
 | 
			
		||||
  IS_DARK: 'isDark',
 | 
			
		||||
  LANG: 'lang',
 | 
			
		||||
  THEME: 'theme',
 | 
			
		||||
  LAYOUT: 'layout',
 | 
			
		||||
  ROLE_ROUTERS: 'roleRouters',
 | 
			
		||||
  DICT_CACHE: 'dictCache'
 | 
			
		||||
  DICT_CACHE: 'dictCache',
 | 
			
		||||
  // 登录表单
 | 
			
		||||
  LoginForm: 'loginForm',
 | 
			
		||||
  TenantId: 'tenantId'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useCache = (type: CacheType = 'localStorage') => {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,3 +30,10 @@ export const useCache = (type: CacheType = 'localStorage') => {
 | 
			
		|||
    wsCache
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const deleteUserCache = () => {
 | 
			
		||||
  const { wsCache } = useCache()
 | 
			
		||||
  wsCache.delete(CACHE_KEY.USER)
 | 
			
		||||
  wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
 | 
			
		||||
  // 注意,不要清理 LoginForm 登录表单
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,13 +24,12 @@ const toggleCollapse = () => {
 | 
			
		|||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div :class="prefixCls">
 | 
			
		||||
  <div :class="prefixCls" @click="toggleCollapse">
 | 
			
		||||
    <Icon
 | 
			
		||||
      :color="color"
 | 
			
		||||
      :icon="collapse ? 'ep:expand' : 'ep:fold'"
 | 
			
		||||
      :size="18"
 | 
			
		||||
      class="cursor-pointer"
 | 
			
		||||
      @click="toggleCollapse"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,16 +124,6 @@ export default defineComponent({
 | 
			
		|||
<style lang="scss" scoped>
 | 
			
		||||
$prefix-cls: #{$namespace}-menu;
 | 
			
		||||
 | 
			
		||||
.is-active--after {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  width: 4px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  background-color: var(--el-color-primary);
 | 
			
		||||
  content: '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.#{$prefix-cls} {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  transition: width var(--transition-time-02);
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +149,6 @@ $prefix-cls: #{$namespace}-menu;
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // 设置选中时的高亮背景和高亮颜色
 | 
			
		||||
    .#{$elNamespace}-sub-menu.is-active,
 | 
			
		||||
    .#{$elNamespace}-menu-item.is-active {
 | 
			
		||||
      color: var(--left-menu-text-active-color) !important;
 | 
			
		||||
      background-color: var(--left-menu-bg-active-color) !important;
 | 
			
		||||
| 
						 | 
				
			
			@ -171,10 +160,6 @@ $prefix-cls: #{$namespace}-menu;
 | 
			
		|||
 | 
			
		||||
    .#{$elNamespace}-menu-item.is-active {
 | 
			
		||||
      position: relative;
 | 
			
		||||
 | 
			
		||||
      &::after {
 | 
			
		||||
        @extend .is-active--after;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 设置子菜单的背景颜色
 | 
			
		||||
| 
						 | 
				
			
			@ -194,10 +179,6 @@ $prefix-cls: #{$namespace}-menu;
 | 
			
		|||
    & > .is-active > .#{$elNamespace}-sub-menu__title {
 | 
			
		||||
      position: relative;
 | 
			
		||||
      background-color: var(--left-menu-collapse-bg-active-color) !important;
 | 
			
		||||
 | 
			
		||||
      &::after {
 | 
			
		||||
        @extend .is-active--after;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -245,16 +226,6 @@ $prefix-cls: #{$namespace}-menu;
 | 
			
		|||
<style lang="scss">
 | 
			
		||||
$prefix-cls: #{$namespace}-menu-popper;
 | 
			
		||||
 | 
			
		||||
.is-active--after {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  width: 4px;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  background-color: var(--el-color-primary);
 | 
			
		||||
  content: '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.#{$prefix-cls}--vertical,
 | 
			
		||||
.#{$prefix-cls}--horizontal {
 | 
			
		||||
  // 设置选中时子标题的颜色
 | 
			
		||||
| 
						 | 
				
			
			@ -281,10 +252,6 @@ $prefix-cls: #{$namespace}-menu-popper;
 | 
			
		|||
    &:hover {
 | 
			
		||||
      background-color: var(--left-menu-bg-active-color) !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &::after {
 | 
			
		||||
      @extend .is-active--after;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,59 +1,50 @@
 | 
			
		|||
import { ElSubMenu, ElMenuItem } from 'element-plus'
 | 
			
		||||
import type { RouteMeta } from 'vue-router'
 | 
			
		||||
import { hasOneShowingChild } from '../helper'
 | 
			
		||||
import { isUrl } from '@/utils/is'
 | 
			
		||||
import { useRenderMenuTitle } from './useRenderMenuTitle'
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { pathResolve } from '@/utils/routerHelper'
 | 
			
		||||
 | 
			
		||||
export const useRenderMenuItem = (
 | 
			
		||||
const { renderMenuTitle } = useRenderMenuTitle()
 | 
			
		||||
 | 
			
		||||
export const useRenderMenuItem = () =>
 | 
			
		||||
  // allRouters: AppRouteRecordRaw[] = [],
 | 
			
		||||
  menuMode: 'vertical' | 'horizontal'
 | 
			
		||||
) => {
 | 
			
		||||
  const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
 | 
			
		||||
    return routers.map((v) => {
 | 
			
		||||
      const meta = (v.meta ?? {}) as RouteMeta
 | 
			
		||||
      if (!meta.hidden) {
 | 
			
		||||
        const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
 | 
			
		||||
        const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
 | 
			
		||||
  {
 | 
			
		||||
    const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
 | 
			
		||||
      return routers
 | 
			
		||||
        .filter((v) => !v.meta?.hidden)
 | 
			
		||||
        .map((v) => {
 | 
			
		||||
          const meta = v.meta ?? {}
 | 
			
		||||
          const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
 | 
			
		||||
          const fullPath = isUrl(v.path) ? v.path : pathResolve(parentPath, v.path) // getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
 | 
			
		||||
 | 
			
		||||
        const { renderMenuTitle } = useRenderMenuTitle()
 | 
			
		||||
          if (
 | 
			
		||||
            oneShowingChild &&
 | 
			
		||||
            (!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
 | 
			
		||||
            !meta?.alwaysShow
 | 
			
		||||
          ) {
 | 
			
		||||
            return (
 | 
			
		||||
              <ElMenuItem
 | 
			
		||||
                index={onlyOneChild ? pathResolve(fullPath, onlyOneChild.path) : fullPath}
 | 
			
		||||
              >
 | 
			
		||||
                {{
 | 
			
		||||
                  default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
 | 
			
		||||
                }}
 | 
			
		||||
              </ElMenuItem>
 | 
			
		||||
            )
 | 
			
		||||
          } else {
 | 
			
		||||
            return (
 | 
			
		||||
              <ElSubMenu index={fullPath}>
 | 
			
		||||
                {{
 | 
			
		||||
                  title: () => renderMenuTitle(meta),
 | 
			
		||||
                  default: () => renderMenuItem(v.children!, fullPath)
 | 
			
		||||
                }}
 | 
			
		||||
              </ElSubMenu>
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
          oneShowingChild &&
 | 
			
		||||
          (!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
 | 
			
		||||
          !meta?.alwaysShow
 | 
			
		||||
        ) {
 | 
			
		||||
          return (
 | 
			
		||||
            <ElMenuItem index={onlyOneChild ? pathResolve(fullPath, onlyOneChild.path) : fullPath}>
 | 
			
		||||
              {{
 | 
			
		||||
                default: () => renderMenuTitle(onlyOneChild ? onlyOneChild?.meta : meta)
 | 
			
		||||
              }}
 | 
			
		||||
            </ElMenuItem>
 | 
			
		||||
          )
 | 
			
		||||
        } else {
 | 
			
		||||
          const { getPrefixCls } = useDesign()
 | 
			
		||||
 | 
			
		||||
          const preFixCls = getPrefixCls('menu-popper')
 | 
			
		||||
          return (
 | 
			
		||||
            <ElSubMenu
 | 
			
		||||
              index={fullPath}
 | 
			
		||||
              popperClass={
 | 
			
		||||
                menuMode === 'vertical' ? `${preFixCls}--vertical` : `${preFixCls}--horizontal`
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              {{
 | 
			
		||||
                title: () => renderMenuTitle(meta),
 | 
			
		||||
                default: () => renderMenuItem(v.children!, fullPath)
 | 
			
		||||
              }}
 | 
			
		||||
            </ElSubMenu>
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    return {
 | 
			
		||||
      renderMenuItem
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    renderMenuItem
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import type { RouteMeta } from 'vue-router'
 | 
			
		||||
import { Icon } from '@/components/Icon'
 | 
			
		||||
import { useI18n } from '@/hooks/web/useI18n'
 | 
			
		||||
 | 
			
		||||
export const useRenderMenuTitle = () => {
 | 
			
		||||
  const renderMenuTitle = (meta: RouteMeta) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -9,10 +10,14 @@ export const useRenderMenuTitle = () => {
 | 
			
		|||
    return icon ? (
 | 
			
		||||
      <>
 | 
			
		||||
        <Icon icon={meta.icon}></Icon>
 | 
			
		||||
        <span class="v-menu__title">{t(title as string)}</span>
 | 
			
		||||
        <span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
 | 
			
		||||
          {t(title as string)}
 | 
			
		||||
        </span>
 | 
			
		||||
      </>
 | 
			
		||||
    ) : (
 | 
			
		||||
      <span class="v-menu__title">{t(title as string)}</span>
 | 
			
		||||
      <span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap">
 | 
			
		||||
        {t(title as string)}
 | 
			
		||||
      </span>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ export default defineComponent({
 | 
			
		|||
        id={`${variables.namespace}-menu`}
 | 
			
		||||
        class={[
 | 
			
		||||
          prefixCls,
 | 
			
		||||
          'relative bg-[var(--left-menu-bg-color)] top-1px z-3000 layout-border__right',
 | 
			
		||||
          'relative bg-[var(--left-menu-bg-color)] top-1px layout-border__right',
 | 
			
		||||
          {
 | 
			
		||||
            'w-[var(--tab-menu-max-width)]': !unref(collapse),
 | 
			
		||||
            'w-[var(--tab-menu-min-width)]': unref(collapse)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,9 @@ import avatarImg from '@/assets/imgs/avatar.gif'
 | 
			
		|||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { useTagsViewStore } from '@/store/modules/tagsView'
 | 
			
		||||
import { useUserStore } from '@/store/modules/user'
 | 
			
		||||
import LockDialog from './components/LockDialog.vue'
 | 
			
		||||
import LockPage from './components/LockPage.vue'
 | 
			
		||||
import { useLockStore } from '@/store/modules/lock'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'UserInfo' })
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +26,14 @@ const prefixCls = getPrefixCls('user-info')
 | 
			
		|||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
 | 
			
		||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
 | 
			
		||||
 | 
			
		||||
// 锁定屏幕
 | 
			
		||||
const lockStore = useLockStore()
 | 
			
		||||
const getIsLock = computed(() => lockStore.getLockInfo?.isLock ?? false)
 | 
			
		||||
const dialogVisible = ref<boolean>(false)
 | 
			
		||||
const lockScreen = () => {
 | 
			
		||||
  dialogVisible.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const loginOut = async () => {
 | 
			
		||||
  try {
 | 
			
		||||
    await ElMessageBox.confirm(t('common.loginOutMessage'), t('common.reminder'), {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +44,7 @@ const loginOut = async () => {
 | 
			
		|||
    await userStore.loginOut()
 | 
			
		||||
    tagsViewStore.delAllViews()
 | 
			
		||||
    replace('/login?redirect=/index')
 | 
			
		||||
  }
 | 
			
		||||
  catch { }
 | 
			
		||||
  } catch {}
 | 
			
		||||
}
 | 
			
		||||
const toProfile = async () => {
 | 
			
		||||
  push('/user/profile')
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +72,10 @@ const toDocument = () => {
 | 
			
		|||
          <Icon icon="ep:menu" />
 | 
			
		||||
          <div @click="toDocument">{{ t('common.document') }}</div>
 | 
			
		||||
        </ElDropdownItem>
 | 
			
		||||
        <ElDropdownItem divided>
 | 
			
		||||
          <Icon icon="ep:lock" />
 | 
			
		||||
          <div @click="lockScreen">{{ t('lock.lockScreen') }}</div>
 | 
			
		||||
        </ElDropdownItem>
 | 
			
		||||
        <ElDropdownItem divided @click="loginOut">
 | 
			
		||||
          <Icon icon="ep:switch-button" />
 | 
			
		||||
          <div>{{ t('common.loginOut') }}</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -69,4 +83,31 @@ const toDocument = () => {
 | 
			
		|||
      </ElDropdownMenu>
 | 
			
		||||
    </template>
 | 
			
		||||
  </ElDropdown>
 | 
			
		||||
 | 
			
		||||
  <LockDialog v-if="dialogVisible" v-model="dialogVisible" />
 | 
			
		||||
 | 
			
		||||
  <teleport to="body">
 | 
			
		||||
    <transition name="fade-bottom" mode="out-in">
 | 
			
		||||
      <LockPage v-if="getIsLock" />
 | 
			
		||||
    </transition>
 | 
			
		||||
  </teleport>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="scss">
 | 
			
		||||
.fade-bottom-enter-active,
 | 
			
		||||
.fade-bottom-leave-active {
 | 
			
		||||
  transition:
 | 
			
		||||
    opacity 0.25s,
 | 
			
		||||
    transform 0.3s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fade-bottom-enter-from {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transform: translateY(-10%);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fade-bottom-leave-to {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transform: translateY(10%);
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
import { useValidator } from '@/hooks/web/useValidator'
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { useLockStore } from '@/store/modules/lock'
 | 
			
		||||
import avatarImg from '@/assets/imgs/avatar.gif'
 | 
			
		||||
import { useUserStore } from '@/store/modules/user'
 | 
			
		||||
 | 
			
		||||
const { getPrefixCls } = useDesign()
 | 
			
		||||
const prefixCls = getPrefixCls('lock-dialog')
 | 
			
		||||
 | 
			
		||||
const { required } = useValidator()
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
const lockStore = useLockStore()
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  modelValue: {
 | 
			
		||||
    type: Boolean
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const userStore = useUserStore()
 | 
			
		||||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
 | 
			
		||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['update:modelValue'])
 | 
			
		||||
 | 
			
		||||
const dialogVisible = computed({
 | 
			
		||||
  get: () => props.modelValue,
 | 
			
		||||
  set: (val) => {
 | 
			
		||||
    console.log('set: ', val)
 | 
			
		||||
    emit('update:modelValue', val)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const dialogTitle = ref(t('lock.lockScreen'))
 | 
			
		||||
 | 
			
		||||
const formData = ref({
 | 
			
		||||
  password: undefined
 | 
			
		||||
})
 | 
			
		||||
const formRules = reactive({
 | 
			
		||||
  password: [required()]
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const formRef = ref() // 表单 Ref
 | 
			
		||||
const handleLock = async () => {
 | 
			
		||||
  // 校验表单
 | 
			
		||||
  if (!formRef) return
 | 
			
		||||
  const valid = await formRef.value.validate()
 | 
			
		||||
  if (!valid) return
 | 
			
		||||
  // 提交请求
 | 
			
		||||
  dialogVisible.value = false
 | 
			
		||||
  lockStore.setLockInfo({
 | 
			
		||||
    ...formData.value,
 | 
			
		||||
    isLock: true
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <Dialog
 | 
			
		||||
    v-model="dialogVisible"
 | 
			
		||||
    width="500px"
 | 
			
		||||
    max-height="170px"
 | 
			
		||||
    :class="prefixCls"
 | 
			
		||||
    :title="dialogTitle"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex flex-col items-center">
 | 
			
		||||
      <img :src="avatar" alt="" class="w-70px h-70px rounded-[50%]" />
 | 
			
		||||
      <span class="text-14px my-10px text-[var(--top-header-text-color)]">
 | 
			
		||||
        {{ userName }}
 | 
			
		||||
      </span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
 | 
			
		||||
      <el-form-item :label="t('lock.lockPassword')" prop="password">
 | 
			
		||||
        <el-input
 | 
			
		||||
          type="password"
 | 
			
		||||
          v-model="formData.password"
 | 
			
		||||
          :placeholder="'请输入' + t('lock.lockPassword')"
 | 
			
		||||
          clearable
 | 
			
		||||
          show-password
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
    </el-form>
 | 
			
		||||
    <template #footer>
 | 
			
		||||
      <ElButton type="primary" @click="handleLock">{{ t('lock.lock') }}</ElButton>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
:global(.v-lock-dialog) {
 | 
			
		||||
  @media (max-width: 767px) {
 | 
			
		||||
    max-width: calc(100vw - 16px);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,270 @@
 | 
			
		|||
<script lang="ts" setup>
 | 
			
		||||
import { resetRouter } from '@/router'
 | 
			
		||||
import { deleteUserCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { useLockStore } from '@/store/modules/lock'
 | 
			
		||||
import { useNow } from '@/hooks/web/useNow'
 | 
			
		||||
import { useDesign } from '@/hooks/web/useDesign'
 | 
			
		||||
import { useTagsViewStore } from '@/store/modules/tagsView'
 | 
			
		||||
import { useUserStore } from '@/store/modules/user'
 | 
			
		||||
import avatarImg from '@/assets/imgs/avatar.gif'
 | 
			
		||||
 | 
			
		||||
const tagsViewStore = useTagsViewStore()
 | 
			
		||||
 | 
			
		||||
const { replace } = useRouter()
 | 
			
		||||
 | 
			
		||||
const userStore = useUserStore()
 | 
			
		||||
 | 
			
		||||
const password = ref('')
 | 
			
		||||
const loading = ref(false)
 | 
			
		||||
const errMsg = ref(false)
 | 
			
		||||
const showDate = ref(true)
 | 
			
		||||
 | 
			
		||||
const { getPrefixCls } = useDesign()
 | 
			
		||||
const prefixCls = getPrefixCls('lock-page')
 | 
			
		||||
 | 
			
		||||
const avatar = computed(() => userStore.user.avatar ?? avatarImg)
 | 
			
		||||
const userName = computed(() => userStore.user.nickname ?? 'Admin')
 | 
			
		||||
 | 
			
		||||
const lockStore = useLockStore()
 | 
			
		||||
 | 
			
		||||
const { hour, month, minute, meridiem, year, day, week } = useNow(true)
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n()
 | 
			
		||||
 | 
			
		||||
// 解锁
 | 
			
		||||
async function unLock() {
 | 
			
		||||
  if (!password.value) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  let pwd = password.value
 | 
			
		||||
  try {
 | 
			
		||||
    loading.value = true
 | 
			
		||||
    const res = await lockStore.unLock(pwd)
 | 
			
		||||
    errMsg.value = !res
 | 
			
		||||
  } finally {
 | 
			
		||||
    loading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 返回登录
 | 
			
		||||
async function goLogin() {
 | 
			
		||||
  await userStore.loginOut().catch(() => {})
 | 
			
		||||
  // 登出后清理
 | 
			
		||||
  deleteUserCache() // 清空用户缓存
 | 
			
		||||
  tagsViewStore.delAllViews()
 | 
			
		||||
  resetRouter() // 重置静态路由表
 | 
			
		||||
  lockStore.resetLockInfo()
 | 
			
		||||
  replace('/login')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleShowForm(show = false) {
 | 
			
		||||
  showDate.value = show
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div
 | 
			
		||||
    :class="prefixCls"
 | 
			
		||||
    class="fixed inset-0 flex h-screen w-screen bg-black items-center justify-center"
 | 
			
		||||
  >
 | 
			
		||||
    <div
 | 
			
		||||
      :class="`${prefixCls}__unlock`"
 | 
			
		||||
      class="absolute top-0 left-1/2 flex pt-5 h-16 items-center justify-center sm:text-md xl:text-xl text-white flex-col cursor-pointer transform translate-x-1/2"
 | 
			
		||||
      @click="handleShowForm(false)"
 | 
			
		||||
      v-show="showDate"
 | 
			
		||||
    >
 | 
			
		||||
      <Icon icon="ep:lock" />
 | 
			
		||||
      <span>{{ t('lock.unlock') }}</span>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="flex w-screen h-screen justify-center items-center">
 | 
			
		||||
      <div :class="`${prefixCls}__hour`" class="relative mr-5 md:mr-20 w-2/5 h-2/5 md:h-4/5">
 | 
			
		||||
        <span>{{ hour }}</span>
 | 
			
		||||
        <span class="meridiem absolute left-5 top-5 text-md xl:text-xl" v-show="showDate">
 | 
			
		||||
          {{ meridiem }}
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div :class="`${prefixCls}__minute w-2/5 h-2/5 md:h-4/5 `">
 | 
			
		||||
        <span> {{ minute }}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <transition name="fade-slide">
 | 
			
		||||
      <div :class="`${prefixCls}-entry`" v-show="!showDate">
 | 
			
		||||
        <div :class="`${prefixCls}-entry-content`">
 | 
			
		||||
          <div class="flex flex-col items-center">
 | 
			
		||||
            <img :src="avatar" alt="" class="w-70px h-70px rounded-[50%]" />
 | 
			
		||||
            <span class="text-14px my-10px text-[var(--logo-title-text-color)]">
 | 
			
		||||
              {{ userName }}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <ElInput
 | 
			
		||||
            type="password"
 | 
			
		||||
            :placeholder="t('lock.placeholder')"
 | 
			
		||||
            class="enter-x"
 | 
			
		||||
            v-model="password"
 | 
			
		||||
          />
 | 
			
		||||
          <span :class="`text-14px ${prefixCls}-entry__err-msg enter-x`" v-if="errMsg">
 | 
			
		||||
            {{ t('lock.message') }}
 | 
			
		||||
          </span>
 | 
			
		||||
          <div :class="`${prefixCls}-entry__footer enter-x`">
 | 
			
		||||
            <ElButton
 | 
			
		||||
              type="primary"
 | 
			
		||||
              size="small"
 | 
			
		||||
              class="mt-2 mr-2 enter-x"
 | 
			
		||||
              link
 | 
			
		||||
              :disabled="loading"
 | 
			
		||||
              @click="handleShowForm(true)"
 | 
			
		||||
            >
 | 
			
		||||
              {{ t('common.back') }}
 | 
			
		||||
            </ElButton>
 | 
			
		||||
            <ElButton
 | 
			
		||||
              type="primary"
 | 
			
		||||
              size="small"
 | 
			
		||||
              class="mt-2 mr-2 enter-x"
 | 
			
		||||
              link
 | 
			
		||||
              :disabled="loading"
 | 
			
		||||
              @click="goLogin"
 | 
			
		||||
            >
 | 
			
		||||
              {{ t('lock.backToLogin') }}
 | 
			
		||||
            </ElButton>
 | 
			
		||||
            <ElButton
 | 
			
		||||
              type="primary"
 | 
			
		||||
              class="mt-2"
 | 
			
		||||
              size="small"
 | 
			
		||||
              link
 | 
			
		||||
              @click="unLock()"
 | 
			
		||||
              :disabled="loading"
 | 
			
		||||
            >
 | 
			
		||||
              {{ t('lock.entrySystem') }}
 | 
			
		||||
            </ElButton>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </transition>
 | 
			
		||||
 | 
			
		||||
    <div class="absolute bottom-5 w-full text-gray-300 xl:text-xl 2xl:text-3xl text-center enter-y">
 | 
			
		||||
      <div class="text-5xl mb-4 enter-x" v-show="!showDate">
 | 
			
		||||
        {{ hour }}:{{ minute }} <span class="text-3xl">{{ meridiem }}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="text-2xl">{{ year }}/{{ month }}/{{ day }} {{ week }}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
$prefix-cls: '#{$namespace}-lock-page';
 | 
			
		||||
 | 
			
		||||
// Small screen / tablet
 | 
			
		||||
$screen-sm: 576px;
 | 
			
		||||
 | 
			
		||||
// Medium screen / desktop
 | 
			
		||||
$screen-md: 768px;
 | 
			
		||||
 | 
			
		||||
// Large screen / wide desktop
 | 
			
		||||
$screen-lg: 992px;
 | 
			
		||||
 | 
			
		||||
// Extra large screen / full hd
 | 
			
		||||
$screen-xl: 1200px;
 | 
			
		||||
 | 
			
		||||
// Extra extra large screen / large desktop
 | 
			
		||||
$screen-2xl: 1600px;
 | 
			
		||||
 | 
			
		||||
$error-color: #ed6f6f;
 | 
			
		||||
 | 
			
		||||
.#{$prefix-cls} {
 | 
			
		||||
  z-index: 3000;
 | 
			
		||||
 | 
			
		||||
  &__unlock {
 | 
			
		||||
    transform: translate(-50%, 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__hour,
 | 
			
		||||
  &__minute {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    font-weight: 700;
 | 
			
		||||
    color: #bababa;
 | 
			
		||||
    background-color: #141313;
 | 
			
		||||
    border-radius: 30px;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 | 
			
		||||
    @media screen and (max-width: $screen-md) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 160px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media screen and (min-width: $screen-md) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 160px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media screen and (max-width: $screen-sm) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 90px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    @media screen and (min-width: $screen-lg) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 220px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @media screen and (min-width: $screen-xl) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 260px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    @media screen and (min-width: $screen-2xl) {
 | 
			
		||||
      span:not(.meridiem) {
 | 
			
		||||
        font-size: 320px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-entry {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    background-color: rgba(0, 0, 0, 0.5);
 | 
			
		||||
    backdrop-filter: blur(8px);
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 | 
			
		||||
    &-content {
 | 
			
		||||
      width: 260px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__header {
 | 
			
		||||
      text-align: center;
 | 
			
		||||
 | 
			
		||||
      &-img {
 | 
			
		||||
        width: 70px;
 | 
			
		||||
        margin: 0 auto;
 | 
			
		||||
        border-radius: 50%;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &-name {
 | 
			
		||||
        margin-top: 5px;
 | 
			
		||||
        font-weight: 500;
 | 
			
		||||
        color: #bababa;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__err-msg {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      margin-top: 10px;
 | 
			
		||||
      color: $error-color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__footer {
 | 
			
		||||
      display: flex;
 | 
			
		||||
      justify-content: space-between;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +56,16 @@ export default {
 | 
			
		|||
    copySuccess: 'Copy Success',
 | 
			
		||||
    copyError: 'Copy Error'
 | 
			
		||||
  },
 | 
			
		||||
  lock: {
 | 
			
		||||
    lockScreen: 'Lock screen',
 | 
			
		||||
    lock: 'Lock',
 | 
			
		||||
    lockPassword: 'Lock screen password',
 | 
			
		||||
    unlock: 'Click to unlock',
 | 
			
		||||
    backToLogin: 'Back to login',
 | 
			
		||||
    entrySystem: 'Entry the system',
 | 
			
		||||
    placeholder: 'Please enter the lock screen password',
 | 
			
		||||
    message: 'Lock screen password error'
 | 
			
		||||
  },
 | 
			
		||||
  error: {
 | 
			
		||||
    noPermission: `Sorry, you don't have permission to access this page.`,
 | 
			
		||||
    pageError: 'Sorry, the page you visited does not exist.',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,6 +56,16 @@ export default {
 | 
			
		|||
    copySuccess: '复制成功',
 | 
			
		||||
    copyError: '复制失败'
 | 
			
		||||
  },
 | 
			
		||||
  lock: {
 | 
			
		||||
    lockScreen: '锁定屏幕',
 | 
			
		||||
    lock: '锁定',
 | 
			
		||||
    lockPassword: '锁屏密码',
 | 
			
		||||
    unlock: '点击解锁',
 | 
			
		||||
    backToLogin: '返回登录',
 | 
			
		||||
    entrySystem: '进入系统',
 | 
			
		||||
    placeholder: '请输入锁屏密码',
 | 
			
		||||
    message: '锁屏密码错误'
 | 
			
		||||
  },
 | 
			
		||||
  error: {
 | 
			
		||||
    noPermission: `抱歉,您无权访问此页面。`,
 | 
			
		||||
    pageError: '抱歉,您访问的页面不存在。',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,22 +1,26 @@
 | 
			
		|||
import type { App } from 'vue'
 | 
			
		||||
// 👇使用 form-create 需额外全局引入 element plus 组件
 | 
			
		||||
import {
 | 
			
		||||
  ElAlert,
 | 
			
		||||
  ElAside,
 | 
			
		||||
  ElPopconfirm,
 | 
			
		||||
  ElHeader,
 | 
			
		||||
  ElMain,
 | 
			
		||||
  ElContainer,
 | 
			
		||||
  ElDivider,
 | 
			
		||||
  ElTransfer,
 | 
			
		||||
  ElAlert,
 | 
			
		||||
  ElTabs,
 | 
			
		||||
  ElHeader,
 | 
			
		||||
  ElMain,
 | 
			
		||||
  ElPopconfirm,
 | 
			
		||||
  ElTable,
 | 
			
		||||
  ElTableColumn,
 | 
			
		||||
  ElTabPane
 | 
			
		||||
  ElTabPane,
 | 
			
		||||
  ElTabs,
 | 
			
		||||
  ElTransfer
 | 
			
		||||
} from 'element-plus'
 | 
			
		||||
 | 
			
		||||
import FcDesigner from '@form-create/designer'
 | 
			
		||||
import formCreate from '@form-create/element-ui'
 | 
			
		||||
import install from '@form-create/element-ui/auto-import'
 | 
			
		||||
//======================= 自定义组件 =======================
 | 
			
		||||
import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
 | 
			
		||||
import { DictSelect } from '@/components/DictSelect'
 | 
			
		||||
import UserSelect from '@/views/system/user/components/UserSelect.vue'
 | 
			
		||||
 | 
			
		||||
const components = [
 | 
			
		||||
  ElAside,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +34,12 @@ const components = [
 | 
			
		|||
  ElTabs,
 | 
			
		||||
  ElTable,
 | 
			
		||||
  ElTableColumn,
 | 
			
		||||
  ElTabPane
 | 
			
		||||
  ElTabPane,
 | 
			
		||||
  UploadImg,
 | 
			
		||||
  UploadImgs,
 | 
			
		||||
  UploadFile,
 | 
			
		||||
  DictSelect,
 | 
			
		||||
  UserSelect
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
 | 
			
		||||
| 
						 | 
				
			
			@ -40,4 +49,5 @@ export const setupFormCreate = (app: App<Element>) => {
 | 
			
		|||
  })
 | 
			
		||||
  formCreate.use(install)
 | 
			
		||||
  app.use(formCreate)
 | 
			
		||||
  app.use(FcDesigner)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -243,7 +243,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
    },
 | 
			
		||||
    children: [
 | 
			
		||||
      {
 | 
			
		||||
        path: '/manager/form/edit',
 | 
			
		||||
        path: 'manager/form/edit',
 | 
			
		||||
        component: () => import('@/views/bpm/form/editor/index.vue'),
 | 
			
		||||
        name: 'BpmFormEditor',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			@ -255,7 +255,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/manager/model/edit',
 | 
			
		||||
        path: 'manager/model/edit',
 | 
			
		||||
        component: () => import('@/views/bpm/model/editor/index.vue'),
 | 
			
		||||
        name: 'BpmModelEditor',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +267,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/manager/definition',
 | 
			
		||||
        path: 'manager/simple/workflow/model/edit',
 | 
			
		||||
        component: () => import('@/views/bpm/simpleWorkflow/index.vue'),
 | 
			
		||||
        name: 'SimpleWorkflowDesignEditor',
 | 
			
		||||
        meta: {
 | 
			
		||||
          noCache: true,
 | 
			
		||||
          hidden: true,
 | 
			
		||||
          canTo: true,
 | 
			
		||||
          title: '仿钉钉设计流程',
 | 
			
		||||
          activeMenu: '/bpm/manager/model'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: 'manager/definition',
 | 
			
		||||
        component: () => import('@/views/bpm/definition/index.vue'),
 | 
			
		||||
        name: 'BpmProcessDefinition',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			@ -279,30 +291,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/manager/task-assign-rule',
 | 
			
		||||
        component: () => import('@/views/bpm/taskAssignRule/index.vue'),
 | 
			
		||||
        name: 'BpmTaskAssignRuleList',
 | 
			
		||||
        meta: {
 | 
			
		||||
          noCache: true,
 | 
			
		||||
          hidden: true,
 | 
			
		||||
          canTo: true,
 | 
			
		||||
          title: '任务分配规则'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/process-instance/create',
 | 
			
		||||
        component: () => import('@/views/bpm/processInstance/create/index.vue'),
 | 
			
		||||
        name: 'BpmProcessInstanceCreate',
 | 
			
		||||
        meta: {
 | 
			
		||||
          noCache: true,
 | 
			
		||||
          hidden: true,
 | 
			
		||||
          canTo: true,
 | 
			
		||||
          title: '发起流程',
 | 
			
		||||
          activeMenu: 'bpm/processInstance/create'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/process-instance/detail',
 | 
			
		||||
        path: 'process-instance/detail',
 | 
			
		||||
        component: () => import('@/views/bpm/processInstance/detail/index.vue'),
 | 
			
		||||
        name: 'BpmProcessInstanceDetail',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			@ -310,11 +299,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
          hidden: true,
 | 
			
		||||
          canTo: true,
 | 
			
		||||
          title: '流程详情',
 | 
			
		||||
          activeMenu: 'bpm/processInstance/detail'
 | 
			
		||||
          activeMenu: '/bpm/task/my'
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/bpm/oa/leave/create',
 | 
			
		||||
        path: 'oa/leave/create',
 | 
			
		||||
        component: () => import('@/views/bpm/oa/leave/create.vue'),
 | 
			
		||||
        name: 'OALeaveCreate',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			@ -326,7 +315,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
 | 
			
		|||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/bpm/oa/leave/detail',
 | 
			
		||||
        path: 'oa/leave/detail',
 | 
			
		||||
        component: () => import('@/views/bpm/oa/leave/detail.vue'),
 | 
			
		||||
        name: 'OALeaveDetail',
 | 
			
		||||
        meta: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
import type { App } from 'vue'
 | 
			
		||||
import { createPinia } from 'pinia'
 | 
			
		||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
 | 
			
		||||
 | 
			
		||||
const store = createPinia()
 | 
			
		||||
store.use(piniaPluginPersistedstate)
 | 
			
		||||
 | 
			
		||||
export const setupStore = (app: App<Element>) => {
 | 
			
		||||
  app.use(store)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -268,7 +268,8 @@ export const useAppStore = defineStore('app', {
 | 
			
		|||
    setFooter(footer: boolean) {
 | 
			
		||||
      this.footer = footer
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  persist: false
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const useAppStoreWithOut = () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
import { defineStore } from 'pinia'
 | 
			
		||||
import { store } from '@/store'
 | 
			
		||||
 | 
			
		||||
interface lockInfo {
 | 
			
		||||
  isLock?: boolean
 | 
			
		||||
  password?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface LockState {
 | 
			
		||||
  lockInfo: lockInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useLockStore = defineStore('lock', {
 | 
			
		||||
  state: (): LockState => {
 | 
			
		||||
    return {
 | 
			
		||||
      lockInfo: {
 | 
			
		||||
        // isLock: false, // 是否锁定屏幕
 | 
			
		||||
        // password: '' // 锁屏密码
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  getters: {
 | 
			
		||||
    getLockInfo(): lockInfo {
 | 
			
		||||
      return this.lockInfo
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  actions: {
 | 
			
		||||
    setLockInfo(lockInfo: lockInfo) {
 | 
			
		||||
      this.lockInfo = lockInfo
 | 
			
		||||
    },
 | 
			
		||||
    resetLockInfo() {
 | 
			
		||||
      this.lockInfo = {}
 | 
			
		||||
    },
 | 
			
		||||
    unLock(password: string) {
 | 
			
		||||
      if (this.lockInfo?.password === password) {
 | 
			
		||||
        this.resetLockInfo()
 | 
			
		||||
        return true
 | 
			
		||||
      } else {
 | 
			
		||||
        return false
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  persist: true
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const useLockStoreWithOut = () => {
 | 
			
		||||
  return useLockStore(store)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { defineStore } from 'pinia'
 | 
			
		||||
import { store } from '../index'
 | 
			
		||||
import { store } from '@/store'
 | 
			
		||||
import { cloneDeep } from 'lodash-es'
 | 
			
		||||
import remainingRouter from '@/router/modules/remaining'
 | 
			
		||||
import { flatMultiLevelRoutes, generateRoute } from '@/utils/routerHelper'
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,8 @@ export const usePermissionStore = defineStore('permission', {
 | 
			
		|||
    setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
 | 
			
		||||
      this.menuTabRouters = routers
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  persist: false
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const usePermissionStoreWithOut = () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
import { store } from '../index'
 | 
			
		||||
import { defineStore } from 'pinia'
 | 
			
		||||
 | 
			
		||||
export const useWorkFlowStore = defineStore('simpleWorkflow', {
 | 
			
		||||
  state: () => ({
 | 
			
		||||
    tableId: '',
 | 
			
		||||
    isTried: false,
 | 
			
		||||
    promoterDrawer: false,
 | 
			
		||||
    flowPermission1: {},
 | 
			
		||||
    approverDrawer: false,
 | 
			
		||||
    approverConfig1: {},
 | 
			
		||||
    copyerDrawer: false,
 | 
			
		||||
    copyerConfig1: {},
 | 
			
		||||
    conditionDrawer: false,
 | 
			
		||||
    conditionsConfig1: {
 | 
			
		||||
      conditionNodes: []
 | 
			
		||||
    }
 | 
			
		||||
  }),
 | 
			
		||||
  actions: {
 | 
			
		||||
    setTableId(payload) {
 | 
			
		||||
      this.tableId = payload
 | 
			
		||||
    },
 | 
			
		||||
    setIsTried(payload) {
 | 
			
		||||
      this.isTried = payload
 | 
			
		||||
    },
 | 
			
		||||
    setPromoter(payload) {
 | 
			
		||||
      this.promoterDrawer = payload
 | 
			
		||||
    },
 | 
			
		||||
    setFlowPermission(payload) {
 | 
			
		||||
      this.flowPermission1 = payload
 | 
			
		||||
    },
 | 
			
		||||
    setApprover(payload) {
 | 
			
		||||
      this.approverDrawer = payload
 | 
			
		||||
    },
 | 
			
		||||
    setApproverConfig(payload) {
 | 
			
		||||
      this.approverConfig1 = payload
 | 
			
		||||
    },
 | 
			
		||||
    setCopyer(payload) {
 | 
			
		||||
      this.copyerDrawer = payload
 | 
			
		||||
    },
 | 
			
		||||
    setCopyerConfig(payload) {
 | 
			
		||||
      this.copyerConfig1 = payload
 | 
			
		||||
    },
 | 
			
		||||
    setCondition(payload) {
 | 
			
		||||
      this.conditionDrawer = payload
 | 
			
		||||
    },
 | 
			
		||||
    setConditionsConfig(payload) {
 | 
			
		||||
      this.conditionsConfig1 = payload
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const useWorkFlowStoreWithOut = () => {
 | 
			
		||||
  return useWorkFlowStore(store)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +132,8 @@ export const useTagsViewStore = defineStore('tagsView', {
 | 
			
		|||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
  persist: false
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const useTagsViewStoreWithOut = () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { store } from '../index'
 | 
			
		||||
import { store } from '@/store'
 | 
			
		||||
import { defineStore } from 'pinia'
 | 
			
		||||
import { getAccessToken, removeToken } from '@/utils/auth'
 | 
			
		||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { CACHE_KEY, useCache, deleteUserCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { getInfo, loginOut } from '@/api/login'
 | 
			
		||||
 | 
			
		||||
const { wsCache } = useCache()
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ interface UserVO {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
interface UserInfoVO {
 | 
			
		||||
  // USER 缓存
 | 
			
		||||
  permissions: string[]
 | 
			
		||||
  roles: string[]
 | 
			
		||||
  isSetUser: boolean
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +81,7 @@ export const useUserStore = defineStore('admin-user', {
 | 
			
		|||
    async loginOut() {
 | 
			
		||||
      await loginOut()
 | 
			
		||||
      removeToken()
 | 
			
		||||
      wsCache.clear()
 | 
			
		||||
      deleteUserCache() // 删除用户缓存
 | 
			
		||||
      this.resetState()
 | 
			
		||||
    },
 | 
			
		||||
    resetState() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { useCache } from '@/hooks/web/useCache'
 | 
			
		||||
import { useCache, CACHE_KEY } from '@/hooks/web/useCache'
 | 
			
		||||
import { TokenType } from '@/api/login/types'
 | 
			
		||||
import { decrypt, encrypt } from '@/utils/jsencrypt'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,8 +36,6 @@ export const formatToken = (token: string): string => {
 | 
			
		|||
}
 | 
			
		||||
// ========== 账号相关 ==========
 | 
			
		||||
 | 
			
		||||
const LoginFormKey = 'LOGINFORM'
 | 
			
		||||
 | 
			
		||||
export type LoginFormType = {
 | 
			
		||||
  tenantName: string
 | 
			
		||||
  username: string
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +44,7 @@ export type LoginFormType = {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export const getLoginForm = () => {
 | 
			
		||||
  const loginForm: LoginFormType = wsCache.get(LoginFormKey)
 | 
			
		||||
  const loginForm: LoginFormType = wsCache.get(CACHE_KEY.LoginForm)
 | 
			
		||||
  if (loginForm) {
 | 
			
		||||
    loginForm.password = decrypt(loginForm.password) as string
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -55,38 +53,19 @@ export const getLoginForm = () => {
 | 
			
		|||
 | 
			
		||||
export const setLoginForm = (loginForm: LoginFormType) => {
 | 
			
		||||
  loginForm.password = encrypt(loginForm.password) as string
 | 
			
		||||
  wsCache.set(LoginFormKey, loginForm, { exp: 30 * 24 * 60 * 60 })
 | 
			
		||||
  wsCache.set(CACHE_KEY.LoginForm, loginForm, { exp: 30 * 24 * 60 * 60 })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeLoginForm = () => {
 | 
			
		||||
  wsCache.delete(LoginFormKey)
 | 
			
		||||
  wsCache.delete(CACHE_KEY.LoginForm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ========== 租户相关 ==========
 | 
			
		||||
 | 
			
		||||
const TenantIdKey = 'TENANT_ID'
 | 
			
		||||
const TenantNameKey = 'TENANT_NAME'
 | 
			
		||||
 | 
			
		||||
export const getTenantName = () => {
 | 
			
		||||
  return wsCache.get(TenantNameKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setTenantName = (username: string) => {
 | 
			
		||||
  wsCache.set(TenantNameKey, username, { exp: 30 * 24 * 60 * 60 })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeTenantName = () => {
 | 
			
		||||
  wsCache.delete(TenantNameKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getTenantId = () => {
 | 
			
		||||
  return wsCache.get(TenantIdKey)
 | 
			
		||||
  return wsCache.get(CACHE_KEY.TenantId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const setTenantId = (username: string) => {
 | 
			
		||||
  wsCache.set(TenantIdKey, username)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const removeTenantId = () => {
 | 
			
		||||
  wsCache.delete(TenantIdKey)
 | 
			
		||||
  wsCache.set(CACHE_KEY.TenantId, username)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -248,15 +248,15 @@ export const CouponTemplateTakeTypeEnum = {
 | 
			
		|||
 */
 | 
			
		||||
export const PromotionProductScopeEnum = {
 | 
			
		||||
  ALL: {
 | 
			
		||||
    scope: 10,
 | 
			
		||||
    scope: 1,
 | 
			
		||||
    name: '通用劵'
 | 
			
		||||
  },
 | 
			
		||||
  SPU: {
 | 
			
		||||
    scope: 20,
 | 
			
		||||
    scope: 2,
 | 
			
		||||
    name: '商品劵'
 | 
			
		||||
  },
 | 
			
		||||
  CATEGORY: {
 | 
			
		||||
    scope: 30,
 | 
			
		||||
    scope: 3,
 | 
			
		||||
    name: '品类劵'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Independent time operation tool to facilitate subsequent switch to dayjs
 | 
			
		||||
 */
 | 
			
		||||
// TODO 芋艿:【锁屏】可能后面删除掉
 | 
			
		||||
import dayjs from 'dayjs'
 | 
			
		||||
 | 
			
		||||
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
 | 
			
		||||
const DATE_FORMAT = 'YYYY-MM-DD'
 | 
			
		||||
 | 
			
		||||
export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string {
 | 
			
		||||
  return dayjs(date).format(format)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string {
 | 
			
		||||
  return dayjs(date).format(format)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const dateUtil = dayjs
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/**
 | 
			
		||||
 * 数据字典工具类
 | 
			
		||||
 */
 | 
			
		||||
import { useDictStoreWithOut } from '@/store/modules/dict'
 | 
			
		||||
import { ElementPlusInfoType } from '@/types/elementPlus'
 | 
			
		||||
import {useDictStoreWithOut} from '@/store/modules/dict'
 | 
			
		||||
import {ElementPlusInfoType} from '@/types/elementPlus'
 | 
			
		||||
 | 
			
		||||
const dictStore = useDictStoreWithOut()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +104,7 @@ export enum DICT_TYPE {
 | 
			
		|||
  USER_TYPE = 'user_type',
 | 
			
		||||
  COMMON_STATUS = 'common_status',
 | 
			
		||||
  TERMINAL = 'terminal', // 终端
 | 
			
		||||
  DATE_INTERVAL = 'date_interval', // 数据间隔
 | 
			
		||||
 | 
			
		||||
  // ========== SYSTEM 模块 ==========
 | 
			
		||||
  SYSTEM_USER_SEX = 'system_user_sex',
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +112,6 @@ export enum DICT_TYPE {
 | 
			
		|||
  SYSTEM_ROLE_TYPE = 'system_role_type',
 | 
			
		||||
  SYSTEM_DATA_SCOPE = 'system_data_scope',
 | 
			
		||||
  SYSTEM_NOTICE_TYPE = 'system_notice_type',
 | 
			
		||||
  SYSTEM_OPERATE_TYPE = 'system_operate_type',
 | 
			
		||||
  SYSTEM_LOGIN_TYPE = 'system_login_type',
 | 
			
		||||
  SYSTEM_LOGIN_RESULT = 'system_login_result',
 | 
			
		||||
  SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code',
 | 
			
		||||
| 
						 | 
				
			
			@ -134,15 +134,16 @@ export enum DICT_TYPE {
 | 
			
		|||
  INFRA_CODEGEN_FRONT_TYPE = 'infra_codegen_front_type',
 | 
			
		||||
  INFRA_CODEGEN_SCENE = 'infra_codegen_scene',
 | 
			
		||||
  INFRA_FILE_STORAGE = 'infra_file_storage',
 | 
			
		||||
  INFRA_OPERATE_TYPE = 'infra_operate_type',
 | 
			
		||||
 | 
			
		||||
  // ========== BPM 模块 ==========
 | 
			
		||||
  BPM_MODEL_CATEGORY = 'bpm_model_category',
 | 
			
		||||
  BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
 | 
			
		||||
  BPM_TASK_ASSIGN_RULE_TYPE = 'bpm_task_assign_rule_type',
 | 
			
		||||
  BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
 | 
			
		||||
  BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
 | 
			
		||||
  BPM_PROCESS_INSTANCE_RESULT = 'bpm_process_instance_result',
 | 
			
		||||
  BPM_TASK_ASSIGN_SCRIPT = 'bpm_task_assign_script',
 | 
			
		||||
  BPM_TASK_STATUS = 'bpm_task_status',
 | 
			
		||||
  BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type',
 | 
			
		||||
  BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_type',
 | 
			
		||||
  BPM_PROCESS_LISTENER_VALUE_TYPE = 'bpm_process_listener_value_type',
 | 
			
		||||
 | 
			
		||||
  // ========== PAY 模块 ==========
 | 
			
		||||
  PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +158,7 @@ export enum DICT_TYPE {
 | 
			
		|||
  MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
 | 
			
		||||
  MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型
 | 
			
		||||
 | 
			
		||||
  // ========== MALL - 会员模块 ==========
 | 
			
		||||
  // ========== Member 会员模块 ==========
 | 
			
		||||
  MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型
 | 
			
		||||
  MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ export const decodeFields = (fields: string[]) => {
 | 
			
		|||
  return rule
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置表单的 Conf 和 Fields
 | 
			
		||||
// 设置表单的 Conf 和 Fields,适用 FcDesigner 场景
 | 
			
		||||
export const setConfAndFields = (designerRef: object, conf: string, fields: string) => {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  designerRef.value.setOption(JSON.parse(conf))
 | 
			
		||||
| 
						 | 
				
			
			@ -36,19 +36,22 @@ export const setConfAndFields = (designerRef: object, conf: string, fields: stri
 | 
			
		|||
  designerRef.value.setRule(decodeFields(fields))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 设置表单的 Conf 和 Fields
 | 
			
		||||
// 设置表单的 Conf 和 Fields,适用 form-create 场景
 | 
			
		||||
export const setConfAndFields2 = (
 | 
			
		||||
  detailPreview: object,
 | 
			
		||||
  conf: string,
 | 
			
		||||
  fields: string,
 | 
			
		||||
  fields: string[],
 | 
			
		||||
  value?: object
 | 
			
		||||
) => {
 | 
			
		||||
  if (isRef(detailPreview)) {
 | 
			
		||||
    detailPreview = detailPreview.value
 | 
			
		||||
  }
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  detailPreview.value.option = JSON.parse(conf)
 | 
			
		||||
  detailPreview.option = JSON.parse(conf)
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  detailPreview.value.rule = decodeFields(fields)
 | 
			
		||||
  detailPreview.rule = decodeFields(fields)
 | 
			
		||||
  if (value) {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    detailPreview.value.value = value
 | 
			
		||||
    detailPreview.value = value
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,18 +175,18 @@ export function formatPast2(ms: number): string {
 | 
			
		|||
  const minute = Math.floor(ms / (60 * 1000) - day * 24 * 60 - hour * 60)
 | 
			
		||||
  const second = Math.floor(ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60)
 | 
			
		||||
  if (day > 0) {
 | 
			
		||||
    return day + '天' + hour + '小时' + minute + '分钟'
 | 
			
		||||
    return day + ' 天' + hour + ' 小时 ' + minute + ' 分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (hour > 0) {
 | 
			
		||||
    return hour + '小时' + minute + '分钟'
 | 
			
		||||
    return hour + ' 小时 ' + minute + ' 分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (minute > 0) {
 | 
			
		||||
    return minute + '分钟'
 | 
			
		||||
    return minute + ' 分钟'
 | 
			
		||||
  }
 | 
			
		||||
  if (second > 0) {
 | 
			
		||||
    return second + '秒'
 | 
			
		||||
    return second + ' 秒'
 | 
			
		||||
  } else {
 | 
			
		||||
    return 0 + '秒'
 | 
			
		||||
    return 0 + ' 秒'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -329,10 +329,11 @@ const ERP_PRICE_DIGIT = 2
 | 
			
		|||
 * 例如说:库存数量
 | 
			
		||||
 *
 | 
			
		||||
 * @param num 数量
 | 
			
		||||
 * @package digit 保留的小数位数
 | 
			
		||||
 * @return 格式化后的数量
 | 
			
		||||
 */
 | 
			
		||||
export const erpNumberFormatter = (num: number | string | undefined, digit: number) => {
 | 
			
		||||
  if (num === null) {
 | 
			
		||||
  if (num == null) {
 | 
			
		||||
    return ''
 | 
			
		||||
  }
 | 
			
		||||
  if (typeof num === 'string') {
 | 
			
		||||
| 
						 | 
				
			
			@ -404,3 +405,16 @@ export const erpPriceMultiply = (price: number, count: number) => {
 | 
			
		|||
  }
 | 
			
		||||
  return parseFloat((price * count).toFixed(ERP_PRICE_DIGIT))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 【ERP】百分比计算,四舍五入保留两位小数
 | 
			
		||||
 *
 | 
			
		||||
 * 如果 total 为 0,则返回 0
 | 
			
		||||
 *
 | 
			
		||||
 * @param value 当前值
 | 
			
		||||
 * @param total 总值
 | 
			
		||||
 */
 | 
			
		||||
export const erpCalculatePercentage = (value: number, total: number) => {
 | 
			
		||||
  if (total === 0) return 0
 | 
			
		||||
  return ((value / total) * 100).toFixed(2)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -188,7 +188,7 @@ const loginData = reactive({
 | 
			
		|||
    username: 'admin',
 | 
			
		||||
    password: 'admin123',
 | 
			
		||||
    captchaVerification: '',
 | 
			
		||||
    rememberMe: false
 | 
			
		||||
    rememberMe: true // 默认记录我。如果不需要,可手动修改
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -218,14 +218,14 @@ const getTenantId = async () => {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
// 记住我
 | 
			
		||||
const getCookie = () => {
 | 
			
		||||
const getLoginFormCache = () => {
 | 
			
		||||
  const loginForm = authUtil.getLoginForm()
 | 
			
		||||
  if (loginForm) {
 | 
			
		||||
    loginData.loginForm = {
 | 
			
		||||
      ...loginData.loginForm,
 | 
			
		||||
      username: loginForm.username ? loginForm.username : loginData.loginForm.username,
 | 
			
		||||
      password: loginForm.password ? loginForm.password : loginData.loginForm.password,
 | 
			
		||||
      rememberMe: loginForm.rememberMe ? true : false,
 | 
			
		||||
      rememberMe: loginForm.rememberMe,
 | 
			
		||||
      tenantName: loginForm.tenantName ? loginForm.tenantName : loginData.loginForm.tenantName
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -326,7 +326,7 @@ watch(
 | 
			
		|||
  }
 | 
			
		||||
)
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  getCookie()
 | 
			
		||||
  getLoginFormCache()
 | 
			
		||||
  getTenantByWebsite()
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <Dialog :title="dialogTitle" v-model="dialogVisible">
 | 
			
		||||
    <el-form
 | 
			
		||||
      ref="formRef"
 | 
			
		||||
      :model="formData"
 | 
			
		||||
      :rules="formRules"
 | 
			
		||||
      label-width="100px"
 | 
			
		||||
      v-loading="formLoading"
 | 
			
		||||
    >
 | 
			
		||||
      <el-form-item label="分类名" prop="name">
 | 
			
		||||
        <el-input v-model="formData.name" placeholder="请输入分类名" />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="分类标志" prop="code">
 | 
			
		||||
        <el-input v-model="formData.code" placeholder="请输入分类标志" />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="分类状态" prop="status">
 | 
			
		||||
        <el-radio-group v-model="formData.status">
 | 
			
		||||
          <el-radio
 | 
			
		||||
            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
 | 
			
		||||
            :key="dict.value"
 | 
			
		||||
            :label="dict.value"
 | 
			
		||||
          >
 | 
			
		||||
            {{ dict.label }}
 | 
			
		||||
          </el-radio>
 | 
			
		||||
        </el-radio-group>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="分类排序" prop="sort">
 | 
			
		||||
        <el-input-number
 | 
			
		||||
          v-model="formData.sort"
 | 
			
		||||
          placeholder="请输入分类排序"
 | 
			
		||||
          class="!w-1/1"
 | 
			
		||||
          :precision="0"
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
    </el-form>
 | 
			
		||||
    <template #footer>
 | 
			
		||||
      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
 | 
			
		||||
      <el-button @click="dialogVisible = false">取 消</el-button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </Dialog>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
 | 
			
		||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
 | 
			
		||||
 | 
			
		||||
/** BPM 流程分类 表单 */
 | 
			
		||||
defineOptions({ name: 'CategoryForm' })
 | 
			
		||||
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
 | 
			
		||||
const dialogVisible = ref(false) // 弹窗的是否展示
 | 
			
		||||
const dialogTitle = ref('') // 弹窗的标题
 | 
			
		||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 | 
			
		||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
 | 
			
		||||
const formData = ref({
 | 
			
		||||
  id: undefined,
 | 
			
		||||
  name: undefined,
 | 
			
		||||
  code: undefined,
 | 
			
		||||
  status: undefined,
 | 
			
		||||
  sort: undefined
 | 
			
		||||
})
 | 
			
		||||
const formRules = reactive({
 | 
			
		||||
  name: [{ required: true, message: '分类名不能为空', trigger: 'blur' }],
 | 
			
		||||
  code: [{ required: true, message: '分类标志不能为空', trigger: 'blur' }],
 | 
			
		||||
  status: [{ required: true, message: '分类状态不能为空', trigger: 'blur' }],
 | 
			
		||||
  sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }]
 | 
			
		||||
})
 | 
			
		||||
const formRef = ref() // 表单 Ref
 | 
			
		||||
 | 
			
		||||
/** 打开弹窗 */
 | 
			
		||||
const open = async (type: string, id?: number) => {
 | 
			
		||||
  dialogVisible.value = true
 | 
			
		||||
  dialogTitle.value = t('action.' + type)
 | 
			
		||||
  formType.value = type
 | 
			
		||||
  resetForm()
 | 
			
		||||
  // 修改时,设置数据
 | 
			
		||||
  if (id) {
 | 
			
		||||
    formLoading.value = true
 | 
			
		||||
    try {
 | 
			
		||||
      formData.value = await CategoryApi.getCategory(id)
 | 
			
		||||
    } finally {
 | 
			
		||||
      formLoading.value = false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 | 
			
		||||
 | 
			
		||||
/** 提交表单 */
 | 
			
		||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 | 
			
		||||
const submitForm = async () => {
 | 
			
		||||
  // 校验表单
 | 
			
		||||
  await formRef.value.validate()
 | 
			
		||||
  // 提交请求
 | 
			
		||||
  formLoading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    const data = formData.value as unknown as CategoryVO
 | 
			
		||||
    if (formType.value === 'create') {
 | 
			
		||||
      await CategoryApi.createCategory(data)
 | 
			
		||||
      message.success(t('common.createSuccess'))
 | 
			
		||||
    } else {
 | 
			
		||||
      await CategoryApi.updateCategory(data)
 | 
			
		||||
      message.success(t('common.updateSuccess'))
 | 
			
		||||
    }
 | 
			
		||||
    dialogVisible.value = false
 | 
			
		||||
    // 发送操作成功的事件
 | 
			
		||||
    emit('success')
 | 
			
		||||
  } finally {
 | 
			
		||||
    formLoading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 重置表单 */
 | 
			
		||||
const resetForm = () => {
 | 
			
		||||
  formData.value = {
 | 
			
		||||
    id: undefined,
 | 
			
		||||
    name: undefined,
 | 
			
		||||
    code: undefined,
 | 
			
		||||
    status: undefined,
 | 
			
		||||
    sort: undefined
 | 
			
		||||
  }
 | 
			
		||||
  formRef.value?.resetFields()
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,6 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
 | 
			
		||||
 | 
			
		||||
  <ContentWrap>
 | 
			
		||||
    <!-- 搜索工作栏 -->
 | 
			
		||||
    <el-form
 | 
			
		||||
| 
						 | 
				
			
			@ -8,19 +10,28 @@
 | 
			
		|||
      :inline="true"
 | 
			
		||||
      label-width="68px"
 | 
			
		||||
    >
 | 
			
		||||
      <el-form-item label="文件名称" prop="name">
 | 
			
		||||
      <el-form-item label="分类名" prop="name">
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="queryParams.name"
 | 
			
		||||
          placeholder="请输入文件名称"
 | 
			
		||||
          placeholder="请输入分类名"
 | 
			
		||||
          clearable
 | 
			
		||||
          @keyup.enter="handleQuery"
 | 
			
		||||
          class="!w-240px"
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="状态" prop="status">
 | 
			
		||||
      <el-form-item label="分类标志" prop="code">
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="queryParams.code"
 | 
			
		||||
          placeholder="请输入分类标志"
 | 
			
		||||
          clearable
 | 
			
		||||
          @keyup.enter="handleQuery"
 | 
			
		||||
          class="!w-240px"
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="分类状态" prop="status">
 | 
			
		||||
        <el-select
 | 
			
		||||
          v-model="queryParams.status"
 | 
			
		||||
          placeholder="请选择状态"
 | 
			
		||||
          placeholder="请选择分类状态"
 | 
			
		||||
          clearable
 | 
			
		||||
          class="!w-240px"
 | 
			
		||||
        >
 | 
			
		||||
| 
						 | 
				
			
			@ -32,15 +43,6 @@
 | 
			
		|||
          />
 | 
			
		||||
        </el-select>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="备注" prop="remark">
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="queryParams.remark"
 | 
			
		||||
          placeholder="请输入备注"
 | 
			
		||||
          clearable
 | 
			
		||||
          @keyup.enter="handleQuery"
 | 
			
		||||
          class="!w-240px"
 | 
			
		||||
        />
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
      <el-form-item label="创建时间" prop="createTime">
 | 
			
		||||
        <el-date-picker
 | 
			
		||||
          v-model="queryParams.createTime"
 | 
			
		||||
| 
						 | 
				
			
			@ -59,19 +61,10 @@
 | 
			
		|||
          type="primary"
 | 
			
		||||
          plain
 | 
			
		||||
          @click="openForm('create')"
 | 
			
		||||
          v-hasPermi="['report:ureport-data:create']"
 | 
			
		||||
          v-hasPermi="['bpm:category:create']"
 | 
			
		||||
        >
 | 
			
		||||
          <Icon icon="ep:plus" class="mr-5px" /> 新增
 | 
			
		||||
        </el-button>
 | 
			
		||||
        <el-button
 | 
			
		||||
          type="success"
 | 
			
		||||
          plain
 | 
			
		||||
          @click="handleExport"
 | 
			
		||||
          :loading="exportLoading"
 | 
			
		||||
          v-hasPermi="['report:ureport-data:export']"
 | 
			
		||||
        >
 | 
			
		||||
          <Icon icon="ep:download" class="mr-5px" /> 导出
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </el-form-item>
 | 
			
		||||
    </el-form>
 | 
			
		||||
  </ContentWrap>
 | 
			
		||||
| 
						 | 
				
			
			@ -79,15 +72,16 @@
 | 
			
		|||
  <!-- 列表 -->
 | 
			
		||||
  <ContentWrap>
 | 
			
		||||
    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
 | 
			
		||||
      <el-table-column label="ID" align="center" prop="id" />
 | 
			
		||||
      <el-table-column label="文件名称" align="center" prop="name" />
 | 
			
		||||
      <el-table-column label="状态" align="center" prop="status">
 | 
			
		||||
      <el-table-column label="分类编号" align="center" prop="id" />
 | 
			
		||||
      <el-table-column label="分类名" align="center" prop="name" />
 | 
			
		||||
      <el-table-column label="分类标志" align="center" prop="code" />
 | 
			
		||||
      <el-table-column label="分类描述" align="center" prop="description" />
 | 
			
		||||
      <el-table-column label="分类状态" align="center" prop="status">
 | 
			
		||||
        <template #default="scope">
 | 
			
		||||
          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
      <el-table-column label="文件内容" align="center" prop="content" />
 | 
			
		||||
      <el-table-column label="备注" align="center" prop="remark" />
 | 
			
		||||
      <el-table-column label="分类排序" align="center" prop="sort" />
 | 
			
		||||
      <el-table-column
 | 
			
		||||
        label="创建时间"
 | 
			
		||||
        align="center"
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +95,7 @@
 | 
			
		|||
            link
 | 
			
		||||
            type="primary"
 | 
			
		||||
            @click="openForm('update', scope.row.id)"
 | 
			
		||||
            v-hasPermi="['report:ureport-data:update']"
 | 
			
		||||
            v-hasPermi="['bpm:category:update']"
 | 
			
		||||
          >
 | 
			
		||||
            编辑
 | 
			
		||||
          </el-button>
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +103,7 @@
 | 
			
		|||
            link
 | 
			
		||||
            type="danger"
 | 
			
		||||
            @click="handleDelete(scope.row.id)"
 | 
			
		||||
            v-hasPermi="['report:ureport-data:delete']"
 | 
			
		||||
            v-hasPermi="['bpm:category:delete']"
 | 
			
		||||
          >
 | 
			
		||||
            删除
 | 
			
		||||
          </el-button>
 | 
			
		||||
| 
						 | 
				
			
			@ -126,31 +120,32 @@
 | 
			
		|||
  </ContentWrap>
 | 
			
		||||
 | 
			
		||||
  <!-- 表单弹窗:添加/修改 -->
 | 
			
		||||
  <UReportDataForm ref="formRef" @success="getList" />
 | 
			
		||||
  <CategoryForm ref="formRef" @success="getList" />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
 | 
			
		||||
import { dateFormatter } from '@/utils/formatTime'
 | 
			
		||||
import download from '@/utils/download'
 | 
			
		||||
import * as UReportDataApi from '@/api/report/ureport'
 | 
			
		||||
import UReportDataForm from './UReportDataForm.vue'
 | 
			
		||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
 | 
			
		||||
import CategoryForm from './CategoryForm.vue'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'UReportData' })
 | 
			
		||||
/** BPM 流程分类 列表 */
 | 
			
		||||
defineOptions({ name: 'BpmCategory' })
 | 
			
		||||
 | 
			
		||||
const message = useMessage() // 消息弹窗
 | 
			
		||||
const { t } = useI18n() // 国际化
 | 
			
		||||
 | 
			
		||||
const loading = ref(true) // 列表的加载中
 | 
			
		||||
const list = ref([]) // 列表的数据
 | 
			
		||||
const list = ref<CategoryVO[]>([]) // 列表的数据
 | 
			
		||||
const total = ref(0) // 列表的总页数
 | 
			
		||||
const queryParams = reactive({
 | 
			
		||||
  pageNo: 1,
 | 
			
		||||
  pageSize: 10,
 | 
			
		||||
  name: null,
 | 
			
		||||
  status: null,
 | 
			
		||||
  remark: null,
 | 
			
		||||
  createTime: [],
 | 
			
		||||
  name: undefined,
 | 
			
		||||
  code: undefined,
 | 
			
		||||
  status: undefined,
 | 
			
		||||
  createTime: []
 | 
			
		||||
})
 | 
			
		||||
const queryFormRef = ref() // 搜索的表单
 | 
			
		||||
const exportLoading = ref(false) // 导出的加载中
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +154,7 @@ const exportLoading = ref(false) // 导出的加载中
 | 
			
		|||
const getList = async () => {
 | 
			
		||||
  loading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    const data = await UReportDataApi.getUReportDataPage(queryParams)
 | 
			
		||||
    const data = await CategoryApi.getCategoryPage(queryParams)
 | 
			
		||||
    list.value = data.list
 | 
			
		||||
    total.value = data.total
 | 
			
		||||
  } finally {
 | 
			
		||||
| 
						 | 
				
			
			@ -191,28 +186,13 @@ const handleDelete = async (id: number) => {
 | 
			
		|||
    // 删除的二次确认
 | 
			
		||||
    await message.delConfirm()
 | 
			
		||||
    // 发起删除
 | 
			
		||||
    await UReportDataApi.deleteUReportData(id)
 | 
			
		||||
    await CategoryApi.deleteCategory(id)
 | 
			
		||||
    message.success(t('common.delSuccess'))
 | 
			
		||||
    // 刷新列表
 | 
			
		||||
    await getList()
 | 
			
		||||
  } catch {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 导出按钮操作 */
 | 
			
		||||
const handleExport = async () => {
 | 
			
		||||
  try {
 | 
			
		||||
    // 导出的二次确认
 | 
			
		||||
    await message.exportConfirm()
 | 
			
		||||
    // 发起导出
 | 
			
		||||
    exportLoading.value = true
 | 
			
		||||
    const data = await UReportDataApi.exportUReportData(queryParams)
 | 
			
		||||
    download.excel(data, 'Ureport2报表.xls')
 | 
			
		||||
  } catch {
 | 
			
		||||
  } finally {
 | 
			
		||||
    exportLoading.value = false
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 初始化 **/
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  getList()
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue