import { ChangeEvent, KeyboardEvent } from 'react' import Icon from '@ant-design/icons' import { useTheme } from 'antd-style' import { DATABASE_DELETE_SUCCESS, DATABASE_SELECT_SUCCESS, DATABASE_UPDATE_SUCCESS, TOOL_NOT_UNDER_REVIEW } from '@/constants/common.constants' import { message, modal } from '@/util/common' import { navigateToCode } from '@/util/navigation' import { r_sys_tool_delete, r_sys_tool_get, r_sys_tool_get_one, r_sys_tool_off_shelve, r_sys_tool_pass, r_sys_tool_reject } from '@/services/system' import FlexBox from '@/components/common/FlexBox' import Card from '@/components/common/Card' import FitFullscreen from '@/components/common/FitFullscreen' import HideScrollbar from '@/components/common/HideScrollbar' import compiler from '@/components/Playground/compiler' import { IImportMap } from '@/components/Playground/shared' import { base64ToFiles, IMPORT_MAP_FILE_NAME, strToBase64 } from '@/components/Playground/files' import Permission from '@/components/common/Permission' const Tools = () => { const theme = useTheme() const navigate = useNavigate() const [tableParams, setTableParams] = useState({ 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([]) 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 = [ { dataIndex: 'icon', title: '图标', render: (value) => ( }} src={`data:image/svg+xml;base64,${value}`} alt={'Avatar'} /> } style={{ background: theme.colorBgLayout }} /> ), width: '0', align: 'center' }, { title: '名称', render: (_, record) => ( {record.name} ) }, { dataIndex: 'toolId', title: '工具 ID' }, { dataIndex: 'ver', title: '版本' }, { title: '平台', dataIndex: 'platform', render: (value: string) => `${value.slice(0, 1)}${value.slice(1).toLowerCase()}`, filters: [ { text: 'Web', value: 'WEB' }, { text: 'Desktop', value: 'DESKTOP' }, { text: 'Android', value: 'ANDROID' } ] }, { title: '作者', render: (_, record) => `${record.author.userInfo.nickname}(${record.author.username})` }, { dataIndex: 'keywords', title: '关键词', render: (value: string[]) => value.map((item) => {item}) }, { dataIndex: 'categories', title: '类别', render: (value: { name: string }[]) => value.map((item) => {item.name}) }, { dataIndex: 'review', title: '状态', width: '4em', render: (value) => { switch (value) { case 'NONE': return 编码 case 'PROCESSING': return 审核 case 'REJECT': return 驳回 case 'PASS': return 通过 } }, filters: [ { text: '编码', value: 'NONE' }, { text: '审核', value: 'PROCESSING' }, { text: '驳回', value: 'REJECT' }, { text: '通过', value: 'PASS' } ] }, { title: '操作', width: '12em', align: 'center', render: (_, record) => ( <> 查看 {record.review === 'PROCESSING' && ( 审核 )} {record.review === 'PASS' && ( 下架 )} 删除 {' '} ) } ] const handleOnTableChange = ( pagination: _TablePaginationConfig, filters: Record, sorter: _SorterResult | _SorterResult[] ) => { 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 () => { navigateToCode(navigate, value.id) } } const handleOnReviewBtnClick = (value: ToolVo) => { return () => { form.setFieldValue('pass', undefined) void modal.confirm({ centered: true, maskClosable: true, title: '审核', footer: (_, { OkBtn, CancelBtn }) => ( <> ), content: ( 通过 驳回 ), onOk: () => form.validateFields().then(() => { return new Promise((resolve) => { switch (form.getFieldValue('pass')) { case true: void message.loading({ content: '加载工具中……', key: 'LOADING_TOOL', duration: 0 }) void r_sys_tool_get_one(value.id) .then((res) => { message.destroy('LOADING_TOOL') const response = res.data switch (response.code) { case DATABASE_SELECT_SUCCESS: void message.loading({ content: '编译中……', key: 'COMPILING', duration: 0 }) try { const toolVo = response.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) => { message.destroy('COMPILING') void message.loading({ content: '发布中……', key: 'UPLOADING', duration: 0 }) void r_sys_tool_pass(value.id, { dist: strToBase64( result.outputFiles[0].text ) }) .then((res) => { message.destroy('UPLOADING') const response = res.data switch (response.code) { case DATABASE_UPDATE_SUCCESS: void message.success( '发布成功' ) getTool() break case TOOL_NOT_UNDER_REVIEW: void message.warning( '工具处于非审核状态' ) break default: void message.error( '发布失败,请稍后重试' ) } }) .catch(() => { message.destroy('UPLOADING') }) .finally(() => { resolve() }) }) } catch (e) { resolve() message.destroy('COMPILING') void message.error( '编译失败,请检查代码后重试' ) } break default: resolve() void message.error('加载工具失败,稍后重试') } }) .catch(() => { message.destroy('LOADING_TOOL') 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() } }) .finally(() => { getTool() }) } }) }) }) } } const handleOnOffShelveBtnClick = (value: ToolVo) => { return () => { modal .confirm({ centered: true, maskClosable: true, 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({ centered: true, maskClosable: true, title: '确定删除', content: `确定删除工具 ${value.author.username}:${value.toolId}:${value.platform.slice(0, 1)}${value.platform.slice(1).toLowerCase()}:${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) => { 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 = ( 全部 名称 工具 ID 昵称 用户名 关键词 } suffix={ <> {!isRegexLegal && ( 非法表达式 )} .* } allowClear value={searchValue} onChange={handleOnSearchValueChange} onKeyDown={handleOnSearchValueKeyDown} status={isRegexLegal ? undefined : 'error'} placeholder={'请输入搜索内容'} /> 查询 ) const table = ( ) return ( {toolbar} {table} ) } export default Tools