Commit b1e46c76c58ef3a52b5d50583c3a5be7a4ed6020

Authored by ww
1 parent 42a1080c

perf: 优化账号管理组织树回显

@@ -54,7 +54,7 @@ export const basicProps = { @@ -54,7 +54,7 @@ export const basicProps = {
54 }, 54 },
55 55
56 checkedKeys: { 56 checkedKeys: {
57 - type: Array as PropType<CheckKeys>, 57 + type: [Array, Object] as PropType<CheckKeys>,
58 default: () => [], 58 default: () => [],
59 }, 59 },
60 60
@@ -14,9 +14,10 @@ @@ -14,9 +14,10 @@
14 </Button> 14 </Button>
15 <BasicTree 15 <BasicTree
16 v-if="organizationTreeData.length" 16 v-if="organizationTreeData.length"
  17 + :check-strictly="checkStrictly"
17 v-model:value="model[field]" 18 v-model:value="model[field]"
18 :treeData="organizationTreeData" 19 :treeData="organizationTreeData"
19 - :checked-keys="checkGroup" 20 + :checked-keys="checkedKeys"
20 :expandedKeys="treeExpandData" 21 :expandedKeys="treeExpandData"
21 ref="basicTreeRef" 22 ref="basicTreeRef"
22 @check="handleCheckClick" 23 @check="handleCheckClick"
@@ -25,6 +26,7 @@ @@ -25,6 +26,7 @@
25 checkable 26 checkable
26 toolbar 27 toolbar
27 @change="handleTreeSelect" 28 @change="handleTreeSelect"
  29 + :replace-fields="{ children: 'children', title: 'name', key: 'id' }"
28 /> 30 />
29 </template> 31 </template>
30 <template #roleSlot="{ model, field }"> 32 <template #roleSlot="{ model, field }">
@@ -53,8 +55,8 @@ @@ -53,8 +55,8 @@
53 </BasicModal> 55 </BasicModal>
54 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" /> 56 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" />
55 </template> 57 </template>
56 -<script lang="ts">  
57 - import { defineComponent, ref, computed, unref, reactive, onMounted } from 'vue'; 58 +<script lang="ts" setup>
  59 + import { ref, computed, unref, reactive, onMounted, toRaw } from 'vue';
58 import { BasicModal, useModalInner } from '/@/components/Modal'; 60 import { BasicModal, useModalInner } from '/@/components/Modal';
59 import { BasicForm, useForm } from '/@/components/Form/index'; 61 import { BasicForm, useForm } from '/@/components/Form/index';
60 import { accountFormSchema } from './account.data'; 62 import { accountFormSchema } from './account.data';
@@ -64,262 +66,264 @@ @@ -64,262 +66,264 @@
64 SaveOrUpdateUserInfo, 66 SaveOrUpdateUserInfo,
65 filterRoleList, 67 filterRoleList,
66 } from '/@/api/system/system'; 68 } from '/@/api/system/system';
67 - import { BasicTree, TreeItem, CheckKeys, CheckEvent } from '/@/components/Tree'; 69 + import { BasicTree, TreeItem, CheckKeys } from '/@/components/Tree';
68 import { findCurrentUserGroups } from '/@/api/system/group'; 70 import { findCurrentUserGroups } from '/@/api/system/group';
69 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel'; 71 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel';
70 import { useMessage } from '/@/hooks/web/useMessage'; 72 import { useMessage } from '/@/hooks/web/useMessage';
71 - import { copyTransTreeFun } from '/@/utils/fnUtils';  
72 import { TOption } from '/@/views/rule/linkedge/config/config.data'; 73 import { TOption } from '/@/views/rule/linkedge/config/config.data';
73 import { PlusOutlined } from '@ant-design/icons-vue'; 74 import { PlusOutlined } from '@ant-design/icons-vue';
74 import { useDrawer } from '/@/components/Drawer'; 75 import { useDrawer } from '/@/components/Drawer';
75 import RoleDrawer from '../../role/CustomRoleDrawer.vue'; 76 import RoleDrawer from '../../role/CustomRoleDrawer.vue';
76 import OrganizationDrawer from '/@/views/system/organization/OrganizationDrawer.vue'; 77 import OrganizationDrawer from '/@/views/system/organization/OrganizationDrawer.vue';
  78 + import { GroupListResultModel } from '/@/api/system/model/groupModel';
  79 + import { isArray } from '/@/utils/is';
