添加用户页面.

This commit is contained in:
lijiahang
2023-08-16 15:32:08 +08:00
parent 4f8bc6c046
commit d19cc66baa
12 changed files with 153 additions and 212 deletions

View File

@@ -1,5 +1,4 @@
import axios from 'axios';
import qs from 'query-string';
import { DataGrid, Pagination } from '@/types/global';
/**
@@ -21,6 +20,8 @@ export interface UserCreateRequest {
*/
export interface UserUpdateRequest extends UserCreateRequest {
id: number;
roles?: Array<number>;
password?: string;
}
/**
@@ -44,7 +45,6 @@ export interface UserQueryRequest extends Pagination {
export interface UserQueryResponse {
id?: number;
username?: string;
password?: string;
nickname?: string;
avatar?: string;
mobile?: string;
@@ -71,6 +71,27 @@ export function updateUser(request: UserUpdateRequest) {
return axios.put('/infra/system-user/update', request);
}
/**
* 修改用户状态
*/
export function updateUserStatus(request: UserUpdateRequest) {
return axios.put('/infra/system-user/update-status', request);
}
/**
* 修改用户角色
*/
export function updateUserRole(request: UserUpdateRequest) {
return axios.put('/infra/system-user/update-role', request);
}
/**
* 重置用户密码
*/
export function resetUserPassword(request: UserUpdateRequest) {
return axios.put('/infra/system-user/reset-password', request);
}
/**
* 通过 id 查询用户
*/
@@ -79,15 +100,10 @@ export function getUser(id: number) {
}
/**
* 通过 id 批量查询用户
* 查询所有用户
*/
export function getUserList(idList: Array<number>) {
return axios.get<UserQueryResponse[]>('/infra/system-user/list', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
export function getUserList() {
return axios.get<UserQueryResponse[]>('/infra/system-user/list');
}
/**
@@ -104,14 +120,3 @@ export function deleteUser(id: number) {
return axios.delete('/infra/system-user/delete', { params: { id } });
}
/**
* 通过 id 批量删除用户
*/
export function batchDeleteUser(idList: Array<number>) {
return axios.delete('/infra/system-user/delete-batch', {
params: { idList },
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'comma' });
}
});
}

View File

