fix: 修复移动端我的订单卡片与搜索栏Flex样式异常
This commit is contained in:
@@ -680,6 +680,83 @@ export async function countWorkerAcceptedOrders(workerId: number | string): Prom
|
||||
return Number(result.rows[0]?.total || 0)
|
||||
}
|
||||
|
||||
export async function listWorkerCompletionLeaderboard(input: {
|
||||
limit: number
|
||||
currentWorkerId: number
|
||||
}): Promise<
|
||||
Array<{
|
||||
rank: number
|
||||
workerId: number
|
||||
displayName: string
|
||||
levelName: string
|
||||
acceptedOrderCount: number
|
||||
}>
|
||||
> {
|
||||
const limit = Math.min(50, Math.max(1, Math.floor(Number(input.limit || 10))))
|
||||
const result = await query<{
|
||||
rank: number
|
||||
worker_id: number
|
||||
display_name: string
|
||||
level_name: string | null
|
||||
accepted_order_count: number
|
||||
}>(
|
||||
`
|
||||
WITH accepted_totals AS (
|
||||
SELECT
|
||||
worker_id,
|
||||
SUM(accepted_order_count)::int AS accepted_order_count,
|
||||
MAX(last_accepted_at) AS last_accepted_at
|
||||
FROM (
|
||||
SELECT
|
||||
assigned_worker_id AS worker_id,
|
||||
COUNT(*)::int AS accepted_order_count,
|
||||
MAX(accepted_at) AS last_accepted_at
|
||||
FROM work_orders
|
||||
WHERE assigned_worker_id IS NOT NULL AND status = 'accepted'
|
||||
GROUP BY assigned_worker_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
worker_id,
|
||||
COALESCE(SUM(quantity), 0)::int AS accepted_order_count,
|
||||
MAX(accepted_at) AS last_accepted_at
|
||||
FROM work_order_shares
|
||||
WHERE status = 'accepted'
|
||||
GROUP BY worker_id
|
||||
) AS accepted_records
|
||||
GROUP BY worker_id
|
||||
),
|
||||
ranked_workers AS (
|
||||
SELECT
|
||||
ROW_NUMBER() OVER (
|
||||
ORDER BY totals.accepted_order_count DESC, totals.last_accepted_at DESC NULLS LAST, wu.id ASC
|
||||
)::int AS rank,
|
||||
wu.id AS worker_id,
|
||||
COALESCE(NULLIF(wu.display_name, ''), wu.username) AS display_name,
|
||||
wl.name AS level_name,
|
||||
totals.accepted_order_count
|
||||
FROM worker_users wu
|
||||
INNER JOIN accepted_totals totals ON totals.worker_id = wu.id
|
||||
LEFT JOIN worker_levels wl ON wl.id = wu.level_id
|
||||
WHERE wu.status = 'active'
|
||||
)
|
||||
SELECT rank, worker_id, display_name, level_name, accepted_order_count
|
||||
FROM ranked_workers
|
||||
WHERE rank <= $1 OR worker_id = $2
|
||||
ORDER BY rank ASC
|
||||
`,
|
||||
[limit, Number(input.currentWorkerId)],
|
||||
)
|
||||
return result.rows.map((row) => ({
|
||||
rank: Number(row.rank || 0),
|
||||
workerId: Number(row.worker_id || 0),
|
||||
displayName: String(row.display_name || '').trim(),
|
||||
levelName: String(row.level_name || '').trim(),
|
||||
acceptedOrderCount: Number(row.accepted_order_count || 0),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function maybeUpgradeWorkerLevelWithClient(
|
||||
client: PoolClient,
|
||||
workerId: number,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
createWorkerWithdrawRequest,
|
||||
getWorkerProfile,
|
||||
getWorkerSessionSummary,
|
||||
getWorkerHallLeaderboard,
|
||||
grabWorkerHallOrder,
|
||||
joinWorkerSharingOrder,
|
||||
listWorkerProfileFinanceRequests,
|
||||
@@ -187,6 +188,16 @@ router.get(
|
||||
}),
|
||||
)
|
||||
|
||||
router.get(
|
||||
'/hall/leaderboard',
|
||||
requireActiveWorker,
|
||||
createRouteHandler((req) => getWorkerHallLeaderboard(getRequiredWorkerSession(req)), {
|
||||
successMessage: 'ok',
|
||||
errorMessage: '读取接单排行榜失败',
|
||||
scope: '[worker/hall/leaderboard]',
|
||||
}),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/hall/orders/:workOrderId/grab',
|
||||
requireActiveWorker,
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
listAllWorkCategories,
|
||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||
listWorkerFinanceRequests,
|
||||
listWorkerCompletionLeaderboard,
|
||||
listWorkerSharesByWorker,
|
||||
listWorkerWalletLedgers,
|
||||
listWorkerWorkOrderNotes,
|
||||
@@ -841,6 +842,22 @@ export async function listWorkerHallOrders(query: JsonObject = {}, session: Work
|
||||
}
|
||||
}
|
||||
|
||||
export async function getWorkerHallLeaderboard(session: WorkerSession) {
|
||||
requireActiveWorkerSession(session)
|
||||
const entries = await listWorkerCompletionLeaderboard({
|
||||
limit: 10,
|
||||
currentWorkerId: session.workerId,
|
||||
})
|
||||
const current = entries.find((entry) => entry.workerId === session.workerId)
|
||||
return {
|
||||
items: entries.filter((entry) => entry.rank <= 10),
|
||||
current: {
|
||||
rank: current?.rank || null,
|
||||
acceptedOrderCount: current?.acceptedOrderCount || 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function grabWorkerHallOrder(workOrderId: number | string, session: WorkerSession) {
|
||||
requireActiveWorkerSession(session)
|
||||
const worker = await getRequiredWorker(session.workerId)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ClockCircleOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons'
|
||||
import { ClockCircleOutlined, ReloadOutlined, SearchOutlined, TrophyOutlined } from '@ant-design/icons'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
Alert,
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Drawer,
|
||||
Empty,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -23,6 +24,7 @@ import { useEffect, useState } from 'react'
|
||||
|
||||
import {
|
||||
fetchWorkerHallOrders,
|
||||
fetchWorkerHallLeaderboard,
|
||||
fetchWorkerMyOrders,
|
||||
fetchWorkerProfile,
|
||||
grabWorkerOrder,
|
||||
@@ -47,6 +49,7 @@ export default function WorkerHallPage() {
|
||||
const [pageSize, setPageSize] = useState(20)
|
||||
const [grabbingId, setGrabbingId] = useState(0)
|
||||
const [sharingOrder, setSharingOrder] = useState<WorkOrder | null>(null)
|
||||
const [leaderboardOpen, setLeaderboardOpen] = useState(false)
|
||||
const [sharingQuantity, setSharingQuantity] = useState(1)
|
||||
const [submittingSharing, setSubmittingSharing] = useState(false)
|
||||
const [autoRefresh, setAutoRefresh] = useState(loadAutoRefreshPreference)
|
||||
@@ -105,6 +108,14 @@ export default function WorkerHallPage() {
|
||||
retry: false,
|
||||
})
|
||||
|
||||
const leaderboardQuery = useQuery({
|
||||
queryKey: ['worker-hall-leaderboard'],
|
||||
queryFn: () => fetchWorkerHallLeaderboard(),
|
||||
enabled: leaderboardOpen,
|
||||
staleTime: 30_000,
|
||||
retry: false,
|
||||
})
|
||||
|
||||
const orders = ordersQuery.data?.data.items || []
|
||||
const pagination = ordersQuery.data?.data.pagination
|
||||
const categories = ordersQuery.data?.data.categories || []
|
||||
@@ -125,6 +136,7 @@ export default function WorkerHallPage() {
|
||||
queryClient.invalidateQueries({ queryKey: ['worker-profile'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['worker-my-orders'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['worker-my-orders-overview'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['worker-hall-leaderboard'] }),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -219,6 +231,15 @@ export default function WorkerHallPage() {
|
||||
</div>
|
||||
|
||||
<div className="worker-hall-mobile-top-actions">
|
||||
<Tooltip title="接单排行榜">
|
||||
<Button
|
||||
aria-label="接单排行榜"
|
||||
size="small"
|
||||
shape="circle"
|
||||
icon={<TrophyOutlined />}
|
||||
onClick={() => setLeaderboardOpen(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<div className="worker-hall-mobile-autorefresh-switch">
|
||||
<span className="worker-hall-mobile-autorefresh-text">自动刷新</span>
|
||||
<Switch
|
||||
@@ -442,6 +463,13 @@ export default function WorkerHallPage() {
|
||||
<div className="worker-page-head-title-row">
|
||||
<Typography.Title level={3}>抢单大厅</Typography.Title>
|
||||
<div className="worker-hall-head-controls">
|
||||
<Tooltip title="接单排行榜">
|
||||
<Button
|
||||
aria-label="接单排行榜"
|
||||
icon={<TrophyOutlined />}
|
||||
onClick={() => setLeaderboardOpen(true)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={
|
||||
autoRefresh
|
||||
@@ -727,6 +755,77 @@ export default function WorkerHallPage() {
|
||||
</Space>
|
||||
) : null}
|
||||
</Modal>
|
||||
|
||||
<Drawer
|
||||
title={
|
||||
<Space size={8}>
|
||||
<TrophyOutlined className="worker-hall-leaderboard-title-icon" />
|
||||
<span>接单排行榜</span>
|
||||
</Space>
|
||||
}
|
||||
open={leaderboardOpen}
|
||||
placement="right"
|
||||
width={isMobile ? '88vw' : 360}
|
||||
onClose={() => setLeaderboardOpen(false)}
|
||||
>
|
||||
<div className="worker-hall-leaderboard">
|
||||
<Typography.Text type="secondary" className="worker-hall-leaderboard-caption">
|
||||
按累计验收完成数量排行
|
||||
</Typography.Text>
|
||||
{leaderboardQuery.isLoading ? (
|
||||
<div className="worker-hall-leaderboard-loading">
|
||||
<Spin />
|
||||
</div>
|
||||
) : leaderboardQuery.error ? (
|
||||
<Typography.Text type="danger">
|
||||
{leaderboardQuery.error instanceof Error
|
||||
? leaderboardQuery.error.message
|
||||
: '排行榜读取失败'}
|
||||
</Typography.Text>
|
||||
) : (leaderboardQuery.data?.data.items.length || 0) > 0 ? (
|
||||
<div className="worker-hall-leaderboard-list">
|
||||
{leaderboardQuery.data?.data.items.map((entry) => (
|
||||
<div
|
||||
key={entry.workerId}
|
||||
className={`worker-hall-leaderboard-row ${
|
||||
entry.workerId === worker?.workerId ? 'is-current' : ''
|
||||
}`}
|
||||
>
|
||||
<span className={`worker-hall-leaderboard-rank rank-${Math.min(entry.rank, 3)}`}>
|
||||
{entry.rank}
|
||||
</span>
|
||||
<div className="worker-hall-leaderboard-worker">
|
||||
<Typography.Text strong ellipsis>
|
||||
{entry.displayName || '未命名打手'}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary" ellipsis>
|
||||
{entry.levelName || '未设置等级'}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<Typography.Text strong className="worker-hall-leaderboard-count">
|
||||
{entry.acceptedOrderCount} 单
|
||||
</Typography.Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无完成订单" />
|
||||
)}
|
||||
{leaderboardQuery.data ? (
|
||||
<div className="worker-hall-leaderboard-current">
|
||||
<Typography.Text type="secondary">我的排名</Typography.Text>
|
||||
<Typography.Text strong>
|
||||
{leaderboardQuery.data.data.current.rank
|
||||
? `第 ${leaderboardQuery.data.data.current.rank} 名`
|
||||
: '暂未上榜'}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
已完成 {leaderboardQuery.data.data.current.acceptedOrderCount} 单
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Drawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
WorkOrderShare,
|
||||
WorkerFinanceRequest,
|
||||
WorkerHallOrdersResponse,
|
||||
WorkerLeaderboardResponse,
|
||||
WorkerListResponse,
|
||||
WorkerLoginResponse,
|
||||
WorkerProfileResponse,
|
||||
@@ -109,6 +110,10 @@ export function fetchWorkerHallOrders(params?: Record<string, unknown>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchWorkerHallLeaderboard() {
|
||||
return apiGet<WorkerLeaderboardResponse>('/api/v1/worker/hall/leaderboard')
|
||||
}
|
||||
|
||||
export function grabWorkerOrder(workOrderId: number) {
|
||||
return apiPost<{ order: WorkOrder }>(
|
||||
`/api/v1/worker/hall/orders/${workOrderId}/grab`,
|
||||
|
||||
@@ -675,7 +675,7 @@ body {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 2px;
|
||||
padding: 2px 0 4px 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
@@ -709,7 +709,7 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worker-orders-pill-badge {
|
||||
.worker-orders-status-pill-count {
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
color: inherit;
|
||||
font-size: 10px;
|
||||
@@ -718,13 +718,13 @@ body {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.worker-orders-status-pill.active .worker-orders-pill-badge {
|
||||
.worker-orders-status-pill.active .worker-orders-status-pill-count {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 搜索与快捷刷新栏 */
|
||||
.worker-orders-mobile-search-bar {
|
||||
.worker-orders-mobile-search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
@@ -739,30 +739,30 @@ body {
|
||||
.worker-orders-mobile-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.worker-order-mobile-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
border-radius: 14px;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.04);
|
||||
gap: 10px;
|
||||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
/* 头部:分类 + 订单号 + 状态 */
|
||||
.worker-order-mobile-card-top {
|
||||
.worker-order-mobile-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-top-left {
|
||||
.worker-order-mobile-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
@@ -770,14 +770,14 @@ body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.worker-order-mobile-category-tag {
|
||||
.worker-order-mobile-tag {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 18px;
|
||||
padding: 0 6px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-orderno {
|
||||
.worker-order-mobile-orderno-text {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
overflow: hidden;
|
||||
@@ -785,27 +785,23 @@ body {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.worker-order-mobile-top-right {
|
||||
.worker-order-mobile-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.worker-order-mobile-deadline {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* 商品名称与奖励 */
|
||||
/* 商品名称与金额 */
|
||||
.worker-order-mobile-main-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-product-title {
|
||||
font-size: 14px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
line-height: 1.35;
|
||||
@@ -816,52 +812,50 @@ body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.worker-order-mobile-reward {
|
||||
.worker-order-mobile-price-badge {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
align-items: baseline;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.worker-order-mobile-reward-val {
|
||||
.worker-order-mobile-price-label {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.worker-order-mobile-price-num {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: #ea580c;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.worker-order-mobile-reward-label {
|
||||
font-size: 10px;
|
||||
color: #94a3b8;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* 辅助信息行 */
|
||||
.worker-order-mobile-meta-row {
|
||||
/* 辅助信息行 (押金 & 接单时间) */
|
||||
.worker-order-mobile-sub-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.worker-order-mobile-tag {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
padding: 0 6px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-deposit-text {
|
||||
font-size: 11px;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.worker-order-mobile-sub-item {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.worker-order-mobile-sub-time {
|
||||
color: #94a3b8;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* 问题单提示 */
|
||||
.worker-order-mobile-problem-box {
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 6px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
color: #b91c1c;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
@@ -872,7 +866,7 @@ body {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
@@ -880,7 +874,7 @@ body {
|
||||
|
||||
.worker-order-mobile-credentials-grid {
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -907,15 +901,15 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-copy-toolbar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
gap: 8px;
|
||||
margin-top: 6px;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
padding-top: 6px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
/* 验收凭据条 */
|
||||
@@ -923,41 +917,69 @@ body {
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
border-radius: 8px;
|
||||
padding: 6px 10px;
|
||||
padding: 8px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-acceptance-left {
|
||||
.worker-order-mobile-acceptance-files,
|
||||
.worker-order-mobile-draft-files {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.worker-order-mobile-acceptance-tag {
|
||||
font-size: 12px;
|
||||
color: #166534;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worker-order-mobile-acceptance-text {
|
||||
font-size: 12px;
|
||||
color: #166534;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.worker-order-mobile-acceptance-time {
|
||||
font-size: 11px;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
/* 底部操作行 */
|
||||
.worker-order-mobile-actions-row {
|
||||
/* 底部操作与备注行 */
|
||||
.worker-order-mobile-footer-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f1f5f9;
|
||||
padding-top: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-action-btn {
|
||||
.worker-order-mobile-note-block {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.worker-order-mobile-add-note-btn {
|
||||
border: 1px dashed #cbd5e1;
|
||||
background: transparent;
|
||||
color: #64748b;
|
||||
border-radius: 6px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.worker-order-mobile-btn-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.worker-order-mobile-action-submit {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -1626,6 +1648,112 @@ body {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-title-icon {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-caption {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-row {
|
||||
display: grid;
|
||||
grid-template-columns: 30px minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 56px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-row.is-current {
|
||||
border-color: #fb923c;
|
||||
background: #fff7ed;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-rank {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-rank.rank-1 {
|
||||
background: #fef3c7;
|
||||
color: #a16207;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-rank.rank-2 {
|
||||
background: #e2e8f0;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-rank.rank-3 {
|
||||
background: #fee2e2;
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-worker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-worker .ant-typography {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-worker .ant-typography-secondary {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-count {
|
||||
color: #c2410c;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-current {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
gap: 4px 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.worker-hall-leaderboard-current .ant-typography:last-child {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ===== 打手端响应式断点 ===== */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
@@ -291,6 +291,22 @@ export type WorkerHallOrdersResponse = {
|
||||
visibleDelaySeconds?: number
|
||||
}
|
||||
|
||||
export type WorkerLeaderboardEntry = {
|
||||
rank: number
|
||||
workerId: number
|
||||
displayName: string
|
||||
levelName: string
|
||||
acceptedOrderCount: number
|
||||
}
|
||||
|
||||
export type WorkerLeaderboardResponse = {
|
||||
items: WorkerLeaderboardEntry[]
|
||||
current: {
|
||||
rank: number | null
|
||||
acceptedOrderCount: number
|
||||
}
|
||||
}
|
||||
|
||||
export type CollectField = {
|
||||
key: string
|
||||
label: string
|
||||
|
||||
Reference in New Issue
Block a user