新增全局 YjyfTabBar 组件,替换各页面自定义导航,实现一致的 tab 切换逻辑与图标样式 新增全局设计令牌与 SCSS 变量系统,统一色彩、间距、字阶、圆角与投影规范 新增 POST 请求防并发锁,避免重复提交 重构多个页面,添加骨架屏、空状态引导、点击反馈类,修复硬编码样式与假数据 删除废弃的后台入口文件与 nginx 配置,清理冗余代码
119 行
4.8 KiB
Vue
119 行
4.8 KiB
Vue
<template>
|
||
<view class="page">
|
||
<!-- 骨架屏呼吸占位 (彻底解决白屏) -->
|
||
<view class="skeleton-box" v-if="loading && !detail">
|
||
<view class="sk-hero yjyf-shimmer"></view>
|
||
<view class="sk-card yjyf-shimmer"></view>
|
||
<view class="sk-card yjyf-shimmer"></view>
|
||
</view>
|
||
|
||
<view class="main-body" v-else-if="detail">
|
||
<view class="hero">
|
||
<view class="hero-thumb"><text class="hero-txt">{{ detail.name.slice(0, 2) }}</text></view>
|
||
<view class="hero-info">
|
||
<text class="name">{{ detail.name }}</text>
|
||
<view class="tags">
|
||
<text class="tag">{{ detail.price_type_text }}</text>
|
||
<text class="tag">{{ detail.cat_name }}</text>
|
||
</view>
|
||
<text class="price">{{ detail.price_text }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card">
|
||
<view class="card-title">费用说明</view>
|
||
<view class="li"><text class="k">基础价</text><text class="v">¥{{ detail.base_price }} / {{ detail.unit }}</text></view>
|
||
<view class="li"><text class="k">楼层费</text><text class="v">{{ detail.floor_fee }} 元/层({{ detail.free_floor }} 层内免)</text></view>
|
||
<view class="li"><text class="k">加急费</text><text class="v">基础价 20%</text></view>
|
||
<view class="li"><text class="k">无电梯</text><text class="v">超免楼层按楼层费计</text></view>
|
||
</view>
|
||
|
||
<view class="card">
|
||
<view class="card-title">服务保障</view>
|
||
<view class="li"><text class="k">平台认证</text><text class="v">实名 + 技能审核</text></view>
|
||
<view class="li"><text class="k">验收付款</text><text class="v">客户验收后平台结算</text></view>
|
||
<view class="li"><text class="k">现场取证</text><text class="v">施工拍照留痕</text></view>
|
||
<text class="desc" v-if="detail.description">{{ detail.description }}</text>
|
||
</view>
|
||
|
||
<view class="bar">
|
||
<view class="bar-left">
|
||
<text class="bar-price">{{ detail.price_text }}</text>
|
||
<text class="bar-tip">无隐藏费用</text>
|
||
</view>
|
||
<view class="bar-btn clickable-hover" @tap="goCreate">立即下单</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import { onLoad } from '@dcloudio/uni-app';
|
||
import api from '@/utils/api.js';
|
||
|
||
const detail = ref(null);
|
||
const loading = ref(true);
|
||
let serviceId = 0;
|
||
|
||
function goCreate() {
|
||
uni.navigateTo({ url: '/pages/order/create?service_id=' + serviceId });
|
||
}
|
||
|
||
onLoad(async (options) => {
|
||
serviceId = Number(options.id || 0);
|
||
const res = await api.serviceDetail(serviceId);
|
||
detail.value = res.detail;
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page { min-height: 100vh; background: #F5F6F8; padding-bottom: 160rpx; }
|
||
.hero {
|
||
display: flex; align-items: center; padding: 34rpx 28rpx;
|
||
background: linear-gradient(160deg, #FFB85C 0%, #F98D25 60%, #ED6F0E 100%);
|
||
}
|
||
.hero-thumb {
|
||
width: 130rpx; height: 130rpx; border-radius: 22rpx; flex-shrink: 0;
|
||
background: rgba(255,255,255,0.92); display: flex; align-items: center; justify-content: center;
|
||
}
|
||
.hero-txt { font-size: 36rpx; font-weight: 700; color: #B85E2A; }
|
||
.hero-info { flex: 1; min-width: 0; margin-left: 22rpx; display: flex; flex-direction: column; }
|
||
.name { font-size: 38rpx; font-weight: 700; color: #fff; letter-spacing: 1rpx; }
|
||
.tags { display: flex; margin-top: 12rpx; }
|
||
.tag {
|
||
padding: 0 14rpx; line-height: 34rpx; margin-right: 10rpx; border-radius: 17rpx;
|
||
background: rgba(255,255,255,0.22); color: #fff; font-size: 20rpx;
|
||
}
|
||
.price { margin-top: 14rpx; font-size: 34rpx; font-weight: 700; color: #FFF3DF; }
|
||
.card {
|
||
margin: 22rpx 28rpx 0; padding: 26rpx 24rpx; background: #fff;
|
||
border-radius: 20rpx; box-shadow: 0 6rpx 20rpx rgba(240,129,31,0.06);
|
||
}
|
||
.card-title { font-size: 30rpx; font-weight: 700; color: #1F1F1F; margin-bottom: 14rpx; }
|
||
.li { display: flex; justify-content: space-between; padding: 12rpx 0; border-bottom: 1rpx solid #F4F4F4; }
|
||
.k { font-size: 26rpx; color: #666; }
|
||
.v { font-size: 26rpx; color: #333; }
|
||
.desc { display: block; margin-top: 16rpx; font-size: 24rpx; color: #9A9A9A; line-height: 36rpx; }
|
||
.bar {
|
||
position: fixed; left: 0; right: 0; bottom: 0; height: 120rpx;
|
||
padding: 0 28rpx; padding-bottom: env(safe-area-inset-bottom);
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
background: #fff; box-shadow: 0 -6rpx 24rpx rgba(0,0,0,0.07);
|
||
}
|
||
.bar-left { display: flex; flex-direction: column; }
|
||
.bar-price { font-size: 34rpx; font-weight: 700; color: #F0710D; }
|
||
.bar-tip { font-size: 20rpx; color: #B5B5B5; }
|
||
.bar-btn {
|
||
height: 84rpx; padding: 0 56rpx; display: flex; align-items: center;
|
||
border-radius: 42rpx; color: #fff; font-size: 30rpx; font-weight: 600;
|
||
background: linear-gradient(135deg, #FFA93F 0%, #F0710D 100%);
|
||
box-shadow: 0 8rpx 20rpx rgba(240,113,13,0.3);
|
||
}
|
||
|
||
.skeleton-box { padding: 30rpx 28rpx; }
|
||
.sk-hero { height: 180rpx; border-radius: 20rpx; margin-bottom: 24rpx; }
|
||
.sk-card { height: 260rpx; border-radius: 20rpx; margin-bottom: 24rpx; }
|
||
|
||
</style>
|