Files
dukang/apps/h5-shop/src/pages/StatusPage.tsx
T
2026-08-04 21:38:49 +08:00

172 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useStoreSession } from '../contexts/StoreSessionContext';
import { request } from '../lib/api';
function formatShopHours(store: Record<string, unknown> | null) {
const parts: string[] = [];
const openTime = store?.openTime ? String(store.openTime) : '';
const closeTime = store?.closeTime ? String(store.closeTime) : '';
const openTime2 = store?.openTime2 ? String(store.openTime2) : '';
const closeTime2 = store?.closeTime2 ? String(store.closeTime2) : '';
if (openTime && closeTime) parts.push(`${openTime} - ${closeTime}`);
if (openTime2 && closeTime2) parts.push(`${openTime2} - ${closeTime2}`);
return parts.length ? parts.join('') : '09:30 - 22:00';
}
export default function StatusPage() {
const navigate = useNavigate();
const { resetSession } = useStoreSession();
const [open, setOpen] = useState(true);
const [permanentlyClosed, setPermanentlyClosed] = useState(false);
const [store, setStore] = useState<Record<string, unknown> | null>(null);
const [lastUpdate, setLastUpdate] = useState('');
const [showModal, setShowModal] = useState(false);
const [pendingOpen, setPendingOpen] = useState<boolean | null>(null);
const [error, setError] = useState('');
useEffect(() => {
request('SHOP_H5', '/shop/dashboard').then((d) => {
const s = d.store as Record<string, unknown>;
setStore(s);
const status = String(s?.status || '');
setPermanentlyClosed(status === 'CLOSED');
setOpen(status === 'OPEN');
if (s?.updatedAt) {
setLastUpdate(new Date(String(s.updatedAt)).toLocaleString('zh-CN'));
}
});
}, []);
function requestToggle(next: boolean) {
if (permanentlyClosed) return;
if (next === open) return;
setPendingOpen(next);
setShowModal(true);
}
async function confirmToggle() {
if (pendingOpen === null || permanentlyClosed) return;
const next = pendingOpen ? 'OPEN' : 'PAUSED';
try {
setError('');
await request('SHOP_H5', '/shop/store/status', {
method: 'PUT',
body: JSON.stringify({ status: next }),
});
setOpen(pendingOpen);
setLastUpdate(new Date().toLocaleString('zh-CN'));
} catch (e) {
setError(e instanceof Error ? e.message : '状态切换失败');
} finally {
setShowModal(false);
setPendingOpen(null);
}
}
const hoursText = formatShopHours(store);
const statusLabel = permanentlyClosed ? '永久关闭' : open ? '营业中' : '临时闭店';
return (
<div className="shop-status-page">
<header className="shop-status-header app-page-header">
<button type="button" className="app-page-header-action shop-redeem-back" onClick={() => navigate(-1)} aria-label="返回">
<span className="material-symbols-outlined">arrow_back</span>
</button>
<h1 className="app-page-title">门店管理</h1>
<button
type="button"
className="shop-status-logout app-page-header-action app-page-header-action--end"
onClick={() => { resetSession(); navigate('/login'); }}
>
<span className="material-symbols-outlined" style={{ fontSize: 20 }}>logout</span>
退出登录
</button>
</header>
<div className="shop-status-content">
<section className="shop-status-card">
<div className="shop-status-icon-wrap">
<div className={`shop-status-icon-outer${open && !permanentlyClosed ? ' open' : ' closed'}`}>
<div className={`shop-status-icon-inner${open && !permanentlyClosed ? ' open' : ' closed'}`}>
<span className="material-symbols-outlined">storefront</span>
</div>
</div>
<span className="shop-status-check">
<span
className="material-symbols-outlined shop-fill-icon"
style={{
fontSize: 14,
color: open && !permanentlyClosed ? 'var(--color-success-green)' : 'var(--color-subtle-gray)',
}}
>
{open && !permanentlyClosed ? 'check_circle' : 'cancel'}
</span>
</span>
</div>
<h2 className={`shop-status-label${open && !permanentlyClosed ? ' open' : ' closed'}`}>
{statusLabel}
</h2>
{permanentlyClosed ? (
<p className="shop-status-switch-caption" style={{ marginTop: 12 }}>
总部已永久关闭本店,门店端无法自行恢复营业
</p>
) : (
<label className={`shop-status-switch${open ? ' open' : ' closed'}`}>
<input
type="checkbox"
checked={open}
onChange={(e) => requestToggle(e.target.checked)}
aria-label={open ? '切换为临时闭店' : '切换为营业中'}
/>
<span className="shop-status-switch-track" />
<span className="shop-status-switch-caption">
{open ? '点击可临时闭店' : '点击恢复营业'}
</span>
</label>
)}
<p className="shop-status-hours-label">营业时间</p>
<p className="shop-status-hours">{hoursText}</p>
{lastUpdate && <p className="shop-status-updated">最后修改于 {lastUpdate}</p>}
{error ? <p className="shop-status-updated" style={{ color: 'var(--color-error, #c62828)' }}>{error}</p> : null}
</section>
<div className={`shop-status-hint${open && !permanentlyClosed ? ' open' : ' closed'}`}>
<span className="material-symbols-outlined">info</span>
<p>
{permanentlyClosed
? '门店已永久关闭。如需重新营业,请联系总部或合伙人处理。'
: open
? '当前处于营业状态,用户可在您的门店核销餐券。'
: '当前处于临时闭店状态,用户将无法看到您的门店或进行核销。'}
</p>
</div>
</div>
{showModal && (
<div className="shop-status-modal" role="dialog" aria-modal="true">
<div className="shop-status-modal-card">
<h4 className="shop-status-modal-title">确认切换状态?</h4>
<p className="shop-status-modal-desc">
{pendingOpen
? '切换至“营业中”后,用户可正常选择本店核销餐券。'
: '切换至“临时闭店”后,用户将无法选择本店核销餐券。'}
</p>
<div className="shop-status-modal-actions">
<button type="button" className="shop-status-modal-cancel" onClick={() => { setShowModal(false); setPendingOpen(null); }}>
取消
</button>
<button type="button" className="shop-status-modal-confirm" onClick={confirmToggle}>
确认切换
</button>
</div>
</div>
</div>
)}
</div>
);
}