252 lines
7.7 KiB
TypeScript
252 lines
7.7 KiB
TypeScript
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import {
|
|
App,
|
|
Button,
|
|
Card,
|
|
Form,
|
|
Input,
|
|
InputNumber,
|
|
Modal,
|
|
Space,
|
|
Switch,
|
|
Table,
|
|
Tag,
|
|
Typography,
|
|
} from 'antd'
|
|
import type { TableColumnsType } from 'antd'
|
|
import { useState } from 'react'
|
|
|
|
import {
|
|
deleteAdminWorkerLevel,
|
|
fetchAdminWorkerLevels,
|
|
saveAdminWorkerLevel,
|
|
} from '@/services/admin'
|
|
import type { WorkerLevel } from '@/types/worker-platform'
|
|
import { formatMoney } from './shared'
|
|
|
|
export default function LevelsPanel() {
|
|
const { message } = App.useApp()
|
|
const queryClient = useQueryClient()
|
|
const levelsQuery = useQuery({
|
|
queryKey: ['admin-worker-platform-levels'],
|
|
queryFn: () => fetchAdminWorkerLevels(),
|
|
})
|
|
const [form] = Form.useForm()
|
|
const [editingLevel, setEditingLevel] = useState<WorkerLevel | null>(null)
|
|
const [deletingLevel, setDeletingLevel] = useState<WorkerLevel | null>(null)
|
|
|
|
async function refreshLevels() {
|
|
await levelsQuery.refetch()
|
|
}
|
|
|
|
function startEdit(level: WorkerLevel) {
|
|
setEditingLevel(level)
|
|
form.setFieldsValue({
|
|
levelKey: level.levelKey,
|
|
name: level.name,
|
|
depositFreeAmount: level.depositFreeAmount,
|
|
maxActiveOrders: level.maxActiveOrders,
|
|
upgradeThreshold: level.upgradeThreshold,
|
|
autoAcceptWithoutEvidence: level.autoAcceptWithoutEvidence,
|
|
visibleDelaySeconds: level.visibleDelaySeconds,
|
|
})
|
|
}
|
|
|
|
function cancelEdit() {
|
|
setEditingLevel(null)
|
|
form.resetFields()
|
|
}
|
|
|
|
async function saveLevel(values: {
|
|
levelKey: string
|
|
name: string
|
|
depositFreeAmount?: number
|
|
maxActiveOrders?: number
|
|
upgradeThreshold?: number
|
|
autoAcceptWithoutEvidence?: boolean
|
|
visibleDelaySeconds?: number
|
|
}) {
|
|
try {
|
|
await saveAdminWorkerLevel(values)
|
|
message.success(editingLevel ? '等级已更新' : '等级已保存')
|
|
cancelEdit()
|
|
await refreshLevels()
|
|
} catch (error) {
|
|
message.error(error instanceof Error ? error.message : '保存失败')
|
|
}
|
|
}
|
|
|
|
async function toggleLevel(level: WorkerLevel) {
|
|
try {
|
|
await saveAdminWorkerLevel({
|
|
levelKey: level.levelKey,
|
|
name: level.name,
|
|
depositFreeAmount: level.depositFreeAmount,
|
|
maxActiveOrders: level.maxActiveOrders,
|
|
upgradeThreshold: level.upgradeThreshold,
|
|
autoAcceptWithoutEvidence: level.autoAcceptWithoutEvidence,
|
|
visibleDelaySeconds: level.visibleDelaySeconds,
|
|
status: level.status === 'active' ? 'disabled' : 'active',
|
|
})
|
|
message.success(level.status === 'active' ? '等级已停用' : '等级已启用')
|
|
await refreshLevels()
|
|
} catch (error) {
|
|
message.error(error instanceof Error ? error.message : '操作失败')
|
|
}
|
|
}
|
|
|
|
async function submitDelete() {
|
|
if (!deletingLevel) return
|
|
try {
|
|
await deleteAdminWorkerLevel(deletingLevel.levelId)
|
|
message.success('等级已删除')
|
|
setDeletingLevel(null)
|
|
await refreshLevels()
|
|
await queryClient.invalidateQueries({
|
|
queryKey: ['admin-worker-platform-workers'],
|
|
})
|
|
} catch (error) {
|
|
message.error(error instanceof Error ? error.message : '删除失败')
|
|
setDeletingLevel(null)
|
|
}
|
|
}
|
|
|
|
const columns: TableColumnsType<WorkerLevel> = [
|
|
{ title: '等级', dataIndex: 'name' },
|
|
{ title: '标识', dataIndex: 'levelKey' },
|
|
{
|
|
title: '免押额度',
|
|
render: (_, row) => formatMoney(row.depositFreeAmount),
|
|
},
|
|
{ title: '最大同时接单', dataIndex: 'maxActiveOrders' },
|
|
{ title: '升级所需累计完成', dataIndex: 'upgradeThreshold' },
|
|
{
|
|
title: '免审核后补图',
|
|
width: 130,
|
|
render: (_, row) =>
|
|
row.autoAcceptWithoutEvidence ? <Tag color="purple">开启</Tag> : <Tag>关闭</Tag>,
|
|
},
|
|
{
|
|
title: '可见延迟',
|
|
width: 110,
|
|
render: (_, row) => (row.visibleDelaySeconds > 0 ? `${row.visibleDelaySeconds} 秒` : '立即'),
|
|
},
|
|
{
|
|
title: '状态',
|
|
width: 110,
|
|
render: (_, row) => (
|
|
<Tag color={row.status === 'active' ? 'green' : 'default'}>
|
|
{row.status === 'active' ? '启用' : '停用'}
|
|
</Tag>
|
|
),
|
|
},
|
|
{
|
|
title: '操作',
|
|
width: 240,
|
|
render: (_, row) => (
|
|
<Space wrap>
|
|
<Button size="small" onClick={() => startEdit(row)}>
|
|
编辑
|
|
</Button>
|
|
<Button size="small" onClick={() => toggleLevel(row)}>
|
|
{row.status === 'active' ? '停用' : '启用'}
|
|
</Button>
|
|
<Button size="small" danger onClick={() => setDeletingLevel(row)}>
|
|
删除
|
|
</Button>
|
|
</Space>
|
|
),
|
|
},
|
|
]
|
|
|
|
return (
|
|
<section className="platform-panel-stack">
|
|
<Card title={editingLevel ? `编辑等级:${editingLevel.name}` : '保存等级'} bordered={false}>
|
|
<Form
|
|
form={form}
|
|
layout="inline"
|
|
initialValues={{
|
|
levelKey: 'vip1',
|
|
name: 'VIP1',
|
|
depositFreeAmount: 0,
|
|
maxActiveOrders: 1,
|
|
upgradeThreshold: 0,
|
|
autoAcceptWithoutEvidence: false,
|
|
visibleDelaySeconds: 20,
|
|
}}
|
|
onFinish={saveLevel}
|
|
>
|
|
<Form.Item
|
|
label="标识"
|
|
name="levelKey"
|
|
rules={[{ required: true, message: '请输入标识' }]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label="名称" name="name" rules={[{ required: true, message: '请输入名称' }]}>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label="免押额度" name="depositFreeAmount">
|
|
<InputNumber min={0} step={10} addonAfter="元" />
|
|
</Form.Item>
|
|
<Form.Item label="最大同时接单" name="maxActiveOrders">
|
|
<InputNumber min={1} max={999} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label="升级所需累计完成单数"
|
|
name="upgradeThreshold"
|
|
tooltip="打手累计验收通过该单数后自动升级到本等级"
|
|
>
|
|
<InputNumber min={0} max={100000} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label="免审核后补图"
|
|
name="autoAcceptWithoutEvidence"
|
|
valuePropName="checked"
|
|
tooltip="开启后,打手可无验收图提交并自动验收;验收后仅可在 24 小时内补图。"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
<Form.Item
|
|
label="大厅可见延迟(秒)"
|
|
name="visibleDelaySeconds"
|
|
tooltip="发布到大厅后该等级打手延迟可见秒数,默认 VIP1=20 / VIP2=15 / VIP3=10 / VIP4=5 / VIP5=0,填 -1 使用默认"
|
|
>
|
|
<InputNumber min={-1} max={3600} />
|
|
</Form.Item>
|
|
<Form.Item>
|
|
<Space>
|
|
<Button type="primary" htmlType="submit">
|
|
保存
|
|
</Button>
|
|
{editingLevel ? <Button onClick={cancelEdit}>取消</Button> : null}
|
|
</Space>
|
|
</Form.Item>
|
|
</Form>
|
|
</Card>
|
|
<Card title="等级列表" bordered={false}>
|
|
<Table<WorkerLevel>
|
|
rowKey="levelId"
|
|
loading={levelsQuery.isLoading}
|
|
dataSource={levelsQuery.data?.data.items || []}
|
|
columns={columns}
|
|
pagination={false}
|
|
/>
|
|
</Card>
|
|
<Modal
|
|
title="删除等级"
|
|
open={Boolean(deletingLevel)}
|
|
onOk={submitDelete}
|
|
onCancel={() => setDeletingLevel(null)}
|
|
okButtonProps={{ danger: true }}
|
|
okText="删除"
|
|
cancelText="取消"
|
|
>
|
|
<Typography.Text>
|
|
确定删除等级「{deletingLevel?.name}」?正在使用该等级的打手将无法删除,需先调整。
|
|
</Typography.Text>
|
|
</Modal>
|
|
</section>
|
|
)
|
|
}
|