77 80
78 - export default defineComponent({  
79 - name: 'AccountModal',  
80 - components: {  
81 - BasicModal,  
82 - BasicForm,  
83 - Button,  
84 - BasicTree,  
85 - OrganizationDrawer,  
86 - PlusOutlined,  
87 - RoleDrawer,  
88 - VNodes: (_, { attrs }) => {  
89 - return attrs.vnodes;  
90 - },  
91 - },  
92 - emits: ['success', 'register'],  
93 - setup(_, { emit }) {  
94 - const roleOptions = ref<TOption[]>([]);  
95 - const isUpdate = ref(true);  
96 - const rowId = ref('');  
97 - const organizationTreeData = ref<TreeItem[]>([]);  
98 - const basicTreeRef = ref();  
99 - const checkGroup = ref<string[]>([]);  
100 - const treeExpandData = ref([]);  
101 - const olderPhoneNumber = ref();  
102 - const postData = reactive({});  
103 - const singleEditPostPhoneNumber = reactive({  
104 - phoneNumber: '',  
105 - });  
106 - const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);  
107 - const getRoleList = async () => {  
108 - const res = await filterRoleList();  
109 - roleOptions.value = res.map((m) => {  
110 - return {  
111 - label: m.name,  
112 - value: m.id,  
113 - };  
114 - });  
115 - };  
116 - onMounted(async () => {  
117 - await getRoleList();  
118 - });  
119 - const [registerRoleDrawer, { openDrawer }] = useDrawer(); 81 + const VNodes = (_, { attrs }) => {
  82 + return attrs.vnodes;
  83 + };
120 84
121 - const handleOpenRole = () => {  
122 - openDrawer(true, {  
123 - isUpdate: false,  
124 - });  
125 - };  
126 - const clearValidateByField = (field: string) => {  
127 - clearValidate(field);  
128 - };  
129 - const handleRoleSelect = (e) => {  
130 - if (e?.length > 0) clearValidateByField('roleIds');  
131 - else validateFields(['roleIds']);  
132 - };  
133 - const handleTreeSelect = (e) => {  
134 - if (e) clearValidateByField('organizationIds');  
135 - };  
136 - const handleSuccess = async () => {  
137 - await getRoleList(); 85 + const emit = defineEmits(['register', 'success']);
  86 +
  87 + const checkStrictly = ref(true);
  88 + const roleOptions = ref<TOption[]>([]);
  89 + const isUpdate = ref(true);
  90 + const rowId = ref('');
  91 + const organizationTreeData = ref<TreeItem[]>([]);
  92 + const basicTreeRef = ref();
  93 + const checkedKeys = reactive<CheckKeys>({ checked: [], halfChecked: [] });
  94 + const treeExpandData = ref([]);
  95 + const olderPhoneNumber = ref();
  96 + const postData = reactive({});
  97 + const singleEditPostPhoneNumber = reactive({
  98 + phoneNumber: '',
  99 + });
  100 + const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);
  101 + const getRoleList = async () => {
  102 + const res = await filterRoleList();
  103 + roleOptions.value = res.map((m) => {
  104 + return {
  105 + label: m.name,
  106 + value: m.id,
138 }; 107 };
139 - const [  
140 - registerForm,  
141 - {  
142 - setFieldsValue,  
143 - updateSchema,  
144 - resetFields,  
145 - validate,  
146 - getFieldsValue,  
147 - clearValidate,  
148 - validateFields,  
149 - },  
150 - ] = useForm({  
151 - labelWidth: 100,  
152 - schemas: accountFormSchema,  
153 - showActionButtonGroup: false,  
154 - actionColOptions: {  
155 - span: 18,  
156 - }, 108 + });
  109 + };
  110 + onMounted(async () => {
  111 + await getRoleList();
  112 + });
  113 + const [registerRoleDrawer, { openDrawer }] = useDrawer();
  114 +
  115 + const handleOpenRole = () => {
  116 + openDrawer(true, {
  117 + isUpdate: false,
  118 + });
  119 + };
  120 + const clearValidateByField = (field: string) => {
  121 + clearValidate(field);
  122 + };
  123 + const handleRoleSelect = (e) => {
  124 + if (e?.length > 0) clearValidateByField('roleIds');
  125 + else validateFields(['roleIds']);
  126 + };
  127 + const handleTreeSelect = (e) => {
  128 + if (e) clearValidateByField('organizationIds');
  129 + };
  130 + const handleSuccess = async () => {
  131 + await getRoleList();
  132 + };
  133 + const [
  134 + registerForm,
  135 + {
  136 + setFieldsValue,
  137 + updateSchema,
  138 + resetFields,
  139 + validate,
  140 + getFieldsValue,
  141 + clearValidate,
  142 + validateFields,
  143 + },
  144 + ] = useForm({
  145 + labelWidth: 100,
  146 + schemas: accountFormSchema,
  147 + showActionButtonGroup: false,
  148 + actionColOptions: {
  149 + span: 18,
  150 + },
  151 + });
  152 + //获取所有父级id
  153 + function findForAllId(data = [], arr = []) {
  154 + for (const item of data) {
  155 + arr.push(item.id);
  156 + }
  157 + return arr;
  158 + }
  159 +
  160 + const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  161 + checkStrictly.value = true;
  162 + await resetFields();
  163 + setModalProps({ confirmLoading: false });
  164 + isUpdate.value = !!data?.isUpdate;
  165 + const groupListModel = await findCurrentUserGroups();
  166 + if (!unref(organizationTreeData).length) {
  167 + organizationTreeData.value = groupListModel;
  168 + buildNodeMap(toRaw(unref(groupListModel)));
  169 + const getAllIds = findForAllId(organizationTreeData.value as any, []);
  170 + //设置要展开的id
  171 + treeExpandData.value = getAllIds;
  172 + }
  173 + if (unref(isUpdate)) {
  174 + rowId.value = data.record.id;
  175 + const roleParams = new RoleOrOrganizationParam(rowId.value, true, false);
  176 + olderPhoneNumber.value = data.record.phoneNumber;
  177 + singleEditPostPhoneNumber.phoneNumber = data.record.phoneNumber;
  178 + findCurrentUserRelation(roleParams).then((result) => {
  179 + Reflect.set(data.record, 'roleIds', result);
  180 + Reflect.set(data.record, 'password', '******');
  181 + setFieldsValue(data.record);
157 }); 182 });
158 - //获取所有父级id  
159 - function findForAllId(data = [], arr = []) {  
160 - for (const item of data) {  
161 - arr.push(item.id);  
162 - }  
163 - return arr; 183 + const organizationParams = new RoleOrOrganizationParam(rowId.value, false, true);
  184 + const checked = await findCurrentUserRelation(organizationParams);
  185 + const halfChecked = getHalfCheckedNode(checked);
  186 + Object.assign(checkedKeys, { checked, halfChecked });
  187 + setFieldsValue({ organizationIds: toRaw(checkedKeys) });
  188 + }
  189 + await updateSchema([
  190 + {
  191 + field: 'username',
  192 + dynamicDisabled: unref(isUpdate),
  193 + },
  194 + {
  195 + field: 'password',
  196 + ifShow: !unref(isUpdate),
  197 + },
  198 + ]);
  199 + });
  200 + const getTitle = computed(() => (!unref(isUpdate) ? '新增客户账号' : '编辑客户账号'));
  201 +
  202 + const getFormatValues = (values: Recordable) => {
  203 + const organizationIds = values.organizationIds;
  204 + if (!organizationIds || isArray(organizationIds)) return values;
  205 +
  206 + values.organizationIds = values?.organizationIds?.checked;
  207 +
  208 + return values;
  209 + };
  210 +
  211 + async function handleSubmit() {
  212 + setModalProps({ confirmLoading: true });
  213 + try {
  214 + const { createMessage } = useMessage();
  215 + if (unref(isUpdate)) {
  216 + Object.assign(postData, singleEditPostPhoneNumber);
164 } 217 }
  218 + const values = await validate([
  219 + 'id',
  220 + 'username',
  221 + 'realName',
  222 + 'password',
  223 + 'roleIds',
  224 + 'email',
  225 + 'accountExpireTime',
  226 + 'enabled',
  227 + 'remark',
  228 + 'organizationIds',
  229 + olderPhoneNumber.value === getFieldsValue().phoneNumber ? '' : 'phoneNumber',
  230 + ]);
165 231
166 - const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {  
167 - await resetFields();  
168 - setModalProps({ confirmLoading: false });  
169 - isUpdate.value = !!data?.isUpdate;  
170 - const groupListModel = await findCurrentUserGroups();  
171 - if (!unref(organizationTreeData).length) {  
172 - copyTransTreeFun(groupListModel);  
173 - organizationTreeData.value = groupListModel;  
174 - const getAllIds = findForAllId(organizationTreeData.value as any, []);  
175 - //设置要展开的id  
176 - treeExpandData.value = getAllIds; 232 + values.accountExpireTime =
  233 + typeof values.accountExpireTime != 'undefined' && values.accountExpireTime != null
  234 + ? values.accountExpireTime.format('YYYY-MM-DD HH:mm:ss')
  235 + : null;
  236 +
  237 + Object.assign(postData, getFormatValues(values));
  238 + if (unref(isUpdate)) {
  239 + if (values.email == '') {
  240 + delete postData.email;
177 } 241 }
178 - if (unref(isUpdate)) {  
179 - rowId.value = data.record.id;  
180 - const roleParams = new RoleOrOrganizationParam(rowId.value, true, false);  
181 - olderPhoneNumber.value = data.record.phoneNumber;  
182 - singleEditPostPhoneNumber.phoneNumber = data.record.phoneNumber;  
183 - findCurrentUserRelation(roleParams).then((result) => {  
184 - Reflect.set(data.record, 'roleIds', result);  
185 - Reflect.set(data.record, 'password', '******');  
186 - setFieldsValue(data.record);  
187 - });  
188 - const organizationParams = new RoleOrOrganizationParam(rowId.value, false, true);  
189 - checkGroup.value = await findCurrentUserRelation(organizationParams); 242 + } else {
  243 + if (values.email == '') {
  244 + delete postData.email;
190 } 245 }
191 - await updateSchema([  
192 - {  
193 - field: 'username',  
194 - dynamicDisabled: unref(isUpdate),  
195 - },  
196 - {  
197 - field: 'password',  
198 - ifShow: !unref(isUpdate),  
199 - },  
200 - ]);  
201 - });  
202 - const getTitle = computed(() => (!unref(isUpdate) ? '新增客户账号' : '编辑客户账号')); 246 + }
  247 + if (!Reflect.get(values, 'accountExpireTime')) {
  248 + Reflect.deleteProperty(postData, 'accountExpireTime');
  249 + }
  250 + await SaveOrUpdateUserInfo(postData as any, unref(isUpdate));
  251 + closeModal();
  252 + emit('success');
  253 + createMessage.success(unref(isUpdate) ? '编辑成功' : '新增成功');
  254 + } finally {
  255 + setTimeout(() => {
  256 + setModalProps({ confirmLoading: false });
  257 + }, 300);
  258 + }
  259 + }
  260 + // 取消全部的时候清除回显时获取的
  261 + const handleUnSelectAll = () => {
  262 + checkedKeysWithHalfChecked.value = [];
  263 + };
  264 +
  265 + const strictlyStatus = ref(false); //层级关联或独立的状态 false为层级关联 true为层级独立
