mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-23 20:06:37 +08:00
refactor(projects): refactor page: user-management [重构用户管理页面]
This commit is contained in:
parent
88e535f63c
commit
468b4bb0e1
@ -1,20 +0,0 @@
|
||||
import type { MockMethod } from 'vite-plugin-mock';
|
||||
|
||||
const apis: MockMethod[] = [
|
||||
{
|
||||
url: '/mock/apiDemoWithAdapter',
|
||||
method: 'post',
|
||||
response: (): Service.MockServiceResult<ApiDemo.DataWithAdapter> => {
|
||||
return {
|
||||
code: 200,
|
||||
message: 'ok',
|
||||
data: {
|
||||
dataId: '123',
|
||||
dataName: 'demoName'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default apis;
|
@ -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<ApiUserManagement.UserTable[]> => {
|
||||
response: (): Service.MockServiceResult<ApiUserManagement.User[]> => {
|
||||
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]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -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"
|
||||
},
|
||||
|
25
src/constants/business.ts
Normal file
25
src/constants/business.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/** 用户性别 */
|
||||
export const genderLabels: Record<UserManagement.GenderKey, string> = {
|
||||
0: '女',
|
||||
1: '男'
|
||||
};
|
||||
|
||||
export const genderOptions: { value: UserManagement.GenderKey; label: string }[] = [
|
||||
{ value: '0', label: genderLabels['0'] },
|
||||
{ value: '1', label: genderLabels['1'] }
|
||||
];
|
||||
|
||||
/** 用户状态 */
|
||||
export const userStatusLabels: Record<UserManagement.UserStatusKey, string> = {
|
||||
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'] }
|
||||
];
|
1
src/constants/index.ts
Normal file
1
src/constants/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './business';
|
@ -13,9 +13,3 @@ export enum EnumLoginModule {
|
||||
'reset-pwd' = '重置密码',
|
||||
'bind-wechat' = '微信绑定'
|
||||
}
|
||||
|
||||
export enum EnumGender {
|
||||
male = '男',
|
||||
female = '女',
|
||||
null = ''
|
||||
}
|
||||
|
1
src/hooks/business/useTable.ts
Normal file
1
src/hooks/business/useTable.ts
Normal file
@ -0,0 +1 @@
|
||||
export function useTable() {}
|
@ -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;
|
||||
}
|
@ -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<ApiDemo.DataWithAdapter>('/apiDemoWithAdapter');
|
||||
return adapter(adapterOfFetchDataWithAdapter, res);
|
||||
}
|
||||
|
||||
/** 测试代理后的请求 */
|
||||
export function testRequestWithProxy() {
|
||||
return request.get('/test-proxy'); // 确保.env-config的url和当前地址组成的 `${url}/test-proxy` 是有效的后端接口
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
export * from './auth';
|
||||
export * from './demo';
|
||||
export * from './management';
|
||||
|
@ -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<ApiUserManagement.UserTable['gender']>,
|
||||
NonNullable<UserManagement.UserTable['userGender']>
|
||||
> = {
|
||||
'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;
|
||||
});
|
||||
}
|
||||
|
@ -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<ApiUserManagement.UserTable[]>('/getUserManagementList');
|
||||
|
||||
return adapter(adapterOfFetchUserManagementList, data);
|
||||
}
|
||||
/** 获取用户列表 */
|
||||
export const fetchUserList = async () => {
|
||||
const data = await mockRequest.post<ApiUserManagement.User[] | null>('/getAllUserList');
|
||||
return adapter(adapterOfFetchUserList, data);
|
||||
};
|
||||
|
31
src/typings/api.d.ts
vendored
31
src/typings/api.d.ts
vendored
@ -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;
|
||||
}
|
||||
}
|
||||
|
86
src/typings/business.d.ts
vendored
86
src/typings/business.d.ts
vendored
@ -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<User['gender']>;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
* - 1: 启用
|
||||
* - 2: 禁用
|
||||
* - 3: 冻结
|
||||
* - 4: 软删除
|
||||
*/
|
||||
type UserStatusKey = NonNullable<User['userStatus']>;
|
||||
}
|
||||
|
3
src/typings/naive-ui.d.ts
vendored
Normal file
3
src/typings/naive-ui.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
declare namespace NaiveUI {
|
||||
type ThemeColor = 'default' | 'error' | 'primary' | 'info' | 'success' | 'warning';
|
||||
}
|
38
src/typings/system.d.ts
vendored
38
src/typings/system.d.ts
vendored
@ -72,7 +72,7 @@ declare namespace Service {
|
||||
|
||||
/** 多个请求数据结果 */
|
||||
type MultiRequestResult<T extends any[]> = T extends [infer First, ...infer Rest]
|
||||
? First extends any
|
||||
? [First] extends [any]
|
||||
? Rest extends any[]
|
||||
? [Service.RequestResult<First>, ...MultiRequestResult<Rest>]
|
||||
: [Service.RequestResult<First>]
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<string>) {
|
||||
const confirmPwdRule: FormItemRule[] = [
|
||||
@ -66,8 +76,3 @@ export function getImgCodeRule(imgCode: Ref<string>) {
|
||||
];
|
||||
return imgCodeRule;
|
||||
}
|
||||
|
||||
/** 是否为空字符串 */
|
||||
function isBlankString(str: string) {
|
||||
return str.trim() === '';
|
||||
}
|
||||
|
69
src/views/management/user/components/ColumnSetting.vue
Normal file
69
src/views/management/user/components/ColumnSetting.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<n-popover placement="bottom" trigger="click">
|
||||
<template #trigger>
|
||||
<n-button size="small" type="primary">
|
||||
<icon-ant-design-setting-outlined class="mr-4px text-16px" />
|
||||
表格列设置
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="w-180px">
|
||||
<vue-draggable v-model="list" item-key="key">
|
||||
<template #item="{ element }">
|
||||
<div v-if="element.key" class="flex-y-center h-36px px-12px hover:bg-primary_active">
|
||||
<icon-mdi-drag class="mr-8px text-20px cursor-move" />
|
||||
<n-checkbox v-model:checked="element.checked">
|
||||
{{ element.title }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
</template>
|
||||
</vue-draggable>
|
||||
</div>
|
||||
</n-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import type { DataTableColumn } from 'naive-ui';
|
||||
import VueDraggable from 'vuedraggable';
|
||||
|
||||
type Column = DataTableColumn<UserManagement.User>;
|
||||
|
||||
interface Props {
|
||||
columns: Column[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:columns', columns: Column[]): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
type List = Column & { checked?: boolean };
|
||||
|
||||
const list = ref(initList());
|
||||
|
||||
function initList(): List[] {
|
||||
return props.columns.map(item => ({ ...item, checked: true }));
|
||||
}
|
||||
|
||||
watch(
|
||||
list,
|
||||
newValue => {
|
||||
const newColumns = newValue.filter(item => item.checked);
|
||||
|
||||
const columns: Column[] = newColumns.map(item => {
|
||||
const column = { ...item };
|
||||
delete column.checked;
|
||||
|
||||
return column;
|
||||
}) as Column[];
|
||||
|
||||
emit('update:columns', columns);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -1,49 +1,59 @@
|
||||
<template>
|
||||
<n-modal v-model:show="modalVisible" preset="card" :title="title" class="w-700px">
|
||||
<n-form label-placement="left" :label-width="80" :model="formModel">
|
||||
<n-form ref="formRef" label-placement="left" :label-width="80" :model="formModel" :rules="rules">
|
||||
<n-grid :cols="24" :x-gap="18">
|
||||
<n-form-item-grid-item :span="12" label="用户名" path="userName">
|
||||
<n-input v-model:value="formModel.userName" />
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="年龄" path="userAge">
|
||||
<n-input v-model:value="formModel.userAge" />
|
||||
<n-form-item-grid-item :span="12" label="年龄" path="age">
|
||||
<n-input-number v-model:value="formModel.age" clearable />
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="性别" path="userGender">
|
||||
<n-input v-model:value="formModel.userGender" />
|
||||
<n-form-item-grid-item :span="12" label="性别" path="gender">
|
||||
<n-radio-group v-model:value="formModel.gender">
|
||||
<n-radio v-for="item in genderOptions" :key="item.value" :value="item.value">{{ item.label }}</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="手机号" path="userPhone">
|
||||
<n-input v-model:value="formModel.userPhone" />
|
||||
<n-form-item-grid-item :span="12" label="手机号" path="phone">
|
||||
<n-input v-model:value="formModel.phone" />
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="邮箱" path="userEmail">
|
||||
<n-input v-model:value="formModel.userEmail" />
|
||||
<n-form-item-grid-item :span="12" label="邮箱" path="email">
|
||||
<n-input v-model:value="formModel.email" />
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="角色" path="userRole">
|
||||
<n-input v-model:value="formModel.userRole" />
|
||||
</n-form-item-grid-item>
|
||||
<n-form-item-grid-item :span="12" label="状态" path="disabled">
|
||||
<n-switch v-model:value="formModel.disabled" />
|
||||
<n-form-item-grid-item :span="12" label="状态" path="userStatus">
|
||||
<n-select v-model:value="formModel.userStatus" :options="userStatusOptions" />
|
||||
</n-form-item-grid-item>
|
||||
</n-grid>
|
||||
<n-space class="w-full pt-16px" :size="24" justify="end">
|
||||
<n-button class="w-72px" @click="closeModal">取消</n-button>
|
||||
<n-button class="w-72px" type="primary" @click="handleSubmit">确定</n-button>
|
||||
</n-space>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, watch } from 'vue';
|
||||
import { ref, computed, reactive, watch } from 'vue';
|
||||
import type { FormInst, FormItemRule } from 'naive-ui';
|
||||
import { formRules, createRequiredFormRule } from '@/utils';
|
||||
import { genderOptions, userStatusOptions } from '@/constants';
|
||||
|
||||
defineOptions({ name: 'TableActionModal' });
|
||||
|
||||
type ModalType = 'add' | 'edit';
|
||||
|
||||
interface Props {
|
||||
export interface Props {
|
||||
/** 弹窗可见性 */
|
||||
visible: boolean;
|
||||
/** 弹窗类型 */
|
||||
type?: ModalType;
|
||||
/**
|
||||
* 弹窗类型
|
||||
* add: 新增
|
||||
* edit: 编辑
|
||||
*/
|
||||
type?: 'add' | 'edit';
|
||||
/** 编辑的表格行数据 */
|
||||
editData?: UserManagement.UserTable | null;
|
||||
editData?: UserManagement.User | null;
|
||||
}
|
||||
|
||||
export type ModalType = NonNullable<Props['type']>;
|
||||
|
||||
defineOptions({ name: 'TableActionModal' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'add',
|
||||
editData: null
|
||||
@ -63,6 +73,9 @@ const modalVisible = computed({
|
||||
emit('update:visible', visible);
|
||||
}
|
||||
});
|
||||
const closeModal = () => {
|
||||
modalVisible.value = false;
|
||||
};
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<ModalType, string> = {
|
||||
@ -72,22 +85,29 @@ const title = computed(() => {
|
||||
return titles[props.type];
|
||||
});
|
||||
|
||||
type FormModel = Pick<
|
||||
UserManagement.UserTable,
|
||||
'userName' | 'userAge' | 'userGender' | 'userPhone' | 'userEmail' | 'userRole' | 'disabled'
|
||||
>;
|
||||
const formRef = ref<HTMLElement & FormInst>();
|
||||
|
||||
type FormModel = Pick<UserManagement.User, 'userName' | 'age' | 'gender' | 'phone' | 'email' | 'userStatus'>;
|
||||
|
||||
const formModel = reactive<FormModel>(createDefaultFormModel());
|
||||
|
||||
const rules: Record<keyof FormModel, FormItemRule | FormItemRule[]> = {
|
||||
userName: createRequiredFormRule('请输入用户名'),
|
||||
age: createRequiredFormRule('请输入年龄'),
|
||||
gender: createRequiredFormRule('请选择性别'),
|
||||
phone: formRules.phone,
|
||||
email: formRules.email,
|
||||
userStatus: createRequiredFormRule('请选择用户状态')
|
||||
};
|
||||
|
||||
function createDefaultFormModel(): FormModel {
|
||||
return {
|
||||
userName: '',
|
||||
userAge: '',
|
||||
userGender: 'null',
|
||||
userPhone: '',
|
||||
userEmail: '',
|
||||
userRole: 'user',
|
||||
disabled: true
|
||||
age: null,
|
||||
gender: null,
|
||||
phone: '',
|
||||
email: null,
|
||||
userStatus: null
|
||||
};
|
||||
}
|
||||
|
||||
@ -111,6 +131,12 @@ function handleUpdateFormModelByModalType() {
|
||||
handlers[props.type]();
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await formRef.value?.validate();
|
||||
window.$message?.success('新增成功!');
|
||||
closeModal();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
newValue => {
|
||||
|
@ -1,3 +0,0 @@
|
||||
import TableActionModal from './TableActionModal.vue';
|
||||
|
||||
export { TableActionModal };
|
@ -15,9 +15,12 @@
|
||||
导出Excel
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-space>
|
||||
<n-switch />
|
||||
<icon-mdi-refresh class="text-20px" />
|
||||
<n-space align="center" :size="18">
|
||||
<n-button size="small" type="primary" @click="getTableData">
|
||||
<icon-mdi-refresh class="mr-4px text-16px" :class="{ 'animate-spin': loading }" />
|
||||
刷新表格
|
||||
</n-button>
|
||||
<column-setting v-model:columns="columns" />
|
||||
</n-space>
|
||||
</n-space>
|
||||
<n-data-table :columns="columns" :data="tableData" :loading="loading" :pagination="pagination" />
|
||||
@ -27,23 +30,27 @@
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { NButton, NPopconfirm, NSpace, NSwitch, NTag } from 'naive-ui';
|
||||
import type { Ref } from 'vue';
|
||||
import { NButton, NPopconfirm, NSpace, NTag } from 'naive-ui';
|
||||
import type { DataTableColumns, PaginationProps } from 'naive-ui';
|
||||
import { fetchUserManagementList } from '@/service';
|
||||
import { fetchUserList } from '@/service';
|
||||
import { useBoolean, useLoading } from '@/hooks';
|
||||
import { TableActionModal } from './components';
|
||||
import { genderLabels, userStatusLabels } from '@/constants';
|
||||
import TableActionModal from './components/TableActionModal.vue';
|
||||
import type { ModalType } from './components/TableActionModal.vue';
|
||||
import ColumnSetting from './components/ColumnSetting.vue';
|
||||
|
||||
const { loading, startLoading, endLoading } = useLoading(false);
|
||||
const { bool: visible, setTrue: openModal } = useBoolean();
|
||||
|
||||
const tableData = ref<UserManagement.UserTable[]>([]);
|
||||
function setTableData(data: UserManagement.UserTable[]) {
|
||||
const tableData = ref<UserManagement.User[]>([]);
|
||||
function setTableData(data: UserManagement.User[]) {
|
||||
tableData.value = data;
|
||||
}
|
||||
|
||||
async function getTableData() {
|
||||
startLoading();
|
||||
const { data } = await fetchUserManagementList();
|
||||
const { data } = await fetchUserList();
|
||||
if (data) {
|
||||
setTimeout(() => {
|
||||
setTableData(data);
|
||||
@ -52,7 +59,7 @@ async function getTableData() {
|
||||
}
|
||||
}
|
||||
|
||||
const columns: DataTableColumns = [
|
||||
const columns: Ref<DataTableColumns<UserManagement.User>> = ref([
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center'
|
||||
@ -68,66 +75,53 @@ const columns: DataTableColumns = [
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
key: 'userAge',
|
||||
key: 'age',
|
||||
title: '用户年龄',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
key: 'userGenderLabel',
|
||||
key: 'gender',
|
||||
title: '性别',
|
||||
align: 'center',
|
||||
render: row => {
|
||||
const rowData = row as unknown as UserManagement.UserTable;
|
||||
if (row.gender) {
|
||||
const tagTypes: Record<UserManagement.GenderKey, NaiveUI.ThemeColor> = {
|
||||
'0': 'success',
|
||||
'1': 'warning'
|
||||
};
|
||||
|
||||
if (rowData.userGender !== 'null') {
|
||||
const tagType = {
|
||||
male: 'success',
|
||||
female: 'warning'
|
||||
} as const;
|
||||
return <NTag type={tagType[rowData.userGender]}>{rowData.userGenderLabel}</NTag>;
|
||||
return <NTag type={tagTypes[row.gender]}>{genderLabels[row.gender]}</NTag>;
|
||||
}
|
||||
|
||||
return <span></span>;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'userPhone',
|
||||
key: 'phone',
|
||||
title: '手机号码',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
key: 'userEmail',
|
||||
key: 'email',
|
||||
title: '邮箱',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
key: 'userRole',
|
||||
title: '角色',
|
||||
align: 'center',
|
||||
render: row => {
|
||||
const rowData = row as unknown as UserManagement.UserTable;
|
||||
|
||||
const tagType = {
|
||||
super: 'primary',
|
||||
admin: 'warning',
|
||||
user: 'success'
|
||||
} as const;
|
||||
|
||||
return <NTag type={tagType[rowData.userRole]}>{rowData.userRole}</NTag>;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'disabled',
|
||||
key: 'userStatus',
|
||||
title: '状态',
|
||||
align: 'center',
|
||||
render: row => {
|
||||
const rowData = row as unknown as UserManagement.UserTable;
|
||||
if (row.userStatus) {
|
||||
const tagTypes: Record<UserManagement.UserStatusKey, NaiveUI.ThemeColor> = {
|
||||
'1': 'success',
|
||||
'2': 'error',
|
||||
'3': 'warning',
|
||||
'4': 'default'
|
||||
};
|
||||
|
||||
return (
|
||||
<NSwitch value={rowData.disabled} onUpdateValue={disabled => handleUpdateDisabled(disabled, rowData.id)}>
|
||||
{{ checked: () => '启用', unchecked: () => '禁用' }}
|
||||
</NSwitch>
|
||||
);
|
||||
return <NTag type={tagTypes[row.userStatus]}>{userStatusLabels[row.userStatus]}</NTag>;
|
||||
}
|
||||
return <span></span>;
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -135,13 +129,12 @@ const columns: DataTableColumns = [
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
render: row => {
|
||||
const rowData = row as unknown as UserManagement.UserTable;
|
||||
return (
|
||||
<NSpace justify={'center'}>
|
||||
<NButton size={'small'} onClick={() => handleEditTable(rowData.id)}>
|
||||
<NButton size={'small'} onClick={() => handleEditTable(row.id)}>
|
||||
编辑
|
||||
</NButton>
|
||||
<NPopconfirm onPositiveClick={() => handleDeleteTable(rowData.id)}>
|
||||
<NPopconfirm onPositiveClick={() => handleDeleteTable(row.id)}>
|
||||
{{
|
||||
default: () => '确认删除',
|
||||
trigger: () => <NButton size={'small'}>删除</NButton>
|
||||
@ -151,24 +144,17 @@ const columns: DataTableColumns = [
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function handleUpdateDisabled(disabled: boolean, rowId: string) {
|
||||
const index = tableData.value.findIndex(item => item.id === rowId);
|
||||
if (index > -1) {
|
||||
tableData.value[index].disabled = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
type ModalType = 'add' | 'edit';
|
||||
]) as Ref<DataTableColumns<UserManagement.User>>;
|
||||
|
||||
const modalType = ref<ModalType>('add');
|
||||
|
||||
function setModalType(type: ModalType) {
|
||||
modalType.value = type;
|
||||
}
|
||||
|
||||
const editData = ref<UserManagement.UserTable | null>(null);
|
||||
function setEditData(data: UserManagement.UserTable | null) {
|
||||
const editData = ref<UserManagement.User | null>(null);
|
||||
|
||||
function setEditData(data: UserManagement.User | null) {
|
||||
editData.value = data;
|
||||
}
|
||||
|
||||
@ -185,6 +171,7 @@ function handleEditTable(rowId: string) {
|
||||
setModalType('edit');
|
||||
openModal();
|
||||
}
|
||||
|
||||
function handleDeleteTable(rowId: string) {
|
||||
window.$message?.info(`点击了删除,rowId为${rowId}`);
|
||||
}
|
||||
|
@ -29,12 +29,14 @@ import { formRules } from '@/utils';
|
||||
const { toLoginModule } = useRouterPush();
|
||||
const { label, isCounting, loading: smsLoading, getSmsCode } = useSmsCode();
|
||||
|
||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
|
||||
const formRef = ref<HTMLElement & FormInst>();
|
||||
|
||||
const model = reactive({
|
||||
phone: '',
|
||||
code: '',
|
||||
imgCode: ''
|
||||
});
|
||||
|
||||
const rules = {
|
||||
phone: formRules.phone,
|
||||
code: formRules.code
|
||||
@ -44,17 +46,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('验证成功!');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -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<HTMLElement & FormInst>();
|
||||
|
||||
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('验证成功!');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -48,26 +48,25 @@ const auth = useAuthStore();
|
||||
const { login } = useAuthStore();
|
||||
const { toLoginModule } = useRouterPush();
|
||||
|
||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
|
||||
const formRef = ref<HTMLElement & FormInst>();
|
||||
|
||||
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 }) {
|
||||
|
@ -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<HTMLElement & FormInst>();
|
||||
|
||||
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('验证成功!');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -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<HTMLElement & FormInst>();
|
||||
|
||||
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('验证成功!');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user