fix:提交修复6个问题

This commit is contained in:
ljy
2026-07-08 23:01:18 +08:00
parent 99be8e0237
commit ab9654564e
43 changed files with 3671 additions and 277 deletions
+156
View File
@@ -0,0 +1,156 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import PageHeader from '@dukang/shared-ui/PageHeader';
import {
AccountStatus,
PARTNER_STAFF_ROLE_LABELS,
PartnerStaffRole,
type PartnerStaffItem,
} from '@dukang/shared-types';
import { deletePartnerStaff, listPartnerStaff, updatePartnerStaff } from '../lib/staff';
export default function StaffListPage() {
const navigate = useNavigate();
const [staff, setStaff] = useState<PartnerStaffItem[]>([]);
const [q, setQ] = useState('');
const [error, setError] = useState('');
const [busyId, setBusyId] = useState<string | null>(null);
const loadStaff = useCallback(() => {
return listPartnerStaff().then(setStaff).catch((e) => {
setError(e instanceof Error ? e.message : '加载失败');
});
}, []);
useEffect(() => {
void loadStaff();
}, [loadStaff]);
const filtered = useMemo(() => staff.filter((item) => {
if (!q.trim()) return true;
const keyword = q.trim();
return item.name.includes(keyword) || item.phone.includes(keyword);
}), [staff, q]);
async function toggleStatus(item: PartnerStaffItem) {
const next = item.status === AccountStatus.ACTIVE ? AccountStatus.DISABLED : AccountStatus.ACTIVE;
setBusyId(item.id);
setError('');
try {
await updatePartnerStaff(item.id, { status: next });
await loadStaff();
} catch (e) {
setError(e instanceof Error ? e.message : '操作失败');
} finally {
setBusyId(null);
}
}
async function removeStaff(item: PartnerStaffItem) {
const ok = window.confirm(`确认删除子账号「${item.name}」?`);
if (!ok) return;
setBusyId(item.id);
setError('');
try {
await deletePartnerStaff(item.id);
await loadStaff();
} catch (e) {
setError(e instanceof Error ? e.message : '删除失败');
} finally {
setBusyId(null);
}
}
async function editStaff(item: PartnerStaffItem) {
const name = window.prompt('修改姓名', item.name);
if (name == null || !name.trim()) return;
const roleOptions = Object.values(PartnerStaffRole);
const roleLabels = roleOptions.map((r) => PARTNER_STAFF_ROLE_LABELS[r]).join(' / ');
const roleInput = window.prompt(`分配角色(${roleLabels}`, item.staffRole);
if (roleInput == null) return;
const staffRole = roleOptions.find((r) => r === roleInput || PARTNER_STAFF_ROLE_LABELS[r] === roleInput);
if (!staffRole) {
setError('无效的角色');
return;
}
setBusyId(item.id);
setError('');
try {
await updatePartnerStaff(item.id, { name: name.trim(), staffRole });
await loadStaff();
} catch (e) {
setError(e instanceof Error ? e.message : '编辑失败');
} finally {
setBusyId(null);
}
}
return (
<div className="page-no-tab partner-staff-page">
<PageHeader title="子账号管理" onBack={() => navigate('/center')} />
<div style={{ padding: '0 20px 16px' }}>
<div className="partner-search">
<span className="material-symbols-outlined">search</span>
<input placeholder="搜索姓名或手机号" value={q} onChange={(e) => setQ(e.target.value)} />
</div>
</div>
{error && <p className="partner-form-error" role="alert" style={{ margin: '0 20px 12px' }}>{error}</p>}
<div style={{ padding: '0 20px 100px' }}>
{filtered.length === 0 && (
<div className="empty">
<p></p>
<p className="label-md text-muted"></p>
</div>
)}
{filtered.map((item) => {
const active = item.status === AccountStatus.ACTIVE;
const busy = busyId === item.id;
return (
<div key={item.id} className="partner-staff-card">
<div className="partner-staff-avatar">{item.name.slice(0, 1)}</div>
<div className="partner-staff-info">
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<span className="headline-md">{item.name}</span>
<span className="partner-role-badge partner-role-badge--subtle">
{PARTNER_STAFF_ROLE_LABELS[item.staffRole]}
</span>
</div>
<p className="label-md text-muted">{item.phone}</p>
</div>
<div className="partner-staff-actions">
<button
type="button"
className={`partner-toggle${active ? ' partner-toggle--on' : ''}`}
disabled={busy}
aria-label={active ? '禁用' : '启用'}
onClick={() => void toggleStatus(item)}
>
<span className="partner-toggle-thumb" />
</button>
<div style={{ display: 'flex', gap: 12 }}>
<button type="button" className="partner-icon-btn" disabled={busy} onClick={() => void editStaff(item)}>
<span className="material-symbols-outlined">edit</span>
</button>
<button type="button" className="partner-icon-btn partner-icon-btn--danger" disabled={busy} onClick={() => void removeStaff(item)}>
<span className="material-symbols-outlined">delete</span>
</button>
</div>
</div>
</div>
);
})}
</div>
<div className="partner-sticky-footer">
<Link to="/center/staff/new" className="partner-btn-primary" style={{ display: 'flex', textDecoration: 'none' }}>
<span className="material-symbols-outlined">person_add</span>
</Link>
</div>
</div>
);
}