From 2521b0c9519d3d02765bbe97c0330c5ced73f09c Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Fri, 19 Jan 2024 18:29:31 +0800 Subject: [PATCH] Add ToolBase management page --- src/assets/css/pages/system/tools/base.scss | 8 + src/constants/urls.constants.ts | 1 + src/global.d.ts | 20 + src/pages/System/Tools/Base.tsx | 492 +++++++++++++++++++- src/pages/System/Tools/Category.tsx | 28 +- src/services/system.tsx | 19 +- 6 files changed, 554 insertions(+), 14 deletions(-) create mode 100644 src/assets/css/pages/system/tools/base.scss diff --git a/src/assets/css/pages/system/tools/base.scss b/src/assets/css/pages/system/tools/base.scss new file mode 100644 index 0000000..627cc25 --- /dev/null +++ b/src/assets/css/pages/system/tools/base.scss @@ -0,0 +1,8 @@ +[data-component=system-tools-base] { + .root-content { + padding: 20px; + gap: 10px; + height: 100%; + width: 100%; + } +} \ No newline at end of file diff --git a/src/constants/urls.constants.ts b/src/constants/urls.constants.ts index 84eb783..4b04f01 100644 --- a/src/constants/urls.constants.ts +++ b/src/constants/urls.constants.ts @@ -28,6 +28,7 @@ export const URL_SYS_STATISTICS_ACTIVE = `${URL_SYS_STATISTICS}/active` export const URL_SYS_TOOL = '/system/tool' export const URL_SYS_TOOL_CATEGORY = `${URL_SYS_TOOL}/category` export const URL_SYS_TOOL_BASE = `${URL_SYS_TOOL}/base` +export const URL_SYS_TOOL_BASE_LIST = `${URL_SYS_TOOL_BASE}/list` export const URL_SYS_TOOL_TEMPLATE = `${URL_SYS_TOOL}/template` export const URL_API_V1 = '/api/v1' diff --git a/src/global.d.ts b/src/global.d.ts index 408aaea..d771a46 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -491,10 +491,19 @@ interface ToolBaseVo { name: string source: ToolDataVo dist: ToolDataVo + enable: boolean createTime: string updateTime: string } +interface ToolBaseAddEditParam { + id?: string + name?: string + source?: string + dist?: string + enable?: boolean +} + interface ToolTemplateVo { id: string name: string @@ -502,10 +511,21 @@ interface ToolTemplateVo { baseId: string source: ToolDataVo dist: ToolDataVo + enable: boolean createTime: string updateTime: string } +interface ToolTemplateAddEditParam { + id: string + name: string + ver: string + baseId: string + source?: string + dist?: string + enable: boolean +} + interface ToolVo { id: string name: string diff --git a/src/pages/System/Tools/Base.tsx b/src/pages/System/Tools/Base.tsx index 4870323..78278a6 100644 --- a/src/pages/System/Tools/Base.tsx +++ b/src/pages/System/Tools/Base.tsx @@ -1,5 +1,495 @@ +import '@/assets/css/pages/system/tools/base.scss' +import FitFullscreen from '@/components/common/FitFullscreen.tsx' +import FlexBox from '@/components/common/FlexBox.tsx' +import HideScrollbar from '@/components/common/HideScrollbar.tsx' +import Card from '@/components/common/Card.tsx' +import CodeEditor from '@/components/Playground/CodeEditor' +import { useState } from 'react' +import { IFile, IFiles } from '@/components/Playground/shared.ts' +import { + r_sys_tool_base_add, + r_sys_tool_base_delete, + r_sys_tool_base_get_one, + r_sys_tool_base_getList, + r_sys_tool_base_update +} from '@/services/system.tsx' +import { + COLOR_PRODUCTION, + DATABASE_DELETE_SUCCESS, + DATABASE_DUPLICATE_KEY, + DATABASE_INSERT_SUCCESS, + DATABASE_SELECT_SUCCESS, + DATABASE_UPDATE_SUCCESS +} from '@/constants/common.constants.ts' +import { utcToLocalTime } from '@/util/datetime.tsx' +import Permission from '@/components/common/Permission.tsx' +import { base64ToFiles, filesToBase64, getFilesSize } from '@/components/Playground/files.ts' + const Base = () => { - return <>3 + const [modal, contextHolder] = AntdModal.useModal() + const [form] = AntdForm.useForm() + const formValues = AntdForm.useWatch([], form) + const [newFormValues, setNewFormValues] = useState() + const [isLoading, setIsLoading] = useState(false) + const [isDrawerOpen, setIsDrawerOpen] = useState(false) + const [isDrawerEdit, setIsDrawerEdit] = useState(false) + const [submittable, setSubmittable] = useState(false) + const [isSubmitting, setIsSubmitting] = useState(false) + const [editingFiles, setEditingFiles] = useState() + const [editingFileName, setEditingFileName] = useState('') + const [hasEdited, setHasEdited] = useState(false) + const [baseData, setBaseData] = useState([]) + const [baseDetailData, setBaseDetailData] = useState>({}) + const [baseDetailLoading, setBaseDetailLoading] = useState>({}) + + const handleOnAddBtnClick = () => { + setIsDrawerEdit(false) + setIsDrawerOpen(true) + form.setFieldValue('id', undefined) + form.setFieldValue('name', newFormValues?.name) + form.setFieldValue('enable', newFormValues?.enable ?? true) + } + + const baseColumns: _ColumnsType = [ + { title: '名称', dataIndex: 'name' }, + { + title: '创建时间', + dataIndex: 'createTime', + width: '20%', + align: 'center', + render: (value: string) => utcToLocalTime(value) + }, + { + title: '修改时间', + dataIndex: 'updateTime', + width: '20%', + align: 'center', + render: (value: string) => utcToLocalTime(value) + }, + { + title: '状态', + dataIndex: 'enable', + width: '5%', + align: 'center', + render: (value) => + value ? 启用 : 禁用 + }, + { + title: ( + <> + 操作 ( + + 新增 + + ) + + ), + dataIndex: 'enable', + width: '15em', + align: 'center', + render: (_, record) => ( + <> + + + + 编辑 + + + + + 删除 + + + + + ) + } + ] + + const handleOnEditBtnClick = (value: ToolBaseVo) => { + return () => { + setIsDrawerEdit(true) + setIsDrawerOpen(true) + form.setFieldValue('id', value.id) + form.setFieldValue('name', value.name) + form.setFieldValue('enable', value.enable) + void form.validateFields() + } + } + + const handleOnDeleteBtnClick = (value: ToolBaseVo) => { + return () => { + modal + .confirm({ + title: '确定删除', + content: `确定删除基板 ${value.name} 吗?` + }) + .then( + (confirmed) => { + if (confirmed) { + setIsLoading(true) + + void r_sys_tool_base_delete(value.id) + .then((res) => { + const response = res.data + if (response.code === DATABASE_DELETE_SUCCESS) { + void message.success('删除成功') + setTimeout(() => { + getBase() + }) + } else { + void message.error('删除失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + }, + () => {} + ) + } + } + + const handleOnSubmit = () => { + if (isSubmitting) { + return + } + setIsSubmitting(true) + + if (isDrawerEdit) { + void r_sys_tool_base_update(formValues) + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_UPDATE_SUCCESS: + setIsDrawerOpen(false) + void message.success('更新成功') + getBase() + break + case DATABASE_DUPLICATE_KEY: + void message.error('已存在相同名称的基板') + break + default: + void message.error('更新失败,请稍后重试') + } + }) + .finally(() => { + setIsSubmitting(false) + }) + } else { + void r_sys_tool_base_add(formValues) + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_INSERT_SUCCESS: + setIsDrawerOpen(false) + void message.success('添加成功') + setNewFormValues(undefined) + getBase() + break + case DATABASE_DUPLICATE_KEY: + void message.error('已存在相同名称的基板') + break + default: + void message.error('添加失败,请稍后重试') + } + }) + .finally(() => { + setIsSubmitting(false) + }) + } + } + + const handleOnDrawerClose = () => { + setIsDrawerOpen(false) + } + + const getBase = () => { + if (isLoading) { + return + } + setIsLoading(true) + + void r_sys_tool_base_getList() + .then((res) => { + const response = res.data + if (response.code === DATABASE_SELECT_SUCCESS) { + setBaseData(response.data!) + } else { + void message.error('获取失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + + const handleOnExpand = (expanded: boolean, record: ToolBaseVo) => { + if (!expanded) { + setEditingFiles(undefined) + return + } + getBaseDetail(record) + } + + const getBaseDetail = (record: ToolBaseVo) => { + if (baseDetailLoading[record.id]) { + return + } + setBaseDetailLoading({ ...baseDetailLoading, [record.id]: true }) + + void r_sys_tool_base_get_one(record.id) + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_SELECT_SUCCESS: + setBaseDetailData({ ...baseDetailData, [record.id]: response.data! }) + break + default: + void message.error(`获取基板 ${record.name} 文件内容失败,请稍后重试`) + } + }) + .finally(() => { + setBaseDetailLoading({ ...baseDetailLoading, [record.id]: false }) + }) + } + + const expandedRowRender = (record: ToolBaseVo) => { + const baseDetailVo = baseDetailData[record.id] + let sourceFiles: IFiles | undefined = undefined + let sourceFileList: IFile[] = [] + if (baseDetailVo) { + sourceFiles = base64ToFiles(baseDetailVo.source.data) + sourceFileList = Object.values(sourceFiles) + } + + const detailColumns: _ColumnsType = [ + { title: '文件名', dataIndex: 'name' }, + { + title: ( + <> + 文件总大小 +
+ {sourceFiles ? getFilesSize(sourceFiles) : 'Unknown'} + + ), + width: '10em', + align: 'center' + }, + { + title: ( + <> + 修改时间 +
+ {baseDetailVo ? utcToLocalTime(baseDetailVo.source.updateTime) : 'Unknown'} + + ), + width: '15em', + align: 'center' + }, + { + title: ( + <> + 操作 (新增) + + ), + dataIndex: 'enable', + width: '10em', + align: 'center', + render: (_, record) => ( + <> + + + + 编辑 + + + + + 删除 + + + + + ) + } + ] + + const handleOnEditFile = () => { + return () => {} + } + + const handleOnDeleteFile = (fileName: string) => { + return () => { + if (hasEdited) { + void message.warning('删除文件前请先保存更改') + return + } + + modal + .confirm({ + title: '确定删除', + content: `确定删除文件 ${fileName} 吗?` + }) + .then( + (confirmed) => { + if (confirmed) { + setBaseDetailLoading({ ...baseDetailLoading, [record.id]: true }) + + delete sourceFiles![fileName] + + void r_sys_tool_base_update({ + id: record.id, + source: filesToBase64(sourceFiles!) + }) + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_UPDATE_SUCCESS: + void message.success('删除成功') + setTimeout(() => { + getBaseDetail(record) + }) + break + default: + void message.error('删除失败,请稍后重试') + } + }) + .finally(() => { + setBaseDetailLoading({ + ...baseDetailLoading, + [record.id]: false + }) + }) + } + }, + () => {} + ) + } + } + + return ( + + + + ) + } + + useEffect(() => { + form.validateFields({ validateOnly: true }).then( + () => { + setSubmittable(true) + }, + () => { + setSubmittable(false) + } + ) + + if (!isDrawerEdit && formValues) { + setNewFormValues({ + name: formValues.name, + enable: formValues.enable + }) + } + }, [formValues]) + + useEffect(() => { + getBase() + }, []) + + const drawerToolbar = ( + + + 取消 + + + 提交 + + + ) + + const addAndEditForm = ( + + + + + + + + + + ) + + return ( + <> + + + + + record.id} + loading={isLoading} + pagination={false} + expandable={{ + expandedRowRender, + onExpand: handleOnExpand + }} + /> + + + {editingFiles && ( + + {}} + showFileSelector={false} + /> + + )} + + + {addAndEditForm} + + + {contextHolder} + + ) } export default Base diff --git a/src/pages/System/Tools/Category.tsx b/src/pages/System/Tools/Category.tsx index 6ac6519..f7dd19c 100644 --- a/src/pages/System/Tools/Category.tsx +++ b/src/pages/System/Tools/Category.tsx @@ -38,17 +38,6 @@ const Category = () => { form.setFieldValue('enable', newFormValues?.enable ?? true) } - const handleOnEditBtnClick = (value: ToolCategoryVo) => { - return () => { - setIsDrawerEdit(true) - setIsDrawerOpen(true) - form.setFieldValue('id', value.id) - form.setFieldValue('name', value.name) - form.setFieldValue('enable', value.enable) - void form.validateFields() - } - } - const categoryColumns: _ColumnsType = [ { title: 'ID', @@ -84,7 +73,11 @@ const Category = () => { { title: ( <> - 操作 (新增) + 操作 ( + + 新增 + + ) ), dataIndex: 'enable', @@ -115,6 +108,17 @@ const Category = () => { } ] + const handleOnEditBtnClick = (value: ToolCategoryVo) => { + return () => { + setIsDrawerEdit(true) + setIsDrawerOpen(true) + form.setFieldValue('id', value.id) + form.setFieldValue('name', value.name) + form.setFieldValue('enable', value.enable) + void form.validateFields() + } + } + const handleOnDeleteBtnClick = (value: ToolCategoryVo) => { return () => { modal diff --git a/src/services/system.tsx b/src/services/system.tsx index 690bb21..9271605 100644 --- a/src/services/system.tsx +++ b/src/services/system.tsx @@ -17,7 +17,9 @@ import { URL_SYS_STATISTICS_ACTIVE, URL_SYS_SETTINGS_BASE, URL_SYS_SETTINGS_SENSITIVE, - URL_SYS_TOOL_CATEGORY + URL_SYS_TOOL_CATEGORY, + URL_SYS_TOOL_BASE, + URL_SYS_TOOL_BASE_LIST } from '@/constants/urls.constants' import request from '@/services/index' @@ -125,3 +127,18 @@ export const r_sys_tool_category_update = (param: ToolCategoryAddEditParam) => export const r_sys_tool_category_delete = (id: string) => request.delete(`${URL_SYS_TOOL_CATEGORY}/${id}`) + +export const r_sys_tool_base_getList = () => request.get(URL_SYS_TOOL_BASE_LIST) + +export const r_sys_tool_base_get = () => request.get(URL_SYS_TOOL_BASE) + +export const r_sys_tool_base_get_one = (id: string) => + request.get(`${URL_SYS_TOOL_BASE}/${id}`) + +export const r_sys_tool_base_add = (param: ToolBaseAddEditParam) => + request.post(URL_SYS_TOOL_BASE, param) + +export const r_sys_tool_base_update = (param: ToolBaseAddEditParam) => + request.put(URL_SYS_TOOL_BASE, param) + +export const r_sys_tool_base_delete = (id: string) => request.delete(`${URL_SYS_TOOL_BASE}/${id}`)