diff --git a/src/pages-service/tickets/components/TicketHome.vue b/src/pages-service/tickets/components/TicketHome.vue
index 47d4b14..f404799 100644
--- a/src/pages-service/tickets/components/TicketHome.vue
+++ b/src/pages-service/tickets/components/TicketHome.vue
@@ -95,7 +95,7 @@
- 已售 {{ product.sold || 0 }}
+ 已售 {{ product.sold }}
¥{{ product.salePrice }}
@@ -106,7 +106,7 @@
- 暂无可售商品
+ {{ loading ? '加载中...' : '暂无可售商品' }}
@@ -124,6 +124,7 @@ const props = defineProps({
scenicSpot: { type: Object, required: true },
groups: { type: Array, default: () => [] },
products: { type: Array, default: () => [] },
+ loading: { type: Boolean, default: false },
})
const emit = defineEmits(['back', 'open-travelers', 'open-orders', 'open-detail', 'buy'])
diff --git a/src/pages-service/tickets/data.js b/src/pages-service/tickets/data.js
index 61d4543..33f9d0a 100644
--- a/src/pages-service/tickets/data.js
+++ b/src/pages-service/tickets/data.js
@@ -49,12 +49,9 @@ export const SCENIC_SPOT = {
}
export const GROUPS = [
- { id: 'ticket', name: '景点门票', subtitle: '累计销量 5000+', description: '一票通玩,核销后门票7日内有效', tag: '官方', productIds: ['adult', 'elderly', 'minor', 'free'], sortOrder: 1, enabled: true },
- { id: 'vip', name: 'VIP速通', subtitle: '专属服务', description: '减少排队等待时间,按预约场次使用', tag: '推荐', productIds: ['vip-fast'], sortOrder: 2, enabled: true },
- { id: 'bus', name: '直通车', subtitle: '往返直达', description: '贵阳市区集合,直达小七孔景区', tag: '省心', productIds: ['direct-bus'], sortOrder: 3, enabled: true },
- { id: 'package', name: '门票+观光车', subtitle: '累计销量 1.5万+', description: '门票与观光车由第三方出票,园内项目使用自营权益码', tag: '热卖', productIds: ['classic'], sortOrder: 4, enabled: true },
- { id: 'tour', name: '一日游', subtitle: '精选线路', description: '轻松游览小七孔代表性景观', tag: '品质', productIds: ['day-tour'], sortOrder: 5, enabled: true },
- { id: 'play', name: '玩乐体验', subtitle: '园内自营项目', description: '工作人员扫描园内权益码后选择项目核销', tag: '自营', productIds: ['boat', 'guide'], sortOrder: 6, enabled: true },
+ { id: 'ticket', name: '门票', commodityTypeCode: '1', productIds: [], sortOrder: 1, enabled: true },
+ { id: 'maolan', name: '茂兰景区', commodityTypeCode: '67d07734e0af4caa918309f5a346a86e', productIds: [], sortOrder: 2, enabled: true },
+ { id: 'heritage-tribe', name: '世遗部落', commodityTypeCode: 'd5766c63028e46f1bd9aaa25d1dc7482', productIds: [], sortOrder: 3, enabled: true },
]
const TIME_SLOTS = ['08:00—09:30', '09:30—11:30', '11:30—13:30', '13:30—15:00', '15:00—16:00']
diff --git a/src/pages-service/tickets/index.vue b/src/pages-service/tickets/index.vue
index 7240d02..7f0c48f 100644
--- a/src/pages-service/tickets/index.vue
+++ b/src/pages-service/tickets/index.vue
@@ -3,7 +3,8 @@
diff --git a/src/pages-service/tickets/model.test.mjs b/src/pages-service/tickets/model.test.mjs
index 7882514..91d9cdc 100644
--- a/src/pages-service/tickets/model.test.mjs
+++ b/src/pages-service/tickets/model.test.mjs
@@ -23,18 +23,13 @@ test('provides coherent default visitor ticket data', async () => {
assert.deepEqual(SCENIC_SPOT, {
id: 'libo-xiaoqikong', name: '荔波小七孔景区', level: '国家5A级景区', heritage: '世界自然遗产', address: '贵州省黔南布依族苗族自治州荔波县', phone: '0854-3619810', latitude: 25.2534, longitude: 107.7255, heroImage: SCENIC_HERO_IMAGE,
})
- assert.deepEqual(GROUPS.map(({ id, name, sortOrder, enabled, productIds }) => ({ id, name, sortOrder, enabled, productIds })), [
- { id: 'ticket', name: '景点门票', sortOrder: 1, enabled: true, productIds: ['adult', 'elderly', 'minor', 'free'] },
- { id: 'vip', name: 'VIP速通', sortOrder: 2, enabled: true, productIds: ['vip-fast'] },
- { id: 'bus', name: '直通车', sortOrder: 3, enabled: true, productIds: ['direct-bus'] },
- { id: 'package', name: '门票+观光车', sortOrder: 4, enabled: true, productIds: ['classic'] },
- { id: 'tour', name: '一日游', sortOrder: 5, enabled: true, productIds: ['day-tour'] },
- { id: 'play', name: '玩乐体验', sortOrder: 6, enabled: true, productIds: ['boat', 'guide'] },
+ assert.deepEqual(GROUPS.map(({ id, name, commodityTypeCode, sortOrder, enabled, productIds }) => ({ id, name, commodityTypeCode, sortOrder, enabled, productIds })), [
+ { id: 'ticket', name: '门票', commodityTypeCode: '1', sortOrder: 1, enabled: true, productIds: [] },
+ { id: 'maolan', name: '茂兰景区', commodityTypeCode: '67d07734e0af4caa918309f5a346a86e', sortOrder: 2, enabled: true, productIds: [] },
+ { id: 'heritage-tribe', name: '世遗部落', commodityTypeCode: 'd5766c63028e46f1bd9aaa25d1dc7482', sortOrder: 3, enabled: true, productIds: [] },
])
assert.deepEqual(PRODUCTS.map(({ id }) => id), ['adult', 'elderly', 'minor', 'free', 'vip-fast', 'direct-bus', 'classic', 'day-tour', 'boat', 'guide'])
const productsById = new Map(PRODUCTS.map((product) => [product.id, product]))
- assert.ok(GROUPS.every((group) => group.productIds.every((productId) => productsById.has(productId))))
- assert.deepEqual(GROUPS.flatMap((group) => group.productIds).sort(), PRODUCTS.map(({ id }) => id).sort())
const productKeys = ['id', 'name', 'shortTitle', 'category', 'description', 'salePrice', 'marketPrice', 'stock', 'sold', 'status', 'clientVisible', 'highlights', 'ageRule', 'included', 'excluded', 'detail', 'purchaseNotice', 'images', 'reservation', 'entitlements']
const reservationKeys = ['requiresDate', 'advanceDays', 'requiresTimeSlot', 'timeSlots', 'realNameRequired', 'idTypes', 'maxQuantity', 'contactPhoneRequired']
const entitlementKeys = ['id', 'name', 'fulfillment', 'quantity', 'unit', 'refundAllocation']
diff --git a/src/pages-service/tickets/view-contract.test.mjs b/src/pages-service/tickets/view-contract.test.mjs
index fd02aa2..4b8dd00 100644
--- a/src/pages-service/tickets/view-contract.test.mjs
+++ b/src/pages-service/tickets/view-contract.test.mjs
@@ -63,13 +63,17 @@ test("storefront synchronizes the left category from right-column scrolling", as
assert.match(source, /TopNavBar/);
});
-test("product detail is an independent prototype-aligned page", async () => {
+test("storefront products open the API-backed goods detail page", async () => {
const indexPage = await readTicketFile("index.vue");
const detailPage = await readTicketFile("product-detail.vue");
const component = await readTicketFile("components/ProductDetail.vue");
const data = await readTicketFile("data.js");
- assert.match(indexPage, /uni\.navigateTo\(\{\s*url: `\/pages-service\/tickets\/product-detail\?productId=\$\{productId\}`/);
+ assert.match(indexPage, /quickBookingList/);
+ assert.match(indexPage, /commodityTypeCode: group\.commodityTypeCode/);
+ assert.match(indexPage, /current: 1/);
+ assert.match(indexPage, /size: 10/);
+ assert.match(indexPage, /url: `\/pages\/goods\/index\?commodityId=\$\{encodeURIComponent\(productId\)\}`/);
assert.doesNotMatch(indexPage, /detailProduct|