203 266
204 - async function handleSubmit() {  
205 - setModalProps({ confirmLoading: true });  
206 - try {  
207 - const { createMessage } = useMessage();  
208 - if (unref(isUpdate)) {  
209 - Object.assign(postData, singleEditPostPhoneNumber);  
210 - }  
211 - const values = await validate([  
212 - 'id',  
213 - 'username',  
214 - 'realName',  
215 - 'password',  
216 - 'roleIds',  
217 - 'email',  
218 - 'accountExpireTime',  
219 - 'enabled',  
220 - 'remark',  
221 - 'organizationIds',  
222 - olderPhoneNumber.value === getFieldsValue().phoneNumber ? '' : 'phoneNumber',  
223 - ]);  
224 - let treeCheckedKeys: string[] | CheckKeys =  
225 - (unref(basicTreeRef)?.getCheckedKeys() as string[] | CheckKeys) || [];  
226 - //fix 取消层级独立后(unref(treeRef)?.getCheckedKeys() as string[])的数据不是数组,是{checked:[],halfChecked:[]}对象,迭代报错  
227 - if (!Array.isArray(treeCheckedKeys)) {  
228 - treeCheckedKeys = treeCheckedKeys?.checked;  
229 - }  
230 - const organizationIds = [  
231 - ...new Set([...unref(checkedKeysWithHalfChecked), ...treeCheckedKeys]),  
232 - ];  
233 - values.accountExpireTime =  
234 - typeof values.accountExpireTime != 'undefined' && values.accountExpireTime != null  
235 - ? values.accountExpireTime.format('YYYY-MM-DD HH:mm:ss')  
236 - : null;  
237 - values.organizationIds = organizationIds;  
238 - Object.assign(postData, values);  
239 - if (unref(isUpdate)) {  
240 - if (values.email == '') {  
241 - delete postData.email;  
242 - }  
243 - } else {  
244 - if (values.email == '') {  
245 - delete postData.email;  
246 - }  
247 - }  
248 - if (!Reflect.get(values, 'accountExpireTime')) {  
249 - Reflect.deleteProperty(postData, 'accountExpireTime');  
250 - }  
251 - await SaveOrUpdateUserInfo(postData as any, unref(isUpdate));  
252 - closeModal();  
253 - emit('success');  
254 - createMessage.success(unref(isUpdate) ? '编辑成功' : '新增成功');  
255 - } finally {  
256 - setTimeout(() => {  
257 - setModalProps({ confirmLoading: false });  
258 - }, 300); 267 + const handleStrictlyStatus = (status) => (strictlyStatus.value = status);
  268 +
  269 + const handleCheckClick = () => {
  270 + if (unref(checkStrictly)) {
  271 + checkStrictly.value = false;
  272 + }
  273 + };
  274 +
  275 + const [registerDrawer, { openDrawer: addOpenDrawer }] = useDrawer();
  276 +
  277 + const handleOpenCreate = () => {
  278 + addOpenDrawer(true, { isUpdate: false });
  279 + };
  280 + const handleReload = async () => {
  281 + const groupListModel = await findCurrentUserGroups();
  282 + organizationTreeData.value = groupListModel;
  283 + buildNodeMap(toRaw(unref(groupListModel)));
  284 + };
  285 +
  286 + const treeNodeMap = ref<Record<string, { parentId?: string; children?: string[] }>>();
  287 +
  288 + function buildNodeMap(tree: GroupListResultModel) {
  289 + const nodeMap: Record<string, { parentId?: string; children?: string[] }> = {};
  290 +
  291 + function traverse(tree: GroupListResultModel) {
  292 + for (let node of tree) {
  293 + Reflect.set(nodeMap, node.id, {
  294 + parentId: node.parentId,
  295 + children: node.children?.map((item) => item.id),
  296 + });
  297 +
  298 + if (node.children && node.children.length) {
  299 + traverse(node.children);
259 } 300 }
260 } 301 }
261 - // 取消全部的时候清除回显时获取的  
262 - const handleUnSelectAll = () => {  
263 - checkedKeysWithHalfChecked.value = [];  
264 - }; 302 + }
  303 + traverse(tree);
265 304
266 - const strictlyStatus = ref(false); //层级关联或独立的状态 false为层级关联 true为层级独立 305 + treeNodeMap.value = nodeMap;
  306 + }
