feat(worker-platform): 抢单大厅首页新增联系客服胶囊入口并抽象通用客服弹窗组件
1. 抽离打手端通用联系客服弹窗组件 WorkerContactModal,统一维护微信二维码、打手账号/状态/余额回显、一键复制及沟通提示; 2. 抢单大厅移动端顶部状态栏(余额上限右侧首屏黄金位)新增精致微动效【联系客服】胶囊按钮,大幅缩短找客服沟通链路; 3. 抢单大厅桌面端头部操作栏同步补齐【联系客服】按钮,并在个人中心账号弹窗中统一复用 WorkerContactModal; 4. 适配移动端与超窄屏自适应响应式样式,完成前端59项测试与全量打包构建验证。
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { Alert, Button, Descriptions, Image, Modal, Space, Typography } from 'antd'
|
||||
|
||||
import { formatMoney, formatWorkerStatus } from '@/pages/worker/worker-profile-view-utils'
|
||||
import type { WorkerFinanceConfig, WorkerUser } from '@/types/worker-platform'
|
||||
|
||||
export type WorkerContactModalProps = {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
isMobile?: boolean
|
||||
worker?: WorkerUser | null
|
||||
financeConfig?: WorkerFinanceConfig | null
|
||||
title?: string
|
||||
}
|
||||
|
||||
export function WorkerContactModal({
|
||||
open,
|
||||
onClose,
|
||||
isMobile = false,
|
||||
worker,
|
||||
financeConfig,
|
||||
title = '联系客服',
|
||||
}: WorkerContactModalProps) {
|
||||
return (
|
||||
<Modal
|
||||
title={title}
|
||||
open={open}
|
||||
width={isMobile ? '92%' : 500}
|
||||
destroyOnHidden
|
||||
footer={
|
||||
<Button type="primary" onClick={onClose}>
|
||||
我知道了
|
||||
</Button>
|
||||
}
|
||||
onCancel={onClose}
|
||||
>
|
||||
<Space direction="vertical" size={16} className="full-width">
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="如需取消已接订单,请扫码联系客服处理。充值、提现与审核问题也由后台人工客服处理。"
|
||||
/>
|
||||
{financeConfig?.adminContact?.wechatQrCodeImage ? (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
width={200}
|
||||
src={
|
||||
financeConfig.adminContact.wechatQrCodeImage.mediumUrl ||
|
||||
financeConfig.adminContact.wechatQrCodeImage.url
|
||||
}
|
||||
alt="客服微信二维码"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Typography.Text type="secondary">
|
||||
客服暂未配置微信二维码,请按下方账号信息联系。
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Descriptions column={1} bordered size="small">
|
||||
<Descriptions.Item label="账号">
|
||||
<Typography.Text copyable>{worker?.username || '-'}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="昵称">{worker?.displayName || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="手机号">
|
||||
<Typography.Text copyable>{worker?.phone || '-'}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="账号状态">
|
||||
{formatWorkerStatus(worker?.status || '')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="当前余额">
|
||||
{formatMoney(worker?.wallet?.availableAmount)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Typography.Paragraph className="worker-profile-help-text">
|
||||
建议沟通时一起提供:申请类型、金额、订单号或转账备注、收款方式,以及当前账号信息。
|
||||
</Typography.Paragraph>
|
||||
</Space>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ArrowRightOutlined,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
CustomerServiceOutlined,
|
||||
FireOutlined,
|
||||
PushpinOutlined,
|
||||
QuestionCircleOutlined,
|
||||
@@ -35,6 +36,8 @@ import {
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import { WorkerContactModal } from '@/components/worker/WorkerContactModal'
|
||||
|
||||
import {
|
||||
fetchWorkerHallCategories,
|
||||
fetchWorkerHallOrders,
|
||||
@@ -83,6 +86,7 @@ export default function WorkerHallPage() {
|
||||
const [grabbingId, setGrabbingId] = useState(0)
|
||||
const [sharingOrder, setSharingOrder] = useState<WorkOrder | null>(null)
|
||||
const [leaderboardOpen, setLeaderboardOpen] = useState(false)
|
||||
const [contactOpen, setContactOpen] = useState(false)
|
||||
const [leaderboardPeriod, setLeaderboardPeriod] = useState<WorkerLeaderboardPeriod>('all')
|
||||
const [sharingQuantity, setSharingQuantity] = useState(1)
|
||||
const [submittingSharing, setSubmittingSharing] = useState(false)
|
||||
@@ -236,6 +240,7 @@ export default function WorkerHallPage() {
|
||||
setFailedImageKeys({})
|
||||
}, [allCategories.length, allIconUrl])
|
||||
const worker = profileQuery.data?.data.worker
|
||||
const financeConfig = profileQuery.data?.data.financeConfig
|
||||
const isInternalWorker = worker?.workerType === 'internal'
|
||||
const activeCount = Number(profileQuery.data?.data.summary.activeOrderCount || 0)
|
||||
const pendingAcceptanceCount = hallSummaryQuery.data?.pendingAcceptanceCount || 0
|
||||
@@ -411,6 +416,16 @@ export default function WorkerHallPage() {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="worker-hall-mobile-contact-pill"
|
||||
onClick={() => setContactOpen(true)}
|
||||
aria-label="联系客服"
|
||||
>
|
||||
<CustomerServiceOutlined className="worker-hall-mobile-contact-icon" />
|
||||
<span>联系客服</span>
|
||||
</button>
|
||||
|
||||
<div className="worker-hall-mobile-top-actions">
|
||||
<Tooltip title={`抢单确认已${grabConfirmEnabled ? '开启' : '关闭'}`}>
|
||||
<Button
|
||||
@@ -784,6 +799,9 @@ export default function WorkerHallPage() {
|
||||
<Button icon={<TrophyOutlined />} onClick={() => setLeaderboardOpen(true)}>
|
||||
接单排行榜
|
||||
</Button>
|
||||
<Button icon={<CustomerServiceOutlined />} onClick={() => setContactOpen(true)}>
|
||||
联系客服
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1298,6 +1316,14 @@ export default function WorkerHallPage() {
|
||||
) : null}
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
<WorkerContactModal
|
||||
open={contactOpen}
|
||||
onClose={() => setContactOpen(false)}
|
||||
isMobile={isMobile}
|
||||
worker={worker}
|
||||
financeConfig={financeConfig}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ import type { WorkerFinanceConfig, WorkerSessionDevice, WorkerUser } from '@/typ
|
||||
import { formatDateTime } from '@/utils/date-time'
|
||||
import { isWorkerPasswordStrong, WORKER_PASSWORD_RULE_MESSAGE } from '@/utils/worker-password'
|
||||
|
||||
import { formatMoney, formatWorkerStatus } from './worker-profile-view-utils'
|
||||
import { WorkerContactModal } from '@/components/worker/WorkerContactModal'
|
||||
import { formatWorkerStatus } from './worker-profile-view-utils'
|
||||
|
||||
export type PasswordFormValues = {
|
||||
currentPassword?: string
|
||||
@@ -184,60 +185,13 @@ export function WorkerProfileAccountModals({
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="联系管理员"
|
||||
<WorkerContactModal
|
||||
open={contactOpen}
|
||||
width={isMobile ? '92%' : 500}
|
||||
destroyOnHidden
|
||||
footer={
|
||||
<Button type="primary" onClick={() => setContactOpen(false)}>
|
||||
我知道了
|
||||
</Button>
|
||||
}
|
||||
onCancel={() => setContactOpen(false)}
|
||||
>
|
||||
<Space direction="vertical" size={16} className="full-width">
|
||||
<Alert
|
||||
showIcon
|
||||
type="info"
|
||||
message="如需取消已接订单,请扫码联系管理员处理。充值、提现与审核问题也由后台管理员人工处理。"
|
||||
/>
|
||||
{financeConfig?.adminContact?.wechatQrCodeImage ? (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Image
|
||||
width={200}
|
||||
src={
|
||||
financeConfig.adminContact.wechatQrCodeImage.mediumUrl ||
|
||||
financeConfig.adminContact.wechatQrCodeImage.url
|
||||
}
|
||||
alt="管理员微信二维码"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Typography.Text type="secondary">
|
||||
管理员暂未配置微信二维码,请按下方账号信息联系。
|
||||
</Typography.Text>
|
||||
)}
|
||||
<Descriptions column={1} bordered size="small">
|
||||
<Descriptions.Item label="账号">
|
||||
<Typography.Text copyable>{worker?.username || '-'}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="昵称">{worker?.displayName || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="手机号">
|
||||
<Typography.Text copyable>{worker?.phone || '-'}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="账号状态">
|
||||
{formatWorkerStatus(worker?.status || '')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="当前余额">
|
||||
{formatMoney(worker?.wallet.availableAmount)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Typography.Paragraph className="worker-profile-help-text">
|
||||
建议沟通时一起提供:申请类型、金额、订单号或转账备注、收款方式,以及当前账号信息。
|
||||
</Typography.Paragraph>
|
||||
</Space>
|
||||
</Modal>
|
||||
onClose={() => setContactOpen(false)}
|
||||
isMobile={isMobile}
|
||||
worker={worker}
|
||||
financeConfig={financeConfig}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="修改昵称"
|
||||
|
||||
@@ -2308,6 +2308,62 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.worker-hall-mobile-contact-pill {
|
||||
background: #fff7ed;
|
||||
border: 1px solid #fed7aa;
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #c2410c;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 2px rgba(234, 88, 12, 0.08);
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.worker-hall-mobile-contact-pill:hover,
|
||||
.worker-hall-mobile-contact-pill:focus-visible {
|
||||
background: #ffedd5;
|
||||
border-color: #fdba74;
|
||||
color: #9a3412;
|
||||
}
|
||||
|
||||
.worker-hall-mobile-contact-pill:active {
|
||||
transform: scale(0.96);
|
||||
background: #fed7aa;
|
||||
}
|
||||
|
||||
.worker-hall-mobile-contact-icon {
|
||||
font-size: 13px;
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.worker-hall-mobile-stat-pill {
|
||||
padding: 4px 8px;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.worker-hall-mobile-contact-pill {
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
.worker-hall-mobile-contact-pill span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 游戏 / 一级分类横向卡片条 */
|
||||
|
||||
@@ -200,3 +200,44 @@
|
||||
4. **自动化测试与全量构建**:
|
||||
- 前端 59 个自动化测试全绿(**59 pass / 0 fail**);
|
||||
- `format:check`、`lint:check`、`typecheck`、`build` 全部通过。
|
||||
|
||||
---
|
||||
|
||||
## 八、 专项优化:打手抢单大厅首页新增「联系客服」入口与通用客服弹窗组件(2026-09-21 已闭环)
|
||||
|
||||
### 1. 现状与用户诉求(User Feedback & Target)
|
||||
- **用户反馈**:“首页 增加联系客服 现在联系客服藏的太深了”,并附带手机端首页截图,用红圈标注了移动端顶部状态栏(`余额 / 上限` 胶囊右侧、操作按钮区左侧的空白区域)。
|
||||
- **目标**:
|
||||
1. 在抢单大厅首页移动端顶部状态栏(红圈标注的核心首屏黄金位置)增加醒目高质感的【联系客服】胶囊按钮;
|
||||
2. 在桌面端抢单大厅头部操作栏同步补齐【联系客服】入口,打通双端闭环;
|
||||
3. 将原本深藏在个人中心底层的“联系客服/管理员”弹窗抽象为全站通用的 `WorkerContactModal` 组件,统一维护二维码、账号、余额等信息,提供一键复制与标准沟通提示。
|
||||
|
||||
### 2. 证据分类与代码级根因(Evidence & Root Cause)
|
||||
- **已知信息 [确信度: 100%]**:
|
||||
1. 官方公告栏循环播放:“☆接单请先个人中心联系客服☆方便客服处理”,证明新打手或接单打手频繁需要找客服沟通充值、提现或报备;
|
||||
2. 原联系管理员弹窗代码内联在 `apps/frontend/src/pages/worker/WorkerProfileAccountModals.tsx:187-240`,打手必须切换至“个人中心”页面并向下滑动到底部才能找到入口,链路过长、深度太深;
|
||||
3. `WorkerHallPage.tsx` 中的 `profileQuery.data?.data` 原生就包含了 `worker` 实体与 `financeConfig`(含管理员微信二维码配置 `adminContact.wechatQrCodeImage`),数据已就绪,仅缺前端呈现入口。
|
||||
- **未知/风险预判 [确信度: 100%]**:
|
||||
- 移动端 375px 或超窄屏(如 320px)下顶部栏元素横向空间是否会挤压拥挤;
|
||||
- 应对对策:采用弹性缩放与媒体查询控制,超小屏下保持单行不折叠。
|
||||
|
||||
### 3. 解决对策与落地成果(Solution & Milestones)
|
||||
1. **抽离通用联系客服弹窗组件**:
|
||||
- 新建 `apps/frontend/src/components/worker/WorkerContactModal.tsx`,集中管理微信二维码大图、当前打手账号、昵称、手机号、账号状态、可用余额,以及一键复制和温馨沟通提示;
|
||||
- 在 `WorkerProfileAccountModals.tsx` 与 `WorkerHallPage.tsx` 中全面复用该组件,消灭重复代码。
|
||||
2. **移动端黄金位置新增【联系客服】胶囊按钮**:
|
||||
- 在 `WorkerHallPage.tsx` 移动端顶部状态条(用户红圈位置)插入专属胶囊:
|
||||
```html
|
||||
<button class="worker-hall-mobile-contact-pill" onClick={() => setContactOpen(true)}>
|
||||
<CustomerServiceOutlined />
|
||||
<span>联系客服</span>
|
||||
</button>
|
||||
```
|
||||
- 采用浅暖橙背景(`#fff7ed`)+ 优雅橙边框(`#fed7aa`)+ 微动效,与打手端整体暖橙调性浑然一体,点击即弹出客服微信与账号卡片。
|
||||
3. **桌面端同步补齐入口**:
|
||||
- 在桌面端头部操作按钮栏(刷新与排行榜旁)新增 `<Button icon={<CustomerServiceOutlined />} onClick={() => setContactOpen(true)}>联系客服</Button>`。
|
||||
4. **验证门禁全绿通过 [确信度: 100%]**:
|
||||
- 前端 59 个自动化测试全绿(**59 pass / 0 fail**);
|
||||
- 前端 `format:check`、`lint:check`、`typecheck`、`build` 均零错误零警告通过;
|
||||
- 后端 504 个自动化测试全绿(**501 pass / 3 skipped / 0 fail**)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user