feat(store): 门店套餐 v3.4.10 全端实现
新增 StorePackage 与变更审核流,覆盖总部直存、合伙/门店提审、C 端展示与套餐异议工单;同步 PRD/开发文档与 REQ 索引。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { useState } from 'react';
|
||||
import type { PackageFormItem } from '../lib/storePackages';
|
||||
import { STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
|
||||
import { emptyPackage } from '../lib/storePackages';
|
||||
|
||||
type Props = {
|
||||
items: PackageFormItem[];
|
||||
onChange: (items: PackageFormItem[]) => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export default function StorePackagesForm({ items, onChange, disabled }: Props) {
|
||||
function updateAt(index: number, patch: Partial<PackageFormItem>) {
|
||||
const next = items.map((item, i) => (i === index ? { ...item, ...patch } : item));
|
||||
onChange(next);
|
||||
}
|
||||
|
||||
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="partner-packages-form">
|
||||
{list.map((item, index) => (
|
||||
<div key={index} className="partner-card" style={{ marginBottom: 12 }}>
|
||||
<div className="partner-card-title" style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>套餐 {index + 1}</span>
|
||||
{!disabled && list.length > 1 ? (
|
||||
<button type="button" className="partner-link-btn" onClick={() => removeAt(index)}>
|
||||
删除
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<label className="partner-field">
|
||||
<span className="partner-label">套餐名称</span>
|
||||
<input
|
||||
className="partner-input"
|
||||
value={item.name}
|
||||
disabled={disabled}
|
||||
placeholder="如:套餐A"
|
||||
onChange={(e) => updateAt(index, { name: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="partner-field">
|
||||
<span className="partner-label">价格(元)</span>
|
||||
<input
|
||||
className="partner-input"
|
||||
type="number"
|
||||
min={0}
|
||||
step={0.01}
|
||||
value={item.price}
|
||||
disabled={disabled}
|
||||
placeholder="198"
|
||||
onChange={(e) => updateAt(index, { price: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="partner-field">
|
||||
<span className="partner-label">菜品</span>
|
||||
<textarea
|
||||
className="partner-input"
|
||||
rows={2}
|
||||
value={item.dishes}
|
||||
disabled={disabled}
|
||||
placeholder="红烧肉、红烧鱼、油焖茄子"
|
||||
onChange={(e) => updateAt(index, { dishes: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="partner-field">
|
||||
<span className="partner-label">使用时间</span>
|
||||
<input
|
||||
className="partner-input"
|
||||
value={item.usableTime || ''}
|
||||
disabled={disabled}
|
||||
placeholder="节假日除外"
|
||||
onChange={(e) => updateAt(index, { usableTime: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="partner-field">
|
||||
<span className="partner-label">其他说明</span>
|
||||
<input
|
||||
className="partner-input"
|
||||
value={item.otherNotes || ''}
|
||||
disabled={disabled}
|
||||
placeholder="不可叠加"
|
||||
onChange={(e) => updateAt(index, { otherNotes: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
{!disabled && list.length < STORE_PACKAGE_MAX_COUNT ? (
|
||||
<button type="button" className="partner-btn-outline" onClick={addItem}>
|
||||
+ 添加套餐
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user