style(store): 统一门店套餐页 UI 与拓店校验弹窗

管理后台套餐 Tab 对齐 Ant Design 表单;合伙人/门店端套餐表单与基本信息页风格一致;拓店下一步校验增加弹窗提示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 19:59:44 +08:00
parent 4e7079c221
commit 3ba7b179e8
8 changed files with 637 additions and 235 deletions
@@ -0,0 +1,108 @@
import { STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
import { emptyPackage, type PackageFormItem } from '../lib/storePackages';
type Props = {
items: PackageFormItem[];
onChange: (items: PackageFormItem[]) => void;
disabled?: boolean;
};
export default function ShopPackagesForm({ items, onChange, disabled }: Props) {
function updateAt(index: number, patch: Partial<PackageFormItem>) {
onChange(items.map((item, i) => (i === index ? { ...item, ...patch } : item)));
}
function addItem() {
if (items.length >= STORE_PACKAGE_MAX_COUNT) return;
onChange([...items, emptyPackage(items.length)]);
}
function removeAt(index: number) {
onChange(items.filter((_, i) => i !== index).map((item, i) => ({ ...item, sortOrder: i })));
}
const list = items.length ? items : [emptyPackage(0)];
return (
<div className="shop-packages-form">
{list.map((item, index) => (
<section key={index} className="shop-packages-card">
<div className="shop-packages-card-head">
<h3 className="shop-packages-card-title"> {index + 1}</h3>
{!disabled && list.length > 1 ? (
<button type="button" className="shop-packages-remove" onClick={() => removeAt(index)}>
</button>
) : null}
</div>
<label className="shop-packages-field">
<span className="shop-packages-label"> *</span>
<input
className="shop-packages-input"
placeholder="如:套餐A"
value={item.name}
disabled={disabled}
onChange={(e) => updateAt(index, { name: e.target.value })}
/>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label"> *</span>
<input
className="shop-packages-input"
type="number"
min={0}
step={0.01}
placeholder="198"
value={item.price}
disabled={disabled}
onChange={(e) => updateAt(index, { price: e.target.value })}
/>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label"> *</span>
<textarea
className="shop-packages-textarea"
rows={3}
placeholder="红烧肉、红烧鱼、油焖茄子"
value={item.dishes}
disabled={disabled}
onChange={(e) => updateAt(index, { dishes: e.target.value })}
/>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label">使</span>
<input
className="shop-packages-input"
placeholder="节假日除外"
value={item.usableTime || ''}
disabled={disabled}
onChange={(e) => updateAt(index, { usableTime: e.target.value })}
/>
</label>
<label className="shop-packages-field">
<span className="shop-packages-label"></span>
<input
className="shop-packages-input"
placeholder="不可叠加"
value={item.otherNotes || ''}
disabled={disabled}
onChange={(e) => updateAt(index, { otherNotes: e.target.value })}
/>
</label>
</section>
))}
{!disabled && list.length < STORE_PACKAGE_MAX_COUNT ? (
<button type="button" className="shop-packages-add" onClick={addItem}>
<span className="material-symbols-outlined">add_circle</span>
</button>
) : null}
</div>
);
}