Add code and execute page to tool management

This commit is contained in:
2024-02-02 17:42:40 +08:00
parent 06213d3414
commit 838c37595d
18 changed files with 770 additions and 30 deletions

View File

@@ -11,6 +11,7 @@
width: 200px; width: 200px;
height: 360px; height: 360px;
flex: 0 0 auto; flex: 0 0 auto;
overflow: hidden !important;
cursor: pointer; cursor: pointer;
.common-card { .common-card {

View 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;
}
}
}

View File

@@ -0,0 +1,8 @@
[data-component=system-tools-execute] {
padding: 30px;
.card-box {
width: 100%;
height: 100%;
}
}

View File

@@ -28,7 +28,7 @@
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 10px; left: 10px;
width: 6em; width: 8em;
} }
.upgrade-bt { .upgrade-bt {

View 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

View File

@@ -80,7 +80,8 @@ export const TOOL_ILLEGAL_VERSION = 40050
export const TOOL_UNDER_REVIEW = 40051 export const TOOL_UNDER_REVIEW = 40051
export const TOOL_NOT_UNDER_REVIEW = 40052 export const TOOL_NOT_UNDER_REVIEW = 40052
export const TOOL_HAS_UNPUBLISHED_VERSION = 40053 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_SUBMIT_ERROR = 40060
export const TOOL_CANCEL_ERROR = 40061 export const TOOL_CANCEL_ERROR = 40061

25
src/global.d.ts vendored
View File

@@ -89,6 +89,23 @@ interface LoginParam {
captchaCode: string 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 { interface UserWithPowerInfoVo {
id: string id: string
username: string username: string
@@ -532,7 +549,7 @@ interface ToolVo {
icon: string icon: string
description: string description: string
base: ToolBaseVo base: ToolBaseVo
author: UserInfoVo author: UserWithInfoVo
ver: string ver: string
keywords: string[] keywords: string[]
categories: ToolCategoryVo[] categories: ToolCategoryVo[]
@@ -570,3 +587,9 @@ interface ToolUpdateParam {
categories?: string[] categories?: string[]
source?: string source?: string
} }
interface ToolManagementGetParam extends PageParam {
searchType?: string
searchValue?: string
searchRegex?: boolean
}

View 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

View 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

View File

@@ -1,14 +1,485 @@
import Playground from '@/components/Playground' import { ChangeEvent, KeyboardEvent } from 'react'
import templates from '@/components/Playground/templates' 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 Tools = () => {
const template = templates['base'] 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 ( return (
<Playground <>
initFiles={template.files} <FitFullscreen>
initTsconfigRaw={JSON.stringify(template.tsconfig, null, 2)} <HideScrollbar
initImportMapRaw={JSON.stringify(template.importMap, null, 2)} style={{ padding: 30 }}
/> isShowVerticalScrollbar
autoHideWaitingTime={1000}
>
<FlexBox gap={20}>
{toolbar}
{table}
</FlexBox>
</HideScrollbar>
</FitFullscreen>
{contextHolder}
</>
) )
} }

View File

@@ -77,7 +77,7 @@ const User = () => {
const dataColumns: _ColumnsType<UserWithRoleInfoVo> = [ const dataColumns: _ColumnsType<UserWithRoleInfoVo> = [
{ {
dataIndex: 'username', dataIndex: 'username',
title: '用户', title: '用户',
render: (value, record) => <AntdTooltip title={record.id}>{value}</AntdTooltip>, render: (value, record) => <AntdTooltip title={record.id}>{value}</AntdTooltip>,
width: '0' width: '0'
}, },
@@ -249,7 +249,7 @@ const User = () => {
} }
if (pagination.pageSize !== tableParams.pagination?.pageSize) { 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') { if (e.key === 'Enter') {
getUser() getUser()
} }
@@ -936,7 +936,7 @@ const User = () => {
allowClear allowClear
value={searchValue} value={searchValue}
onChange={handleOnSearchValueChange} onChange={handleOnSearchValueChange}
onKeyDown={handleOnSearchNameKeyDown} onKeyDown={handleOnSearchValueKeyDown}
status={isRegexLegal ? undefined : 'error'} status={isRegexLegal ? undefined : 'error'}
/> />
</Card> </Card>

View File

@@ -25,7 +25,7 @@ const SystemFramework = () => {
> >
{route.children?.map( {route.children?.map(
(subRoute) => (subRoute) =>
subRoute && subRoute.menu &&
subRoute.name && ( subRoute.name && (
<Sidebar.Item <Sidebar.Item
end end

View File

@@ -44,7 +44,7 @@ const Create = () => {
void message.success( void message.success(
`创建工具 ${response.data!.name}<${response.data!.toolId}>:${response.data!.ver} 成功` `创建工具 ${response.data!.name}<${response.data!.toolId}>:${response.data!.ver} 成功`
) )
navigate(`/view/!/${response.data!.toolId}/${response.data!.ver}`) navigate(`/edit/${response.data!.toolId}`)
break break
case DATABASE_DUPLICATE_KEY: case DATABASE_DUPLICATE_KEY:
void message.warning('已存在相同 ID 的应用') void message.warning('已存在相同 ID 的应用')

View File

@@ -96,11 +96,15 @@ const Edit = () => {
break break
case TOOL_UNDER_REVIEW: case TOOL_UNDER_REVIEW:
void message.error('保存失败:工具审核中') void message.error('保存失败:工具审核中')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
case TOOL_HAS_BEEN_PUBLISHED: case TOOL_HAS_BEEN_PUBLISHED:
void message.error('保存失败:工具已发布') void message.error('保存失败:工具已发布')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
default: default:
void message.error('保存失败,请稍后重试') void message.error('保存失败,请稍后重试')
@@ -158,11 +162,15 @@ const Edit = () => {
break break
case TOOL_UNDER_REVIEW: case TOOL_UNDER_REVIEW:
void message.error('保存失败:工具审核中') void message.error('保存失败:工具审核中')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
case TOOL_HAS_BEEN_PUBLISHED: case TOOL_HAS_BEEN_PUBLISHED:
void message.error('保存失败:工具已发布') void message.error('保存失败:工具已发布')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
default: default:
void message.error('保存失败,请稍后重试') void message.error('保存失败,请稍后重试')
@@ -215,16 +223,22 @@ const Edit = () => {
break break
case 'PROCESSING': case 'PROCESSING':
void message.warning('工具审核中,请勿修改') void message.warning('工具审核中,请勿修改')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
default: default:
void message.warning('请先创建新版本后编辑工具') void message.warning('请先创建新版本后编辑工具')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
} }
break break
case DATABASE_NO_RECORD_FOUND: case DATABASE_NO_RECORD_FOUND:
void message.error('未找到指定工具') void message.error('未找到指定工具')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
default: default:
void message.error('获取工具信息失败,请稍后重试') void message.error('获取工具信息失败,请稍后重试')

View File

@@ -51,7 +51,9 @@ const View = () => {
break break
case DATABASE_NO_RECORD_FOUND: case DATABASE_NO_RECORD_FOUND:
void message.error('未找到指定工具') void message.error('未找到指定工具')
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
break break
default: default:
void message.error('获取工具信息失败,请稍后重试') void message.error('获取工具信息失败,请稍后重试')
@@ -65,7 +67,9 @@ const View = () => {
useEffect(() => { useEffect(() => {
if (username === '!' && !getLoginStatus()) { if (username === '!' && !getLoginStatus()) {
navigate('/') setTimeout(() => {
navigate(-1)
}, 3000)
return return
} }
if (username !== '!' && ver) { if (username !== '!' && ver) {

View File

@@ -284,9 +284,7 @@ const Tools = () => {
switch (response.code) { switch (response.code) {
case DATABASE_UPDATE_SUCCESS: case DATABASE_UPDATE_SUCCESS:
void message.success('创建新版本成功') void message.success('创建新版本成功')
navigate( navigate(`/edit/${response.data!.toolId}`)
`/view/!/${response.data!.toolId}/${response.data!.ver}`
)
resolve() resolve()
break break
case TOOL_ILLEGAL_VERSION: case TOOL_ILLEGAL_VERSION:

View File

@@ -49,6 +49,20 @@ const system: RouteJsonObject[] = [
menu: true, menu: true,
autoHide: 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', path: 'template',
absolutePath: '/system/tools/template', absolutePath: '/system/tools/template',

View File

@@ -19,7 +19,8 @@ import {
URL_SYS_SETTINGS_SENSITIVE, URL_SYS_SETTINGS_SENSITIVE,
URL_SYS_TOOL_CATEGORY, URL_SYS_TOOL_CATEGORY,
URL_SYS_TOOL_BASE, URL_SYS_TOOL_BASE,
URL_SYS_TOOL_TEMPLATE URL_SYS_TOOL_TEMPLATE,
URL_SYS_TOOL
} from '@/constants/urls.constants' } from '@/constants/urls.constants'
import request from '@/services/index' 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) => export const r_sys_tool_template_delete = (id: string) =>
request.delete(`${URL_SYS_TOOL_TEMPLATE}/${id}`) 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}`)