新增打手改昵称卡功能默认一张且一年冷却
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Checkbox,
|
||||
Descriptions,
|
||||
Form,
|
||||
Image,
|
||||
@@ -24,6 +25,11 @@ export type PasswordFormValues = {
|
||||
confirmPassword?: string
|
||||
}
|
||||
|
||||
export type RenameFormValues = {
|
||||
displayName?: string
|
||||
confirmed?: boolean
|
||||
}
|
||||
|
||||
export function WorkerProfileAccountModals({
|
||||
isMobile,
|
||||
worker,
|
||||
@@ -41,6 +47,11 @@ export function WorkerProfileAccountModals({
|
||||
submitPasswordChange,
|
||||
contactOpen,
|
||||
setContactOpen,
|
||||
renameOpen,
|
||||
setRenameOpen,
|
||||
submittingRename,
|
||||
renameForm,
|
||||
submitRename,
|
||||
}: {
|
||||
isMobile: boolean
|
||||
worker: WorkerUser | undefined
|
||||
@@ -58,6 +69,11 @@ export function WorkerProfileAccountModals({
|
||||
submitPasswordChange: (values: PasswordFormValues) => Promise<void>
|
||||
contactOpen: boolean
|
||||
setContactOpen: (open: boolean) => void
|
||||
renameOpen: boolean
|
||||
setRenameOpen: (open: boolean) => void
|
||||
submittingRename: boolean
|
||||
renameForm: FormInstance<RenameFormValues>
|
||||
submitRename: (values: RenameFormValues) => Promise<void>
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
@@ -220,6 +236,74 @@ export function WorkerProfileAccountModals({
|
||||
</Typography.Paragraph>
|
||||
</Space>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="修改昵称"
|
||||
open={renameOpen}
|
||||
width={isMobile ? '92%' : 560}
|
||||
destroyOnHidden
|
||||
footer={null}
|
||||
onCancel={() => setRenameOpen(false)}
|
||||
>
|
||||
<Alert
|
||||
showIcon
|
||||
type="warning"
|
||||
message="修改昵称后将锁定一年"
|
||||
description={
|
||||
<Space direction="vertical" size={0}>
|
||||
<Typography.Text type="danger" strong>
|
||||
昵称修改成功后,一年内无法再次修改,请谨慎操作!
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
当前剩余改名卡 {worker?.renameCards ?? 1} 张,本次修改昵称将消耗 1 张。
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<Form<RenameFormValues>
|
||||
form={renameForm}
|
||||
layout="vertical"
|
||||
onFinish={(values) => void submitRename(values)}
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
<Form.Item label="当前昵称">
|
||||
<Typography.Text strong>
|
||||
{worker?.displayName || worker?.username || '-'}
|
||||
</Typography.Text>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="displayName"
|
||||
label="新昵称"
|
||||
rules={[
|
||||
{ required: true, whitespace: true, message: '请输入新的昵称' },
|
||||
{ max: 32, message: '昵称最长 32 个字符' },
|
||||
{
|
||||
validator: (_, value) =>
|
||||
value && value.trim() === String(worker?.displayName || '').trim()
|
||||
? Promise.reject(new Error('新昵称不能与当前昵称一致'))
|
||||
: Promise.resolve(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input allowClear placeholder="最长 32 个字符,全平台不可重复" autoComplete="off" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="confirmed"
|
||||
valuePropName="checked"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) =>
|
||||
value ? Promise.resolve() : Promise.reject(new Error('请先勾选确认')),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Checkbox>我已知晓改名后将消耗 1 张改名卡,且一年内无法再次修改用户名</Checkbox>
|
||||
</Form.Item>
|
||||
<Button type="primary" block loading={submittingRename} htmlType="submit">
|
||||
确认修改昵称
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ import {
|
||||
GlobalOutlined,
|
||||
LockOutlined,
|
||||
LogoutOutlined,
|
||||
UserOutlined,
|
||||
WalletOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Avatar, Button, Card, Descriptions, Progress, Space, Tag, Typography } from 'antd'
|
||||
import { Avatar, Button, Card, Descriptions, Progress, Space, Tag, Tooltip, Typography } from 'antd'
|
||||
|
||||
import type {
|
||||
WorkerLevelProgress,
|
||||
@@ -124,6 +125,9 @@ export function WorkerProfileDesktopActions({
|
||||
onAddWithdrawalAccount,
|
||||
onAddAlipayQrCode,
|
||||
onChangePassword,
|
||||
onRename,
|
||||
renameDisabled,
|
||||
renameHint,
|
||||
onManageDevices,
|
||||
onContact,
|
||||
onLogout,
|
||||
@@ -136,6 +140,9 @@ export function WorkerProfileDesktopActions({
|
||||
onAddWithdrawalAccount: () => void
|
||||
onAddAlipayQrCode: () => void
|
||||
onChangePassword: () => void
|
||||
onRename: () => void
|
||||
renameDisabled: boolean
|
||||
renameHint: string
|
||||
onManageDevices: () => void
|
||||
onContact: () => void
|
||||
onLogout: () => void
|
||||
@@ -177,6 +184,11 @@ export function WorkerProfileDesktopActions({
|
||||
补充支付宝收款码
|
||||
</Button>
|
||||
) : null}
|
||||
<Tooltip title={renameHint}>
|
||||
<Button icon={<UserOutlined />} disabled={renameDisabled} onClick={onRename}>
|
||||
修改昵称
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button icon={<LockOutlined />} onClick={onChangePassword}>
|
||||
修改密码
|
||||
</Button>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
LockOutlined,
|
||||
LogoutOutlined,
|
||||
ReloadOutlined,
|
||||
UserSwitchOutlined,
|
||||
WalletOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Alert, Avatar, Button, Progress, Tag } from 'antd'
|
||||
@@ -37,6 +38,8 @@ type WorkerProfileMobileViewProps = {
|
||||
onRecharge: () => void
|
||||
onWithdraw: () => void
|
||||
onChangePassword: () => void
|
||||
onRename: () => void
|
||||
renameDisabled: boolean
|
||||
onManageDevices: () => void
|
||||
onContact: () => void
|
||||
onAddAlipayQrCode: () => void
|
||||
@@ -59,6 +62,8 @@ export function WorkerProfileMobileView({
|
||||
onRecharge,
|
||||
onWithdraw,
|
||||
onChangePassword,
|
||||
onRename,
|
||||
renameDisabled,
|
||||
onManageDevices,
|
||||
onContact,
|
||||
onAddAlipayQrCode,
|
||||
@@ -190,6 +195,14 @@ export function WorkerProfileMobileView({
|
||||
</div>
|
||||
|
||||
<div className="worker-profile-mobile-tools-card">
|
||||
<MobileTool
|
||||
label="修改昵称"
|
||||
icon={<UserSwitchOutlined />}
|
||||
color="#4338ca"
|
||||
background="#eef2ff"
|
||||
disabled={renameDisabled}
|
||||
onClick={onRename}
|
||||
/>
|
||||
<MobileTool
|
||||
label="修改密码"
|
||||
icon={<LockOutlined />}
|
||||
|
||||
@@ -15,12 +15,18 @@ import {
|
||||
deleteWorkerSession,
|
||||
fetchWorkerWalletLedgers,
|
||||
logoutWorker,
|
||||
renameWorkerAccount,
|
||||
saveWorkerAlipayWithdrawalQrCode,
|
||||
} from '@/services/worker'
|
||||
import type { UploadedFile, WorkerSessionDevice } from '@/types/worker-platform'
|
||||
import { formatDateTime } from '@/utils/date-time'
|
||||
import { useIsMobile } from '@/utils/use-is-mobile'
|
||||
import { clearWorkerSession } from '@/utils/worker-auth'
|
||||
import { WorkerProfileAccountModals, type PasswordFormValues } from './WorkerProfileAccountModals'
|
||||
import {
|
||||
WorkerProfileAccountModals,
|
||||
type PasswordFormValues,
|
||||
type RenameFormValues,
|
||||
} from './WorkerProfileAccountModals'
|
||||
import { WorkerProfileFinanceModals } from './WorkerProfileFinanceModals'
|
||||
import { WorkerProfileMobileView } from './WorkerProfileMobileView'
|
||||
import {
|
||||
@@ -80,6 +86,9 @@ export default function WorkerProfilePage() {
|
||||
const [withdrawalAccountOpen, setWithdrawalAccountOpen] = useState(false)
|
||||
const [alipayQrCodeOpen, setAlipayQrCodeOpen] = useState(false)
|
||||
const [passwordOpen, setPasswordOpen] = useState(false)
|
||||
const [renameOpen, setRenameOpen] = useState(false)
|
||||
const [submittingRename, setSubmittingRename] = useState(false)
|
||||
const [renameForm] = Form.useForm<RenameFormValues>()
|
||||
const [contactOpen, setContactOpen] = useState(false)
|
||||
const [devicesOpen, setDevicesOpen] = useState(false)
|
||||
const [submittingRecharge, setSubmittingRecharge] = useState(false)
|
||||
@@ -119,6 +128,15 @@ export default function WorkerProfilePage() {
|
||||
(account) => account.accountChannel === 'alipay',
|
||||
)
|
||||
const canUseFinanceActions = worker?.status === 'active'
|
||||
const renameCards = worker?.renameCards ?? 1
|
||||
const renameLocked =
|
||||
Boolean(worker?.renameUnlockedAt) && Date.parse(worker?.renameUnlockedAt || '') > Date.now()
|
||||
const canRename = renameCards > 0 && !renameLocked
|
||||
const renameHint = renameLocked
|
||||
? `修改昵称后一年内不可再次修改,${formatDateTime(worker?.renameUnlockedAt || null)} 后可改`
|
||||
: renameCards <= 0
|
||||
? '改名卡已用完'
|
||||
: `剩余改名卡 ${renameCards} 张,修改昵称后一年内不可再次修改`
|
||||
|
||||
async function copyInviteCode() {
|
||||
if (!worker?.inviteCode) return
|
||||
@@ -326,6 +344,23 @@ export default function WorkerProfilePage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function submitRename(values: RenameFormValues) {
|
||||
setSubmittingRename(true)
|
||||
try {
|
||||
const response = await renameWorkerAccount({
|
||||
displayName: String(values.displayName || '').trim(),
|
||||
})
|
||||
message.success(`昵称已修改为「${response.data.worker.displayName}」,一年内无法再次修改`)
|
||||
setRenameOpen(false)
|
||||
renameForm.resetFields()
|
||||
await refreshAll()
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '修改昵称失败')
|
||||
} finally {
|
||||
setSubmittingRename(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function submitPasswordChange(values: PasswordFormValues) {
|
||||
setSubmittingPassword(true)
|
||||
try {
|
||||
@@ -440,6 +475,8 @@ export default function WorkerProfilePage() {
|
||||
onRecharge={openRechargeModal}
|
||||
onWithdraw={openWithdrawModal}
|
||||
onChangePassword={() => setPasswordOpen(true)}
|
||||
onRename={() => setRenameOpen(true)}
|
||||
renameDisabled={!canRename}
|
||||
onManageDevices={() => setDevicesOpen(true)}
|
||||
onContact={() => setContactOpen(true)}
|
||||
onAddAlipayQrCode={openAlipayQrCodeModal}
|
||||
@@ -493,6 +530,9 @@ export default function WorkerProfilePage() {
|
||||
onAddWithdrawalAccount={openWithdrawalAccountModal}
|
||||
onAddAlipayQrCode={openAlipayQrCodeModal}
|
||||
onChangePassword={() => setPasswordOpen(true)}
|
||||
onRename={() => setRenameOpen(true)}
|
||||
renameDisabled={!canRename}
|
||||
renameHint={renameHint}
|
||||
onManageDevices={() => setDevicesOpen(true)}
|
||||
onContact={() => setContactOpen(true)}
|
||||
onLogout={submitLogout}
|
||||
@@ -627,6 +667,11 @@ export default function WorkerProfilePage() {
|
||||
submitPasswordChange={submitPasswordChange}
|
||||
contactOpen={contactOpen}
|
||||
setContactOpen={setContactOpen}
|
||||
renameOpen={renameOpen}
|
||||
setRenameOpen={setRenameOpen}
|
||||
submittingRename={submittingRename}
|
||||
renameForm={renameForm}
|
||||
submitRename={submitRename}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -194,6 +194,13 @@ export function changeWorkerPassword(payload: { currentPassword: string; newPass
|
||||
)
|
||||
}
|
||||
|
||||
export function renameWorkerAccount(payload: { displayName: string }) {
|
||||
return apiPost<{ worker: WorkerUser; reloginRequired: boolean }>(
|
||||
'/api/v1/worker/profile/rename',
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchWorkerHallOrders(params?: Record<string, unknown>) {
|
||||
return apiGet<WorkerHallOrdersResponse>('/api/v1/worker/hall/orders', params)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@ export type WorkerUser = {
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
reviewedAt: string | null
|
||||
/** 剩余改名卡数量,注册默认 1 张 */
|
||||
renameCards: number
|
||||
lastRenamedAt: string | null
|
||||
/** 上次改名后的一年锁定期结束时间 */
|
||||
renameUnlockedAt: string | null
|
||||
}
|
||||
|
||||
export type WorkerSessionDevice = {
|
||||
|
||||
Reference in New Issue
Block a user