门店开通流程
This commit is contained in:
@@ -1,42 +1,33 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { isLoggedIn, request } from '../lib/api';
|
||||
import { storeStatusLabel, storeStatusPillClass, type StoreStatusValue } from '../lib/storeStatus';
|
||||
|
||||
type StatusFilter = 'ALL' | 'OPEN' | 'PAUSED' | 'CLOSED';
|
||||
|
||||
function statusPill(status: string) {
|
||||
const s = String(status).toUpperCase();
|
||||
if (s === 'OPEN') return { cls: 'partner-status-pill--open', label: '营业中' };
|
||||
if (s === 'PAUSED') return { cls: 'partner-status-pill--paused', label: '暂时闭店' };
|
||||
return { cls: 'partner-status-pill--closed', label: '已关闭' };
|
||||
}
|
||||
type StatusFilter = 'ALL' | StoreStatusValue;
|
||||
|
||||
const FILTERS: { key: StatusFilter; label: string }[] = [
|
||||
{ key: 'ALL', label: '全部' },
|
||||
{ key: 'OPEN', label: '营业中' },
|
||||
{ key: 'PAUSED', label: '暂时闭店' },
|
||||
{ key: 'CLOSED', label: '已关闭' },
|
||||
{ key: 'CLOSED', label: '关闭' },
|
||||
];
|
||||
|
||||
function auditLabel(store: Record<string, unknown>) {
|
||||
const audits = store.audits as Array<{ status?: string }> | undefined;
|
||||
const s = String(audits?.[0]?.status ?? '').toUpperCase();
|
||||
if (s === 'PENDING') return { cls: 'partner-status-pill--paused', label: '待审核' };
|
||||
if (s === 'REJECTED') return { cls: 'partner-status-pill--closed', label: '已驳回' };
|
||||
if (s === 'APPROVED') return { cls: 'partner-status-pill--open', label: '已通过' };
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function StoreListPage() {
|
||||
const navigate = useNavigate();
|
||||
const [stores, setStores] = useState<Array<Record<string, unknown>>>([]);
|
||||
const [q, setQ] = useState('');
|
||||
const [filter, setFilter] = useState<StatusFilter>('ALL');
|
||||
const [updatingId, setUpdatingId] = useState<string | null>(null);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const loadStores = useCallback(() => {
|
||||
return request<Array<Record<string, unknown>>>('PARTNER_H5', '/partner/stores').then(setStores);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn()) { navigate('/login'); return; }
|
||||
request<Array<Record<string, unknown>>>('PARTNER_H5', '/partner/stores').then(setStores);
|
||||
}, [navigate]);
|
||||
void loadStores();
|
||||
}, [navigate, loadStores]);
|
||||
|
||||
const filtered = useMemo(() => stores.filter((s) => {
|
||||
const matchQ = !q || String(s.name).includes(q) || String(s.address).includes(q);
|
||||
@@ -44,6 +35,26 @@ export default function StoreListPage() {
|
||||
return matchQ && matchStatus;
|
||||
}), [stores, q, filter]);
|
||||
|
||||
async function updateStatus(storeId: string, next: StoreStatusValue) {
|
||||
if (next === 'CLOSED') {
|
||||
const ok = window.confirm('关闭后不可恢复营业,确认关闭该门店?');
|
||||
if (!ok) return;
|
||||
}
|
||||
setUpdatingId(storeId);
|
||||
setError('');
|
||||
try {
|
||||
await request('PARTNER_H5', `/partner/stores/${storeId}/status`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ status: next }),
|
||||
});
|
||||
await loadStores();
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : '状态更新失败');
|
||||
} finally {
|
||||
setUpdatingId(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page partner-store-page">
|
||||
<header className="header app-page-header">
|
||||
@@ -55,6 +66,8 @@ export default function StoreListPage() {
|
||||
<p className="text-muted body-md">管理您的合作门店及其运营状态</p>
|
||||
</div>
|
||||
|
||||
{error && <p className="partner-form-error" role="alert" style={{ margin: '0 20px 12px' }}>{error}</p>}
|
||||
|
||||
<div className="partner-sticky-filter">
|
||||
<div className="partner-search">
|
||||
<span className="material-symbols-outlined">search</span>
|
||||
@@ -79,29 +92,56 @@ export default function StoreListPage() {
|
||||
{filtered.length === 0 && <div className="empty">暂无门店</div>}
|
||||
|
||||
{filtered.map((s) => {
|
||||
const pill = statusPill(String(s.status));
|
||||
const audit = auditLabel(s);
|
||||
const dim = String(s.status).toUpperCase() === 'CLOSED';
|
||||
const storeId = String(s.id);
|
||||
const currentStatus = String(s.status).toUpperCase() as StoreStatusValue;
|
||||
const dim = currentStatus === 'CLOSED';
|
||||
const storeName = String(s.name || '未命名门店');
|
||||
const busy = updatingId === storeId;
|
||||
return (
|
||||
<div key={String(s.id)} className={`partner-store-card${dim ? ' partner-store-card--dim' : ''}`}>
|
||||
<Link to={`/stores/${s.id}`} style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||
<div key={storeId} className={`partner-store-card${dim ? ' partner-store-card--dim' : ''}`}>
|
||||
<Link to={`/stores/${storeId}`} className="partner-store-card-hit" style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||
<div className="partner-store-card-header">
|
||||
<div>
|
||||
<p className="label-md text-muted" style={{ marginBottom: 2 }}>门店名称</p>
|
||||
<h3 className="headline-md">{storeName}</h3>
|
||||
<p className="label-md text-muted" style={{ marginTop: 4 }}>{String(s.address || s.district || '')}</p>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}>
|
||||
<span className={`partner-status-pill ${pill.cls}`}>{pill.label}</span>
|
||||
{audit && <span className={`partner-status-pill ${audit.cls}`}>{audit.label}</span>}
|
||||
</div>
|
||||
<span className={`partner-status-pill ${storeStatusPillClass(currentStatus)}`}>
|
||||
{storeStatusLabel(currentStatus)}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="partner-store-card-actions">
|
||||
<button type="button" className="btn btn-outline" style={{ fontSize: 12, padding: '8px 12px' }}>暂时闭店</button>
|
||||
<button type="button" className="btn btn-outline" style={{ fontSize: 12, padding: '8px 12px', borderColor: 'var(--color-subtle-gray)', color: 'var(--color-subtle-gray)' }}>正式关闭</button>
|
||||
<Link to={`/stores/${s.id}`} className="partner-menu-icon" style={{ width: 40, height: 40, borderRadius: 8, textDecoration: 'none' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline"
|
||||
style={{ fontSize: 12, padding: '8px 12px' }}
|
||||
disabled={busy || currentStatus === 'CLOSED' || currentStatus === 'PAUSED'}
|
||||
onClick={() => void updateStatus(storeId, 'PAUSED')}
|
||||
>
|
||||
暂时闭店
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline"
|
||||
style={{ fontSize: 12, padding: '8px 12px', borderColor: 'var(--color-subtle-gray)', color: 'var(--color-subtle-gray)' }}
|
||||
disabled={busy || currentStatus === 'CLOSED'}
|
||||
onClick={() => void updateStatus(storeId, 'CLOSED')}
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
{currentStatus === 'PAUSED' && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-outline"
|
||||
style={{ fontSize: 12, padding: '8px 12px' }}
|
||||
disabled={busy}
|
||||
onClick={() => void updateStatus(storeId, 'OPEN')}
|
||||
>
|
||||
恢复营业
|
||||
</button>
|
||||
)}
|
||||
<Link to={`/stores/${storeId}`} className="partner-menu-icon" style={{ width: 40, height: 40, borderRadius: 8, textDecoration: 'none' }}>
|
||||
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>edit</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user