7731293f by lihua

用户管理

1 parent b5d70ee4
...@@ -41,6 +41,8 @@ declare module '@vue/runtime-core' { ...@@ -41,6 +41,8 @@ declare module '@vue/runtime-core' {
41 PageHeader: typeof import('./src/components/PageHeader/index.vue')['default'] 41 PageHeader: typeof import('./src/components/PageHeader/index.vue')['default']
42 PageMain: typeof import('./src/components/PageMain/index.vue')['default'] 42 PageMain: typeof import('./src/components/PageMain/index.vue')['default']
43 PageNav: typeof import('./src/components/PageNav/index.vue')['default'] 43 PageNav: typeof import('./src/components/PageNav/index.vue')['default']
44 PasswordStrengthContent: typeof import('./src/components/PasswordStrengthMeter/PasswordStrengthContent.vue')['default']
45 PasswordStrengthMeter: typeof import('./src/components/PasswordStrengthMeter/index.vue')['default']
44 PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] 46 PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default']
45 Popover: typeof import('./src/components/Popover/index.vue')['default'] 47 Popover: typeof import('./src/components/Popover/index.vue')['default']
46 RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default'] 48 RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default']
...@@ -49,6 +51,7 @@ declare module '@vue/runtime-core' { ...@@ -49,6 +51,7 @@ declare module '@vue/runtime-core' {
49 Schedule: typeof import('./src/components/Schedule/index.vue')['default'] 51 Schedule: typeof import('./src/components/Schedule/index.vue')['default']
50 SearchBar: typeof import('./src/components/SearchBar/index.vue')['default'] 52 SearchBar: typeof import('./src/components/SearchBar/index.vue')['default']
51 SecondAndMinute: typeof import('./src/components/Schedule/component/secondAndMinute.vue')['default'] 53 SecondAndMinute: typeof import('./src/components/Schedule/component/secondAndMinute.vue')['default']
54 SelectPersonnel: typeof import('./src/components/SelectPersonnel/src/SelectPersonnel.vue')['default']
52 SplitPane: typeof import('./src/components/SplitPane/index.vue')['default'] 55 SplitPane: typeof import('./src/components/SplitPane/index.vue')['default']
53 StepBar: typeof import('./src/components/StepBar/index.vue')['default'] 56 StepBar: typeof import('./src/components/StepBar/index.vue')['default']
54 SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default'] 57 SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
...@@ -59,6 +62,7 @@ declare module '@vue/runtime-core' { ...@@ -59,6 +62,7 @@ declare module '@vue/runtime-core' {
59 Table_search: typeof import('./src/components/Tools/table_search.vue')['default'] 62 Table_search: typeof import('./src/components/Tools/table_search.vue')['default']
60 Table_tools: typeof import('./src/components/Tools/table_tools.vue')['default'] 63 Table_tools: typeof import('./src/components/Tools/table_tools.vue')['default']
61 Table_v2: typeof import('./src/components/Table/table_v2.vue')['default'] 64 Table_v2: typeof import('./src/components/Table/table_v2.vue')['default']
65 TableActions: typeof import('./src/components/TablePlus/src/components/TableActions.vue')['default']
62 Tabs: typeof import('./src/components/Tabs/index.vue')['default'] 66 Tabs: typeof import('./src/components/Tabs/index.vue')['default']
63 Toolbar: typeof import('./src/components/LineageGraph/toolbar.vue')['default'] 67 Toolbar: typeof import('./src/components/LineageGraph/toolbar.vue')['default']
64 Topbar: typeof import('./src/components/LineageGraph/topbar.vue')['default'] 68 Topbar: typeof import('./src/components/LineageGraph/topbar.vue')['default']
......
1 import request from "@/utils/request"; 1 import request from "@/utils/request";
2 import { ElMessage } from "element-plus";
2 3
3 /** 获取租户列表(分页) */ 4 /** 获取租户列表(分页) */
4 export const getTenantSinglePage = (params) => request({ 5 export const getTenantSinglePage = (params) => request({
...@@ -70,6 +71,17 @@ export const removeOrganisation = (guids) => request({ ...@@ -70,6 +71,17 @@ export const removeOrganisation = (guids) => request({
70 71
71 72
72 /** 73 /**
74 * 获取部门列表
75 * @param param
76 * @returns
77 */
78 export const getOrganisationListApi = (params) => request({
79 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/singlePage`,
80 method: 'post',
81 data: params
82 })
83
84 /**
73 * 修改部门关系 85 * 修改部门关系
74 * @param param 86 * @param param
75 * @returns 87 * @returns
...@@ -91,3 +103,117 @@ export const addOrganisation = (params) => request({ ...@@ -91,3 +103,117 @@ export const addOrganisation = (params) => request({
91 data: params 103 data: params
92 }); 104 });
93 105
106 /**
107 * 获取人员列表
108 * @param param
109 * @returns
110 */
111 export const getStaff = (params) => request({
112 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/singlePage`,
113 method: 'post',
114 data: params
115 });
116
117 /**
118 * 删除人员
119 * @param guids:string[]
120 * @returns
121 */
122 export const removeStaff = (guids:string[]) => request({
123 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/removeListByGuids`,
124 method: 'delete',
125 data: guids
126 })
127
128 /**
129 * !获取部门关系树(不带人员)
130 * @param tenantGuid
131 * @returns
132 */
133 export const getOrganisationRelTreeListPromise = (tenantGuid:string):Promise<any> => {
134 let organisationTree:any[] = []
135 let param = {
136 tenantGuid,
137 bizState: "Y"
138 }
139 return new Promise((resolve, reject) => {
140 getOrganisationRelTreeList(param).then((res: any) => {
141 if (res?.code === '00000') {
142 organisationTree = res.data || []
143 } else {
144 res?.msg && ElMessage.error(res?.msg)
145 }
146 resolve(organisationTree)
147 })
148 })
149 }
150
151 /**
152 * 获取部门关系列表tree
153 * @param param
154 * @returns
155 */
156 export const getOrganisationRelTreeList = (param:{tenantGuid:string}) => request({
157 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/tree-list`,
158 method: 'post',
159 data: param
160 });
161
162 export const resetPwd2 = (userGuid) => request({
163 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/user/data/resetPwd?userGuid=${userGuid}`,
164 method: 'put'
165 });
166
167
168 // 模板列表
169 export const getPermissionTemplateList = (param) => request({
170 url: `${import.meta.env.VITE_APP_AUTH_URL}/func-permission-template/page-list`,
171 method: 'post',
172 data: param
173 });
174
175 export const getTemplateListPromise = (customParam?: {}): Promise<any> => {
176 let list = []
177 let param = customParam ? Object.assign({}, { pageIndex: 1, pageSize: -1, bizState: 'Y' }, customParam) : { pageIndex: 1, pageSize: -1, bizState: 'Y' }
178 return new Promise((resolve, reject) => {
179 getPermissionTemplateList(param).then((res: any) => {
180 if (res?.code === '00000') {
181 list = res?.data?.records || []
182 } else {
183 res?.msg && ElMessage.error(res?.msg)
184 }
185 resolve(list)
186 })
187 })
188 }
189
190 /**
191 * 获取人员详情
192 * @param guid:string
193 * @returns
194 */
195 export const getStaffDetail = (guid: string) => request({
196 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/getByGuid/${guid}`,
197 method: 'get'
198 });
199
200 /**
201 * 获取组织人员树
202 * @param guids
203 * @returns
204 */
205 export const getOrganisationTree = (tenantGuid:string) => request({
206 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/get-tenant-organisation-staff-tree?tenantGuid=${tenantGuid}`,
207 method: 'get'
208 });
209
210 /**
211 * 新增修改人员基本信息
212 * @param param
213 * @returns
214 */
215 export const addorUpdateStaff = (param) => request({
216 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/save-or-update`,
217 method: 'put',
218 data: param
219 });
......
1 <svg t="1772248555140" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1764" width="200" height="200"><path d="M438.851435 3.158997c100.42378-18.02947 311.950052 41.873717 329.942951 160.363538V302.089797s46.66451 9.179304 38.472621 81.553179c-8.191889 72.264162-62.243727 92.341604-99.838645 127.705696-37.66806 35.364091-30.719583 91.756469-92.085608 120.574363 0 0-1.645692 1.023986-4.607937 2.669678v69.228775s200.774418 61.658592 261.66502 78.261795C943.969151 805.817818 1005.700885 899.329692 1005.700885 1024H18.285714c0-124.670308 61.731734-218.182182 136.592432-241.953288 21.393995-6.765622 48.419914-16.09121 76.140681-25.89222l16.639774-5.961062c63.889419-22.856833 126.096574-46.408513 126.096575-46.408513l-0.036571-69.594484c-2.559965-1.462837-3.91309-2.303969-3.91309-2.303969-61.402595-28.817895-52.771855-85.210272-92.085607-120.574363a328.187546 328.187546 0 0 0-20.479722-16.712916l-10.569-8.009034c-31.670427-23.91739-62.682578-48.749053-68.789923-102.983746-8.191889-72.300733 16.749487-81.553179 35.985797-81.553179V179.065181C222.826938 79.628816 339.232216 14.057135 438.888006 3.158997z" fill="#B2B2B2" p-id="1765"></path></svg>
...\ No newline at end of file ...\ No newline at end of file
...@@ -34,6 +34,7 @@ import { ...@@ -34,6 +34,7 @@ import {
34 getPathUrl 34 getPathUrl
35 } from "@/api/modules/obsService"; 35 } from "@/api/modules/obsService";
36 import { Editor, EditorExpose } from '@/components/Editor' 36 import { Editor, EditorExpose } from '@/components/Editor'
37 import PasswordStrengthMeter from "../PasswordStrengthMeter/index.vue";
37 38
38 const userStore = useUserStore() 39 const userStore = useUserStore()
39 40
...@@ -51,6 +52,7 @@ const emits = defineEmits([ ...@@ -51,6 +52,7 @@ const emits = defineEmits([
51 "uploadFileChange", 52 "uploadFileChange",
52 "scheduleChange", 53 "scheduleChange",
53 "inputAppendClick", 54 "inputAppendClick",
55 "inputClick",
54 "cascaderChange" 56 "cascaderChange"
55 ]); 57 ]);
56 const props = defineProps({ 58 const props = defineProps({
...@@ -88,7 +90,6 @@ const formRules = computed(() => { ...@@ -88,7 +90,6 @@ const formRules = computed(() => {
88 return reactive<FormRules>(props.rules); 90 return reactive<FormRules>(props.rules);
89 }); 91 });
90 const formItemList: any = computed(() => { 92 const formItemList: any = computed(() => {
91 const readonly = props.readonly ?? false;
92 let itemList = props.itemList ?? []; 93 let itemList = props.itemList ?? [];
93 // return setItemsDisabled(itemList, readonly); 94 // return setItemsDisabled(itemList, readonly);
94 return itemList; 95 return itemList;
...@@ -293,6 +294,7 @@ const handlerIptClick = (row) => { ...@@ -293,6 +294,7 @@ const handlerIptClick = (row) => {
293 ) { 294 ) {
294 row.autocompleteSetting.readonly = false; 295 row.autocompleteSetting.readonly = false;
295 } 296 }
297 emits("inputClick", row, formInline.value)
296 }; 298 };
297 299
298 const toolBtnClick = (btn) => { 300 const toolBtnClick = (btn) => {
...@@ -614,6 +616,12 @@ const panelChange = (scope, row) => { ...@@ -614,6 +616,12 @@ const panelChange = (scope, row) => {
614 } 616 }
615 } 617 }
616 618
619 const handlePwdLenMeterChange = (val, item) => {
620 nextTick(() => {
621 ruleFormRef.value?.validateField([item.field])
622 })
623 }
624
617 </script> 625 </script>
618 <template> 626 <template>
619 <el-form ref="ruleFormRef" class="dialog-form-inline" :class="[props.col]" :inline="true" :model="formInline" @submit.prevent 627 <el-form ref="ruleFormRef" class="dialog-form-inline" :class="[props.col]" :inline="true" :model="formInline" @submit.prevent
...@@ -705,6 +713,20 @@ const panelChange = (scope, row) => { ...@@ -705,6 +713,20 @@ const panelChange = (scope, row) => {
705 {{ item.getName(data, node) }} 713 {{ item.getName(data, node) }}
706 </div> 714 </div>
707 </template> 715 </template>
716 <span v-else-if="item.customInfo" class="custom-tree-node">
717 <span v-if="item.customInfo?.type == 'prefixIcon'" class="custom_icon">
718 <el-icon v-if="data.nodeType == 2" style="margin-top: 3px;">
719 <svg-icon name="file-person" />
720 </el-icon>
721 <el-icon v-else-if="node.expanded" style="margin-top: 3px;">
722 <svg-icon name="file-open" />
723 </el-icon>
724 <el-icon v-else-if="!node.expanded" style="margin-top: 3px;">
725 <svg-icon name="file-closed" />
726 </el-icon>
727 </span>
728 <span style="margin-left: 4px;vertical-align: top;">{{ data[item.props.label] }}</span>
729 </span>
708 <span v-else>{{ data[item.props.label] }}</span> 730 <span v-else>{{ data[item.props.label] }}</span>
709 </template> 731 </template>
710 </el-tree-select> 732 </el-tree-select>
...@@ -1530,6 +1552,15 @@ const panelChange = (scope, row) => { ...@@ -1530,6 +1552,15 @@ const panelChange = (scope, row) => {
1530 v-model.trim="formInline[item.field]" type="password" :placeholder="item.placeholder" 1552 v-model.trim="formInline[item.field]" type="password" :placeholder="item.placeholder"
1531 :clearable="item.clearable" :maxlength="item.maxlength ?? ''" show-password :disabled="item.disabled" 1553 :clearable="item.clearable" :maxlength="item.maxlength ?? ''" show-password :disabled="item.disabled"
1532 :autocomplete="item.autocompleteSetting?.autocomplete || item.autocomplete" :readonly="item.autocompleteSetting?.readonly || item.readonly" @click="handlerIptClick(item)" /> 1554 :autocomplete="item.autocompleteSetting?.autocomplete || item.autocomplete" :readonly="item.autocompleteSetting?.readonly || item.readonly" @click="handlerIptClick(item)" />
1555 <PasswordStrengthMeter
1556 :class="[item.col, { is_block: item.block }]"
1557 v-else-if="item.type == 'password-len-meter'"
1558 :placeholder="item.placeholder"
1559 :disabled="item.disabled"
1560 :store-key="'passwordLenMeter' + item.field"
1561 v-model.trim="formInline[item.field]"
1562 @change="(val) => handlePwdLenMeterChange(val, item)"
1563 ></PasswordStrengthMeter>
1533 <div class="input_def" v-else> 1564 <div class="input_def" v-else>
1534 <span v-if="item.beforeMsg" style="color: #212121;">{{ item.beforeMsg }}</span> 1565 <span v-if="item.beforeMsg" style="color: #212121;">{{ item.beforeMsg }}</span>
1535 <el-input :class="[item.col, { is_block: item.block }]" v-model.trim="formInline[item.field]" :style="item.width ? { width: item.width } : null" 1566 <el-input :class="[item.col, { is_block: item.block }]" v-model.trim="formInline[item.field]" :style="item.width ? { width: item.width } : null"
......
1 <!-- PasswordStrengthContent.vue -->
2 <template>
3 <div class="password-strength-content">
4 <div class="strength-label">密码需满足以下要求:</div>
5
6 <div class="strength-bar">
7 <div
8 class="strength-fill"
9 :class="levelClass"
10 :style="{ width: fillWidth }"
11 ></div>
12 </div>
13 <div class="strength-text" :class="levelClass">
14 密码强度:{{ strengthText }}
15 </div>
16
17 <div class="requirements">
18 <div v-for="(req, key) in requirements" :key="key" class="requirement" :class="getReqClass(key)">
19 <span class="requirement-icon">{{ getReqIcon(key) }}</span>
20 <span>{{ req.label }}</span>
21 </div>
22 </div>
23 </div>
24 </template>
25
26 <script setup lang="ts">
27 import { computed } from 'vue';
28 import { useStorage } from '@/hooks/useStorage'
29
30
31 const props = defineProps({
32 password: {
33 type: String,
34 default: ''
35 },
36 storeKey: {
37 type: String,
38 default: 'firstUnmetRequirement'
39 }
40 });
41
42 const emits = defineEmits(['getCheckResult']);
43
44
45 const { setStorage } = useStorage()
46 const checkPasswordStrength = (pwd) => {
47 if (!pwd) return { score: 0, level: 'weak', checks: {} };
48 const checks = {
49 length: pwd.length >= 8,
50 lower: /[a-z]/.test(pwd),
51 upper: /[A-Z]/.test(pwd),
52 number: /[0-9]/.test(pwd),
53 special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(pwd)
54 };
55 const score = Object.values(checks).filter(Boolean).length;
56 let level = 'weak';
57 if (score >= 5) level = 'strong';
58 else if (score >= 3) level = 'medium';
59 console.log(checks,'checkPasswordStrength');
60 findFirstUnmetRequirement(checks)
61 return { score, level, checks };
62 };
63
64 const result = computed(() => checkPasswordStrength(props.password));
65 const fillWidth = computed(() => `${(result.value.score / 5) * 100}%`);
66 const strengthText = computed(() => ({ weak: '弱', medium: '中', strong: '强' })[result.value.level]);
67 const levelClass = computed(() => result.value.level);
68
69 const requirements = {
70 number: { label: '包含数字 (0-9)' },
71 length: { label: '至少 8 个字符' },
72 lower: { label: '包含小写字母 (a-z)' },
73 upper: { label: '包含大写字母 (A-Z)' },
74 special: { label: '包含特殊符号' }
75 };
76
77 const getReqClass = (key) => (result.value.checks[key] ? 'met' : props.password.length >= 3 ? 'unmet' : 'pending');
78 const getReqIcon = (key) => {
79 if (result.value.checks[key]) return '✓';
80 if (props.password.length >= 3) return '✗';
81 return '○';
82 };
83
84 /**
85 * 匹配未满足的规则
86 * @param checks
87 */
88 const findFirstUnmetRequirement = (checks) => {
89 if (!props.password) return null;
90 console.log(checks,'firstUnmetRequirement');
91 for (const key in requirements) {
92 const isMet = checks[key];
93 if (!isMet) {
94 let label = requirements[key].label;
95 setStorage(props.storeKey, label)
96 return label; // 返回未满足的第一条规则的 label
97 }
98 }
99 setStorage(props.storeKey, '')
100 return null; // 全部满足
101 };
102
103
104 // 暴露方法给父组件
105 defineExpose({
106 });
107 </script>
108
109 <style scoped lang="scss">
110 /* 同前,省略样式 */
111 .password-strength-content {
112 font-size: 14px;
113 }
114
115 .strength-label {
116 margin-bottom: 12px;
117 color: #333;
118 font-weight: 500;
119 }
120
121 .strength-bar {
122 height: 6px;
123 border-radius: 3px;
124 background: #e9ecef;
125 overflow: hidden;
126 margin: 8px 0;
127 }
128
129 .strength-fill {
130 height: 100%;
131 border-radius: 3px;
132 transition: all 0.3s ease-out;
133 }
134
135 .strength-fill.weak {
136 background-color: #dc3545;
137 }
138
139 .strength-fill.medium {
140 background-color: #ffc107;
141 }
142
143 .strength-fill.strong {
144 background-color: #28a745;
145 }
146
147 .strength-text {
148 font-weight: 600;
149 font-size: 13px;
150 margin-bottom: 12px;
151 }
152
153 .strength-text.weak {
154 color: #dc3545;
155 }
156
157 .strength-text.medium {
158 color: #e0a800;
159 }
160
161 .strength-text.strong {
162 color: #28a745;
163 }
164
165 .requirements {
166 color: #555;
167 }
168
169 .requirement {
170 display: flex;
171 align-items: center;
172 margin: 4px 0;
173 }
174
175 .requirement-icon {
176 width: 16px;
177 text-align: center;
178 margin-right: 6px;
179 font-weight: bold;
180 }
181
182 .requirement.met .requirement-icon {
183 color: #28a745;
184 }
185
186 .requirement.unmet .requirement-icon {
187 color: #dc3545;
188 }
189
190 .requirement.pending .requirement-icon {
191 color: #adb5bd;
192 }
193
194 .requirement.met {
195 color: #28a745;
196 }
197
198 .requirement.unmet {
199 color: #dc3545;
200 }
201
202 .requirement.pending {
203 color: #6c757d;
204 }
205 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <!-- PasswordStrengthPopover.vue -->
2 <template>
3 <div class="password-wrapper">
4 <el-popover
5 v-model:visible="popoverVisible"
6 placement="top"
7 :width="300"
8 trigger="manual"
9 popper-class="password-strength-popper"
10 :disabled="!internalValue"
11 >
12 <template #reference>
13 <el-input
14 ref="inputRef"
15 :model-value="internalValue"
16 :placeholder="placeholder"
17 type="password"
18 clearable
19 showPassword
20 :disabled="disabled"
21 autocomplete="new-password"
22 @update:model-value="onInput"
23 @focus="onFocus"
24 @blur="onBlur"
25 @click="focusInput"
26 />
27 </template>
28
29 <PasswordStrengthContent
30 ref="strengthRef"
31 :storeKey="storeKey"
32 :password="internalValue"
33 />
34 </el-popover>
35 </div>
36 </template>
37
38 <script setup lang="ts">
39 import { ref, watch, nextTick } from 'vue';
40 import { ElInput } from 'element-plus';
41 import PasswordStrengthContent from './PasswordStrengthContent.vue';
42
43 const props = defineProps({
44 modelValue: {
45 type: String,
46 default: ''
47 },
48 placeholder: {
49 type: String,
50 default: '请输入密码'
51 },
52 // 新增:用于表单校验的字段名(可选)
53 name: {
54 type: String,
55 default: 'password'
56 },
57 storeKey: {
58 type: String,
59 default: 'firstUnmetRequirement'
60 },
61 disabled: {
62 type: Boolean,
63 default: false
64 }
65 });
66
67 const emit = defineEmits(['update:modelValue','change']);
68
69 const internalValue = ref(props.modelValue);
70
71 watch(
72 () => props.modelValue,
73 (newVal) => {
74 internalValue.value = newVal;
75 }
76 );
77
78 const onInput = (val) => {
79 internalValue.value = val;
80 emit('update:modelValue', val);
81 emit('change', val);
82 };
83
84 const popoverVisible = ref(false);
85 const inputRef = ref(null);
86 const strengthRef = ref(null);
87
88 const onFocus = () => {
89 if (internalValue.value) {
90 popoverVisible.value = true;
91 }
92 };
93
94 const onBlur = () => {
95 setTimeout(() => {
96 const activeEl = document.activeElement;
97 const popoverEl = document.querySelector('.password-strength-popper');
98 if (!popoverEl?.contains(activeEl)) {
99 popoverVisible.value = false;
100 }
101 }, 200);
102 };
103
104 const focusInput = () => {
105 nextTick(() => {
106 inputRef.value?.focus();
107 });
108 };
109
110 watch(internalValue, (newVal) => {
111 if (newVal) {
112 if (document.activeElement === inputRef.value?.$el?.querySelector('input')) {
113 popoverVisible.value = true;
114 }
115 } else {
116 popoverVisible.value = false;
117 }
118 });
119
120
121
122 // 暴露给 el-form 使用
123 defineExpose({
124 });
125 </script>
126
127 <style scoped lang="scss">
128 .password-wrapper {
129 width: 100%;
130 // max-width: 400px;
131 }
132 .password-strength-popper {
133 padding: 14px !important;
134 background: #fff;
135 border: 1px solid #ddd;
136 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
137 border-radius: 8px;
138 }
139 </style>
...\ No newline at end of file ...\ No newline at end of file
1 import dayjs from '@/utils/dayjs'
2 import useUserStore from '@/store/modules/user'
3
4 import { getOrganisationRelTreeListPromise, getTemplateListPromise } from "@/api/modules/dataBasic"
5
6 const currentDate = dayjs(new Date()).format('YYYY-MM-DD')
7
8 const isArray = (val: any): val is Array<any> => {
9 return val && Array.isArray(val)
10 }
11
12 const isNonEmptyArray = (val: any): boolean => {
13 return isArray(val) && val.length > 0;
14 }
15
16 const useGetData = (param = {}) => {
17 const BasicInfo: any = ref({}) // 基础资料
18 const gradeList = ref<any>([]) // 职级关系
19 const platformGradeList = ref<any>([]) // 平台职级
20 const postionList = ref<any>([]) // 职位
21 const tenantRelList = ref<any>([]) // 公司关系
22 const templateList = ref<any>([]) // 菜单模板列表
23 const orgMap = ref<orgMapRes>() // 人员组织信息
24 const organisationTree = ref<any>([]) // 组织树
25 const personelTree = ref<any>([]) // 人员树
26 const amoebaTree = ref<any>([]) // 阿米巴树
27 const financeSubjectDict = ref<any>([]) // 关联财务科目字典
28 const systemSideList = ref<{ // 系统列表
29 systemName: string,
30 guid: string
31 }[]>([])
32 const getFinanceSubject = async (customParam = {}) => { // 获取业务线
33 return await budgetApi.getFinanceSubjectTreePromise((Object.assign({}, param, customParam)))
34 }
35 // !基础数据缓存机制
36 async function getFinanceSubjectTree({ useCache = true, customParam = {} } = {}) {
37 if (isNonEmptyArray(BasicInfo.financeSubject) && useCache) {
38 return BasicInfo.financeSubject
39 } else {
40 let res = await getFinanceSubject(customParam)
41 BasicInfo.financeSubject = useCache ? res : []
42 return res
43 }
44 }
45 async function getGradeTitleRelList({ useCache = true, customParam = {} } = {}) { // 获取职级关系列表
46 if ( isNonEmptyArray(BasicInfo.gradeList) && useCache) {
47 gradeList.value = BasicInfo.gradeList
48 return gradeList.value
49 } else {
50 let res = await userApi.getGradeRelListPromise(customParam)
51 BasicInfo.gradeList = useCache ? res : []
52 gradeList.value = res
53 return gradeList.value
54 }
55 }
56
57 async function getGradeList({ useCache = true, customParam = {} } = {}) { // 获取平台职级列表
58 if ( isNonEmptyArray(BasicInfo.platformGradeList) && useCache) {
59 platformGradeList.value = BasicInfo.platformGradeList
60 return platformGradeList.value
61 } else {
62 let res = await userApi.getGradeListPromise(customParam)
63 BasicInfo.platformGradeList = useCache ? res : []
64 platformGradeList.value = res
65 return platformGradeList.value
66 }
67 }
68
69 async function getPostionList({ useCache = true, customParam = {} } = {}) { // 获取职位
70 if ( isNonEmptyArray(BasicInfo.postionList) && useCache) {
71 postionList.value = BasicInfo.postionList
72 return postionList.value
73 } else {
74 let res = await userApi.getPositionPromise(customParam)
75 BasicInfo.postionList = useCache ? res : []
76 postionList.value = res
77 return postionList.value
78 }
79 }
80
81 async function getTenantRelList(useCache = true) { // 获取公司关系列表
82 if (isNonEmptyArray(BasicInfo.tenantRelList) && useCache) {
83 tenantRelList.value = BasicInfo.tenantRelList
84 return tenantRelList.value
85 } else {
86 let res = await tenantApi.getTenantListPromise('Y')
87 BasicInfo.tenantRelList = useCache ? res : []
88 tenantRelList.value = res
89 return tenantRelList.value
90 }
91 }
92
93 async function getPermissionTemplateList({ useCache = true, customParam = {} } = {}) { // 获取菜单模板
94 if (isNonEmptyArray(templateList.value) && useCache) {
95 return templateList.value
96 } else {
97 let res = await getTemplateListPromise(customParam)
98 templateList.value = res || []
99 return templateList.value
100 }
101 }
102
103 interface orgMapRes {
104 [key:string]:{
105 guid:string, // staffGuid
106 userGuid:string, // userGuid
107 orgGuid:string, // 部门
108 orgName:string,
109 orgGuidTop:string, // 一级部门guid
110 orgNameTop:number,
111 }
112 }
113 // 获取人员组织信息
114 async function getOrgMap({ useCache = true, customParam = {} } = {}) { // 获取人员组织信息
115 const userStore = useUserStore()
116 let staffGuid = userStore.userInfo.staffGuid
117 if (isObject(BasicInfo.orgMap) && BasicInfo.orgMap[staffGuid] && useCache) {
118 orgMap.value = BasicInfo.orgMap
119 return orgMap.value
120 } else {
121 const userStore = useUserStore()
122 let res = await staffApi.getOrgMap([userStore.userInfo.staffGuid])
123 BasicInfo.orgMap = useCache ? res : null
124 orgMap.value = res
125 return orgMap.value
126 }
127 }
128
129 // 获取组织树
130 async function getPersonelTree({ useCache = true, customParam = {} } = {}) {
131 if ( isNonEmptyArray(BasicInfo.personelTree) && useCache) {
132 personelTree.value = BasicInfo.personelTree
133 return personelTree.value
134 } else {
135 const userStore = useUserStore()
136 let res = await tenantApi.getOrganisationTreePromise(userStore.userInfo.tenantGuid)
137 mulTreeData(res)
138 BasicInfo.personelTree = useCache ? res : []
139 personelTree.value = res
140 return personelTree.value
141 }
142 }
143
144 async function getOrganisationTree({ useCache = true, tenantGuid = '' } = {}) { // 获取组织树
145 if ( isNonEmptyArray(BasicInfo.organisationTree) && useCache) {
146 organisationTree.value = BasicInfo.organisationTree
147 return organisationTree.value
148 } else {
149 let res = await getOrganisationRelTreeListPromise(tenantGuid)
150 // getOrgtreeData(res,false)
151 BasicInfo.organisationTree = useCache ? res : []
152 organisationTree.value = res
153 return organisationTree.value
154 }
155 }
156
157 async function getAmoebaTree({ useCache = true, customParam = {} } = {}) { // 获取组织树
158 if ( isNonEmptyArray(BasicInfo.amoebaTree) && useCache) {
159 amoebaTree.value = BasicInfo.amoebaTree
160 return amoebaTree.value
161 } else {
162 let res = await tenantApi.getAmoebaTreePromise2(currentDate)
163 getOrgtreeData(res,false)
164 BasicInfo.amoebaTree = useCache ? res : []
165 amoebaTree.value = res
166 return amoebaTree.value
167 }
168 }
169
170 async function getFinanceDict({ useCache = true, customParam = {} } = {}) { // 获取财务科目关联字典
171 if ( isNonEmptyArray(BasicInfo.financeSubjectDict) && useCache) {
172 financeSubjectDict.value = BasicInfo.financeSubjectDict
173 return financeSubjectDict.value
174 } else {
175 let res = await budgetApi.getSubjectDict(customParam)
176 getOrgtreeData(res,false)
177 BasicInfo.financeSubjectDict = useCache ? res : []
178 financeSubjectDict.value = res
179 return financeSubjectDict.value
180 }
181 }
182
183 // 获取子系统列表
184 async function getSystemSideList({ useCache = true, customParam = {} } = {}) {
185 if ( isNonEmptyArray(BasicInfo.systemSideList) && useCache) {
186 systemSideList.value = BasicInfo.systemSideList
187 return systemSideList.value
188 } else {
189 let res = await authApi.getSystemSideData()
190 BasicInfo.systemSideList = useCache ? res : []
191 systemSideList.value = res
192 return systemSideList.value
193 }
194 }
195
196 return {
197 getPersonelTree,
198 getFinanceSubject,
199 getGradeTitleRelList,
200 getGradeList,
201 getPostionList,
202 getTenantRelList,
203 getFinanceSubjectTree,
204 getPermissionTemplateList,
205 getOrgMap,
206 getOrganisationTree,
207 getAmoebaTree,
208 getFinanceDict,
209 getSystemSideList
210 }
211 }
212
213 export default useGetData
1 // 获取传入的值的类型
2 const getValueType = (value: any) => {
3 const type = Object.prototype.toString.call(value)
4 return type.slice(8, -1)
5 }
6
7 export const useStorage = (type: 'sessionStorage' | 'localStorage' = 'sessionStorage') => {
8 const setStorage = (key: string, value: any) => {
9 const valueType = getValueType(value)
10 window[type].setItem(key, JSON.stringify({ type: valueType, value }))
11 }
12
13 const getStorage = (key: string) => {
14 const value = window[type].getItem(key)
15 if (value) {
16 const { value: val } = JSON.parse(value)
17 return val
18 } else {
19 return value
20 }
21 }
22
23 const removeStorage = (key: string) => {
24 window[type].removeItem(key)
25 }
26
27 const clear = (excludes?: string[]) => {
28 // 获取排除项
29 const keys = Object.keys(window[type])
30 const defaultExcludes = ['dynamicRouter', 'serverDynamicRouter']
31 const excludesArr = excludes ? [...excludes, ...defaultExcludes] : defaultExcludes
32 const excludesKeys = excludesArr ? keys.filter((key) => !excludesArr.includes(key)) : keys
33 // 排除项不清除
34 excludesKeys.forEach((key) => {
35 window[type].removeItem(key)
36 })
37 // window[type].clear()
38 }
39
40 return {
41 setStorage,
42 getStorage,
43 removeStorage,
44 clear
45 }
46 }
...@@ -363,6 +363,19 @@ export const useValidator = () => { ...@@ -363,6 +363,19 @@ export const useValidator = () => {
363 } 363 }
364 } 364 }
365 365
366 const phone = (message?: string): FormItemRule => {
367 return {
368 validator: (_, val, callback) => {
369 if (val && !/^\d+(?:-\d+)?$/i.test(val)) {
370 callback(new Error(message || '电话格式不正确'));
371 } else {
372 callback();
373 }
374 },
375 trigger: 'blur'
376 };
377 };
378
366 const mobileNumber = (message?: string): FormItemRule => { 379 const mobileNumber = (message?: string): FormItemRule => {
367 return { 380 return {
368 validator: (_, val, callback) => { 381 validator: (_, val, callback) => {
...@@ -393,6 +406,7 @@ export const useValidator = () => { ...@@ -393,6 +406,7 @@ export const useValidator = () => {
393 validateDomainList, 406 validateDomainList,
394 minChineseCount, 407 minChineseCount,
395 email, 408 email,
396 mobileNumber 409 mobileNumber,
410 phone
397 } 411 }
398 } 412 }
......
...@@ -22,6 +22,46 @@ const routes: RouteRecordRaw[] = [ ...@@ -22,6 +22,46 @@ const routes: RouteRecordRaw[] = [
22 }, 22 },
23 }] 23 }]
24 }, 24 },
25 {
26 path: '/data-basic/user-manage',
27 component: Layout,
28 meta: {
29 title: '用户管理',
30 icon: 'sidebar-videos',
31 },
32 children: [
33 {
34 path: '',
35 name: 'userManagement',
36 component: () => import('@/views/data_basic/userManagement.vue'),
37 meta: {
38 title: '用户管理',
39 sidebar: false,
40 breadcrumb: false,
41 cache: true
42 },
43 },
44 {
45 path: 'user-edit',
46 name: 'addUser',
47 component: () => import('@/views/data_basic/addUser.vue'),
48 meta: {
49 title: '新增用户',
50 sidebar: false,
51 breadcrumb: false,
52 cache: true,
53 reuse: true,
54 editPage: true,
55 activeMenu: '/data-basic/user-manage'
56 },
57 beforeEnter: (to) => {
58 if (to.query.userName) {
59 to.meta.title = `编辑-${to.query.userName}`;
60 }
61 }
62 },
63 ],
64 },
25 ] 65 ]
26 66
27 67
......
1 const useDataBasicStore = defineStore(
2 // api标签分类guid
3 'dataBasic',
4
5 () => {
6
7 const isUpdate = ref(false);
8 function setIsUpdate(update: boolean) {
9 isUpdate.value = update;
10 }
11
12 return {
13 isUpdate,
14 setIsUpdate,
15 }
16 },
17 )
18
19 export default useDataBasicStore
...\ No newline at end of file ...\ No newline at end of file
...@@ -41,6 +41,8 @@ declare module '@vue/runtime-core' { ...@@ -41,6 +41,8 @@ declare module '@vue/runtime-core' {
41 PageHeader: typeof import('./../components/PageHeader/index.vue')['default'] 41 PageHeader: typeof import('./../components/PageHeader/index.vue')['default']
42 PageMain: typeof import('./../components/PageMain/index.vue')['default'] 42 PageMain: typeof import('./../components/PageMain/index.vue')['default']
43 PageNav: typeof import('./../components/PageNav/index.vue')['default'] 43 PageNav: typeof import('./../components/PageNav/index.vue')['default']
44 PasswordStrengthContent: typeof import('./../components/PasswordStrengthMeter/PasswordStrengthContent.vue')['default']
45 PasswordStrengthMeter: typeof import('./../components/PasswordStrengthMeter/index.vue')['default']
44 PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] 46 PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']
45 Popover: typeof import('./../components/Popover/index.vue')['default'] 47 Popover: typeof import('./../components/Popover/index.vue')['default']
46 RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default'] 48 RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default']
...@@ -49,6 +51,7 @@ declare module '@vue/runtime-core' { ...@@ -49,6 +51,7 @@ declare module '@vue/runtime-core' {
49 Schedule: typeof import('./../components/Schedule/index.vue')['default'] 51 Schedule: typeof import('./../components/Schedule/index.vue')['default']
50 SearchBar: typeof import('./../components/SearchBar/index.vue')['default'] 52 SearchBar: typeof import('./../components/SearchBar/index.vue')['default']
51 SecondAndMinute: typeof import('./../components/Schedule/component/secondAndMinute.vue')['default'] 53 SecondAndMinute: typeof import('./../components/Schedule/component/secondAndMinute.vue')['default']
54 SelectPersonnel: typeof import('./../components/SelectPersonnel/src/SelectPersonnel.vue')['default']
52 SplitPane: typeof import('./../components/SplitPane/index.vue')['default'] 55 SplitPane: typeof import('./../components/SplitPane/index.vue')['default']
53 StepBar: typeof import('./../components/StepBar/index.vue')['default'] 56 StepBar: typeof import('./../components/StepBar/index.vue')['default']
54 SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default'] 57 SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
...@@ -59,6 +62,7 @@ declare module '@vue/runtime-core' { ...@@ -59,6 +62,7 @@ declare module '@vue/runtime-core' {
59 Table_search: typeof import('./../components/Tools/table_search.vue')['default'] 62 Table_search: typeof import('./../components/Tools/table_search.vue')['default']
60 Table_tools: typeof import('./../components/Tools/table_tools.vue')['default'] 63 Table_tools: typeof import('./../components/Tools/table_tools.vue')['default']
61 Table_v2: typeof import('./../components/Table/table_v2.vue')['default'] 64 Table_v2: typeof import('./../components/Table/table_v2.vue')['default']
65 TableActions: typeof import('./../components/TablePlus/src/components/TableActions.vue')['default']
62 Tabs: typeof import('./../components/Tabs/index.vue')['default'] 66 Tabs: typeof import('./../components/Tabs/index.vue')['default']
63 Toolbar: typeof import('./../components/LineageGraph/toolbar.vue')['default'] 67 Toolbar: typeof import('./../components/LineageGraph/toolbar.vue')['default']
64 Topbar: typeof import('./../components/LineageGraph/topbar.vue')['default'] 68 Topbar: typeof import('./../components/LineageGraph/topbar.vue')['default']
......
...@@ -1228,3 +1228,30 @@ export const getPublicIP = async (): Promise<string> => { ...@@ -1228,3 +1228,30 @@ export const getPublicIP = async (): Promise<string> => {
1228 } 1228 }
1229 } 1229 }
1230 } 1230 }
1231
1232 export const isArray = (val: any): val is Array<any> => {
1233 return val && Array.isArray(val)
1234 }
1235 export const isNonEmptyArray = (val: any): boolean => {
1236 return isArray(val) && val.length > 0;
1237 }
1238
1239 /**
1240 * 判断对象是否为空
1241 * @param obj
1242 * @param keyList 需要判断的keys 不传则判断所有属性
1243 * @returns
1244 */
1245 export const isAllPropertiesEmpty = (obj,keyList?: string[]) => {
1246 const keys = keyList && keyList.length ? keyList : Object.keys(obj);
1247 // 遍历每个属性
1248 for (let key of keys) {
1249 const value = obj[key];
1250 if (value === null || value === undefined || value === "" || (Array.isArray(value) && value.length === 0) || (typeof value === "object" && Object.keys(value).length === 0)) {
1251 // 如果属性没有值,则返回false
1252 return true;
1253 }
1254 }
1255 // 如果所有属性都有值,则返回true
1256 return false;
1257 }
...\ No newline at end of file ...\ No newline at end of file
......
1 <template>
2 <div class="container_wrap full" v-loading="fullscreenLoading">
3 <div class="content_main panel">
4 <ContentWrap title="账户信息" expandSwicth style="margin-top: 15px">
5 <Form ref="formItem1" :itemList="schemaParam1" :rules="schemaParamRules" formId="model-select-edit"
6 col="col3" />
7 </ContentWrap>
8 <ContentWrap title="入职部门" expandSwicth style="margin-top: 15px">
9 <EntryOrgList ref="entryOrgListRef" :organisationJson="organisationInfo"></EntryOrgList>
10 </ContentWrap>
11 </div>
12 <div class="submit_btn_wrap">
13 <template v-for="(item, index) in schemaBtns" :key="index">
14 <ElButton v-if="item.visible" :type="item.type" @click="() => { if (item.event) item.event(item.param) }">
15 {{ item.label }}
16 </ElButton>
17 </template>
18 </div>
19 </div>
20 </template>
21
22 <script setup lang="ts" name="addUser">
23 import EntryOrgList from './components/EntryOrgList.vue'
24 import { unref, ref, computed, onBeforeMount, onMounted, onUnmounted } from 'vue'
25 import { useValidator } from '@/hooks/useValidator'
26 import useGetData from '@/hooks/useGetData';
27 import useUserStore from "@/store/modules/user";
28 import { getStaffDetail, getOrganisationTree, addorUpdateStaff } from '@/api/modules/dataBasic'
29 import useDataBasicStore from '@/store/modules/dataBasic';
30
31 const userStore = useUserStore();
32 const userData = JSON.parse(userStore.userData)
33 const dataBasicStore = useDataBasicStore();
34
35 // 全局状态和工具
36 const { getPermissionTemplateList } = useGetData()
37 const { proxy } = getCurrentInstance() as any;
38 const { required, phone, minChineseCount } = useValidator()
39 const route = useRoute()
40 const router = useRouter()
41
42 const fullPath = route.fullPath;
43
44 // 加载状态
45 const fullscreenLoading = ref(false)
46
47 // 响应式数据
48 const formItem1 = ref() // 账户信息表单引用
49 const password = ref('')
50 const detailData = ref<any>({}) // 员工详情数据
51 const organisationInfo = ref<any[]>([]) // 入职部门信息
52 const templateList = ref<any[]>([])
53
54 // 提交状态计算属性
55 const isDetail = computed((): boolean => route.query.isDetail === 'isDetail')
56 const isEdit = computed((): boolean => route.query.isEdit === 'isEdit')
57
58 const tenantGuid = computed((): string => userData.tenantGuid)
59
60 // 业务GUID计算属性
61 const bizGuid = computed((): string => route.query.guid as string)
62
63 /** 选择人员上级的列表 */
64 const schemaSatffTreeList: any = ref([]);
65
66 // 表单配置
67 const schemaParam1 = ref([
68 {
69 label: '员工编号',
70 type: 'input',
71 placeholder: '请输入',
72 field: 'staffNo',
73 maxlength: 50,
74 default: '',
75 required: false,
76 clearable: true,
77 visible: true,
78 },
79 {
80 label: '人员姓名',
81 type: 'input',
82 placeholder: '请输入',
83 field: 'staffName',
84 maxlength: 50,
85 default: '',
86 required: true,
87 clearable: true,
88 visible: true,
89 },
90 {
91 field: 'mobileNo',
92 label: '手机号',
93 type: 'input',
94 placeholder: '请输入',
95 maxlength: 50,
96 default: '',
97 required: true,
98 clearable: true,
99 visible: true,
100 },
101 {
102 label: '密码',
103 type: 'password-len-meter',
104 placeholder: '请输入',
105 field: 'password',
106 default: '',
107 clearable: true,
108 required: true,
109 autocompleteSetting: {
110 autocomplete: "one-time-code",
111 },
112 },
113 {
114 label: "功能权限",
115 type: "select",
116 placeholder: "请选择",
117 field: "funcPermissionTemplateJson",
118 options: templateList.value,
119 props: {
120 label: 'templateName',
121 value: 'guid'
122 },
123 default: [],
124 multiple: true,
125 collapse: true,
126 tagsTooltip: true,
127 filterable: true,
128 clearable: true,
129 required: true,
130 },
131 {
132 label: '账号状态',
133 type: 'switch',
134 field: 'bizState',
135 default: 'Y',
136 placeholder: '请选择',
137 activeValue: 'Y',
138 inactiveValue: 'S',
139 switchWidth: 32,
140 required: true,
141 },
142 {
143 label: '直接上级',
144 type: 'tree-select',
145 placeholder: '请选择',
146 field: 'leaderGuid',
147 nodeKey: 'value',
148 options: schemaSatffTreeList.value,
149 checkStrictly: false,//只能选择叶子节点。
150 lazy: false,
151 multiple: false,
152 props: {
153 label: "name",
154 value: "guid",
155 children: 'children'
156 },
157 customInfo: {
158 type: 'prefixIcon'
159 },
160 filterable: true,
161 clearable: true,
162 default: '',
163 required: false,
164 },
165 // {
166 // field: 'leaderName',
167 // label: '直接上级:',
168 // type: 'input',
169 // placeholder: '请选择',
170 // required: false,
171 // clearable: true,
172 // // componentProps: {
173 // // readonly: true,
174 // // on: {
175 // // click: (val: any) => {
176 // // schemaSatff.value.visible = true
177 // // operate.value = '直接上级'
178 // // }
179 // // },
180 // // formItemProps: {}
181 // // }
182 // },
183 // {
184 // field: 'leaderGuid',
185 // label: '直接上级:',
186 // type: 'input',
187 // visible: false
188 // }
189 ])
190
191 const schemaParamRules = ref({
192 staffName: [required(), minChineseCount()],
193 mobileNo: [required(), phone()],
194 funcPermissionTemplateJson: [{ type: 'array', required: true, trigger: 'change', message: "请选择功能权限" }],
195 password: [{
196 required: true,
197 message: "该项为必填项",
198 trigger: "blur",
199 },
200 {
201 validator: (rule: any, value: any, callback: any) => {
202 let getStorage = (key) => {
203 const value = window['sessionStorage'].getItem(key);
204 if (value) {
205 const { value: val } = JSON.parse(value);
206 return val;
207 } else {
208 return value;
209 }
210 };
211 if (!value) {
212 callback();
213 // callback(new Error('该项为必填项'));
214 } else if (getStorage('passwordLenMeter' + 'pwd')) {
215 callback(new Error(getStorage('passwordLenMeter' + 'pwd')));
216 } else {
217 callback();
218 }
219 },
220 trigger: ['change', 'blur']
221 }],
222 });
223
224 // 页面按钮配置
225 const schemaBtns = computed(() => {
226 return <any>[
227 { visible: true, type: "", label: "关闭", value: "cancel", event: cancel, param: "" },
228 // { type: "primary", visible: !unref(isEdit) && !unref(isDetail), label: "提交并添加下一个", value: "cancel", event: submitForm, param: "submitNext" },
229 { type: "primary", visible: !unref(isDetail), label: "提交", value: "cancel", event: submitHandle, param: "submit" }
230 ]
231 })
232
233 const entryOrgListRef = ref();
234
235 const submitHandle = async () => {
236 formItem1.value.ruleFormRef?.validate((valid, errorItem) => {
237 if (valid) {
238 let resultForm1 = formItem1.value.formInline;
239 let orgOne = organisationInfo.value.find(item => item.isMainOrg == 'Y')
240
241 if (!orgOne) {
242 proxy.$message.warning('请选择主责部门')
243 return
244 }
245
246 let result = entryOrgListRef.value.validateData();
247 if (!result) {
248 return;
249 }
250
251 let submitParam: any = {
252 staffName: resultForm1.staffName,
253 leaderGuid: resultForm1.leaderGuid,
254 staffMain: {
255 ...resultForm1,
256 staffNo: resultForm1.staffNo || null,
257 userGuid: detailData.value.userGuid,
258 guid: unref(bizGuid) || '',
259 tenantGuid: tenantGuid.value,
260 },
261 organisationInfo: organisationInfo.value
262 }
263
264 let message = '提交成功'
265 let param = {
266 guid: unref(bizGuid) || '',
267 ...submitParam
268 }
269
270 addorUpdateStaff(param).then((res: any) => {
271 if (res?.code == proxy.$passCode) {
272 proxy.$ElMessage({
273 type: "success",
274 message: message,
275 });
276 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
277 router.push({
278 name: "userManagement",
279 });
280 dataBasicStore.setIsUpdate(true);
281 } else {
282 res?.msg && proxy.$ElMessage({
283 type: "error",
284 message: res.msg,
285 });
286 }
287 })
288 } else {
289 }
290 });
291 }
292
293 const cancel = () => {
294 if (isDetail.value) {
295 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
296 router.push({
297 name: 'userManagement'
298 });
299 return;
300 }
301 proxy.$openMessageBox("当前页面尚未保存,确定放弃修改吗?", () => {
302 userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
303 router.push({
304 name: 'userManagement'
305 });
306 }, () => {
307 proxy.$ElMessage.info("已取消");
308 });
309 }
310
311 // 获取详情
312 async function getDetail() {
313 if (!unref(bizGuid)) return
314
315 fullscreenLoading.value = true
316 try {
317 let res: any = await getStaffDetail(unref(bizGuid))
318 if (res?.code == proxy.$passCode) {
319 detailData.value = res.data || {}
320 setDetail()
321 }
322 } catch (error) {
323 console.error('获取详情失败:', error)
324 proxy.$ElMessage.error('获取详情失败')
325 } finally {
326 fullscreenLoading.value = false
327 }
328 }
329
330 function setDetail() {
331 let funcPermissionTemplateJson = detailData.value.funcPermissionTemplateJson
332 let formData = {
333 funcPermissionTemplateJson,
334 mobileNo: detailData.value.mobileNo,
335 staffName: detailData.value.staffName,
336 staffNo: detailData.value.staffNo,
337 bizState: detailData.value.bizState,
338 leaderGuid: detailData.value.leaderGuid,
339 leaderName: detailData.value.leaderName,
340 }
341
342 nextTick(() => {
343 formItem1.value.setValue(formData)
344 organisationInfo.value = detailData.value.staffOrgRelRSVOList || []
345 })
346 }
347
348 // 初始化权限模板列表
349 const initPermissionTemplates = async () => {
350 try {
351 const res = await getPermissionTemplateList({
352 useCache: false,
353 customParam: {
354 tenantGuid: tenantGuid.value
355 }
356 })
357
358 templateList.value = res || [];
359 let item = schemaParam1.value.find(item => item.field === 'funcPermissionTemplateJson');
360 item && (item.options = templateList.value);
361 } catch (error) {
362 console.error('获取权限模板失败:', error)
363 }
364 }
365
366 // 生命周期钩子
367 onBeforeMount(() => {
368 initPermissionTemplates()
369
370 if (unref(bizGuid)) {
371 getDetail()
372 }
373 // 组件挂载前的初始化逻辑
374 getOrganisationTree(tenantGuid.value).then((res: any) => {
375 if (res?.code == proxy.$passCode) {
376 schemaSatffTreeList.value = res.data || [];
377 let item = schemaParam1.value.at(-1);
378 item && (item.options = schemaSatffTreeList.value);
379 } else {
380 proxy.$ElMessage({
381 type: 'error',
382 message: res?.msg || '获取组织人员树列表失败',
383 })
384 }
385 })
386 })
387
388 onMounted(() => {
389
390 })
391
392 onUnmounted(() => {
393 // 组件销毁时的清理工作
394 })
395
396 </script>
397
398 <style lang="scss" scoped>
399 .add-bank-item {
400 height: 40px;
401 width: 100%;
402 background-color: #FAFAFA;
403 border: 1px solid rgba(229, 229, 229, 1);
404 border-radius: 2px;
405 text-align: center;
406 line-height: 40px;
407 color: #999999;
408 padding-bottom: 10px;
409 margin-top: 5px;
410 }
411
412 .add-bank-item:hover {
413 background: #EBF6F7;
414 border: 1px solid rgba(79, 161, 164, 1);
415 }
416
417 .container_wrap {
418 overflow: hidden;
419
420 .content_main {
421 height: calc(100% - 45px);
422 overflow: hidden auto;
423
424 &.panel {
425 padding: 0 16px 16px;
426 }
427 }
428 }
429
430 .submit_btn_wrap {
431 height: 44px;
432 margin: 0 -8px;
433 display: flex;
434 justify-content: center;
435 align-items: center;
436 border-top: 1px solid #d9d9d9;
437 }
438 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div class="entry-org-container">
3 <el-table ref="tableRef" class="entry-org-table" :data="tableData" :height="tableHeight"
4 :highlight-current-row="true" stripe tooltip-effect="light" border>
5 <el-table-column label="序号" width="56" align="center" fixed="left" :formatter="formatIndex" />
6 <el-table-column prop="organisationName" label="所属部门" :width="columnWidths.organisationName" align="left"
7 :show-overflow-tooltip="true">
8 <template #header>
9 <span>所属部门</span>
10 <span style="color:red;margin-left: 2px;">*</span>
11 </template>
12 <template #default="scope">
13 <el-tree-select v-if="!isDetail" v-model="scope.row.organisationGuid" placeholder="请选择" :data="organisationList"
14 @node-click="(node, nodeObj) => handleTreeSelectNodeChange(node, nodeObj, scope)"
15 :props="{
16 value: 'guid',
17 label: 'name',
18 children: 'children'
19 }" clearable filterable :checkStrictly="true" :showAllLevels="false" class="input-width">
20 </el-tree-select>
21 <!-- <el-input v-if="!isDetail" v-model="scope.row.organisationName" placeholder="请选择" readonly
22 @click="openOrgSelector(scope.$index)"></el-input> -->
23 <span v-else>{{ scope.row.organisationName || '--' }}</span>
24 </template>
25 </el-table-column>
26 <el-table-column prop="isLeader" label="是否部门负责人" :width="columnWidths.isLeader" align="left"
27 :show-overflow-tooltip="true">
28 <template #header>
29 <span>是否部门负责人</span>
30 <span style="color:red;margin-left: 2px;">*</span>
31 </template>
32 <template #default="scope">
33 <el-select v-if="!isDetail" v-model="scope.row.isLeader" placeholder="请选择" clearable filterable>
34 <el-option v-for="opt in yesNoOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
35 </el-select>
36 <span v-else>{{ scope.row.isLeader == 'Y' ? '是' : '否' }}</span>
37 </template>
38 </el-table-column>
39 <el-table-column prop="isMainOrg" label="是否主责部门" :width="columnWidths.isMainOrg" align="left"
40 :show-overflow-tooltip="true">
41 <template #header>
42 <span>是否主责部门</span>
43 <span style="color:red;margin-left: 2px;">*</span>
44 </template>
45 <template #default="scope">
46 <el-select v-if="!isDetail" v-model="scope.row.isMainOrg" placeholder="请选择" clearable filterable
47 @change="handleMainOrgChange(scope.row)">
48 <el-option v-for="opt in yesNoOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
49 </el-select>
50 <span v-else>{{ scope.row.isMainOrg == 'Y' ? '是' : '否' }}</span>
51 </template>
52 </el-table-column>
53 <el-table-column v-if="!isDetail" label="操作" width="140" align="left" fixed="right">
54 <template #default="scope">
55 <span class="text_btn" @click="deleteItem(scope)" v-preReClick>删除</span>
56 </template>
57 </el-table-column>
58 </el-table>
59 </div>
60 <div v-if="!isDetail" class="add-btn-container">
61 <el-button link @click="addNewRow" :icon="CirclePlus" v-preReClick>
62 新增入职部门
63 </el-button>
64 </div>
65 </template>
66
67 <script lang="ts" setup name="EntryOrgList">
68 import { CirclePlus } from "@element-plus/icons-vue";
69 import {
70 getOrganisationRelTreeList
71 } from '@/api/modules/dataBasic';
72 import { isAllPropertiesEmpty } from '@/utils/common';
73 import useUserStore from "@/store/modules/user";
74
75 const userStore = useUserStore();
76 const userData = JSON.parse(userStore.userData)
77
78 const { proxy } = getCurrentInstance() as any;
79
80 // Props 定义
81 const props = defineProps({
82 disabled: {
83 type: Boolean,
84 default: false
85 },
86 organisationJson: {
87 type: Array as PropType<any[]>,
88 default: () => []
89 },
90 projectDate: {
91 type: Array as PropType<any[]>,
92 default: () => []
93 },
94 });
95
96 // 响应式数据
97 const tableRef = ref();
98 const currentIndex = ref<number>(0);
99 const organisationList = ref<any[]>([]);
100 const yesNoOptions = ref<any[]>([]);
101
102 // 表格数据计算属性
103 const tableData = computed(() => {
104 return props.organisationJson;
105 });
106
107 // 详情状态计算属性
108 const isDetail = computed((): boolean => {
109 return proxy.$route.query.isDetail || props.disabled;
110 });
111
112 // 表格高度计算
113 const tableHeight = computed(() => {
114 return 'auto';
115 });
116
117 // 列宽配置
118 const columnWidths = computed(() => {
119 return {
120 organisationName: '180px',
121 isLeader: '140px',
122 isMainOrg: '140px'
123 };
124 });
125
126 // 初始化数据
127 const initializeData = async () => {
128 try {
129 // 获取是否字典选项
130 yesNoOptions.value = [{
131 value: 'Y',
132 label: '是'
133 }, {
134 value: 'N',
135 label: '否'
136 }];
137
138 // 获取组织列表
139 await getOrganisationList();
140 } catch (error) {
141 console.error('初始化数据失败:', error);
142 }
143 };
144
145 // 获取组织列表
146 const getOrganisationList = async () => {
147 try {
148 const res: any = await getOrganisationRelTreeList({
149 tenantGuid: userData.tenantGuid
150 });
151 organisationList.value = res?.data || [];
152 } catch (error) {
153 console.error('获取组织列表失败:', error);
154 }
155 };
156
157 // 格式化序号
158 const formatIndex = (row: any, column: any, cellValue: any, index: number) => {
159 return String(index + 1);
160 };
161
162 // 处理主责部门变更
163 const handleMainOrgChange = (row: any) => {
164 // 检查是否已经有主责部门
165 const mainOrgCount = tableData.value.filter(item => item.isMainOrg === 'Y').length;
166 if (mainOrgCount > 1) {
167 row.isMainOrg = null;
168 proxy.$message.warning('只能选择一个主责部门');
169 }
170 };
171
172 // 添加新行
173 const addNewRow = () => {
174 // 检查最后一行是否填写完整
175 if (tableData.value.length > 0) {
176 const lastItem = tableData.value[tableData.value.length - 1];
177 const isEmpty = isAllPropertiesEmpty(lastItem, ['organisationGuid', 'isLeader', 'isMainOrg']);
178 if (isEmpty) {
179 proxy.$message.warning('请填写完整');
180 return;
181 }
182 }
183 unref(tableData).push({});
184 };
185
186 // 删除行
187 const deleteItem = (scope: any) => {
188 if (isDetail.value) return;
189
190 proxy.$openMessageBox('确定删除吗?', () => {
191 unref(tableData).splice(scope.$index, 1);
192 proxy.$ElMessage.success('删除成功');
193 }, () => {
194 proxy.$ElMessage.info('已取消删除');
195 });
196 };
197
198 const currentTreeNode = ref({});
199 const handleTreeSelectNodeChange = (node, nodeObj, scope) => {
200 currentTreeNode.value = node;
201 scope.row.organisationName = node.name;
202 }
203
204 // 验证数据
205 const validateData = (): boolean => {
206 const guidSet = new Set();
207 for (const item of tableData.value) {
208 if (!item.organisationGuid) {
209 proxy.$ElMessage.error('所属部门必填,请填写完整');
210 return false;
211 }
212 if (!item.isLeader) {
213 proxy.$ElMessage.error('是否部门负责人必填,请填写完整');
214 return false;
215 }
216 if (!item.isMainOrg) {
217 proxy.$ElMessage.error('是否主责部门必填,请填写完整');
218 return false;
219 }
220 if (guidSet.has(item.organisationGuid)) {
221 proxy.$ElMessage.error('部门不能重复');
222 return false;
223 }
224 guidSet.add(item.organisationGuid);
225 }
226
227 return true;
228 };
229
230 // 暴露方法
231 defineExpose({
232 tableData,
233 validateData,
234 tableRef
235 });
236
237 // 生命周期钩子
238 onBeforeMount(() => {
239 initializeData();
240 });
241
242 onMounted(() => {
243 // 组件挂载后的逻辑
244 });
245 </script>
246
247 <style lang="scss" scoped>
248 .entry-org-container {
249 .entry-org-table {
250 :deep(.el-table__row) {
251 .el-input.is-disabled .el-input__inner {
252 background-color: #f5f7fa;
253 cursor: not-allowed;
254 }
255 }
256 }
257 }
258
259
260 .add-btn-container {
261 .el-button--default {
262 padding: 4px 0px;
263 margin-top: 4px;
264 }
265
266 :deep(.el-icon) {
267 width: 16px;
268 height: 16px;
269
270 svg {
271 width: 16px;
272 height: 16px;
273 }
274 }
275 }
276 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <route lang="yaml">
2 # 用户管理
3 name: userManagement
4 </route>
5
6 <template>
7 <div class="container_wrap full flex">
8 <!-- 侧边tree组件 start -->
9 <div class="aside_wrap">
10 <div class="aside_title">{{ '企业组织列表' }}</div>
11 <Tree :treeInfo="treeInfo" @nodeClick="nodeClick" />
12 </div>
13 <!-- 侧边tree组件 end -->
14 <!-- 列表区域 start -->
15 <div class="main_wrap">
16 <!-- 头部搜索和工具栏 -->
17 <div class="table_tool_wrap">
18 <div class="aside_title">
19 {{ title }}
20 </div>
21 <TableTools :searchItems="searchItemList" :searchId="'user-manage-search'" @search="toSearch" :init="false" />
22 <div class="tools_btns">
23 <el-button type="primary" @click="handleCreate">新增用户</el-button>
24 </div>
25 </div>
26 <!-- 表格展示 -->
27 <div class="table_panel_wrap">
28 <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
29 </div>
30 </div>
31 <!-- 列表区域 end -->
32 </div>
33 </template>
34
35 <script lang="ts" setup name="userManagement">
36
37 import { ref, computed } from "vue";
38 import TableTools from "@/components/Tools/table_tools.vue";
39 import { TableColumnWidth } from "@/utils/enum";
40 import { commonPageConfig } from '@/components/PageNav/index';
41 import useGetData from '@/hooks/useGetData'
42 import {
43 getStaff,
44 removeStaff,
45 resetPwd2,
46 } from '@/api/modules/dataBasic';
47 import Moment from 'moment';
48 import useUserStore from "@/store/modules/user";
49 import useDataBasicStore from "@/store/modules/dataBasic";
50
51 const userStore = useUserStore();
52 const userData = JSON.parse(userStore.userData)
53 const dataBasicStore = useDataBasicStore();
54
55 const { getOrganisationTree } = useGetData()
56 const route = useRoute();
57 const router = useRouter();
58
59 const { proxy } = getCurrentInstance() as any;
60
61 const tenantGuid = computed(() => userData.tenantGuid)
62 const tenantName = computed(() => userData.tenantName)
63
64 // 搜索配置
65 const searchItemList = ref([
66 {
67 type: "input",
68 label: "",
69 field: "staffName",
70 default: "",
71 placeholder: "姓名",
72 maxlength: 50,
73 clearable: true,
74 },
75 {
76 type: "input",
77 label: "",
78 field: "mobileNo",
79 default: "",
80 placeholder: "手机号",
81 maxlength: 50,
82 clearable: true,
83 },
84 {
85 type: "select",
86 label: "",
87 field: "bizState",
88 default: "",
89 options: [{
90 value: 'Y',
91 label: '启用'
92 }, {
93 value: 'S',
94 label: '停用'
95 }],
96 placeholder: "启用状态",
97 clearable: true,
98 }
99 ]);
100
101 // 分页配置
102 const page = ref({
103 ...commonPageConfig,
104 staffName: '',
105 mobileNo: '',
106 bizState: ''
107 });
108
109 const currentRow: any = ref({});
110
111 // 表格配置
112 const tableInfo = ref({
113 id: 'user-table',
114 rowKey: 'guid',
115 fields: [
116 { label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
117 { label: "姓名", field: "staffName", width: 120 },
118 { label: "手机号", field: "mobileNo", width: 140 },
119 { label: "员工编号", field: "staffNo", width: 140 },
120 {
121 label: "所属部门",
122 field: "organisationNameList",
123 width: 180,
124 getName: (scope) => {
125 const cellValue = scope.row.organisationNameList;
126 return Array.isArray(cellValue) && cellValue.length > 0 ? cellValue.join('、') : '-';
127 }
128 },
129 {
130 label: "权限模板",
131 field: "funcPermissionTemplateJson",
132 width: 180,
133 getName: (scope) => {
134 const cellValue = scope.row.funcPermissionTemplateJson;
135 return Array.isArray(cellValue) && cellValue.length > 0 ? cellValue.map(item => item.name).join('、') : '-';
136 }
137 },
138 {
139 label: "状态",
140 field: "bizState",
141 width: TableColumnWidth.STATE,
142 align: 'center',
143 type: "tag",
144 getName: (scope) => {
145 return scope.row.bizState === 'Y' ? '启用' : '停用';
146 }
147 },
148 { label: "创建人", field: "createUserName", width: 140 },
149 {
150 label: "创建时间",
151 field: "createTime",
152 width: TableColumnWidth.DATETIME,
153 getName: (scope) => {
154 return scope.row.createTime ? Moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') : '--';
155 }
156 },
157 ],
158 data: [],
159 page: {
160 type: "normal",
161 rows: 0,
162 ...page.value,
163 },
164 loading: false,
165 actionInfo: {
166 label: "操作",
167 type: "btn",
168 width: 210,
169 fixed: 'right',
170 btns: (scope) => {
171 return [
172 { label: "详情", value: "detail", click: toDetail },
173 { label: "编辑", value: "edit", click: toEdit },
174 { label: "重置密码", value: "resetPwd", click: resetPassword },
175 { label: "删除", value: "delete", click: toDelete }
176 ];
177 }
178 }
179 })
180
181 const title = computed(() => {
182 return currentRow.value.name ? `${currentRow.value.name}(${tableInfo.value.data.length}人)` : ''
183 })
184
185 const organisationGuidList = ref<any[]>([])
186
187 // 搜索方法
188 const toSearch = (val: any = {}, clear: boolean = false) => {
189 if (clear) {
190 searchItemList.value.map(item => (item.default = ""));
191 page.value.staffName = '';
192 page.value.mobileNo = '';
193 page.value.bizState = '';
194 } else {
195 page.value.staffName = val.staffName || '';
196 page.value.mobileNo = val.mobileNo || '';
197 page.value.bizState = val.bizState || '';
198 }
199 getTableData();
200 };
201
202 // 获取表格数据
203 const getTableData = () => {
204 tableInfo.value.loading = true;
205 const params = {
206 pageIndex: page.value.curr,
207 pageSize: page.value.limit,
208 staffName: page.value.staffName,
209 mobileNo: page.value.mobileNo,
210 bizState: page.value.bizState,
211 organisationGuidList: organisationGuidList.value,
212 tenantGuid: unref(tenantGuid),
213 };
214 getStaff(params).then((res: any) => {
215 if (res?.code == proxy.$passCode) {
216 const data = res.data || {};
217 tableInfo.value.data = data.records ?? [];
218 tableInfo.value.page.limit = data.pageSize
219 tableInfo.value.page.curr = data.pageIndex
220 tableInfo.value.page.rows = data.totalRows
221 // 总数需要通过其他接口获取或者在返回数据中包含
222 } else {
223 proxy.$ElMessage({
224 type: 'error',
225 message: res?.msg || '获取数据失败',
226 })
227 }
228 tableInfo.value.loading = false;
229 }).catch((err) => {
230 tableInfo.value.loading = false;
231 });
232 }
233
234 // 分页变化
235 const tablePageChange = (info) => {
236 page.value.curr = Number(info.curr);
237 page.value.limit = Number(info.limit);
238 tableInfo.value.page.curr = page.value.curr;
239 tableInfo.value.page.limit = page.value.limit;
240 getTableData();
241 };
242
243 // 操作方法
244 const toDetail = (scope) => {
245 router.push({
246 name: 'addUser',
247 query: {
248 guid: scope.row.guid,
249 isDetail: 'isDetail',
250 userName: scope.row.staffName
251 }
252 })
253 }
254
255 const toEdit = (scope) => {
256 router.push({
257 name: 'addUser',
258 query: {
259 guid: scope.row.guid,
260 isEdit: 'isEdit',
261 userName: scope.row.staffName
262 }
263 })
264 }
265
266 const resetPassword = (scope) => {
267 proxy.$openMessageBox("确定是重置密码?", "warning", () => {
268 resetPwd2([scope.row.userGuid]).then((res: any) => {
269 if (res?.code === proxy.$passCode) {
270 proxy.$ElMessage({
271 type: 'success',
272 message: '重置成功,密码为:YzU&+66w'
273 })
274 } else {
275 proxy.$ElMessage({
276 type: 'error',
277 message: res?.msg || '重置密码失败',
278 })
279 }
280 })
281 })
282 }
283
284 const toDelete = (scope) => {
285 proxy.$openMessageBox("数据删除后不可恢复,确定是否删除?", "warning", () => {
286 removeStaff([scope.row.guid]).then((res: any) => {
287 if (res?.code === proxy.$passCode) {
288 proxy.$ElMessage({
289 type: 'success',
290 message: '删除成功'
291 })
292 getTableData();
293 } else {
294 proxy.$ElMessage({
295 type: 'error',
296 message: res?.msg || '删除失败',
297 })
298 }
299 })
300 })
301 }
302
303 // 新增用户
304 const handleCreate = () => {
305 router.push({
306 name: 'addUser',
307 query: {}
308 })
309 }
310
311 const expandedKey: any = ref([]);
312 const currentNodeKey = ref('');
313
314 // 获取公司部门树
315 const getTenantList = async () => {
316 let orgTree = await getOrganisationTree({
317 useCache: false,
318 tenantGuid: tenantGuid.value
319 })
320 let tree = [{
321 guid: tenantGuid.value,
322 name: tenantName.value,
323 id: tenantGuid.value,
324 label: tenantName.value,
325 nodeType: 'tenant',
326 children: orgTree
327 }]
328 treeInfo.value.data = tree
329 currentRow.value = tree[0] || {}
330 expandedKey.value = expandedKey.value.length == 0 ? [tree[0].guid] : expandedKey.value
331 currentNodeKey.value = currentNodeKey.value == '' ? tree[0].guid : currentNodeKey.value
332 treeInfo.value.currentNodeKey = tree[0]?.guid;
333 nextTick(() => {
334 treeInfo.value.currentNodeKey = currentNodeKey.value
335 treeInfo.value.expandedKey = expandedKey.value
336 })
337 toSearch({})
338 }
339
340 // 树节点点击
341 const nodeClick = (node) => {
342 currentRow.value = node
343 let children = node.children || []
344 let guids = children.map(item => item.guid)
345 guids.push(node.guid)
346 organisationGuidList.value = node.$treeNodeId == 1 ? [] : guids
347 page.value.curr = 1;
348 toSearch({})
349 }
350
351 const treeInfo = ref<any>({
352 filter: true,
353 defaultExpandAll: true,
354 queryValue: "",
355 currentNodeKey: '',
356 expandOnNodeClick: false,
357 queryPlaceholder: "输入关键字搜索",
358 props: {
359 label: "name",
360 value: "guid",
361 },
362 nodeKey: 'guid',
363 expandedKey: [],
364 data: [],
365 });
366
367 onActivated(() => {
368 if (dataBasicStore.isUpdate) {//如果是首次加载,则不需要调用
369 page.value.curr = 1;
370 toSearch({})
371 dataBasicStore.setIsUpdate(false);
372 }
373 })
374
375 onMounted(() => {
376 getTenantList()
377 });
378 </script>
379
380 <style lang="scss" scoped>
381 .container_wrap {
382 .aside_wrap {
383 width: 200px;
384 }
385
386 .main_wrap {
387 width: calc(100% - 200px);
388
389 .table_tool_wrap {
390 width: 100%;
391 // height: 84px !important;
392 // padding: 0 8px;
393
394 :deep(.tools_search) {
395 padding: 0px;
396 }
397
398 .tools_btns {
399 padding: 0px 0 8px;
400 }
401 }
402
403 .table_panel_wrap {
404 width: 100%;
405 height: calc(100% - 112px);
406 // padding: 0px 8px 8px;
407 }
408 }
409
410 .tree_panel {
411 height: calc(100% - 36px);
412 padding-top: 0;
413
414 :deep(.el-tree) {
415 margin: 0;
416 overflow: hidden auto;
417 }
418 }
419 }
420
421 .aside_title {
422 font-size: 14px;
423 color: var(--el-color-regular);
424 height: 36px;
425 line-height: 36px;
426 font-weight: 600;
427 }
428 </style>
...\ No newline at end of file ...\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!