接单平台分类管理完善:抢单大厅分类筛选与分类 CRUD
- 抢单大厅新增分类 Tab 筛选(仅显示启用分类,支持按分类查询工单) - 管理端分类面板支持编辑、启用/停用切换、删除(被工单/规则引用时禁止删除) - 后端工单列表支持 categoryId 筛选,大厅接口返回分类列表
This commit is contained in:
@@ -166,6 +166,7 @@ type ListInput = {
|
|||||||
status?: string
|
status?: string
|
||||||
keyword?: string
|
keyword?: string
|
||||||
workerId?: number
|
workerId?: number
|
||||||
|
categoryId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductRuleListInput = {
|
type ProductRuleListInput = {
|
||||||
@@ -912,6 +913,14 @@ export async function getWorkCategoryByKey(categoryKey: string): Promise<WorkCat
|
|||||||
return result.rows[0] || null
|
return result.rows[0] || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getWorkCategoryById(categoryId: number | string): Promise<WorkCategoryRow | null> {
|
||||||
|
const result = await query<WorkCategoryRow>(
|
||||||
|
'SELECT * FROM work_categories WHERE id = $1 LIMIT 1',
|
||||||
|
[Number(categoryId)],
|
||||||
|
)
|
||||||
|
return result.rows[0] || null
|
||||||
|
}
|
||||||
|
|
||||||
export async function listWorkCategories(): Promise<WorkCategoryRow[]> {
|
export async function listWorkCategories(): Promise<WorkCategoryRow[]> {
|
||||||
const result = await query<WorkCategoryRow>(
|
const result = await query<WorkCategoryRow>(
|
||||||
`SELECT * FROM work_categories WHERE status = 'active' ORDER BY sort_order ASC, id ASC`,
|
`SELECT * FROM work_categories WHERE status = 'active' ORDER BY sort_order ASC, id ASC`,
|
||||||
@@ -946,6 +955,36 @@ export async function upsertWorkCategory(input: {
|
|||||||
return result.rows[0] || null
|
return result.rows[0] || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function countWorkCategoryUsages(categoryId: number | string): Promise<{
|
||||||
|
workOrderCount: number
|
||||||
|
productRuleCount: number
|
||||||
|
}> {
|
||||||
|
const [orders, rules] = await Promise.all([
|
||||||
|
query<{ total: number }>(
|
||||||
|
'SELECT COUNT(*)::int AS total FROM work_orders WHERE category_id = $1',
|
||||||
|
[Number(categoryId)],
|
||||||
|
),
|
||||||
|
query<{ total: number }>(
|
||||||
|
'SELECT COUNT(*)::int AS total FROM work_product_rules WHERE category_id = $1',
|
||||||
|
[Number(categoryId)],
|
||||||
|
),
|
||||||
|
])
|
||||||
|
return {
|
||||||
|
workOrderCount: Number(orders.rows[0]?.total || 0),
|
||||||
|
productRuleCount: Number(rules.rows[0]?.total || 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteWorkCategory(
|
||||||
|
categoryId: number | string,
|
||||||
|
): Promise<{ deleted: boolean }> {
|
||||||
|
const result = await query<{ id: number }>(
|
||||||
|
'DELETE FROM work_categories WHERE id = $1 RETURNING id',
|
||||||
|
[Number(categoryId)],
|
||||||
|
)
|
||||||
|
return { deleted: Boolean(result.rows[0]) }
|
||||||
|
}
|
||||||
|
|
||||||
export async function listWorkProductRules({
|
export async function listWorkProductRules({
|
||||||
enabled = null,
|
enabled = null,
|
||||||
keyword = '',
|
keyword = '',
|
||||||
@@ -1123,8 +1162,14 @@ export async function listWorkOrders({
|
|||||||
status = '',
|
status = '',
|
||||||
keyword = '',
|
keyword = '',
|
||||||
workerId = 0,
|
workerId = 0,
|
||||||
|
categoryId = 0,
|
||||||
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
||||||
const { whereClause, params } = buildWorkOrderWhere({ status, keyword, workerId })
|
const { whereClause, params } = buildWorkOrderWhere({
|
||||||
|
status,
|
||||||
|
keyword,
|
||||||
|
workerId,
|
||||||
|
categoryId,
|
||||||
|
})
|
||||||
const totalResult = await query<{ total: number }>(
|
const totalResult = await query<{ total: number }>(
|
||||||
`
|
`
|
||||||
SELECT COUNT(*)::int AS total
|
SELECT COUNT(*)::int AS total
|
||||||
@@ -1713,7 +1758,12 @@ function buildWorkerUserWhere({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildWorkOrderWhere({ status = '', keyword = '', workerId = 0 }: ListInput) {
|
function buildWorkOrderWhere({
|
||||||
|
status = '',
|
||||||
|
keyword = '',
|
||||||
|
workerId = 0,
|
||||||
|
categoryId = 0,
|
||||||
|
}: ListInput) {
|
||||||
const filters: string[] = []
|
const filters: string[] = []
|
||||||
const params: unknown[] = []
|
const params: unknown[] = []
|
||||||
if (status) {
|
if (status) {
|
||||||
@@ -1730,6 +1780,10 @@ function buildWorkOrderWhere({ status = '', keyword = '', workerId = 0 }: ListIn
|
|||||||
params.push(workerId)
|
params.push(workerId)
|
||||||
filters.push(`wo.assigned_worker_id = $${params.length}`)
|
filters.push(`wo.assigned_worker_id = $${params.length}`)
|
||||||
}
|
}
|
||||||
|
if (categoryId) {
|
||||||
|
params.push(categoryId)
|
||||||
|
filters.push(`wo.category_id = $${params.length}`)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
|
whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
acceptAdminWorkOrder,
|
acceptAdminWorkOrder,
|
||||||
creditAdminWorkerWallet,
|
creditAdminWorkerWallet,
|
||||||
createAdminMockWorkOrder,
|
createAdminMockWorkOrder,
|
||||||
|
deleteAdminWorkCategory,
|
||||||
getAdminWorkerFinanceConfig,
|
getAdminWorkerFinanceConfig,
|
||||||
getAdminWorkerPlatformSummary,
|
getAdminWorkerPlatformSummary,
|
||||||
listAdminWorkerFinanceRequests,
|
listAdminWorkerFinanceRequests,
|
||||||
@@ -92,6 +93,22 @@ router.post(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/worker-platform/categories/:categoryId',
|
||||||
|
requireAdminRoles(['admin', 'operator']),
|
||||||
|
createJsonHandler((req) => deleteAdminWorkCategory(String(req.params.categoryId || '')), {
|
||||||
|
successMessage: '接单分类已删除',
|
||||||
|
errorMessage: '删除接单分类失败',
|
||||||
|
scope: '[admin/worker-platform/categories/:categoryId]',
|
||||||
|
audit: (_req, data) => ({
|
||||||
|
action: 'work_category_deleted',
|
||||||
|
targetType: 'work_category',
|
||||||
|
targetId: String(_req.params.categoryId || ''),
|
||||||
|
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/worker-platform/product-rules',
|
'/worker-platform/product-rules',
|
||||||
requireAdminRoles(['admin', 'operator', 'support']),
|
requireAdminRoles(['admin', 'operator', 'support']),
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ import {
|
|||||||
addWorkerWalletCredit,
|
addWorkerWalletCredit,
|
||||||
countWorkerAcceptedOrders,
|
countWorkerAcceptedOrders,
|
||||||
countWorkerActiveOrders,
|
countWorkerActiveOrders,
|
||||||
|
countWorkCategoryUsages,
|
||||||
createWorkOrder,
|
createWorkOrder,
|
||||||
createWorkOrderEvent,
|
createWorkOrderEvent,
|
||||||
createWorkerFinanceRequest,
|
createWorkerFinanceRequest,
|
||||||
createWorkerUser,
|
createWorkerUser,
|
||||||
|
deleteWorkCategory,
|
||||||
getWorkerFinanceRequestById,
|
getWorkerFinanceRequestById,
|
||||||
getWorkerFinanceRequestSummary,
|
getWorkerFinanceRequestSummary,
|
||||||
|
getWorkCategoryById,
|
||||||
getWorkerLevelById,
|
getWorkerLevelById,
|
||||||
getWorkerLevelByKey,
|
getWorkerLevelByKey,
|
||||||
getWorkerUserByInviteCode,
|
getWorkerUserByInviteCode,
|
||||||
@@ -28,6 +31,7 @@ import {
|
|||||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||||
listWorkerWalletLedgers,
|
listWorkerWalletLedgers,
|
||||||
grabWorkOrder,
|
grabWorkOrder,
|
||||||
|
listWorkCategories,
|
||||||
listWorkOrders,
|
listWorkOrders,
|
||||||
listWorkProductRules,
|
listWorkProductRules,
|
||||||
listWorkerLevels,
|
listWorkerLevels,
|
||||||
@@ -627,17 +631,21 @@ export async function listWorkerHallOrders(query: JsonObject = {}, session: Work
|
|||||||
requireActiveWorkerSession(session)
|
requireActiveWorkerSession(session)
|
||||||
const page = normalizePage(query.page)
|
const page = normalizePage(query.page)
|
||||||
const pageSize = normalizePageSize(query.pageSize)
|
const pageSize = normalizePageSize(query.pageSize)
|
||||||
|
const categoryId = normalizeOptionalId(query.categoryId)
|
||||||
const { items, total } = await listWorkOrders({
|
const { items, total } = await listWorkOrders({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
status: WORK_ORDER_STATUS.OPEN,
|
status: WORK_ORDER_STATUS.OPEN,
|
||||||
keyword: String(query.keyword || '').trim(),
|
keyword: String(query.keyword || '').trim(),
|
||||||
|
categoryId: categoryId || 0,
|
||||||
})
|
})
|
||||||
const worker = await getRequiredWorker(session.workerId)
|
const worker = await getRequiredWorker(session.workerId)
|
||||||
const permissions = resolveWorkerPermissions(worker)
|
const permissions = resolveWorkerPermissions(worker)
|
||||||
|
const categories = (await listWorkCategories()).map(mapWorkCategory)
|
||||||
return {
|
return {
|
||||||
items: items.map((item) => mapWorkOrderForWorker(item, permissions)),
|
items: items.map((item) => mapWorkOrderForWorker(item, permissions)),
|
||||||
pagination: { page, pageSize, total },
|
pagination: { page, pageSize, total },
|
||||||
|
categories,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -880,6 +888,28 @@ export async function saveAdminWorkCategory(payload: JsonObject = {}) {
|
|||||||
return { category: mapWorkCategory(category) }
|
return { category: mapWorkCategory(category) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteAdminWorkCategory(categoryId: number | string) {
|
||||||
|
const category = await getWorkCategoryById(Number(categoryId))
|
||||||
|
if (!category) {
|
||||||
|
throw createHttpError('分类不存在', {
|
||||||
|
statusCode: 404,
|
||||||
|
errorCode: 'work_category_not_found',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const usages = await countWorkCategoryUsages(Number(categoryId))
|
||||||
|
if (usages.workOrderCount > 0 || usages.productRuleCount > 0) {
|
||||||
|
throw createHttpError(
|
||||||
|
`该分类已被 ${usages.workOrderCount} 个工单和 ${usages.productRuleCount} 条物品规则使用,无法删除,请先停用`,
|
||||||
|
{
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'work_category_in_use',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const { deleted } = await deleteWorkCategory(Number(categoryId))
|
||||||
|
return { deleted }
|
||||||
|
}
|
||||||
|
|
||||||
export async function saveAdminWorkerLevel(payload: JsonObject = {}) {
|
export async function saveAdminWorkerLevel(payload: JsonObject = {}) {
|
||||||
const now = nowIso()
|
const now = nowIso()
|
||||||
const levelKey = String(payload.levelKey || payload.level_key || '').trim() || DEFAULT_LEVEL_KEY
|
const levelKey = String(payload.levelKey || payload.level_key || '').trim() || DEFAULT_LEVEL_KEY
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
acceptAdminWorkOrder,
|
acceptAdminWorkOrder,
|
||||||
createAdminMockWorkOrder,
|
createAdminMockWorkOrder,
|
||||||
creditAdminWorkerWallet,
|
creditAdminWorkerWallet,
|
||||||
|
deleteAdminWorkCategory,
|
||||||
fetchAdminWorkerFinanceConfig,
|
fetchAdminWorkerFinanceConfig,
|
||||||
fetchAdminWorkerFinanceRequests,
|
fetchAdminWorkerFinanceRequests,
|
||||||
fetchAdminWorkCategories,
|
fetchAdminWorkCategories,
|
||||||
@@ -1034,11 +1035,34 @@ function CategoriesPanel() {
|
|||||||
const { message } = App.useApp()
|
const { message } = App.useApp()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
|
const [editingCategory, setEditingCategory] = useState<WorkCategory | null>(null)
|
||||||
|
const [deletingCategory, setDeletingCategory] = useState<WorkCategory | null>(null)
|
||||||
const categoriesQuery = useQuery({
|
const categoriesQuery = useQuery({
|
||||||
queryKey: ['admin-worker-platform-categories'],
|
queryKey: ['admin-worker-platform-categories'],
|
||||||
queryFn: () => fetchAdminWorkCategories(),
|
queryFn: () => fetchAdminWorkCategories(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function refreshCategories() {
|
||||||
|
await queryClient.invalidateQueries({
|
||||||
|
queryKey: ['admin-worker-platform-categories'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEdit(category: WorkCategory) {
|
||||||
|
setEditingCategory(category)
|
||||||
|
form.setFieldsValue({
|
||||||
|
categoryKey: category.categoryKey,
|
||||||
|
name: category.name,
|
||||||
|
sortOrder: category.sortOrder,
|
||||||
|
status: category.status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEdit() {
|
||||||
|
setEditingCategory(null)
|
||||||
|
form.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
async function saveCategory(values: {
|
async function saveCategory(values: {
|
||||||
categoryKey: string
|
categoryKey: string
|
||||||
name: string
|
name: string
|
||||||
@@ -1047,16 +1071,42 @@ function CategoriesPanel() {
|
|||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
await saveAdminWorkCategory(values)
|
await saveAdminWorkCategory(values)
|
||||||
message.success('分类已保存')
|
message.success(editingCategory ? '分类已更新' : '分类已保存')
|
||||||
form.resetFields()
|
cancelEdit()
|
||||||
await queryClient.invalidateQueries({
|
await refreshCategories()
|
||||||
queryKey: ['admin-worker-platform-categories'],
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(error instanceof Error ? error.message : '保存失败')
|
message.error(error instanceof Error ? error.message : '保存失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function toggleCategory(category: WorkCategory) {
|
||||||
|
try {
|
||||||
|
await saveAdminWorkCategory({
|
||||||
|
categoryKey: category.categoryKey,
|
||||||
|
name: category.name,
|
||||||
|
sortOrder: category.sortOrder,
|
||||||
|
status: category.status === 'active' ? 'disabled' : 'active',
|
||||||
|
})
|
||||||
|
message.success(category.status === 'active' ? '分类已停用' : '分类已启用')
|
||||||
|
await refreshCategories()
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error instanceof Error ? error.message : '操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitDelete() {
|
||||||
|
if (!deletingCategory) return
|
||||||
|
try {
|
||||||
|
await deleteAdminWorkCategory(deletingCategory.categoryId)
|
||||||
|
message.success('分类已删除')
|
||||||
|
setDeletingCategory(null)
|
||||||
|
await refreshCategories()
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error instanceof Error ? error.message : '删除失败')
|
||||||
|
setDeletingCategory(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns: TableColumnsType<WorkCategory> = [
|
const columns: TableColumnsType<WorkCategory> = [
|
||||||
{ title: '分类', dataIndex: 'name' },
|
{ title: '分类', dataIndex: 'name' },
|
||||||
{ title: '标识', dataIndex: 'categoryKey' },
|
{ title: '标识', dataIndex: 'categoryKey' },
|
||||||
@@ -1070,11 +1120,28 @@ function CategoriesPanel() {
|
|||||||
</Tag>
|
</Tag>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 240,
|
||||||
|
render: (_, row) => (
|
||||||
|
<Space wrap>
|
||||||
|
<Button size="small" onClick={() => startEdit(row)}>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button size="small" onClick={() => toggleCategory(row)}>
|
||||||
|
{row.status === 'active' ? '停用' : '启用'}
|
||||||
|
</Button>
|
||||||
|
<Button size="small" danger onClick={() => setDeletingCategory(row)}>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="platform-panel-stack">
|
<section className="platform-panel-stack">
|
||||||
<Card title="保存分类" bordered={false}>
|
<Card title={editingCategory ? `编辑分类:${editingCategory.name}` : '保存分类'} bordered={false}>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="inline"
|
layout="inline"
|
||||||
@@ -1108,9 +1175,12 @@ function CategoriesPanel() {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
|
<Space>
|
||||||
<Button type="primary" htmlType="submit">
|
<Button type="primary" htmlType="submit">
|
||||||
保存
|
保存
|
||||||
</Button>
|
</Button>
|
||||||
|
{editingCategory ? <Button onClick={cancelEdit}>取消</Button> : null}
|
||||||
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1123,6 +1193,19 @@ function CategoriesPanel() {
|
|||||||
pagination={false}
|
pagination={false}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Modal
|
||||||
|
title="删除分类"
|
||||||
|
open={Boolean(deletingCategory)}
|
||||||
|
onOk={submitDelete}
|
||||||
|
onCancel={() => setDeletingCategory(null)}
|
||||||
|
okButtonProps={{ danger: true }}
|
||||||
|
okText="删除"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Typography.Text>
|
||||||
|
确定删除分类「{deletingCategory?.name}」?已引用该分类的工单和物品规则将无法删除,需先处理。
|
||||||
|
</Typography.Text>
|
||||||
|
</Modal>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Pagination,
|
Pagination,
|
||||||
Space,
|
Space,
|
||||||
Spin,
|
Spin,
|
||||||
|
Tabs,
|
||||||
Tag,
|
Tag,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
@@ -37,14 +38,16 @@ export default function WorkerHallPage() {
|
|||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const [keywordInput, setKeywordInput] = useState('')
|
const [keywordInput, setKeywordInput] = useState('')
|
||||||
const [keyword, setKeyword] = useState('')
|
const [keyword, setKeyword] = useState('')
|
||||||
|
const [categoryId, setCategoryId] = useState(0)
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [pageSize, setPageSize] = useState(20)
|
const [pageSize, setPageSize] = useState(20)
|
||||||
const [grabbingId, setGrabbingId] = useState(0)
|
const [grabbingId, setGrabbingId] = useState(0)
|
||||||
const [detailOrder, setDetailOrder] = useState<WorkOrder | null>(null)
|
const [detailOrder, setDetailOrder] = useState<WorkOrder | null>(null)
|
||||||
|
|
||||||
const ordersQuery = useQuery({
|
const ordersQuery = useQuery({
|
||||||
queryKey: ['worker-hall-orders', keyword, page, pageSize],
|
queryKey: ['worker-hall-orders', keyword, categoryId, page, pageSize],
|
||||||
queryFn: () => fetchWorkerHallOrders({ keyword, page, pageSize }),
|
queryFn: () =>
|
||||||
|
fetchWorkerHallOrders({ keyword, page, pageSize, categoryId: categoryId || undefined }),
|
||||||
retry: false,
|
retry: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ export default function WorkerHallPage() {
|
|||||||
|
|
||||||
const orders = ordersQuery.data?.data.items || []
|
const orders = ordersQuery.data?.data.items || []
|
||||||
const pagination = ordersQuery.data?.data.pagination
|
const pagination = ordersQuery.data?.data.pagination
|
||||||
|
const categories = ordersQuery.data?.data.categories || []
|
||||||
const worker = profileQuery.data?.data.worker
|
const worker = profileQuery.data?.data.worker
|
||||||
const activeCount = hallSummaryQuery.data?.activeCount || 0
|
const activeCount = hallSummaryQuery.data?.activeCount || 0
|
||||||
const pendingAcceptanceCount = hallSummaryQuery.data?.pendingAcceptanceCount || 0
|
const pendingAcceptanceCount = hallSummaryQuery.data?.pendingAcceptanceCount || 0
|
||||||
@@ -210,6 +214,24 @@ export default function WorkerHallPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{categories.length > 0 ? (
|
||||||
|
<Tabs
|
||||||
|
size="small"
|
||||||
|
activeKey={String(categoryId)}
|
||||||
|
items={[
|
||||||
|
{ key: '0', label: '全部' },
|
||||||
|
...categories.map((category) => ({
|
||||||
|
key: String(category.categoryId),
|
||||||
|
label: category.name,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
onChange={(nextKey) => {
|
||||||
|
setCategoryId(Number(nextKey) || 0)
|
||||||
|
setPage(1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{shouldWarn ? (
|
{shouldWarn ? (
|
||||||
<Alert
|
<Alert
|
||||||
className="worker-hall-warning"
|
className="worker-hall-warning"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { apiGet, apiPost } from '@/lib/http'
|
import { apiDelete, apiGet, apiPost } from '@/lib/http'
|
||||||
import type {
|
import type {
|
||||||
WorkCategory,
|
WorkCategory,
|
||||||
WorkOrder,
|
WorkOrder,
|
||||||
@@ -43,6 +43,12 @@ export function saveAdminWorkCategory(payload: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deleteAdminWorkCategory(categoryId: number) {
|
||||||
|
return apiDelete<{ deleted: boolean }>(
|
||||||
|
`/api/v1/admin/worker-platform/categories/${categoryId}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function saveAdminWorkerLevel(payload: {
|
export function saveAdminWorkerLevel(payload: {
|
||||||
levelKey: string
|
levelKey: string
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
UploadedFile,
|
UploadedFile,
|
||||||
WorkOrder,
|
WorkOrder,
|
||||||
WorkerFinanceRequest,
|
WorkerFinanceRequest,
|
||||||
|
WorkerHallOrdersResponse,
|
||||||
WorkerListResponse,
|
WorkerListResponse,
|
||||||
WorkerLoginResponse,
|
WorkerLoginResponse,
|
||||||
WorkerProfileResponse,
|
WorkerProfileResponse,
|
||||||
@@ -92,7 +93,7 @@ export function changeWorkerPassword(payload: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fetchWorkerHallOrders(params?: Record<string, unknown>) {
|
export function fetchWorkerHallOrders(params?: Record<string, unknown>) {
|
||||||
return apiGet<WorkerListResponse<WorkOrder>>(
|
return apiGet<WorkerHallOrdersResponse>(
|
||||||
'/api/v1/worker/hall/orders',
|
'/api/v1/worker/hall/orders',
|
||||||
params,
|
params,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -224,6 +224,12 @@ export type WorkerListResponse<T> = {
|
|||||||
pagination: Pagination
|
pagination: Pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WorkerHallOrdersResponse = {
|
||||||
|
items: WorkOrder[]
|
||||||
|
pagination: Pagination
|
||||||
|
categories: WorkCategory[]
|
||||||
|
}
|
||||||
|
|
||||||
export type CollectField = {
|
export type CollectField = {
|
||||||
key: string
|
key: string
|
||||||
label: string
|
label: string
|
||||||
|
|||||||
Reference in New Issue
Block a user