接单工单状态筛选支持多选
- 前端:状态筛选改为多选(Select mode=multiple),可一次筛选多个状态 - 后端:listWorkOrders 支持 statuses 数组查询(ANY 匹配),兼容旧单状态参数 - 批量验收入口仅在单独筛选待验收时显示,避免多选误操作
This commit is contained in:
@@ -209,7 +209,10 @@ export type GrabWorkOrderResult = {
|
|||||||
export type ListInput = {
|
export type ListInput = {
|
||||||
page?: number
|
page?: number
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
|
/** 单状态筛选(兼容 worker 用户等其他列表) */
|
||||||
status?: string
|
status?: string
|
||||||
|
/** 支持多个状态同时筛选(空数组表示全部状态) */
|
||||||
|
statuses?: string[]
|
||||||
keyword?: string
|
keyword?: string
|
||||||
workerId?: number
|
workerId?: number
|
||||||
categoryId?: number
|
categoryId?: number
|
||||||
|
|||||||
@@ -737,6 +737,7 @@ export async function listWorkOrdersByPlatformOrderId(
|
|||||||
export async function listWorkOrders({
|
export async function listWorkOrders({
|
||||||
page = 1,
|
page = 1,
|
||||||
pageSize = 20,
|
pageSize = 20,
|
||||||
|
statuses = [],
|
||||||
status = '',
|
status = '',
|
||||||
keyword = '',
|
keyword = '',
|
||||||
workerId = 0,
|
workerId = 0,
|
||||||
@@ -745,8 +746,10 @@ export async function listWorkOrders({
|
|||||||
visibleAfterIso = '',
|
visibleAfterIso = '',
|
||||||
sort = 'id_desc',
|
sort = 'id_desc',
|
||||||
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
||||||
|
const effectiveStatuses =
|
||||||
|
statuses.length > 0 ? statuses : status.trim() ? [status.trim()] : []
|
||||||
const { whereClause, params } = buildWorkOrderWhere({
|
const { whereClause, params } = buildWorkOrderWhere({
|
||||||
status,
|
statuses: effectiveStatuses,
|
||||||
keyword,
|
keyword,
|
||||||
workerId,
|
workerId,
|
||||||
categoryId,
|
categoryId,
|
||||||
@@ -2522,7 +2525,7 @@ async function getWorkOrderByIdWithClient(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildWorkOrderWhere({
|
function buildWorkOrderWhere({
|
||||||
status = '',
|
statuses = [],
|
||||||
keyword = '',
|
keyword = '',
|
||||||
workerId = 0,
|
workerId = 0,
|
||||||
categoryId = 0,
|
categoryId = 0,
|
||||||
@@ -2531,9 +2534,16 @@ function buildWorkOrderWhere({
|
|||||||
}: ListInput) {
|
}: ListInput) {
|
||||||
const filters: string[] = []
|
const filters: string[] = []
|
||||||
const params: unknown[] = []
|
const params: unknown[] = []
|
||||||
if (status) {
|
const normalizedStatuses = [
|
||||||
params.push(status)
|
...new Set(
|
||||||
filters.push(`wo.status = $${params.length}`)
|
statuses
|
||||||
|
.map((item) => String(item || '').trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
if (normalizedStatuses.length > 0) {
|
||||||
|
params.push(normalizedStatuses)
|
||||||
|
filters.push(`wo.status = ANY($${params.length}::text[])`)
|
||||||
}
|
}
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
params.push(`%${keyword}%`)
|
params.push(`%${keyword}%`)
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ import {
|
|||||||
saveWorkerFinanceConfig,
|
saveWorkerFinanceConfig,
|
||||||
} from './worker-finance-config-service.js'
|
} from './worker-finance-config-service.js'
|
||||||
|
|
||||||
import { DEFAULT_CATEGORY_KEY, DEFAULT_DEPOSIT_THRESHOLD_AMOUNT, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, mapFinanceRequest, mapWallet, mapWorkCategory, mapWorkOrderAdmin, mapWorkOrderShare, mapWorkProductRule, mapWorkerLevel, mapWorkerUser, normalizeAdminFinanceReviewStatus, normalizeAmountFen, normalizeBoolean, normalizeEnabledStatus, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeMatchType, normalizeOptionalId, normalizePositiveInteger, normalizeProblemResolutionAction, normalizeRequirementFields, normalizeRequirementFieldsFromPayload, normalizeReviewStatus, normalizeSessionVersion, normalizeSlugKey, normalizeSubmittedFields, normalizeUploadedFiles, normalizeWorkerType, resolveFreezeDepositAmount, resolveMatchingProductRule, resolveRequirementFields, resolveSkuNameQuantity, resolveWorkerPermissions } from './mappers.js'
|
import { DEFAULT_CATEGORY_KEY, DEFAULT_DEPOSIT_THRESHOLD_AMOUNT, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, mapFinanceRequest, mapWallet, mapWorkCategory, mapWorkOrderAdmin, mapWorkOrderShare, mapWorkProductRule, mapWorkerLevel, mapWorkerUser, normalizeAdminFinanceReviewStatus, normalizeAmountFen, normalizeBoolean, normalizeEnabledStatus, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeMatchType, normalizeOptionalId, normalizePositiveInteger, normalizeProblemResolutionAction, normalizeRequirementFields, normalizeRequirementFieldsFromPayload, normalizeReviewStatus, normalizeSessionVersion, normalizeSlugKey, normalizeStatuses, normalizeSubmittedFields, normalizeUploadedFiles, normalizeWorkerType, resolveFreezeDepositAmount, resolveMatchingProductRule, resolveRequirementFields, resolveSkuNameQuantity, resolveWorkerPermissions } from './mappers.js'
|
||||||
import { ensureWorkerPlatformDefaults, getRequiredWorkOrder, getRequiredWorker, normalizeWorkOrderTimeoutPolicy } from './worker-service.js'
|
import { ensureWorkerPlatformDefaults, getRequiredWorkOrder, getRequiredWorker, normalizeWorkOrderTimeoutPolicy } from './worker-service.js'
|
||||||
|
|
||||||
export async function listAdminWorkerLevels() {
|
export async function listAdminWorkerLevels() {
|
||||||
@@ -566,7 +566,7 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
|
|||||||
const { items, total } = await listWorkOrders({
|
const { items, total } = await listWorkOrders({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
status: String(query.status || '').trim(),
|
statuses: normalizeStatuses(query.status),
|
||||||
keyword: String(query.keyword || '').trim(),
|
keyword: String(query.keyword || '').trim(),
|
||||||
workerSharingId: workerId,
|
workerSharingId: workerId,
|
||||||
})
|
})
|
||||||
@@ -1321,9 +1321,9 @@ export async function cancelAdminWorkOrder(
|
|||||||
export async function getAdminWorkerPlatformSummary() {
|
export async function getAdminWorkerPlatformSummary() {
|
||||||
const [pendingWorkers, pendingMaterial, openOrders, inProgressOrders] = await Promise.all([
|
const [pendingWorkers, pendingMaterial, openOrders, inProgressOrders] = await Promise.all([
|
||||||
listWorkerUsers({ page: 1, pageSize: 1, status: 'pending_review' }),
|
listWorkerUsers({ page: 1, pageSize: 1, status: 'pending_review' }),
|
||||||
listWorkOrders({ page: 1, pageSize: 1, status: WORK_ORDER_STATUS.PENDING_MATERIAL }),
|
listWorkOrders({ page: 1, pageSize: 1, statuses: [WORK_ORDER_STATUS.PENDING_MATERIAL] }),
|
||||||
listWorkOrders({ page: 1, pageSize: 1, status: WORK_ORDER_STATUS.OPEN }),
|
listWorkOrders({ page: 1, pageSize: 1, statuses: [WORK_ORDER_STATUS.OPEN] }),
|
||||||
listWorkOrders({ page: 1, pageSize: 1, status: WORK_ORDER_STATUS.IN_PROGRESS }),
|
listWorkOrders({ page: 1, pageSize: 1, statuses: [WORK_ORDER_STATUS.IN_PROGRESS] }),
|
||||||
])
|
])
|
||||||
return {
|
return {
|
||||||
pendingWorkerCount: pendingWorkers.total,
|
pendingWorkerCount: pendingWorkers.total,
|
||||||
|
|||||||
@@ -758,6 +758,27 @@ export function normalizeStringArray(value: unknown): string[] {
|
|||||||
return value.map((item) => String(item || '').trim()).filter(Boolean)
|
return value.map((item) => String(item || '').trim()).filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将查询参数中的状态过滤解析为去重后的状态数组。
|
||||||
|
* 支持:逗号分隔的字符串(如 "open,in_progress")、字符串数组、单个字符串。
|
||||||
|
*/
|
||||||
|
export function normalizeStatuses(value: unknown): string[] {
|
||||||
|
const rawList: unknown[] = Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: typeof value === 'string' && value.trim()
|
||||||
|
? value.split(',')
|
||||||
|
: value === undefined || value === null
|
||||||
|
? []
|
||||||
|
: [value]
|
||||||
|
return [
|
||||||
|
...new Set(
|
||||||
|
rawList
|
||||||
|
.map((item) => String(item || '').trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeUploadedFiles(value: unknown) {
|
export function normalizeUploadedFiles(value: unknown) {
|
||||||
if (!Array.isArray(value)) return []
|
if (!Array.isArray(value)) return []
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ import {
|
|||||||
saveWorkerFinanceConfig,
|
saveWorkerFinanceConfig,
|
||||||
} from './worker-finance-config-service.js'
|
} from './worker-finance-config-service.js'
|
||||||
|
|
||||||
import { DEFAULT_CATEGORY_KEY, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, INVITE_CODE_LENGTH, INVITE_CODE_RETRY_LIMIT, createWorkerSession, ensureWorkerAuthConfigured, hashWorkerPassword, isIgnorableWorkerAuthError, mapCollectLookupOrder, mapFinanceRequest, mapWalletLedger, mapWorkCategory, mapWorkOrderForWorker, mapWorkOrderPublic, mapWorkOrderShare, mapWorkerUser, normalizeAmountFen, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeOptionalId, normalizePassword, normalizePositiveInteger, normalizeProofFiles, normalizeSessionVersion, normalizeStringArray, normalizeSubmittedFields, normalizeUploadedFiles, normalizeUsername, normalizeWalletLedgerType, normalizeWithdrawChannel, resolveFreezeDepositAmount, resolveRequirementFields, resolveWorkerPermissions, safeCompare, signWorkerPayload, throwGrabWorkOrderFailure, validateWorkerPassword, validateWorkerUsername, verifyWorkerPassword } from './mappers.js'
|
import { DEFAULT_CATEGORY_KEY, DEFAULT_LEVEL_KEY, DEFAULT_LEVEL_NAME, INVITE_CODE_LENGTH, INVITE_CODE_RETRY_LIMIT, createWorkerSession, ensureWorkerAuthConfigured, hashWorkerPassword, isIgnorableWorkerAuthError, mapCollectLookupOrder, mapFinanceRequest, mapWalletLedger, mapWorkCategory, mapWorkOrderForWorker, mapWorkOrderPublic, mapWorkOrderShare, mapWorkerUser, normalizeAmountFen, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, normalizeOptionalId, normalizePassword, normalizePositiveInteger, normalizeProofFiles, normalizeSessionVersion, normalizeStatuses, normalizeStringArray, normalizeSubmittedFields, normalizeUploadedFiles, normalizeUsername, normalizeWalletLedgerType, normalizeWithdrawChannel, resolveFreezeDepositAmount, resolveRequirementFields, resolveWorkerPermissions, safeCompare, signWorkerPayload, throwGrabWorkOrderFailure, validateWorkerPassword, validateWorkerUsername, verifyWorkerPassword } from './mappers.js'
|
||||||
|
|
||||||
export type WorkerSession = {
|
export type WorkerSession = {
|
||||||
sessionId: string
|
sessionId: string
|
||||||
@@ -820,7 +820,7 @@ export async function listWorkerHallOrders(query: JsonObject = {}, session: Work
|
|||||||
const { items, total } = await listWorkOrders({
|
const { items, total } = await listWorkOrders({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
status: WORK_ORDER_STATUS.OPEN,
|
statuses: [WORK_ORDER_STATUS.OPEN],
|
||||||
keyword: String(query.keyword || '').trim(),
|
keyword: String(query.keyword || '').trim(),
|
||||||
categoryId: categoryId || 0,
|
categoryId: categoryId || 0,
|
||||||
visibleAfterIso,
|
visibleAfterIso,
|
||||||
@@ -991,7 +991,7 @@ export async function listWorkerMyOrders(query: JsonObject = {}, session: Worker
|
|||||||
const { items, total } = await listWorkOrders({
|
const { items, total } = await listWorkOrders({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
status: String(query.status || '').trim(),
|
statuses: normalizeStatuses(query.status),
|
||||||
keyword: String(query.keyword || '').trim(),
|
keyword: String(query.keyword || '').trim(),
|
||||||
workerSharingId: worker.id,
|
workerSharingId: worker.id,
|
||||||
sort: 'worker_claimed_at_desc',
|
sort: 'worker_claimed_at_desc',
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export default function WorkOrdersPanel() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const canEditRequirementFields = hasAdminRole('operator')
|
const canEditRequirementFields = hasAdminRole('operator')
|
||||||
const [status, setStatus] = useState('')
|
const [statuses, setStatuses] = useState<string[]>([])
|
||||||
const [keyword, setKeyword] = useState('')
|
const [keyword, setKeyword] = useState('')
|
||||||
const [keywordInput, setKeywordInput] = useState('')
|
const [keywordInput, setKeywordInput] = useState('')
|
||||||
const [workerId, setWorkerId] = useState<number | undefined>()
|
const [workerId, setWorkerId] = useState<number | undefined>()
|
||||||
@@ -181,10 +181,27 @@ export default function WorkOrdersPanel() {
|
|||||||
unitReward?: number
|
unitReward?: number
|
||||||
totalAmount?: number
|
totalAmount?: number
|
||||||
}>()
|
}>()
|
||||||
|
// 仅单独筛选「待验收」时提供批量验收入口(多选状态下不启用勾选,避免误操作)
|
||||||
|
const isPendingAcceptanceOnly =
|
||||||
|
statuses.length === 1 && statuses[0] === 'pending_acceptance'
|
||||||
|
|
||||||
const ordersQuery = useQuery({
|
const ordersQuery = useQuery({
|
||||||
queryKey: ['admin-worker-platform-orders', status, keyword, workerId, page, pageSize],
|
queryKey: [
|
||||||
queryFn: () => fetchAdminWorkOrders({ status, keyword, workerId, page, pageSize }),
|
'admin-worker-platform-orders',
|
||||||
|
statuses.join(','),
|
||||||
|
keyword,
|
||||||
|
workerId,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
],
|
||||||
|
queryFn: () =>
|
||||||
|
fetchAdminWorkOrders({
|
||||||
|
status: statuses.length > 0 ? statuses.join(',') : undefined,
|
||||||
|
keyword,
|
||||||
|
workerId,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
const ordersPagination = ordersQuery.data?.data.pagination
|
const ordersPagination = ordersQuery.data?.data.pagination
|
||||||
const categoriesQuery = useQuery({
|
const categoriesQuery = useQuery({
|
||||||
@@ -208,7 +225,7 @@ export default function WorkOrdersPanel() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedAcceptanceOrderIds([])
|
setSelectedAcceptanceOrderIds([])
|
||||||
}, [status, keyword, workerId, page, pageSize])
|
}, [statuses, keyword, workerId, page, pageSize])
|
||||||
|
|
||||||
function openAssignModal(row: WorkOrder) {
|
function openAssignModal(row: WorkOrder) {
|
||||||
setAssignOrder(row)
|
setAssignOrder(row)
|
||||||
@@ -441,7 +458,7 @@ export default function WorkOrdersPanel() {
|
|||||||
function clearOrderFilters() {
|
function clearOrderFilters() {
|
||||||
setKeywordInput('')
|
setKeywordInput('')
|
||||||
setKeyword('')
|
setKeyword('')
|
||||||
setStatus('')
|
setStatuses([])
|
||||||
setWorkerId(undefined)
|
setWorkerId(undefined)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}
|
}
|
||||||
@@ -854,17 +871,18 @@ export default function WorkOrdersPanel() {
|
|||||||
onPressEnter={searchOrders}
|
onPressEnter={searchOrders}
|
||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
value={status}
|
mode="multiple"
|
||||||
style={{ width: 150 }}
|
allowClear
|
||||||
options={[
|
value={statuses}
|
||||||
{ value: '', label: '全部状态' },
|
placeholder="全部状态(可多选)"
|
||||||
...FILTERABLE_WORK_ORDER_STATUSES.map((item) => ({
|
style={{ minWidth: 220, maxWidth: 420 }}
|
||||||
value: item,
|
maxTagCount="responsive"
|
||||||
label: <Tag color={resolveStatusColor(item)}>{formatStatus(item)}</Tag>,
|
options={FILTERABLE_WORK_ORDER_STATUSES.map((item) => ({
|
||||||
})),
|
value: item,
|
||||||
]}
|
label: <Tag color={resolveStatusColor(item)}>{formatStatus(item)}</Tag>,
|
||||||
onChange={(nextStatus) => {
|
}))}
|
||||||
setStatus(nextStatus)
|
onChange={(nextStatuses: string[]) => {
|
||||||
|
setStatuses(nextStatuses)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -903,7 +921,7 @@ export default function WorkOrdersPanel() {
|
|||||||
title="接单工单"
|
title="接单工单"
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
{status === 'pending_acceptance' ? (
|
{isPendingAcceptanceOnly ? (
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<CheckOutlined />}
|
icon={<CheckOutlined />}
|
||||||
@@ -930,7 +948,7 @@ export default function WorkOrdersPanel() {
|
|||||||
dataSource={ordersQuery.data?.data.items || []}
|
dataSource={ordersQuery.data?.data.items || []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowSelection={
|
rowSelection={
|
||||||
status === 'pending_acceptance'
|
isPendingAcceptanceOnly
|
||||||
? {
|
? {
|
||||||
selectedRowKeys: selectedAcceptanceOrderIds,
|
selectedRowKeys: selectedAcceptanceOrderIds,
|
||||||
onChange: (selectedRowKeys) =>
|
onChange: (selectedRowKeys) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user