import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { STORE_STAFF_ROLE_LABELS, type StoreStaffRole } from '@dukang/shared-types'; import { useStoreSession } from '../contexts/StoreSessionContext'; import { getStoreProfile, request } from '../lib/api'; type StaffItem = { id: string; name: string; phone: string; staffRole: StoreStaffRole; status: string; storeIds: string[]; stores: Array<{ storeId: string; name: string }>; }; type StoreOption = { storeId: string; name: string }; function maskPhone(phone: string) { if (!phone || phone.length < 7) return phone; return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); } export default function StaffPage() { const navigate = useNavigate(); const { store } = useStoreSession(); const profile = store ?? getStoreProfile(); const [list, setList] = useState([]); const [ownedStores, setOwnedStores] = useState([]); const [msg, setMsg] = useState(''); const [showForm, setShowForm] = useState(false); const [submitting, setSubmitting] = useState(false); const [form, setForm] = useState({ phone: '', name: '', storeIds: [] as string[] }); async function reload() { const [staff, stores] = await Promise.all([ request('SHOP_H5', '/shop/staff'), request('SHOP_H5', '/shop/auth/stores'), ]); setList(staff); setOwnedStores(stores); } useEffect(() => { if (!profile?.isPrimary) { navigate('/mine', { replace: true }); return; } void reload().catch((e) => setMsg(e instanceof Error ? e.message : '加载失败')); }, [navigate, profile?.isPrimary]); async function createStaff() { const phone = form.phone.trim(); const name = form.name.trim(); if (!/^1[3-9]\d{9}$/.test(phone)) { setMsg('请输入正确的手机号'); return; } if (!name) { setMsg('请填写姓名'); return; } setMsg(''); setSubmitting(true); try { await request('SHOP_H5', '/shop/staff', { method: 'POST', body: JSON.stringify({ phone, name, storeIds: form.storeIds.length ? form.storeIds : ownedStores.map((s) => s.storeId), }), }); setShowForm(false); setForm({ phone: '', name: '', storeIds: [] }); await reload(); } catch (e) { setMsg(e instanceof Error ? e.message : '创建失败'); } finally { setSubmitting(false); } } async function toggleStatus(item: StaffItem) { const next = item.status === 'ACTIVE' ? 'DISABLED' : 'ACTIVE'; try { await request('SHOP_H5', `/shop/staff/${item.id}`, { method: 'PUT', body: JSON.stringify({ status: next }), }); await reload(); } catch (e) { setMsg(e instanceof Error ? e.message : '更新失败'); } } function toggleStoreId(storeId: string) { setForm((prev) => { const allIds = ownedStores.map((s) => s.storeId); // 空数组表示「全部」;取消某一家时转为「除该店外的全部」 if (prev.storeIds.length === 0) { return { ...prev, storeIds: allIds.filter((id) => id !== storeId) }; } const next = prev.storeIds.includes(storeId) ? prev.storeIds.filter((id) => id !== storeId) : [...prev.storeIds, storeId]; // 又选回全部时,恢复默认空数组语义 if (next.length === allIds.length) { return { ...prev, storeIds: [] }; } return { ...prev, storeIds: next }; }); } const selectedCount = form.storeIds.length === 0 ? ownedStores.length : form.storeIds.length; return (

{showForm ? '添加子账号' : '子账号管理'}

{!showForm ? ( ) : ( )}
{!showForm ? (
group

门店员工子账号

共 {list.length} 人 · 可授权员工用手机号登录门店端进行核销

) : null} {msg ? (

{msg}

) : null} {showForm ? (
smartphone setForm((f) => ({ ...f, phone: e.target.value.replace(/\D/g, '').slice(0, 11) })) } />
badge setForm((f) => ({ ...f, name: e.target.value }))} />

绑定门店 {form.storeIds.length === 0 ? `(默认全部 ${ownedStores.length} 家)` : `(已选 ${selectedCount} 家)`}

{ownedStores.map((s) => { const checked = form.storeIds.length === 0 || form.storeIds.includes(s.storeId); return ( ); })}
) : (

账号列表

    {list.map((item) => { const active = item.status === 'ACTIVE'; const initial = (item.name || item.phone || '?').slice(0, 1); return (
  • {initial}
    {item.name || '未命名'} {active ? '启用' : '停用'}

    {maskPhone(item.phone)}

    {STORE_STAFF_ROLE_LABELS[item.staffRole] ?? item.staffRole} {item.stores.length ? ` · ${item.stores.map((s) => s.name).join('、')}` : ''}

  • ); })}
{!list.length ? (
person_off

暂无子账号

) : null}
)}
); }