feat(consignment): 补齐寄卖前端权限门控,收口调拨状态与运营体验

权限门控(P1-8 剩余):
- 新增 resolveAdminPermission(sessionPermissions, code):会话接口的最新权限列表优先,
  接口异常才回退登录时的本地快照;空数组也判无权限,避免"刚被收回权限仍能点"。
- 寄售仓库面板:consignment.manage 控制手动入库/编辑/作废/退单回库/匹配开关(无权限时开关禁用)。
- 寄卖提报面板:consignment.review 控制审核入库/驳回。
- 接单平台页:无 consignment.view 不渲染两个寄卖 tab,并把 URL/localStorage 的旧选择拉回工单页。

数据约束(P2-19 剩余):
- 迁移 080:allocation_status 历史非法值归位「待安排」(备注留痕)后加 CHECK(待安排/已安排),
  并顺手修正 077 遗留的"收集链接/玩家提交"权限描述文案。
- 编辑接口拒绝直接改 allocationStatus(400 consignment_allocation_status_update_forbidden)。

运营体验:
- 两个面板补 isError 错误态与重试;审核通过/驳回加 confirmLoading 与提交中守卫(防双提交)。
- 待审徽标改用后端真实总数(原来只统计当前页);编辑弹窗押金不再兜底 ¥100(会误写押金)。
- 履约拦截弹窗冷静期文案不再硬编码"3 天",改为按商品规则设定。
- 核实并撤回"倒计时时区偏移"误报:deadlineAt 为带 Z 的 ISO,前端无偏移问题。

验证:后端 412 例(409 通过)、前端 21 例(新增 5 例权限解析)全绿,
typecheck / lint / format / check:sql / build 通过;迁移 001→080 在全新空库跑通并在 dev 库应用。
This commit is contained in:
yml2213
2026-09-16 20:39:52 +08:00
parent 5d11bf2012
commit 563e548b2e
10 changed files with 346 additions and 61 deletions
@@ -6,6 +6,7 @@ import {
} from '@ant-design/icons'
import { useQuery } from '@tanstack/react-query'
import {
Alert,
Badge,
Button,
Card,
@@ -33,8 +34,10 @@ import {
fetchAdminConsignmentSubmissions,
rejectAdminConsignmentSubmission,
} from '@/services/admin/consignment'
import { fetchAdminSession } from '@/services/admin/auth'
import { fetchAdminWorkerUsers } from '@/services/admin/worker-platform'
import type { ConsignmentSubmission } from '@/types/consignment'
import { resolveAdminPermission } from '@/utils/admin-auth'
const { Text } = Typography
@@ -51,6 +54,17 @@ export default function ConsignmentSubmissionsPanel() {
const [viewingSubmission, setViewingSubmission] = useState<ConsignmentSubmission | null>(null)
const [rejectNote, setRejectNote] = useState('')
const [approveForm] = Form.useForm()
const [approving, setApproving] = useState(false)
const [rejecting, setRejecting] = useState(false)
// 通过/驳回要 consignment.review:后端接口已挂权限,前端不隐藏会让人点了必 403
const adminSessionQuery = useQuery({
queryKey: ['admin-session'],
queryFn: fetchAdminSession,
retry: false,
})
const sessionPermissions = adminSessionQuery.data?.data.user.permissions
const canReviewConsignment = resolveAdminPermission(sessionPermissions, 'consignment.review')
const workersQuery = useQuery({
queryKey: ['admin-worker-users-consignment-options'],
@@ -82,10 +96,23 @@ export default function ConsignmentSubmissionsPanel() {
const submissions = submissionsQuery.data?.data.items || []
const submissionTotal = submissionsQuery.data?.data.total || 0
const pendingCount = submissions.filter((s) => s.status === 'pending_review').length
// 待审徽标必须用后端总数:原来只统计当前页(pageSize=20 时最多显示 20,且翻页就变)。
const pendingTotalQuery = useQuery({
queryKey: ['admin-consignment-submissions-pending-total'],
queryFn: () =>
fetchAdminConsignmentSubmissions({
status: 'pending_review',
page: 1,
pageSize: 1,
}),
refetchInterval: 60_000,
})
const pendingCount = pendingTotalQuery.data?.data.total || 0
async function handleApproveSubmit() {
if (!approveModalItem) return
if (!approveModalItem || approving) return
setApproving(true)
try {
const values = await approveForm.validateFields()
const costPriceFen = Math.round(Number(values.costPrice || 0) * 100)
@@ -118,23 +145,28 @@ export default function ConsignmentSubmissionsPanel() {
message.success('审核通过!已正式存入寄售库存待匹配!')
setApproveModalItem(null)
await submissionsQuery.refetch()
await Promise.all([submissionsQuery.refetch(), pendingTotalQuery.refetch()])
} catch (err: any) {
if (err.errorFields) return
message.error(err.message || '审核入库失败')
} finally {
setApproving(false)
}
}
async function handleRejectConfirm() {
if (!rejectModalItem) return
if (!rejectModalItem || rejecting) return
setRejecting(true)
try {
await rejectAdminConsignmentSubmission(rejectModalItem.id, { reviewNote: rejectNote })
message.success('已驳回该提报申请')
setRejectModalItem(null)
setRejectNote('')
await submissionsQuery.refetch()
await Promise.all([submissionsQuery.refetch(), pendingTotalQuery.refetch()])
} catch (err: any) {
message.error(err.message || '驳回失败')
} finally {
setRejecting(false)
}
}
@@ -256,7 +288,7 @@ export default function ConsignmentSubmissionsPanel() {
>
详情
</Button>
{record.status === 'pending_review' ? (
{record.status === 'pending_review' && canReviewConsignment ? (
<>
<Button
size="small"
@@ -338,6 +370,23 @@ export default function ConsignmentSubmissionsPanel() {
</Card>
<Card size="small">
{submissionsQuery.isError ? (
<Alert
type="error"
showIcon
style={{ marginBottom: 12 }}
message="读取寄卖提报单失败"
description={
(submissionsQuery.error as any)?.message ||
'请检查网络或稍后重试;也可能是当前账号缺少 consignment.view 权限。'
}
action={
<Button size="small" onClick={() => void submissionsQuery.refetch()}>
重试
</Button>
}
/>
) : null}
<Table
rowKey="id"
columns={submissionColumns}
@@ -363,6 +412,7 @@ export default function ConsignmentSubmissionsPanel() {
width={680}
onCancel={() => setApproveModalItem(null)}
onOk={handleApproveSubmit}
confirmLoading={approving}
okText="审核通过并正式入库"
okType="primary"
destroyOnClose
@@ -519,6 +569,7 @@ export default function ConsignmentSubmissionsPanel() {
open={Boolean(rejectModalItem)}
onCancel={() => setRejectModalItem(null)}
onOk={handleRejectConfirm}
confirmLoading={rejecting}
okText="确认驳回"
okType="danger"
>