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] {
.root-content {
padding: 20px;
padding: 30px;
gap: 10px;
height: 100%;
width: 100%;
@@ -11,5 +11,31 @@
content: '*';
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 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 {
COLOR_PRODUCTION,
DATABASE_DELETE_SUCCESS,
@@ -20,22 +7,40 @@ import {
DATABASE_INSERT_SUCCESS,
DATABASE_SELECT_SUCCESS,
DATABASE_UPDATE_SUCCESS
} from '@/constants/common.constants.ts'
} from '@/constants/common.constants'
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 {
base64ToFiles,
fileNameToLanguage,
filesToBase64,
getFilesSize,
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 blocker = useBlocker(
({ currentLocation, nextLocation }) =>
currentLocation.pathname !== nextLocation.pathname && Object.keys(hasEdited).length > 0
)
const [modal, contextHolder] = AntdModal.useModal()
const [form] = AntdForm.useForm<ToolBaseAddEditParam>()
const formValues = AntdForm.useWatch([], form)
const [addFileForm] = AntdForm.useForm<{ fileName: string }>()
const [renameFileForm] = AntdForm.useForm<{ fileName: string }>()
const [newFormValues, setNewFormValues] = useState<ToolBaseAddEditParam>()
const [isLoading, setIsLoading] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
@@ -51,6 +56,19 @@ const Base = () => {
const [baseDetailLoading, setBaseDetailLoading] = useState<Record<string, boolean>>({})
const [tsconfig, setTsconfig] = useState<ITsconfig>()
useBeforeUnload(
useCallback(
(event) => {
if (Object.keys(hasEdited).length) {
event.preventDefault()
event.returnValue = ''
}
},
[hasEdited]
),
{ capture: true }
)
const handleOnAddBtnClick = () => {
if (Object.keys(hasEdited).length) {
void message.warning('新增前请保存修改')
@@ -275,6 +293,10 @@ const Base = () => {
}
}
const handleOnCloseBtnClick = () => {
setEditingFileName('')
}
const handleOnDrawerClose = () => {
setIsDrawerOpen(false)
}
@@ -301,7 +323,6 @@ const Base = () => {
const handleOnExpand = (expanded: boolean, record: ToolBaseVo) => {
if (!expanded) {
setEditingFileName('')
return
}
getBaseDetail(record)
@@ -361,7 +382,11 @@ const Base = () => {
({ getFieldValue }) => ({
validator() {
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.resolve()
@@ -451,7 +476,7 @@ const Base = () => {
</>
),
dataIndex: 'enable',
width: '10em',
width: '12em',
align: 'center',
render: (_, record) => (
<>
@@ -464,6 +489,14 @@ const Base = () => {
</a>
</Permission>
<Permission operationCode={'system:tool:modify:category'}>
<a
onClick={handleOnRenameFile(record.name)}
style={{ color: COLOR_PRODUCTION }}
>
</a>
</Permission>
<Permission operationCode={'system:tool:delete:category'}>
<a
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) => {
return () => {
if (hasEdited) {
if (Object.keys(hasEdited).length) {
void message.warning('删除文件前请先保存更改')
return
}
@@ -519,6 +657,12 @@ const Base = () => {
switch (response.code) {
case DATABASE_UPDATE_SUCCESS:
void message.success('删除成功')
if (
editingBaseId === record.id &&
editingFileName === fileName
) {
setEditingFileName('')
}
setTimeout(() => {
getBaseDetail(record)
})
@@ -547,6 +691,7 @@ const Base = () => {
dataSource={sourceFileList}
columns={detailColumns}
pagination={false}
rowKey={(record) => record.name}
/>
</Card>
)
@@ -625,8 +770,8 @@ const Base = () => {
return (
<>
<FitFullscreen data-component={'system-tools-base'}>
<FlexBox direction={'horizontal'} className={'root-content'}>
<HideScrollbar>
<HideScrollbar>
<FlexBox direction={'horizontal'} className={'root-content'}>
<Card>
<AntdTable
dataSource={baseData}
@@ -640,20 +785,23 @@ const Base = () => {
}}
/>
</Card>
</HideScrollbar>
{editingFileName && (
<Card>
<CodeEditor
files={editingFiles[editingBaseId]}
selectedFileName={editingFileName}
onSelectedFileChange={() => {}}
onChangeFileContent={handleOnChangeFileContent}
showFileSelector={false}
tsconfig={tsconfig}
/>
</Card>
)}
</FlexBox>
{editingFileName && (
<Card>
<CodeEditor
files={editingFiles[editingBaseId]}
selectedFileName={editingFileName}
onSelectedFileChange={() => {}}
onChangeFileContent={handleOnChangeFileContent}
showFileSelector={false}
tsconfig={tsconfig}
/>
<div className={'close-editor-btn'} onClick={handleOnCloseBtnClick}>
<Icon component={IconOxygenClose} />
</div>
</Card>
)}
</FlexBox>
</HideScrollbar>
<AntdDrawer
title={isDrawerEdit ? '编辑基板' : '添加基板'}
onClose={handleOnDrawerClose}
@@ -666,6 +814,14 @@ const Base = () => {
</AntdDrawer>
</FitFullscreen>
{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: ['Suspense', 'createContext'],
'react-router': ['useMatches', 'RouterProvider'],
'react-router-dom': ['createBrowserRouter'],
'react-router': ['useMatches', 'RouterProvider', 'useBlocker'],
'react-router-dom': ['createBrowserRouter', 'useBeforeUnload'],
antd: ['message', 'notification']
},
{