Complete main UI #37
@@ -11,6 +11,7 @@
|
||||
width: 200px;
|
||||
height: 360px;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden !important;
|
||||
cursor: pointer;
|
||||
|
||||
.common-card {
|
||||
|
||||
20
src/assets/css/pages/system/tools/code.scss
Normal file
20
src/assets/css/pages/system/tools/code.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
[data-component=system-tools-code] {
|
||||
padding: 30px;
|
||||
|
||||
.card-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.draggable-content {
|
||||
position: fixed;
|
||||
inset-inline-end: 48px;
|
||||
inset-block-end: 48px;
|
||||
|
||||
> * {
|
||||
position: relative;
|
||||
inset-inline-end: 0;
|
||||
inset-block-end: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/assets/css/pages/system/tools/execute.scss
Normal file
8
src/assets/css/pages/system/tools/execute.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
[data-component=system-tools-execute] {
|
||||
padding: 30px;
|
||||
|
||||
.card-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 6em;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.upgrade-bt {
|
||||
|
||||
1
src/assets/svg/execute.svg
Normal file
1
src/assets/svg/execute.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M750.912 505.216L256 198.016c-27.712-17.024-64 4.288-64 36.288v614.4c0 32 36.288 53.312 64 36.288l492.8-307.2c29.888-14.976 29.888-57.6 2.112-72.576z" /></svg>
|
||||
|
After Width: | Height: | Size: 232 B |
@@ -80,7 +80,8 @@ export const TOOL_ILLEGAL_VERSION = 40050
|
||||
export const TOOL_UNDER_REVIEW = 40051
|
||||
export const TOOL_NOT_UNDER_REVIEW = 40052
|
||||
export const TOOL_HAS_UNPUBLISHED_VERSION = 40053
|
||||
export const TOOL_HAS_BEEN_PUBLISHED = 40054
|
||||
export const TOOL_HAS_NOT_BEEN_PUBLISHED = 40054
|
||||
export const TOOL_HAS_BEEN_PUBLISHED = 40055
|
||||
export const TOOL_SUBMIT_ERROR = 40060
|
||||
export const TOOL_CANCEL_ERROR = 40061
|
||||
|
||||
|
||||
25
src/global.d.ts
vendored
25
src/global.d.ts
vendored
@@ -89,6 +89,23 @@ interface LoginParam {
|
||||
captchaCode: string
|
||||
}
|
||||
|
||||
interface UserWithInfoVo {
|
||||
id: string
|
||||
username: string
|
||||
verified: boolean
|
||||
locking: boolean
|
||||
expiration: string
|
||||
credentialsExpiration: string
|
||||
enable: boolean
|
||||
currentLoginTime: string
|
||||
currentLoginIp: string
|
||||
lastLoginTime: string
|
||||
lastLoginIp: string
|
||||
createTime: string
|
||||
updateTime: string
|
||||
userInfo: UserInfoVo
|
||||
}
|
||||
|
||||
interface UserWithPowerInfoVo {
|
||||
id: string
|
||||
username: string
|
||||
@@ -532,7 +549,7 @@ interface ToolVo {
|
||||
icon: string
|
||||
description: string
|
||||
base: ToolBaseVo
|
||||
author: UserInfoVo
|
||||
author: UserWithInfoVo
|
||||
ver: string
|
||||
keywords: string[]
|
||||
categories: ToolCategoryVo[]
|
||||
@@ -570,3 +587,9 @@ interface ToolUpdateParam {
|
||||
categories?: string[]
|
||||
source?: string
|
||||
}
|
||||
|
||||
interface ToolManagementGetParam extends PageParam {
|
||||
searchType?: string
|
||||
searchValue?: string
|
||||
searchRegex?: boolean
|
||||
}
|
||||
|
||||
91
src/pages/System/Tools/Code.tsx
Normal file
91
src/pages/System/Tools/Code.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import { r_sys_tool_get_one } from '@/services/system.tsx'
|
||||
import '@/assets/css/pages/system/tools/code.scss'
|
||||
import { DATABASE_NO_RECORD_FOUND, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants.ts'
|
||||
import Playground from '@/components/Playground'
|
||||
import { useState } from 'react'
|
||||
import { IFiles } from '@/components/Playground/shared.ts'
|
||||
import { base64ToFiles } from '@/components/Playground/files.ts'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
||||
import Card from '@/components/common/Card.tsx'
|
||||
import Draggable from 'react-draggable'
|
||||
import Icon from '@ant-design/icons'
|
||||
|
||||
const Code = () => {
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [files, setFiles] = useState<IFiles>({})
|
||||
const [selectedFileName, setSelectedFileName] = useState('')
|
||||
|
||||
const handleOnRunTool = () => {
|
||||
navigate(`/system/tools/execute/${id}`)
|
||||
}
|
||||
|
||||
const render = (toolVo: ToolVo) => {
|
||||
try {
|
||||
setFiles(base64ToFiles(toolVo.source.data!))
|
||||
setSelectedFileName(toolVo.entryPoint)
|
||||
} catch (e) {
|
||||
void message.error('载入工具失败')
|
||||
}
|
||||
}
|
||||
|
||||
const getTool = () => {
|
||||
if (loading) {
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
void message.loading({ content: '加载中……', key: 'LOADING', duration: 0 })
|
||||
|
||||
void r_sys_tool_get_one(id!)
|
||||
.then((res) => {
|
||||
const response = res.data
|
||||
switch (response.code) {
|
||||
case DATABASE_SELECT_SUCCESS:
|
||||
render(response.data!)
|
||||
break
|
||||
case DATABASE_NO_RECORD_FOUND:
|
||||
void message.error('未找到指定工具')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('获取工具信息失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
message.destroy('LOADING')
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTool()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<FitFullscreen data-component={'system-tools-code'}>
|
||||
<Card>
|
||||
<Playground.CodeEditor
|
||||
readonly
|
||||
files={files}
|
||||
selectedFileName={selectedFileName}
|
||||
onSelectedFileChange={setSelectedFileName}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Draggable bounds={'#root'}>
|
||||
<div className={'draggable-content'}>
|
||||
<AntdFloatButton
|
||||
type={'primary'}
|
||||
icon={<Icon component={IconOxygenExecute} />}
|
||||
onClick={handleOnRunTool}
|
||||
/>
|
||||
</div>
|
||||
</Draggable>
|
||||
</FitFullscreen>
|
||||
)
|
||||
}
|
||||
|
||||
export default Code
|
||||
80
src/pages/System/Tools/Execute.tsx
Normal file
80
src/pages/System/Tools/Execute.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
||||
import '@/assets/css/pages/system/tools/execute.scss'
|
||||
import { base64ToFiles, base64ToStr, IMPORT_MAP_FILE_NAME } from '@/components/Playground/files.ts'
|
||||
import { IImportMap } from '@/components/Playground/shared.ts'
|
||||
import compiler from '@/components/Playground/compiler.ts'
|
||||
import { DATABASE_NO_RECORD_FOUND, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants.ts'
|
||||
import { r_sys_tool_get_one } from '@/services/system.tsx'
|
||||
import Card from '@/components/common/Card.tsx'
|
||||
import Playground from '@/components/Playground'
|
||||
|
||||
const Execute = () => {
|
||||
const navigate = useNavigate()
|
||||
const { id } = useParams()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [compiledCode, setCompiledCode] = useState('')
|
||||
|
||||
const render = (toolVo: ToolVo) => {
|
||||
try {
|
||||
const baseDist = base64ToStr(toolVo.base.dist.data!)
|
||||
const files = base64ToFiles(toolVo.source.data!)
|
||||
const importMap = JSON.parse(files[IMPORT_MAP_FILE_NAME].value) as IImportMap
|
||||
|
||||
void compiler
|
||||
.compile(files, importMap, toolVo.entryPoint)
|
||||
.then((result) => {
|
||||
const output = result.outputFiles[0].text
|
||||
setCompiledCode(`${output}\n${baseDist}`)
|
||||
})
|
||||
.catch((reason) => {
|
||||
void message.error(`编译失败:${reason}`)
|
||||
})
|
||||
} catch (e) {
|
||||
void message.error('载入工具失败')
|
||||
}
|
||||
}
|
||||
|
||||
const getTool = () => {
|
||||
if (loading) {
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
void message.loading({ content: '加载中……', key: 'LOADING', duration: 0 })
|
||||
|
||||
void r_sys_tool_get_one(id!)
|
||||
.then((res) => {
|
||||
const response = res.data
|
||||
switch (response.code) {
|
||||
case DATABASE_SELECT_SUCCESS:
|
||||
render(response.data!)
|
||||
break
|
||||
case DATABASE_NO_RECORD_FOUND:
|
||||
void message.error('未找到指定工具')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('获取工具信息失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
message.destroy('LOADING')
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTool()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<FitFullscreen data-component={'system-tools-execute'}>
|
||||
<Card>
|
||||
<Playground.Output.Preview.Render iframeKey={`${id}`} compiledCode={compiledCode} />
|
||||
</Card>
|
||||
</FitFullscreen>
|
||||
)
|
||||
}
|
||||
|
||||
export default Execute
|
||||
@@ -1,14 +1,485 @@
|
||||
import Playground from '@/components/Playground'
|
||||
import templates from '@/components/Playground/templates'
|
||||
import { ChangeEvent, KeyboardEvent } from 'react'
|
||||
import {
|
||||
r_sys_tool_delete,
|
||||
r_sys_tool_get,
|
||||
r_sys_tool_off_shelve,
|
||||
r_sys_tool_pass,
|
||||
r_sys_tool_reject
|
||||
} from '@/services/system'
|
||||
import {
|
||||
COLOR_BACKGROUND,
|
||||
COLOR_ERROR_SECONDARY,
|
||||
COLOR_PRODUCTION,
|
||||
DATABASE_DELETE_SUCCESS,
|
||||
DATABASE_SELECT_SUCCESS,
|
||||
DATABASE_UPDATE_SUCCESS,
|
||||
TOOL_NOT_UNDER_REVIEW
|
||||
} from '@/constants/common.constants.ts'
|
||||
import FlexBox from '@/components/common/FlexBox.tsx'
|
||||
import Card from '@/components/common/Card.tsx'
|
||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
||||
import HideScrollbar from '@/components/common/HideScrollbar.tsx'
|
||||
import Icon from '@ant-design/icons'
|
||||
|
||||
const Tools = () => {
|
||||
const template = templates['base']
|
||||
return (
|
||||
<Playground
|
||||
initFiles={template.files}
|
||||
initTsconfigRaw={JSON.stringify(template.tsconfig, null, 2)}
|
||||
initImportMapRaw={JSON.stringify(template.importMap, null, 2)}
|
||||
const navigate = useNavigate()
|
||||
const [modal, contextHolder] = AntdModal.useModal()
|
||||
const [tableParams, setTableParams] = useState<TableParam>({
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
position: ['bottomCenter'],
|
||||
showTotal: (total, range) =>
|
||||
`第 ${
|
||||
range[0] === range[1] ? `${range[0]}` : `${range[0]}~${range[1]}`
|
||||
} 项 共 ${total} 项`
|
||||
}
|
||||
})
|
||||
const [toolData, setToolData] = useState<ToolVo[]>([])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [searchType, setSearchType] = useState('ALL')
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [isUseRegex, setIsUseRegex] = useState(false)
|
||||
const [isRegexLegal, setIsRegexLegal] = useState(true)
|
||||
const [form] = AntdForm.useForm<{ pass: boolean }>()
|
||||
|
||||
const dataColumns: _ColumnsType<ToolVo> = [
|
||||
{
|
||||
dataIndex: 'icon',
|
||||
title: '图标',
|
||||
render: (value) => (
|
||||
<AntdAvatar
|
||||
src={
|
||||
<AntdImage
|
||||
preview={{ mask: <Icon component={IconOxygenEye}></Icon> }}
|
||||
src={`data:image/svg+xml;base64,${value}`}
|
||||
alt={'Avatar'}
|
||||
/>
|
||||
}
|
||||
style={{ background: COLOR_BACKGROUND }}
|
||||
/>
|
||||
),
|
||||
width: '0',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
render: (_, record) => (
|
||||
<AntdTooltip title={record.description}>{record.name}</AntdTooltip>
|
||||
)
|
||||
},
|
||||
{ dataIndex: 'toolId', title: '工具 ID' },
|
||||
{ dataIndex: 'ver', title: '版本' },
|
||||
{
|
||||
title: '作者',
|
||||
render: (_, record) => `${record.author.userInfo.nickname}(${record.author.username})`
|
||||
},
|
||||
{
|
||||
dataIndex: 'keywords',
|
||||
title: '关键词',
|
||||
render: (value: string[]) => value.map((item) => <AntdTag>{item}</AntdTag>)
|
||||
},
|
||||
{
|
||||
dataIndex: 'categories',
|
||||
title: '类别',
|
||||
render: (value: { name: string }[]) =>
|
||||
value.map((item) => <AntdTag>{item.name}</AntdTag>)
|
||||
},
|
||||
{
|
||||
dataIndex: 'review',
|
||||
title: '状态',
|
||||
width: '4em',
|
||||
render: (value) => {
|
||||
switch (value) {
|
||||
case 'NONE':
|
||||
return <AntdTag>编码</AntdTag>
|
||||
case 'PROCESSING':
|
||||
return <AntdTag color={'purple'}>审核</AntdTag>
|
||||
case 'REJECT':
|
||||
return <AntdTag color={'yellow'}>驳回</AntdTag>
|
||||
case 'PASS':
|
||||
return <AntdTag color={'green'}>通过</AntdTag>
|
||||
}
|
||||
},
|
||||
filters: [
|
||||
{ text: '编码', value: 'NONE' },
|
||||
{ text: '审核', value: 'PROCESSING' },
|
||||
{ text: '驳回', value: 'REJECT' },
|
||||
{ text: '通过', value: 'PASS' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: '12em',
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<AntdSpace size={'middle'}>
|
||||
<a
|
||||
style={{ color: COLOR_PRODUCTION }}
|
||||
onClick={handleOnViewBtnClick(record)}
|
||||
>
|
||||
查看
|
||||
</a>
|
||||
{record.review === 'PROCESSING' && (
|
||||
<a
|
||||
style={{ color: COLOR_PRODUCTION }}
|
||||
onClick={handleOnReviewBtnClick(record)}
|
||||
>
|
||||
审核
|
||||
</a>
|
||||
)}
|
||||
{record.review === 'PASS' && (
|
||||
<a
|
||||
style={{ color: COLOR_PRODUCTION }}
|
||||
onClick={handleOnOffShelveBtnClick(record)}
|
||||
>
|
||||
下架
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
style={{ color: COLOR_PRODUCTION }}
|
||||
onClick={handleOnDeleteBtnClick(record)}
|
||||
>
|
||||
删除
|
||||
</a>
|
||||
</AntdSpace>{' '}
|
||||
</>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
const handleOnTableChange = (
|
||||
pagination: _TablePaginationConfig,
|
||||
filters: Record<string, _FilterValue | null>,
|
||||
sorter: _SorterResult<ToolVo> | _SorterResult<ToolVo>[]
|
||||
) => {
|
||||
pagination = { ...tableParams.pagination, ...pagination }
|
||||
if (Array.isArray(sorter)) {
|
||||
setTableParams({
|
||||
pagination,
|
||||
filters,
|
||||
sortField: sorter.map((value) => value.field).join(',')
|
||||
})
|
||||
} else {
|
||||
setTableParams({
|
||||
pagination,
|
||||
filters,
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order
|
||||
})
|
||||
}
|
||||
|
||||
if (pagination.pageSize !== tableParams.pagination?.pageSize) {
|
||||
setToolData([])
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnViewBtnClick = (value: ToolVo) => {
|
||||
return () => {
|
||||
navigate(`/system/tools/code/${value.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnReviewBtnClick = (value: ToolVo) => {
|
||||
return () => {
|
||||
form.setFieldValue('pass', undefined)
|
||||
void modal.confirm({
|
||||
title: '审核',
|
||||
content: (
|
||||
<AntdForm form={form}>
|
||||
<AntdForm.Item
|
||||
name={'pass'}
|
||||
style={{ marginTop: 10 }}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<AntdRadio.Group>
|
||||
<AntdRadio value={true}>通过</AntdRadio>
|
||||
<AntdRadio value={false}>驳回</AntdRadio>
|
||||
</AntdRadio.Group>
|
||||
</AntdForm.Item>
|
||||
</AntdForm>
|
||||
),
|
||||
onOk: () =>
|
||||
form.validateFields().then(() => {
|
||||
return new Promise<void>((resolve) => {
|
||||
switch (form.getFieldValue('pass')) {
|
||||
case true:
|
||||
void r_sys_tool_pass(value.id).then((res) => {
|
||||
const response = res.data
|
||||
switch (response.code) {
|
||||
case DATABASE_UPDATE_SUCCESS:
|
||||
void message.success('更新成功')
|
||||
getTool()
|
||||
resolve()
|
||||
break
|
||||
case TOOL_NOT_UNDER_REVIEW:
|
||||
void message.warning('工具处于非审核状态')
|
||||
resolve()
|
||||
break
|
||||
default:
|
||||
void message.error('更新失败,请稍后重试')
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
void r_sys_tool_reject(value.id).then((res) => {
|
||||
const response = res.data
|
||||
switch (response.code) {
|
||||
case DATABASE_UPDATE_SUCCESS:
|
||||
void message.success('更新成功')
|
||||
resolve()
|
||||
break
|
||||
case TOOL_NOT_UNDER_REVIEW:
|
||||
void message.warning('工具处于非审核状态')
|
||||
resolve()
|
||||
break
|
||||
default:
|
||||
void message.error('更新失败,请稍后重试')
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnOffShelveBtnClick = (value: ToolVo) => {
|
||||
return () => {
|
||||
modal
|
||||
.confirm({
|
||||
title: '确定下架',
|
||||
content: `确定下架工具 ${value.author.username}:${value.toolId}:${value.ver} 吗?`
|
||||
})
|
||||
.then(
|
||||
(confirmed) => {
|
||||
if (confirmed) {
|
||||
setIsLoading(true)
|
||||
|
||||
void r_sys_tool_off_shelve(value.id)
|
||||
.then((res) => {
|
||||
const response = res.data
|
||||
if (response.code === DATABASE_UPDATE_SUCCESS) {
|
||||
void message.success('下架成功')
|
||||
setTimeout(() => {
|
||||
getTool()
|
||||
})
|
||||
} else {
|
||||
void message.error('下架失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnDeleteBtnClick = (value: ToolVo) => {
|
||||
return () => {
|
||||
modal
|
||||
.confirm({
|
||||
title: '确定删除',
|
||||
content: `确定删除工具 ${value.author.username}:${value.toolId}:${value.ver} 吗?`
|
||||
})
|
||||
.then(
|
||||
(confirmed) => {
|
||||
if (confirmed) {
|
||||
setIsLoading(true)
|
||||
|
||||
void r_sys_tool_delete(value.id)
|
||||
.then((res) => {
|
||||
const response = res.data
|
||||
if (response.code === DATABASE_DELETE_SUCCESS) {
|
||||
void message.success('删除成功')
|
||||
setTimeout(() => {
|
||||
getTool()
|
||||
})
|
||||
} else {
|
||||
void message.error('删除失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnSearchValueChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value)
|
||||
|
||||
if (isUseRegex) {
|
||||
try {
|
||||
RegExp(e.target.value)
|
||||
setIsRegexLegal(!(e.target.value.includes('{}') || e.target.value.includes('[]')))
|
||||
} catch (e) {
|
||||
setIsRegexLegal(false)
|
||||
}
|
||||
} else {
|
||||
setIsRegexLegal(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnSearchValueKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
getTool()
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnSearchTypeChange = (value: string) => {
|
||||
setSearchType(value)
|
||||
}
|
||||
|
||||
const handleOnUseRegexChange = (e: _CheckboxChangeEvent) => {
|
||||
setIsUseRegex(e.target.checked)
|
||||
if (e.target.checked) {
|
||||
try {
|
||||
RegExp(searchValue)
|
||||
setIsRegexLegal(!(searchValue.includes('{}') || searchValue.includes('[]')))
|
||||
} catch (e) {
|
||||
setIsRegexLegal(false)
|
||||
}
|
||||
} else {
|
||||
setIsRegexLegal(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnQueryBtnClick = () => {
|
||||
getTool()
|
||||
}
|
||||
|
||||
const getTool = () => {
|
||||
if (isLoading) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isRegexLegal) {
|
||||
void message.error('非法正则表达式')
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
||||
void r_sys_tool_get({
|
||||
currentPage: tableParams.pagination?.current,
|
||||
pageSize: tableParams.pagination?.pageSize,
|
||||
sortField:
|
||||
tableParams.sortField && tableParams.sortOrder
|
||||
? (tableParams.sortField as string)
|
||||
: undefined,
|
||||
sortOrder:
|
||||
tableParams.sortField && tableParams.sortOrder ? tableParams.sortOrder : undefined,
|
||||
searchType,
|
||||
searchValue: searchValue.trim().length ? searchValue : undefined,
|
||||
searchRegex: isUseRegex ? isUseRegex : undefined,
|
||||
...tableParams.filters
|
||||
})
|
||||
.then((res) => {
|
||||
const response = res.data
|
||||
switch (response.code) {
|
||||
case DATABASE_SELECT_SUCCESS:
|
||||
setToolData(response.data!.records)
|
||||
setTableParams({
|
||||
...tableParams,
|
||||
pagination: {
|
||||
...tableParams.pagination,
|
||||
total: response.data!.total
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
void message.error('获取失败,请稍后重试')
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getTool()
|
||||
}, [
|
||||
JSON.stringify(tableParams.filters),
|
||||
JSON.stringify(tableParams.sortField),
|
||||
JSON.stringify(tableParams.sortOrder),
|
||||
JSON.stringify(tableParams.pagination?.pageSize),
|
||||
JSON.stringify(tableParams.pagination?.current)
|
||||
])
|
||||
|
||||
const toolbar = (
|
||||
<FlexBox direction={'horizontal'} gap={10}>
|
||||
<Card style={{ overflow: 'inherit' }}>
|
||||
<AntdInput
|
||||
addonBefore={
|
||||
<AntdSelect
|
||||
value={searchType}
|
||||
onChange={handleOnSearchTypeChange}
|
||||
style={{ width: '6em' }}
|
||||
dropdownStyle={{ textAlign: 'center' }}
|
||||
></AntdSelect>
|
||||
}
|
||||
suffix={
|
||||
<>
|
||||
{!isRegexLegal && (
|
||||
<span style={{ color: COLOR_ERROR_SECONDARY }}>非法表达式</span>
|
||||
)}
|
||||
<AntdCheckbox checked={isUseRegex} onChange={handleOnUseRegexChange}>
|
||||
<AntdTooltip title={'正则表达式'}>.*</AntdTooltip>
|
||||
</AntdCheckbox>
|
||||
</>
|
||||
}
|
||||
allowClear
|
||||
value={searchValue}
|
||||
onChange={handleOnSearchValueChange}
|
||||
onKeyDown={handleOnSearchValueKeyDown}
|
||||
status={isRegexLegal ? undefined : 'error'}
|
||||
/>
|
||||
</Card>
|
||||
<Card style={{ overflow: 'inherit', flex: '0 0 auto' }}>
|
||||
<AntdButton onClick={handleOnQueryBtnClick} type={'primary'}>
|
||||
查询
|
||||
</AntdButton>
|
||||
</Card>
|
||||
</FlexBox>
|
||||
)
|
||||
|
||||
const table = (
|
||||
<Card>
|
||||
<AntdTable
|
||||
dataSource={toolData}
|
||||
columns={dataColumns}
|
||||
pagination={tableParams.pagination}
|
||||
loading={isLoading}
|
||||
onChange={handleOnTableChange}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<FitFullscreen>
|
||||
<HideScrollbar
|
||||
style={{ padding: 30 }}
|
||||
isShowVerticalScrollbar
|
||||
autoHideWaitingTime={1000}
|
||||
>
|
||||
<FlexBox gap={20}>
|
||||
{toolbar}
|
||||
{table}
|
||||
</FlexBox>
|
||||
</HideScrollbar>
|
||||
</FitFullscreen>
|
||||
{contextHolder}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ const User = () => {
|
||||
const dataColumns: _ColumnsType<UserWithRoleInfoVo> = [
|
||||
{
|
||||
dataIndex: 'username',
|
||||
title: '用户',
|
||||
title: '用户名',
|
||||
render: (value, record) => <AntdTooltip title={record.id}>{value}</AntdTooltip>,
|
||||
width: '0'
|
||||
},
|
||||
@@ -249,7 +249,7 @@ const User = () => {
|
||||
}
|
||||
|
||||
if (pagination.pageSize !== tableParams.pagination?.pageSize) {
|
||||
setGroupData([])
|
||||
setUserData([])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,7 +584,7 @@ const User = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnSearchNameKeyDown = (e: KeyboardEvent) => {
|
||||
const handleOnSearchValueKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
getUser()
|
||||
}
|
||||
@@ -936,7 +936,7 @@ const User = () => {
|
||||
allowClear
|
||||
value={searchValue}
|
||||
onChange={handleOnSearchValueChange}
|
||||
onKeyDown={handleOnSearchNameKeyDown}
|
||||
onKeyDown={handleOnSearchValueKeyDown}
|
||||
status={isRegexLegal ? undefined : 'error'}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -25,7 +25,7 @@ const SystemFramework = () => {
|
||||
>
|
||||
{route.children?.map(
|
||||
(subRoute) =>
|
||||
subRoute &&
|
||||
subRoute.menu &&
|
||||
subRoute.name && (
|
||||
<Sidebar.Item
|
||||
end
|
||||
|
||||
@@ -44,7 +44,7 @@ const Create = () => {
|
||||
void message.success(
|
||||
`创建工具 ${response.data!.name}<${response.data!.toolId}>:${response.data!.ver} 成功`
|
||||
)
|
||||
navigate(`/view/!/${response.data!.toolId}/${response.data!.ver}`)
|
||||
navigate(`/edit/${response.data!.toolId}`)
|
||||
break
|
||||
case DATABASE_DUPLICATE_KEY:
|
||||
void message.warning('已存在相同 ID 的应用')
|
||||
|
||||
@@ -96,11 +96,15 @@ const Edit = () => {
|
||||
break
|
||||
case TOOL_UNDER_REVIEW:
|
||||
void message.error('保存失败:工具审核中')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
case TOOL_HAS_BEEN_PUBLISHED:
|
||||
void message.error('保存失败:工具已发布')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('保存失败,请稍后重试')
|
||||
@@ -158,11 +162,15 @@ const Edit = () => {
|
||||
break
|
||||
case TOOL_UNDER_REVIEW:
|
||||
void message.error('保存失败:工具审核中')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
case TOOL_HAS_BEEN_PUBLISHED:
|
||||
void message.error('保存失败:工具已发布')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('保存失败,请稍后重试')
|
||||
@@ -215,16 +223,22 @@ const Edit = () => {
|
||||
break
|
||||
case 'PROCESSING':
|
||||
void message.warning('工具审核中,请勿修改')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.warning('请先创建新版本后编辑工具')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
}
|
||||
break
|
||||
case DATABASE_NO_RECORD_FOUND:
|
||||
void message.error('未找到指定工具')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('获取工具信息失败,请稍后重试')
|
||||
|
||||
@@ -51,7 +51,9 @@ const View = () => {
|
||||
break
|
||||
case DATABASE_NO_RECORD_FOUND:
|
||||
void message.error('未找到指定工具')
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
break
|
||||
default:
|
||||
void message.error('获取工具信息失败,请稍后重试')
|
||||
@@ -65,7 +67,9 @@ const View = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (username === '!' && !getLoginStatus()) {
|
||||
navigate('/')
|
||||
setTimeout(() => {
|
||||
navigate(-1)
|
||||
}, 3000)
|
||||
return
|
||||
}
|
||||
if (username !== '!' && ver) {
|
||||
|
||||
@@ -284,9 +284,7 @@ const Tools = () => {
|
||||
switch (response.code) {
|
||||
case DATABASE_UPDATE_SUCCESS:
|
||||
void message.success('创建新版本成功')
|
||||
navigate(
|
||||
`/view/!/${response.data!.toolId}/${response.data!.ver}`
|
||||
)
|
||||
navigate(`/edit/${response.data!.toolId}`)
|
||||
resolve()
|
||||
break
|
||||
case TOOL_ILLEGAL_VERSION:
|
||||
|
||||
@@ -49,6 +49,20 @@ const system: RouteJsonObject[] = [
|
||||
menu: true,
|
||||
autoHide: true
|
||||
},
|
||||
{
|
||||
path: 'code/:id',
|
||||
absolutePath: '/system/tools/code',
|
||||
id: 'system-tools-code',
|
||||
component: lazy(() => import('@/pages/System/Tools/Code')),
|
||||
name: '查看工具'
|
||||
},
|
||||
{
|
||||
path: 'execute/:id',
|
||||
absolutePath: '/system/tools/execute',
|
||||
id: 'system-tools-execute',
|
||||
component: lazy(() => import('@/pages/System/Tools/Execute')),
|
||||
name: '运行工具'
|
||||
},
|
||||
{
|
||||
path: 'template',
|
||||
absolutePath: '/system/tools/template',
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
URL_SYS_SETTINGS_SENSITIVE,
|
||||
URL_SYS_TOOL_CATEGORY,
|
||||
URL_SYS_TOOL_BASE,
|
||||
URL_SYS_TOOL_TEMPLATE
|
||||
URL_SYS_TOOL_TEMPLATE,
|
||||
URL_SYS_TOOL
|
||||
} from '@/constants/urls.constants'
|
||||
import request from '@/services/index'
|
||||
|
||||
@@ -154,3 +155,16 @@ export const r_sys_tool_template_update = (param: ToolTemplateAddEditParam) =>
|
||||
|
||||
export const r_sys_tool_template_delete = (id: string) =>
|
||||
request.delete(`${URL_SYS_TOOL_TEMPLATE}/${id}`)
|
||||
|
||||
export const r_sys_tool_get = (param: ToolManagementGetParam) =>
|
||||
request.get<PageVo<ToolVo>>(URL_SYS_TOOL, param)
|
||||
|
||||
export const r_sys_tool_get_one = (id: string) => request.get<ToolVo>(`${URL_SYS_TOOL}/${id}`)
|
||||
|
||||
export const r_sys_tool_pass = (id: string) => request.post<ToolVo>(`${URL_SYS_TOOL}/${id}`)
|
||||
|
||||
export const r_sys_tool_reject = (id: string) => request.put<ToolVo>(`${URL_SYS_TOOL}/${id}`)
|
||||
|
||||
export const r_sys_tool_off_shelve = (id: string) => request.patch<ToolVo>(`${URL_SYS_TOOL}/${id}`)
|
||||
|
||||
export const r_sys_tool_delete = (id: string) => request.delete(`${URL_SYS_TOOL}/${id}`)
|
||||
|
||||
Reference in New Issue
Block a user