fix:提交修改信息
This commit is contained in:
@@ -65,6 +65,9 @@ all | pending_pay | pending_ship | pending_receive | completed
|
|||||||
| /orders/:id | OrderDetailPage | /partner/orders/:id |
|
| /orders/:id | OrderDetailPage | /partner/orders/:id |
|
||||||
| /center | CenterPage | /partner/me |
|
| /center | CenterPage | /partner/me |
|
||||||
| /center/bills | BillsPage | /partner/settlement/* |
|
| /center/bills | BillsPage | /partner/settlement/* |
|
||||||
|
| /reshipments | ReshipPage | /partner/after-sales/reshipments |
|
||||||
|
| /reports/weekly | WeeklyReportPage | /partner/reports/weekly |
|
||||||
|
| /leaderboard | LeaderboardPage | /partner/dashboard/leaderboard |
|
||||||
|
|
||||||
## admin-web 路由(总部)
|
## admin-web 路由(总部)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ export type StoreDraftForm = {
|
|||||||
bankBranch: string;
|
bankBranch: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type StoreDraft = {
|
||||||
|
step: number;
|
||||||
|
form: StoreDraftForm;
|
||||||
|
};
|
||||||
|
|
||||||
export const STORE_DRAFT_KEY = 'partner_store_draft_v1';
|
export const STORE_DRAFT_KEY = 'partner_store_draft_v1';
|
||||||
|
|
||||||
export const defaultStoreForm = (): StoreDraftForm => ({
|
export const defaultStoreForm = (): StoreDraftForm => ({
|
||||||
@@ -22,18 +27,39 @@ export const defaultStoreForm = (): StoreDraftForm => ({
|
|||||||
bankBranch: '',
|
bankBranch: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
export function loadStoreDraft(): StoreDraftForm | null {
|
function normalizeForm(raw: Record<string, unknown>): StoreDraftForm {
|
||||||
|
const base = defaultStoreForm();
|
||||||
|
return {
|
||||||
|
name: String(raw.name ?? base.name),
|
||||||
|
phone: String(raw.phone ?? base.phone),
|
||||||
|
district: String(raw.district ?? base.district),
|
||||||
|
address: String(raw.address ?? base.address),
|
||||||
|
intro: String(raw.intro ?? base.intro),
|
||||||
|
bankAccountName: String(raw.bankAccountName ?? base.bankAccountName),
|
||||||
|
bankAccountNo: String(raw.bankAccountNo ?? base.bankAccountNo),
|
||||||
|
bankBranch: String(raw.bankBranch ?? base.bankBranch),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadStoreDraft(): StoreDraft | null {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(STORE_DRAFT_KEY);
|
const raw = localStorage.getItem(STORE_DRAFT_KEY);
|
||||||
if (!raw) return null;
|
if (!raw) return null;
|
||||||
return { ...defaultStoreForm(), ...JSON.parse(raw) };
|
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
||||||
|
if (parsed.form && typeof parsed.form === 'object') {
|
||||||
|
return {
|
||||||
|
step: Math.min(3, Math.max(1, Number(parsed.step) || 1)),
|
||||||
|
form: normalizeForm(parsed.form as Record<string, unknown>),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { step: 1, form: normalizeForm(parsed) };
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveStoreDraft(form: StoreDraftForm) {
|
export function saveStoreDraft(draft: StoreDraft) {
|
||||||
localStorage.setItem(STORE_DRAFT_KEY, JSON.stringify(form));
|
localStorage.setItem(STORE_DRAFT_KEY, JSON.stringify(draft));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearStoreDraft() {
|
export function clearStoreDraft() {
|
||||||
|
|||||||
@@ -103,13 +103,34 @@ export default function CenterPage() {
|
|||||||
<Link to="/center/bills" className="partner-menu-item">
|
<Link to="/center/bills" className="partner-menu-item">
|
||||||
<div className="partner-menu-item-left">
|
<div className="partner-menu-item-left">
|
||||||
<div className="partner-menu-icon"><span className="material-symbols-outlined">description</span></div>
|
<div className="partner-menu-icon"><span className="material-symbols-outlined">description</span></div>
|
||||||
<span className="body-md" style={{ fontSize: 16 }}>待确认账单</span>
|
<span className="body-md" style={{ fontSize: 16 }}>财务对账</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
{pendingBills.length > 0 && <span className="label-md text-primary" style={{ background: 'rgba(166,29,36,0.1)', padding: '2px 8px', borderRadius: 999 }}>{pendingBills.length}</span>}
|
{pendingBills.length > 0 && <span className="label-md text-primary" style={{ background: 'rgba(166,29,36,0.1)', padding: '2px 8px', borderRadius: 999 }}>{pendingBills.length}</span>}
|
||||||
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link to="/reports/weekly" className="partner-menu-item">
|
||||||
|
<div className="partner-menu-item-left">
|
||||||
|
<div className="partner-menu-icon"><span className="material-symbols-outlined">monitoring</span></div>
|
||||||
|
<span className="body-md" style={{ fontSize: 16 }}>数据周报</span>
|
||||||
|
</div>
|
||||||
|
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||||
|
</Link>
|
||||||
|
<Link to="/reshipments" className="partner-menu-item">
|
||||||
|
<div className="partner-menu-item-left">
|
||||||
|
<div className="partner-menu-icon"><span className="material-symbols-outlined">assignment_return</span></div>
|
||||||
|
<span className="body-md" style={{ fontSize: 16 }}>补发处理</span>
|
||||||
|
</div>
|
||||||
|
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||||
|
</Link>
|
||||||
|
<Link to="/leaderboard" className="partner-menu-item">
|
||||||
|
<div className="partner-menu-item-left">
|
||||||
|
<div className="partner-menu-icon"><span className="material-symbols-outlined">military_tech</span></div>
|
||||||
|
<span className="body-md" style={{ fontSize: 16 }}>合伙人贡献榜</span>
|
||||||
|
</div>
|
||||||
|
<span className="material-symbols-outlined text-muted">chevron_right</span>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ function auditStatus(store: Record<string, unknown>) {
|
|||||||
return String(audits?.[0]?.status ?? '').toUpperCase();
|
return String(audits?.[0]?.status ?? '').toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function storeStatusLabel(store: Record<string, unknown>) {
|
||||||
|
const audit = auditStatus(store);
|
||||||
|
if (audit === 'PENDING') return '待审核';
|
||||||
|
const s = String(store.status).toUpperCase();
|
||||||
|
if (s === 'OPEN') return '营业中';
|
||||||
|
if (s === 'PAUSED') return '暂时闭店';
|
||||||
|
if (s === 'CLOSED') return '已关闭';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [dash, setDash] = useState<Record<string, unknown> | null>(null);
|
const [dash, setDash] = useState<Record<string, unknown> | null>(null);
|
||||||
@@ -42,6 +52,8 @@ export default function HomePage() {
|
|||||||
return created.getMonth() === now.getMonth() && created.getFullYear() === now.getFullYear();
|
return created.getMonth() === now.getMonth() && created.getFullYear() === now.getFullYear();
|
||||||
}).length;
|
}).length;
|
||||||
|
|
||||||
|
const recentFromDash = (dash?.recentStores as Array<Record<string, unknown>> | undefined) ?? [];
|
||||||
|
const displayStores = stores.length > 0 ? stores : recentFromDash;
|
||||||
const previewLeaderboard = LEADERBOARD_MOCK.slice(0, 2);
|
const previewLeaderboard = LEADERBOARD_MOCK.slice(0, 2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -167,14 +179,29 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{stores.length > 0 && (
|
{pendingStores.length > 0 && (
|
||||||
<ul className="partner-store-name-list">
|
<div className="partner-pending-stores">
|
||||||
{stores.slice(0, 5).map((s) => (
|
<p className="label-md text-muted" style={{ marginBottom: 8 }}>待审核门店名称</p>
|
||||||
|
<ul className="partner-store-name-list partner-store-name-list--compact">
|
||||||
|
{pendingStores.map((s) => (
|
||||||
<li key={String(s.id)}>
|
<li key={String(s.id)}>
|
||||||
<span className="headline-md">{String(s.name)}</span>
|
<span className="headline-md">{String(s.name || '未命名门店')}</span>
|
||||||
<span className="label-md text-muted">
|
</li>
|
||||||
{auditStatus(s) === 'PENDING' ? '待审核' : String(s.status) === 'OPEN' ? '营业中' : String(s.status)}
|
))}
|
||||||
</span>
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{displayStores.length > 0 && (
|
||||||
|
<ul className="partner-store-name-list">
|
||||||
|
<li className="partner-store-name-list-header">
|
||||||
|
<span className="label-md text-muted">门店名称</span>
|
||||||
|
<span className="label-md text-muted">状态</span>
|
||||||
|
</li>
|
||||||
|
{displayStores.slice(0, 5).map((s) => (
|
||||||
|
<li key={String(s.id)}>
|
||||||
|
<span className="headline-md">{String(s.name || '未命名门店')}</span>
|
||||||
|
<span className="label-md text-muted">{storeStatusLabel(s)}</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,35 +1,74 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import PageHeader from '@dukang/shared-ui/PageHeader';
|
import PageHeader from '@dukang/shared-ui/PageHeader';
|
||||||
|
|
||||||
/** BUWR-23:补发处理(preV1 占位,待 `/partner/after-sales/reshipments` 接入) */
|
type ReshipStatus = 'ALL' | 'PENDING' | 'SHIPPING' | 'DONE';
|
||||||
|
|
||||||
const MOCK_ITEMS = [
|
const MOCK_ITEMS = [
|
||||||
{ id: '1', orderNo: 'DK20260301001', status: '待配送', productName: '杜康窖藏', originalOrderNo: 'DK20260228088' },
|
{ id: '1', orderNo: 'DK20260301001', status: 'PENDING' as const, productName: '杜康窖藏', originalOrderNo: 'DK20260228088', address: '郑州市金水区花园路 88 号' },
|
||||||
|
{ id: '2', orderNo: 'DK20260302002', status: 'SHIPPING' as const, productName: '杜康特曲', originalOrderNo: 'DK20260215012', address: '郑州市二七区大学路 12 号' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const STATUS_LABEL: Record<string, string> = {
|
||||||
|
PENDING: '待配送',
|
||||||
|
SHIPPING: '配送中',
|
||||||
|
DONE: '已完成',
|
||||||
|
};
|
||||||
|
|
||||||
|
const FILTERS: { key: ReshipStatus; label: string }[] = [
|
||||||
|
{ key: 'ALL', label: '全部' },
|
||||||
|
{ key: 'PENDING', label: '待配送' },
|
||||||
|
{ key: 'SHIPPING', label: '配送中' },
|
||||||
|
{ key: 'DONE', label: '已完成' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/** BUWR-23:补发处理 */
|
||||||
export default function ReshipPage() {
|
export default function ReshipPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [filter, setFilter] = useState<ReshipStatus>('ALL');
|
||||||
|
|
||||||
|
const list = MOCK_ITEMS.filter((item) => filter === 'ALL' || item.status === filter);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-no-tab partner-orders-page">
|
<div className="page-no-tab partner-orders-page">
|
||||||
<PageHeader title="补发处理" onBack={() => navigate('/')} />
|
<PageHeader title="补发处理" onBack={() => navigate('/')} />
|
||||||
|
|
||||||
<p className="label-md text-muted" style={{ padding: '0 20px 12px' }}>
|
<p className="label-md text-muted" style={{ padding: '0 20px 8px' }}>
|
||||||
总部下达的补发工单,确认后可推进配送
|
总部下达的补发工单,确认后可推进配送
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{MOCK_ITEMS.length === 0 && <div className="empty">暂无补发工单</div>}
|
<div className="partner-filter-row" style={{ padding: '0 20px 12px' }}>
|
||||||
|
{FILTERS.map((f) => (
|
||||||
|
<button
|
||||||
|
key={f.key}
|
||||||
|
type="button"
|
||||||
|
className={`partner-chip${filter === f.key ? ' active' : ''}`}
|
||||||
|
onClick={() => setFilter(f.key)}
|
||||||
|
>
|
||||||
|
{f.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
{MOCK_ITEMS.map((item) => (
|
{list.length === 0 && <div className="empty">暂无补发工单</div>}
|
||||||
|
|
||||||
|
{list.map((item) => (
|
||||||
<div key={item.id} className="partner-order-card" style={{ margin: '0 20px 12px' }}>
|
<div key={item.id} className="partner-order-card" style={{ margin: '0 20px 12px' }}>
|
||||||
<div className="partner-order-card-top">
|
<div className="partner-order-card-top">
|
||||||
<span className="label-md text-muted">原单 {item.originalOrderNo}</span>
|
<span className="label-md text-muted">原单 {item.originalOrderNo}</span>
|
||||||
<span className="label-md text-primary" style={{ fontWeight: 600 }}>{item.status}</span>
|
<span className="label-md text-primary" style={{ fontWeight: 600 }}>{STATUS_LABEL[item.status]}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="headline-md" style={{ marginTop: 8 }}>{item.productName}</h3>
|
<h3 className="headline-md" style={{ marginTop: 8 }}>{item.productName}</h3>
|
||||||
<p className="label-md text-muted" style={{ marginTop: 4 }}>补发单号 {item.orderNo}</p>
|
<p className="label-md text-muted" style={{ marginTop: 4 }}>补发单号 {item.orderNo}</p>
|
||||||
|
<div className="partner-order-address" style={{ marginTop: 12 }}>
|
||||||
|
<span className="material-symbols-outlined" style={{ fontSize: 18 }}>location_on</span>
|
||||||
|
<p className="line-2-clamp">{item.address}</p>
|
||||||
|
</div>
|
||||||
|
{item.status === 'PENDING' && (
|
||||||
<button type="button" className="partner-btn-primary" style={{ marginTop: 16, width: '100%' }}>
|
<button type="button" className="partner-btn-primary" style={{ marginTop: 16, width: '100%' }}>
|
||||||
开始配送
|
开始配送
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,13 +17,22 @@ const STEPS = ['基本信息', '照片上传', '结算资质'] as const;
|
|||||||
export default function StoreCreatePage() {
|
export default function StoreCreatePage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [params, setParams] = useSearchParams();
|
const [params, setParams] = useSearchParams();
|
||||||
const step = Number(params.get('step') || 1);
|
const saved = loadStoreDraft();
|
||||||
const [form, setForm] = useState<StoreDraftForm>(() => loadStoreDraft() ?? defaultStoreForm());
|
const [form, setForm] = useState<StoreDraftForm>(saved?.form ?? defaultStoreForm());
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
|
const stepFromUrl = Number(params.get('step') || 0);
|
||||||
|
const step = stepFromUrl >= 1 && stepFromUrl <= 3 ? stepFromUrl : (saved?.step ?? 1);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
saveStoreDraft(form);
|
if (!stepFromUrl && saved?.step) {
|
||||||
}, [form]);
|
setParams({ step: String(saved.step) }, { replace: true });
|
||||||
|
}
|
||||||
|
}, [stepFromUrl, saved?.step, setParams]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
saveStoreDraft({ step, form });
|
||||||
|
}, [step, form]);
|
||||||
|
|
||||||
function patchForm(patch: Partial<StoreDraftForm>) {
|
function patchForm(patch: Partial<StoreDraftForm>) {
|
||||||
setForm((prev) => ({ ...prev, ...patch }));
|
setForm((prev) => ({ ...prev, ...patch }));
|
||||||
@@ -144,7 +153,7 @@ export default function StoreCreatePage() {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="body-md text-primary" style={{ fontWeight: 700 }}>杜康合伙人身份认证</h3>
|
<h3 className="body-md text-primary" style={{ fontWeight: 700 }}>杜康合伙人身份认证</h3>
|
||||||
<p className="label-md text-variant" style={{ marginTop: 4, lineHeight: 1.4 }}>
|
<p className="label-md text-variant" style={{ marginTop: 4, lineHeight: 1.4 }}>
|
||||||
传承千年窖香,共筑美酒传奇。录入信息后,系统将在 1-3 个工作日内完成审核。
|
填写内容将自动保存,退出后可继续录入。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -173,18 +182,12 @@ export default function StoreCreatePage() {
|
|||||||
<h3 className="headline-md">环境照片 *</h3>
|
<h3 className="headline-md">环境照片 *</h3>
|
||||||
<p className="label-md text-muted" style={{ margin: '4px 0 12px' }}>至少3张,展示店内整洁环境</p>
|
<p className="label-md text-muted" style={{ margin: '4px 0 12px' }}>至少3张,展示店内整洁环境</p>
|
||||||
<div className="partner-upload-grid">
|
<div className="partner-upload-grid">
|
||||||
<button type="button" className="partner-upload-dashed">
|
{[0, 1, 2].map((i) => (
|
||||||
<span className="material-symbols-outlined text-primary">add_a_photo</span>
|
<button key={i} type="button" className="partner-upload-dashed">
|
||||||
<span>添加照片</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" className="partner-upload-dashed">
|
|
||||||
<span className="material-symbols-outlined text-primary">add_a_photo</span>
|
|
||||||
<span>添加照片</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" className="partner-upload-dashed">
|
|
||||||
<span className="material-symbols-outlined text-primary">add_a_photo</span>
|
<span className="material-symbols-outlined text-primary">add_a_photo</span>
|
||||||
<span>添加照片</span>
|
<span>添加照片</span>
|
||||||
</button>
|
</button>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -205,7 +208,7 @@ export default function StoreCreatePage() {
|
|||||||
<div className="partner-info-banner" style={{ background: 'rgba(254,214,91,0.2)', borderColor: 'rgba(254,214,91,0.5)' }}>
|
<div className="partner-info-banner" style={{ background: 'rgba(254,214,91,0.2)', borderColor: 'rgba(254,214,91,0.5)' }}>
|
||||||
<span className="material-symbols-outlined text-secondary" style={{ fontSize: 18 }}>info</span>
|
<span className="material-symbols-outlined text-secondary" style={{ fontSize: 18 }}>info</span>
|
||||||
<p className="label-md" style={{ lineHeight: 1.5, color: 'var(--color-on-secondary-container)' }}>
|
<p className="label-md" style={{ lineHeight: 1.5, color: 'var(--color-on-secondary-container)' }}>
|
||||||
温馨提示:请确保照片清晰无反光。preV1 占位上传,可直接下一步。
|
温馨提示:preV1 占位上传,可直接下一步。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -220,21 +223,21 @@ export default function StoreCreatePage() {
|
|||||||
<section className="partner-form-card">
|
<section className="partner-form-card">
|
||||||
<div className="partner-field">
|
<div className="partner-field">
|
||||||
<label>户主姓名 *</label>
|
<label>户主姓名 *</label>
|
||||||
<input className="partner-field-input" style={{ width: '100%', padding: '0 16px', minHeight: 48, border: '1px solid rgba(226,190,188,0.3)', borderRadius: 8, background: 'var(--color-background)' }} placeholder="请输入银行卡实名姓名" value={form.bankAccountName} onChange={(e) => patchForm({ bankAccountName: e.target.value })} />
|
<input className="partner-field-input partner-field-input--block" placeholder="请输入银行卡实名姓名" value={form.bankAccountName} onChange={(e) => patchForm({ bankAccountName: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="partner-field">
|
<div className="partner-field">
|
||||||
<label>银行卡号 *</label>
|
<label>银行卡号 *</label>
|
||||||
<input className="partner-field-input" style={{ width: '100%', padding: '0 16px', minHeight: 48, border: '1px solid rgba(226,190,188,0.3)', borderRadius: 8, background: 'var(--color-background)' }} placeholder="请输入16-19位银行卡号" value={form.bankAccountNo} onChange={(e) => patchForm({ bankAccountNo: e.target.value })} />
|
<input className="partner-field-input partner-field-input--block" placeholder="请输入16-19位银行卡号" value={form.bankAccountNo} onChange={(e) => patchForm({ bankAccountNo: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
<div className="partner-field">
|
<div className="partner-field">
|
||||||
<label>开户支行 *</label>
|
<label>开户支行 *</label>
|
||||||
<input className="partner-field-input" style={{ width: '100%', padding: '0 16px', minHeight: 48, border: '1px solid rgba(226,190,188,0.3)', borderRadius: 8, background: 'var(--color-background)' }} placeholder="例如:中国工商银行洛阳分行" value={form.bankBranch} onChange={(e) => patchForm({ bankBranch: e.target.value })} />
|
<input className="partner-field-input partner-field-input--block" placeholder="例如:中国工商银行洛阳分行" value={form.bankBranch} onChange={(e) => patchForm({ bankBranch: e.target.value })} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div className="partner-info-banner" style={{ background: 'rgba(254,214,91,0.15)', borderColor: 'rgba(254,214,91,0.3)' }}>
|
<div className="partner-info-banner" style={{ background: 'rgba(254,214,91,0.15)', borderColor: 'rgba(254,214,91,0.3)' }}>
|
||||||
<span className="material-symbols-outlined text-secondary">info</span>
|
<span className="material-symbols-outlined text-secondary">info</span>
|
||||||
<p className="body-md" style={{ color: 'var(--color-on-secondary-container)' }}>
|
<p className="body-md" style={{ color: 'var(--color-on-secondary-container)' }}>
|
||||||
请确保银行卡信息准确,以免影响每月的餐费结算。结算将按合同约定账期执行。
|
请确保银行卡信息准确,以免影响每月的餐费结算。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ const FILTERS: { key: StatusFilter; label: string }[] = [
|
|||||||
{ 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() {
|
export default function StoreListPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [stores, setStores] = useState<Array<Record<string, unknown>>>([]);
|
const [stores, setStores] = useState<Array<Record<string, unknown>>>([]);
|
||||||
@@ -71,16 +80,22 @@ export default function StoreListPage() {
|
|||||||
|
|
||||||
{filtered.map((s) => {
|
{filtered.map((s) => {
|
||||||
const pill = statusPill(String(s.status));
|
const pill = statusPill(String(s.status));
|
||||||
|
const audit = auditLabel(s);
|
||||||
const dim = String(s.status).toUpperCase() === 'CLOSED';
|
const dim = String(s.status).toUpperCase() === 'CLOSED';
|
||||||
|
const storeName = String(s.name || '未命名门店');
|
||||||
return (
|
return (
|
||||||
<div key={String(s.id)} className={`partner-store-card${dim ? ' partner-store-card--dim' : ''}`}>
|
<div key={String(s.id)} className={`partner-store-card${dim ? ' partner-store-card--dim' : ''}`}>
|
||||||
<Link to={`/stores/${s.id}`} style={{ color: 'inherit', textDecoration: 'none' }}>
|
<Link to={`/stores/${s.id}`} style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
<div className="partner-store-card-header">
|
<div className="partner-store-card-header">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="headline-md">{String(s.name)}</h3>
|
<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>
|
<p className="label-md text-muted" style={{ marginTop: 4 }}>{String(s.address || s.district || '')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}>
|
||||||
<span className={`partner-status-pill ${pill.cls}`}>{pill.label}</span>
|
<span className={`partner-status-pill ${pill.cls}`}>{pill.label}</span>
|
||||||
|
{audit && <span className={`partner-status-pill ${audit.cls}`}>{audit.label}</span>}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="partner-store-card-actions">
|
<div className="partner-store-card-actions">
|
||||||
|
|||||||
@@ -606,6 +606,30 @@ html, body, #root {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.partner-store-name-list-header {
|
||||||
|
padding-bottom: 4px !important;
|
||||||
|
border-bottom: 1px solid rgba(226, 190, 188, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-store-name-list--compact li {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-pending-stores {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid rgba(226, 190, 188, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.partner-field-input--block {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
min-height: 48px;
|
||||||
|
border: 1px solid rgba(226, 190, 188, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--color-background);
|
||||||
|
}
|
||||||
|
|
||||||
.partner-leaderboard-section {
|
.partner-leaderboard-section {
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,13 +115,24 @@ export class StoreService {
|
|||||||
|
|
||||||
async partnerDashboard(partnerAccountId: bigint) {
|
async partnerDashboard(partnerAccountId: bigint) {
|
||||||
const account = await this.getPartnerAccount(partnerAccountId);
|
const account = await this.getPartnerAccount(partnerAccountId);
|
||||||
const [storeCount, orderCount] = await Promise.all([
|
const [storeCount, orderCount, recentStores] = await Promise.all([
|
||||||
this.prisma.store.count({ where: { partnerId: account.partnerId } }),
|
this.prisma.store.count({ where: { partnerId: account.partnerId } }),
|
||||||
this.prisma.order.count({
|
this.prisma.order.count({
|
||||||
where: { city: { partnerId: account.partnerId } },
|
where: { city: { partnerId: account.partnerId } },
|
||||||
}),
|
}),
|
||||||
|
this.prisma.store.findMany({
|
||||||
|
where: { partnerId: account.partnerId },
|
||||||
|
select: { id: true, name: true, status: true, createdAt: true },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 10,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
return { storeCount, orderCount, companyName: account.partner.companyName };
|
return {
|
||||||
|
storeCount,
|
||||||
|
orderCount,
|
||||||
|
companyName: account.partner.companyName,
|
||||||
|
recentStores: serializeBigInt(recentStores),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPartnerAccount(partnerAccountId: bigint) {
|
private async getPartnerAccount(partnerAccountId: bigint) {
|
||||||
|
|||||||
Reference in New Issue
Block a user