فهرست منبع

feat: 增加Excel导入、用户选择组件及工作流相关功能

- 新增loadExcel组件,支持Excel文件上传和模板下载
- 新增selectUser组件,支持部门树形结构和用户多选功能
- 新增gfast.ts工具类,提供通用方法和数据处理功能
- 新增zipdownload.ts工具类,支持文件下载和导出功能
- 优化工作流相关组件的属性设置和流程检查功能
- 添加表单创建、逻辑流程等相关依赖包
kagg886 3 ماه پیش
والد
کامیت
22970b9ca8

+ 2 - 0
package.json

@@ -26,6 +26,7 @@
     "@antv/g2plot": "2.4.20",
     "@element-plus/icons-vue": "2.0.9",
     "@form-create/component-wangeditor": "^2.6.2",
+    "@form-create/designer": "^1.1.9",
     "@form-create/element-ui": "^3",
     "@guolao/vue-monaco-editor": "^1.5.5",
     "@logicflow/core": "^2.0.16",
@@ -69,6 +70,7 @@
     "vue-data-ui": "^2.4.17",
     "vue-grid-layout": "3.0.0-beta1",
     "vue-i18n": "9.1.10",
+    "vue-json-pretty": "^2.5.0",
     "vue-router": "4.0.13",
     "vue3-clipboard": "1.0.0",
     "vue3-cron": "1.1.8",

+ 1 - 1
src/components/gFlow/checkFlow.vue

@@ -110,7 +110,7 @@ import {FlowDemoTableColumns} from "/@/views/flow/flowDemo/list/component/model"
 import {ElMessage, FormInstance} from "element-plus";
 import {getCheckData, saveCheckWf, saveStartWf, wfStart} from "/@/api/flow/flowModel";
 import ShowFlowDesign from "/@/components/gFlow/showDesign.vue"
