v4.0.9版本更新-门店分类支持多选
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-09-02 11:12:51 +08:00
parent 77e9917a18
commit fe557c912d
18 changed files with 742 additions and 239 deletions
+20 -2
View File
@@ -25,7 +25,10 @@ export type StoreDraftForm = {
/** 人均费用(选填) */
avgPrice: string;
categoryParentId: string;
/** @deprecated 使用 categoryIds */
categoryId: string;
/** 二级分类多选 */
categoryIds: string[];
intro: string;
/** 好客权益券使用规则 */
benefitUsageRule: string;
@@ -68,6 +71,7 @@ export const defaultStoreForm = (): StoreDraftForm => ({
avgPrice: '',
categoryParentId: '',
categoryId: '',
categoryIds: [],
intro: '',
benefitUsageRule: '',
coverUrl: '',
@@ -122,6 +126,20 @@ export function normalizeStoreDraftForm(raw: Partial<StoreDraftForm> | null | un
openTime2: String(raw.openTime2 ?? base.openTime2),
closeTime2: String(raw.closeTime2 ?? base.closeTime2),
avgPrice: String(raw.avgPrice ?? base.avgPrice),
categoryIds: (() => {
if (Array.isArray(raw.categoryIds) && raw.categoryIds.length) {
return raw.categoryIds.map(String).filter(Boolean);
}
const legacy = String(raw.categoryId ?? '').trim();
return legacy ? [legacy] : base.categoryIds;
})(),
categoryId: (() => {
const ids = Array.isArray(raw.categoryIds) && raw.categoryIds.length
? raw.categoryIds.map(String).filter(Boolean)
: [];
if (ids.length) return ids[0];
return String(raw.categoryId ?? base.categoryId);
})(),
envPhotoUrls: normalizeStringArray(raw.envPhotoUrls, MIN_ENV_PHOTO_COUNT),
// 兼容旧草稿:单个 contractUrl 迁移为数组
contractUrls: (() => {
@@ -201,7 +219,7 @@ export function validateStoreStep1(
| 'openTime2'
| 'closeTime2'
| 'avgPrice'
| 'categoryId'
| 'categoryIds'
| 'intro'
| 'benefitUsageRule'
>,
@@ -233,7 +251,7 @@ export function validateStoreStep1(
const n = Number(form.avgPrice);
if (Number.isNaN(n) || n < 0) return '人均费用须为非负数字';
}
if (!form.categoryId.trim()) return '请选择店铺类型';
if (!form.categoryIds?.length) return '请至少选择一个店铺类型';
if (form.intro.trim()) {
const len = form.intro.trim().length;
if (len < 2 || len > 500) return '门店简介须为 2~500 字';
+37 -58
View File
@@ -206,9 +206,9 @@ export default function StoreCreatePage() {
.then((list) => {
const tree = Array.isArray(list) ? list : [];
setCategoryTree(tree);
if (form.categoryId && !form.categoryParentId) {
if (form.categoryIds.length && !form.categoryParentId) {
const parent = tree.find((root) =>
(root.children ?? []).some((child) => child.id === form.categoryId),
(root.children ?? []).some((child) => form.categoryIds.includes(child.id)),
);
if (parent) patchForm({ categoryParentId: parent.id });
}
@@ -216,12 +216,6 @@ export default function StoreCreatePage() {
.catch(() => setCategoryTree([]));
}, []);
const categoryChildren = useMemo(() => {
const parent = categoryTree.find((item) => item.id === form.categoryParentId);
return Array.isArray(parent?.children) ? parent!.children! : [];
}, [categoryTree, form.categoryParentId]);
useEffect(() => {
@@ -523,7 +517,8 @@ export default function StoreCreatePage() {
...(form.avgPrice.trim() ? { avgPrice: Number(form.avgPrice) } : {}),
categoryId: form.categoryId.trim(),
categoryIds: form.categoryIds,
categoryId: form.categoryIds[0] || form.categoryId.trim(),
intro: form.intro.trim() || undefined,
benefitUsageRule: form.benefitUsageRule.trim() || undefined,
@@ -690,56 +685,40 @@ export default function StoreCreatePage() {
<div className="partner-field">
<label> <span className="text-primary">*</span></label>
<div className="partner-input-row" style={{ gap: 8 }}>
<select
className="partner-field-input partner-field-input--block"
value={form.categoryParentId}
onChange={(e) => patchForm({ categoryParentId: e.target.value, categoryId: '' })}
aria-label="一级店铺类型"
>
<option value=""></option>
{categoryTree.map((item) => (
<option key={item.id} value={item.id}>{item.name}</option>
))}
</select>
<select
className="partner-field-input partner-field-input--block"
value={form.categoryId}
onChange={(e) => patchForm({ categoryId: e.target.value })}
disabled={!form.categoryParentId}
aria-label="二级店铺类型"
>
<option value="">{form.categoryParentId ? '选择细类' : '请先选大类'}</option>
{categoryChildren.map((item) => (
<option key={item.id} value={item.id}>{item.name}</option>
))}
</select>
<label> <span className="text-primary">*</span> <span className="label-md text-muted"></span></label>
<div className="partner-category-multi">
{categoryTree.map((parent) => (
<div key={parent.id} className="partner-category-group">
<div className="partner-category-group-title">{parent.name}</div>
<div className="partner-category-options">
{(parent.children ?? []).map((child) => {
const checked = form.categoryIds.includes(child.id);
return (
<label key={child.id} className="partner-category-option">
<input
type="checkbox"
checked={checked}
onChange={() => {
const next = checked
? form.categoryIds.filter((id) => id !== child.id)
: [...form.categoryIds, child.id];
patchForm({
categoryIds: next,
categoryId: next[0] ?? '',
categoryParentId: next.length
? parent.id
: form.categoryParentId,
});
}}
/>
<span>{child.name}</span>
</label>
);
})}
</div>
</div>
))}
</div>
</div>