feat(store): add store sortOrder and swipeable package gallery

Enable HQ-controlled store list ordering and horizontal multi-image browsing on package detail.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-07 19:58:38 +08:00
parent fe1c6c8158
commit 34a1897c09
9 changed files with 69 additions and 32 deletions
+1
View File
@@ -27,6 +27,7 @@ export type StoreCreateForm = {
bankAccountNo: string;
bankBranch: string;
settlementRate?: number;
sortOrder?: number;
visibilityWhitelistEnabled?: boolean;
visibilityPhones?: string[];
};
+16
View File
@@ -192,6 +192,7 @@ type StoreRow = {
closeTime2?: string | null;
avgPrice?: number | null;
settlementRate?: number;
sortOrder?: number;
category?: { id: string; name: string; parentId?: string | null } | null;
};
@@ -409,6 +410,7 @@ export default function StoresPage() {
? (d.visibilityPhones as string[])
: [],
isTest: !!d.isTest,
sortOrder: d.sortOrder != null ? Number(d.sortOrder) : 0,
});
setDrawerOpen(true);
}
@@ -454,6 +456,7 @@ export default function StoresPage() {
bankBranch: v.bankBranch ?? null,
visibilityWhitelistEnabled: !!v.visibilityWhitelistEnabled,
isTest: !!v.isTest,
sortOrder: v.sortOrder != null ? Number(v.sortOrder) : 0,
...(hasCoords
? { latitude: Number(v.latitude), longitude: Number(v.longitude) }
: {}),
@@ -538,6 +541,7 @@ export default function StoresPage() {
createForm.setFieldsValue({
envPhotoUrls: [],
settlementRate: 60,
sortOrder: 0,
openTime: '10:00',
closeTime: '22:00',
openTime2: undefined,
@@ -626,6 +630,7 @@ export default function StoresPage() {
bankAccountNo: values.bankAccountNo.replace(/\s/g, ''),
bankBranch: values.bankBranch.trim(),
settlementRate: Number(values.settlementRate ?? 60) / 100,
sortOrder: values.sortOrder != null ? Number(values.sortOrder) : 0,
visibilityWhitelistEnabled: !!values.visibilityWhitelistEnabled,
}),
});
@@ -699,6 +704,7 @@ export default function StoresPage() {
v ? <Tag color="orange"></Tag> : <Tag></Tag>,
},
{ title: '介绍', dataIndex: 'intro', width: 160, ellipsis: true, render: (v) => v || '—' },
{ title: '排序', dataIndex: 'sortOrder', width: 70 },
{ title: '店长', dataIndex: ['account', 'name'], width: 90, render: (v) => v || '—' },
{ title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime },
{
@@ -978,6 +984,13 @@ export default function StoresPage() {
<Form.Item name="avgPrice" label="人均费用(选填)">
<InputNumber min={0} precision={0} style={{ width: '100%' }} addonAfter="元" placeholder="用户端展示" />
</Form.Item>
<Form.Item
name="sortOrder"
label="排序"
extra="数值越小越靠前;同排序时按距离(有定位)或创建时间"
>
<InputNumber min={0} precision={0} style={{ width: '100%' }} placeholder="0" />
</Form.Item>
<Space wrap style={{ width: '100%' }}>
<Form.Item name="openTime" label="营业开始" rules={[{ required: true }]}>
<Input type="time" style={{ width: 140 }} />
@@ -1154,6 +1167,9 @@ export default function StoresPage() {
<Form.Item name="phone" label="门店手机号(登录账号)" rules={[{ required: true, message: '请填写门店手机号' }]}>
<Input placeholder="11位手机号" />
</Form.Item>
<Form.Item name="sortOrder" label="排序" extra="数值越小越靠前">
<InputNumber min={0} precision={0} style={{ width: '100%' }} placeholder="0" />
</Form.Item>
<Form.Item name="address" label="详细地址" rules={[{ required: true, message: '请填写详细地址' }]}>
<Input.TextArea rows={2} placeholder="请输入详细门牌号" />
</Form.Item>
@@ -1,9 +1,10 @@
import { useCallback, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import { View, Text } from '@tarojs/components';
import Taro, { useLoad, useRouter } from '@tarojs/taro';
import { normalizeStorePackageImageUrls } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
import PageNavBar from '../../components/PageNavBar';
import ProductCarousel from '../../components/ProductCarousel';
import { request, toast } from '../../lib/api';
type StorePackage = {
@@ -99,10 +100,6 @@ export default function StorePackageDetailPage() {
}
}
function previewImage(urls: string[], current: string) {
Taro.previewImage({ urls, current }).catch(() => toast('无法预览图片'));
}
if (loading) {
return (
<PageShell variant="scroll" className="store-package-detail-page">
@@ -139,19 +136,16 @@ export default function StorePackageDetailPage() {
) : null}
</View>
{imageUrls.map((url) => (
<View
key={url}
className="store-package-detail-photo"
onClick={() => previewImage(imageUrls, url)}
>
<Image
className="store-package-detail-photo-image"
src={url}
mode="widthFix"
{imageUrls.length > 0 ? (
<View className="store-package-detail-gallery">
<ProductCarousel
images={imageUrls}
alt={pkg.name}
variant="detail"
previewable
/>
</View>
))}
) : null}
<View className="store-package-detail-content">
<View className="store-detail-package-field">
+3 -5
View File
@@ -352,7 +352,7 @@
padding-left: 4px;
}
.store-package-detail-photo {
.store-package-detail-gallery {
margin: 14px 0 0;
border-radius: var(--radius-lg);
overflow: hidden;
@@ -360,10 +360,8 @@
box-shadow: var(--shadow-card);
}
.store-package-detail-photo-image {
width: 100%;
display: block;
vertical-align: top;
.store-package-detail-gallery .detail-carousel-wrap {
border-radius: 0;
}
.store-package-detail-content {