267 307
268 - const handleStrictlyStatus = (status) => (strictlyStatus.value = status); 308 + function getHalfCheckedNode(keys: string[]) {
  309 + const relation = unref(treeNodeMap) || {};
  310 + const halfChecked: string[] = [];
269 311
270 - const handleCheckClick = (selectedKeys: CheckKeys, event: CheckEvent) => {  
271 - //fix 取消层级独立后selectedKeys不是数组,是{checked:[],halfChecked:[]}对象 迭代报错  
272 - // 层级独立  
273 - if (strictlyStatus.value) {  
274 - if (!Array.isArray(selectedKeys)) {  
275 - selectedKeys = selectedKeys?.checked;  
276 - event.halfCheckedKeys = [];  
277 - }  
278 - } else {  
279 - // 层级关联  
280 - event.halfCheckedKeys = [];  
281 - }  
282 - checkedKeysWithHalfChecked.value = [  
283 - ...selectedKeys,  
284 - ...(event.halfCheckedKeys as string[]),  
285 - ];  
286 - }; 312 + for (const key of keys) {
  313 + let current = relation[key];
287 314
288 - const [registerDrawer, { openDrawer: addOpenDrawer }] = useDrawer(); 315 + while (current) {
  316 + if (keys.includes(current.parentId!) || !current.parentId) {
  317 + break;
  318 + }
289 319
290 - const handleOpenCreate = () => {  
291 - addOpenDrawer(true, { isUpdate: false });  
292 - };  
293 - const handleReload = async () => {  
294 - const groupListModel = await findCurrentUserGroups();  
295 - copyTransTreeFun(groupListModel);  
296 - organizationTreeData.value = groupListModel;  
297 - }; 320 + halfChecked.push(current.parentId!);
  321 + current = relation[current.parentId!];
  322 + }
  323 + }
298 324
299 - return {  
300 - registerModal,  
301 - registerForm,  
302 - handleSubmit,  
303 - getTitle,  
304 - organizationTreeData,  
305 - checkGroup,  
306 - basicTreeRef,  
307 - treeExpandData,  
308 - roleOptions,  
309 - registerRoleDrawer,  
310 - handleOpenRole,  
311 - handleSuccess,  
312 - handleRoleSelect,  
313 - handleTreeSelect,  
314 - handleCheckClick,  
315 - handleUnSelectAll,  
316 - handleStrictlyStatus,  
317 - handleOpenCreate,  
318 - registerDrawer,  
319 - handleReload,  
320 - };  
321 - },  
322 - }); 325 + return Array.from(new Set(halfChecked));
  326 + }
