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
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Button, Space, Typography, message } from 'antd';
import { Alert, Button, Form, Input, InputNumber, Space, Typography, message } from 'antd';
import type { StorePackageItemDto } from '@dukang/shared-types';
import { STORE_PACKAGE_MAX_COUNT } from '@dukang/shared-types';
import { request } from '../lib/api';
@@ -87,68 +87,101 @@ export default function AdminStorePackagesSection({ storeId }: { storeId: string
}
}
if (loading) return <Typography.Text type="secondary"></Typography.Text>;
if (loading) {
return <Typography.Text type="secondary"></Typography.Text>;
}
return (
<div>
<Typography.Paragraph type="secondary">
{STORE_PACKAGE_MAX_COUNT}
</Typography.Paragraph>
<Form layout="vertical" requiredMark={false}>
<Alert
type="info"
showIcon
style={{ marginBottom: 16 }}
message={`总部直存立即生效,无需审核。同一门店最多 ${STORE_PACKAGE_MAX_COUNT} 条套餐。`}
/>
{items.map((item, index) => (
<div key={index} style={{ border: '1px solid #f0f0f0', borderRadius: 8, padding: 12, marginBottom: 12 }}>
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 8 }}>
<Typography.Text strong> {index + 1}</Typography.Text>
<div
key={index}
style={{
marginBottom: 16,
padding: 16,
border: '1px solid #f0f0f0',
borderRadius: 8,
background: '#fafafa',
}}
>
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 12 }}>
<Typography.Title level={5} style={{ margin: 0 }}>
{index + 1}
</Typography.Title>
{items.length > 1 ? (
<Button type="link" danger onClick={() => removeAt(index)}>
</Button>
) : null}
</Space>
<Space direction="vertical" style={{ width: '100%' }}>
<input
placeholder="套餐名称"
<Form.Item label="套餐名称" required style={{ marginBottom: 12 }}>
<Input
placeholder="如:套餐A"
value={item.name}
onChange={(e) => updateAt(index, { name: e.target.value })}
style={{ width: '100%', padding: 8 }}
/>
<input
placeholder="价格(元)"
type="number"
</Form.Item>
<Form.Item label="价格(元)" required style={{ marginBottom: 12 }}>
<InputNumber
min={0}
value={item.price}
onChange={(e) => updateAt(index, { price: e.target.value })}
style={{ width: '100%', padding: 8 }}
precision={2}
style={{ width: '100%' }}
addonAfter="元"
placeholder="198"
value={item.price === '' ? undefined : Number(item.price)}
onChange={(v) => updateAt(index, { price: v != null ? String(v) : '' })}
/>
<textarea
placeholder="菜品"
</Form.Item>
<Form.Item label="菜品" required style={{ marginBottom: 12 }}>
<Input.TextArea
rows={2}
placeholder="红烧肉、红烧鱼、油焖茄子"
value={item.dishes}
onChange={(e) => updateAt(index, { dishes: e.target.value })}
style={{ width: '100%', padding: 8 }}
/>
<input
placeholder="使用时间"
</Form.Item>
<Form.Item label="使用时间" style={{ marginBottom: 12 }}>
<Input
placeholder="节假日除外"
value={item.usableTime || ''}
onChange={(e) => updateAt(index, { usableTime: e.target.value })}
style={{ width: '100%', padding: 8 }}
/>
<input
placeholder="其他说明"
</Form.Item>
<Form.Item label="其他说明" style={{ marginBottom: 0 }}>
<Input
placeholder="不可叠加"
value={item.otherNotes || ''}
onChange={(e) => updateAt(index, { otherNotes: e.target.value })}
style={{ width: '100%', padding: 8 }}
/>
</Space>
</Form.Item>
</div>
))}
{items.length < STORE_PACKAGE_MAX_COUNT ? (
<Button onClick={addRow} style={{ marginBottom: 12 }}>
<Button onClick={addRow} style={{ marginBottom: 16 }}>
</Button>
) : null}
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
C
</Typography.Paragraph>
<Button type="primary" loading={saving} onClick={() => void save()}>
</Button>
</div>
</Form>
);
}