-import UploadFile from "/@/components/uploadFile/index.vue";
+import UploadFile from "/@/components/upload/uploadFile.vue";
 import FlowLog from "/@/components/gFlow/flowLog.vue";
 const props = defineProps({
   saveCheckWfAttr:{

+ 265 - 281
src/components/gFlow/propertySetting/CommonProperty.vue

@@ -1,308 +1,292 @@
 <template>
-  <div>
-    <el-form label-width="130px" :model="formData">
-      <el-form-item label="节点名称">
-        <el-input v-model="formData.text" :disabled="readonly"></el-input>
-      </el-form-item>
-      <el-form-item label="审核方式" v-if="nodeType===NodeTypeStart">
-        <el-select v-model="formData.actionRule" placeholder="请选择审核方式" clearable :disabled="readonly">
-          <el-option v-for="item in actionRuleOption"
-              :key="item.value"
-              :label="item.label"
-              :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="接收方式" v-else>
-        <el-select v-model="formData.actionRule" placeholder="请选择接收方式" clearable :disabled="readonly">
-          <el-option v-for="item in actionRuleOption"
-                     :key="item.value"
-                     :label="item.label"
-                     :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-			<el-form-item label="指定上级部门" v-show="formData.actionRule===FlowCheckRuleFormCreatorDeptPrev || formData.actionRule===FlowCheckRuleFormCreatorDeptLeaderPrev">
-				<el-input-number
-					v-model="formData.deptProv"
-					style="width: 200px"
-					placeholder="Please input"
-				>
+	<div>
+		<el-form label-width="130px" :model="formData">
+			<el-form-item label="节点名称">
+				<el-input v-model="formData.text" :disabled="readonly"></el-input>
+			</el-form-item>
+			<el-form-item label="审核方式" v-if="nodeType === NodeTypeStart">
+				<el-select v-model="formData.actionRule" placeholder="请选择审核方式" clearable :disabled="readonly">
+					<el-option v-for="item in actionRuleOption" :key="item.value" :label="item.label" :value="item.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="接收方式" v-else>
+				<el-select v-model="formData.actionRule" placeholder="请选择接收方式" clearable :disabled="readonly">
+					<el-option v-for="item in actionRuleOption" :key="item.value" :label="item.label" :value="item.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item
+				label="指定上级部门"
+				v-show="formData.actionRule === FlowCheckRuleFormCreatorDeptPrev || formData.actionRule === FlowCheckRuleFormCreatorDeptLeaderPrev"
+			>
+				<el-input-number v-model="formData.deptProv" style="width: 200px" placeholder="Please input">
 					<template #prefix><span>上</span></template>
 					<template #suffix><span>级部门</span></template>
 				</el-input-number>
 			</el-form-item>
-      <el-form-item label="指定角色" v-show="formData.actionRule===FlowCheckRuleRole">
-        <el-cascader
-            :options="roleList"
-            :props="{ checkStrictly: true,emitPath: false, value: 'id', label: 'name',multiple: true }"
-            placeholder="请选择角色"
-            clearable
-            class="w100"
-            v-model="formData.roleIds"
-            :disabled="readonly"
-        >
-          <template #default="{ node, data }">
-            <span>{{ data.name }}</span>
-            <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-          </template>
-        </el-cascader>
-      </el-form-item>
-      <el-form-item label="指定部门" prop="deptId" v-show="formData.actionRule===FlowCheckRuleDept ||formData.actionRule===FlowCheckRuleDeptLeader">
-        <el-cascader
-            :options="deptData"
-            :props="{ checkStrictly: true,emitPath: false, value: 'deptId', label: 'deptName',multiple: true }"
-            placeholder="请选择部门"
-            clearable
-            class="w100"
-            v-model="formData.deptIds"
-            :disabled="readonly"
-        >
-          <template #default="{ node, data }">
-            <span>{{ data.deptName }}</span>
-            <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
-          </template>
-        </el-cascader>
-      </el-form-item>
-      <el-form-item label="指定岗位" prop="postIds" v-show="formData.actionRule===FlowCheckRulePost">
-        <el-select v-model="formData.postIds" placeholder="请选择" clearable class="w100" multiple :disabled="readonly">
-          <el-option
-              v-for="post in postList"
-              :key="'post-'+post.postId"
-              :label="post.postName"
-              :value="post.postId">
-          </el-option>
-        </el-select>
-      </el-form-item>
-      <el-form-item label="指定人员"  v-show="formData.actionRule===FlowCheckRuleUser">
-        <select-user v-model="formData.userIds"></select-user>
-      </el-form-item>
-      <el-form-item label="多人审批处理规则"  >
-        <el-select v-model="formData.approveRule" placeholder="请选择多人审批处理规则" style="width: 100%" :disabled="readonly">
-          <el-option v-for="item in approveRuleOption"
-                     :key="item.value"
-                     :label="item.label"
-                     :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="网关审批处理规则" v-show="nodeType===NodeTypeConcurrent||nodeType===NodeTypePush||nodeType===NodeTypeCondition">
-        <el-select v-model="nodeType" placeholder="请选择多人审批处理规则" :disabled="true" style="width: 100%">
-          <el-option v-for="item in conditionRuleOption"
-                     :key="item.value"
-                     :label="item.label"
-                     :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="发送通知" >
-        <el-checkbox-group v-model="formData.notice" :disabled="readonly">
-          <el-checkbox value="sms" >短信</el-checkbox>
-          <el-checkbox value="email" >邮件</el-checkbox>
-          <el-checkbox value="innerNotice" >站内通知</el-checkbox>
-        </el-checkbox-group>
-      </el-form-item>
-      <el-form-item label="允许退回" v-show="nodeType!==NodeTypeStart">
-        <el-radio-group v-model="formData.rollback" :disabled="readonly">
-          <el-radio :value="1">是</el-radio>
-          <el-radio :value="0">否</el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="分支条件" v-show="nodeType===NodeTypeCondition">
-        <Branch ref="branchRef" :modelValue="formData.nodeExt" @setNodeExt="setNodeExt"></Branch>
-      </el-form-item>
-       <el-form-item>
-        <el-button type="primary" @click="onSubmit" v-if="!readonly">保存</el-button>
-      </el-form-item>
-    </el-form>
-  </div>
+			<el-form-item label="指定角色" v-show="formData.actionRule === FlowCheckRuleRole">
+				<el-cascader
+					:options="roleList"
+					:props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name', multiple: true }"
+					placeholder="请选择角色"
+					clearable
+					class="w100"
+					v-model="formData.roleIds"
+					:disabled="readonly"
+				>
+					<template #default="{ node, data }">
+						<span>{{ data.name }}</span>
+						<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+					</template>
+				</el-cascader>
+			</el-form-item>
+			<el-form-item
+				label="指定部门"
+				prop="deptId"
+				v-show="formData.actionRule === FlowCheckRuleDept || formData.actionRule === FlowCheckRuleDeptLeader"
+			>
+				<el-cascader
+					:options="deptData"
+					:props="{ checkStrictly: true, emitPath: false, value: 'deptId', label: 'deptName', multiple: true }"
+					placeholder="请选择部门"
+					clearable
+					class="w100"
+					v-model="formData.deptIds"
+					:disabled="readonly"
+				>
+					<template #default="{ node, data }">
+						<span>{{ data.deptName }}</span>
+						<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+					</template>
+				</el-cascader>
+			</el-form-item>
+			<el-form-item label="指定岗位" prop="postIds" v-show="formData.actionRule === FlowCheckRulePost">
+				<el-select v-model="formData.postIds" placeholder="请选择" clearable class="w100" multiple :disabled="readonly">
+					<el-option v-for="post in postList" :key="'post-' + post.postId" :label="post.postName" :value="post.postId"> </el-option>
+				</el-select>
+			</el-form-item>
+			<el-form-item label="指定人员" v-show="formData.actionRule === FlowCheckRuleUser">
+				<select-user v-model="formData.userIds"></select-user>
+			</el-form-item>
+			<el-form-item label="多人审批处理规则">
+				<el-select v-model="formData.approveRule" placeholder="请选择多人审批处理规则" style="width: 100%" :disabled="readonly">
+					<el-option v-for="item in approveRuleOption" :key="item.value" :label="item.label" :value="item.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="网关审批处理规则" v-show="nodeType === NodeTypeConcurrent || nodeType === NodeTypePush || nodeType === NodeTypeCondition">
+				<el-select v-model="nodeType" placeholder="请选择多人审批处理规则" :disabled="true" style="width: 100%">
+					<el-option v-for="item in conditionRuleOption" :key="item.value" :label="item.label" :value="item.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="发送通知">
+				<el-checkbox-group v-model="formData.notice" :disabled="readonly">
+					<el-checkbox value="sms">短信</el-checkbox>
+					<el-checkbox value="email">邮件</el-checkbox>
+					<el-checkbox value="innerNotice">站内通知</el-checkbox>
+				</el-checkbox-group>
+			</el-form-item>
+			<el-form-item label="允许退回" v-show="nodeType !== NodeTypeStart">
+				<el-radio-group v-model="formData.rollback" :disabled="readonly">
+					<el-radio :value="1">是</el-radio>
+					<el-radio :value="0">否</el-radio>
+				</el-radio-group>
+			</el-form-item>
+			<el-form-item label="分支条件" v-show="nodeType === NodeTypeCondition">
+				<Branch ref="branchRef" :modelValue="formData.nodeExt" @setNodeExt="setNodeExt"></Branch>
+			</el-form-item>
+			<el-form-item>
+				<el-button type="primary" @click="onSubmit" v-if="!readonly">保存</el-button>
+			</el-form-item>
+		</el-form>
+	</div>
 </template>
 <script setup lang="ts">
-import {NodeConditionData} from "/@/components/gFlow/propertySetting/model";
+import { NodeConditionData } from '/@/components/gFlow/propertySetting/model'
 
-interface UserInfo  {
-  userNickname:string;
-  userId:number;
-}
-interface PostInfo{
-  postId:number;
-  postName:string;
+interface PostInfo {
+	postId: number
+	postName: string
 }
-import {getDeptTree, getParams, getUserByIds} from "/@/api/system/user";
-import selectUser from "/@/components/selectUser/index.vue";
-import LogicFlow from "@logicflow/core";
-import {computed, getCurrentInstance, onMounted, reactive, ref, toRefs} from "vue";
-import Branch from "/@/components/gFlow/propertySetting/branch.vue"
+
+const getDeptTree = systemApi.dept.getList({ status: -1 })
+const getParams = systemApi.post.getList({ status: -1 })
+
+import selectUser from '/@/components/selectUser/index.vue'
+import LogicFlow from '@logicflow/core'
+import { computed, getCurrentInstance, onMounted, reactive, ref, toRefs } from 'vue'
+import Branch from '/@/components/gFlow/propertySetting/branch.vue'
 import {
-  FlowCheckRuleAll,
-  FlowCheckRuleDept,
-  FlowCheckRuleDeptLeader,
-  FlowCheckRuleFormCreator,
-  FlowCheckRuleFormCreatorDept,
-  FlowCheckRuleFormCreatorDeptLeader,
+	FlowCheckRuleAll,
+	FlowCheckRuleDept,
+	FlowCheckRuleDeptLeader,
+	FlowCheckRuleFormCreator,
+	FlowCheckRuleFormCreatorDept,
+	FlowCheckRuleFormCreatorDeptLeader,
 	FlowCheckRuleFormCreatorDeptPrev,
 	FlowCheckRuleFormCreatorDeptLeaderPrev,
-  FlowCheckRulePost,
-  FlowCheckRuleRole,
-  FlowCheckRuleUser, NodeTypeConcurrent, NodeTypeCondition, NodeTypePush, NodeTypeStart
-} from "/@/components/gFlow/consts";
-defineOptions({ name: "CommonProperty"})
+	FlowCheckRulePost,
+	FlowCheckRuleRole,
+	FlowCheckRuleUser,
+	NodeTypeConcurrent,
+	NodeTypeCondition,
+	NodeTypePush,
+	NodeTypeStart,
+} from '/@/components/gFlow/consts'
+import systemApi from '/@/api/system'
+
+defineOptions({ name: 'CommonProperty' })
 const branchRef = ref()
-const { proxy } = <any>getCurrentInstance();
+const { proxy } = <any>getCurrentInstance()
 const props = defineProps({
-  nodeData: {
-    type: Object,
-    default: () => {
-      return null
-    }
-  },
-  lf:{
-    type:LogicFlow,
-    default: () => {
-      return {} as LogicFlow
-    }
-  },
-  readonly:{
-    type:Boolean,
-    default:false
-  }
+	nodeData: {
+		type: Object,
+		default: () => {
+			return null
+		},
+	},
+	lf: {
+		type: LogicFlow,
+		default: () => {
+			return {} as LogicFlow
+		},
+	},
+	readonly: {
+		type: Boolean,
+		default: false,
+	},
 })
-const nodeType = computed(()=>{
-  return props.nodeData.type.replace('Done','')
+const nodeType = computed(() => {
+	return props.nodeData.type.replace('Done', '')
 })
 const emit = defineEmits(['onClose'])
-const selectUserRef =ref()
 const state = reactive({
-  text: '',
-  roleList:[],//角色选项列表
-  postList:[] as PostInfo[],//岗位选项列表
-  deptData:[],//部门选项列表
-  actionRuleOption:[
-    {
-      label:'任何人',
-      value:FlowCheckRuleAll
-    },
-    {
-      label:'指定角色',
-      value:FlowCheckRuleRole
-    },
-    {
-      label:'指定部门',
-      value:FlowCheckRuleDept
-    },
-    {
-      label:'指定部门负责人',
-      value:FlowCheckRuleDeptLeader
-    },
-    {
-      label:'指定人员',
-      value:FlowCheckRuleUser
-    },
-    {
-      label:'指定岗位',
-      value:FlowCheckRulePost
-    },
-    {
-      label:'表单业务创建人',
-      value:FlowCheckRuleFormCreator
-    },
-    {
-      label:'表单业务创建人所属部门',
-      value:FlowCheckRuleFormCreatorDept
-    },
-    {
-      label:'表单业务创建人所属部门负责人',
-      value:FlowCheckRuleFormCreatorDeptLeader
-    },
+	text: '',
+	roleList: [], //角色选项列表
+	postList: [] as PostInfo[], //岗位选项列表
+	deptData: [], //部门选项列表
+	actionRuleOption: [
+		{
+			label: '任何人',
+			value: FlowCheckRuleAll,
+		},
 		{
-			label:'表单业务创建人所属上级部门',
-			value:FlowCheckRuleFormCreatorDeptPrev
+			label: '指定角色',
+			value: FlowCheckRuleRole,
 		},
 		{
-			label:'表单业务创建人所属上级部门负责人',
-			value:FlowCheckRuleFormCreatorDeptLeaderPrev
-		}
-  ],
-  approveRuleOption:[
-    {
-      label:'抢办模式(任何人第一个处理则通过)',
-      value:1
-    },
-    {
-      label:'顺序执行(所有人按顺序执行完才通过)',
-      value:2
-    },
-    {
-      label:'并行执行(所有人执行完才通过)',
-      value:3
-    },
-  ],
-  //分支条件审核规则
-  conditionRuleOption:[
-    {label:"并行网关-所有分支都必须完成审核",value:NodeTypeConcurrent},
-    {label:"互斥网关-所有分支中只能有其中一条审核",value:NodeTypePush},
-    {label:"条件网关-符合条件的某条分或多条支进行审核",value:NodeTypeCondition},
-  ],
-  formData: {
-    text: '',
-    actionRule: undefined,
-    roleIds:[],
-    deptIds:[],
-    userIds:[],
-    postIds:[],
-    approveRule:'',
-    notice:[],
-    rollback:undefined,
-    nodeExt:[] as Array<NodeConditionData[]>,
-		deptProv:1
-  }
+			label: '指定部门',
+			value: FlowCheckRuleDept,
+		},
+		{
+			label: '指定部门负责人',
+			value: FlowCheckRuleDeptLeader,
+		},
+		{
+			label: '指定人员',
+			value: FlowCheckRuleUser,
+		},
+		{
+			label: '指定岗位',
+			value: FlowCheckRulePost,
+		},
+		{
+			label: '表单业务创建人',
+			value: FlowCheckRuleFormCreator,
+		},
+		{
+			label: '表单业务创建人所属部门',
+			value: FlowCheckRuleFormCreatorDept,
+		},
+		{
+			label: '表单业务创建人所属部门负责人',
+			value: FlowCheckRuleFormCreatorDeptLeader,
+		},
+		{
+			label: '表单业务创建人所属上级部门',
+			value: FlowCheckRuleFormCreatorDeptPrev,
+		},
+		{
+			label: '表单业务创建人所属上级部门负责人',
+			value: FlowCheckRuleFormCreatorDeptLeaderPrev,
+		},
+	],
+	approveRuleOption: [
+		{
+			label: '抢办模式(任何人第一个处理则通过)',
+			value: 1,
+		},
+		{
+			label: '顺序执行(所有人按顺序执行完才通过)',
+			value: 2,
+		},
+		{
+			label: '并行执行(所有人执行完才通过)',
+			value: 3,
+		},
+	],
+	//分支条件审核规则
+	conditionRuleOption: [
+		{ label: '并行网关-所有分支都必须完成审核', value: NodeTypeConcurrent },
+		{ label: '互斥网关-所有分支中只能有其中一条审核', value: NodeTypePush },
+		{ label: '条件网关-符合条件的某条分或多条支进行审核', value: NodeTypeCondition },
+	],
+	formData: {
+		text: '',
+		actionRule: undefined,
+		roleIds: [],
+		deptIds: [],
+		userIds: [],
+		postIds: [],
+		approveRule: '',
+		notice: [],
+		rollback: undefined,
+		nodeExt: [] as Array<NodeConditionData[]>,
+		deptProv: 1,
+	},
 })
-const { formData, deptData, roleList, postList, actionRuleOption, approveRuleOption, conditionRuleOption} = toRefs(state)
-onMounted(()=>{
-  const { properties, text } = props.nodeData
-  if (properties) {
-    state.formData = Object.assign({}, state.formData, properties)
-    //设置条件
-    branchRef.value.initNodeData(state.formData.nodeExt)
-  }
-  if (text && text.value) {
-    state.formData.text = text.value
-  }
-  if (text && text.value) {
-    state.text = text.value
-  }
-  getSelector()
+const { formData, deptData, roleList, postList, actionRuleOption, approveRuleOption, conditionRuleOption } = toRefs(state)
+onMounted(() => {
+	const { properties, text } = props.nodeData
+	if (properties) {
+		state.formData = Object.assign({}, state.formData, properties)
+		//设置条件
+		branchRef.value.initNodeData(state.formData.nodeExt)
+	}
+	if (text && text.value) {
+		state.formData.text = text.value
+	}
+	if (text && text.value) {
+		state.text = text.value
+	}
+	getSelector()
 })
-const getSelector = () =>{
-  //获取角色岗位选项
-  getParams().then((res)=>{
-    const roles = res.data.roleList??[];
-    const roleAccess = res.data.roleAccess??[];
-    roles.map((item:any)=>{
-      if(!roleAccess.includes(item.id)){
-        item.disabled = true
-      }
-    })
-    state.roleList = proxy.handleTree(roles??[], "id","pid","children",true);
-    state.postList = res.data.posts??[];
-  });
-  getDeptTree().then((res)=>{
-    state.deptData = res.data.deps
-  })
+const getSelector = () => {
+	//获取角色岗位选项
+	getParams().then((res) => {
+		const roles = res.data.roleList ?? []
+		const roleAccess = res.data.roleAccess ?? []
+		roles.map((item: any) => {
+			if (!roleAccess.includes(item.id)) {
+				item.disabled = true
+			}
+		})
+		state.roleList = proxy.handleTree(roles ?? [], 'id', 'pid', 'children', true)
+		state.postList = res.data.posts ?? []
+	})
+	getDeptTree().then((res) => {
+		state.deptData = res.data.deps
+	})
 }
-const onSubmit = () =>{
-  branchRef.value.setNodeExt()
-  const { id } = props.nodeData
-  props.lf.setProperties(id, {
-    ...state.formData
-  });
-  props.lf.updateText(id, state.formData.text);
-  emit('onClose')
+const onSubmit = () => {
+	branchRef.value.setNodeExt()
+	const { id } = props.nodeData
+	props.lf.setProperties(id, {
+		...state.formData,
+	})
+	props.lf.updateText(id, state.formData.text)
+	emit('onClose')
 }
-const setNodeExt = (data:Array<NodeConditionData[]>) =>{
-  state.formData.nodeExt = data
+const setNodeExt = (data: Array<NodeConditionData[]>) => {
+	state.formData.nodeExt = data
 }
 </script>
-<style scoped>
-
-</style>
+<style scoped></style>

+ 134 - 0
src/components/loadExcel/index.vue

@@ -0,0 +1,134 @@
+<template>
+  <div class="excel-uploader">
+    <!-- 添加或修改代码生成测试对话框 -->
+    <el-dialog v-model="upload.open" width="500px" :close-on-click-modal="false" :destroy-on-close="true">
+      <template #header>
+        <div v-drag="['.excel-uploader .el-dialog', '.excel-uploader .el-dialog__header']">{{upload.title}}</div>
+      </template>
+      <el-upload
+          ref="uploadRef"
+          :limit="1"
+          accept=".xlsx, .xls"
+          :headers="upload.headers"
+          :action="upload.url"
+          :disabled="upload.isUploading"
+          :on-progress="handleFileUploadProgress"
+          :on-success="handleFileSuccess"
+          :auto-upload="false"
+          drag
+          class="up-selector"
+      >
+        <el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
+        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        <template #tip>
+          <div class="text-center el-upload__tip">
+            <span>仅允许导入xls、xlsx格式文件。</span>
+            <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">确 定</el-button>
+          <el-button @click="upload.open = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="loadExcel" lang="ts">
+import {reactive, ref} from "vue";
+import {getToken} from "/@/utils/gfast";
+import {ElMessageBox, UploadFile} from "element-plus";
+import {downLoadXml} from "/@/utils/zipdownload";
+const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
+const uploadRef = ref();
+const emit = defineEmits(['getList'])
+const props = defineProps(['upUrl','tplUrl'])
+const globalHeaders = ():object => {
+  return {
+    Authorization:'Bearer '+getToken()
+  }
+}
+/*** 用户导入参数 */
+const upload = reactive({
+  // 是否显示弹出层(用户导入)
+  open: false,
+  // 弹出层标题(用户导入)
+  title: "excel导入",
+  // 是否禁用上传
+  isUploading: false,
+  // 设置上传的请求头部
+  headers: globalHeaders(),
+  // 上传的地址
+  url: baseURL + props.upUrl
+})
+  /**文件上传中处理 */
+  const handleFileUploadProgress = () => {
+    upload.isUploading = true;
+  }
+  /** 文件上传成功处理 */
+  const handleFileSuccess = (response: any, file: UploadFile) => {
+    upload.open = false;
+    upload.isUploading = false;
+    uploadRef.value?.handleRemove(file);
+    let message = '导入成功!'
+    if(response.code!==0){
+      message = response.message
+    }
+    ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + message + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
+    emit('getList')
+  }
+/** 下载模板操作 */
+const importTemplate = () => {
+  downLoadXml(props.tplUrl,{},'get')
+}
+
+/** 提交上传文件 */
+const submitFileForm = () => {
+  uploadRef.value?.submit();
+}
+
+const open = ()=>{
+  upload.open = true
+}
+defineExpose({ open })
+</script>
+
+<style scoped lang="scss">
+.excel-uploader :deep(.avatar-uploader .avatar) {
+  width: 178px;
+  height: 178px;
+  display: block;
+}
+.excel-uploader :deep(.avatar-uploader .el-upload) {
+  border: 1px dashed var(--el-border-color);
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+  transition: var(--el-transition-duration-fast);
+}
+.excel-uploader :deep(.avatar-uploader .el-upload:hover) {
+  border-color: var(--el-color-primary);
+}
+.excel-uploader :deep(.el-icon.avatar-uploader-icon) {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  text-align: center;
+}
+.kv-label{margin-bottom: 15px;font-size: 14px;}
+.mini-btn i.el-icon{margin: unset;}
+.kv-row{margin-bottom: 12px;}
+:deep(.up-selector .el-upload.is-drag){
+  margin: auto;
+  width: 210px!important;
+}
+
+.text-center{
+  text-align: center;
+}
+</style>

+ 121 - 0
src/components/selectUser/component/userList.vue

@@ -0,0 +1,121 @@
+<template>
+	<div class="system-user-list-container"  style="min-height: 600px;">
+    <el-table :data="tableData.data" style="width: 100%" @selection-change="handleSelectionChange">
+      <el-table-column v-if="multiple" type="selection" width="55" align="center" />
+      <el-table-column type="index" label="序号" width="60" />
+      <el-table-column prop="userNickname" label="姓名" show-overflow-tooltip></el-table-column>
+      <el-table-column label="操作" width="100" >
+        <template #default="scope">
+          <el-button size="small" text type="primary" @click="onOpenSelectUser(scope.row)">选择</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination
+        v-show="tableData.total>0"
+        :total="tableData.total"
+        v-model:page="tableData.param.pageNum"
+        v-model:limit="tableData.param.pageSize"
+        @pagination="setUserList"
+    />
+	</div>
+</template>
+
+<script lang="ts">
+import {toRefs, reactive, defineComponent, getCurrentInstance} from 'vue';
+import system from '/@/api/system';
+
+interface TableDataState {
+  ids:number[];
+  userItem:any[];
+  deptProps:{};
+	tableData: {
+		data: any[];
+		total: number;
+		loading: boolean;
+		param: TableParam;
+	};
+}
+interface TableParam {
+  pageNum: number;
+  pageSize: number;
+  deptId:string;
+  roleId:number | undefined;
+  mobile:string;
+  status:string;
+  keyWords:string;
+  dateRange: string[];
+}
+export default defineComponent({
+	name: 'systemUserList',
+  props:{
+    deptData:{
+      type:Array,
+      default:()=>[]
+    },
+    param:{
+      type:Object,
+      default:()=>{}
+    },
+    genderData:{
+      type:Array,
+      default:()=>[]
+    },
+    multiple:{
+      type:Boolean,
+      default:true
+    }
+  },
+  emits:['ok','okBatch'],
+	components: {  },
+	setup(prop,{emit}) {
+    const {proxy,props} = <any>getCurrentInstance();
+    const {sys_user_sex} = proxy.useDict('sys_user_sex')
+		const state = reactive<TableDataState>({
+      ids:[],
+      userItem:[],
+      deptProps:{
+        id:"deptId",
+        children: "children",
+        label: "deptName"
+      },
+			tableData: {
+				data: [],
+				total: 0,
+				loading: false,
+				param: {
+          pageNum: 1,
+          pageSize: 10,
+          roleId:undefined,
+          deptId:'',
+          mobile:'',
+          status:'',
+          keyWords:'',
+          dateRange:[]
+				},
+			},
+		});
+    const setUserList = ()=>{
+      const param = {...state.tableData.param, ...props.param};
+			//FIXME 没有对应的api映射,需要fix。
+      system.user.getList(param).then((res:any)=>{
+        state.tableData.data = res.data.userList??[];
+        state.tableData.total = res.data.total;
+      });
+    };
+    const onOpenSelectUser = (row:any) => {
+      emit("ok",row);
+    }
+    // 多选框选中数据
+    const handleSelectionChange = (selection:any[])=> {
+      emit("okBatch",selection)
+    };
+		return {
+      sys_user_sex,
+      setUserList,
+      onOpenSelectUser,
+      handleSelectionChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>

+ 316 - 0
src/components/selectUser/index.vue

@@ -0,0 +1,316 @@
+<template>
+	<div>
+		<div v-if="deptUser.length > 0">
+			<el-tag closable :disable-transitions="false" class="u-m-r-10" v-for="item in deptUser" :key="'sel_' + item.id" @close="handleClose(item.id)">{{
+				item.userNickname
+			}}</el-tag>
+		</div>
+		<el-button style="padding-left: 10px" type="primary" link @click="handleSelectUser">请选择</el-button>
+		<el-dialog title="选择用户" v-model="visible" width="80%" top="5vh" append-to-body :close-on-click-modal="false">
+			<div class="system-user-container">
+				<el-row :gutter="10" style="width: 100%">
+					<el-col :span="6">
+						<el-card shadow="hover">
+							<el-aside>
+								<el-scrollbar>
+									<el-input :prefix-icon="search" v-model="filterText" placeholder="请输入部门名称" clearable style="width: 80%" />
+									<el-tree
+										ref="treeRef"
+										class="filter-tree"
+										:data="deptData"
+										:props="deptProps"
+										default-expand-all
+										:filter-node-method="deptFilterNode"
+										@node-click="handleNodeClick"
+									/>
+								</el-scrollbar>
+							</el-aside>
+						</el-card>
+					</el-col>
+					<el-col :span="12">
+						<el-card shadow="hover">
+							<div class="system-user-search mb15">
+								<el-form :model="param" ref="queryRef" :inline="true" label-width="68px">
+									<el-form-item label="关键字" prop="keyWords">
+										<el-input v-model="param.keyWords" placeholder="请输入姓名" clearable style="width: 240px" @keyup.enter.native="getUserList" />
+									</el-form-item>
+									<el-form-item label="创建时间" prop="dateRange">
+										<el-date-picker
+											v-model="param.dateRange"
+											style="width: 240px"
+											value-format="YYYY-MM-DD"
+											type="daterange"
+											range-separator="-"
+											start-placeholder="开始日期"
+											end-placeholder="结束日期"
+										></el-date-picker>
+									</el-form-item>
+									<el-form-item>
+										<el-button size="default" type="primary" class="ml10" @click="getUserList">
+											<el-icon>
+												<ele-Search />
+											</el-icon>
+											查询
+										</el-button>
+										<el-button size="default" @click="resetQuery(queryRef)">
+											<el-icon>
+												<ele-Refresh />
+											</el-icon>
+											重置
+										</el-button>
+									</el-form-item>
+								</el-form>
+							</div>
+							<UserList
+								ref="userListRef"
+								:dept-data="deptData"
+								:gender-data="sys_user_sex"
+								:param="param"
+								:multiple="multiple"
+								@ok="handleSelectUserOk"
+								@okBatch="handleSelectUserOkBatch"
+							/>
+						</el-card>
+					</el-col>
+					<el-col :span="6">
+						<el-card shadow="hover">
+							<el-row :gutter="10">
+								<el-col :span="10">
+									<p style="height: 32px; line-height: 32px">已选择:{{ selectedUsers.length }}</p>
+								</el-col>
+								<el-col :span="7">
+									<el-button type="success" plain @click="goBack">确认返回</el-button>
+								</el-col>
+								<el-col :span="7">
+									<el-button type="danger" plain @click="removeAll">全部移除</el-button>
+								</el-col>
+								<el-col :span="24">
+									<el-table :data="selectedUserInfo">
+										<el-table-column label="操作">
+											<template #default="scope">
+												<el-button type="danger" plain @click="remove(scope.row.id)">移除</el-button>
+											</template>
+										</el-table-column>
+										<el-table-column prop="userNickname" label="姓名" />
+									</el-table>
+								</el-col>
+							</el-row>
+							<el-pagination layout="prev, pager, next" :total="selectedUsers.length" v-model:current-page="selectedUsersPage" />
+						</el-card>
+					</el-col>
+				</el-row>
+			</div>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import {toRefs, reactive, ref, defineComponent, watch, getCurrentInstance, nextTick, computed, onMounted} from 'vue';
+import {ElTree,FormInstance} from 'element-plus';
+import { Search } from '@element-plus/icons-vue'
+import UserList from './component/userList.vue';
+import api from '/@/api/system'
+
+const getDeptTree = api.dept.getList({status: -1})
+
+//TODO 批量操作需要在后端完成
+const getUserByIds = async (id: number[]) => await Promise.all(id.map(it=>api.user.detail(it)))
+// import {getDeptTree, getUserByIds} from '/@/api/system/user/index';
+
+interface QueryParam {
+  ids:number[];
+  deptProps:{};
+  deptData:any[];
+  param: {
+    deptId:string;
+    mobile:string;
+    status:string;
+    keyWords:string;
+    dateRange: string[];
+  };
+}
+
+export default defineComponent({
+  name: 'systemUser',
+  components: { UserList },
+  props:{
+    multiple:{
+      type:Boolean,
+      default:true
+    },
+    modelValue:{
+      type:Array<number>,
+      default:()=>[]
+    }
+  },
+  emits:['update:modelValue'],
+  setup(prop,{emit}) {
+    const selectedUsersPage = ref(1)
+    const visible = ref(false)
+    const {proxy} = <any>getCurrentInstance();
+    const {sys_user_sex} = proxy.useDict('sys_user_sex')
+    const userListRef = ref();
+    const queryRef = ref();
+    const filterText = ref('');
+    const treeRef = ref<InstanceType<typeof ElTree>>();
+    const search = Search
+    const  deptUser = ref<any>([]);
+    const selectedUsers = computed({
+      get: ()=>{
+        return prop.modelValue??[]
+      },
+      set:(val:any[])=>{
+        emit('update:modelValue',val)
+      }
+    })
+    const selectedUserInfo = computed(()=>{
+      let start = (selectedUsersPage.value-1)*10
+      let end = start+10
+      return deptUser.value.slice(start,end)
+    });
+    const initData = ()=>{
+      if(prop.modelValue&&prop.modelValue.length>0){
+        getUserByIds({ids:prop.modelValue}).then((res:any)=>{
+          if(res.code === 0){
+            deptUser.value = res.data.userList??[];
+          }
+        });
+      }else{
+        deptUser.value = []
+      }
+    };
+    onMounted(()=>{
+      initData()
+    })
+    watch(selectedUsers,(value, oldValue)=>{
+      if(value!=oldValue){
+        initData()
+      }
+    })
+    const state = reactive<QueryParam>({
+      ids:[],
+      deptProps:{
+        id:"deptId",
+        children: "children",
+        label: "deptName"
+      },
+      deptData:[],
+      param: {
+        deptId:'',
+        mobile:'',
+        status:'',
+        keyWords:'',
+        dateRange:[]
+      },
+    });
+    const getUserList = ()=>{
+        userListRef.value.setUserList();
+    };
+    // 初始化表格数据
+    const initTableData = () => {
+      getDeptTree().then((res:any)=>{
+        state.deptData = res.data.deps
+      })
+      getUserList()
+    };
+
+    watch(filterText, (val) => {
+      treeRef.value!.filter(val)
+    });
+    const deptFilterNode = (value: string, data:any) => {
+      if (!value) return true;
+      return data.deptName.includes(value)
+    };
+    // 节点单击事件
+    const handleNodeClick = (data:any) => {
+      state.param.deptId = data.deptId;
+      getUserList();
+    };
+    /** 重置按钮操作 */
+    const resetQuery = (formEl: FormInstance | undefined) => {
+      if (!formEl) return
+      formEl.resetFields()
+      getUserList()
+    };
+    const openDialog = ()=>{
+      visible.value = true
+      nextTick(()=>{
+        initTableData();
+      })
+    }
+		const handleSelectUserOk = (row: any) => {
+			if (!prop.multiple) {
+				selectedUsers.value = [row.id]
+			} else {
+				if (!selectedUsers.value.includes(row.id)) {
+					selectedUsers.value = [...selectedUsers.value!, row.id]
+				}
+			}
+
+		}
+    const handleSelectUserOkBatch = (row:any[])=>{
+      if(prop.multiple){
+        const ids:any[] = []
+        row.forEach((item:any)=>{
+          if(!selectedUsers.value.includes(item.id)){
+            ids.push(item.id)
+          }
+        })
+        if(ids.length>0){
+          selectedUsers.value = [...selectedUsers.value!,...ids]
+        }
+      }
+    }
+    const goBack = ()=>{
+      visible.value = false;
+    }
+    const removeAll = ()=>{
+      selectedUsers.value = []
+    }
+    const remove = (id:number)=>{
+      selectedUsers.value = selectedUsers.value.filter((item:any)=>{
+        return item !== id
+      })
+    }
+    const handleSelectUser = ()=>{
+      openDialog()
+    }
+    const handleClose = (id:number)=>{
+      selectedUsers.value =  selectedUsers.value.filter((item:any)=>{
+        return item !== id
+      })
+    }
+    return {
+      selectedUsersPage,
+      visible,
+      queryRef,
+      userListRef,
+      deptFilterNode,
+      filterText,
+      treeRef,
+      search,
+      sys_user_sex,
+      selectedUsers,
+      deptUser,
+      selectedUserInfo,
+      openDialog,
+      getUserList,
+      handleSelectUserOk,
+      handleSelectUserOkBatch,
+      handleNodeClick,
+      resetQuery,
+      goBack,
+      removeAll,
+      remove,
+      handleClose,
+      handleSelectUser,
+      ...toRefs(state),
+    };
+  },
+});
+</script>
+<style scoped>
+.u-m-r-10 {
+	margin-right: 8px;
+}
+</style>

+ 169 - 0
src/utils/gfast.ts

@@ -0,0 +1,169 @@
+import {Session} from "/@/utils/storage";
+
+/**
+ * 通用js方法封装处理
+ * Copyright (c) 2022 gfast
+ */
+
+export const baseURL:string|undefined|boolean = import.meta.env.VITE_API_URL
+
+
+export function getUpFileUrl(url:string){
+    if(!url){
+        return url
+    }
+    if (/^http|^blob/i.test(url)) {
+        return url
+    }
+    let reg = new RegExp('^/*' + baseURL + "/*");
+    return baseURL+url.replace(reg,'')
+}
+
+
+
+/**
+ * 构造树型结构数据
+ * @param {*} data 数据源
+ * @param {*} id id字段 默认 'id'
+ * @param {*} parentId 父节点字段 默认 'parentId'
+ * @param {*} children 孩子节点字段 默认 'children'
+ * @param {*} rootId 根Id 默认 0
+ */
+export function handleTree(data:any[], id:string, parentId:string, children:string, rootId:any):any[] {
+	id = id || 'id'
+	parentId = parentId || 'parentId'
+	children = children || 'children'
+	let rootIds:any = []
+	if(typeof rootId === 'boolean' && rootId){
+		//自动获取rootId
+		let idSet:any = {}
+		data.map((item:any)=>{
+			idSet[item[id]] = true
+		})
+		data.map((item:any)=>{
+			if(!idSet[item[parentId]]){
+				rootIds.push(item[parentId])
+			}
+		})
+	}else if(typeof rootId ==='string'){
+		rootId = rootId || ''
+		rootIds = [rootId]
+	}else{
+		rootId = rootId || 0
+		rootIds = [rootId]
+	}
+	rootIds = [...new Set(rootIds)]
+	let treeData:any = []
+	//对源数据深度克隆
+	const cloneData = JSON.parse(JSON.stringify(data))
+	rootIds.map((rItem:any)=>{
+		//循环所有项
+		const td =  cloneData.filter((father:any) => {
+			let branchArr = cloneData.filter((child:any) => {
+				//返回每一项的子级数组
+				return father[id] === child[parentId]
+			});
+			branchArr.length > 0 ? father[children] = branchArr : '';
+			//返回第一层
+			switch (typeof father[parentId]){
+				case 'string':
+					if(father[parentId]===''&&rItem===0){
+						return true
+					}
+					return father[parentId]===rItem.toString();
+				case 'number':
+					return father[parentId] === rItem;
+			}
+			return false;
+		});
+		if(td.length>0){
+			treeData = [...treeData,...td]
+		}
+	})
+	return treeData != '' ? treeData : data;
+}
+
+export function flattenTree(treeArray:any[]):any[] {
+    const result:any[] = [];
+
+    function flatten(node:any) {
+        result.push(node);
+        if (node.children && node.children.length > 0) {
+            node.children.forEach((child:any[]) => flatten(child));
+        }
+    }
+    treeArray.forEach(node => flatten(node));
+    return result;
+}
+
+export function findChildrenByPid(pid:any, flattenedArray :any[]):any[] {
+    const result:any[] = [];
+    flattenedArray.forEach(node => {
+        if (node.pid === pid) {
+            result.push(node);
+            const grandchildren = findChildrenByPid(node.id, flattenedArray);
+            if (grandchildren.length > 0) {
+                result.push(...grandchildren);
+            }
+        }
+    });
+    return result;
+}
+
+
+// 回显数据字典
+export function selectDictLabel(data:any[], value:string):string {
+    let actions:string[]=[]
+    data.map((item) => {
+        if (item.value == value) {
+            actions.push(item.label);
+            return false;
+        }
+    })
+    return actions.join('');
+}
+
+export function getToken():string{
+    return Session.get('token')
+}
+
+// 日期格式化
+export function parseTime(time:any, pattern:string) {
+    if (arguments.length === 0 || !time) {
+        return null
+    }
+    const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+    let date
+    if (typeof time === 'object') {
+        date = time
+    } else {
+        if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+            time = parseInt(time)
+        } else if (typeof time === 'string') {
+            time = time.replace(new RegExp(/-/gm), '/');
+        }
+        if ((typeof time === 'number') && (time.toString().length === 10)) {
+            time = time * 1000
+        }
+        date = new Date(time)
+    }
+    const formatObj:any = {
+        y: date.getFullYear(),
+        m: date.getMonth() + 1,
+        d: date.getDate(),
+        h: date.getHours(),
+        i: date.getMinutes(),
+        s: date.getSeconds(),
+        a: date.getDay()
+    }
+    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+        let value = formatObj[key]
+        // Note: getDay() returns 0 on Sunday
+        if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+        if (result.length > 0 && value < 10) {
+            value = '0' + value
+        }
+        return value || 0
+    })
+    return time_str
+}

+ 118 - 0
src/utils/zipdownload.ts

@@ -0,0 +1,118 @@
+import axios from 'axios'
+import {baseURL, getToken} from "/@/utils/gfast"
+import { ElMessage,ElLoading } from 'element-plus';
+const mimeMap = {
+  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+  zip: 'application/zip'
+}
+
+const baseUrl = import.meta.env.VITE_API_URL
+export function downLoadZip(str:string, filename:string) {
+  var url = baseUrl + str
+  axios({
+    method: 'get',
+    url: url,
+    responseType: 'blob',
+    headers: { 'Authorization': 'Bearer ' + getToken() }
+  }).then(res => {
+    if(filename){
+      res.headers['content-disposition'] = 'attachment; filename='+filename;
+    }
+    resolveBlob(res, mimeMap.zip)
+  })
+}
+
+const getHost = ():string=>{
+  return window.location.origin
+}
+
+// 下载插件
+export function downLoadXml (url:string,params:any,rType='get') {
+  if (baseUrl=='' ||baseURL=='/'){
+    url = getHost() + url
+  }else{
+    url = baseUrl + url
+  }
+  const loading = ElLoading.service({
+    lock: true,
+    text: '正在生成导出数据...',
+    spinner: 'el-icon-loading',
+    background: 'rgba(0, 0, 0, 0.7)'
+  })
+  let option = {
+    url: url,
+    params:params,
+    timeout:300000,
+    method: 'get',
+    responseType: 'blob',
+    headers: { 'Authorization': 'Bearer ' + getToken() }
+  }
+  if(rType=='post'){
+    option = {
+      url: url,
+      data:params,
+      timeout:300000,
+      method: 'post',
+      responseType: 'blob',
+      headers: { 'Authorization': 'Bearer ' + getToken() }
+    }
+  }
+  axios(option).then(res => {
+    loading.close()
+    if (res.status !== 200) {
+      ElMessage({
+        message: '发生错误,导出失败。',
+        type: 'error'
+      })
+      return
+    }
+    try {
+      const aLink = document.createElement('a')
+      var blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+      var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
+      var contentDisposition = decodeURI(res.headers['content-disposition'] || res.headers['Content-Disposition'])
+      var result = patt.exec(contentDisposition)
+      var fileName = result[1]
+      fileName = fileName.replace(/"/g, '')
+      aLink.href = URL.createObjectURL(blob)
+      aLink.setAttribute('download', fileName) // 设置下载文件名称
+      document.body.appendChild(aLink)
+      aLink.click()
+      document.body.appendChild(aLink)
+    } catch (e) {
+      console.log(e)
+      ElMessage({
+        message: '下载失败,可能您登录状态过期,请重新登录后下载。',
+        type: 'error'
+      })
+    }
+  }).catch(e=>{
+    console.log(e)
+    loading.close()
+    ElMessage({
+      message: '导出失败',
+      type: 'error'
+    })
+  })
+}
+
+/**
+ * 解析blob响应内容并下载
+ * @param {*} res blob响应内容
+ * @param {String} mimeType MIME类型
+ */
+export function resolveBlob(res: any, mimeType:any) {
+  const aLink = document.createElement('a')
+  var blob = new Blob([res.data], { type: mimeType })
+  // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
+  var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
+  var contentDisposition = decodeURI(res.headers['content-disposition']||res.headers['Content-Disposition'])
+  var result = patt.exec(contentDisposition)
+  var fileName = result[1]
+  fileName = fileName.replace(/\"/g, '')
+  aLink.href = URL.createObjectURL(blob)
+  aLink.setAttribute('download', fileName) // 设置下载文件名称
+  document.body.appendChild(aLink)
+  aLink.click()
+  document.body.appendChild(aLink)
+}

+ 514 - 5
yarn.lock

@@ -203,11 +203,21 @@
   resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz"
   integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==
 
+"@babel/helper-string-parser@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+  integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
 "@babel/helper-validator-identifier@^7.24.7":
   version "7.24.7"
   resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz"
   integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
 
+"@babel/helper-validator-identifier@^7.27.1":
+  version "7.27.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
+  integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+
 "@babel/parser@^7.12.0", "@babel/parser@^7.16.4", "@babel/parser@^7.25.3":
   version "7.25.6"
   resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.6.tgz"
@@ -215,6 +225,13 @@
   dependencies:
     "@babel/types" "^7.25.6"
 
+"@babel/parser@^7.23.5":
+  version "7.28.0"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
+  integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
+  dependencies:
+    "@babel/types" "^7.28.0"
+
 "@babel/runtime-corejs3@^7.11.2":
   version "7.25.6"
   resolved "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.25.6.tgz"
@@ -239,6 +256,14 @@
     "@babel/helper-validator-identifier" "^7.24.7"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.28.0":
+  version "7.28.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.0.tgz#2fd0159a6dc7353933920c43136335a9b264d950"
+  integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==
+  dependencies:
+    "@babel/helper-string-parser" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.27.1"
+
 "@ctrl/tinycolor@^3.4.1":
   version "3.6.1"
   resolved "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz"
@@ -299,6 +324,13 @@
   resolved "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.7.tgz"
   integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==
 
+"@form-create/component-elm-checkbox@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-checkbox/-/component-elm-checkbox-2.7.8.tgz#1c95a49dc728df1249c32c30381412fb1c8d55f4"
+  integrity sha512-CC9vXlomXo/xqoSZeV9KsASWGtKDA1VWc9xlvN5a7fvsLEhqy05GCbIp2bOfwpxuGwKzBzZHLPK1UnzOFRvbqw==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-checkbox@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-checkbox/-/component-elm-checkbox-3.2.23.tgz#a4cd9fe01a33b0ac09ae2e496c7fa0fcb5242a90"
@@ -306,6 +338,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-frame@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-frame/-/component-elm-frame-2.7.8.tgz#92ab1654b6d0147615ebdb50e44ebd526026c6d6"
+  integrity sha512-AtHnfZzfZEMpO/mYnj43Snjweua+6rAZPj67zPva1u3QJWeWxkhtulEwKhK/wekckMD9mXNkzO9Nze6VbLqG2w==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-frame@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-frame/-/component-elm-frame-3.2.23.tgz#60d3f56fba369a1d42fd9c9cb12aa8c1194f1320"
@@ -313,6 +352,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-group@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-group/-/component-elm-group-2.7.8.tgz#2743a4d32d963a065d7bb59c91482927dabda629"
+  integrity sha512-0rEWSnU+U8vBC/FZ3wdQLAsxyjZ5n18+iU9L82gOJDVESdMvwTKwjhvNmJ1zPbHOlYodblAPOADsgKBaGcuNqw==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-group@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-group/-/component-elm-group-3.2.23.tgz#53ad85706e4b2871d8e1fa8b1cd42161f193317e"
@@ -320,6 +366,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-radio@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-radio/-/component-elm-radio-2.7.8.tgz#f8d4e95312243f2e1ccbcce038e436ea5e5f96a1"
+  integrity sha512-LKykitghN57bpUFbeDggNiTX3Iz0lBkD7HeoA7OvStZUluw7hOfaDn4uqE8NwS0hfH9lVgRInUFBOhNc8JT+eg==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-radio@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-radio/-/component-elm-radio-3.2.23.tgz#33a9958918ef8c1caaa3d7da9e9b93dd327a18e3"
@@ -327,6 +380,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-select@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-select/-/component-elm-select-2.7.8.tgz#a8d3ec37782a260d4008f4712ed5e2a37285eba7"
+  integrity sha512-JRn6WSevxqE3nuVQwOT/eoqGKKbivqa0k/bwl2lzf5wwTlDnL+JOMqkmoSNxLssII4vz33txJxZAKUUWULYK5w==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-select@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-select/-/component-elm-select-3.2.23.tgz#e70f7c8edb9ff8748bde13c5bf667b5f05191332"
@@ -334,6 +394,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-tree@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-tree/-/component-elm-tree-2.7.8.tgz#a9d45696aa58c608879e42f7513b6fa89e3a7e37"
+  integrity sha512-MD4PzYnbOGI3Q5VEIhEr4uM0L0Gmi36pZ5XZARNdwG7Pt29w0EjUTOuXmuW6jbfWENke6HI/4yI69/T2lhHE/A==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-tree@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-tree/-/component-elm-tree-3.2.23.tgz#984c60e62fbb7ba836cb275add3ee60ba1bac102"
@@ -341,6 +408,13 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-elm-upload@^2.7.11":
+  version "2.7.11"
+  resolved "https://registry.yarnpkg.com/@form-create/component-elm-upload/-/component-elm-upload-2.7.11.tgz#c868ff90c1b6193203ac63593aad8a7e439ab570"
+  integrity sha512-2RHRT7tHbAqVyWlJS9qwE/Sf9Uh6IPUYcyUZxu4JKeIgwJmgGjEY5sT/9hhgsbKnmI39xtWD3Sprf9RBoC7oZA==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/component-elm-upload@^3.2.26":
   version "3.2.26"
   resolved "https://registry.yarnpkg.com/@form-create/component-elm-upload/-/component-elm-upload-3.2.26.tgz#c893763e1981e9d4395e5273e33bb73ff057b66d"
@@ -348,6 +422,11 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/component-subform@^2.7.1":
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/@form-create/component-subform/-/component-subform-2.7.1.tgz#fec9f27582ed5f216eac23ec127a07b94de07b36"
+  integrity sha512-qR6uZFCMnUfFyjh1A3Q6CTwHWp7piLGS0fbaS8k0MfjcQCPDuhvMt1WYBGQBJZMcNwuDx2kceHcjFtk/ZMYOjg==
+
 "@form-create/component-subform@^3.1.34":
   version "3.1.34"
   resolved "https://registry.yarnpkg.com/@form-create/component-subform/-/component-subform-3.1.34.tgz#d394ab3eed5e69c5b5e969effd617b83496b4002"
@@ -360,6 +439,20 @@
   dependencies:
     wangeditor "^4.6.0"
 
+"@form-create/component-wangeditor@^2.7.0":
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/@form-create/component-wangeditor/-/component-wangeditor-2.7.1.tgz#357fc1f3f435fe88395cd961d67bcc55d352d668"
+  integrity sha512-gt2CpKTl/U6cup8Ic1Gab5BsEry6+txwCon0krS0a5Elk905o3gWtHQs3cZOt3AIwh7T1U6pKeLYuV9v0TgWnQ==
+  dependencies:
+    wangeditor "^4.6.0"
+
+"@form-create/core@^2.7.11":
+  version "2.7.11"
+  resolved "https://registry.yarnpkg.com/@form-create/core/-/core-2.7.11.tgz#be52a2ed606852ea4cdd29a8cf4594647c4c645f"
+  integrity sha512-2LV36UgV/ixBP0VxyFzYYBCQebKlnYr16Eikmf38mD+Hcvj2GIWG9+vA//eE6ZyYdm2GCzIGtdw/G2oarR0OBg==
+  dependencies:
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/core@^3.2.26":
   version "3.2.26"
   resolved "https://registry.yarnpkg.com/@form-create/core/-/core-3.2.26.tgz#ea20ff70e27212949d93513dcd9de2d776ee7780"
@@ -367,6 +460,36 @@
   dependencies:
     "@form-create/utils" "^3.2.23"
 
+"@form-create/designer@^1.1.9":
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/@form-create/designer/-/designer-1.1.9.tgz#91d7fc362b16b6131c91516c5065ea3ed7d80d1e"
+  integrity sha512-Yp1GaBLekMlmzU0Wjk1bCL9qzrgUjWLeX1av0AvA8fyOwkcOtYt9NfGvI8gaG2oLWcx7afXdJkKzo8pRETrhOg==
+  dependencies:
+    "@form-create/component-wangeditor" "^2.7.0"
+    "@form-create/element-ui" "^2.7.3"
+    "@form-create/utils" "^2.7.0"
+    codemirror "5.60.0"
+    element-ui "^2.15.2"
+    js-beautify "^1.15.1"
+    vue "^2.7.14"
+    vuedraggable "^2.24.3"
+
+"@form-create/element-ui@^2.7.3":
+  version "2.7.11"
+  resolved "https://registry.yarnpkg.com/@form-create/element-ui/-/element-ui-2.7.11.tgz#5184eb2a7910026451494192749f7edde106d73c"
+  integrity sha512-4YWSd55Nd8EUQqD5ipKrNxLRujyjyHaDOkAR2R5Y8TwWEf6XDT9pDJ20BwOQTKZfsXlyRWMm/qbBA7t7Czectw==
+  dependencies:
+    "@form-create/component-elm-checkbox" "^2.7.8"
+    "@form-create/component-elm-frame" "^2.7.8"
+    "@form-create/component-elm-group" "^2.7.8"
+    "@form-create/component-elm-radio" "^2.7.8"
+    "@form-create/component-elm-select" "^2.7.8"
+    "@form-create/component-elm-tree" "^2.7.8"
+    "@form-create/component-elm-upload" "^2.7.11"
+    "@form-create/component-subform" "^2.7.1"
+    "@form-create/core" "^2.7.11"
+    "@form-create/utils" "^2.7.8"
+
 "@form-create/element-ui@^3":
   version "3.2.26"
   resolved "https://registry.yarnpkg.com/@form-create/element-ui/-/element-ui-3.2.26.tgz#a73eda703b6ef893c4663404c14d71e06a9c3b81"
@@ -383,6 +506,11 @@
     "@form-create/core" "^3.2.26"
     "@form-create/utils" "^3.2.23"
 
+"@form-create/utils@^2.7.0", "@form-create/utils@^2.7.8":
+  version "2.7.8"
+  resolved "https://registry.yarnpkg.com/@form-create/utils/-/utils-2.7.8.tgz#3e8ae511ac82539e55f720ac130f63e6201bf118"
+  integrity sha512-49bOop5HUh7ixOtDpIACB83wi6VR9e3kQvTZqEdsYmhL3Cli9OTkP/Cn8solw/KGGthmW67UyW5v0+meaelBHA==
+
 "@form-create/utils@^3.2.23":
   version "3.2.23"
   resolved "https://registry.yarnpkg.com/@form-create/utils/-/utils-3.2.23.tgz#0e7e66d67314a5a8027c9b0ad9e4fc4b7be1f635"
@@ -577,6 +705,18 @@
     "@intlify/runtime" "9.1.10"
     "@intlify/shared" "9.1.10"
 
+"@isaacs/cliui@^8.0.2":
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+  integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+  dependencies:
+    string-width "^5.1.2"
+    string-width-cjs "npm:string-width@^4.2.0"
+    strip-ansi "^7.0.1"
+    strip-ansi-cjs "npm:strip-ansi@^6.0.1"
+    wrap-ansi "^8.1.0"
+    wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
+
 "@jridgewell/sourcemap-codec@^1.5.0":
   version "1.5.0"
   resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
@@ -653,6 +793,16 @@
     "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
+"@one-ini/wasm@0.1.1":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
+  integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==
+
+"@pkgjs/parseargs@^0.11.0":
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
+  integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
+
 "@popperjs/core@^2.4.4", "@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
   version "2.11.7"
   resolved "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz"
@@ -896,6 +1046,17 @@
     "@vue/compiler-core" "3.5.3"
     "@vue/shared" "3.5.3"
 
+"@vue/compiler-sfc@2.7.16":
+  version "2.7.16"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83"
+  integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==
+  dependencies:
+    "@babel/parser" "^7.23.5"
+    postcss "^8.4.14"
+    source-map "^0.6.1"
+  optionalDependencies:
+    prettier "^1.18.2 || ^2.0.0"
+
 "@vue/compiler-sfc@3.2.37":
   version "3.2.37"
   resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz"
@@ -1140,6 +1301,11 @@ JSV@^4.0.2:
   resolved "https://registry.npmmirror.com/JSV/-/JSV-4.0.2.tgz"
   integrity sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==
 
+abbrev@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
+  integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
+
 acorn-jsx@^5.3.2:
   version "5.3.2"
   resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
@@ -1197,6 +1363,11 @@ ansi-regex@^5.0.1:
   resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz"
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
 
+ansi-regex@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+  integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-2.2.1.tgz"
@@ -1209,13 +1380,18 @@ ansi-styles@^3.2.1:
   dependencies:
     color-convert "^1.9.0"
 
-ansi-styles@^4.1.0:
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   version "4.3.0"
   resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz"
   integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
   dependencies:
     color-convert "^2.0.1"
 
+ansi-styles@^6.1.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
 anymatch@~3.1.2:
   version "3.1.3"
   resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz"
@@ -1266,6 +1442,13 @@ async-validator@^4.2.5:
   resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz"
   integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
 
+async-validator@~1.8.1:
+  version "1.8.5"
+  resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0"
+  integrity sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==
+  dependencies:
+    babel-runtime "6.x"
+
 available-typed-arrays@^1.0.7:
   version "1.0.7"
   resolved "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz"
@@ -1280,6 +1463,19 @@ axios@0.26.0:
   dependencies:
     follow-redirects "^1.14.8"
 
+babel-helper-vue-jsx-merge-props@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
+  integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==
+
+babel-runtime@6.x:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
 balanced-match@^1.0.0:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz"
@@ -1308,6 +1504,13 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+brace-expansion@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
+  integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
+  dependencies:
+    balanced-match "^1.0.0"
+
 braces@^3.0.3, braces@~3.0.2:
   version "3.0.3"
   resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz"
@@ -1434,6 +1637,11 @@ codemirror-editor-vue3@2.5.8:
     diff-match-patch "^1.0.5"
     jsonlint-mod "^1.7.6"
 
+codemirror@5.60.0:
+  version "5.60.0"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.60.0.tgz#00a8cfd287d5d8737ceb73987f04aee2fe5860da"
+  integrity sha512-AEL7LhFOlxPlCL8IdTcJDblJm8yrAGib7I+DErJPdZd4l6imx8IMgKK3RblVgBQqz3TZJR4oknQ03bz+uNjBYA==
+
 codemirror@5.65.16, codemirror@^5:
   version "5.65.16"
   resolved "https://registry.npmmirror.com/codemirror/-/codemirror-5.65.16.tgz"
@@ -1471,6 +1679,11 @@ color-name@~1.1.4:
   resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
+commander@^10.0.0:
+  version "10.0.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
+  integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
+
 commander@^2.19.0:
   version "2.20.3"
   resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -1491,6 +1704,14 @@ concat-map@0.0.1:
   resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz"
   integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
 
+config-chain@^1.1.13:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
+  integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
+  dependencies:
+    ini "^1.3.4"
+    proto-list "~1.2.1"
+
 contour_plot@^0.0.1:
   version "0.0.1"
   resolved "https://registry.npmmirror.com/contour_plot/-/contour_plot-0.0.1.tgz"
@@ -1501,6 +1722,11 @@ core-js-pure@^3.30.2:
   resolved "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.38.1.tgz"
   integrity sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==
 
+core-js@^2.4.0:
+  version "2.6.12"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
+  integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
+
 core-js@^3.6.5:
   version "3.38.1"
   resolved "https://registry.npmmirror.com/core-js/-/core-js-3.38.1.tgz"
@@ -1537,6 +1763,15 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+cross-spawn@^7.0.6:
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
 css-line-break@^2.1.0:
   version "2.1.0"
   resolved "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz"
@@ -1557,7 +1792,7 @@ csstype@^2.6.8:
   resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz"
   integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
 
-csstype@^3.0.8, csstype@^3.1.3:
+csstype@^3.0.8, csstype@^3.1.0, csstype@^3.1.3:
   version "3.1.3"
   resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz"
   integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
@@ -1655,6 +1890,11 @@ deep-is@^0.1.3:
   resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz"
   integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
 
+deepmerge@^1.2.0:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
+  integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
+
 define-data-property@^1.0.1, define-data-property@^1.1.1, define-data-property@^1.1.4:
   version "1.1.4"
   resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz"
@@ -1729,6 +1969,11 @@ downloadjs@1.4.7:
   resolved "https://registry.npmmirror.com/downloadjs/-/downloadjs-1.4.7.tgz"
   integrity sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==
 
+eastasianwidth@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
 echarts-gl@2.0.9:
   version "2.0.9"
   resolved "https://registry.npmmirror.com/echarts-gl/-/echarts-gl-2.0.9.tgz"
@@ -1750,6 +1995,16 @@ echarts@5.5.0:
     tslib "2.3.0"
     zrender "5.5.0"
 
+editorconfig@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.4.tgz#040c9a8e9a6c5288388b87c2db07028aa89f53a3"
+  integrity sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==
+  dependencies:
+    "@one-ini/wasm" "0.1.1"
+    commander "^10.0.0"
+    minimatch "9.0.1"
+    semver "^7.5.3"
+
 element-plus@2.2.28:
   version "2.2.28"
   resolved "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.28.tgz"
@@ -1792,6 +2047,28 @@ element-resize-detector@^1.2.1:
   dependencies:
     batch-processor "1.0.0"
 
+element-ui@^2.15.2:
+  version "2.15.14"
+  resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.15.14.tgz#3c34df79467636592812d720d2e6784e7a6ec2ea"
+  integrity sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==
+  dependencies:
+    async-validator "~1.8.1"
+    babel-helper-vue-jsx-merge-props "^2.0.0"
+    deepmerge "^1.2.0"
+    normalize-wheel "^1.0.1"
+    resize-observer-polyfill "^1.5.0"
+    throttle-debounce "^1.0.1"
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emoji-regex@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
 entities@^4.5.0:
   version "4.5.0"
   resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz"
@@ -2260,6 +2537,14 @@ for-each@^0.3.3, for-each@~0.3.3:
   dependencies:
     is-callable "^1.1.3"
 
+foreground-child@^3.1.0:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
+  integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
+  dependencies:
+    cross-spawn "^7.0.6"
+    signal-exit "^4.0.1"
+
 frac@~1.1.2:
   version "1.1.2"
   resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz"
@@ -2348,6 +2633,18 @@ glob-parent@^6.0.1:
   dependencies:
     is-glob "^4.0.3"
 
+glob@^10.4.2:
+  version "10.4.5"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
+  integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+  dependencies:
+    foreground-child "^3.1.0"
+    jackspeak "^3.1.2"
+    minimatch "^9.0.4"
+    minipass "^7.1.2"
+    package-json-from-dist "^1.0.0"
+    path-scurry "^1.11.1"
+
 glob@^7.1.3, glob@~7.2.3:
   version "7.2.3"
   resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz"
@@ -2518,6 +2815,11 @@ inherits@2, inherits@~2.0.4:
   resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
+ini@^1.3.4:
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+  integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
 internal-slot@^1.0.7:
   version "1.0.7"
   resolved "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.0.7.tgz"
@@ -2601,6 +2903,11 @@ is-extglob@^2.1.1:
   resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz"
   integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
 
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
 is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
   version "4.0.3"
   resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz"
@@ -2678,7 +2985,27 @@ isexe@^2.0.0:
   resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz"
   integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
 
-js-cookie@3.0.5:
+jackspeak@^3.1.2:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
+  integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+  optionalDependencies:
+    "@pkgjs/parseargs" "^0.11.0"
+
+js-beautify@^1.15.1:
+  version "1.15.4"
+  resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.4.tgz#f579f977ed4c930cef73af8f98f3f0a608acd51e"
+  integrity sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==
+  dependencies:
+    config-chain "^1.1.13"
+    editorconfig "^1.0.4"
+    glob "^10.4.2"
+    js-cookie "^3.0.5"
+    nopt "^7.2.1"
+
+js-cookie@3.0.5, js-cookie@^3.0.5:
   version "3.0.5"
   resolved "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz"
   integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
@@ -2797,6 +3124,11 @@ longest@^1.0.1:
   resolved "https://registry.npmmirror.com/longest/-/longest-1.0.1.tgz"
   integrity sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==
 
+lru-cache@^10.2.0:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+  integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
 magic-string@^0.25.7:
   version "0.25.9"
   resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz"
@@ -2839,6 +3171,13 @@ micromatch@^4.0.4:
     braces "^3.0.3"
     picomatch "^2.3.1"
 
+minimatch@9.0.1:
+  version "9.0.1"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253"
+  integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz"
@@ -2846,11 +3185,23 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@^9.0.4:
+  version "9.0.5"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+  integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimist@^1.2.0, minimist@~1.2.8:
   version "1.2.8"
   resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
 
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+  integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
 mitt@3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz"
@@ -2910,6 +3261,11 @@ ms@^2.1.3:
   resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz"
   integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
 
+nanoid@^3.3.11:
+  version "3.3.11"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
+  integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+
 nanoid@^3.3.7:
   version "3.3.7"
   resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz"
@@ -2935,6 +3291,13 @@ neo-async@^2.6.2:
   resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz"
   integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
 
+nopt@^7.2.1:
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7"
+  integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==
+  dependencies:
+    abbrev "^2.0.0"
+
 normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz"
@@ -3007,6 +3370,11 @@ optionator@^0.9.1:
     type-check "^0.4.0"
     word-wrap "^1.2.5"
 
+package-json-from-dist@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+  integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
 pako@1.0.11:
   version "1.0.11"
   resolved "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz"
@@ -3034,6 +3402,14 @@ path-parse@^1.0.7:
   resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
+path-scurry@^1.11.1:
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
+  integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
+  dependencies:
+    lru-cache "^10.2.0"
+    minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
 path-type@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz"
@@ -3049,6 +3425,11 @@ picocolors@^1.0.1:
   resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.0.tgz"
   integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
 
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
 picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz"
@@ -3068,6 +3449,15 @@ postcss@^8.1.10, postcss@^8.4.18, postcss@^8.4.44:
     picocolors "^1.0.1"
     source-map-js "^1.2.0"
 
+postcss@^8.4.14:
+  version "8.5.6"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
+  integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
+  dependencies:
+    nanoid "^3.3.11"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
 preact@^10.17.1:
   version "10.26.9"
   resolved "https://registry.yarnpkg.com/preact/-/preact-10.26.9.tgz#b3898d1b65140640799062ad73b89846c293b6a7"
@@ -3083,6 +3473,11 @@ prettier@2.5.1:
   resolved "https://registry.npmmirror.com/prettier/-/prettier-2.5.1.tgz"
   integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
 
+"prettier@^1.18.2 || ^2.0.0":
+  version "2.8.8"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
+  integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
+
 print-js@1.6.0:
   version "1.6.0"
   resolved "https://registry.npmmirror.com/print-js/-/print-js-1.6.0.tgz"
@@ -3098,6 +3493,11 @@ prismjs@^1.29.0:
   resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz"
   integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==
 
+proto-list@~1.2.1:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+  integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
+
 province-city-china@8.5.7:
   version "8.5.7"
   resolved "https://registry.npmmirror.com/province-city-china/-/province-city-china-8.5.7.tgz"
@@ -3145,6 +3545,11 @@ readdirp@~3.6.0:
   dependencies:
     picomatch "^2.2.1"
 
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
 regenerator-runtime@^0.14.0:
   version "0.14.1"
   resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz"
@@ -3170,7 +3575,7 @@ repeat-string@^1.5.2:
   resolved "https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz"
   integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==
 
-resize-observer-polyfill@^1.5.1:
+resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
   version "1.5.1"
   resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
   integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
@@ -3291,6 +3696,11 @@ semver@7.6.2, semver@^7.3.5:
   resolved "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz"
   integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
 
+semver@^7.5.3:
+  version "7.7.2"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
+  integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
+
 set-function-length@^1.2.1:
   version "1.2.2"
   resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz"
@@ -3335,6 +3745,11 @@ side-channel@^1.0.4:
     get-intrinsic "^1.2.4"
     object-inspect "^1.13.1"
 
+signal-exit@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+  integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
 size-sensor@^1.0.1:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/size-sensor/-/size-sensor-1.0.2.tgz"
@@ -3345,6 +3760,11 @@ slash@^3.0.0:
   resolved "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
+sortablejs@1.10.2:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
+  integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
+
 sortablejs@1.14.0:
   version "1.14.0"
   resolved "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz"
@@ -3355,6 +3775,11 @@ sortablejs@1.14.0:
   resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.0.tgz"
   integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
 
+source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+
 source-map-support@^0.3.2:
   version "0.3.3"
   resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.3.3.tgz"
@@ -3409,6 +3834,33 @@ state-local@^1.0.6:
   resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz"
   integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==
 
+"string-width-cjs@npm:string-width@^4.2.0":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^4.1.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string-width@^5.0.1, string-width@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+  dependencies:
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
+
 string.prototype.trim@^1.2.9, string.prototype.trim@~1.2.8:
   version "1.2.9"
   resolved "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz"
@@ -3437,6 +3889,13 @@ string.prototype.trimstart@^1.0.8:
     define-properties "^1.2.1"
     es-object-atoms "^1.0.0"
 
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-ansi@^3.0.0:
   version "3.0.1"
   resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz"
@@ -3444,13 +3903,20 @@ strip-ansi@^3.0.0:
   dependencies:
     ansi-regex "^2.0.0"
 
-strip-ansi@^6.0.1:
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
   dependencies:
     ansi-regex "^5.0.1"
 
+strip-ansi@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+  dependencies:
+    ansi-regex "^6.0.1"
+
 strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
@@ -3514,6 +3980,11 @@ text-table@^0.2.0:
   resolved "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz"
   integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
 
+throttle-debounce@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz#51853da37be68a155cb6e827b3514a3c422e89cd"
+  integrity sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==
+
 tiny-emitter@^2.0.0:
   version "2.1.0"
   resolved "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz"
@@ -3769,6 +4240,11 @@ vue-i18n@9.1.10:
     "@intlify/vue-devtools" "9.1.10"
     "@vue/devtools-api" "^6.0.0-beta.7"
 
+vue-json-pretty@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/vue-json-pretty/-/vue-json-pretty-2.5.0.tgz#cb31d9a3f1fb385a209609dbb1cbb7aeaf891fa9"
+  integrity sha512-nZA6qXYaiMaE2J0HkKtkrLcPJoN03SDSBdZWEPiRwoOVySWOfoZCyadhBwBN6wAHTga+c/R49ExGWoKFXnu37A==
+
 vue-router@4.0.13:
   version "4.0.13"
   resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.13.tgz"
@@ -3810,6 +4286,14 @@ vue@3, vue@3.2.37:
     "@vue/server-renderer" "3.2.37"
     "@vue/shared" "3.2.37"
 
+vue@^2.7.14:
+  version "2.7.16"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
+  integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
+  dependencies:
+    "@vue/compiler-sfc" "2.7.16"
+    csstype "^3.1.0"
+
 vue@^3.0.0:
   version "3.1.5"
   resolved "https://registry.npmmirror.com/vue/-/vue-3.1.5.tgz"
@@ -3830,6 +4314,13 @@ vue@^3.4.31:
     "@vue/server-renderer" "3.5.3"
     "@vue/shared" "3.5.3"
 
+vuedraggable@^2.24.3:
+  version "2.24.3"
+  resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
+  integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
+  dependencies:
+    sortablejs "1.10.2"
+
 vuex@4.0.2:
   version "4.0.2"
   resolved "https://registry.npmmirror.com/vuex/-/vuex-4.0.2.tgz"
@@ -3909,6 +4400,24 @@ wordwrap@0.0.2:
   resolved "https://registry.npmmirror.com/wordwrap/-/wordwrap-0.0.2.tgz"
   integrity sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==
 
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+  integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+  dependencies:
+    ansi-styles "^6.1.0"
+    string-width "^5.0.1"
+    strip-ansi "^7.0.1"
+
 wrappy@1:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz"