feat(category): 接单平台支持二级分类目录与接单大厅联动展示及限额优化

This commit is contained in:
yml2213
2026-09-12 18:45:56 +08:00
parent 111f400bdb
commit d1e06f362e
17 changed files with 819 additions and 135 deletions
+158 -35
View File
@@ -1,4 +1,5 @@
import {
AppstoreOutlined,
ClockCircleOutlined,
PushpinOutlined,
QuestionCircleOutlined,
@@ -63,7 +64,9 @@ export default function WorkerHallPage() {
const queryClient = useQueryClient()
const [keywordInput, setKeywordInput] = useState('')
const [keyword, setKeyword] = useState('')
const [categoryId, setCategoryId] = useState(0)
const [primaryCategoryId, setPrimaryCategoryId] = useState(0)
const [subCategoryId, setSubCategoryId] = useState(0)
const effectiveCategoryId = subCategoryId || primaryCategoryId || 0
const [grabbingId, setGrabbingId] = useState(0)
const [sharingOrder, setSharingOrder] = useState<WorkOrder | null>(null)
const [leaderboardOpen, setLeaderboardOpen] = useState(false)
@@ -74,14 +77,14 @@ export default function WorkerHallPage() {
const loadMoreRef = useRef<HTMLDivElement | null>(null)
const ordersQuery = useInfiniteQuery({
queryKey: ['worker-hall-orders', keyword, categoryId],
queryKey: ['worker-hall-orders', keyword, effectiveCategoryId],
initialPageParam: 1,
queryFn: ({ pageParam }) =>
fetchWorkerHallOrders({
keyword,
page: Number(pageParam),
pageSize: HALL_PAGE_SIZE,
categoryId: categoryId || undefined,
categoryId: effectiveCategoryId || undefined,
}),
getNextPageParam: (lastPage) => {
const pagination = lastPage.data.pagination
@@ -127,7 +130,28 @@ export default function WorkerHallPage() {
const orders = ordersQuery.data?.pages.flatMap((page) => page.data.items) || []
const firstPage = ordersQuery.data?.pages[0]?.data
const categories = firstPage?.categories || []
const allCategories = firstPage?.categories || []
const showSubcategoriesConfig = firstPage?.showSubcategories ?? true
const primaryCategories = allCategories.filter((c) => !c.parentId)
const subCategoriesMap = new Map<number, typeof allCategories>()
for (const c of allCategories) {
if (c.parentId) {
const list = subCategoriesMap.get(c.parentId) || []
list.push(c)
subCategoriesMap.set(c.parentId, list)
}
}
const currentSubCategories = primaryCategoryId
? subCategoriesMap.get(primaryCategoryId) || []
: []
const shouldShowSubcategories = showSubcategoriesConfig && currentSubCategories.length > 0
function handleSelectPrimaryCategory(nextId: number) {
setPrimaryCategoryId(nextId)
setSubCategoryId(0)
}
const worker = profileQuery.data?.data.worker
const activeCount = Number(profileQuery.data?.data.summary.activeOrderCount || 0)
const pendingAcceptanceCount = hallSummaryQuery.data?.pendingAcceptanceCount || 0
@@ -331,28 +355,69 @@ export default function WorkerHallPage() {
</div>
</div>
{/* 移动端横向滑动分类条 */}
{categories.length > 0 ? (
<div className="worker-hall-mobile-category-bar">
{/* 移动端一级分类/游戏卡片横向滑动条 */}
{primaryCategories.length > 0 ? (
<div className="worker-hall-game-cards-scroll">
<button
type="button"
className={`worker-hall-mobile-cat-pill ${categoryId === 0 ? 'active' : ''}`}
onClick={() => {
setCategoryId(0)
}}
className={`worker-hall-game-card ${primaryCategoryId === 0 ? 'active' : ''}`}
onClick={() => handleSelectPrimaryCategory(0)}
>
全部
<div className="worker-hall-game-icon-box">
<AppstoreOutlined
style={{
fontSize: 20,
color: primaryCategoryId === 0 ? 'var(--theme-primary)' : '#64748b',
}}
/>
</div>
<span className="worker-hall-game-title">全部</span>
</button>
{categories.map((category) => (
{primaryCategories.map((category) => (
<button
key={category.categoryId}
type="button"
className={`worker-hall-mobile-cat-pill ${categoryId === category.categoryId ? 'active' : ''}`}
onClick={() => {
setCategoryId(category.categoryId)
}}
className={`worker-hall-game-card ${primaryCategoryId === category.categoryId ? 'active' : ''}`}
onClick={() => handleSelectPrimaryCategory(category.categoryId)}
>
{category.name}
<div className="worker-hall-game-icon-box">
{category.iconUrl ? (
<img
src={category.iconUrl}
alt={category.name}
className="worker-hall-game-img"
/>
) : (
<span className="worker-hall-game-initial">{category.name.slice(0, 1)}</span>
)}
</div>
<span className="worker-hall-game-title" title={category.name}>
{category.name}
</span>
</button>
))}
</div>
) : null}
{/* 移动端二级分类联动条 */}
{shouldShowSubcategories ? (
<div className="worker-hall-subcat-bar">
<span className="worker-hall-subcat-label">业务类型:</span>
<button
type="button"
className={`worker-hall-mobile-cat-pill ${subCategoryId === 0 ? 'active' : ''}`}
onClick={() => setSubCategoryId(0)}
>
全部
</button>
{currentSubCategories.map((sub) => (
<button
key={sub.categoryId}
type="button"
className={`worker-hall-mobile-cat-pill ${subCategoryId === sub.categoryId ? 'active' : ''}`}
onClick={() => setSubCategoryId(sub.categoryId)}
>
{sub.name}
</button>
))}
</div>
@@ -442,7 +507,9 @@ export default function WorkerHallPage() {
) : null}
{order.categoryName ? (
<Tag color="blue" className="worker-hall-mobile-tag">
{order.categoryName}
{order.parentCategoryName
? `${order.parentCategoryName} · ${order.categoryName}`
: order.categoryName}
</Tag>
) : null}
{order.sharing?.enabled ? (
@@ -608,21 +675,75 @@ export default function WorkerHallPage() {
))}
</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)
}}
/>
{/* 桌面端一级分类卡片与二级联动 */}
{primaryCategories.length > 0 ? (
<div style={{ marginBottom: 12 }}>
<div className="worker-hall-game-cards-scroll">
<button
type="button"
className={`worker-hall-game-card ${primaryCategoryId === 0 ? 'active' : ''}`}
onClick={() => handleSelectPrimaryCategory(0)}
>
<div className="worker-hall-game-icon-box">
<AppstoreOutlined
style={{
fontSize: 20,
color: primaryCategoryId === 0 ? 'var(--theme-primary)' : '#64748b',
}}
/>
</div>
<span className="worker-hall-game-title">全部</span>
</button>
{primaryCategories.map((category) => (
<button
key={category.categoryId}
type="button"
className={`worker-hall-game-card ${primaryCategoryId === category.categoryId ? 'active' : ''}`}
onClick={() => handleSelectPrimaryCategory(category.categoryId)}
>
<div className="worker-hall-game-icon-box">
{category.iconUrl ? (
<img
src={category.iconUrl}
alt={category.name}
className="worker-hall-game-img"
/>
) : (
<span className="worker-hall-game-initial">
{category.name.slice(0, 1)}
</span>
)}
</div>
<span className="worker-hall-game-title" title={category.name}>
{category.name}
</span>
</button>
))}
</div>
{shouldShowSubcategories ? (
<div className="worker-hall-subcat-bar" style={{ marginTop: 8 }}>
<span className="worker-hall-subcat-label">业务类型:</span>
<button
type="button"
className={`worker-hall-mobile-cat-pill ${subCategoryId === 0 ? 'active' : ''}`}
onClick={() => setSubCategoryId(0)}
>
全部
</button>
{currentSubCategories.map((sub) => (
<button
key={sub.categoryId}
type="button"
className={`worker-hall-mobile-cat-pill ${subCategoryId === sub.categoryId ? 'active' : ''}`}
onClick={() => setSubCategoryId(sub.categoryId)}
>
{sub.name}
</button>
))}
</div>
) : null}
</div>
) : null}
{shouldWarn ? (
@@ -682,7 +803,9 @@ export default function WorkerHallPage() {
{productName}
</strong>
<Typography.Text type="secondary">
{order.categoryName || '默认分类'}
{order.parentCategoryName
? `${order.parentCategoryName} · ${order.categoryName}`
: order.categoryName || '默认分类'}
</Typography.Text>
<Typography.Text type="secondary">
订单号:{getDisplayOrderNo(order)}