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

@@ -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']
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 (
<Playground
initFiles={template.files}
initTsconfigRaw={JSON.stringify(template.tsconfig, null, 2)}
initImportMapRaw={JSON.stringify(template.importMap, null, 2)}
/>
<>
<FitFullscreen>
<HideScrollbar
style={{ padding: 30 }}
isShowVerticalScrollbar
autoHideWaitingTime={1000}
>
<FlexBox gap={20}>
{toolbar}
{table}
</FlexBox>
</HideScrollbar>
</FitFullscreen>
{contextHolder}
</>
)
}