1. 特色方功能迭代

This commit is contained in:
李琦
2026-07-03 16:48:46 +08:00
parent cef264c692
commit fb0df465a1
7 changed files with 642 additions and 245 deletions

View File

@@ -0,0 +1,9 @@
---
description:
alwaysApply: true
---
1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
2. 注意不要生成太多的空行上一部分代码和下一部分代码中间的空行不要大于2行
3. 有封装好的方法、组件需要复用,不要重复造轮子
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面

1
.gitignore vendored
View File

@@ -118,3 +118,4 @@ dist
/.idea/ /.idea/
/database/ /database/
/utils/utils.js /utils/utils.js
/.cursor/skills/

View File

@@ -14,7 +14,7 @@
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues"> <option name="myValues">
<value> <value>
<list size="16"> <list size="17">
<item index="0" class="java.lang.String" itemvalue="nobr" /> <item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" /> <item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" /> <item index="2" class="java.lang.String" itemvalue="comment" />
@@ -31,6 +31,7 @@
<item index="13" class="java.lang.String" itemvalue="u-form-item" /> <item index="13" class="java.lang.String" itemvalue="u-form-item" />
<item index="14" class="java.lang.String" itemvalue="u-checkbox-group" /> <item index="14" class="java.lang.String" itemvalue="u-checkbox-group" />
<item index="15" class="java.lang.String" itemvalue="u-checkbox" /> <item index="15" class="java.lang.String" itemvalue="u-checkbox" />
<item index="16" class="java.lang.String" itemvalue="u-parse" />
</list> </list>
</value> </value>
</option> </option>

View File

@@ -49,13 +49,25 @@ export async function createSpecialPrescriptionPatientRecordApi(params) {
return await post('/special-prescription/create-patient-record', params, 3) return await post('/special-prescription/create-patient-record', params, 3)
} }
/**
* 保存特色方挂号上下文merge 已有值,避免后续页面未传 sku_id 时覆盖为 0
* @param ctx {{ special_prescription_id, dose_count?, sku_id?, doctor_id? }}
*/
export function saveSpecialPrescriptionRegisterContext(ctx) { export function saveSpecialPrescriptionRegisterContext(ctx) {
if (!ctx || !ctx.special_prescription_id) return if (!ctx || !ctx.special_prescription_id) return
uni.setStorageSync(SPECIAL_PRESCRIPTION_STORAGE_KEY, { const prev = getSpecialPrescriptionRegisterContext() || {}
const merged = {
special_prescription_id: ctx.special_prescription_id, special_prescription_id: ctx.special_prescription_id,
dose_count: ctx.dose_count || 1, dose_count: ctx.dose_count != null ? ctx.dose_count : (prev.dose_count || 1),
doctor_id: ctx.doctor_id || '', doctor_id: ctx.doctor_id != null ? ctx.doctor_id : (prev.doctor_id || ''),
}) }
// 未传 sku_id 字段时保留 storage 已有值,防止中间页覆盖为 0
if (Object.prototype.hasOwnProperty.call(ctx, 'sku_id')) {
merged.sku_id = Number(ctx.sku_id) || 0
} else {
merged.sku_id = Number(prev.sku_id) || 0
}
uni.setStorageSync(SPECIAL_PRESCRIPTION_STORAGE_KEY, merged)
} }
export function getSpecialPrescriptionRegisterContext() { export function getSpecialPrescriptionRegisterContext() {
@@ -80,6 +92,7 @@ export async function tryCreateSpecialPrescriptionPatientRecord(registerId) {
register_id: registerId, register_id: registerId,
special_prescription_id: ctx.special_prescription_id, special_prescription_id: ctx.special_prescription_id,
dose_count: ctx.dose_count || 1, dose_count: ctx.dose_count || 1,
sku_id: ctx.sku_id || 0,
store_id: uni.getStorageSync('store_id') || '11001', store_id: uni.getStorageSync('store_id') || '11001',
doctor_id: ctx.doctor_id, doctor_id: ctx.doctor_id,
}) })

View File