323 </script> 327 </script>
324 <style scoped lang="less"> 328 <style scoped lang="less">
325 :deep(.vben-basic-tree) { 329 :deep(.vben-basic-tree) {
@@ -14,9 +14,10 @@ @@ -14,9 +14,10 @@
14 </Button> 14 </Button>
15 <BasicTree 15 <BasicTree
16 v-if="organizationTreeData.length" 16 v-if="organizationTreeData.length"
  17 + :check-strictly="checkStrictly"
17 v-model:value="model[field]" 18 v-model:value="model[field]"
18 :treeData="organizationTreeData" 19 :treeData="organizationTreeData"
19 - :checked-keys="checkGroup" 20 + :checked-keys="checkedKeys"
20 :expandedKeys="treeExpandData" 21 :expandedKeys="treeExpandData"
21 ref="basicTreeRef" 22 ref="basicTreeRef"
22 @check="handleCheckClick" 23 @check="handleCheckClick"
@@ -25,6 +26,7 @@ @@ -25,6 +26,7 @@
25 checkable 26 checkable
26 toolbar 27 toolbar
27 @change="handleTreeSelect" 28 @change="handleTreeSelect"
  29 + :replace-fields="{ children: 'children', title: 'name', key: 'id' }"
28 /> 30 />
29 </template> 31 </template>
30 <template #roleSlot="{ model, field }"> 32 <template #roleSlot="{ model, field }">
@@ -53,19 +55,18 @@ @@ -53,19 +55,18 @@
53 </BasicModal> 55 </BasicModal>
54 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" /> 56 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" />
55 </template> 57 </template>
56 -<script lang="ts">  
57 - import { defineComponent, ref, computed, unref, reactive, onMounted } from 'vue'; 58 +<script lang="ts" setup>
  59 + import { ref, computed, unref, reactive, onMounted } from 'vue';
58 import { BasicModal, useModalInner } from '/@/components/Modal'; 60 import { BasicModal, useModalInner } from '/@/components/Modal';
59 import { BasicForm, useForm } from '/@/components/Form/index'; 61 import { BasicForm, useForm } from '/@/components/Form/index';
60 import { accountFormSchema } from './config'; 62 import { accountFormSchema } from './config';
61 import { Button } from 'ant-design-vue'; 63 import { Button } from 'ant-design-vue';
62 import { findCurrentUserRelation, filterRoleList } from '/@/api/system/system'; 64 import { findCurrentUserRelation, filterRoleList } from '/@/api/system/system';
63 import { addTenantList } from '/@/api/system/account'; 65 import { addTenantList } from '/@/api/system/account';
64 - import { BasicTree, TreeItem, CheckKeys, CheckEvent } from '/@/components/Tree'; 66 + import { BasicTree, TreeItem, CheckKeys } from '/@/components/Tree';
65 import { findCurrentUserGroups } from '/@/api/system/group'; 67 import { findCurrentUserGroups } from '/@/api/system/group';
66 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel'; 68 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel';
67 import { useMessage } from '/@/hooks/web/useMessage'; 69 import { useMessage } from '/@/hooks/web/useMessage';
68 - import { copyTransTreeFun } from '/@/utils/fnUtils';  
69 import { TOption } from '/@/views/rule/linkedge/config/config.data'; 70 import { TOption } from '/@/views/rule/linkedge/config/config.data';
70 import { PlusOutlined } from '@ant-design/icons-vue'; 71 import { PlusOutlined } from '@ant-design/icons-vue';
71 import { useDrawer } from '/@/components/Drawer'; 72 import { useDrawer } from '/@/components/Drawer';
@@ -74,267 +75,285 @@ @@ -74,267 +75,285 @@
74 import { useUserStore } from '/@/store/modules/user'; 75 import { useUserStore } from '/@/store/modules/user';
75 import { IsPhoneExist } from '/@/api/system/system'; 76 import { IsPhoneExist } from '/@/api/system/system';
76 import { phoneRegexp } from '/@/utils/rules'; 77 import { phoneRegexp } from '/@/utils/rules';
  78 + import { GroupListResultModel } from '/@/api/system/model/groupModel';
  79 + import { toRaw } from 'vue';
  80 + import { isArray } from '/@/utils/is';
77 81
78 - export default defineComponent({  
79 - name: 'TenantModal',  
80 - components: {  
81 - BasicModal,  
82 - BasicForm,  
83 - Button,  
84 - BasicTree,  
85 - OrganizationDrawer,  
86 - PlusOutlined,  
87 - RoleDrawer,  
88 - VNodes: (_, { attrs }) => {  
89 - return attrs.vnodes;  
90 - },  
91 - },  
92 - emits: ['success', 'register'],  
93 - setup(_, { emit }) {  
94 - const [registerRoleDrawer, { openDrawer }] = useDrawer();  
95 - const { createMessage } = useMessage();  
96 - const userInfo = useUserStore(); 82 + const VNodes = (_, { attrs }) => {
  83 + return attrs.vnodes;
  84 + };
97 85
98 - const roleOptions = ref<TOption[]>([]);  
99 - const isAdd = ref(true);  
100 - const rowId = ref('');  
101 - const organizationTreeData = ref<TreeItem[]>([]);  
102 - const basicTreeRef = ref();  
103 - const checkGroup = ref<string[]>([]);  
104 - const treeExpandData = ref([]);  
105 - const olderPhoneNumber = ref();  
106 - const singleEditPostPhoneNumber = reactive({  
107 - phoneNumber: '',  
108 - });  
109 - const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);  
110 - const getRoleList = async () => {  
111 - const res = await filterRoleList({ roleType: 'TENANT_ADMIN' });  
112 - roleOptions.value = res.map((m) => {  
113 - return {  
114 - label: m.name,  
115 - value: m.id,  
116 - };  
117 - });  
118 - }; 86 + const emit = defineEmits(['register', 'success']);
119 87
120 - onMounted(async () => {  
121 - await getRoleList();  
122 - });  
123 - const handleOpenRole = () => {  
124 - openDrawer(true, {  
125 - isAdd: false,  
126 - });  
127 - };  
128 - const clearValidateByField = (field: string) => {  
129 - clearValidate(field);  
130 - };  
131 - const handleRoleSelect = (e) => {  
132 - if (e?.length > 0) clearValidateByField('roleIds');  
133 - else validateFields(['roleIds']);  
134 - };  
135 - const handleTreeSelect = (e) => {  
136 - if (e) clearValidateByField('organizationIds');  
137 - };  
138 - const handleSuccess = async () => {  
139 - await getRoleList(); 88 + const [registerRoleDrawer, { openDrawer }] = useDrawer();
  89 + const { createMessage } = useMessage();
  90 + const userInfo = useUserStore();
  91 +
  92 + const checkStrictly = ref(true);
  93 +
  94 + const roleOptions = ref<TOption[]>([]);
  95 + const isAdd = ref(true);
  96 + const rowId = ref('');
  97 + const organizationTreeData = ref<TreeItem[]>([]);
  98 + const basicTreeRef = ref();
  99 + const checkedKeys = reactive<CheckKeys>({ checked: [], halfChecked: [] });
  100 + const treeExpandData = ref([]);
  101 + const olderPhoneNumber = ref();
  102 + const singleEditPostPhoneNumber = reactive({
  103 + phoneNumber: '',
  104 + });
  105 + const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);
  106 + const getRoleList = async () => {
  107 + const res = await filterRoleList({ roleType: 'TENANT_ADMIN' });
  108 + roleOptions.value = res.map((m) => {
  109 + return {
  110 + label: m.name,
  111 + value: m.id,
140 }; 112 };
141 - const [  
142 - registerForm,  
143 - {  
144 - setFieldsValue,  
145 - updateSchema,  
146 - resetFields,  
147 - validate,  
148 - getFieldsValue,  
149 - clearValidate,  
150 - validateFields,  
151 - },  
152 - ] = useForm({  
153 - labelWidth: 100,  
154 - schemas: accountFormSchema,  
155 - showActionButtonGroup: false,  
156 - actionColOptions: {  
157 - span: 18,  
158 - },  
159 - });  
160 - //获取所有父级id  
161 - function findForAllId(data = [], arr = []) {  
162 - for (const item of data) {  
163 - arr.push(item.id);  
164 - }  
165 - return arr;  
166 - } 113 + });
  114 + };
167 115
168 - const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {  
169 - await resetFields();  
170 - setModalProps({ confirmLoading: false });  
171 - isAdd.value = !!data?.isAdd;  
172 - const groupListModel = await findCurrentUserGroups();  
173 - if (!unref(organizationTreeData).length) {  
174 - copyTransTreeFun(groupListModel);  
175 - organizationTreeData.value = groupListModel;  
176 - const getAllIds = findForAllId(organizationTreeData.value as any, []);  
177 - //设置要展开的id  
178 - treeExpandData.value = getAllIds;  
179 - } 116 + onMounted(async () => {
  117 + await getRoleList();
  118 + });
  119 + const handleOpenRole = () => {
  120 + openDrawer(true, {
  121 + isAdd: false,
  122 + });
  123 + };
  124 + const clearValidateByField = (field: string) => {
  125 + clearValidate(field);
  126 + };
  127 + const handleRoleSelect = (e) => {
  128 + if (e?.length > 0) clearValidateByField('roleIds');
  129 + else validateFields(['roleIds']);
  130 + };
  131 + const handleTreeSelect = (e) => {
  132 + if (e) clearValidateByField('organizationIds');
  133 + };
  134 + const handleSuccess = async () => {
  135 + await getRoleList();
  136 + };
  137 + const [
  138 + registerForm,
  139 + {
  140 + setFieldsValue,
  141 + updateSchema,
  142 + resetFields,
  143 + validate,
  144 + getFieldsValue,
  145 + clearValidate,
  146 + validateFields,
  147 + },
  148 + ] = useForm({
  149 + labelWidth: 100,
  150 + schemas: accountFormSchema,
  151 + showActionButtonGroup: false,
  152 + actionColOptions: {
  153 + span: 18,
  154 + },
  155 + });
  156 + //获取所有父级id
  157 + function findForAllId(data = [], arr = []) {
  158 + for (const item of data) {
  159 + arr.push(item.id);
  160 + }
  161 + return arr;
  162 + }
180 163
181 - if (!unref(isAdd)) {  
182 - rowId.value = data.record.id;  
183 - const roleParams = new RoleOrOrganizationParam(rowId.value, true, false);  
184 - olderPhoneNumber.value = data.record.phoneNumber;  
185 - singleEditPostPhoneNumber.phoneNumber = data.record.phoneNumber;  
186 - findCurrentUserRelation(roleParams).then((result) => {  
187 - Reflect.set(data.record, 'roleIds', result);  
188 - setFieldsValue(data.record);  
189 - });  
190 - updateSchema([  
191 - {  
192 - field: 'phoneNumber',  
193 - dynamicRules: () => {  
194 - return [  
195 - {  
196 - required: true,  
197 - validator(_, value) {  
198 - return new Promise((resolve, reject) => {  
199 - if (value == '') {  
200 - reject('请输入手机号');  
201 - } else if (!phoneRegexp.test(value)) {  
202 - reject('请输入正确的手机号');  
203 - } else {  
204 - resolve();  
205 - }  
206 - });  
207 - },  
208 - },  
209 - ]; 164 + const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  165 + await resetFields();
  166 + checkStrictly.value = true;
  167 + setModalProps({ confirmLoading: false });
  168 + isAdd.value = !!data?.isAdd;
  169 + const groupListModel = await findCurrentUserGroups();
  170 + if (!unref(organizationTreeData).length) {
  171 + organizationTreeData.value = groupListModel;
  172 + buildNodeMap(toRaw(unref(organizationTreeData) as GroupListResultModel));
  173 + const getAllIds = findForAllId(organizationTreeData.value as any, []);
  174 + //设置要展开的id
  175 + treeExpandData.value = getAllIds;
  176 + }
  177 +
  178 + if (!unref(isAdd)) {
  179 + rowId.value = data.record.id;
  180 + const roleParams = new RoleOrOrganizationParam(rowId.value, true, false);
  181 + olderPhoneNumber.value = data.record.phoneNumber;
  182 + singleEditPostPhoneNumber.phoneNumber = data.record.phoneNumber;
  183 + findCurrentUserRelation(roleParams).then((result) => {
  184 + Reflect.set(data.record, 'roleIds', result);
  185 + setFieldsValue(data.record);
  186 + });
  187 + updateSchema([
  188 + {
  189 + field: 'phoneNumber',
  190 + dynamicRules: () => {
  191 + return [
  192 + {
  193 + required: true,
  194 + validator(_, value) {
  195 + return new Promise((resolve, reject) => {
  196 + if (value == '') {
  197 + reject('请输入手机号');
  198 + } else if (!phoneRegexp.test(value)) {
  199 + reject('请输入正确的手机号');
  200 + } else {
  201 + resolve();
  202 + }
  203 + });
  204 + },
210 }, 205 },
211 - },  
212 - ]);  
213 - const organizationParams = new RoleOrOrganizationParam(rowId.value, false, true);  
214 - checkGroup.value = await findCurrentUserRelation(organizationParams);  
215 - } else {  
216 - updateSchema([  
217 - {  
218 - field: 'phoneNumber',  
219 - dynamicRules: ({ values }) => {  
220 - return [  
221 - {  
222 - required: true,  
223 - validator(_, value) {  
224 - return new Promise((resolve, reject) => {  
225 - if (value == '') {  
226 - reject('请输入手机号');  
227 - } else if (!phoneRegexp.test(value)) {  
228 - reject('请输入正确的手机号');  
229 - } else {  
230 - if (values.phoneNumber != undefined) {  
231 - // 此处可以用防抖函数优化性能  
232 - IsPhoneExist(value).then(({ data }) => {  
233 - if (data != null) {  
234 - reject('手机号已存在');  
235 - } else {  
236 - resolve();  
237 - }  
238 - }); 206 + ];
  207 + },
  208 + },
  209 + ]);
  210 + const organizationParams = new RoleOrOrganizationParam(rowId.value, false, true);
  211 + const checked = await findCurrentUserRelation(organizationParams);
  212 + const halfChecked = getHalfCheckedNode(checked);
  213 + Object.assign(checkedKeys, { checked, halfChecked });
  214 + setFieldsValue({ organizationIds: toRaw(checkedKeys) });
  215 + } else {
  216 + updateSchema([
  217 + {
  218 + field: 'phoneNumber',
  219 + dynamicRules: ({ values }) => {
  220 + return [
  221 + {
  222 + required: true,
  223 + validator(_, value) {
  224 + return new Promise((resolve, reject) => {
  225 + if (value == '') {
  226 + reject('请输入手机号');
  227 + } else if (!phoneRegexp.test(value)) {
  228 + reject('请输入正确的手机号');
  229 + } else {
  230 + if (values.phoneNumber != undefined) {
  231 + // 此处可以用防抖函数优化性能
  232 + IsPhoneExist(value).then(({ data }) => {
  233 + if (data != null) {
  234 + reject('手机号已存在');
239 } else { 235 } else {
240 resolve(); 236 resolve();
241 } 237 }
242 - }  
243 - });  
244 - },  
245 - },  
246 - ]; 238 + });
  239 + } else {
  240 + resolve();
  241 + }
  242 + }
  243 + });
  244 + },
247 }, 245 },
248 - },  
249 - ]);  
250 - }  
251 - await updateSchema([  
252 - {  
253 - field: 'username',  
254 - dynamicDisabled: !unref(isAdd), 246 + ];
255 }, 247 },
256 - ]); 248 + },
  249 + ]);
  250 + }
  251 + await updateSchema([
  252 + {
  253 + field: 'username',
  254 + dynamicDisabled: !unref(isAdd),
  255 + },
  256 + ]);
  257 + });
  258 + const getTitle = computed(() => (unref(isAdd) ? '新增管理员账号' : '编辑管理员账号'));
  259 +
  260 + const getFormatValues = (values: Recordable) => {
  261 + const organizationIds = values.organizationIds;
  262 + if (!organizationIds || isArray(organizationIds)) return values;
  263 +
  264 + values.organizationIds = values?.organizationIds?.checked;
  265 +
  266 + return values;
  267 + };
  268 +
  269 + async function handleSubmit() {
  270 + setModalProps({ confirmLoading: true });
  271 + try {
  272 + const values = getFieldsValue();
  273 + if (!('organizationIds' in values)) {
  274 + createMessage.error('组织必选');
  275 + }
  276 +
  277 + await validate();
  278 + await addTenantList({
  279 + ...getFormatValues(values),
  280 + level: 4,
  281 + tenantId: userInfo.getUserInfo.tenantId!,
257 }); 282 });
258 - const getTitle = computed(() => (unref(isAdd) ? '新增管理员账号' : '编辑管理员账号')); 283 + createMessage.success(unref(isAdd) ? '新增成功' : '编辑成功');
  284 + closeModal();
  285 + emit('success');
  286 + } finally {
  287 + setModalProps({ confirmLoading: false });
  288 + }
  289 + }
  290 + // 取消全部的时候清除回显时获取的
  291 + const handleUnSelectAll = () => {
  292 + checkedKeysWithHalfChecked.value = [];
  293 + };
  294 +
  295 + const strictlyStatus = ref(false); //层级关联或独立的状态 false为层级关联 true为层级独立
  296 +
  297 + const handleStrictlyStatus = (status) => (strictlyStatus.value = status);
  298 +
  299 + const handleCheckClick = () => {
  300 + if (unref(checkStrictly)) {
  301 + checkStrictly.value = false;
  302 + }
  303 + };
  304 +
  305 + const [registerDrawer, { openDrawer: addOpenDrawer }] = useDrawer();
  306 +
  307 + const handleOpenCreate = () => {
  308 + addOpenDrawer(true, { isAdd: false });
  309 + };
  310 + const handleReload = async () => {
  311 + const groupListModel = await findCurrentUserGroups();
  312 + organizationTreeData.value = groupListModel;
  313 + buildNodeMap(toRaw(unref(groupListModel)));
  314 + };
  315 +
  316 + const treeNodeMap = ref<Record<string, { parentId?: string; children?: string[] }>>();
