feat(store): add two-level store categories for admin and partner open-store
CI / verify (pull_request) Has been cancelled

Admin CRUD under stores menu; partner picks leaf category on create.
Default sync only inserts missing rows and never updates existing ones.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-22 12:10:02 +08:00
parent 823e439101
commit 9a550cbaaf
18 changed files with 848 additions and 26 deletions
+15 -1
View File
@@ -10,6 +10,8 @@ export type StoreDraftForm = {
address: string;
openTime: string;
closeTime: string;
categoryParentId: string;
categoryId: string;
intro: string;
coverUrl: string;
envPhotoUrls: string[];
@@ -41,6 +43,8 @@ export const defaultStoreForm = (): StoreDraftForm => ({
address: '',
openTime: '10:00',
closeTime: '22:00',
categoryParentId: '',
categoryId: '',
intro: '',
coverUrl: '',
envPhotoUrls: ['', '', ''],
@@ -74,6 +78,8 @@ function normalizeForm(raw: Record<string, unknown>): StoreDraftForm {
address: String(raw.address ?? base.address),
openTime: String(raw.openTime ?? base.openTime),
closeTime: String(raw.closeTime ?? base.closeTime),
categoryParentId: String(raw.categoryParentId ?? base.categoryParentId),
categoryId: String(raw.categoryId ?? base.categoryId),
intro: String(raw.intro ?? base.intro),
coverUrl: String(raw.coverUrl ?? base.coverUrl),
envPhotoUrls: normalizeStringArray(raw.envPhotoUrls, 3),
@@ -135,7 +141,14 @@ function timeToMinutes(value: string): number {
export function validateStoreStep1(
form: Pick<
StoreDraftForm,
'regionCodes' | 'cityId' | 'name' | 'address' | 'openTime' | 'closeTime' | 'intro'
| 'regionCodes'
| 'cityId'
| 'name'
| 'address'
| 'openTime'
| 'closeTime'
| 'categoryId'
| 'intro'
>,
): string | null {
if (!form.regionCodes || form.regionCodes.length < 3) return '请选择省 / 市 / 区县';
@@ -149,6 +162,7 @@ export function validateStoreStep1(
if (timeToMinutes(form.openTime.trim()) >= timeToMinutes(form.closeTime.trim())) {
return '营业结束时间须晚于开始时间';
}
if (!form.categoryId.trim()) return '请选择店铺类型';
if (form.intro.trim()) {
const len = form.intro.trim().length;
if (len < 10 || len > 500) return '门店简介须为 10~500 字';