Commit db118eaadf933beac7c66117623680b048eae397

Authored by xp.Huang
2 parents 42a1080c b1e46c76

Merge branch 'perf/account-manage' into 'feat/account-role-manage'

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

See merge request yunteng/thingskit-front!1320
... ... @@ -54,7 +54,7 @@ export const basicProps = {
54 54 },
55 55
56 56 checkedKeys: {
57   - type: Array as PropType<CheckKeys>,
  57 + type: [Array, Object] as PropType<CheckKeys>,
58 58 default: () => [],
59 59 },
60 60
... ...
... ... @@ -14,9 +14,10 @@
14 14 </Button>
15 15 <BasicTree
16 16 v-if="organizationTreeData.length"
  17 + :check-strictly="checkStrictly"
17 18 v-model:value="model[field]"
18 19 :treeData="organizationTreeData"
19   - :checked-keys="checkGroup"
  20 + :checked-keys="checkedKeys"
20 21 :expandedKeys="treeExpandData"
21 22 ref="basicTreeRef"
22 23 @check="handleCheckClick"
... ... @@ -25,6 +26,7 @@
25 26 checkable
26 27 toolbar
27 28 @change="handleTreeSelect"
  29 + :replace-fields="{ children: 'children', title: 'name', key: 'id' }"
28 30 />
29 31 </template>
30 32 <template #roleSlot="{ model, field }">
... ... @@ -53,8 +55,8 @@
53 55 </BasicModal>
54 56 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" />
55 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 60 import { BasicModal, useModalInner } from '/@/components/Modal';
59 61 import { BasicForm, useForm } from '/@/components/Form/index';
60 62 import { accountFormSchema } from './account.data';
... ... @@ -64,262 +66,264 @@
64 66 SaveOrUpdateUserInfo,
65 67 filterRoleList,
66 68 } from '/@/api/system/system';
67   - import { BasicTree, TreeItem, CheckKeys, CheckEvent } from '/@/components/Tree';
  69 + import { BasicTree, TreeItem, CheckKeys } from '/@/components/Tree';
68 70 import { findCurrentUserGroups } from '/@/api/system/group';
69 71 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel';
70 72 import { useMessage } from '/@/hooks/web/useMessage';
71   - import { copyTransTreeFun } from '/@/utils/fnUtils';
72 73 import { TOption } from '/@/views/rule/linkedge/config/config.data';
73 74 import { PlusOutlined } from '@ant-design/icons-vue';
74 75 import { useDrawer } from '/@/components/Drawer';
75 76 import RoleDrawer from '../../role/CustomRoleDrawer.vue';
76 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 327 </script>
324 328 <style scoped lang="less">
325 329 :deep(.vben-basic-tree) {
... ...
... ... @@ -14,9 +14,10 @@
14 14 </Button>
15 15 <BasicTree
16 16 v-if="organizationTreeData.length"
  17 + :check-strictly="checkStrictly"
17 18 v-model:value="model[field]"
18 19 :treeData="organizationTreeData"
19   - :checked-keys="checkGroup"
  20 + :checked-keys="checkedKeys"
20 21 :expandedKeys="treeExpandData"
21 22 ref="basicTreeRef"
22 23 @check="handleCheckClick"
... ... @@ -25,6 +26,7 @@
25 26 checkable
26 27 toolbar
27 28 @change="handleTreeSelect"
  29 + :replace-fields="{ children: 'children', title: 'name', key: 'id' }"
28 30 />
29 31 </template>
30 32 <template #roleSlot="{ model, field }">
... ... @@ -53,19 +55,18 @@
53 55 </BasicModal>
54 56 <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" />
55 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 60 import { BasicModal, useModalInner } from '/@/components/Modal';
59 61 import { BasicForm, useForm } from '/@/components/Form/index';
60 62 import { accountFormSchema } from './config';
61 63 import { Button } from 'ant-design-vue';
62 64 import { findCurrentUserRelation, filterRoleList } from '/@/api/system/system';
63 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 67 import { findCurrentUserGroups } from '/@/api/system/group';
66 68 import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel';
67 69 import { useMessage } from '/@/hooks/web/useMessage';
68   - import { copyTransTreeFun } from '/@/utils/fnUtils';
69 70 import { TOption } from '/@/views/rule/linkedge/config/config.data';
70 71 import { PlusOutlined } from '@ant-design/icons-vue';
71 72 import { useDrawer } from '/@/components/Drawer';
... ... @@ -74,267 +75,285 @@
74 75 import { useUserStore } from '/@/store/modules/user';
75 76 import { IsPhoneExist } from '/@/api/system/system';
76 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 235 } else {
240 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 357 </script>
339 358 <style scoped lang="less">
340 359 :deep(.vben-basic-tree) {
... ...