@@ -38,7 +38,6 @@ const columns = [
title: '组件名称',
dataIndex: 'component',
slotName: 'component',
width: 150,
ellipsis: true,
tooltip: true,
}, {

View File

@@ -3,7 +3,7 @@
body-class="modal-form"
title-align="start"
:title="title"
:top="120"
:top="80"
:align-center="false"
:draggable="true"
:mask-closable="false"

View File

@@ -17,7 +17,7 @@
<a-form-item field="status" label="角色状态" label-col-flex="50px">
<a-select v-model="formModel.status"
placeholder="请选择角色状态"
:options="toOptions(ROLE_STATUS_ENUM)"
:options="toOptions(RoleStatusEnum)"
allow-clear />
</a-form-item>
</a-query-header>
@@ -62,24 +62,24 @@
</template>
<!-- 状态 -->
<template #status="{ record }">
<a-tag :color="getEnumValue(record.status, ROLE_STATUS_ENUM,'color')">
{{ getEnumValue(record.status, ROLE_STATUS_ENUM) }}
<a-tag :color="getEnumValue(record.status, RoleStatusEnum,'color')">
{{ getEnumValue(record.status, RoleStatusEnum) }}
</a-tag>
</template>
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">
<!-- 修改状态 -->
<a-popconfirm :content="`确定要${toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'label')}当前角色吗?`"
<a-popconfirm :content="`确定要${toggleEnumValue(record.status, RoleStatusEnum, 'label')}当前角色吗?`"
position="left"
type="warning"
@ok="toggleRoleStatus(record)">
<a-button v-permission="['infra:system-role:delete']"
:disabled="record.code === 'admin'"
:status="toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'status')"
:status="toggleEnumValue(record.status, RoleStatusEnum, 'status')"
type="text"
size="mini">
{{ toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'label') }}
{{ toggleEnumValue(record.status, RoleStatusEnum, 'label') }}
</a-button>
</a-popconfirm>
<!-- 绑定菜单 -->
@@ -128,7 +128,7 @@
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
import { ROLE_STATUS_ENUM } from '../types/enum.types';
import { RoleStatusEnum } from '../types/enum.types';
import { toOptions, getEnumValue, toggleEnumValue, toggleEnum } from '@/utils/enum';
import { defaultPagination } from '@/types/table';
@@ -149,7 +149,7 @@
const toggleRoleStatus = async (record: any) => {
try {
setLoading(true);
const toggleStatus = toggleEnum(record.status, ROLE_STATUS_ENUM);
const toggleStatus = toggleEnum(record.status, RoleStatusEnum);
// 调用修改接口
await updateRoleStatus({ id: record.id, status: toggleStatus.value });
Message.success(`${toggleStatus.label}成功`);

View File

@@ -1,7 +1,7 @@
/**
* 角色状态
*/
export const ROLE_STATUS_ENUM = {
export const RoleStatusEnum = {
DISABLED: {
value: 0,
label: '停用',

View File

@@ -26,6 +26,7 @@ const columns = [
title: '创建时间',
dataIndex: 'createTime',
slotName: 'createTime',
width: 180,
align: 'center',
render: ({ record }) => {
return dateFormat(new Date(record.createTime));

View File

@@ -3,7 +3,7 @@
body-class="modal-form"
title-align="start"
:title="title"
:top="120"
:top="80"
:align-center="false"
:draggable="true"
:mask-closable="false"
@@ -22,20 +22,18 @@
:rules="formRules">
<!-- 用户名 -->
<a-form-item field="username" label="用户名">
<a-input v-model="formModel.username" placeholder="请输入用户名" />
<a-input v-model="formModel.username" :disabled="!isAddHandle" placeholder="请输入用户名" />
</a-form-item>
<!-- 密码 -->
<a-form-item field="password" label="密码">
<a-input v-model="formModel.password" placeholder="请输入密码" />
<a-form-item v-if="isAddHandle"
field="password"
label="密码">
<a-input-password v-model="formModel.password" placeholder="请输入密码" />
</a-form-item>
<!-- 花名 -->
<a-form-item field="nickname" label="花名">
<a-input v-model="formModel.nickname" placeholder="请输入花名" />
</a-form-item>
<!-- 头像地址 -->
<a-form-item field="avatar" label="头像地址">
<a-input v-model="formModel.avatar" placeholder="请输入头像地址" />
</a-form-item>
<!-- 手机号 -->
<a-form-item field="mobile" label="手机号">
<a-input v-model="formModel.mobile" placeholder="请输入手机号" />
@@ -44,17 +42,6 @@
<a-form-item field="email" label="邮箱">
<a-input v-model="formModel.email" placeholder="请输入邮箱" />
</a-form-item>
<!-- 用户状态 0停用 1启用 2锁定 -->
<a-form-item field="status" label="用户状态 0停用 1启用 2锁定">
<a-input-number v-model="formModel.status" placeholder="请输入用户状态 0停用 1启用 2锁定" />
</a-form-item>
<!-- 最后登录时间 -->
<a-form-item field="lastLoginTime" label="最后登录时间">
<a-date-picker v-model="formModel.lastLoginTime"
style="width: 100%"
placeholder="请选择最后登录时间"
show-time />
</a-form-item>
</a-form>
</a-spin>
</a-modal>
@@ -73,6 +60,7 @@
import formRules from '../types/form.rules';
import { createUser, updateUser } from '@/api/user/user';
import { Message } from '@arco-design/web-vue';
import { md5 } from '@/utils';
const { visible, setVisible } = useVisible();
const { loading, setLoading } = useLoading();
@@ -86,11 +74,8 @@
username: undefined,
password: undefined,
nickname: undefined,
avatar: undefined,
mobile: undefined,
email: undefined,
status: undefined,
lastLoginTime: undefined,
};
};
@@ -135,7 +120,7 @@
}
if (isAddHandle.value) {
// 新增
await createUser(formModel as any);
await createUser({ ...formModel, password: md5(formModel.password) } as any);
Message.success('创建成功');
emits('added');
} else {

View File

@@ -6,44 +6,33 @@
@submit="fetchTableData"
@reset="fetchTableData">
<!-- id -->
<a-form-item field="id" label="id" label-col-flex="60px">
<a-input-number v-model="formModel.id" placeholder="请输入id" allow-clear/>
<a-form-item field="id" label="id" label-col-flex="50px">
<a-input-number v-model="formModel.id" placeholder="请输入id" allow-clear />
</a-form-item>
<!-- 用户名 -->
<a-form-item field="username" label="用户名" label-col-flex="60px">
<a-input v-model="formModel.username" placeholder="请输入用户名" allow-clear/>
</a-form-item>
<!-- 密码 -->
<a-form-item field="password" label="密码" label-col-flex="60px">
<a-input v-model="formModel.password" placeholder="请输入密码" allow-clear/>
<a-form-item field="username" label="用户名" label-col-flex="50px">
<a-input v-model="formModel.username" placeholder="请输入用户名" allow-clear />
</a-form-item>
<!-- 花名 -->
<a-form-item field="nickname" label="花名" label-col-flex="60px">
<a-input v-model="formModel.nickname" placeholder="请输入花名" allow-clear/>
<a-form-item field="nickname" label="花名" label-col-flex="50px">
<a-input v-model="formModel.nickname" placeholder="请输入花名" allow-clear />
</a-form-item>
<!-- 头像地址 -->
<a-form-item field="avatar" label="头像地址" label-col-flex="60px">
<a-input v-model="formModel.avatar" placeholder="请输入头像地址" allow-clear/>
<!-- 用户状态 -->
<a-form-item field="status" label="用户状态 " label-col-flex="50px">
<a-select
v-model="formModel.status"
:options="toOptions(UserStatusEnum)"
placeholder="请选择用户状态"
allow-clear
/>
</a-form-item>
<!-- 手机号 -->
<a-form-item field="mobile" label="手机号" label-col-flex="60px">
<a-input v-model="formModel.mobile" placeholder="请输入手机号" allow-clear/>
<a-form-item field="mobile" label="手机号" label-col-flex="50px">
<a-input v-model="formModel.mobile" placeholder="请输入手机号" allow-clear />
</a-form-item>
<!-- 邮箱 -->
<a-form-item field="email" label="邮箱" label-col-flex="60px">
<a-input v-model="formModel.email" placeholder="请输入邮箱" allow-clear/>
</a-form-item>
<!-- 用户状态 0停用 1启用 2锁定 -->
<a-form-item field="status" label="用户状态 0停用 1启用 2锁定" label-col-flex="60px">
<a-input-number v-model="formModel.status" placeholder="请输入用户状态 0停用 1启用 2锁定" allow-clear/>
</a-form-item>
<!-- 最后登录时间 -->
<a-form-item field="lastLoginTime" label="最后登录时间" label-col-flex="60px">
<a-date-picker v-model="formModel.lastLoginTime"
style="width: 100%"
placeholder="请选择最后登录时间"
show-time
allow-clear/>
<a-form-item field="email" label="邮箱" label-col-flex="50px">
<a-input v-model="formModel.email" placeholder="请输入邮箱" allow-clear />
</a-form-item>
</a-query-header>
</a-card>
@@ -66,21 +55,6 @@
<icon-plus />
</template>
</a-button>
<!-- 删除 -->
<a-popconfirm position="br"
type="warning"
:content="`确认删除选中的${selectedKeys.length}条记录吗?`"
@ok="deleteSelectRows">
<a-button v-permission="['infra:system-user:delete']"
type="secondary"
status="danger"
:disabled="selectedKeys.length === 0">
删除
<template #icon>
<icon-delete />
</template>
</a-button>
</a-popconfirm>
</a-space>
</div>
</template>
@@ -91,16 +65,38 @@
label-align="left"
:loading="loading"
:columns="columns"
v-model:selectedKeys="selectedKeys"
:row-selection="rowSelection"
:data="tableRenderData"
:pagination="pagination"
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
@page-size-change="(size) => fetchTableData(pagination.current, size)"
:bordered="false">
<!-- 状态 -->
<template #status="{ record }">
<a-tag :color="getEnumValue(record.status, UserStatusEnum,'color')">
{{ getEnumValue(record.status, UserStatusEnum) }}
</a-tag>
</template>
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">
<!-- 启用/停用 -->
<a-popconfirm :content="`确定要${UserStatusEnum.ENABLED.value === record.status
? UserStatusEnum.DISABLED.label
: UserStatusEnum.ENABLED.label}当前用户?`"
position="left"
type="warning"
@ok="updateStatus(record)">
<a-button type="text"
size="mini"
:disabled="record.id === userStore.id"
v-permission="['infra:system-user:update-status']">
{{
UserStatusEnum.ENABLED.value === record.status
? UserStatusEnum.DISABLED.label
: UserStatusEnum.ENABLED.label
}}
</a-button>
</a-popconfirm>
<!-- 修改 -->
<a-button type="text"
size="mini"
@@ -108,6 +104,20 @@
@click="emits('openUpdate', record)">
修改
</a-button>
<!-- 重置密码 -->
<a-button type="text"
size="mini"
v-permission="['infra:system-user:reset-password']"
@click="emits('openResetPassword', record)">
重置密码
</a-button>
<!-- 分配角色 -->
<a-button type="text"
size="mini"
v-permission="['infra:system-user:update-role']"
@click="emits('openUpdateRole', record)">
分配角色
</a-button>
<!-- 删除 -->
<a-popconfirm content="确认删除这条记录吗?"
position="left"
@@ -115,6 +125,7 @@
@ok="deleteRow(record)">
<a-button type="text" size="mini"
status="danger"
:disabled="record.id === userStore.id"
v-permission="['infra:system-user:delete']">
删除
</a-button>
@@ -133,19 +144,20 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { batchDeleteUser, deleteUser, getUserPage, UserQueryRequest, UserQueryResponse } from '@/api/user/user';
import { deleteUser, getUserPage, updateUserStatus, UserQueryRequest, UserQueryResponse } from '@/api/user/user';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from '../types/table.columns';
import { defaultPagination, defaultRowSelection } from '@/types/table';
import { UserStatusEnum } from '../types/enum.types';
import { defaultPagination } from '@/types/table';
import { toOptions, getEnumValue } from '@/utils/enum';
import { useUserStore } from '@/store';
const tableRenderData = ref<UserQueryResponse[]>();
const { loading, setLoading } = useLoading();
const emits = defineEmits(['openAdd', 'openUpdate']);
const emits = defineEmits(['openAdd', 'openUpdate', 'openResetPassword', 'openUpdateRole']);
const pagination = reactive(defaultPagination());
const selectedKeys = ref<number[]>([]);
const rowSelection = reactive(defaultRowSelection());
const formModel = reactive<UserQueryRequest>({
id: undefined,
@@ -159,20 +171,7 @@
lastLoginTime: undefined,
});
// 删除选中行
const deleteSelectRows = async () => {
try {
setLoading(true);
// 调用删除接口
await batchDeleteUser(selectedKeys.value);
Message.success(`成功删除${selectedKeys.value.length}条数据`);
selectedKeys.value = [];
// 重新加载数据
await fetchTableData();
} finally {
setLoading(false);
}
};
const userStore = useUserStore();
// 删除当前行
const deleteRow = async ({ id }: { id: number }) => {
@@ -188,6 +187,25 @@
}
};
// 更新状态
const updateStatus = async (record: any) => {
try {
setLoading(true);
// 更新状态
const newStatus = UserStatusEnum.ENABLED.value === record.status
? UserStatusEnum.DISABLED
: UserStatusEnum.ENABLED;
await updateUserStatus({
id: record.id,
status: newStatus.value
});
Message.success(`${newStatus.label}成功`);
record.status = newStatus.value;
} finally {
setLoading(false);
}
};
// 添加后回调
const addedCallback = () => {
fetchTableData();

View File

@@ -2,12 +2,12 @@
<div class="layout-container">
<!-- 表格 -->
<user-table ref="table"
@openAdd="() => modal.openAdd()"
@openUpdate="(e) => modal.openUpdate(e)" />
@openAdd="() => modal.openAdd()"
@openUpdate="(e) => modal.openUpdate(e)" />
<!-- 添加修改模态框 -->
<user-form-modal ref="modal"
@added="() => table.addedCallback()"
@updated="() => table.updatedCallback()" />
@added="() => table.addedCallback()"
@updated="() => table.updatedCallback()" />
</div>
</template>

View File

@@ -1,27 +1,21 @@
/**
*
*用户状态
*/
export const USER_STATUS_ENUM = {
export const UserStatusEnum = {
DISABLED: {
value: null,
label: '',
color: '',
status: '0',
name: 'DISABLED',
value: 0,
label: '停用',
color: 'orange',
},
ENABLED: {
value: null,
label: '',
color: '',
status: '1',
name: 'ENABLED',
value: 1,
label: '启用',
color: 'blue',
},
LOCKED: {
value: null,
label: '',
color: '',
status: '2',
name: 'LOCKED',
value: 2,
label: '锁定',
color: 'orange',
},
}
};

View File

@@ -6,14 +6,18 @@ export const username = [{
}, {
maxLength: 32,
message: '用户名长度不能大于32位'
}, {
match: /^[a-zA-Z0-9]{4,32}$/,
message: '用户名只能是 4-32 位的数字以及字母'
}] as FieldRule[];
export const password = [{
required: true,
message: '请输入密码'
}, {
maxLength: 64,
message: '密码长度不能大于64位'
minLength: 8,
maxLength: 32,
message: '密码长度需要在 8-32 位之间'
}] as FieldRule[];
export const nickname = [{
@@ -24,47 +28,20 @@ export const nickname = [{
message: '花名长度不能大于16位'
}] as FieldRule[];
export const avatar = [{
required: true,
message: '请输入头像地址'
}, {
maxLength: 500,
message: '头像地址长度不能大于500位'
}] as FieldRule[];
export const mobile = [{
required: true,
message: '请输入手机号'
}, {
maxLength: 15,
message: '手机号长度不能大于15位'
}] as FieldRule[];
export const email = [{
required: true,
message: '请输入邮箱'
}, {
maxLength: 64,
message: '邮箱长度不能大于64位'
}] as FieldRule[];
export const status = [{
required: true,
message: '请输入用户状态 0停用 1启用 2锁定'
}] as FieldRule[];
export const lastLoginTime = [{
required: true,
message: '请输入最后登录时间'
}] as FieldRule[];
export default {
username,
password,
nickname,
avatar,
mobile,
email,
status,
lastLoginTime,
} as Record<string, FieldRule | FieldRule[]>;

View File

@@ -13,28 +13,12 @@ const columns = [
title: '用户名',
dataIndex: 'username',
slotName: 'username',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '密码',
dataIndex: 'password',
slotName: 'password',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '花名',
dataIndex: 'nickname',
slotName: 'nickname',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '头像地址',
dataIndex: 'avatar',
slotName: 'avatar',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
@@ -48,11 +32,10 @@ const columns = [
title: '邮箱',
dataIndex: 'email',
slotName: 'email',
align: 'center',
ellipsis: true,
tooltip: true,
}, {
title: '用户状态 0停用 1启用 2锁定',
title: '用户状态',
dataIndex: 'status',
slotName: 'status',
align: 'center',
@@ -60,36 +43,15 @@ const columns = [
title: '最后登录时间',
dataIndex: 'lastLoginTime',
slotName: 'lastLoginTime',
width: 180,
align: 'center',
render: ({ record }) => {
return record.lastLoginTime && dateFormat(new Date(record.lastLoginTime));
},
}, {
title: '创建时间',
dataIndex: 'createTime',
slotName: 'createTime',
render: ({ record }) => {
return dateFormat(new Date(record.createTime));
},
}, {
title: '修改时间',
dataIndex: 'updateTime',
slotName: 'updateTime',
render: ({ record }) => {
return dateFormat(new Date(record.createTime));
},
}, {
title: '创建人',
dataIndex: 'creator',
slotName: 'creator',
}, {
title: '修改人',
dataIndex: 'updater',
slotName: 'updater',
}, {
title: '操作',
slotName: 'handle',
width: 130,
width: 290,
align: 'center',
fixed: 'right',
},