@@ -191,11 +191,16 @@ export default {
} }
this.delegateStoreId = e.delegate_store_id || ''; this.delegateStoreId = e.delegate_store_id || '';
if (e.special_prescription_id) { if (e.special_prescription_id) {
saveSpecialPrescriptionRegisterContext({ const spCtx = {
special_prescription_id: e.special_prescription_id, special_prescription_id: e.special_prescription_id,
dose_count: e.special_prescription_dose_count || 1, dose_count: e.special_prescription_dose_count || 1,
doctor_id: e.id || '', doctor_id: e.id || '',
}); };
// 仅 URL 带参时才写入 sku_id避免 0 覆盖 detail 页已保存的 SKU
if (e.special_prescription_sku_id != null && e.special_prescription_sku_id !== '') {
spCtx.sku_id = Number(e.special_prescription_sku_id);
}
saveSpecialPrescriptionRegisterContext(spCtx);
} }
this.setNavTitle(STEP1_TITLE); this.setNavTitle(STEP1_TITLE);
}, },

View File

@@ -32,11 +32,15 @@
onLoad(e) { onLoad(e) {
this.register_id = e.id this.register_id = e.id
if (e.special_prescription_id) { if (e.special_prescription_id) {
saveSpecialPrescriptionRegisterContext({ const spCtx = {
special_prescription_id: e.special_prescription_id, special_prescription_id: e.special_prescription_id,
dose_count: e.special_prescription_dose_count || 1, dose_count: e.special_prescription_dose_count || 1,
doctor_id: e.doctor_id || '', doctor_id: e.doctor_id || '',
}) }
if (e.special_prescription_sku_id != null && e.special_prescription_sku_id !== '') {
spCtx.sku_id = Number(e.special_prescription_sku_id)
}
saveSpecialPrescriptionRegisterContext(spCtx)
} }
this.bindSpecialPrescriptionRecord() this.bindSpecialPrescriptionRecord()
}, },

View File

