接单工单与资金申请列表支持创建时间区间筛选
This commit is contained in:
@@ -414,6 +414,9 @@ export type ListInput = {
|
|||||||
sort?: 'id_desc' | 'worker_claimed_at_desc'
|
sort?: 'id_desc' | 'worker_claimed_at_desc'
|
||||||
/** 后台使用拼单聚合业务状态;大厅和打手列表使用原始生命周期。 */
|
/** 后台使用拼单聚合业务状态;大厅和打手列表使用原始生命周期。 */
|
||||||
useOperationalStatus?: boolean
|
useOperationalStatus?: boolean
|
||||||
|
/** 创建时间区间(YYYY-MM-DD,上海时区,含起止日);后台列表筛选使用。 */
|
||||||
|
createdFrom?: string
|
||||||
|
createdTo?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkOrderStatistics = {
|
export type WorkOrderStatistics = {
|
||||||
@@ -465,6 +468,9 @@ export type FinanceRequestListInput = {
|
|||||||
status?: string
|
status?: string
|
||||||
requestType?: string
|
requestType?: string
|
||||||
keyword?: string
|
keyword?: string
|
||||||
|
/** 申请时间区间(YYYY-MM-DD,上海时区,含起止日);后台列表筛选使用。 */
|
||||||
|
createdFrom?: string
|
||||||
|
createdTo?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateWorkerInput = {
|
export type CreateWorkerInput = {
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ export async function listWorkOrders({
|
|||||||
useOperationalStatus = false,
|
useOperationalStatus = false,
|
||||||
pinnedFirst = false,
|
pinnedFirst = false,
|
||||||
sort = 'id_desc',
|
sort = 'id_desc',
|
||||||
|
createdFrom = '',
|
||||||
|
createdTo = '',
|
||||||
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
}: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> {
|
||||||
const effectiveStatuses = statuses.length > 0 ? statuses : status.trim() ? [status.trim()] : []
|
const effectiveStatuses = statuses.length > 0 ? statuses : status.trim() ? [status.trim()] : []
|
||||||
const { joins, whereClause, params } = buildWorkOrderWhere({
|
const { joins, whereClause, params } = buildWorkOrderWhere({
|
||||||
@@ -185,6 +187,8 @@ export async function listWorkOrders({
|
|||||||
excludeFilledSharing,
|
excludeFilledSharing,
|
||||||
vipEvidencePending,
|
vipEvidencePending,
|
||||||
useOperationalStatus,
|
useOperationalStatus,
|
||||||
|
createdFrom,
|
||||||
|
createdTo,
|
||||||
})
|
})
|
||||||
const totalResult = await query<{ total: number }>(
|
const totalResult = await query<{ total: number }>(
|
||||||
`
|
`
|
||||||
@@ -519,10 +523,24 @@ export function buildWorkOrderWhere({
|
|||||||
excludeFilledSharing = false,
|
excludeFilledSharing = false,
|
||||||
vipEvidencePending = false,
|
vipEvidencePending = false,
|
||||||
useOperationalStatus = false,
|
useOperationalStatus = false,
|
||||||
|
createdFrom = '',
|
||||||
|
createdTo = '',
|
||||||
}: ListInput) {
|
}: ListInput) {
|
||||||
const filters: string[] = []
|
const filters: string[] = []
|
||||||
const params: unknown[] = []
|
const params: unknown[] = []
|
||||||
const joins: string[] = []
|
const joins: string[] = []
|
||||||
|
if (createdFrom) {
|
||||||
|
params.push(createdFrom)
|
||||||
|
filters.push(
|
||||||
|
`wo.created_at >= ($${params.length}::date)::timestamp AT TIME ZONE 'Asia/Shanghai'`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (createdTo) {
|
||||||
|
params.push(createdTo)
|
||||||
|
filters.push(
|
||||||
|
`wo.created_at < (($${params.length}::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')`,
|
||||||
|
)
|
||||||
|
}
|
||||||
let workerSharePlaceholder = ''
|
let workerSharePlaceholder = ''
|
||||||
|
|
||||||
if (excludeFilledSharing) {
|
if (excludeFilledSharing) {
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ export async function listWorkerFinanceRequests({
|
|||||||
status = '',
|
status = '',
|
||||||
requestType = '',
|
requestType = '',
|
||||||
keyword = '',
|
keyword = '',
|
||||||
|
createdFrom = '',
|
||||||
|
createdTo = '',
|
||||||
}: FinanceRequestListInput = {}): Promise<{ items: WorkerFinanceRequestRow[]; total: number }> {
|
}: FinanceRequestListInput = {}): Promise<{ items: WorkerFinanceRequestRow[]; total: number }> {
|
||||||
const { whereClause, params } = buildWorkerFinanceRequestWhere({
|
const { whereClause, params } = buildWorkerFinanceRequestWhere({
|
||||||
requestId,
|
requestId,
|
||||||
@@ -210,6 +212,8 @@ export async function listWorkerFinanceRequests({
|
|||||||
status,
|
status,
|
||||||
requestType,
|
requestType,
|
||||||
keyword,
|
keyword,
|
||||||
|
createdFrom,
|
||||||
|
createdTo,
|
||||||
})
|
})
|
||||||
const totalResult = await query<{ total: number }>(
|
const totalResult = await query<{ total: number }>(
|
||||||
`SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr
|
`SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr
|
||||||
@@ -415,9 +419,23 @@ function buildWorkerFinanceRequestWhere({
|
|||||||
status = '',
|
status = '',
|
||||||
requestType = '',
|
requestType = '',
|
||||||
keyword = '',
|
keyword = '',
|
||||||
|
createdFrom = '',
|
||||||
|
createdTo = '',
|
||||||
}: FinanceRequestListInput) {
|
}: FinanceRequestListInput) {
|
||||||
const filters: string[] = []
|
const filters: string[] = []
|
||||||
const params: unknown[] = []
|
const params: unknown[] = []
|
||||||
|
if (createdFrom) {
|
||||||
|
params.push(createdFrom)
|
||||||
|
filters.push(
|
||||||
|
`wfr.created_at >= ($${params.length}::date)::timestamp AT TIME ZONE 'Asia/Shanghai'`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (createdTo) {
|
||||||
|
params.push(createdTo)
|
||||||
|
filters.push(
|
||||||
|
`wfr.created_at < (($${params.length}::date + 1)::timestamp AT TIME ZONE 'Asia/Shanghai')`,
|
||||||
|
)
|
||||||
|
}
|
||||||
if (requestId) {
|
if (requestId) {
|
||||||
params.push(requestId)
|
params.push(requestId)
|
||||||
filters.push(`wfr.id = $${params.length}`)
|
filters.push(`wfr.id = $${params.length}`)
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ export function normalizeDateQuery(rawValue: unknown, endOfDay = false) {
|
|||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 校验并归一化 YYYY-MM-DD 日期参数,非法或空值返回空字符串。 */
|
||||||
|
export function normalizeDateString(rawValue: unknown) {
|
||||||
|
const normalized = String(rawValue || '').trim()
|
||||||
|
return /^\d{4}-\d{2}-\d{2}$/.test(normalized) ? normalized : ''
|
||||||
|
}
|
||||||
|
|
||||||
export function safeParseJson(rawText: unknown): JsonObject {
|
export function safeParseJson(rawText: unknown): JsonObject {
|
||||||
if (rawText && typeof rawText === 'object') {
|
if (rawText && typeof rawText === 'object') {
|
||||||
return rawText
|
return rawText
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import {
|
|||||||
sumPendingUnfreezeByOrderIds,
|
sumPendingUnfreezeByOrderIds,
|
||||||
} from '../../repositories/worker-platform/index.js'
|
} from '../../repositories/worker-platform/index.js'
|
||||||
import type { JsonObject } from '../../types/json.js'
|
import type { JsonObject } from '../../types/json.js'
|
||||||
import { normalizePage, normalizePageSize } from '../admin/admin-query-utils.js'
|
import {
|
||||||
|
normalizeDateString,
|
||||||
|
normalizePage,
|
||||||
|
normalizePageSize,
|
||||||
|
} from '../admin/admin-query-utils.js'
|
||||||
import {
|
import {
|
||||||
mapWorkOrderAdmin,
|
mapWorkOrderAdmin,
|
||||||
mapWorkOrderEvents,
|
mapWorkOrderEvents,
|
||||||
@@ -32,6 +36,8 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
|
|||||||
.filter((levelId): levelId is number => levelId !== null)
|
.filter((levelId): levelId is number => levelId !== null)
|
||||||
const statuses = normalizeStatuses(query.status)
|
const statuses = normalizeStatuses(query.status)
|
||||||
const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false)
|
const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false)
|
||||||
|
const createdFrom = normalizeDateString(query.createdFrom)
|
||||||
|
const createdTo = normalizeDateString(query.createdTo)
|
||||||
const filters = {
|
const filters = {
|
||||||
statuses,
|
statuses,
|
||||||
workOrderId,
|
workOrderId,
|
||||||
@@ -40,6 +46,8 @@ export async function listAdminWorkOrders(query: JsonObject = {}) {
|
|||||||
workerLevelIds,
|
workerLevelIds,
|
||||||
vipEvidencePending,
|
vipEvidencePending,
|
||||||
useOperationalStatus: true,
|
useOperationalStatus: true,
|
||||||
|
...(createdFrom ? { createdFrom } : {}),
|
||||||
|
...(createdTo ? { createdTo } : {}),
|
||||||
}
|
}
|
||||||
const { items, total } = await listWorkOrders({ page, pageSize, ...filters })
|
const { items, total } = await listWorkOrders({ page, pageSize, ...filters })
|
||||||
const statistics = await getWorkOrderStatistics(filters)
|
const statistics = await getWorkOrderStatistics(filters)
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ import type { WorkerWithdrawalAccountRow } from '../../repositories/worker-platf
|
|||||||
import type { JsonObject } from '../../types/json.js'
|
import type { JsonObject } from '../../types/json.js'
|
||||||
import { createHttpError } from '../../utils/http.js'
|
import { createHttpError } from '../../utils/http.js'
|
||||||
import { nowIso } from '../../utils/time.js'
|
import { nowIso } from '../../utils/time.js'
|
||||||
import { normalizePage, normalizePageSize, safeParseJson } from '../admin/admin-query-utils.js'
|
import {
|
||||||
|
normalizeDateString,
|
||||||
|
normalizePage,
|
||||||
|
normalizePageSize,
|
||||||
|
safeParseJson,
|
||||||
|
} from '../admin/admin-query-utils.js'
|
||||||
import { resolveAdminNotificationEntity } from '../admin/admin-notification-service.js'
|
import { resolveAdminNotificationEntity } from '../admin/admin-notification-service.js'
|
||||||
import { refreshUploadedFileUrls } from '../file-storage/file-storage-service.js'
|
import { refreshUploadedFileUrls } from '../file-storage/file-storage-service.js'
|
||||||
import {
|
import {
|
||||||
@@ -162,6 +167,8 @@ export async function listAdminWorkerFinanceRequests(query: JsonObject = {}) {
|
|||||||
const status = normalizeFinanceRequestStatus(query.status)
|
const status = normalizeFinanceRequestStatus(query.status)
|
||||||
const requestType = normalizeFinanceRequestType(query.requestType)
|
const requestType = normalizeFinanceRequestType(query.requestType)
|
||||||
const keyword = String(query.keyword || '').trim()
|
const keyword = String(query.keyword || '').trim()
|
||||||
|
const createdFrom = normalizeDateString(query.createdFrom)
|
||||||
|
const createdTo = normalizeDateString(query.createdTo)
|
||||||
const { items, total } = await listWorkerFinanceRequests({
|
const { items, total } = await listWorkerFinanceRequests({
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
@@ -169,6 +176,8 @@ export async function listAdminWorkerFinanceRequests(query: JsonObject = {}) {
|
|||||||
status,
|
status,
|
||||||
requestType,
|
requestType,
|
||||||
keyword,
|
keyword,
|
||||||
|
...(createdFrom ? { createdFrom } : {}),
|
||||||
|
...(createdTo ? { createdTo } : {}),
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
items: items.map(mapFinanceRequest),
|
items: items.map(mapFinanceRequest),
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import type {
|
|||||||
WorkerFinanceConfig,
|
WorkerFinanceConfig,
|
||||||
WorkerFinanceRequest,
|
WorkerFinanceRequest,
|
||||||
} from '@/types/worker-platform'
|
} from '@/types/worker-platform'
|
||||||
|
import { AdminRangePicker } from '@/components/admin/AdminDatePicker'
|
||||||
|
import dayjs, { ADMIN_DATE_FORMAT } from '@/lib/dayjs'
|
||||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||||
import { asRecord, formatMoney } from './shared'
|
import { asRecord, formatMoney } from './shared'
|
||||||
@@ -69,6 +71,7 @@ export default function FinancePanel() {
|
|||||||
const [requestType, setRequestType] = useState('')
|
const [requestType, setRequestType] = useState('')
|
||||||
const [keywordInput, setKeywordInput] = useState('')
|
const [keywordInput, setKeywordInput] = useState('')
|
||||||
const [keyword, setKeyword] = useState('')
|
const [keyword, setKeyword] = useState('')
|
||||||
|
const [createdRange, setCreatedRange] = useState<[string, string] | null>(null)
|
||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
||||||
const [savingConfig, setSavingConfig] = useState(false)
|
const [savingConfig, setSavingConfig] = useState(false)
|
||||||
@@ -109,6 +112,8 @@ export default function FinancePanel() {
|
|||||||
keyword,
|
keyword,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
createdRange ? createdRange[0] : '',
|
||||||
|
createdRange ? createdRange[1] : '',
|
||||||
],
|
],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
fetchAdminWorkerFinanceRequests({
|
fetchAdminWorkerFinanceRequests({
|
||||||
@@ -118,6 +123,7 @@ export default function FinancePanel() {
|
|||||||
keyword,
|
keyword,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
...(createdRange ? { createdFrom: createdRange[0], createdTo: createdRange[1] } : {}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
const financeRequestsPagination = financeRequestsQuery.data?.data.pagination
|
const financeRequestsPagination = financeRequestsQuery.data?.data.pagination
|
||||||
@@ -466,6 +472,18 @@ export default function FinancePanel() {
|
|||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<AdminRangePicker
|
||||||
|
value={createdRange ? [dayjs(createdRange[0]), dayjs(createdRange[1])] : null}
|
||||||
|
onChange={(dates) => {
|
||||||
|
const [start, end] = dates || []
|
||||||
|
setCreatedRange(
|
||||||
|
start && end
|
||||||
|
? [start.format(ADMIN_DATE_FORMAT), end.format(ADMIN_DATE_FORMAT)]
|
||||||
|
: null,
|
||||||
|
)
|
||||||
|
setPage(1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<ReloadOutlined />}
|
icon={<ReloadOutlined />}
|
||||||
loading={financeRequestsQuery.isFetching}
|
loading={financeRequestsQuery.isFetching}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ import {
|
|||||||
updateAdminWorkOrderSharing,
|
updateAdminWorkOrderSharing,
|
||||||
} from '@/services/admin'
|
} from '@/services/admin'
|
||||||
import type { UploadedFile, WorkOrder, WorkOrderShare } from '@/types/worker-platform'
|
import type { UploadedFile, WorkOrder, WorkOrderShare } from '@/types/worker-platform'
|
||||||
|
import { AdminRangePicker } from '@/components/admin/AdminDatePicker'
|
||||||
|
import dayjs, { ADMIN_DATE_FORMAT } from '@/lib/dayjs'
|
||||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||||
import { hasAdminRole } from '@/utils/admin-auth'
|
import { hasAdminRole } from '@/utils/admin-auth'
|
||||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||||
@@ -139,6 +141,7 @@ export default function WorkOrdersPanel() {
|
|||||||
const [statuses, setStatuses] = useState<string[]>([])
|
const [statuses, setStatuses] = useState<string[]>([])
|
||||||
const [targetWorkOrderId, setTargetWorkOrderId] = useState<number | undefined>()
|
const [targetWorkOrderId, setTargetWorkOrderId] = useState<number | undefined>()
|
||||||
const [keyword, setKeyword] = useState('')
|
const [keyword, setKeyword] = useState('')
|
||||||
|
const [createdRange, setCreatedRange] = useState<[string, string] | null>(null)
|
||||||
const [keywordInput, setKeywordInput] = useState('')
|
const [keywordInput, setKeywordInput] = useState('')
|
||||||
const [workerId, setWorkerId] = useState<number | undefined>()
|
const [workerId, setWorkerId] = useState<number | undefined>()
|
||||||
const [workerLevelIds, setWorkerLevelIds] = useState<number[]>([])
|
const [workerLevelIds, setWorkerLevelIds] = useState<number[]>([])
|
||||||
@@ -219,6 +222,8 @@ export default function WorkOrdersPanel() {
|
|||||||
targetWorkOrderId,
|
targetWorkOrderId,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
createdRange ? createdRange[0] : '',
|
||||||
|
createdRange ? createdRange[1] : '',
|
||||||
],
|
],
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
fetchAdminWorkOrders({
|
fetchAdminWorkOrders({
|
||||||
@@ -232,6 +237,7 @@ export default function WorkOrdersPanel() {
|
|||||||
showWorkerLevelFilter && workerLevelIds.length > 0 ? workerLevelIds.join(',') : undefined,
|
showWorkerLevelFilter && workerLevelIds.length > 0 ? workerLevelIds.join(',') : undefined,
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
|
...(createdRange ? { createdFrom: createdRange[0], createdTo: createdRange[1] } : {}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
const ordersPagination = ordersQuery.data?.data.pagination
|
const ordersPagination = ordersQuery.data?.data.pagination
|
||||||
@@ -587,6 +593,7 @@ export default function WorkOrdersPanel() {
|
|||||||
setStatuses([])
|
setStatuses([])
|
||||||
setWorkerId(undefined)
|
setWorkerId(undefined)
|
||||||
setWorkerLevelIds([])
|
setWorkerLevelIds([])
|
||||||
|
setCreatedRange(null)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1094,6 +1101,18 @@ export default function WorkOrdersPanel() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
<AdminRangePicker
|
||||||
|
value={createdRange ? [dayjs(createdRange[0]), dayjs(createdRange[1])] : null}
|
||||||
|
onChange={(dates) => {
|
||||||
|
const [start, end] = dates || []
|
||||||
|
setCreatedRange(
|
||||||
|
start && end
|
||||||
|
? [start.format(ADMIN_DATE_FORMAT), end.format(ADMIN_DATE_FORMAT)]
|
||||||
|
: null,
|
||||||
|
)
|
||||||
|
setPage(1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Button type="primary" icon={<SearchOutlined />} onClick={searchOrders}>
|
<Button type="primary" icon={<SearchOutlined />} onClick={searchOrders}>
|
||||||
查询
|
查询
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user