Bladeren bron

reactor:优化通知公告选择通知人或部门数据量太卡顿;

zhangchong 1 jaar geleden
bovenliggende
commit
205468bc65

+ 6 - 4
package.json

@@ -36,7 +36,8 @@
 		"vue": "^3.2.45",
 		"vue-router": "^4.1.6",
 		"vue3-seamless-scroll": "^2.0.1",
-		"vuedraggable": "^4.1.0"
+		"vuedraggable": "^4.1.0",
+		"webpack": "^5.0.0"
 	},
 	"devDependencies": {
 		"@types/node": "^18.11.9",
@@ -44,7 +45,8 @@
 		"@types/qs": "^6.9.7",
 		"@typescript-eslint/eslint-plugin": "^5.44.0",
 		"@typescript-eslint/parser": "^5.44.0",
-		"@vitejs/plugin-vue": "^3.2.0",
+		"@vitejs/plugin-vue": "^4.4.0",
+		"@vitejs/plugin-vue-jsx": "^3.1.0",
 		"@vue/compiler-sfc": "^3.2.45",
 		"dotenv": "^16.0.3",
 		"eslint": "^8.28.0",
@@ -53,8 +55,8 @@
 		"sass": "^1.56.1",
 		"sass-loader": "^13.2.0",
 		"terser": "^5.26.0",
-		"typescript": "^4.9.3",
-		"vite": "^3.2.4",
+		"typescript": "~5.2.2",
+		"vite": "^4.4.11",
 		"vite-plugin-compression": "^0.5.1",
 		"vite-plugin-vue-setup-extend-plus": "^0.1.0",
 		"vue-eslint-parser": "^9.1.0"

+ 1 - 1
src/components/ProcessAudit/index.vue

@@ -491,7 +491,7 @@ const state = reactive<any>({
 		//延期申请表单
 		timeLimitCount: null, // 延期申请数量
 		content: '', // 延期申请理由
-		timeLimitUnit: '', // 延期申请单位
+		timeLimitUnit: 2, // 延期申请单位 默认工作日
 	},
 	discernForm: {
 		// 甄别表单

+ 171 - 111
src/views/auxiliary/notice/component/Notice-add.vue

@@ -7,7 +7,7 @@
 						<el-form-item label="通知类型" prop="circularObj" :rules="[{ required: true, message: '请选择通知类型型', trigger: 'change' }]">
 							<el-select
 								v-model="state.ruleForm.circularObj"
-								placeholder="请选择通知类型"
+								placeholder="请选择通知类型"
 								value-key="dicDataValue"
 								class="w100"
 								@change="
@@ -42,8 +42,8 @@
 								placeholder="请选择失效时间"
 								value-format="YYYY-MM-DD[T]HH:mm:ss"
 								class="w100"
-                :disabled-date="disabledDate"
-                popper-class="no-atTheMoment"
+								:disabled-date="disabledDate"
+								popper-class="no-atTheMoment"
 							/>
 						</el-form-item>
 					</el-col>
@@ -67,71 +67,57 @@
 							<editor v-model:get-html="state.ruleForm.content" placeholder="请输入通知内容" height="450px" />
 						</el-form-item>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
-						<el-form-item label="通知对象" prop="circularType" :rules="[{ required: true, message: '请选择通知对象', trigger: 'change' }]">
-							<el-select v-model="state.ruleForm.circularType" placeholder="请选择通知对象" class="w100">
-								<el-option v-for="item in circularTypeEnum" :value="item.key" :key="item.key" :label="item.value" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="state.ruleForm.circularType === 1">
-						<el-divider content-position="left">
-							<span>选择通知人</span>
-						</el-divider>
+          <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+            <el-form-item label="通知对象" prop="circularType" :rules="[{ required: true, message: '请选择通知对象', trigger: 'change' }]">
+              <el-radio-group v-model="state.ruleForm.circularType">
+                <el-radio :label="item.key" v-for="item in circularTypeEnum" :key="item.key">{{item.value}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+					<el-col
+						:xs="24"
+						:sm="24"
+						:md="24"
+						:lg="24"
+						:xl="24"
+						v-if="state.ruleForm.circularType === 1"
+						style="border: var(--el-border); border-radius: var(--el-border-radius-base)"
+						class="pd15 mb20"
+					>
 						<div class="flex-center-align mb10">
-							<el-input v-model="state.searchContent" placeholder="请输入搜索内容" class="w100" clearable>
+							<el-input v-model="state.searchContent" placeholder="通知人姓名/账号" class="w100" clearable>
 								<template #prefix>
 									<SvgIcon name="ele-Search" />
 								</template>
 							</el-input>
 						</div>
-						<el-table :data="tables" @selection-change="handleSelectionChange" max-height="500" ref="multipleTableRef" row-key="id">
-							<el-table-column type="selection" label="请选择" width="40" :reserve-selection="true" align="center" />
-							<el-table-column prop="name" label="姓名" show-overflow-tooltip width="170"></el-table-column>
-							<el-table-column prop="userName" label="账号" show-overflow-tooltip width="170"></el-table-column>
-							<el-table-column prop="organization.name" label="所属部门" show-overflow-tooltip width="190"></el-table-column>
-							<el-table-column prop="staffNo" label="工号" show-overflow-tooltip width="120"></el-table-column>
-							<el-table-column prop="organization.orgTypeText" label="部门类别" show-overflow-tooltip></el-table-column>
-							<template #empty>
-								<Empty />
-							</template>
-						</el-table>
+						<div style="height: 400px">
+							<el-auto-resizer>
+								<template #default="{ height, width }">
+									<el-table-v2 :columns="columns" :data="tables" :width="width" :height="height" fixed />
+								</template>
+							</el-auto-resizer>
+						</div>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="state.ruleForm.circularType === 2">
-						<el-form-item label="选择部门" prop="org" :rules="[{ required: true, message: '请选择部门', trigger: 'change' }]">
-							<!--								<el-tree-select-->
-							<!--									:props="{ label: 'name' }"-->
-							<!--									node-key="id"-->
-							<!--									class="w100"-->
-							<!--									ref="treeSelectRef"-->
-							<!--									v-model="state.ruleForm.org"-->
-							<!--									:data="circularOrgOptions"-->
-							<!--									multiple-->
-							<!--									:render-after-expand="false"-->
-							<!--									show-checkbox-->
-							<!--									check-strictly-->
-							<!--									default-expand-all-->
-							<!--									filterable-->
-							<!--									@check="selectOrg"-->
-							<!--									collapse-tags-->
-							<!--									collapse-tags-tooltip-->
-							<!--									placeholder="请选择部门"-->
-							<!--								/>-->
-							<el-cascader
-								:options="circularOrgOptions"
-								filterable
-								:props="{ value: 'id', label: 'name', emitPath: false, multiple: true }"
-								placeholder="请选择部门"
-								class="w100"
-								v-model="state.ruleForm.org"
-								ref="treeSelectRef"
-								collapse-tags
-								collapse-tags-tooltip
-								:max-collapse-tags="3"
-								@change="selectOrg"
-							>
-							</el-cascader>
-						</el-form-item>
+					<el-col
+						:xs="24"
+						:sm="24"
+						:md="24"
+						:lg="24"
+						:xl="24"
+						v-if="state.ruleForm.circularType === 2"
+						style="border: var(--el-border); border-radius: var(--el-border-radius-base)"
+						class="pd15 mb20"
+					>
+						<el-input v-model="query" placeholder="部门名称" @input="onQueryChanged" clearable class="mb10" />
+						<el-tree-v2
+							:data="circularOrgOptions"
+							:props="{ value: 'id', label: 'name' }"
+							show-checkbox
+							:height="400"
+							ref="treeSelectRef"
+							:filter-method="filterMethod"
+						/>
 					</el-col>
 				</el-row>
 			</template>
@@ -168,8 +154,8 @@
 								placeholder="请选择失效时间"
 								value-format="YYYY-MM-DD[T]HH:mm:ss"
 								class="w100"
-                :disabled-date="disabledDate"
-                popper-class="no-atTheMoment"
+								:disabled-date="disabledDate"
+								popper-class="no-atTheMoment"
 							/>
 						</el-form-item>
 					</el-col>
@@ -212,13 +198,14 @@
 	</el-dialog>
 </template>
 
-<script setup lang="ts" name="noticeDetail">
-import { reactive, ref, defineAsyncComponent, computed } from 'vue';
+<script setup lang="tsx" name="noticeDetail">
+import { reactive, ref, defineAsyncComponent, computed, unref, FunctionalComponent, Ref } from 'vue';
 import '@wangeditor/editor/dist/css/style.css'; // 引入 css
 import { bulletinAdd, bulletinAddBaseData, circularAdd, circularAddBaseData, getAllUsers } from '/@/api/auxiliary/notice';
 import { throttle } from '/@/utils/tools';
-import { ElMessage, FormInstance } from 'element-plus';
-import {disabledDate} from "/@/utils/constants";
+import { CheckboxValueType, ElMessage, FormInstance } from 'element-plus';
+import { disabledDate } from '/@/utils/constants';
+import { ElCheckbox } from 'element-plus';
 // 引入组件
 const Editor = defineAsyncComponent(() => import('/@/components/Editor/index.vue')); // 富文本编辑器
 // 定义子组件向父组件传值/事件
@@ -289,39 +276,108 @@ const changeOrg = () => {
 	const currentNode = orgRef.value.getCheckedNodes();
 	state.ruleForm.sourceOrgName = currentNode[0].label;
 };
-const tables = computed(() => {
-	// 模糊搜索
-	if (!state.searchContent) return userTables.value;
-	// filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
-	// 注意: filter() 不会对空数组进行检测。
-	// 注意: filter() 不会改变原始数组。
-	return userTables.value.filter((data: any) => {
-		// some() 方法用于检测数组中的元素是否满足指定条件;
-		// some() 方法会依次执行数组的每个元素:
-		// 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
-		// 如果没有满足条件的元素,则返回false。
-		// 注意: some() 不会对空数组进行检测。
-		// 注意: some() 不会改变原始数组。
-		return Object.keys(data).some((key) => {
-			// indexOf() 返回某个指定的字符在某个字符串中首次出现的位置,如果没有找到就返回-1;
-			// 该方法对大小写敏感!所以之前需要toLowerCase()方法将所有查询到内容变为小写。
-			return String(data[key]).toLowerCase().indexOf(state.searchContent) > -1;
-		});
-	});
-});
-// 选择用户(账号)
-const multipleSelection = ref<any[]>([]); // 选中的数据
-const multipleTableRef = ref<RefType>(); // 表格ref
-const handleSelectionChange = (row: any) => {
-	if (row) {
-		multipleSelection.value = row;
-	}
+
+type SelectionCellProps = {
+  value: boolean;
+  intermediate?: boolean;
+  onChange: (value: CheckboxValueType) => void;
 };
+
+const SelectionCell: FunctionalComponent<SelectionCellProps> = ({ value, intermediate = false, onChange }) => {
+  return <ElCheckbox onChange={onChange} modelValue={value} indeterminate={intermediate} />;
+};
+const tables = computed({
+  get() {
+    // 注意: filter() 不会改变原始数组。
+    // 模糊搜索
+    if (!state.searchContent) return userTables.value;
+    // filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
+    // 注意: filter() 不会对空数组进行检测。
+    return userTables.value.filter((data: any) => {
+      // some() 方法用于检测数组中的元素是否满足指定条件;
+      // some() 方法会依次执行数组的每个元素:
+      // 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
+      // 如果没有满足条件的元素,则返回false。
+      // 注意: some() 不会对空数组进行检测。
+      // 注意: some() 不会改变原始数组。
+      return Object.keys(data).some((key) => {
+        // indexOf() 返回某个指定的字符在某个字符串中首次出现的位置,如果没有找到就返回-1;
+        // 该方法对大小写敏感!所以之前需要toLowerCase()方法将所有查询到内容变为小写。
+        return String(data[key]).toLowerCase().indexOf(state.searchContent) > -1;
+      });
+    });
+  },
+  set(val) {
+    // userTables.value = val;
+  },
+});
+const columns = [
+  {
+    key: 'selection',
+    width: 50,
+    align: 'center',
+    cellRenderer: ({ rowData }) => {
+      const onChange = (value: CheckboxValueType) => (rowData.checked = value);
+      return <SelectionCell value={rowData.checked} onChange={onChange} />;
+    },
+    headerCellRenderer: () => {
+      const _data = unref(tables);
+      const onChange = (value: CheckboxValueType) =>
+          (tables.value = _data.map((row) => {
+            row.checked = value;
+            return row;
+          }));
+      const allSelected = _data.every((row) => row.checked);
+      const containsChecked = _data.some((row) => row.checked);
+
+      return <SelectionCell value={allSelected} intermediate={containsChecked && !allSelected} onChange={onChange} />;
+    },
+  },
+  {
+    key: 'name',
+    dataKey: 'name',
+    title: '姓名',
+    width: 300,
+    align: 'center',
+  },
+  {
+    key: 'name',
+    dataKey: 'userName',
+    title: '账号',
+    width: 150,
+    align: 'center',
+  },
+  {
+    key: 'organization.name',
+    dataKey: 'organization.name',
+    title: '所属部门',
+    width: 150,
+    align: 'center',
+  },
+  {
+    key: 'staffNo',
+    dataKey: 'staffNo',
+    title: '工号',
+    width: 100,
+    align: 'center',
+  },
+  {
+    key: 'organization.orgTypeText',
+    dataKey: 'organization.orgTypeText',
+    title: '部门类别',
+    width: 100,
+    align: 'center',
+  },
+];
+
+const query = ref('');
 // 选择部门
 const treeSelectRef = ref<RefType>();
-const selectOrgArray = ref<EmptyArrayType>([]);
-const selectOrg = () => {
-	selectOrgArray.value = treeSelectRef.value.getCheckedNodes();
+const onQueryChanged = (query: string) => {
+  treeSelectRef.value!.filter(query);
+};
+const filterMethod = (query: string, node: any) => {
+  return node.name!.includes(query);
 };
 // 保存
 const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
@@ -331,18 +387,22 @@ const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
 		loading.value = true;
 		switch (dialogTitle.value) {
 			case '新增通知':
-				if (state.ruleForm.circularType === 1 && !multipleSelection.value.length) {
-					ElMessage.error('请选择通知人');
+				const checkedTable = tables.value?.filter((item: any) => item.checked).map((item: any) => item);
+				const nodes = treeSelectRef.value?.getCheckedNodes();
+				if (state.ruleForm.circularType === 1 && !checkedTable.length) {
+					ElMessage.warning('请选择通知人');
+          loading.value = false;
+					return;
+				}
+				if (state.ruleForm.circularType === 2 && !nodes.length) {
+					ElMessage.warning('请选择通知部门');
+          loading.value = false;
 					return;
 				}
-        if (state.ruleForm.circularType === 2 && !multipleSelection.value.length) {
-          ElMessage.error('请选择通知部门');
-          return;
-        }
 				let circularReadGroups: { userId: any; userName: any }[] | { orgId: any; orgName: any }[] = [];
 				if (state.ruleForm.circularType === 1) {
 					//个人
-					circularReadGroups = multipleSelection.value.map((item: any) => {
+					circularReadGroups = checkedTable.map((item: any) => {
 						return {
 							userId: item.id,
 							userName: item.name,
@@ -350,11 +410,11 @@ const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
 					});
 				} else if (state.ruleForm.circularType === 2) {
 					// 部门
-					circularReadGroups = selectOrgArray.value.map((item: any) => {
-            return {
-              orgId: item.value,
-              orgName: item.label,
-            };
+					circularReadGroups = nodes.map((item: any) => {
+						return {
+							orgId: item.id,
+							orgName: item.name,
+						};
 					});
 				}
 				const circularRequest = {
@@ -367,7 +427,7 @@ const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
 					lostEfficacyTime: state.ruleForm.lostEfficacyTime,
 					sourceOrgId: state.ruleForm.sourceOrgId,
 					sourceOrgName: state.ruleForm.sourceOrgName,
-					circularReadGroups,
+          circularReadGroups,
 				};
 				circularAdd(circularRequest)
 					.then(() => {
@@ -416,7 +476,7 @@ const closeDialog = () => {
 	state.dialogVisible = false;
 };
 const close = () => {
-  ruleFormRef.value?.clearValidate();
+	ruleFormRef.value?.clearValidate();
 	ruleFormRef.value?.resetFields();
 };
 // 暴露变量

+ 169 - 86
src/views/auxiliary/notice/component/Notice-edit.vue

@@ -43,7 +43,7 @@
 								value-format="YYYY-MM-DD[T]HH:mm:ss"
 								class="w100"
 								:disabled-date="disabledDate"
-                popper-class="no-atTheMoment"
+								popper-class="no-atTheMoment"
 							/>
 						</el-form-item>
 					</el-col>
@@ -64,56 +64,60 @@
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 						<el-form-item label="通知内容" prop="content" :rules="[{ required: true, message: '请输入通知内容', trigger: 'blur' }]">
-							<editor v-model:get-html="state.ruleForm.content" placeholder="请输入通知内容" height="450px" />
+							<editor v-model:get-html="state.ruleForm.content" placeholder="请输入通知内容" height="350px" />
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
 						<el-form-item label="通知对象" prop="circularType" :rules="[{ required: true, message: '请选择通知对象', trigger: 'change' }]">
-							<el-select v-model="state.ruleForm.circularType" placeholder="请选择通知对象" class="w100">
-								<el-option v-for="item in circularTypeEnum" :value="item.key" :key="item.key" :label="item.value" />
-							</el-select>
+              <el-radio-group v-model="state.ruleForm.circularType">
+                <el-radio :label="item.key" v-for="item in circularTypeEnum" :key="item.key">{{item.value}}</el-radio>
+              </el-radio-group>
 						</el-form-item>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" v-if="state.ruleForm.circularType === 1">
-						<el-divider content-position="left">
-							<span>选择通知人</span>
-						</el-divider>
+					<el-col
+						:xs="24"
+						:sm="24"
+						:md="24"
+						:lg="24"
+						:xl="24"
+						v-if="state.ruleForm.circularType === 1"
+						style="border: var(--el-border); border-radius: var(--el-border-radius-base)"
+						class="pd15 mb20"
+					>
 						<div class="flex-center-align mb10">
-							<el-input v-model="state.searchContent" placeholder="请输入搜索内容" class="w100" clearable>
+							<el-input v-model="state.searchContent" placeholder="通知人姓名/账号" class="w100" clearable>
 								<template #prefix>
 									<SvgIcon name="ele-Search" />
 								</template>
 							</el-input>
 						</div>
-						<el-table :data="tables" @selection-change="handleSelectionChange" max-height="500" ref="multipleTableRef" row-key="id">
-							<el-table-column type="selection" label="请选择" width="40" :reserve-selection="true" align="center" />
-							<el-table-column prop="name" label="姓名" show-overflow-tooltip width="170"></el-table-column>
-							<el-table-column prop="userName" label="账号" show-overflow-tooltip width="170"></el-table-column>
-							<el-table-column prop="organization.name" label="所属部门" show-overflow-tooltip width="190"></el-table-column>
-							<el-table-column prop="staffNo" label="工号" show-overflow-tooltip width="120"></el-table-column>
-							<el-table-column prop="organization.orgTypeText" label="部门类别" show-overflow-tooltip></el-table-column>
-							<template #empty>
-								<Empty />
-							</template>
-						</el-table>
+						<div style="height: 400px">
+							<el-auto-resizer>
+								<template #default="{ height, width }">
+									<el-table-v2 :columns="columns" :data="tables" :width="width" :height="height" fixed />
+								</template>
+							</el-auto-resizer>
+						</div>
 					</el-col>
-					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="state.ruleForm.circularType === 2">
-						<el-form-item label="选择部门" prop="org" :rules="[{ required: true, message: '请选择部门', trigger: 'change' }]">
-							<el-cascader
-								:options="circularOrgOptions"
-								filterable
-								:props="{ value: 'id', label: 'name', emitPath: false, multiple: true }"
-								placeholder="请选择部门"
-								class="w100"
-								v-model="state.ruleForm.org"
-								ref="treeSelectRef"
-								collapse-tags
-								collapse-tags-tooltip
-								:max-collapse-tags="3"
-								@change="selectOrg"
-							>
-							</el-cascader>
-						</el-form-item>
+					<el-col
+						:xs="24"
+						:sm="24"
+						:md="24"
+						:lg="24"
+						:xl="24"
+						v-if="state.ruleForm.circularType === 2"
+						style="border: var(--el-border); border-radius: var(--el-border-radius-base)"
+						class="pd15 mb20"
+					>
+						<el-input v-model="query" placeholder="部门名称" @input="onQueryChanged" clearable class="mb10" />
+						<el-tree-v2
+							:data="circularOrgOptions"
+							:props="{ value: 'id', label: 'name' }"
+							show-checkbox
+							:height="400"
+							ref="treeSelectRef"
+							:filter-method="filterMethod"
+						/>
 					</el-col>
 				</el-row>
 			</template>
@@ -151,7 +155,7 @@
 								value-format="YYYY-MM-DD[T]HH:mm:ss"
 								class="w100"
 								:disabled-date="disabledDate"
-                popper-class="no-atTheMoment"
+								popper-class="no-atTheMoment"
 							/>
 						</el-form-item>
 					</el-col>
@@ -179,7 +183,7 @@
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 						<el-form-item label="公告内容" prop="content" :rules="[{ required: true, message: '请输入公告内容', trigger: 'blur' }]">
-							<editor v-model:get-html="state.ruleForm.content" placeholder="请输入公告内容" height="450px" />
+							<editor v-model:get-html="state.ruleForm.content" placeholder="请输入公告内容" height="350px" />
 						</el-form-item>
 					</el-col>
 				</el-row>
@@ -194,8 +198,8 @@
 	</el-dialog>
 </template>
 
-<script setup lang="ts" name="noticeDetail">
-import { reactive, ref, defineAsyncComponent, computed } from 'vue';
+<script setup lang="tsx" name="noticeDetail">
+import { reactive, ref, defineAsyncComponent, computed, FunctionalComponent, unref, nextTick } from 'vue';
 import '@wangeditor/editor/dist/css/style.css'; // 引入 css
 import {
 	bulletinAddBaseData,
@@ -207,7 +211,7 @@ import {
 	bulletinUpdate,
 } from '/@/api/auxiliary/notice';
 import { throttle } from '/@/utils/tools';
-import { ElMessage, FormInstance } from 'element-plus';
+import { CheckboxValueType, ElMessage, FormInstance } from 'element-plus';
 import { disabledDate } from '/@/utils/constants';
 // 引入组件
 const Editor = defineAsyncComponent(() => import('/@/components/Editor/index.vue')); // 富文本编辑器
@@ -234,7 +238,7 @@ const ruleFormRef = ref<RefType>();
 const dialogTitle = ref<string>('编辑通知');
 
 const bulletinTypeOptions = ref<EmptyArrayType>([]); // 公告类型
-const circularTypeOptions = ref<EmptyArrayType>([]); // 通知类型]
+const circularTypeOptions = ref<EmptyArrayType>([]); // 通知类型
 const circularTypeEnum = ref<EmptyArrayType>([]); // 通知对象
 const pushRangesOptions = ref<EmptyArrayType>([]); // 公告范围
 const orgsOptions = ref<EmptyArrayType>([]); // 来源单位
@@ -265,21 +269,23 @@ const openDialog = async (row: any, type: string) => {
 				};
 				if (state.ruleForm.circularType === 1) {
 					// 个人
-					multipleSelection.value = state.ruleForm.circularReadGroups;
-					for (let i of multipleSelection.value) {
-						for (let j of tables.value) {
+					for (let i of state.ruleForm.circularReadGroups) {
+						for (let j of userTables.value) {
 							if (i.userId === j.id) {
-								setTimeout(() => {
-									multipleTableRef.value!.toggleRowSelection(j, true);
-								}, 0);
+								j.checked = true;
 							}
 						}
 					}
 				} else if (state.ruleForm.circularType === 2) {
 					// 部门
-					state.ruleForm.org = state.ruleForm.circularReadGroups.map((item: any) => {
+					await nextTick();
+					const keys = state.ruleForm.circularReadGroups.map((item: any) => {
 						return item.orgId;
 					});
+					treeSelectRef.value.setExpandedKeys(keys);
+					setTimeout(() => {
+						treeSelectRef.value.setCheckedKeys(keys);
+					}, 100);
 				}
 				loading.value = false;
 			} catch (error) {
@@ -322,39 +328,108 @@ const changeOrg = (type: string) => {
 	const currentNode = orgRef.value.getCheckedNodes();
 	state[type].sourceOrgName = currentNode[0].label;
 };
-const tables = computed(() => {
-	// 模糊搜索
-	if (!state.searchContent) return userTables.value;
-	// filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
-	// 注意: filter() 不会对空数组进行检测。
-	// 注意: filter() 不会改变原始数组。
-	return userTables.value.filter((data: any) => {
-		// some() 方法用于检测数组中的元素是否满足指定条件;
-		// some() 方法会依次执行数组的每个元素:
-		// 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
-		// 如果没有满足条件的元素,则返回false。
-		// 注意: some() 不会对空数组进行检测。
-		// 注意: some() 不会改变原始数组。
-		return Object.keys(data).some((key) => {
-			// indexOf() 返回某个指定的字符在某个字符串中首次出现的位置,如果没有找到就返回-1;
-			// 该方法对大小写敏感!所以之前需要toLowerCase()方法将所有查询到内容变为小写。
-			return String(data[key]).toLowerCase().indexOf(state.searchContent) > -1;
+
+type SelectionCellProps = {
+	value: boolean;
+	intermediate?: boolean;
+	onChange: (value: CheckboxValueType) => void;
+};
+
+const SelectionCell: FunctionalComponent<SelectionCellProps> = ({ value, intermediate = false, onChange }) => {
+	return <ElCheckbox onChange={onChange} modelValue={value} indeterminate={intermediate} />;
+};
+const tables = computed({
+	get() {
+		// 注意: filter() 不会改变原始数组。
+		// 模糊搜索
+		if (!state.searchContent) return userTables.value;
+		// filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
+		// 注意: filter() 不会对空数组进行检测。
+		return userTables.value.filter((data: any) => {
+			// some() 方法用于检测数组中的元素是否满足指定条件;
+			// some() 方法会依次执行数组的每个元素:
+			// 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
+			// 如果没有满足条件的元素,则返回false。
+			// 注意: some() 不会对空数组进行检测。
+			// 注意: some() 不会改变原始数组。
+			return Object.keys(data).some((key) => {
+				// indexOf() 返回某个指定的字符在某个字符串中首次出现的位置,如果没有找到就返回-1;
+				// 该方法对大小写敏感!所以之前需要toLowerCase()方法将所有查询到内容变为小写。
+				return String(data[key]).toLowerCase().indexOf(state.searchContent) > -1;
+			});
 		});
-	});
+	},
+	set(val) {
+		// userTables.value = val;
+	},
 });
-// 选择用户(账号)
-const multipleSelection = ref<any[]>([]); // 选中的数据
-const multipleTableRef = ref<RefType>(); // 表格ref
-const handleSelectionChange = (row: any) => {
-	if (row) {
-		multipleSelection.value = row;
-	}
-};
+const columns = [
+	{
+		key: 'selection',
+		width: 50,
+		align: 'center',
+		cellRenderer: ({ rowData }) => {
+			const onChange = (value: CheckboxValueType) => (rowData.checked = value);
+			return <SelectionCell value={rowData.checked} onChange={onChange} />;
+		},
+		headerCellRenderer: () => {
+			const _data = unref(tables);
+			const onChange = (value: CheckboxValueType) =>
+				(tables.value = _data.map((row) => {
+					row.checked = value;
+					return row;
+				}));
+			const allSelected = _data.every((row) => row.checked);
+			const containsChecked = _data.some((row) => row.checked);
+
+			return <SelectionCell value={allSelected} intermediate={containsChecked && !allSelected} onChange={onChange} />;
+		},
+	},
+	{
+		key: 'name',
+		dataKey: 'name',
+		title: '姓名',
+		width: 300,
+		align: 'center',
+	},
+	{
+		key: 'name',
+		dataKey: 'userName',
+		title: '账号',
+		width: 150,
+		align: 'center',
+	},
+	{
+		key: 'organization.name',
+		dataKey: 'organization.name',
+		title: '所属部门',
+		width: 150,
+		align: 'center',
+	},
+	{
+		key: 'staffNo',
+		dataKey: 'staffNo',
+		title: '工号',
+		width: 100,
+		align: 'center',
+	},
+	{
+		key: 'organization.orgTypeText',
+		dataKey: 'organization.orgTypeText',
+		title: '部门类别',
+		width: 100,
+		align: 'center',
+	},
+];
+
+const query = ref('');
 // 选择部门
 const treeSelectRef = ref<RefType>();
-const selectOrgArray = ref<EmptyArrayType>([]);
-const selectOrg = () => {
-	selectOrgArray.value = treeSelectRef.value.getCheckedNodes();
+const onQueryChanged = (query: string) => {
+	treeSelectRef.value!.filter(query);
+};
+const filterMethod = (query: string, node: any) => {
+	return node.name!.includes(query);
 };
 // 保存
 const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
@@ -364,14 +439,22 @@ const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
 		loading.value = true;
 		switch (dialogTitle.value) {
 			case '编辑通知':
-				if (state.ruleForm.circularType === 1 && !multipleSelection.value.length) {
-					ElMessage.error('请选择通知人');
+				const checkedTable = tables.value?.filter((item: any) => item.checked).map((item: any) => item);
+				const nodes = treeSelectRef.value?.getCheckedNodes();
+				if (state.ruleForm.circularType === 1 && !checkedTable.length) {
+					ElMessage.warning('请选择通知人');
+					loading.value = false;
+					return;
+				}
+				if (state.ruleForm.circularType === 2 && !nodes.length) {
+					ElMessage.warning('请选择通知部门');
+					loading.value = false;
 					return;
 				}
 				let circularReadGroups: { userId: any; userName: any }[] | { orgId: any; orgName: any }[] = [];
 				if (state.ruleForm.circularType === 1) {
 					//个人
-					circularReadGroups = multipleSelection.value.map((item: any) => {
+					circularReadGroups = checkedTable.map((item: any) => {
 						return {
 							userId: item.id,
 							userName: item.name,
@@ -379,10 +462,10 @@ const onSubmit = throttle(async (formEl: FormInstance | undefined) => {
 					});
 				} else if (state.ruleForm.circularType === 2) {
 					// 部门
-					circularReadGroups = selectOrgArray.value.map((item: any) => {
+					circularReadGroups = nodes.map((item: any) => {
 						return {
-							orgId: item.value,
-							orgName: item.label,
+							orgId: item.id,
+							orgName: item.name,
 						};
 					});
 				}

+ 1 - 1
src/views/auxiliary/notice/detail.vue

@@ -183,7 +183,7 @@ const closePage = () => {
 	mittBus.emit('clearCache', 'auxiliaryNotice');
 	if (noticeType.value === '通知详情') {
 		router.push({
-			path: '/auxiliary/noticeAudit',
+			path: '/auxiliary/notice',
 		});
 	} else if (noticeType.value === '公告详情') {
 		if (!router.hasRoute('auxiliaryNotice')) {

+ 15 - 7
src/views/business/special/components/Special-apply-order.vue

@@ -41,20 +41,20 @@
               </el-form-item>
             </el-col>
             <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+
               <el-form-item label="办理对象" prop="nextHandlers" :rules="[{ required: true, message: '请选择办理对象', trigger: 'change' }]">
-                <el-select
+                <el-select-v2
                     v-model="state.ruleForm.nextHandlers"
+                    :options="stepsItems"
                     placeholder="请选择办理对象"
-                    value-key="key"
+                    class="w100"
+                    multiple
                     clearable
                     collapse-tags
                     collapse-tags-tooltip
-                    multiple
                     filterable
-                    class="w100"
-                >
-                  <el-option v-for="item in stepsItems" :value="item" :key="item.key" :label="item.value"> </el-option>
-                </el-select>
+                    value-key="key"
+                />
               </el-form-item>
             </el-col>
           </template>
@@ -156,6 +156,14 @@ const selectTrace = (val: any) => {
   state.ruleForm.nextStepName = step?.value;
   ruleFormRef.value?.resetFields('nextHandlers');
   stepsItems.value = step?.items ?? [];
+  stepsItems.value = stepsItems.value.map((item: any) => {
+    return {
+      value: {
+        ...item,
+      },
+      label: item.value,
+    };
+  })
   state.ruleForm.flowDirection = step?.flowDirection;
 	queryHandleTime();
 };

+ 20 - 13
src/views/business/special/components/Special-apply.vue

@@ -27,19 +27,18 @@
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="state.ruleForm.nextStepCode">
 						<el-form-item label="办理对象" prop="nextHandlers" :rules="[{ required: true, message: '请选择办理对象', trigger: 'change' }]">
-							<el-select
-								v-model="state.ruleForm.nextHandlers"
-								placeholder="请选择办理对象"
-								value-key="key"
-								clearable
-								collapse-tags
-								collapse-tags-tooltip
-								multiple
-								filterable
-								class="w100"
-							>
-								<el-option v-for="item in stepsItems" :value="item" :key="item.key" :label="item.value"> </el-option>
-							</el-select>
+              <el-select-v2
+                  v-model="state.ruleForm.nextHandlers"
+                  :options="stepsItems"
+                  placeholder="请选择办理对象"
+                  class="w100"
+                  multiple
+                  clearable
+                  collapse-tags
+                  collapse-tags-tooltip
+                  filterable
+                  value-key="key"
+              />
 						</el-form-item>
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
@@ -140,6 +139,14 @@ const selectTrace = (val: any) => {
 	state.ruleForm.nextStepName = step?.value;
 	ruleFormRef.value?.resetFields('nextHandlers');
 	stepsItems.value = step?.items ?? [];
+  stepsItems.value = stepsItems.value.map((item: any) => {
+    return {
+      value: {
+        ...item,
+      },
+      label: item.value,
+    };
+  })
 };
 // 提交
 const filesFormat = ref<EmptyArrayType>([]); // 附件列表

+ 46 - 39
src/views/business/special/components/Special-audit.vue

@@ -30,7 +30,7 @@
 					</el-col>
 					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
 						<el-form-item label="附件" class="mb5">
-							<annex-list name="特提附件" readonly classify="特提申请" v-model="state.detail.files"/>
+							<annex-list name="特提附件" readonly classify="特提申请" v-model="state.detail.files" />
 						</el-form-item>
 					</el-col>
 					<el-divider></el-divider>
@@ -81,19 +81,18 @@
 							</el-col>
 							<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
 								<el-form-item label="办理对象" prop="nextHandlers" :rules="[{ required: true, message: '请选择办理对象', trigger: 'change' }]">
-									<el-select
+									<el-select-v2
 										v-model="state.ruleForm.nextHandlers"
+										:options="stepsItems"
 										placeholder="请选择办理对象"
-										value-key="key"
+										class="w100"
+										multiple
 										clearable
 										collapse-tags
 										collapse-tags-tooltip
-										multiple
 										filterable
-										class="w100"
-									>
-										<el-option v-for="item in stepsItems" :value="item" :key="item.key" :label="item.value"> </el-option>
-									</el-select>
+										value-key="key"
+									/>
 								</el-form-item>
 							</el-col>
 						</template>
@@ -126,7 +125,7 @@ import { storeToRefs } from 'pinia';
 import { useUserInfo } from '/@/stores/userInfo';
 import { formatDate } from '/@/utils/formatTime';
 import { specialApplyBase, specialApprove, specialDetail, specialTime } from '/@/api/business/special';
-import {transformFile} from "/@/utils/tools";
+import { transformFile } from '/@/utils/tools';
 
 // 引入组件
 const AnnexList = defineAsyncComponent(() => import('/@/components/AnnexList/index.vue'));
@@ -166,11 +165,11 @@ const openDialog = async (val: any) => {
 		steps.value = baseRes.result?.step?.steps ?? [];
 		specialTimeType.value = baseRes.result.specialTimeType ?? [];
 		state.detail = detailRes.result;
-    state.detail.files = transformFile(detailRes.result.files);
-    state.ruleForm.nextStepCode = detailRes.result.nextStepCode;
-    state.ruleForm.nextStepName = detailRes.result.nextStepName;
-    selectTrace(detailRes.result.nextStepCode)
-    state.ruleForm.nextHandlers = detailRes.result.nextHandlers;
+		state.detail.files = transformFile(detailRes.result.files);
+		state.ruleForm.nextStepCode = detailRes.result.nextStepCode;
+		state.ruleForm.nextStepName = detailRes.result.nextStepName;
+		selectTrace(detailRes.result.nextStepCode);
+		state.ruleForm.nextHandlers = detailRes.result.nextHandlers;
 	} catch (e) {
 		console.log(e);
 	} finally {
@@ -194,6 +193,14 @@ const selectTrace = (val: any) => {
 	state.ruleForm.nextStepName = step?.value;
 	ruleFormRef.value?.resetFields('nextHandlers');
 	stepsItems.value = step?.items ?? [];
+	stepsItems.value = stepsItems.value.map((item: any) => {
+		return {
+			value: {
+				...item,
+			},
+			label: item.value,
+		};
+	});
 	state.ruleForm.flowDirection = step?.flowDirection;
 	queryHandleTime();
 };
@@ -215,31 +222,31 @@ const onAudit = (formEl: FormInstance | undefined) => {
 	formEl.validate((valid: boolean) => {
 		if (!valid) return;
 		state.loading = true;
-    let request = {};
-    if(state.ruleForm.isPass){
-      request = {
-        id: state.detail.id,
-        orderId: state.detail.orderId,
-        timeLimit: state.ruleForm.timeLimit,
-        timeLimitUnit: state.ruleForm.timeLimitUnit,
-        cause: state.detail.cause,
-        opinion: state.ruleForm.opinion,
-        workflowId: state.workflowId,
-        state: 1, // 审核结果 0 待审核 1 审核通过 2 审核不通过
-        nextStepCode: state.ruleForm.nextStepCode,
-        nextStepName: state.ruleForm.nextStepName,
-        nextHandlers: state.ruleForm.nextHandlers,
-      };
-    }else{
-      request = {
-        id: state.detail.id,
-        orderId: state.detail.orderId,
-        cause: state.detail.cause,
-        opinion: state.ruleForm.opinion,
-        workflowId: state.workflowId,
-        state: 2, // 审核结果 0 待审核 1 审核通过 2 审核不通过
-      }
-    }
+		let request: {};
+		if (state.ruleForm.isPass) {
+			request = {
+				id: state.detail.id,
+				orderId: state.detail.orderId,
+				timeLimit: state.ruleForm.timeLimit,
+				timeLimitUnit: state.ruleForm.timeLimitUnit,
+				cause: state.detail.cause,
+				opinion: state.ruleForm.opinion,
+				workflowId: state.workflowId,
+				state: 1, // 审核结果 0 待审核 1 审核通过 2 审核不通过
+				nextStepCode: state.ruleForm.nextStepCode,
+				nextStepName: state.ruleForm.nextStepName,
+				nextHandlers: state.ruleForm.nextHandlers,
+			};
+		} else {
+			request = {
+				id: state.detail.id,
+				orderId: state.detail.orderId,
+				cause: state.detail.cause,
+				opinion: state.ruleForm.opinion,
+				workflowId: state.workflowId,
+				state: 2, // 审核结果 0 待审核 1 审核通过 2 审核不通过
+			};
+		}
 		specialApprove(request)
 			.then(() => {
 				state.loading = false;

+ 2 - 0
tsconfig.json

@@ -10,6 +10,8 @@
 		// "allowJs": true,                       /* Allow javascript files to be compiled. */
 		// "checkJs": true,                       /* Report errors in .js files. */
 		"jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
+		"jsxFactory": "h",
+		"jsxFragmentFactory": "Fragment",
 		// "declaration": true /* Generates corresponding '.d.ts' file. */,
 		// "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
 		// "sourceMap": true,                     /* Generates corresponding '.map' file. */

+ 6 - 1
vite.config.ts

@@ -3,6 +3,7 @@ import { resolve } from 'path';
 import { defineConfig, loadEnv, ConfigEnv } from 'vite';
 import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'; // setup语法糖设置name
 import viteCompression from 'vite-plugin-compression'; //开启gzip打包压缩
+import vueJsx from '@vitejs/plugin-vue-jsx'
 const pathResolve = (dir: string): any => {
 	return resolve(__dirname, '.', dir);
 };
@@ -10,13 +11,17 @@ const pathResolve = (dir: string): any => {
 const alias: Record<string, string> = {
 	'/@': pathResolve('./src/'),
 };
+// @ts-ignore
 export default defineConfig((mode: ConfigEnv) => {
 	const env = loadEnv(mode.mode, process.cwd());
 	return {
 		plugins: [
 			vue(),
 			vueSetupExtend(),
-			viteCompression()
+			viteCompression(),
+			vueJsx({
+				// options are passed on to @vue/babel-plugin-jsx
+			}),
 		],
 		root: process.cwd(),
 		resolve: { alias },

File diff suppressed because it is too large
+ 519 - 173
yarn.lock


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