@@ -3,19 +3,19 @@
<MessageNotification /> <MessageNotification />
<view v-if="loading" class="loading-wrap"> <view v-if="loading" class="loading-wrap">
<u-loading mode="circle" size="48"></u-loading> <u-loading mode="circle" size="48" color="#4175EE"></u-loading>
</view> </view>
<block v-else-if="detail.id"> <block v-else-if="detail.id">
<!-- 顶部轮播 --> <!-- STREAMING_CHUNK:渲染顶部轮播 -->
<view class="swiper-section"> <view class="swiper-section">
<swiper <swiper
class="swiper" class="swiper"
indicator-dots indicator-dots
autoplay autoplay
circular circular
indicator-color="rgba(0,0,0,0.1)" indicator-color="rgba(255,255,255,0.6)"
indicator-active-color="#4175EE" indicator-active-color="#ffffff"
> >
<swiper-item v-for="(img, index) in displayImages" :key="index"> <swiper-item v-for="(img, index) in displayImages" :key="index">
<image <image
@@ -28,9 +28,13 @@
</swiper> </swiper>
</view> </view>
<!-- 基本信息 --> <!-- STREAMING_CHUNK:渲染上浮卡片式基本信息 -->
<view class="info-container-price">
<view class="basic-info"> <view class="basic-info">
<view class="price-row"> <!-- 价格区渐变背景装饰 -->
<view class="price-panel">
<view class="price-bg-decor"></view>
<view class="price-content">
<view class="price-left" v-if="detail.price_available !== false"> <view class="price-left" v-if="detail.price_available !== false">
<text class="symbol">¥</text> <text class="symbol">¥</text>
<text class="amount">{{ detail.price_per_dose }}</text> <text class="amount">{{ detail.price_per_dose }}</text>
@@ -39,19 +43,35 @@
<view class="price-left price-left--unavailable" v-else> <view class="price-left price-left--unavailable" v-else>
<text class="amount">暂无报价</text> <text class="amount">暂无报价</text>
</view> </view>
<text class="sales" v-if="detail.sales_count">已售 {{ detail.sales_count }}</text> <view class="sales-box" v-if="detail.sales_count">
<u-icon name="shopping-cart" size="24" color="#FF8D1A"></u-icon>
<text class="sales-text">已售 {{ detail.sales_count }}</text>
</view>
</view>
</view> </view>
<view class="title">{{ detail.name }}</view> <!-- 标题与标签区 -->
<view class="title-panel">
<view class="title">
<text class="title-text">{{ detail.name }}</text>
</view>
<view class="tag-row" v-if="detail.tags && detail.tags.length"> <view class="tag-row" v-if="detail.tags && detail.tags.length">
<text class="tag" v-for="(tag, idx) in detail.tags" :key="idx">{{ tag }}</text> <view class="tag-item" v-for="(tag, idx) in detail.tags" :key="idx">
<text class="tag-text">{{ tag }}</text>
</view> </view>
</view> </view>
</view>
</view>
</view>
<view class="info-container">
<!-- 图文介绍 --> <!-- STREAMING_CHUNK:渲染图文介绍 -->
<view class="intro-section" v-if="detail.intro_text || (detail.introduction_images && detail.introduction_images.length)"> <view class="intro-section" v-if="detail.intro_text || (detail.introduction_images && detail.introduction_images.length)">
<view class="section-title">详情介绍</view> <view class="section-title">
<view class="title-decor-line"></view>
<text class="title-text">详情介绍</text>
</view>
<view class="intro-text" v-if="detail.intro_text"> <view class="intro-text" v-if="detail.intro_text">
<u-parse :html="detail.intro_text"></u-parse> <u-parse :html="detail.intro_text"></u-parse>
</view> </view>
@@ -66,16 +86,19 @@
/> />
</view> </view>
</view> </view>
</view>
<!-- 底部操作 --> <!-- STREAMING_CHUNK:渲染适度抬高的悬浮操作按钮 -->
<view class="footer-spacer"></view> <view class="footer-spacer"></view>
<view class="footer safe-area-inset-bottom"> <view class="floating-action-bar safe-area-inset-bottom">
<button <button
class="register-btn" class="register-btn-float"
:class="{ 'register-btn--disabled': detail.price_available === false }" :class="{ 'register-btn-float--disabled': detail.price_available === false }"
:disabled="detail.price_available === false" :disabled="detail.price_available === false"
@click="goRegister" @click="goRegister"
>去挂号选方</button> >
<text>去挂号选方</text>
</button>
</view> </view>
</block> </block>
@@ -84,45 +107,98 @@
</view> </view>
<!-- #ifdef MP-WEIXIN --> <!-- #ifdef MP-WEIXIN -->
<page-container :show="registerDrawerShow" :overlay="false" @leave="onPageBackGuardLeave" /> <page-container :v-if="registerDrawerShow" :overlay="false" @leave="onPageBackGuardLeave" />
<!-- #endif --> <!-- #endif -->
<!-- STREAMING_CHUNK:渲染包含自定义项的SKU抽屉 -->
<u-popup <u-popup
v-model="registerDrawerShow" v-model="registerDrawerShow"
mode="bottom" mode="bottom"
border-radius="24" border-radius="32"
safe-area-inset-bottom safe-area-inset-bottom
closeable
close-icon-color="#999"
@close="onRegisterDrawerClose" @close="onRegisterDrawerClose"
> >
<view class="register-drawer"> <view class="register-drawer">
<view class="drawer-header"> <view class="drawer-header">
<text class="drawer-title">确认贴数</text> <image class="header-img" :src="displayImages[0]" mode="aspectFill"></image>
<view class="drawer-close" @tap="closeRegisterDrawer"> <view class="header-info">
<u-icon name="close" size="32" color="#999"></u-icon> <view class="header-price">
<text class="symbol">¥</text>
<text class="amount">{{ unitPrice }}</text>
<text class="unit">/</text>
</view>
<text class="header-stock">
已选{{ selectedSkuId === 0 ? '自定义贴数' : (selectedSku ? selectedSku.sku_name : '') }} {{ doseCount }}
</text>
</view> </view>
</view> </view>
<view class="drawer-dose-row"> <scroll-view scroll-y class="drawer-scroll-area">
<text class="dose-label">贴数</text> <view class="drawer-section">
<text class="section-label">规格选择</text>
<view class="sku-list">
<!-- STREAMING_CHUNK:自定义贴数 SKU 胶囊 (带动态价格) -->
<view
class="sku-item"
:class="{ 'sku-item--active': Number(selectedSkuId) === 0 }"
@tap="selectCustomSku"
>
<text class="sku-name">自定义贴数</text>
<text class="sku-price" v-if="detail.price_available !== false && customSkuDisplayPrice > 0">
¥{{ customSkuDisplayPrice }}
</text>
<view class="active-triangle" v-if="Number(selectedSkuId) === 0">
<u-icon name="checkbox-mark" size="16" color="#fff" class="triangle-icon"></u-icon>
</view>
</view>
<!-- 真实的 SKU 列表 -->
<view
v-for="sku in detail.skus"
:key="sku.id"
class="sku-item"
:class="{ 'sku-item--active': Number(selectedSkuId) === Number(sku.id) }"
@tap="selectSku(sku)"
>
<text class="sku-name">{{ sku.sku_name }}</text>
<text class="sku-price" v-if="sku.sale_price > 0">¥{{ sku.sale_price }}</text>
<view class="active-triangle" v-if="Number(selectedSkuId) === Number(sku.id)">
<u-icon name="checkbox-mark" size="16" color="#fff" class="triangle-icon"></u-icon>
</view>
</view>
</view>
</view>
<!-- 步进器部分 -->
<view class="drawer-section dose-section">
<text class="section-label">贴数</text>
<u-number-box <u-number-box
v-model="doseCount" v-model="doseCount"
:min="1" :min="1"
:max="99" :max="99"
integer integer
:input-width="90"
:input-height="56"
bg-color="#F5F7FA"
@change="onDoseCountChange"
></u-number-box> ></u-number-box>
</view> </view>
</scroll-view>
<view class="drawer-price-row"> <view class="drawer-footer">
<text class="price-label">单价</text> <view class="total-info">
<text class="price-value">¥{{ detail.price_per_dose }}/</text>
</view>
<view class="drawer-total-row">
<text class="total-label">预估金额</text> <text class="total-label">预估金额</text>
<text class="total-price">¥{{ totalPrice }}</text> <view class="total-price-box">
<text class="total-symbol">¥</text>
<text class="total-price">{{ totalPrice }}</text>
</view>
</view>
<button class="confirm-btn" @click="confirmRegister">确认挂号</button>
</view> </view>
<button class="confirm-btn" @click="confirmRegister">确认并挂号</button>
</view> </view>
</u-popup> </u-popup>
@@ -131,6 +207,7 @@
</template> </template>
<script> <script>
/* STREAMING_CHUNK:引入依赖文件 */
import { import {
getSpecialPrescriptionDetailApi, getSpecialPrescriptionDetailApi,
saveSpecialPrescriptionRegisterContext, saveSpecialPrescriptionRegisterContext,
@@ -143,12 +220,14 @@ export default {
id: '', id: '',
detail: {}, detail: {},
doseCount: 1, doseCount: 1,
selectedSkuId: 0,
loading: true, loading: true,
registerDrawerShow: false, registerDrawerShow: false,
defaultCover: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png', defaultCover: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png',
} }
}, },
computed: { computed: {
/* STREAMING_CHUNK:计算属性 */
displayImages() { displayImages() {
const images = [] const images = []
if (this.detail.cover_image) { if (this.detail.cover_image) {
@@ -163,16 +242,42 @@ export default {
} }
return images.length ? images : [this.defaultCover] return images.length ? images : [this.defaultCover]
}, },
hasSkus() {
return Array.isArray(this.detail.skus) && this.detail.skus.length > 0
},
selectedSku() {
if (!this.hasSkus || Number(this.selectedSkuId) === 0) return null
return this.detail.skus.find((item) => Number(item.id) === Number(this.selectedSkuId)) || null
},
unitPrice() {
return parseFloat(this.detail.price_per_dose || 0).toFixed(2)
},
// 为自定义贴数单独提供展示价格
customSkuDisplayPrice() {
return parseFloat(this.detail.price_per_dose || 0).toFixed(2)
},
totalPrice() { totalPrice() {
if (this.detail.price_available === false) { if (this.detail.price_available === false) {
return '0.00' return '0.00'
} }
if (this.selectedSku && Number(this.selectedSku.sale_price) > 0) {
return parseFloat(this.selectedSku.sale_price || 0).toFixed(2)
}
return calculateSpecialPrescriptionEstimatedTotal( return calculateSpecialPrescriptionEstimatedTotal(
this.detail.price_per_dose, this.detail.price_per_dose,
this.doseCount, this.doseCount,
) )
}, },
}, },
watch: {
/* STREAMING_CHUNK:监听器处理 */
doseCount(newVal) {
if (this._suppressDoseSkuClear) {
return
}
this.selectedSkuId = 0
},
},
onLoad(e) { onLoad(e) {
this.id = e.id || '' this.id = e.id || ''
if (this.id) { if (this.id) {
@@ -182,6 +287,7 @@ export default {
} }
}, },
methods: { methods: {
/* STREAMING_CHUNK:核心方法与生命周期 */
getStoreId() { getStoreId() {
return uni.getStorageSync('store_id') || '11001' return uni.getStorageSync('store_id') || '11001'
}, },
@@ -194,6 +300,7 @@ export default {
}) })
if (res.data?.code === 0 || res.data?.code === '0') { if (res.data?.code === 0 || res.data?.code === '0') {
this.detail = res.data.result || {} this.detail = res.data.result || {}
this.initSkuSelection()
} else { } else {
this.$refs.uToast && this.$refs.uToast.show({ this.$refs.uToast && this.$refs.uToast.show({
title: res.data?.message || '加载失败', title: res.data?.message || '加载失败',
@@ -207,6 +314,46 @@ export default {
this.loading = false this.loading = false
} }
}, },
initSkuSelection() {
const skus = this.detail.skus || []
if (skus.length > 0 && this.detail.default_sku_id) {
const defaultId = Number(this.detail.default_sku_id || 0)
const defaultSku = skus.find((item) => Number(item.is_default) === 1)
|| skus.find((item) => Number(item.id) === defaultId)
if (defaultSku) {
this._suppressDoseSkuClear = true
this.selectedSkuId = Number(defaultSku.id)
this.doseCount = defaultSku.dose_count || 1
this.$nextTick(() => {
this._suppressDoseSkuClear = false
})
return
}
}
this.selectedSkuId = 0
this.doseCount = 1
},
selectCustomSku() {
this.selectedSkuId = 0
},
selectSku(sku) {
this._suppressDoseSkuClear = true
if (Number(this.selectedSkuId) === Number(sku.id)) {
this.selectedSkuId = 0
} else {
this.selectedSkuId = Number(sku.id)
this.doseCount = sku.dose_count || 1
}
this.$nextTick(() => {
this._suppressDoseSkuClear = false
})
},
onDoseCountChange(e) {
if (this._suppressDoseSkuClear) return
this.selectedSkuId = 0
},
previewImage(index) { previewImage(index) {
uni.previewImage({ uni.previewImage({
urls: this.displayImages, urls: this.displayImages,
@@ -268,9 +415,14 @@ export default {
return return
} }
const skuId = this.selectedSku
? Number(this.selectedSku.id)
: 0
const ctx = { const ctx = {
special_prescription_id: this.detail.id, special_prescription_id: this.detail.id,
dose_count: this.doseCount, dose_count: this.doseCount,
sku_id: skuId,
doctor_id: doctorId, doctor_id: doctorId,
} }
saveSpecialPrescriptionRegisterContext(ctx) saveSpecialPrescriptionRegisterContext(ctx)
@@ -278,23 +430,38 @@ export default {
this._suppressPageBackGuardLeave = true this._suppressPageBackGuardLeave = true
this.registerDrawerShow = false this.registerDrawerShow = false
uni.navigateTo({ let url = `/subPackages/doctor/doctor-userinfo?id=${doctorId}&r_type=0&special_prescription_id=${ctx.special_prescription_id}&special_prescription_dose_count=${ctx.dose_count}`
url: `/subPackages/doctor/doctor-userinfo?id=${doctorId}&r_type=0&special_prescription_id=${ctx.special_prescription_id}&special_prescription_dose_count=${ctx.dose_count}`, if (ctx.sku_id) {
}) url += `&special_prescription_sku_id=${ctx.sku_id}`
}
uni.navigateTo({ url })
}, },
}, },
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
/* STREAMING_CHUNK:定义全局样式与主题变量 */
$sp-primary: #4175EE; $sp-primary: #4175EE;
$sp-primary-light: rgba(65, 117, 238, 0.1); $sp-primary-light: #EBF1FF;
$sp-gradient: linear-gradient(90deg, #4175EE 0%, #298DFF 100%); $sp-price: #FF4F23;
$sp-price-bg: linear-gradient(135deg, #FFF1EB 0%, #FFFAFA 100%);
$sp-text-main: #222222;
$sp-text-sub: #666666;
$sp-text-muted: #999999;
$sp-bg: #F4F5F7;
$sp-gradient-btn: linear-gradient(90deg, #5C8CFF 0%, #3567E8 100%);
view, text, scroll-view {
box-sizing: border-box;
}
.container { .container {
min-height: 100vh; min-height: 100vh;
background: #F5F7FA; background: $sp-bg;
padding-bottom: 140rpx; /* 调整底部留白,适应稍微降低的悬浮按钮 */
padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
//padding-bottom: 50rpx;
} }
.loading-wrap, .loading-wrap,
@@ -302,241 +469,438 @@ $sp-gradient: linear-gradient(90deg, #4175EE 0%, #298DFF 100%);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
min-height: 60vh; min-height: 70vh;
} }
/* 轮播图样式 */
.swiper-section { .swiper-section {
.swiper { .swiper {
height: 750rpx; height: 750rpx;
width: 100%; width: 100%;
background-color: #f8f8f8;
} }
.swiper-image { .swiper-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.basic-info { /* 详情页面装饰增强 */
background: #fff; .info-container-price {
padding: 32rpx; position: relative;
margin-bottom: 16rpx; margin-top: -32rpx;
z-index: 10;
padding: 0 24rpx;
}
/* 详情页面装饰增强 */
.info-container {
position: relative;
margin-top: -32rpx;
z-index: 10;
}
.price-row { .basic-info {
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
margin-bottom: 24rpx;
.price-panel {
position: relative;
padding: 32rpx 24rpx;
background: $sp-price-bg;
.price-bg-decor {
position: absolute;
right: 0;
top: 0;
width: 200rpx;
height: 100%;
background: radial-gradient(circle at 100% 0%, rgba(255, 79, 35, 0.08) 0%, transparent 70%);
pointer-events: none;
}
.price-content {
position: relative;
display: flex; display: flex;
align-items: baseline; align-items: flex-end;
justify-content: space-between; justify-content: space-between;
margin-bottom: 16rpx; z-index: 2;
} }
.price-left { .price-left {
color: #FF5722; color: $sp-price;
display: flex;
align-items: baseline;
.symbol { .symbol {
font-size: 28rpx; font-size: 32rpx;
font-weight: 600; font-weight: 700;
margin-right: 4rpx;
} }
.amount { .amount {
font-size: 48rpx; font-size: 56rpx;
font-weight: 800; font-weight: 800;
font-family: 'DIN Alternate', sans-serif;
line-height: 1;
} }
.unit { .unit {
font-size: 24rpx; font-size: 24rpx;
color: #999; margin-left: 8rpx;
margin-left: 4rpx;
} }
&--unavailable .amount { &--unavailable .amount {
font-size: 32rpx; font-size: 36rpx;
color: #999; color: $sp-text-muted;
}
}
.sales-box {
display: flex;
align-items: center;
background: rgba(255, 141, 26, 0.1);
padding: 6rpx 16rpx;
border-radius: 24rpx;
.sales-text {
font-size: 22rpx;
color: #E67A15;
margin-left: 6rpx;
font-weight: 500; font-weight: 500;
} }
} }
}
.sales { .title-panel {
font-size: 24rpx; padding: 24rpx;
color: #999;
} }
.title { .title {
font-size: 36rpx; font-size: 34rpx;
font-weight: 700; font-weight: 700;
color: #333; color: $sp-text-main;
line-height: 1.4; line-height: 1.5;
margin-bottom: 16rpx; margin-bottom: 16rpx;
} }
.tag-row { .tag-row {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16rpx;
.tag { .tag-item {
font-size: 22rpx; position: relative;
color: $sp-primary; background: #FAFAFA;
background: $sp-primary-light; border: 1rpx solid #EBEBEB;
padding: 4rpx 16rpx; padding: 6rpx 16rpx;
border-radius: 8rpx; border-radius: 8rpx;
margin-right: 12rpx;
margin-bottom: 8rpx; .tag-text {
font-size: 22rpx;
color: $sp-text-sub;
}
} }
} }
} }
.intro-section { .intro-section {
background: #fff; background: #ffffff;
padding: 32rpx; border-radius: 24rpx;
margin-bottom: 16rpx; padding: 32rpx 24rpx;
} box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
.section-title { .section-title {
font-size: 30rpx; display: flex;
align-items: center;
justify-content: center;
margin-bottom: 32rpx;
position: relative;
.title-decor-line {
position: absolute;
bottom: 4rpx;
width: 120rpx;
height: 12rpx;
background: linear-gradient(90deg, rgba(65, 117, 238, 0.3) 0%, transparent 100%);
border-radius: 6rpx;
z-index: 1;
}
.title-text {
font-size: 32rpx;
font-weight: 700; font-weight: 700;
color: #333; color: $sp-text-main;
margin-bottom: 24rpx; z-index: 2;
padding-left: 16rpx; }
border-left: 6rpx solid $sp-primary;
} }
.intro-images { .intro-images {
font-size: 0; font-size: 0;
line-height: 0; line-height: 0;
border-radius: 12rpx;
overflow: hidden;
.intro-img { .intro-img {
width: 100%; width: 100%;
display: block; display: block;
margin-bottom: 0;
vertical-align: top;
} }
} }
.intro-text { .intro-text {
margin-bottom: 24rpx;
font-size: 28rpx; font-size: 28rpx;
color: #666; color: $sp-text-sub;
line-height: 1.6; line-height: 1.7;
margin-bottom: 20rpx;
}
} }
/* STREAMING_CHUNK:底部适度抬高悬浮操作按钮 */
.footer-spacer { .footer-spacer {
height: 120rpx; height: 20rpx;
} }
.footer { .floating-action-bar {
position: fixed; position: fixed;
left: 0; left: 32rpx;
right: 0; right: 32rpx;
bottom: 0; /* 调整悬浮高度:降低到 16rpx更加稳重 */
background: #fff; //bottom: calc(16rpx + env(safe-area-inset-bottom));
padding: 16rpx 32rpx; bottom: 50rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
z-index: 100; z-index: 100;
display: flex;
justify-content: center;
.register-btn { .register-btn-float {
width: 100%; width: 100%;
height: 88rpx; height: 96rpx;
line-height: 88rpx; background: $sp-gradient-btn;
background: $sp-gradient;
color: #fff; color: #fff;
font-size: 32rpx; font-size: 32rpx;
font-weight: 600; font-weight: 600;
border-radius: 44rpx; border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
border: none; border: none;
box-shadow: 0 12rpx 24rpx rgba(65, 117, 238, 0.3);
transition: all 0.3s;
&::after { &::after { border: none; }
border: none;
&:active {
transform: scale(0.98);
box-shadow: 0 6rpx 12rpx rgba(65, 117, 238, 0.2);
} }
&--disabled { &--disabled {
background: #e0e0e0; background: #E2E4E8;
color: #999; color: #B0B5C1;
box-shadow: none;
} }
} }
} }
/* 抽屉与包含自定义的精简版SKU样式 */
.register-drawer { .register-drawer {
padding: 32rpx; width: 100%;
background: #fff; background: #fff;
border-radius: 32rpx 32rpx 0 0;
display: flex;
flex-direction: column;
max-height: 75vh;
.drawer-header { .drawer-header {
width: 100%;
padding: 32rpx;
border-bottom: 1rpx solid #F2F3F5;
display: flex; display: flex;
align-items: center; position: relative;
justify-content: space-between; padding-right: 80rpx;
margin-bottom: 32rpx;
.header-img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background: $sp-bg;
margin-right: 24rpx;
border: 1rpx solid #EBEBEB;
} }
.drawer-title { .header-info {
font-size: 32rpx; flex: 1;
font-weight: 700;
color: #333;
}
.drawer-close {
padding: 8rpx;
}
.drawer-dose-row {
display: flex; display: flex;
align-items: center; flex-direction: column;
justify-content: space-between; justify-content: flex-end;
margin-bottom: 24rpx; padding-bottom: 12rpx;
.dose-label { .header-price {
color: $sp-price;
display: flex;
align-items: baseline;
margin-bottom: 12rpx;
.symbol { font-size: 28rpx; font-weight: 600; }
.amount { font-size: 48rpx; font-weight: 800; font-family: 'DIN Alternate', sans-serif;}
.unit { font-size: 24rpx; color: $sp-text-muted; margin-left: 4rpx;}
}
.header-stock {
font-size: 24rpx;
color: $sp-text-sub;
background: #F7F7F7;
padding: 4rpx 12rpx;
border-radius: 8rpx;
display: inline-block;
align-self: flex-start;
}
}
}
.drawer-scroll-area {
width: 100%;
max-height: 50vh;
padding: 0 32rpx 40rpx;
}
.drawer-section {
margin-top: 32rpx;
.section-label {
font-size: 28rpx; font-size: 28rpx;
color: #666; font-weight: 600;
color: $sp-text-main;
margin-bottom: 20rpx;
display: block;
} }
} }
.drawer-price-row { .sku-list {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
.sku-item {
position: relative;
display: inline-flex;
align-items: center;
background: #F5F6F8;
border: 2rpx solid transparent;
padding: 14rpx 28rpx;
border-radius: 40rpx;
transition: all 0.2s;
overflow: hidden;
.sku-name {
font-size: 26rpx;
color: $sp-text-main;
}
.sku-name + .sku-price {
margin-left: 8rpx;
}
.sku-price {
font-size: 24rpx;
color: $sp-text-sub;
font-weight: 500;
}
&--active {
background: $sp-primary-light;
border-color: $sp-primary;
.sku-name {
color: $sp-primary;
font-weight: 600;
}
.sku-price {
color: $sp-primary;
}
}
.active-triangle {
position: absolute;
right: -2rpx;
bottom: -2rpx;
width: 0;
height: 0;
border-bottom: 32rpx solid $sp-primary;
border-left: 32rpx solid transparent;
.triangle-icon {
position: absolute;
right: 2rpx;
bottom: -28rpx;
transform: scale(0.6);
}
}
}
.dose-section {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 16rpx; padding-top: 16rpx;
.price-label { .section-label {
font-size: 28rpx; margin-bottom: 0;
color: #666;
}
.price-value {
font-size: 28rpx;
color: #333;
} }
} }
.drawer-total-row { .drawer-footer {
width: 100%;
padding: 20rpx 32rpx;
background: #fff;
border-top: 1rpx solid #F2F3F5;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 24rpx 0 32rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #f0f0f0;
margin-bottom: 16rpx; .total-info {
display: flex;
flex-direction: column;
.total-label { .total-label {
font-size: 24rpx;
color: $sp-text-sub;
}
.total-price-box {
display: flex;
align-items: baseline;
color: $sp-price;
}
.total-symbol {
font-size: 28rpx; font-size: 28rpx;
color: #666; font-weight: 600;
} }
.total-price { .total-price {
font-size: 40rpx; font-size: 44rpx;
font-weight: 800; font-weight: 800;
color: #FF5722; font-family: 'DIN Alternate', sans-serif;
} }
} }
.confirm-btn { .confirm-btn {
width: 100%; margin: 0;
height: 88rpx; width: 280rpx;
line-height: 88rpx; height: 80rpx;
background: $sp-gradient; line-height: 80rpx;
background: $sp-gradient-btn;
color: #fff; color: #fff;
font-size: 32rpx; font-size: 30rpx;
font-weight: 600; font-weight: 600;
border-radius: 44rpx; border-radius: 40rpx;
border: none; border: none;
&::after { &::after { border: none; }
border: none;
} }
} }
} }