Add rename file to ToolBase management page

This commit is contained in:
2024-01-22 16:54:42 +08:00
parent 8a954abedf
commit 691f13381d
3 changed files with 222 additions and 40 deletions

View File

@@ -2,7 +2,7 @@
[data-component=system-tools-base] { [data-component=system-tools-base] {
.root-content { .root-content {
padding: 20px; padding: 30px;
gap: 10px; gap: 10px;
height: 100%; height: 100%;
width: 100%; width: 100%;
@@ -11,5 +11,31 @@
content: '*'; content: '*';
color: constants.$font-secondary-color; color: constants.$font-secondary-color;
} }
>*:first-child {
height: fit-content;
}
> *:nth-child(2) {
position: sticky;
top: 20px;
height: calc(100vh - 40px);
}
.close-editor-btn {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 10px;
right: 10px;
background-color: constants.$font-secondary-color;
width: 32px;
height: 32px;
border-radius: 50%;
color: white;
box-shadow: 2px 2px 10px 0 rgba(0,0,0,0.2);
cursor: pointer;
}
} }
} }

View File

@@ -1,18 +1,5 @@
import Icon from '@ant-design/icons'
import '@/assets/css/pages/system/tools/base.scss' 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, ITsconfig } 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 { import {
COLOR_PRODUCTION, COLOR_PRODUCTION,
DATABASE_DELETE_SUCCESS, DATABASE_DELETE_SUCCESS,
@@ -20,22 +7,40 @@ import {
DATABASE_INSERT_SUCCESS, DATABASE_INSERT_SUCCESS,
DATABASE_SELECT_SUCCESS, DATABASE_SELECT_SUCCESS,
DATABASE_UPDATE_SUCCESS DATABASE_UPDATE_SUCCESS
} from '@/constants/common.constants.ts' } from '@/constants/common.constants'
import { utcToLocalTime } from '@/util/datetime.tsx' import { utcToLocalTime } from '@/util/datetime.tsx'
import Permission from '@/components/common/Permission.tsx' 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'
import { IFile, IFiles, ITsconfig } from '@/components/Playground/shared'
import { import {
base64ToFiles, base64ToFiles,
fileNameToLanguage, fileNameToLanguage,
filesToBase64, filesToBase64,
getFilesSize, getFilesSize,
TS_CONFIG_FILE_NAME TS_CONFIG_FILE_NAME
} from '@/components/Playground/files.ts' } from '@/components/Playground/files'
import FitFullscreen from '@/components/common/FitFullscreen'
import FlexBox from '@/components/common/FlexBox'
import HideScrollbar from '@/components/common/HideScrollbar'
import Card from '@/components/common/Card'
import CodeEditor from '@/components/Playground/CodeEditor'
import Permission from '@/components/common/Permission'
const Base = () => { const Base = () => {
const blocker = useBlocker(
({ currentLocation, nextLocation }) =>
currentLocation.pathname !== nextLocation.pathname && Object.keys(hasEdited).length > 0
)
const [modal, contextHolder] = AntdModal.useModal() const [modal, contextHolder] = AntdModal.useModal()
const [form] = AntdForm.useForm<ToolBaseAddEditParam>() const [form] = AntdForm.useForm<ToolBaseAddEditParam>()
const formValues = AntdForm.useWatch([], form) const formValues = AntdForm.useWatch([], form)
const [addFileForm] = AntdForm.useForm<{ fileName: string }>() const [addFileForm] = AntdForm.useForm<{ fileName: string }>()
const [renameFileForm] = AntdForm.useForm<{ fileName: string }>()
const [newFormValues, setNewFormValues] = useState<ToolBaseAddEditParam>() const [newFormValues, setNewFormValues] = useState<ToolBaseAddEditParam>()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false) const [isDrawerOpen, setIsDrawerOpen] = useState(false)
@@ -51,6 +56,19 @@ const Base = () => {
const [baseDetailLoading, setBaseDetailLoading] = useState<Record<string, boolean>>({}) const [baseDetailLoading, setBaseDetailLoading] = useState<Record<string, boolean>>({})
const [tsconfig, setTsconfig] = useState<ITsconfig>() const [tsconfig, setTsconfig] = useState<ITsconfig>()
useBeforeUnload(
useCallback(
(event) => {
if (Object.keys(hasEdited).length) {
event.preventDefault()
event.returnValue = ''
}
},
[hasEdited]
),
{ capture: true }
)
const handleOnAddBtnClick = () => { const handleOnAddBtnClick = () => {
if (Object.keys(hasEdited).length) { if (Object.keys(hasEdited).length) {
void message.warning('新增前请保存修改') void message.warning('新增前请保存修改')
@@ -275,6 +293,10 @@ const Base = () => {
} }
} }
const handleOnCloseBtnClick = () => {
setEditingFileName('')
}
const handleOnDrawerClose = () => { const handleOnDrawerClose = () => {
setIsDrawerOpen(false) setIsDrawerOpen(false)
} }
@@ -301,7 +323,6 @@ const Base = () => {
const handleOnExpand = (expanded: boolean, record: ToolBaseVo) => { const handleOnExpand = (expanded: boolean, record: ToolBaseVo) => {
if (!expanded) { if (!expanded) {
setEditingFileName('')
return return
} }
getBaseDetail(record) getBaseDetail(record)
@@ -361,7 +382,11 @@ const Base = () => {
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator() { validator() {
const newFileName = getFieldValue('fileName') as string const newFileName = getFieldValue('fileName') as string
if (Object.keys(sourceFiles!).includes(newFileName)) { if (
Object.keys(sourceFiles!)
.map((item) => item.toLowerCase())
.includes(newFileName.toLowerCase())
) {
return Promise.reject(new Error('文件已存在')) return Promise.reject(new Error('文件已存在'))
} }
return Promise.resolve() return Promise.resolve()
@@ -451,7 +476,7 @@ const Base = () => {
</> </>
), ),
dataIndex: 'enable', dataIndex: 'enable',
width: '10em', width: '12em',
align: 'center', align: 'center',
render: (_, record) => ( render: (_, record) => (
<> <>
@@ -464,6 +489,14 @@ const Base = () => {
</a> </a>
</Permission> </Permission>
<Permission operationCode={'system:tool:modify:category'}>
<a
onClick={handleOnRenameFile(record.name)}
style={{ color: COLOR_PRODUCTION }}
>
</a>
</Permission>
<Permission operationCode={'system:tool:delete:category'}> <Permission operationCode={'system:tool:delete:category'}>
<a <a
onClick={handleOnDeleteFile(record.name)} onClick={handleOnDeleteFile(record.name)}
@@ -491,9 +524,114 @@ const Base = () => {
} }
} }
const handleOnRenameFile = (fileName: string) => {
return () => {
if (Object.keys(hasEdited).length) {
void message.warning('重命名文件前请先保存更改')
return
}
renameFileForm.setFieldValue('fileName', fileName)
void modal.confirm({
title: '重命名文件',
content: (
<AntdForm form={renameFileForm}>
<AntdForm.Item
name={'fileName'}
label={'新文件名'}
style={{ marginTop: 10 }}
rules={[
{ required: true },
{
pattern: /\.(jsx|tsx|js|ts|css|json)$/,
message:
'仅支持 *.jsx, *.tsx, *.js, *.ts, *.css, *.json 文件'
},
({ getFieldValue }) => ({
validator() {
const newFileName = getFieldValue('fileName') as string
if (
Object.keys(sourceFiles!)
.map((item) => item.toLowerCase())
.includes(newFileName?.toLowerCase()) &&
newFileName.toLowerCase() !== fileName.toLowerCase()
) {
return Promise.reject(new Error('文件已存在'))
}
return Promise.resolve()
}
})
]}
>
<AntdInput />
</AntdForm.Item>
</AntdForm>
),
onOk: () =>
renameFileForm.validateFields().then(
() => {
return new Promise((resolve) => {
const newFileName = renameFileForm.getFieldValue(
'fileName'
) as string
const temp = sourceFiles![fileName].value
delete sourceFiles![fileName]
sourceFiles = {
...sourceFiles,
[newFileName]: {
name: newFileName,
language: fileNameToLanguage(newFileName),
value: temp
}
}
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('重命名成功')
if (
editingBaseId === record.id &&
editingFileName === fileName
) {
setEditingFileName('')
}
setTimeout(() => {
getBaseDetail(record)
})
break
default:
void message.error('重命名失败,请稍后重试')
}
})
.finally(() => {
setBaseDetailLoading({
...baseDetailLoading,
[record.id]: false
})
})
resolve(true)
})
},
() => {
return new Promise((_, reject) => {
reject('请输入文件名')
})
}
)
})
}
}
const handleOnDeleteFile = (fileName: string) => { const handleOnDeleteFile = (fileName: string) => {
return () => { return () => {
if (hasEdited) { if (Object.keys(hasEdited).length) {
void message.warning('删除文件前请先保存更改') void message.warning('删除文件前请先保存更改')
return return
} }
@@ -519,6 +657,12 @@ const Base = () => {
switch (response.code) { switch (response.code) {
case DATABASE_UPDATE_SUCCESS: case DATABASE_UPDATE_SUCCESS:
void message.success('删除成功') void message.success('删除成功')
if (
editingBaseId === record.id &&
editingFileName === fileName
) {
setEditingFileName('')
}
setTimeout(() => { setTimeout(() => {
getBaseDetail(record) getBaseDetail(record)
}) })
@@ -547,6 +691,7 @@ const Base = () => {
dataSource={sourceFileList} dataSource={sourceFileList}
columns={detailColumns} columns={detailColumns}
pagination={false} pagination={false}
rowKey={(record) => record.name}
/> />
</Card> </Card>
) )
@@ -625,8 +770,8 @@ const Base = () => {
return ( return (
<> <>
<FitFullscreen data-component={'system-tools-base'}> <FitFullscreen data-component={'system-tools-base'}>
<FlexBox direction={'horizontal'} className={'root-content'}>
<HideScrollbar> <HideScrollbar>
<FlexBox direction={'horizontal'} className={'root-content'}>
<Card> <Card>
<AntdTable <AntdTable
dataSource={baseData} dataSource={baseData}
@@ -640,7 +785,6 @@ const Base = () => {
}} }}
/> />
</Card> </Card>
</HideScrollbar>
{editingFileName && ( {editingFileName && (
<Card> <Card>
<CodeEditor <CodeEditor
@@ -651,9 +795,13 @@ const Base = () => {
showFileSelector={false} showFileSelector={false}
tsconfig={tsconfig} tsconfig={tsconfig}
/> />
<div className={'close-editor-btn'} onClick={handleOnCloseBtnClick}>
<Icon component={IconOxygenClose} />
</div>
</Card> </Card>
)} )}
</FlexBox> </FlexBox>
</HideScrollbar>
<AntdDrawer <AntdDrawer
title={isDrawerEdit ? '编辑基板' : '添加基板'} title={isDrawerEdit ? '编辑基板' : '添加基板'}
onClose={handleOnDrawerClose} onClose={handleOnDrawerClose}
@@ -666,6 +814,14 @@ const Base = () => {
</AntdDrawer> </AntdDrawer>
</FitFullscreen> </FitFullscreen>
{contextHolder} {contextHolder}
<AntdModal
open={blocker.state === 'blocked'}
title={'未保存'}
onOk={() => blocker.proceed?.()}
onCancel={() => blocker.reset?.()}
>
</AntdModal>
</> </>
) )
} }

View File

@@ -27,8 +27,8 @@ export default defineConfig({
'react-router-dom', 'react-router-dom',
{ {
react: ['Suspense', 'createContext'], react: ['Suspense', 'createContext'],
'react-router': ['useMatches', 'RouterProvider'], 'react-router': ['useMatches', 'RouterProvider', 'useBlocker'],
'react-router-dom': ['createBrowserRouter'], 'react-router-dom': ['createBrowserRouter', 'useBeforeUnload'],
antd: ['message', 'notification'] antd: ['message', 'notification']
}, },
{ {