259 317
260 - async function handleSubmit() {  
261 - setModalProps({ confirmLoading: true });  
262 - try {  
263 - const values = getFieldsValue();  
264 - if (!('organizationIds' in values)) {  
265 - createMessage.error('组织必选');  
266 - }  
267 - await validate();  
268 - await addTenantList({ ...values, level: 4, tenantId: userInfo.getUserInfo.tenantId! });  
269 - createMessage.success(unref(isAdd) ? '新增成功' : '编辑成功');  
270 - closeModal();  
271 - emit('success');  
272 - } finally {  
273 - setModalProps({ confirmLoading: false }); 318 + function buildNodeMap(tree: GroupListResultModel) {
  319 + const nodeMap: Record<string, { parentId?: string; children?: string[] }> = {};
  320 +
  321 + function traverse(tree: GroupListResultModel) {
  322 + for (let node of tree) {
  323 + Reflect.set(nodeMap, node.id, {
  324 + parentId: node.parentId,
  325 + children: node.children?.map((item) => item.id),
  326 + });
  327 +
  328 + if (node.children && node.children.length) {
  329 + traverse(node.children);
274 } 330 }
275 } 331 }
276 - // 取消全部的时候清除回显时获取的  
277 - const handleUnSelectAll = () => {  
278 - checkedKeysWithHalfChecked.value = [];  
279 - }; 332 + }
  333 + traverse(tree);
280 334
281 - const strictlyStatus = ref(false); //层级关联或独立的状态 false为层级关联 true为层级独立 335 + treeNodeMap.value = nodeMap;
  336 + }
