feat(project): align store delivery pages with live APIs and geocode status
This commit is contained in:
144
apps/web-antd/src/views/product/detail/index.vue
Normal file
144
apps/web-antd/src/views/product/detail/index.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 文件职责:商品详情骨架页。
|
||||
* 1. 根据路由参数加载商品详情数据。
|
||||
* 2. 展示基础信息并提供返回入口。
|
||||
*/
|
||||
import type { ProductDetailDto } from '#/api/product';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, Descriptions, Empty, Spin, Tag } from 'ant-design-vue';
|
||||
|
||||
import { getProductDetailApi } from '#/api/product';
|
||||
|
||||
import {
|
||||
formatCurrency,
|
||||
resolveStatusMeta,
|
||||
} from '../list/composables/product-list-page/helpers';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const isLoading = ref(false);
|
||||
const detail = ref<null | ProductDetailDto>(null);
|
||||
|
||||
const storeId = computed(() => String(route.query.storeId || ''));
|
||||
const productId = computed(() => String(route.query.productId || ''));
|
||||
|
||||
const statusMeta = computed(() => {
|
||||
if (!detail.value) return resolveStatusMeta('off_shelf');
|
||||
return resolveStatusMeta(detail.value.status);
|
||||
});
|
||||
|
||||
/** 加载商品详情。 */
|
||||
async function loadDetail() {
|
||||
if (!storeId.value || !productId.value) {
|
||||
detail.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
try {
|
||||
detail.value = await getProductDetailApi({
|
||||
storeId: storeId.value,
|
||||
productId: productId.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
detail.value = null;
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 返回商品列表页。 */
|
||||
function goBack() {
|
||||
router.push('/product/list');
|
||||
}
|
||||
|
||||
watch([storeId, productId], loadDetail, { immediate: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page title="商品详情" content-class="space-y-4 page-product-detail">
|
||||
<Card :bordered="false">
|
||||
<Button @click="goBack">返回商品列表</Button>
|
||||
</Card>
|
||||
|
||||
<Spin :spinning="isLoading">
|
||||
<Card v-if="detail" :bordered="false">
|
||||
<div class="product-detail-header">
|
||||
<div class="product-detail-cover">
|
||||
{{ detail.name.slice(0, 1) }}
|
||||
</div>
|
||||
<div class="product-detail-title-wrap">
|
||||
<div class="title">{{ detail.name }}</div>
|
||||
<div class="sub">{{ detail.subtitle || '--' }}</div>
|
||||
<div class="spu">{{ detail.spuCode }}</div>
|
||||
</div>
|
||||
<Tag class="product-detail-status" :color="statusMeta.color">
|
||||
{{ statusMeta.label }}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Descriptions :column="2" bordered class="product-detail-descriptions">
|
||||
<Descriptions.Item label="商品ID">{{ detail.id }}</Descriptions.Item>
|
||||
<Descriptions.Item label="分类">
|
||||
{{ detail.categoryName || '--' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="商品类型">
|
||||
{{ detail.kind === 'combo' ? '套餐' : '单品' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="售价">
|
||||
{{ formatCurrency(detail.price) }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="原价">
|
||||
{{
|
||||
detail.originalPrice ? formatCurrency(detail.originalPrice) : '--'
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="库存">
|
||||
{{ detail.stock }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="月销">
|
||||
{{ detail.salesMonthly }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="沽清模式">
|
||||
{{
|
||||
detail.soldoutMode === 'today'
|
||||
? '今日沽清'
|
||||
: detail.soldoutMode === 'timed'
|
||||
? '定时沽清'
|
||||
: detail.soldoutMode === 'permanent'
|
||||
? '永久沽清'
|
||||
: '--'
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="标签" :span="2">
|
||||
<div class="product-detail-tags">
|
||||
<Tag v-for="tag in detail.tags" :key="`${detail.id}-${tag}`">
|
||||
{{ tag }}
|
||||
</Tag>
|
||||
<span v-if="detail.tags.length === 0">--</span>
|
||||
</div>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="描述" :span="2">
|
||||
{{ detail.description || '--' }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
<Card v-else :bordered="false">
|
||||
<Empty description="未找到商品详情" />
|
||||
</Card>
|
||||
</Spin>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
@import './styles/index.less';
|
||||
</style>
|
||||
Reference in New Issue
Block a user