Browse Source

feat: 问题工单模块初始化

master
AaronWu 2 months ago
parent
commit
a2d2905357
  1. 14
      src/api/issue/index.ts
  2. 43
      src/api/issue/model.d.ts
  3. 15
      src/api/upload/index.ts
  4. 3
      src/api/upload/model.d.ts
  5. 1
      src/locales/lang/en-US/routes/system.ts
  6. 1
      src/locales/lang/zh-CN/routes/system.ts
  7. 4
      src/router/staticModules/question.ts
  8. 13
      src/router/staticModules/system.ts
  9. 25
      src/utils/fileUtils.ts
  10. 86
      src/views/dashboard/welcome/index.vue
  11. 90
      src/views/question/issue/columns.tsx
  12. 26
      src/views/question/issue/data.ts
  13. 21
      src/views/question/issue/formSchemas.ts
  14. 171
      src/views/question/issue/formSchemas.tsx
  15. 38
      src/views/question/issue/index.vue
  16. 363
      src/views/system/admin/columns.tsx
  17. 14
      src/views/system/admin/data.ts
  18. 123
      src/views/system/admin/formSchemas.ts
  19. 241
      src/views/system/admin/index.vue

14
src/api/issue/index.ts

@ -16,7 +16,7 @@ import { request } from '@/utils/request';
export function fetchIssueList(data: API.SearchListParams) {
return request<BaseResponse<API.SearchListResult>>(
{
url: 'issue/list',
url: 'question/list',
method: 'post',
data,
},
@ -34,7 +34,7 @@ export function fetchIssueList(data: API.SearchListParams) {
export function fetchIssuePageList(data: API.SearchPageListParams) {
return request<BaseResponse<API.SearchPageListResult>>(
{
url: 'issue/page',
url: 'question/list',
method: 'post',
data,
},
@ -51,7 +51,7 @@ export function fetchIssuePageList(data: API.SearchPageListParams) {
*/
export function createIssue(data: API.IssueType) {
return request({
url: 'issue/create',
url: 'question/create',
method: 'post',
data,
});
@ -64,7 +64,7 @@ export function createIssue(data: API.IssueType) {
*/
export function updateIssue(data: API.IssueType) {
return request({
url: 'issue/update',
url: 'question/update',
method: 'post',
data,
});
@ -75,7 +75,7 @@ export function updateIssue(data: API.IssueType) {
*/
export function findOneById(params: { id: string }) {
return request({
url: `issue/getById`,
url: `question/getById`,
method: 'get',
params,
});
@ -86,7 +86,7 @@ export function findOneById(params: { id: string }) {
*/
export function deleteIssueById(params: API.DeleteIssueParams) {
return request({
url: `issue/delById`,
url: `question/delById`,
method: 'post',
params,
});
@ -97,7 +97,7 @@ export function deleteIssueById(params: API.DeleteIssueParams) {
*/
export function deleteBatchIssueById(data: API.DeleteBatchIssueParams) {
return request({
url: `issue/deleteBatch`,
url: `question/delete`,
method: 'delete',
data,
});

43
src/api/issue/model.d.ts

@ -2,13 +2,48 @@ declare namespace API {
/** 问题工单类型 */
type IssueType = {
id?: string;
title: string;
content: string;
agent?: string; // 代理商
attribute?: string; // 问题属性
billcode?: string; // 问题号
contacts?: string; // 联系人
contactsEmail?: string; // 联系人邮箱
contactsMobile?: string; // 联系人手机号
createTime?: string; // 创建时间
createUserid?: number; // 创建人ID
customer?: string; // 客户
description?: string; // 问题描述
dr?: number; // 删除标记
product?: string; // 产品
serviceNumber?: string; // 服务号
state?: number; // 状态
title?: string; // 问题标题
updateTime?: string; // 更新时间
updateUserid?: number; // 更新人ID
version?: string; // 版本
fileList?: any[]; // 附件列表
};
type CreateIssueParams = {
title: string;
content: string;
id?: string;
agent?: string; // 代理商
attribute?: string; // 问题属性
billcode?: string; // 问题号
contacts?: string; // 联系人
contactsEmail?: string; // 联系人邮箱
contactsMobile?: string; // 联系人手机号
createTime?: string; // 创建时间
createUserid?: number; // 创建人ID
customer?: string; // 客户
description?: string; // 问题描述
dr?: number; // 删除标记
product?: string; // 产品
serviceNumber?: string; // 服务号
state?: number; // 状态
title?: string; // 问题标题
updateTime?: string; // 更新时间
updateUserid?: number; // 更新人ID
version?: string; // 版本
fileList?: any[]; // 附件列表
};
type DeleteIssueParams = {

15
src/api/upload/index.ts

@ -0,0 +1,15 @@
import { request } from '@/utils/request';
/**
* @description
* @param {} data
* @returns
*/
export function commonUpload(data) {
return request({
url: `/file/upload`,
method: 'post',
data,
timeout: 30000,
});
}

3
src/api/upload/model.d.ts

@ -0,0 +1,3 @@
declare namespace API {
}

1
src/locales/lang/en-US/routes/system.ts

@ -10,4 +10,5 @@ export default {
system: 'system',
dictionary: 'Dictionary',
user: 'User',
admin: 'Admin',
};

1
src/locales/lang/zh-CN/routes/system.ts

@ -2,4 +2,5 @@ export default {
system: '系统管理',
dictionary: '字典维护',
user: '用户管理',
admin: '管理员管理',
};

4
src/router/staticModules/question.ts

@ -12,7 +12,7 @@ const routes: Array<RouteRecordRaw> = [
component: RouterView,
meta: {
title: t('routes.question.question'),
icon: 'icon-issue',
icon: 'icon-kehucanshupeizhi',
},
children: [
{
@ -20,7 +20,7 @@ const routes: Array<RouteRecordRaw> = [
name: `${moduleName}-issue`,
meta: {
title: t('routes.question.issue'),
icon: 'icon-issue',
icon: 'icon-icon_shiyongwendang',
},
component: () =>
import(/* webpackChunkName: "question-issue" */ '@/views/question/issue/index.vue'),

13
src/router/staticModules/system.ts

@ -42,7 +42,18 @@ const routes: Array<RouteRecordRaw> = [
keepAlive: true,
},
component: () =>
import(/* webpackChunkName: "tenant-user" */ '@/views/system/user/index.vue'),
import(/* webpackChunkName: "system-user" */ '@/views/system/user/index.vue'),
},
{
path: 'admin',
name: `${moduleName}-admin`,
meta: {
title: t('routes.system.admin'),
icon: 'icon-yonghu1',
keepAlive: true,
},
component: () =>
import(/* webpackChunkName: "system-admin" */ '@/views/system/admin/index.vue'),
},
],
},

25
src/utils/fileUtils.ts

@ -0,0 +1,25 @@
export function downloadByData(data: BlobPart, filename: string, mime?: string, bom?: BlobPart) {
const blobData = typeof bom !== 'undefined' ? [bom, data] : [data];
const blob = new Blob(blobData, { type: mime || 'application/vnd.ms-excel' });
const blobURL = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', filename);
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
window.URL.revokeObjectURL(blobURL);
}
export function getFileExtension(filename: string): string | null {
const dotIndex = filename.lastIndexOf('.');
if (dotIndex === -1) {
return null; // 文件名中不存在扩展名
}
return filename.slice(dotIndex + 1).toLowerCase();
}

86
src/views/dashboard/welcome/index.vue

@ -7,6 +7,8 @@
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
<svg-icon name="icon-wangpan" size="50"></svg-icon>
<div class="welcome-container">
<a-card class="doc-card">
<template #title>
@ -30,61 +32,61 @@
</script>
<style lang="less" scoped>
.welcome-container {
padding: 24px;
.welcome-container {
padding: 24px;
.doc-card {
background: #fff;
border-radius: 8px;
.doc-card {
background: #fff;
border-radius: 8px;
.card-title {
display: flex;
align-items: center;
gap: 12px;
.card-title {
display: flex;
align-items: center;
gap: 12px;
span {
font-size: 16px;
font-weight: 500;
span {
font-size: 16px;
font-weight: 500;
}
}
}
}
:deep(.markdown-body) {
padding: 16px 0;
:deep(.markdown-body) {
padding: 16px 0;
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
th,
td {
padding: 12px;
border: 1px solid #e8e8e8;
}
th,
td {
padding: 12px;
border: 1px solid #e8e8e8;
}
th {
background: #fafafa;
font-weight: 500;
}
th {
background: #fafafa;
font-weight: 500;
}
tr:hover {
background: #fafafa;
tr:hover {
background: #fafafa;
}
}
}
h2 {
font-size: 24px;
margin: 24px 0 16px;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
}
h2 {
font-size: 24px;
margin: 24px 0 16px;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
}
h3 {
font-size: 18px;
margin: 20px 0 12px;
color: #1a1a1a;
h3 {
font-size: 18px;
margin: 20px 0 12px;
color: #1a1a1a;
}
}
}
}
</style>

90
src/views/question/issue/columns.tsx

@ -1,4 +1,6 @@
import type { TableColumn } from '@/components/core/dynamic-table';
import { stateTypeList } from './data';
import { Tag } from 'ant-design-vue';
export type TableListItem = API.IssueType;
export type TableColumnItem = TableColumn<TableListItem>;
// 数据项类型
@ -6,7 +8,7 @@ export type TableColumnItem = TableColumn<TableListItem>;
// 使用TableColumn<ListItemType> 将会限制dataIndex的类型,但换来的是dataIndex有类型提示
export const baseColumns: TableColumnItem[] = [
{
title: '标题',
title: '问题标题',
align: 'center',
dataIndex: 'title',
// sorter: true,
@ -17,7 +19,71 @@ export const baseColumns: TableColumnItem[] = [
required: false,
},
},
{
title: '问题描述',
align: 'center',
dataIndex: 'description',
width: 150,
resizable: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '问题号',
align: 'center',
dataIndex: 'billcode',
width: 150,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '客户',
align: 'center',
dataIndex: 'customer',
width: 150,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '产品',
align: 'center',
dataIndex: 'product',
width: 150,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '代理商',
align: 'center',
dataIndex: 'agent',
width: 150,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '服务号',
align: 'center',
dataIndex: 'serviceNumber',
width: 150,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '创建时间',
align: 'center',
@ -29,4 +95,24 @@ export const baseColumns: TableColumnItem[] = [
component: 'RangePicker',
},
},
// state 问题状态
{
title: '问题状态',
align: 'center',
dataIndex: 'state',
width: 150,
formItemProps: {
defaultValue: undefined,
required: false,
component: 'Select',
componentProps: {
options: stateTypeList,
},
},
customRender: ({ record }) => {
const { label, color } = stateTypeList.find((e) => e.value === record.state);
return <Tag color={color}>{label}</Tag>;
},
},
];

26
src/views/question/issue/data.ts

@ -1,14 +1,32 @@
export const sexTypeList:any = [
export const stateTypeList: any = [
// Init(0,"初始化"),
// Back(1,"退回"),
// Develop(2,"开发"),
// Test(3,"测试"),
// End(4,"结束"),
{
label: '男',
label: '初始化',
value: 0,
color: '#f50',
},
{
label: '退回',
value: 1,
color: '#2db7f5',
},
{
label: '女',
label: '开发',
value: 2,
color: '#87d068',
},
{
label: '未知',
label: '测试',
value: 3,
color: '#108ee9',
},
{
label: '结束',
value: 4,
color: '#f50',
},
];

21
src/views/question/issue/formSchemas.ts

@ -1,21 +0,0 @@
import type { FormSchema } from '@/components/core/schema-form/';
export const issueSchemas: FormSchema<API.CreateIssueParams>[] = [
{
field: 'title',
component: 'Input',
label: '账号',
colProps: {
span: 12,
},
rules: [{ required: true }],
},
{
field: 'content',
label: '内容',
slot: 'content',
colProps: {
span: 12,
},
},
];

171
src/views/question/issue/formSchemas.tsx

@ -0,0 +1,171 @@
import type { FormSchema } from '@/components/core/schema-form/';
import { TableListItem } from './columns';
import { commonUpload } from '@/api/upload';
import { message, Button } from 'ant-design-vue';
import { getFileExtension } from '@/utils/fileUtils';
import { UploadOutlined } from '@ant-design/icons-vue';
import { stateTypeList } from './data';
import { url } from 'inspector';
import component from '@/locales/lang/en-US/component';
// 编辑页字段
export const getEditFormSchema: (
row?: Partial<TableListItem>,
isDetail?: boolean,
) => FormSchema[] = (record = {}, isDetail = false) => {
return [
{
field: 'title',
component: 'Input',
label: '问题标题',
colProps: {
span: 12,
},
rules: [{ required: true }],
},
{
field: 'billcode',
component: 'Input',
label: '问题号',
colProps: {
span: 12,
},
},
{
field: 'customer',
component: 'Input',
label: '客户',
colProps: {
span: 12,
},
},
{
field: 'product',
component: 'Input',
label: '产品',
colProps: {
span: 12,
},
},
{
field: 'agent',
component: 'Input',
label: '代理商',
colProps: {
span: 12,
},
},
{
field: 'attribute',
component: 'Input',
label: '问题属性',
colProps: {
span: 12,
},
},
{
field: 'serviceNumber',
component: 'Input',
label: '服务号',
colProps: {
span: 12,
},
},
{
field: 'contacts',
component: 'Input',
label: '联系人',
colProps: {
span: 12,
},
},
{
field: 'contactsEmail',
component: 'Input',
label: '联系人邮箱',
colProps: {
span: 12,
},
},
{
field: 'contactsMobile',
component: 'Input',
label: '联系人手机号',
colProps: {
span: 12,
},
},
{
field: 'state',
component: 'Select',
label: '状态',
colProps: {
span: 12,
},
componentProps: {
options: stateTypeList,
},
rules: [{ required: true, type: 'number' }],
},
{
field: 'description',
component: 'InputTextArea',
componentProps: {
rows: 4,
placeholder: '请输入问题描述',
},
label: '问题描述',
colProps: {
span: 24,
},
rules: [{ required: true }],
},
{
label: '描述附件',
field: 'fileList',
component: 'Upload',
componentProps: {
disabled: isDetail,
customRequest: async (data) => {
console.log('data: ', data);
const formData = new FormData();
formData.append('file', data.file);
const res = await commonUpload(formData);
console.log('res: ', res);
data?.onSuccess && data?.onSuccess(res, data.file as any);
},
beforeUpload: (file) => {
console.log('file: ', file);
// 限制允许上传的文件类型
const allowedTypes = ['xlsx', 'xls', 'doc', 'docx', 'pdf'];
// 检查文件类型
const fileType = getFileExtension(file.name) || 'unknown';
if (!allowedTypes.includes(fileType)) {
// 文件类型不在允许列表中,拒绝上传
// 可以在这里展示错误信息
message.warning('文件类型不正确');
return false;
}
// 其他验证逻辑...
// 允许上传
return true;
},
},
componentSlots: {
default: () => (
<Button>
<UploadOutlined />
</Button>
),
},
colProps: {
span: 24,
},
rules: [{ required: false }],
},
] as any;
};

38
src/views/question/issue/index.vue

@ -59,7 +59,7 @@
import { Modal, message, Alert } from 'ant-design-vue';
import { useFormModal } from '@/hooks/useModal/index';
import { issueSchemas } from './formSchemas';
import { getEditFormSchema } from './formSchemas';
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue';
defineOptions({
@ -127,6 +127,24 @@
onFinish: async (values) => {
console.log('新增/编辑问题工单', values);
values.id = record.id;
if (values.fileList && Array.isArray(values.fileList) && values.fileList.length) {
values.fileList = values.fileList.map((e) => {
if (e.response) {
return {
name: e.name,
url: e.response.url,
id: e.response.id,
};
}
return {
...e,
};
});
}
values.fileIds = values.fileList.map((e) => e.id).join(',');
await (record.id ? updateIssue : createIssue)(values);
message.success(`${record.id ? '编辑' : '新增'}成功`);
dynamicTableInstance?.reload();
@ -135,7 +153,7 @@
},
formProps: {
labelWidth: 100,
schemas: issueSchemas as any,
schemas: getEditFormSchema(record, isReadOnly) as any,
autoSubmitOnEnter: true,
disabled: isReadOnly,
},
@ -223,6 +241,19 @@
{
id: '1',
name: '1',
state: 0,
fileList: [
{
name: '文件条款信息.xlsx',
url: 'http://192.168.2.116:8089/oss/resource/2025-04-07/67f390f06f14d107a557e10f.xlsx',
id: 'vc-upload-1744009547396-6',
},
{
name: '文件条款信息 (1).xlsx',
url: 'http://192.168.2.116:8089/oss/resource/2025-04-07/67f390fc6f14d107a557e110.xlsx',
id: 'vc-upload-1744009547396-8',
},
],
},
],
...params,
@ -238,7 +269,7 @@
rowSelection.value.selectedRowKeys = [];
return {
list: res.data.list,
list: res.data.records,
pagination: {
total: Number(res.data.total),
...params,
@ -246,7 +277,6 @@
};
};
const handleResizeColumn = (w, col) => {
// console.log('w', w, col);
col.width = w;
};
</script>

363
src/views/system/admin/columns.tsx

@ -0,0 +1,363 @@
import { debounce } from 'lodash-es';
import type { TableColumn } from '@/components/core/dynamic-table';
import { formatToDateTime } from '@/utils/dateUtil';
import { updateUser } from '@/api/user';
import { h } from 'vue';
import { Switch } from 'ant-design-vue';
import { sexTypeList } from './data';
export type TableListItem = API.UserInfoType;
export type TableColumnItem = TableColumn<TableListItem>;
// 数据项类型
// export type ListItemType = typeof tableData[number];
// 使用TableColumn<ListItemType> 将会限制dataIndex的类型,但换来的是dataIndex有类型提示
export const baseColumns: TableColumnItem[] = [
{
title: '账号',
align: 'center',
dataIndex: 'account',
// sorter: true,
width: 150,
resizable: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '姓名',
align: 'center',
dataIndex: 'username',
width: 150,
resizable: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '手机号码',
align: 'center',
dataIndex: 'mobile',
width: 150,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
width: 100,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
component: 'Select',
label: '性别',
componentProps: {
mode: 'single',
request: async () => {
// const data = await getSexList();
return sexTypeList;
},
},
},
customRender: ({ record }) => {
const text = sexTypeList.find((e) => e.value === record.sex)?.label;
return <div>{text}</div>;
},
},
{
title: '帐号状态',
align: 'center',
width: 100,
dataIndex: 'state',
hideInSearch: true,
formItemProps: {
component: 'Select',
componentProps: {
options: [
{
label: '启用',
value: 1,
},
{
label: '禁用',
value: 0,
},
],
},
},
customRender: ({ record }) => {
const onChange = (checked: boolean) => {
console.log('checked: ', checked);
record.pendingStatus = true;
const newState = checked ? 1 : 0;
record.state = newState;
updateUser(record)
.then(() => {
record.state = newState;
})
.catch(() => {})
.finally(() => {
record.pendingStatus = false;
});
};
// return (
// <a-switch
// v-model:checked={record.state}
// v-model:loading={record.pendingStatus}
// onChange={onChange}
// ></a-switch>
// );
// 渲染函数写法
return h(Switch, {
checked: record.state === 1 ? true : false,
loading: record.pendingStatus,
onChange,
});
},
},
{
title: '是否管理员',
align: 'center',
width: 100,
dataIndex: 'isAdmin',
hideInSearch: true,
formItemProps: {
component: 'Select',
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
},
customRender: ({ record }) => {
const onChange = (checked: boolean) => {
console.log('checked: ', checked);
record.pendingStatus = true;
const newState = checked ? 1 : 0;
record.isAdmin = newState;
updateUser(record)
.then(() => {
record.isAdmin = newState;
})
.catch(() => {})
.finally(() => {
record.pendingStatus = false;
});
};
// return (
// <a-switch
// v-model:checked={record.state}
// v-model:loading={record.pendingStatus}
// onChange={onChange}
// ></a-switch>
// );
// 渲染函数写法
return h(Switch, {
checked: record.isAdmin === 1 ? true : false,
loading: record.pendingStatus,
onChange,
});
},
},
{
title: '创建时间',
align: 'center',
width: 200,
dataIndex: 'createTime',
formItemProps: {
defaultValue: '',
required: false,
component: 'RangePicker',
},
},
];
export const getBaseColumns = (tableInstance) => {
return [
{
title: '账号',
align: 'center',
dataIndex: 'account',
// sorter: true,
width: 150,
resizable: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '姓名',
align: 'center',
dataIndex: 'username',
width: 150,
resizable: true,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '手机号码',
align: 'center',
dataIndex: 'mobile',
width: 150,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
width: 100,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
component: 'Select',
label: '性别',
componentProps: {
mode: 'single',
request: async () => {
// const data = await getSexList();
return sexTypeList;
},
},
},
customRender: ({ record }) => {
const text = sexTypeList.find((e) => e.value === record.sex)?.label;
return <div>{text}</div>;
},
},
{
title: '帐号状态',
align: 'center',
width: 100,
dataIndex: 'state',
hideInSearch: true,
formItemProps: {
component: 'Select',
componentProps: {
options: [
{
label: '启用',
value: 1,
},
{
label: '禁用',
value: 0,
},
],
},
},
customRender: ({ record }) => {
const onChange = (checked: boolean) => {
console.log('checked: ', checked);
record.pendingStatus = true;
const newState = checked ? 1 : 0;
record.state = newState;
updateUser(record)
.then(() => {
record.state = newState;
})
.catch(() => {})
.finally(() => {
record.pendingStatus = false;
tableInstance && tableInstance?.reload();
});
};
// return (
// <a-switch
// v-model:checked={record.state}
// v-model:loading={record.pendingStatus}
// onChange={onChange}
// ></a-switch>
// );
// 渲染函数写法
return h(Switch, {
checked: record.state === 1 ? true : false,
loading: record.pendingStatus,
onChange,
});
},
},
{
title: '是否管理员',
align: 'center',
width: 100,
dataIndex: 'isAdmin',
hideInSearch: true,
formItemProps: {
component: 'Select',
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
},
customRender: ({ record }) => {
const onChange = (checked: boolean) => {
console.log('checked: ', checked);
record.pendingStatus = true;
const newState = checked ? 1 : 0;
record.isAdmin = newState;
updateUser(record)
.then(() => {
record.isAdmin = newState;
})
.catch(() => {})
.finally(() => {
record.pendingStatus = false;
tableInstance && tableInstance?.reload();
});
};
// return (
// <a-switch
// v-model:checked={record.state}
// v-model:loading={record.pendingStatus}
// onChange={onChange}
// ></a-switch>
// );
// 渲染函数写法
return h(Switch, {
checked: record.isAdmin === 1 ? true : false,
loading: record.pendingStatus,
onChange,
});
},
},
{
title: '创建时间',
align: 'center',
width: 200,
dataIndex: 'createTime',
formItemProps: {
defaultValue: '',
required: false,
component: 'RangePicker',
},
},
];
};

14
src/views/system/admin/data.ts

@ -0,0 +1,14 @@
export const sexTypeList:any = [
{
label: '男',
value: 1,
},
{
label: '女',
value: 2,
},
{
label: '未知',
value: 3,
},
];

123
src/views/system/admin/formSchemas.ts

@ -0,0 +1,123 @@
/*
* @Author: AaronWu 2463371514@qq.com
* @Date: 2025-04-01 09:08:06
* @LastEditors: AaronWu 2463371514@qq.com
* @LastEditTime: 2025-04-01 16:48:50
* @FilePath: /IssueSupportManage/src/views/system/user/formSchemas.ts
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import type { FormSchema } from '@/components/core/schema-form/';
import { sexTypeList } from './data';
export const userSchemas: FormSchema<API.CreateUserParams>[] = [
{
field: 'account',
component: 'Input',
label: '账号',
colProps: {
span: 12,
},
rules: [{ required: true }],
},
{
field: 'username',
component: 'Input',
label: '用户名',
colProps: {
span: 12,
},
rules: [{ required: true }],
},
{
field: 'password',
component: 'Input',
label: '密码',
colProps: {
span: 12,
},
rules: [{ required: true }],
},
{
field: 'mobile',
component: 'Input',
label: '手机号码',
colProps: {
span: 12,
},
rules: [
{
required: true,
message: '请输入正确格式的电话号码',
pattern: /^1(3[0-9]|4[01456879]|5[0-3,5-9]|6[2567]|7[0-8]|8[0-9]|9[0-3,5-9])\d{8}$/,
},
],
},
{
field: 'sex',
component: 'Select',
label: '性别',
colProps: {
span: 12,
},
componentProps: {
mode: 'single',
request: async () => {
// const data = await getSexList();
return sexTypeList;
},
},
},
{
field: 'state',
component: 'RadioGroup',
label: '帐号状态',
defaultValue: 1,
colProps: {
span: 12,
},
componentProps: {
options: [
{
label: '启用',
value: 1,
},
{
label: '禁用',
value: 0,
},
],
},
// required:true,
rules: [{ required: true, type: 'number' }],
},
{
field: 'isAdmin',
component: 'RadioGroup',
label: '是否管理员',
defaultValue: 0,
colProps: {
span: 12,
},
componentProps: {
options: [
{
label: '是',
value: 1,
},
{
label: '否',
value: 0,
},
],
},
// required:true,
rules: [{ required: true, type: 'number' }],
},
{
field: 'remark',
component: 'InputTextArea',
label: '备注',
},
];

241
src/views/system/admin/index.vue

@ -0,0 +1,241 @@
<!--
功能功能描述
作者Aaron.Wu
时间2023年05月25日 17:00:26
版本v1.0
修改记录
修改内容
修改人员
修改时间
-->
<template>
<DynamicTable
size="small"
showIndex
headerTitle="管理员列表"
titleTooltip=""
:data-request="loadData"
:columns="columns"
row-key="id"
@resize-column="handleResizeColumn"
:row-selection="rowSelection"
:scroll="{ x: '100vw' }"
>
<template v-if="isCheckRows" #title>
<Alert class="w-full" type="info" show-icon>
<template #message>
已选 {{ isCheckRows }}
<a-button type="link" @click="rowSelection.selectedRowKeys = []">取消选择</a-button>
</template>
</Alert>
</template>
<template #toolbar>
<!-- <a-button type="primary" @click="openUserModal({})">新增</a-button> -->
<a-button
type="danger"
:disabled="!isCheckRows"
@click="delRowConfirm(rowSelection.selectedRowKeys)"
>
<DeleteOutlined /> 删除
</a-button>
</template>
</DynamicTable>
</template>
<script lang="tsx" setup>
import { type TableListItem, getBaseColumns } from './columns';
import { useTable, type OnChangeCallbackParams } from '@/components/core/dynamic-table';
import {
fetchUserList,
fetchUserPageList,
createUser,
updateUser,
findOneById,
deleteUserById,
deleteBatchUserById,
} from '@/api/user';
import { computed, reactive, ref, toRaw } from 'vue';
import { Modal, message, Alert } from 'ant-design-vue';
import { useFormModal } from '@/hooks/useModal/index';
import { userSchemas } from './formSchemas';
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue';
defineOptions({
name: 'tanant-user',
});
const [showModal] = useFormModal();
const [DynamicTable, dynamicTableInstance] = useTable();
const columns = [
...getBaseColumns(dynamicTableInstance),
{
title: '操作',
width: 200,
dataIndex: 'ACTION',
hideInSearch: true,
align: 'center',
fixed: 'right',
actions: ({ record }) => [
{
icon: 'searchoutlined',
color: '#3b82f6',
label: '查看',
onClick: () => handleShow(record),
},
// {
// icon: 'edit',
// color: '#3b82f6',
// size: '15',
// label: '',
// onClick: () => handleEdit(record),
// },
{
icon: 'delete',
color: '#ec6f6f',
label: '删除',
popConfirm: {
title: '确定要删除吗?',
onConfirm: () => handleDelete(record.id),
},
},
],
},
];
/**
* @description 打开用户弹窗
*/
const openUserModal = async (record: Partial<TableListItem> = {}, isReadOnly = false) => {
const [formRef] = await showModal<any>({
modalProps: {
title: `${isReadOnly ? '查看' : record.id ? '编辑' : '新增'}用户`,
width: 700,
onFinish: async (values) => {
console.log('新增/编辑用户', values);
values.id = record.id;
await (record.id ? updateUser : createUser)(values);
message.success(`${record.id ? '编辑' : '新增'}成功`);
dynamicTableInstance?.reload();
},
footer: isReadOnly ? null : undefined,
},
formProps: {
labelWidth: 100,
schemas: userSchemas as any,
autoSubmitOnEnter: true,
disabled: isReadOnly,
},
});
formRef?.setFieldsValue(record);
// if (record?.id) {
// const { roles } = await getUserInfo({ userId: record.id });
// formRef?.setFieldsValue({ roles });
// }
};
const handleEdit = (record: TableListItem) => {
openUserModal(record);
};
const handleShow = (record: TableListItem) => {
openUserModal(record, true);
};
const handleDelete = (id: string) => {
delRowConfirm([id]);
};
/**
* @description 表格删除行
*/
const delRowConfirm = async (ids: string[]) => {
Modal.confirm({
title: '确定要删除所选的用户吗?',
icon: <ExclamationCircleOutlined />,
centered: true,
onOk: async () => {
await deleteBatchUserById(ids).finally(dynamicTableInstance?.reload);
},
});
};
const rowSelection = ref<any>({
selectedRowKeys: [] as string[],
onChange: (selectedRowKeys: string[], selectedRows: TableListItem[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
rowSelection.value.selectedRowKeys = selectedRowKeys;
},
});
//
const isCheckRows = computed(() => rowSelection.value.selectedRowKeys.length);
const loadData = async (
params,
onChangeParams?: OnChangeCallbackParams,
): Promise<API.TableListResult> => {
console.log('params', params);
console.log('onChangeParams', onChangeParams);
//
// dynamicTableInstance?.getQueryFormRef()?.updateSchema?.([
// {
// field: 'price',
// componentProps: {
// options: [
// {
// label: '0-199',
// value: '0-199',
// },
// {
// label: '200-999',
// value: '200-999',
// },
// ],
// },
// },
// ]);
// return new Promise((resolve) => {
// setTimeout(() => {
// resolve({
// list: [
// {
// id: '1',
// name: '1',
// },
// ],
// ...params,
// });
// }, 500);
// });
const res = await fetchUserPageList({
...params,
isAdmin: 1,
current: params.page,
size: params.limit,
});
console.log('res: ', res);
rowSelection.value.selectedRowKeys = [];
return {
list: res.data.records,
pagination: {
total: Number(res.data.total),
...params,
},
};
};
const handleResizeColumn = (w, col) => {
// console.log('w', w, col);
col.width = w;
};
</script>
<style lang="less" scoped>
.action-divider {
margin: 0 5px;
}
</style>
Loading…
Cancel
Save