feat: update appraisal return address and test packaging assets

This commit is contained in:
wushumin
2026-06-15 20:08:36 +08:00
parent fa267c4413
commit 9be60fbe17
23 changed files with 1806 additions and 393 deletions

View File

@@ -1,3 +1,3 @@
VITE_API_BASE_URL=https://test.api.anxinjianyan.com
VITE_APP_ENV=test
VITE_API_BASE_URL=http://127.0.0.1:8788
VITE_APP_ENV=development
VITE_APP_TITLE=安心验

View File

@@ -14,6 +14,7 @@ export interface CategoryOption {
category_id: number;
category_name: string;
category_code: string;
image_url?: string;
}
export interface UploadItem {

View File

@@ -50,7 +50,8 @@
{
"path": "pages/order/detail",
"style": {
"navigationBarTitleText": "订单详情"
"navigationBarTitleText": "订单详情",
"navigationStyle": "custom"
}
},
{

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { appraisalApi } from "../../api/appraisal";
import { appraisalApi, type AppraisalServicePackage, type PreviewData } from "../../api/appraisal";
import { appApi, type UserAddressItem } from "../../api/app";
import { useAppraisalStore } from "../../stores/appraisal";
import { isMissingDraftError, rebuildDraftFromStore } from "../../utils/appraisal-flow";
@@ -15,18 +15,24 @@ const submitting = ref(false);
const addressSheetVisible = ref(false);
const addressesLoading = ref(false);
const addressOptions = ref<UserAddressItem[]>([]);
const packageOptions = ref<AppraisalServicePackage[]>([]);
const packageOptionsLoading = ref(false);
const packageOptionsError = ref("");
const packageUpdating = ref(false);
const selectedReturnAddress = computed(() => store.returnAddress);
const recentCreatedAddressStorageKey = "anxinyan_recent_created_address_id";
const serviceProviderText = computed(() => preview.value?.service_summary.service_provider_text || "安心验鉴定");
const currentServiceProvider = computed(() => preview.value?.service_summary.service_provider || store.serviceProvider || "anxinyan");
const packageNameText = computed(() => preview.value?.service_summary.price_package_name || store.pricePackageName || "");
const selectedPackageId = computed(() => Number(preview.value?.service_summary.price_package_id || store.pricePackageId || 0));
const categoryText = computed(() => preview.value?.product_summary.category_name || store.product.categoryName || "-");
const brandText = computed(() => preview.value?.product_summary.brand_name || store.product.brandName || "未填写");
const productNameText = computed(() => preview.value?.product_summary.product_name || `${categoryText.value} ${brandText.value === "未填写" ? "" : brandText.value}`.trim());
const serviceFeeText = computed(() => formatMoney(preview.value?.fee_detail.service_fee || 0));
const discountFeeText = computed(() => formatMoney(preview.value?.fee_detail.discount_fee || 0));
const payAmountText = computed(() => formatMoney(preview.value?.fee_detail.pay_amount || 0));
const canSubmit = computed(() => Boolean(store.draftId && store.returnAddress.id && !loading.value && !submitting.value));
const canSubmit = computed(() => Boolean(store.draftId && store.returnAddress.id && !loading.value && !submitting.value && !packageUpdating.value));
function formatMoney(value: number | string) {
const amount = Number(value || 0);
@@ -34,6 +40,20 @@ function formatMoney(value: number | string) {
return amount % 1 === 0 ? String(amount) : amount.toFixed(2);
}
function normalizeProvider(value = "") {
return value === "zhongjian" ? "zhongjian" : "anxinyan";
}
function syncPackageFromPreview(data: PreviewData) {
store.setServiceProvider(data.service_summary.service_provider || store.serviceProvider);
store.setPricePackage({
id: Number(data.service_summary.price_package_id || 0),
packageName: data.service_summary.price_package_name || "",
packageCode: data.service_summary.price_package_code || "",
price: Number(data.fee_detail.service_fee || 0),
});
}
function applySelectedAddress(item: UserAddressItem) {
store.setReturnAddress({
id: item.id,
@@ -125,11 +145,70 @@ async function loadPreview() {
showInfoToast("草稿已自动恢复,请确认订单信息后继续提交。");
}
store.setPreview(data);
syncPackageFromPreview(data);
} catch (error) {
showErrorToast(error, "订单预览加载失败");
}
}
async function loadPackageOptions() {
if (packageOptionsLoading.value) return;
packageOptionsLoading.value = true;
packageOptionsError.value = "";
try {
const data = await appraisalApi.getServiceConfigs();
const provider = normalizeProvider(currentServiceProvider.value);
const config = data.list.find((item) => normalizeProvider(item.service_provider) === provider);
packageOptions.value = config?.packages || [];
} catch (error) {
packageOptions.value = [];
packageOptionsError.value = "套餐列表加载失败,当前订单仍可按已选套餐继续支付。";
showErrorToast(error, "套餐列表加载失败");
} finally {
packageOptionsLoading.value = false;
}
}
async function selectPricePackage(item: AppraisalServicePackage) {
if (packageUpdating.value || submitting.value || loading.value) return;
if (!item.is_enabled) {
showInfoToast("所选价格套餐已停用,请选择其他套餐");
return;
}
if (item.id === selectedPackageId.value) return;
if (!store.draftId) {
showInfoToast("订单信息未准备完成,请返回上一步检查");
return;
}
packageUpdating.value = true;
try {
await withLoading("正在更新套餐", async () => {
const serviceProvider = normalizeProvider(currentServiceProvider.value);
await appraisalApi.saveDraft({
draft_id: store.draftId,
current_step: store.currentStep || 4,
service_provider: serviceProvider,
price_package_id: item.id,
price_package_code: item.package_code,
});
store.setServiceProvider(serviceProvider);
store.setPricePackage({
id: item.id,
packageName: item.package_name,
packageCode: item.package_code,
price: item.price,
});
store.setPreview(null);
await loadPreview();
});
} catch (error) {
showErrorToast(error, "套餐更新失败");
} finally {
packageUpdating.value = false;
}
}
async function goSuccess() {
if (submitting.value) return;
if (!store.draftId) {
@@ -187,6 +266,7 @@ onLoad(async () => {
store.hydrate();
await fetchAddresses();
await loadPreview();
await loadPackageOptions();
});
onShow(fetchAddresses);
@@ -195,16 +275,8 @@ onShow(fetchAddresses);
<template>
<view class="confirm-page">
<view class="confirm-nav">
<view class="confirm-nav__home" @click="goBack">
<view class="confirm-nav__home-roof"></view>
<view class="confirm-nav__home-body"></view>
</view>
<view class="confirm-nav__back" @click="goBack"></view>
<view class="confirm-nav__title">确认订单</view>
<view class="confirm-nav__capsule">
<text class="confirm-nav__dots"></text>
<view class="confirm-nav__divider"></view>
<view class="confirm-nav__circle"></view>
</view>
</view>
<view v-if="loading" class="confirm-state">
@@ -234,16 +306,51 @@ onShow(fetchAddresses);
<view class="product-summary__thumb">
<view class="product-summary__mark">{{ brandText === "未填写" ? "AXY" : brandText.slice(0, 2) }}</view>
</view>
<view class="product-summary__info">
<view class="product-summary__row">服务{{ serviceProviderText }}</view>
<view v-if="packageNameText" class="product-summary__row">套餐{{ packageNameText }}</view>
<view class="product-summary__row">{{ categoryText }}</view>
<view class="product-summary__row">品牌{{ brandText }}</view>
<view class="product-summary__info">
<view class="product-summary__row">服务{{ serviceProviderText }}</view>
<view class="product-summary__row">品类{{ categoryText }}</view>
<view class="product-summary__row">{{ brandText }}</view>
</view>
</view>
<view class="confirm-card__muted confirm-card__muted--top">{{ productNameText }}</view>
</view>
<view class="confirm-card package-section">
<view class="package-section__head">
<view>
<view class="confirm-card__title">价格套餐</view>
<view class="package-section__desc">选择本次鉴定服务使用的价格套餐</view>
</view>
<view v-if="packageUpdating" class="package-section__status">更新中...</view>
</view>
<view v-if="packageOptionsLoading" class="package-empty">套餐加载中...</view>
<view v-else-if="packageOptionsError" class="package-empty">{{ packageOptionsError }}</view>
<view v-else-if="packageOptions.length" class="package-list">
<view
v-for="item in packageOptions"
:key="item.id"
:class="[
'package-card',
selectedPackageId === item.id ? 'package-card--selected' : '',
packageUpdating ? 'package-card--disabled' : '',
]"
@click="selectPricePackage(item)"
>
<view class="package-card__main">
<view class="package-card__name">{{ item.package_name }}</view>
<view v-if="item.description" class="package-card__desc">{{ item.description }}</view>
</view>
<view class="package-card__side">
<view class="package-card__price">{{ formatMoney(item.price) }}</view>
<view v-if="selectedPackageId === item.id" class="package-card__tag">已选</view>
<view v-else-if="item.is_default" class="package-card__tag package-card__tag--muted">默认</view>
</view>
</view>
</view>
<view v-else class="package-empty">当前服务暂无可切换套餐</view>
</view>
<view class="confirm-card">
<view class="fee-head">
<view class="confirm-card__title">费用明细</view>
@@ -340,51 +447,34 @@ onShow(fetchAddresses);
}
.confirm-nav {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
justify-content: center;
min-height: 72rpx;
margin-bottom: 28rpx;
}
.confirm-nav__home {
position: relative;
width: 52rpx;
height: 52rpx;
}
.confirm-nav__home-roof {
.confirm-nav__back {
position: absolute;
left: 10rpx;
top: 4rpx;
width: 32rpx;
height: 32rpx;
border-left: 5rpx solid #252527;
border-top: 5rpx solid #252527;
transform: rotate(45deg);
left: 0;
top: 50%;
width: 64rpx;
height: 64rpx;
transform: translateY(-50%);
}
.confirm-nav__home-body {
position: absolute;
left: 12rpx;
bottom: 6rpx;
width: 30rpx;
height: 28rpx;
border: 5rpx solid #252527;
border-top: 0;
border-radius: 4rpx;
background: #ffffff;
}
.confirm-nav__home-body::after {
.confirm-nav__back::before {
content: "";
position: absolute;
right: 2rpx;
bottom: 2rpx;
width: 18rpx;
height: 12rpx;
border-radius: 12rpx 12rpx 12rpx 4rpx;
background: #edbd00;
left: 20rpx;
top: 17rpx;
width: 24rpx;
height: 24rpx;
border-left: 5rpx solid #252527;
border-bottom: 5rpx solid #252527;
border-radius: 2rpx;
transform: rotate(45deg);
}
.confirm-nav__title {
@@ -394,38 +484,6 @@ onShow(fetchAddresses);
line-height: 1.2;
}
.confirm-nav__capsule {
display: flex;
align-items: center;
justify-content: center;
width: 150rpx;
height: 64rpx;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.72);
}
.confirm-nav__dots {
color: #111111;
font-size: 36rpx;
font-weight: 800;
line-height: 1;
}
.confirm-nav__divider {
width: 1px;
height: 36rpx;
margin: 0 18rpx;
background: rgba(0, 0, 0, 0.3);
}
.confirm-nav__circle {
width: 34rpx;
height: 34rpx;
border: 7rpx solid #111111;
border-radius: 50%;
}
.confirm-state,
.confirm-card,
.agreement-item {
@@ -595,6 +653,116 @@ onShow(fetchAddresses);
word-break: break-all;
}
.package-section__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
}
.package-section__desc {
margin-top: 6rpx;
color: #a0a0a4;
font-size: 24rpx;
line-height: 1.5;
}
.package-section__status {
flex-shrink: 0;
color: #a0a0a4;
font-size: 24rpx;
line-height: 1.5;
}
.package-list {
display: flex;
flex-direction: column;
gap: 18rpx;
margin-top: 24rpx;
}
.package-card {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
min-height: 128rpx;
padding: 22rpx 24rpx;
border: 2rpx solid #e6e6e8;
border-radius: 16rpx;
background: #ffffff;
}
.package-card--selected {
border-color: #edbd00;
background: #fffaf0;
}
.package-card--disabled {
opacity: 0.68;
}
.package-card__main {
flex: 1;
min-width: 0;
}
.package-card__name {
color: #252527;
font-size: 28rpx;
font-weight: 800;
line-height: 1.35;
}
.package-card__desc {
margin-top: 8rpx;
color: #8c8c90;
font-size: 22rpx;
line-height: 1.45;
}
.package-card__side {
flex-shrink: 0;
text-align: right;
}
.package-card__price {
color: #edbd00;
font-size: 34rpx;
font-weight: 900;
line-height: 1.2;
}
.package-card__tag {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 64rpx;
height: 34rpx;
margin-top: 8rpx;
border-radius: 999rpx;
background: rgba(237, 189, 0, 0.13);
color: #9a7700;
font-size: 20rpx;
font-weight: 700;
}
.package-card__tag--muted {
background: #f0f0f2;
color: #8c8c90;
}
.package-empty {
margin-top: 20rpx;
padding: 30rpx 24rpx;
border-radius: 16rpx;
background: #f7f7f8;
color: #9a9a9d;
font-size: 24rpx;
line-height: 1.5;
text-align: center;
}
.fee-head {
display: flex;
align-items: center;

View File

@@ -175,7 +175,7 @@ async function saveProductAndGoConfirm(payload: { brandId: number; brandName: st
function selectBrand(item: BrandOption) {
void saveProductAndGoConfirm({
brandId: item.id,
brandName: item.name || item.enName || item.displayName,
brandName: item.displayName,
});
}

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { appraisalApi, type AppraisalServiceConfig, type AppraisalServicePackage, type CategoryOption } from "../../api/appraisal";
import { appraisalApi, type CategoryOption } from "../../api/appraisal";
import { useAppraisalStore } from "../../stores/appraisal";
import { isLoggedIn, redirectToLogin } from "../../utils/auth";
import { showErrorToast, showInfoToast, withLoading } from "../../utils/feedback";
@@ -12,6 +12,7 @@ type CategoryPickerItem = {
id: number;
name: string;
code: string;
imageUrl: string;
visual: string;
};
@@ -19,7 +20,6 @@ const providerIntro: Record<ServiceProvider, {
navTitle: string;
logoText: string;
intro: string;
priceText: string;
highlights: string[];
steps: Array<{
title: string;
@@ -31,7 +31,6 @@ const providerIntro: Record<ServiceProvider, {
navTitle: "安心验鉴定",
logoText: "安心验 鉴定",
intro: "安心验(深圳)商品检验鉴定有限责任公司立足深圳核心产业服务区,是一家专业从事商品检验、鉴定、测试及技术咨询的第三方服务机构。公司依托粤港澳大湾区雄厚的产业基础与国际贸易枢纽优势,致力于为 C 端消费者及 B 端电商平台、商家提供网购商品真伪鉴定、成色评级、价值评估及争议仲裁等一站式解决方案。",
priceText: "¥99 起",
highlights: ["独立第三方", "报告可验真", "流程可追踪"],
steps: [
{ title: "下单付款", desc: "商品寄至鉴定中心,录像验收。", visual: "pay" },
@@ -44,7 +43,6 @@ const providerIntro: Record<ServiceProvider, {
navTitle: "中检鉴定",
logoText: "中检 鉴定",
intro: "中检鉴定服务面向更高规格出具需求,沿用安心验标准化下单、寄送与进度追踪流程,由合作机构完成对应服务交付,适用于对报告出具方有明确要求的鉴定场景。",
priceText: "¥199 起",
highlights: ["合作机构", "报告出具方不同", "流程一致"],
steps: [
{ title: "选择中检服务", desc: "首页选定中检鉴定后,确认品类、品牌和费用。", visual: "pay" },
@@ -75,24 +73,8 @@ const categorySheetVisible = ref(false);
const submitting = ref(false);
const loadError = ref("");
const selectedCategoryId = ref(0);
const selectedPackageId = ref(0);
const serviceConfigsLoading = ref(false);
const serviceConfigs = ref<Record<ServiceProvider, AppraisalServiceConfig | undefined>>({
anxinyan: undefined,
zhongjian: undefined,
});
const currentIntro = computed(() => providerIntro[providerCode.value]);
const currentPackages = computed<AppraisalServicePackage[]>(() => serviceConfigs.value[providerCode.value]?.packages || []);
const currentPackage = computed(() => currentPackages.value.find((item) => item.id === selectedPackageId.value) || currentPackages.value.find((item) => item.is_default) || currentPackages.value[0]);
const currentPriceText = computed(() => {
if (currentPackage.value) {
return `¥${formatPrice(Number(currentPackage.value.price))}`;
}
const price = serviceConfigs.value[providerCode.value]?.price;
return Number(price || 0) > 0 ? `¥${formatPrice(Number(price))}` : currentIntro.value.priceText;
});
const currentPriceDesc = computed(() => currentPackage.value?.package_name || "请选择价格套餐");
const providerThemeClass = computed(() => `service-intro--${providerCode.value}`);
function normalizeProvider(value?: string): ServiceProvider {
@@ -116,44 +98,6 @@ function resolveCategoryVisual(item: CategoryOption) {
return matched?.visual || "default";
}
function formatPrice(price: number) {
return Number.isInteger(price) ? String(price) : price.toFixed(2);
}
async function loadServiceConfigs() {
if (serviceConfigsLoading.value) return;
serviceConfigsLoading.value = true;
try {
const data = await appraisalApi.getServiceConfigs();
const nextConfigs: Record<ServiceProvider, AppraisalServiceConfig | undefined> = {
anxinyan: undefined,
zhongjian: undefined,
};
data.list.forEach((item) => {
const provider = normalizeProvider(item.service_provider);
nextConfigs[provider] = item;
});
serviceConfigs.value = nextConfigs;
applyDefaultPackage();
} catch (error) {
console.warn("service config fallback", error);
} finally {
serviceConfigsLoading.value = false;
}
}
function applyDefaultPackage() {
const packages = currentPackages.value;
if (packages.some((item) => item.id === selectedPackageId.value)) return;
const target = packages.find((item) => item.is_default) || packages[0];
selectedPackageId.value = target?.id || 0;
}
function selectPackage(item: AppraisalServicePackage) {
if (!item.is_enabled) return;
selectedPackageId.value = item.id;
}
function buildServicePageUrl(options: ServicePageOptions = {}) {
const params: string[] = [];
const provider = options.provider || providerCode.value;
@@ -189,6 +133,7 @@ async function loadCategories() {
id: item.category_id,
name: item.category_name,
code: item.category_code,
imageUrl: item.image_url || "",
visual: resolveCategoryVisual(item),
}));
categoriesLoaded.value = true;
@@ -209,14 +154,6 @@ async function openCategorySheet() {
return;
}
if (!currentPackage.value) {
await loadServiceConfigs();
}
if (!currentPackage.value) {
showInfoToast("当前服务暂无可用价格套餐");
return;
}
categorySheetVisible.value = true;
await loadCategories();
}
@@ -228,11 +165,6 @@ function closeCategorySheet() {
async function selectCategory(item: CategoryPickerItem) {
if (submitting.value) return;
const selectedPackage = currentPackage.value;
if (!selectedPackage) {
showInfoToast("请先选择价格套餐");
return;
}
selectedCategoryId.value = item.id;
submitting.value = true;
try {
@@ -241,13 +173,7 @@ async function selectCategory(item: CategoryPickerItem) {
store.resetForNewFlow();
store.clearLegacyExtraDefaults();
store.setServiceProvider(providerCode.value);
store.setPricePackage({
id: selectedPackage.id,
packageName: selectedPackage.package_name,
packageCode: selectedPackage.package_code,
price: selectedPackage.price,
});
const draft = await appraisalApi.createDraft(providerCode.value, selectedPackage.id, selectedPackage.package_code);
const draft = await appraisalApi.createDraft(providerCode.value);
draftId = draft.draft_id;
store.setDraft(draftId);
store.setPricePackage({
@@ -293,21 +219,18 @@ function categoryKey(item: CategoryPickerItem) {
return `${item.id}-${item.name}`;
}
function goBack() {
uni.navigateBack();
function goHome() {
uni.switchTab({ url: "/pages/home/index" });
}
function applyProviderFromOptions(options: ServicePageOptions = {}) {
providerCode.value = normalizeProvider(options.provider);
selectedPackageId.value = 0;
applyDefaultPackage();
uni.setNavigationBarTitle({ title: currentIntro.value.navTitle });
}
onLoad((options: ServicePageOptions = {}) => {
const resolvedOptions = resolveServicePageOptions(options);
applyProviderFromOptions(resolvedOptions);
void loadServiceConfigs();
if (resolvedOptions.start === "1" && isLoggedIn()) {
setTimeout(() => {
void openCategorySheet();
@@ -322,16 +245,11 @@ onLoad((options: ServicePageOptions = {}) => {
<view :class="['service-intro', providerThemeClass]">
<view class="service-intro__hero">
<view class="service-intro__nav">
<view class="service-intro__home" @click="goBack">
<view class="service-intro__home" @click="goHome">
<view class="service-intro__home-roof"></view>
<view class="service-intro__home-body"></view>
</view>
<view class="service-intro__title">鉴定服务</view>
<view class="service-intro__capsule">
<text class="service-intro__dots"></text>
<view class="service-intro__divider"></view>
<view class="service-intro__circle"></view>
</view>
</view>
<view class="service-intro__brand">
@@ -363,32 +281,9 @@ onLoad((options: ServicePageOptions = {}) => {
</view>
</view>
<view class="service-intro__package-title">价格套餐</view>
<view v-if="currentPackages.length" class="package-list">
<view
v-for="item in currentPackages"
:key="item.id"
:class="['package-card', selectedPackageId === item.id ? 'package-card--selected' : '']"
@click="selectPackage(item)"
>
<view class="package-card__main">
<view class="package-card__name">{{ item.package_name }}</view>
<view v-if="item.description" class="package-card__desc">{{ item.description }}</view>
</view>
<view class="package-card__side">
<view class="package-card__price">¥{{ formatPrice(item.price) }}</view>
<view v-if="item.is_default" class="package-card__tag">默认</view>
</view>
</view>
</view>
<view v-else class="package-empty">{{ serviceConfigsLoading ? "套餐加载中..." : "当前服务暂无可用套餐" }}</view>
</view>
<view class="service-intro__action-bar">
<view>
<view class="service-intro__price">{{ currentPriceText }}</view>
<view class="service-intro__price-desc">{{ currentPriceDesc }}</view>
</view>
<view :class="['service-intro__primary', submitting ? 'service-intro__primary--disabled' : '']" @click="openCategorySheet">
{{ submitting ? "处理中..." : "发起鉴定" }}
</view>
@@ -412,7 +307,13 @@ onLoad((options: ServicePageOptions = {}) => {
@click="selectCategory(item)"
>
<view class="category-card__name">{{ item.name }}</view>
<view :class="['category-card__visual', `category-card__visual--${item.visual}`]"></view>
<image
v-if="item.imageUrl"
class="category-card__image"
:src="item.imageUrl"
mode="aspectFit"
/>
<view v-else :class="['category-card__visual', `category-card__visual--${item.visual}`]"></view>
</view>
</view>
</view>
@@ -495,22 +396,25 @@ onLoad((options: ServicePageOptions = {}) => {
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
justify-content: center;
min-height: 64rpx;
}
.service-intro__home {
position: relative;
width: 52rpx;
height: 52rpx;
position: absolute;
left: 0;
top: 50%;
width: 64rpx;
height: 64rpx;
transform: translateY(-50%);
}
.service-intro__home-roof {
position: absolute;
left: 10rpx;
top: 4rpx;
width: 32rpx;
height: 32rpx;
left: 15rpx;
top: 9rpx;
width: 34rpx;
height: 34rpx;
border-left: 5rpx solid #252527;
border-top: 5rpx solid #252527;
transform: rotate(45deg);
@@ -518,10 +422,10 @@ onLoad((options: ServicePageOptions = {}) => {
.service-intro__home-body {
position: absolute;
left: 12rpx;
bottom: 6rpx;
width: 30rpx;
height: 28rpx;
left: 17rpx;
bottom: 10rpx;
width: 31rpx;
height: 29rpx;
border: 5rpx solid #252527;
border-top: 0;
border-radius: 4rpx;
@@ -546,38 +450,6 @@ onLoad((options: ServicePageOptions = {}) => {
line-height: 1.2;
}
.service-intro__capsule {
display: flex;
align-items: center;
justify-content: center;
width: 146rpx;
height: 62rpx;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.72);
}
.service-intro__dots {
color: #111111;
font-size: 36rpx;
font-weight: 800;
line-height: 1;
}
.service-intro__divider {
width: 1px;
height: 36rpx;
margin: 0 18rpx;
background: rgba(0, 0, 0, 0.3);
}
.service-intro__circle {
width: 34rpx;
height: 34rpx;
border: 7rpx solid #111111;
border-radius: 50%;
}
.service-intro__brand {
position: relative;
z-index: 1;
@@ -839,109 +711,6 @@ onLoad((options: ServicePageOptions = {}) => {
width: 232rpx;
}
.service-intro__package-title {
margin-top: 6rpx;
color: #252527;
font-size: 32rpx;
font-weight: 800;
line-height: 1.3;
text-align: center;
}
.package-list {
display: flex;
flex-direction: column;
gap: 18rpx;
margin-top: 24rpx;
}
.package-card {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
min-height: 128rpx;
padding: 22rpx 24rpx;
border: 2rpx solid #e6e6e8;
border-radius: 16rpx;
background: #ffffff;
}
.package-card--selected {
border-color: #edbd00;
background: #fffaf0;
}
.service-intro--zhongjian .package-card--selected {
border-color: #416f9e;
background: #f3f7fb;
}
.package-card__main {
flex: 1;
min-width: 0;
}
.package-card__name {
color: #252527;
font-size: 28rpx;
font-weight: 800;
line-height: 1.35;
}
.package-card__desc {
margin-top: 8rpx;
color: #8c8c90;
font-size: 22rpx;
line-height: 1.45;
}
.package-card__side {
flex-shrink: 0;
text-align: right;
}
.package-card__price {
color: #edbd00;
font-size: 34rpx;
font-weight: 900;
line-height: 1.2;
}
.service-intro--zhongjian .package-card__price {
color: #416f9e;
}
.package-card__tag {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 64rpx;
height: 34rpx;
margin-top: 8rpx;
border-radius: 999rpx;
background: rgba(237, 189, 0, 0.13);
color: #9a7700;
font-size: 20rpx;
font-weight: 700;
}
.service-intro--zhongjian .package-card__tag {
background: rgba(65, 111, 158, 0.12);
color: #416f9e;
}
.package-empty {
margin-top: 20rpx;
padding: 30rpx 24rpx;
border-radius: 16rpx;
background: #ffffff;
color: #9a9a9d;
font-size: 24rpx;
line-height: 1.5;
text-align: center;
}
.service-intro__action-bar {
position: fixed;
left: 0;
@@ -950,33 +719,18 @@ onLoad((options: ServicePageOptions = {}) => {
z-index: 90;
display: flex;
align-items: center;
justify-content: space-between;
gap: 28rpx;
justify-content: center;
padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom));
border-top: 1px solid #e9e9eb;
background: rgba(255, 255, 255, 0.96);
}
.service-intro__price {
color: #edbd00;
font-size: 40rpx;
font-weight: 800;
line-height: 1.2;
}
.service-intro__price-desc {
margin-top: 4rpx;
color: #9a9a9d;
font-size: 22rpx;
line-height: 1.4;
}
.service-intro__primary {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
max-width: 360rpx;
max-width: 640rpx;
height: 86rpx;
border-radius: 999rpx;
background: #edbd00;
@@ -1095,6 +849,14 @@ onLoad((options: ServicePageOptions = {}) => {
background: #f7f7f8;
}
.category-card__image {
position: absolute;
right: 18rpx;
bottom: 18rpx;
width: 128rpx;
height: 104rpx;
}
.category-card__visual::before,
.category-card__visual::after {
content: "";

View File

@@ -12,7 +12,7 @@ const pageLoading = ref(false);
const pageReady = ref(false);
const categoryDataLoaded = ref(false);
const loadError = ref("");
const defaultHeroBackground = "/static/home/home-reference.jpg";
const defaultHeroBackground = "/static/home/home-hero-bg.png";
const categoryFallbackVisuals = [
{ visual: "bag", keys: ["luxury_bag", "奢侈品箱包", "箱包"] },
@@ -239,7 +239,7 @@ onShow(fetchHome);
height: 470rpx;
overflow: hidden;
background-size: cover;
background-position: center top;
background-position: center 44%;
background-repeat: no-repeat;
}

View File

@@ -209,6 +209,10 @@ const secondaryActionText = computed(() =>
isPendingPayment.value ? (cancelSubmitting.value ? "取消中..." : "取消订单") : detail.value.available_actions.secondary_action,
);
function goOrderList() {
uni.switchTab({ url: "/pages/order/index" });
}
async function fetchDetail() {
if (!orderId.value) return;
loading.value = true;
@@ -424,6 +428,11 @@ onShow(async () => {
<template>
<view class="app-page app-page--tight">
<view class="order-detail-nav">
<view class="order-detail-nav__back" @click="goOrderList"></view>
<view class="order-detail-nav__title">订单详情</view>
</view>
<view v-if="!pageReady && loading" class="section notice-card">
<view class="notice-card__title">正在加载订单详情</view>
<view class="notice-card__desc">请稍候我们正在同步订单状态资料寄回地址和处理记录</view>
@@ -710,6 +719,44 @@ onShow(async () => {
</template>
<style scoped>
.order-detail-nav {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 72rpx;
margin-bottom: 24rpx;
}
.order-detail-nav__back {
position: absolute;
left: 0;
top: 50%;
width: 64rpx;
height: 64rpx;
transform: translateY(-50%);
}
.order-detail-nav__back::before {
content: "";
position: absolute;
left: 20rpx;
top: 17rpx;
width: 24rpx;
height: 24rpx;
border-left: 5rpx solid #252527;
border-bottom: 5rpx solid #252527;
border-radius: 2rpx;
transform: rotate(45deg);
}
.order-detail-nav__title {
color: var(--color-heading);
font-size: 38rpx;
font-weight: var(--font-weight-semibold);
line-height: 1.2;
}
.order-detail-hero {
padding-bottom: 34rpx;
}

View File

@@ -202,7 +202,7 @@ onShow(async () => {
{{ item.service_provider === "zhongjian" ? "中检鉴定" : "安心验鉴定" }}
<text v-if="item.price_package_name"> / {{ item.price_package_name }}</text>
</view>
<view class="order-card__action">{{ item.primary_action }}</view>
<view v-if="item.primary_action" class="order-card__action">{{ item.primary_action }}</view>
</view>
</view>
</view>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB