diff --git a/src/service/api/user.ts b/src/service/api/user.ts new file mode 100644 index 0000000..258cca6 --- /dev/null +++ b/src/service/api/user.ts @@ -0,0 +1,102 @@ +import { request } from '../request'; + +// 组织列表 +export function orgListApi() { + return request({ + url: '/api/organization/tree', + method: 'get', + }); +} +// 新增组织 +export function addOrgApi(params: { label: string, parent_id: number, sort: number, is_bottom: number, org_code: string }) { + return request({ + url: '/api/organization', + method: 'post', + data: params + }); +} +// 编辑组织 +export function editOrgApi(id: number, params: { label: string, parent_id: number, sort: number, is_bottom: number, org_code: string }) { + return request({ + url: `/api/organization/${id}`, + method: 'put', + data: params + }); +} +// 删除组织 +export function deleteOrgApi(id: number) { + return request({ + url: `/api/organization/${id}`, + method: 'delete', + }); +} + +// 获取用户列表 +export function userListApi(params: {org_id: number, keyword: string, page: number, limit: number }) { + return request({ + url: '/api/user', + method: 'get', + params + }); +} + +// 新增用户 +export function addUserApi(params: { real_name: string, username: string, gender: number, mobile?: string, email?: string, dept_ids: number[], role_id: number | null, org_ids: number[], enabled: boolean }) { + return request({ + url: '/api/user', + method: 'post', + data: params + }); +} + +// 编辑用户 +export function editUserApi(id: number, params: { real_name: string, username: string, gender: number, mobile?: string, email?: string, dept_ids: number[], role_id: number | null, org_ids: number[], enabled: boolean }) { + return request({ + url: `/api/user/${id}`, + method: 'put', + data: params + }); +} + +// 删除用户 +export function deleteUserApi(params: { id: number, type: number }) { + return request({ + url: '/api/user/operation', + method: 'post', + data: params + }); +} + +// 重置数据权限 +export function resetDataApi() { + return request({ + url: `/api/user/reset-data-permission`, + method: 'post', + }); +} + +// 下载用户导入模板 +export function downloadTemplateApi() { + return request({ + url: `/api/user/template`, + method: 'get', + responseType: 'blob', + }); +} + +// 导入用户 +export function importUserApi(params: FormData) { + return request({ + url: `/api/user/import`, + method: 'post', + data: params + }); +} +// 重置用户密码 +export function resetPasswordApi(id: number) { + return request({ + url: `/api/user/${id}/reset-password`, + method: 'post', + }); +} + diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index a8e3710..33571a6 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -65,7 +65,9 @@ declare module 'vue' { NMessageProvider: typeof import('naive-ui')['NMessageProvider'] NModal: typeof import('naive-ui')['NModal'] NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] + NPagination: typeof import('naive-ui')['NPagination'] NPopconfirm: typeof import('naive-ui')['NPopconfirm'] + NPopover: typeof import('naive-ui')['NPopover'] NProgress: typeof import('naive-ui')['NProgress'] NRadio: typeof import('naive-ui')['NRadio'] NRadioGroup: typeof import('naive-ui')['NRadioGroup'] @@ -153,7 +155,9 @@ declare global { const NMessageProvider: typeof import('naive-ui')['NMessageProvider'] const NModal: typeof import('naive-ui')['NModal'] const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] + const NPagination: typeof import('naive-ui')['NPagination'] const NPopconfirm: typeof import('naive-ui')['NPopconfirm'] + const NPopover: typeof import('naive-ui')['NPopover'] const NProgress: typeof import('naive-ui')['NProgress'] const NRadio: typeof import('naive-ui')['NRadio'] const NRadioGroup: typeof import('naive-ui')['NRadioGroup'] diff --git a/src/utils/common.ts b/src/utils/common.ts index 8704adf..c8843fa 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -95,4 +95,49 @@ export function formatTimestamp(timestamp: number | string,format: string = 'yyy return `${year}-${month}-${day} ${hours}:${minutes}`; } return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; -} \ No newline at end of file +} + +// 辅助函数:Fisher-Yates 洗牌算法 +function shuffle(arr:any) { + for (let i = arr.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [arr[i], arr[j]] = [arr[j], arr[i]]; + } + return arr; +} +// 辅助函数:从字符串中随机获取字符 +function getRandomChar(str:any) { + return str[Math.floor(Math.random() * str.length)]; +} +// 生成至少12位,包含四类字符中的至少三种的随机密码 +export function generateSecurePassword(passwordLength:number) { + // 定义字符池 + const charSets:any = { + uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + lowercase: 'abcdefghijklmnopqrstuvwxyz', + numbers: '0123456789', + symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?' + }; + + // 随机选择三种字符类型(保证至少三类) + const requiredTypes = shuffle(Object.keys(charSets)).slice(0, 3); + + // 生成必选字符(每类至少1个) + let password = requiredTypes + .map((type:any) => getRandomChar(charSets[type])) + .join(''); + + // 生成剩余字符(允许包含所有四类) + const remainingLength = passwordLength - password.length; + const allChars = Object.values(charSets).join(''); + for (let i = 0; i < remainingLength; i++) { + password += getRandomChar(allChars); + } + + // 打乱字符顺序 + return shuffle(password.split('')).join(''); +} +// 获取 token +export const getToken = () => { + return localStorage.getItem('token'); +}; \ No newline at end of file diff --git a/src/utils/service.ts b/src/utils/service.ts index 9598038..58652bd 100644 --- a/src/utils/service.ts +++ b/src/utils/service.ts @@ -5,6 +5,7 @@ import json5 from 'json5'; * * @param env The current env */ +let url = 'http://192.168.0.230:8086'; export function createServiceConfig(env: Env.ImportMeta) { const { VITE_SERVICE_BASE_URL, VITE_OTHER_SERVICE_BASE_URL } = env; @@ -15,7 +16,6 @@ export function createServiceConfig(env: Env.ImportMeta) { // eslint-disable-next-line no-console console.error('VITE_OTHER_SERVICE_BASE_URL is not a valid json5 string'); } - let url = 'http://192.168.0.230:8086'; if(env.DEV){ url = 'http://192.168.0.230:8086'; }else{ @@ -44,6 +44,9 @@ export function createServiceConfig(env: Env.ImportMeta) { return config; } +export function getBaseUrl() { + return url; +} /** * get backend service base url diff --git a/src/views/systemManage/role/index.vue b/src/views/systemManage/role/index.vue index 20e0447..7947910 100644 --- a/src/views/systemManage/role/index.vue +++ b/src/views/systemManage/role/index.vue @@ -4,7 +4,7 @@ import { useAppStore } from '@/store/modules/app'; import { Icon } from '@iconify/vue' import { useThemeStore } from '@/store/modules/theme'; import { useRouter } from 'vue-router'; -import { NButton, NIcon } from 'naive-ui'; +import { NButton } from 'naive-ui'; import { roleListApi, editRoleApi } from '@/service/api/role'; const router = useRouter(); @@ -86,12 +86,11 @@ const list = ref<{ id: number, name: string, intro: string, perms: string[] }[]> // 获取列表 const getList = async () => { loading.value = true; - try { - const res = await roleListApi(); - list.value = res.data; - } finally { - loading.value = false; + const {data,error} = await roleListApi(); + if(!error){ + list.value = data; } + loading.value = false; } // 设置权限 @@ -116,18 +115,16 @@ const saveRole = (e: MouseEvent) => { formRef.value?.validate(async (errors: any) => { if (!errors) { btnLoading.value = true; - try { - await editRoleApi(currentRole.value); + const {error} = await editRoleApi(currentRole.value); + if(!error){ const target = list.value.find((item:any) => item.id === currentRole.value.id); if (target) { target.name = currentRole.value.name; } window.$message?.success('操作成功') - btnLoading.value = false; roleModal.value = false; - } finally { - btnLoading.value = false; } + btnLoading.value = false; } }) } @@ -135,15 +132,13 @@ const saveRole = (e: MouseEvent) => { const savePermission = async () => { btnLoading.value = true; currentRole.value.perms = defaultCheckedKeys.value; - try { - await editRoleApi(currentRole.value); + const {error} = await editRoleApi(currentRole.value); + if(!error){ window.$message?.success('操作成功') - btnLoading.value = false; permissionModal.value = false; getList(); - } finally { - btnLoading.value = false; } + btnLoading.value = false; } // 更新选中权限 const updateCheckedKeys = (keys: string[]) => { diff --git a/src/views/systemManage/user/index.vue b/src/views/systemManage/user/index.vue index 77245d0..17b3413 100644 --- a/src/views/systemManage/user/index.vue +++ b/src/views/systemManage/user/index.vue @@ -1,87 +1,97 @@ @@ -504,52 +634,67 @@ onMounted(() => { 添加成员 - - 导入用户 - - - - + + + 下载模板 + + + + 重置数据权限 +
-
- - - - -
- - + +
+ + + + +
+ + +
+

+ 点击层级查看该层级全员名录;右键(或点击 ···)可编辑、添加子级、删除。 +

-

- 点击层级查看该层级全员名录;右键(或点击 ···)可编辑、添加子级、删除。 -

-
+
{ :single-line="false" :columns="columns" :data="list" - :pagination="pagination" + :pagination="false" max-height="calc(100vh - 285px)" /> +
+ + + +
- + - + @@ -586,9 +745,9 @@ onMounted(() => { - + @@ -604,27 +763,30 @@ onMounted(() => { placeholder="请输入电子邮箱" /> - + - + - - 启用 - 禁用 + + 启用 + 禁用 @@ -653,11 +815,12 @@ onMounted(() => { maxlength="10" show-count placeholder="请输入层级名称" + :on-input="convertToPinyin" /> { - +