From 468b4bb0e1ab89eb2abd3abc4884171874249ef0 Mon Sep 17 00:00:00 2001 From: Soybean Date: Thu, 29 Sep 2022 00:24:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(projects):=20refactor=20page:=20user-m?= =?UTF-8?q?anagement=20[=E9=87=8D=E6=9E=84=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=A1=B5=E9=9D=A2]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/api/demo.ts | 20 ---- mock/api/management.ts | 13 +-- package.json | 1 + src/constants/business.ts | 25 +++++ src/constants/index.ts | 1 + src/enum/business.ts | 6 - src/hooks/business/useTable.ts | 1 + src/service/api/demo.adapter.ts | 10 -- src/service/api/demo.ts | 14 --- src/service/api/index.ts | 1 - src/service/api/management.adapter.ts | 43 ++----- src/service/api/management.ts | 15 +-- src/typings/api.d.ts | 31 +++--- src/typings/business.d.ts | 86 +++----------- src/typings/naive-ui.d.ts | 3 + src/typings/system.d.ts | 38 ++++++- src/utils/form/rule.ts | 21 ++-- .../user/components/ColumnSetting.vue | 69 ++++++++++++ .../user/components/TableActionModal.vue | 92 +++++++++------ src/views/management/user/components/index.ts | 3 - src/views/management/user/index.vue | 105 ++++++++---------- .../login/components/BindWechat/index.vue | 18 +-- .../login/components/CodeLogin/index.vue | 19 ++-- .../login/components/PwdLogin/index.vue | 19 ++-- .../login/components/Register/index.vue | 19 +--- .../login/components/ResetPwd/index.vue | 18 +-- 26 files changed, 340 insertions(+), 351 deletions(-) delete mode 100644 mock/api/demo.ts create mode 100644 src/constants/business.ts create mode 100644 src/constants/index.ts create mode 100644 src/hooks/business/useTable.ts delete mode 100644 src/service/api/demo.adapter.ts delete mode 100644 src/service/api/demo.ts create mode 100644 src/typings/naive-ui.d.ts create mode 100644 src/views/management/user/components/ColumnSetting.vue delete mode 100644 src/views/management/user/components/index.ts diff --git a/mock/api/demo.ts b/mock/api/demo.ts deleted file mode 100644 index e264f292..00000000 --- a/mock/api/demo.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { MockMethod } from 'vite-plugin-mock'; - -const apis: MockMethod[] = [ - { - url: '/mock/apiDemoWithAdapter', - method: 'post', - response: (): Service.MockServiceResult => { - return { - code: 200, - message: 'ok', - data: { - dataId: '123', - dataName: 'demoName' - } - }; - } - } -]; - -export default apis; diff --git a/mock/api/management.ts b/mock/api/management.ts index 0314f555..189b93d9 100644 --- a/mock/api/management.ts +++ b/mock/api/management.ts @@ -3,21 +3,20 @@ import type { MockMethod } from 'vite-plugin-mock'; const apis: MockMethod[] = [ { - url: '/mock/getUserManagementList', + url: '/mock/getAllUserList', method: 'post', - response: (): Service.MockServiceResult => { + response: (): Service.MockServiceResult => { const data = mock({ 'list|1000': [ { id: '@id', - name: '@cname', - 'age|20-36': 36, + userName: '@cname', + 'age|18-56': 56, 'gender|1': ['0', '1', null], phone: /^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/, - email: '@email("qq.com")', - 'role|1': ['super', 'admin', 'user'], - 'disabled|1': true + 'email|1': ['@email("qq.com")', null], + 'userStatus|1': ['1', '2', '3', '4', null] } ] }); diff --git a/package.json b/package.json index c8f82bcf..94e3c433 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "vditor": "^3.8.17", "vue": "3.2.40", "vue-router": "^4.1.5", + "vuedraggable": "^4.1.0", "wangeditor": "^4.7.15", "xgplayer": "^2.32.0" }, diff --git a/src/constants/business.ts b/src/constants/business.ts new file mode 100644 index 00000000..60a96b2e --- /dev/null +++ b/src/constants/business.ts @@ -0,0 +1,25 @@ +/** 用户性别 */ +export const genderLabels: Record = { + 0: '女', + 1: '男' +}; + +export const genderOptions: { value: UserManagement.GenderKey; label: string }[] = [ + { value: '0', label: genderLabels['0'] }, + { value: '1', label: genderLabels['1'] } +]; + +/** 用户状态 */ +export const userStatusLabels: Record = { + 1: '启用', + 2: '禁用', + 3: '冻结', + 4: '软删除' +}; + +export const userStatusOptions: { value: UserManagement.UserStatusKey; label: string }[] = [ + { value: '1', label: userStatusLabels['1'] }, + { value: '2', label: userStatusLabels['2'] }, + { value: '3', label: userStatusLabels['3'] }, + { value: '4', label: userStatusLabels['4'] } +]; diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..f4db82dd --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1 @@ +export * from './business'; diff --git a/src/enum/business.ts b/src/enum/business.ts index bd427040..b479cf5d 100644 --- a/src/enum/business.ts +++ b/src/enum/business.ts @@ -13,9 +13,3 @@ export enum EnumLoginModule { 'reset-pwd' = '重置密码', 'bind-wechat' = '微信绑定' } - -export enum EnumGender { - male = '男', - female = '女', - null = '' -} diff --git a/src/hooks/business/useTable.ts b/src/hooks/business/useTable.ts new file mode 100644 index 00000000..ee5dde7e --- /dev/null +++ b/src/hooks/business/useTable.ts @@ -0,0 +1 @@ +export function useTable() {} diff --git a/src/service/api/demo.adapter.ts b/src/service/api/demo.adapter.ts deleted file mode 100644 index bc82f782..00000000 --- a/src/service/api/demo.adapter.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function adapterOfFetchDataWithAdapter(data: ApiDemo.DataWithAdapter): Demo.DataWithAdapter { - const { dataId, dataName } = data; - - const result: Demo.DataWithAdapter = { - id: dataId, - name: dataName - }; - - return result; -} diff --git a/src/service/api/demo.ts b/src/service/api/demo.ts deleted file mode 100644 index f9126dde..00000000 --- a/src/service/api/demo.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { adapter } from '@/utils'; -import { mockRequest, request } from '../request'; -import { adapterOfFetchDataWithAdapter } from './demo.adapter'; - -/** 带有适配器的请求示例 */ -export async function fetchDataWithAdapter() { - const res = await mockRequest.post('/apiDemoWithAdapter'); - return adapter(adapterOfFetchDataWithAdapter, res); -} - -/** 测试代理后的请求 */ -export function testRequestWithProxy() { - return request.get('/test-proxy'); // 确保.env-config的url和当前地址组成的 `${url}/test-proxy` 是有效的后端接口 -} diff --git a/src/service/api/index.ts b/src/service/api/index.ts index 06bf701c..c9ae5d19 100644 --- a/src/service/api/index.ts +++ b/src/service/api/index.ts @@ -1,3 +1,2 @@ export * from './auth'; -export * from './demo'; export * from './management'; diff --git a/src/service/api/management.adapter.ts b/src/service/api/management.adapter.ts index 0b755f47..80bf658f 100644 --- a/src/service/api/management.adapter.ts +++ b/src/service/api/management.adapter.ts @@ -1,42 +1,13 @@ -import { EnumGender } from '@/enum'; -import { isUndefined } from '@/utils'; +export function adapterOfFetchUserList(data: ApiUserManagement.User[] | null): UserManagement.User[] { + if (!data) return []; -export function adapterOfFetchUserManagementList( - requestData: ApiUserManagement.UserTable[] -): UserManagement.UserTable[] { - const genderMap: Record< - NonNullable, - NonNullable - > = { - '0': 'female', - '1': 'male' - }; - - // 1. 有可能依赖于多个接口的结果,再转换成页面的数据 - // 2. 接口定义的字段有可能为null, 例如 预期是数组却返回了null,导致调用数组方法报错 - // 3. 字段可能丢失 - - return requestData.map((item, index) => { - const { id, name: userName, age, gender, phone: userPhone, email: userEmail, role: userRole, disabled } = item; - - const userAge = isUndefined(age) ? '无' : String(age); - - const userGender = gender !== null ? genderMap[gender] : 'null'; - - const result: UserManagement.UserTable = { + return data.map((item, index) => { + const user: UserManagement.User = { index: index + 1, - key: id, - id, - userName, - userAge, - userGender, - userGenderLabel: EnumGender[userGender], - userPhone, - userEmail, - userRole, - disabled + key: item.id, + ...item }; - return result; + return user; }); } diff --git a/src/service/api/management.ts b/src/service/api/management.ts index d37fbc76..712ec325 100644 --- a/src/service/api/management.ts +++ b/src/service/api/management.ts @@ -1,12 +1,9 @@ import { adapter } from '@/utils'; import { mockRequest } from '../request'; -import { adapterOfFetchUserManagementList } from './management.adapter'; +import { adapterOfFetchUserList } from './management.adapter'; -/** - * 获取用户管理列表 - */ -export async function fetchUserManagementList() { - const data = await mockRequest.post('/getUserManagementList'); - - return adapter(adapterOfFetchUserManagementList, data); -} +/** 获取用户列表 */ +export const fetchUserList = async () => { + const data = await mockRequest.post('/getAllUserList'); + return adapter(adapterOfFetchUserList, data); +}; diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index f807c1bd..03b1bd6d 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -22,34 +22,31 @@ declare namespace ApiRoute { } } -declare namespace ApiDemo { - interface DataWithAdapter { - dataId: string; - dataName: string; - } -} - declare namespace ApiUserManagement { - interface UserTable { + interface User { /** 用户id */ id: string; /** 用户名 */ - name: string; + userName: string | null; /** 用户年龄 */ - age: number; + age: number | null; /** * 用户性别 - * - 男 1 - * - 女 0 + * - 0: 女 + * - 1: 男 */ gender: '0' | '1' | null; /** 用户手机号码 */ phone: string; /** 用户邮箱 */ - email: string; - /** 用户角色 */ - role: Auth.RoleType; - /** 是否禁用用户 */ - disabled: boolean; + email: string | null; + /** + * 用户状态 + * - 1: 启用 + * - 2: 禁用 + * - 3: 冻结 + * - 4: 软删除 + */ + userStatus: '1' | '2' | '3' | '4' | null; } } diff --git a/src/typings/business.d.ts b/src/typings/business.d.ts index fd34165b..97cd2008 100644 --- a/src/typings/business.d.ts +++ b/src/typings/business.d.ts @@ -20,77 +20,27 @@ declare namespace Auth { } } -declare namespace Demo { - interface DataWithAdapter { - id: string; - name: string; - } -} - -/** 系统消息 */ -declare namespace Message { - interface Tab { - /** tab的key */ - key: number; - /** tab名称 */ - name: string; - /** badge类型 */ - badgeProps?: import('naive-ui').BadgeProps; - /** 消息数据 */ - list: List[]; - } - - interface List { - /** 数据唯一值 */ - id: number; - /** 头像 */ - avatar?: string; - /** 消息icon */ - icon?: string; - svgIcon?: string; - /** 消息标题 */ - title: string; - /** 消息发送时间 */ - date?: string; - /** 消息是否已读 */ - isRead?: boolean; - /** 消息描述 */ - description?: string; - /** 标签名称 */ - tagTitle?: string; - /** 标签props */ - tagProps?: import('naive-ui').TagProps; - } -} - -/** 用户管理 */ declare namespace UserManagement { - /** 用户表格 */ - interface UserTable { + interface User extends ApiUserManagement.User { /** 序号 */ index: number; - /** 数据的key(id) */ + /** 表格的key(id) */ key: string; - /** 用户id */ - id: string; - /** 用户名 */ - userName: string; - /** 用户年龄 */ - userAge: string; - /** - * 用户性别 - * - male 男 - * - female 女 - */ - userGender: keyof typeof import('@/enum').EnumGender; - userGenderLabel: import('@/enum').EnumGender; - /** 用户手机号 */ - userPhone: string; - /** 用户邮箱 */ - userEmail: string; - /** 用户角色 */ - userRole: Auth.RoleType; - /** 是否禁用用户 */ - disabled: boolean; } + + /** + * 用户性别 + * - 0: 女 + * - 1: 男 + */ + type GenderKey = NonNullable; + + /** + * 用户状态 + * - 1: 启用 + * - 2: 禁用 + * - 3: 冻结 + * - 4: 软删除 + */ + type UserStatusKey = NonNullable; } diff --git a/src/typings/naive-ui.d.ts b/src/typings/naive-ui.d.ts new file mode 100644 index 00000000..1c6a2908 --- /dev/null +++ b/src/typings/naive-ui.d.ts @@ -0,0 +1,3 @@ +declare namespace NaiveUI { + type ThemeColor = 'default' | 'error' | 'primary' | 'info' | 'success' | 'warning'; +} diff --git a/src/typings/system.d.ts b/src/typings/system.d.ts index a244dbbe..60278a82 100644 --- a/src/typings/system.d.ts +++ b/src/typings/system.d.ts @@ -72,7 +72,7 @@ declare namespace Service { /** 多个请求数据结果 */ type MultiRequestResult = T extends [infer First, ...infer Rest] - ? First extends any + ? [First] extends [any] ? Rest extends any[] ? [Service.RequestResult, ...MultiRequestResult] : [Service.RequestResult] @@ -293,3 +293,39 @@ interface GlobalTabRoute top: number; }; } + +/** 系统消息 */ +declare namespace Message { + interface Tab { + /** tab的key */ + key: number; + /** tab名称 */ + name: string; + /** badge类型 */ + badgeProps?: import('naive-ui').BadgeProps; + /** 消息数据 */ + list: List[]; + } + + interface List { + /** 数据唯一值 */ + id: number; + /** 头像 */ + avatar?: string; + /** 消息icon */ + icon?: string; + svgIcon?: string; + /** 消息标题 */ + title: string; + /** 消息发送时间 */ + date?: string; + /** 消息是否已读 */ + isRead?: boolean; + /** 消息描述 */ + description?: string; + /** 标签名称 */ + tagTitle?: string; + /** 标签props */ + tagProps?: import('naive-ui').TagProps; + } +} diff --git a/src/utils/form/rule.ts b/src/utils/form/rule.ts index 951b83d8..6c300487 100644 --- a/src/utils/form/rule.ts +++ b/src/utils/form/rule.ts @@ -2,6 +2,11 @@ import type { Ref } from 'vue'; import type { FormItemRule } from 'naive-ui'; import { REGEXP_CODE_SIX, REGEXP_EMAIL, REGEXP_PHONE, REGEXP_PWD } from '@/config'; +/** 创建自定义错误信息的必填表单规则 */ +export const createRequiredFormRule = (message = '不能为空'): FormItemRule => ({ required: true, message }); + +export const requiredFormRule = createRequiredFormRule(); + /** 表单规则 */ interface CustomFormRules { /** 手机号码 */ @@ -17,20 +22,25 @@ interface CustomFormRules { /** 表单规则 */ export const formRules: CustomFormRules = { phone: [ - { required: true, message: '请输入手机号码' }, + createRequiredFormRule('请输入手机号码'), { pattern: REGEXP_PHONE, message: '手机号码格式错误', trigger: 'input' } ], pwd: [ - { required: true, message: '请输入密码' }, + createRequiredFormRule('请输入密码'), { pattern: REGEXP_PWD, message: '密码为6-18位数字/字符/符号,至少2种组合', trigger: 'input' } ], code: [ - { required: true, message: '请输入验证码' }, + createRequiredFormRule('请输入验证码'), { pattern: REGEXP_CODE_SIX, message: '验证码格式错误', trigger: 'input' } ], email: [{ pattern: REGEXP_EMAIL, message: '邮箱格式错误', trigger: 'blur' }] }; +/** 是否为空字符串 */ +function isBlankString(str: string) { + return str.trim() === ''; +} + /** 获取确认密码的表单规则 */ export function getConfirmPwdRule(pwd: Ref) { const confirmPwdRule: FormItemRule[] = [ @@ -66,8 +76,3 @@ export function getImgCodeRule(imgCode: Ref) { ]; return imgCodeRule; } - -/** 是否为空字符串 */ -function isBlankString(str: string) { - return str.trim() === ''; -} diff --git a/src/views/management/user/components/ColumnSetting.vue b/src/views/management/user/components/ColumnSetting.vue new file mode 100644 index 00000000..a7835f4d --- /dev/null +++ b/src/views/management/user/components/ColumnSetting.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/views/management/user/components/TableActionModal.vue b/src/views/management/user/components/TableActionModal.vue index 66d52563..bbc23732 100644 --- a/src/views/management/user/components/TableActionModal.vue +++ b/src/views/management/user/components/TableActionModal.vue @@ -1,49 +1,59 @@ diff --git a/src/views/system-view/login/components/CodeLogin/index.vue b/src/views/system-view/login/components/CodeLogin/index.vue index 0996b577..6ebc00ae 100644 --- a/src/views/system-view/login/components/CodeLogin/index.vue +++ b/src/views/system-view/login/components/CodeLogin/index.vue @@ -46,13 +46,16 @@ const auth = useAuthStore(); const { toLoginModule } = useRouterPush(); const { label, isCounting, loading: smsLoading, getSmsCode } = useSmsCode(); -const formRef = ref<(HTMLElement & FormInst) | null>(null); +const formRef = ref(); + const model = reactive({ phone: '', code: '', imgCode: '' }); + const imgCode = ref(''); + const rules = { phone: formRules.phone, code: formRules.code, @@ -63,17 +66,9 @@ function handleSmsCode() { getSmsCode(model.phone); } -function handleSubmit(e: MouseEvent) { - if (!formRef.value) return; - e.preventDefault(); - - formRef.value.validate(errors => { - if (!errors) { - window.$message?.success('验证通过!'); - } else { - window.$message?.error('验证失败'); - } - }); +async function handleSubmit() { + await formRef.value?.validate(); + window.$message?.success('验证成功!'); } diff --git a/src/views/system-view/login/components/PwdLogin/index.vue b/src/views/system-view/login/components/PwdLogin/index.vue index b1a5066a..cf0c6a84 100644 --- a/src/views/system-view/login/components/PwdLogin/index.vue +++ b/src/views/system-view/login/components/PwdLogin/index.vue @@ -48,26 +48,25 @@ const auth = useAuthStore(); const { login } = useAuthStore(); const { toLoginModule } = useRouterPush(); -const formRef = ref<(HTMLElement & FormInst) | null>(null); +const formRef = ref(); + const model = reactive({ userName: 'Soybean', password: 'soybean123' }); + const rules: FormRules = { password: formRules.pwd }; + const rememberMe = ref(false); -function handleSubmit(e: MouseEvent) { - if (!formRef.value) return; - e.preventDefault(); +async function handleSubmit() { + await formRef.value?.validate(); - formRef.value.validate(errors => { - if (!errors) { - const { userName, password } = model; - login(userName, password); - } - }); + const { userName, password } = model; + + login(userName, password); } function handleLoginOtherAccount(param: { userName: string; password: string }) { diff --git a/src/views/system-view/login/components/Register/index.vue b/src/views/system-view/login/components/Register/index.vue index e9d520de..abc4828e 100644 --- a/src/views/system-view/login/components/Register/index.vue +++ b/src/views/system-view/login/components/Register/index.vue @@ -36,13 +36,15 @@ import { formRules, getConfirmPwdRule } from '@/utils'; const { toLoginModule } = useRouterPush(); const { label, isCounting, loading: smsLoading, start } = useSmsCode(); -const formRef = ref<(HTMLElement & FormInst) | null>(null); +const formRef = ref(); + const model = reactive({ phone: '', code: '', pwd: '', confirmPwd: '' }); + const rules: FormRules = { phone: formRules.phone, code: formRules.code, @@ -56,18 +58,9 @@ function handleSmsCode() { start(); } -function handleSubmit(e: MouseEvent) { - if (!formRef.value) return; - e.preventDefault(); - - formRef.value.validate(errors => { - if (!errors) { - if (!agreement.value) return; - window.$message?.success('验证成功!'); - } else { - window.$message?.error('验证失败!'); - } - }); +async function handleSubmit() { + await formRef.value?.validate(); + window.$message?.success('验证成功!'); } diff --git a/src/views/system-view/login/components/ResetPwd/index.vue b/src/views/system-view/login/components/ResetPwd/index.vue index 546f9abb..b81c18fe 100644 --- a/src/views/system-view/login/components/ResetPwd/index.vue +++ b/src/views/system-view/login/components/ResetPwd/index.vue @@ -35,13 +35,15 @@ import { formRules, getConfirmPwdRule } from '@/utils'; const { toLoginModule } = useRouterPush(); const { label, isCounting, loading: smsLoading, start } = useSmsCode(); -const formRef = ref<(HTMLElement & FormInst) | null>(null); +const formRef = ref(); + const model = reactive({ phone: '', code: '', pwd: '', confirmPwd: '' }); + const rules: FormRules = { phone: formRules.phone, code: formRules.code, @@ -53,17 +55,9 @@ function handleSmsCode() { start(); } -function handleSubmit(e: MouseEvent) { - if (!formRef.value) return; - e.preventDefault(); - - formRef.value.validate(errors => { - if (!errors) { - window.$message?.success('验证成功'); - } else { - window.$message?.error('验证失败'); - } - }); +async function handleSubmit() { + await formRef.value?.validate(); + window.$message?.success('验证成功!'); }