门店账户多账号
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
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 };
|
||||
|
||||
export default function StaffPage() {
|
||||
const navigate = useNavigate();
|
||||
const { store } = useStoreSession();
|
||||
const profile = store ?? getStoreProfile();
|
||||
const [list, setList] = useState<StaffItem[]>([]);
|
||||
const [ownedStores, setOwnedStores] = useState<StoreOption[]>([]);
|
||||
const [msg, setMsg] = useState('');
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [form, setForm] = useState({ phone: '', name: '', storeIds: [] as string[] });
|
||||
|
||||
async function reload() {
|
||||
const [staff, stores] = await Promise.all([
|
||||
request<StaffItem[]>('SHOP_H5', '/shop/staff'),
|
||||
request<StoreOption[]>('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() {
|
||||
setMsg('');
|
||||
try {
|
||||
await request('SHOP_H5', '/shop/staff', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
phone: form.phone.trim(),
|
||||
name: form.name.trim(),
|
||||
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 : '创建失败');
|
||||
}
|
||||
}
|
||||
|
||||
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) => ({
|
||||
...prev,
|
||||
storeIds: prev.storeIds.includes(storeId)
|
||||
? prev.storeIds.filter((id) => id !== storeId)
|
||||
: [...prev.storeIds, storeId],
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shop-staff-page">
|
||||
<header className="shop-staff-header">
|
||||
<button type="button" className="shop-staff-back" onClick={() => navigate('/mine')}>
|
||||
返回
|
||||
</button>
|
||||
<h1>子账号管理</h1>
|
||||
<button type="button" className="shop-staff-add" onClick={() => setShowForm((v) => !v)}>
|
||||
{showForm ? '取消' : '添加'}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{msg ? <p className="shop-staff-msg">{msg}</p> : null}
|
||||
|
||||
{showForm ? (
|
||||
<div className="shop-staff-form">
|
||||
<input
|
||||
placeholder="手机号"
|
||||
value={form.phone}
|
||||
onChange={(e) => setForm((f) => ({ ...f, phone: e.target.value }))}
|
||||
/>
|
||||
<input
|
||||
placeholder="姓名"
|
||||
value={form.name}
|
||||
onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}
|
||||
/>
|
||||
<p className="shop-staff-form-label">绑定门店</p>
|
||||
<div className="shop-staff-store-picks">
|
||||
{ownedStores.map((s) => (
|
||||
<label key={s.storeId}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={form.storeIds.includes(s.storeId) || form.storeIds.length === 0}
|
||||
onChange={() => toggleStoreId(s.storeId)}
|
||||
/>
|
||||
{s.name}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
<button type="button" onClick={() => void createStaff()}>
|
||||
创建子账号
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<ul className="shop-staff-list">
|
||||
{list.map((item) => (
|
||||
<li key={item.id} className="shop-staff-item">
|
||||
<div>
|
||||
<strong>{item.name}</strong>
|
||||
<span>{item.phone}</span>
|
||||
<span>
|
||||
{STORE_STAFF_ROLE_LABELS[item.staffRole] ?? item.staffRole} ·{' '}
|
||||
{item.status === 'ACTIVE' ? '启用' : '停用'}
|
||||
</span>
|
||||
<span>{item.stores.map((s) => s.name).join('、')}</span>
|
||||
</div>
|
||||
<button type="button" onClick={() => void toggleStatus(item)}>
|
||||
{item.status === 'ACTIVE' ? '停用' : '启用'}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{!list.length ? <p className="shop-staff-empty">暂无子账号</p> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user