282 337
283 - const handleStrictlyStatus = (status) => (strictlyStatus.value = status); 338 + function getHalfCheckedNode(keys: string[]) {
  339 + const relation = unref(treeNodeMap) || {};
  340 + const halfChecked: string[] = [];
284 341
285 - const handleCheckClick = (selectedKeys: CheckKeys, event: CheckEvent) => {  
286 - //fix 取消层级独立后selectedKeys不是数组,是{checked:[],halfChecked:[]}对象 迭代报错  
287 - // 层级独立  
288 - if (strictlyStatus.value) {  
289 - if (!Array.isArray(selectedKeys)) {  
290 - selectedKeys = selectedKeys?.checked;  
291 - event.halfCheckedKeys = [];  
292 - }  
293 - } else {  
294 - // 层级关联  
295 - event.halfCheckedKeys = [];  
296 - }  
297 - checkedKeysWithHalfChecked.value = [  
298 - ...selectedKeys,  
299 - ...(event.halfCheckedKeys as string[]),  
300 - ];  
301 - }; 342 + for (const key of keys) {
  343 + let current = relation[key];
302 344
303 - const [registerDrawer, { openDrawer: addOpenDrawer }] = useDrawer(); 345 + while (current) {
  346 + if (keys.includes(current.parentId!) || !current.parentId) {
  347 + break;
  348 + }
304 349
305 - const handleOpenCreate = () => {  
306 - addOpenDrawer(true, { isAdd: false });  
307 - };  
308 - const handleReload = async () => {  
309 - const groupListModel = await findCurrentUserGroups();  
310 - copyTransTreeFun(groupListModel);  
311 - organizationTreeData.value = groupListModel;  
312 - }; 350 + halfChecked.push(current.parentId!);
  351 + current = relation[current.parentId!];
  352 + }
  353 + }
313 354
314 - return {  
315 - registerModal,  
316 - registerForm,  
317 - handleSubmit,  
318 - getTitle,  
319 - organizationTreeData,  
320 - checkGroup,  
321 - basicTreeRef,  
322 - treeExpandData,  
323 - roleOptions,  
324 - registerRoleDrawer,  
325 - handleOpenRole,  
326 - handleSuccess,  
327 - handleRoleSelect,  
328 - handleTreeSelect,  
329 - handleCheckClick,  
330 - handleUnSelectAll,  
331 - handleStrictlyStatus,  
332 - handleOpenCreate,  
333 - registerDrawer,  
334 - handleReload,  
335 - };  
336 - },  
337 - }); 355 + return Array.from(new Set(halfChecked));
  356 + }
338 </script> 357 </script>
339 <style scoped lang="less"> 358 <style scoped lang="less">
340 :deep(.vben-basic-tree) { 359 :deep(.vben-basic-tree) {