chuangte_bike_newxcx/pages/tuihuang.vue

977 lines
23 KiB
Vue
Raw Normal View History

2026-01-28 18:00:39 +08:00
<template>
<view class="page">
<u-navbar title="推广领取" :border-bottom="false" :background="bgc" back-icon-color="#111827" title-color="#111827"
title-size="34" title-bold height="44" id="navbar">
</u-navbar>
<!-- 顶部卡片 -->
<view class="hero">
<view class="hero-bg">
<view class="b b1"></view>
<view class="b b2"></view>
<view class="b b3"></view>
</view>
<view class="hero-content">
<view class="hero-title">专属福利已解锁</view>
<view class="hero-sub">扫码进入后可领取运营区会员卡并购买</view>
</view>
</view>
<!-- 列表 -->
<view class="list">
<view class="section-title">
<text class="st-main">可购买会员卡</text>
<text class="st-sub" v-if="areaName">运营区{{ areaName }}</text>
</view>
<view class="loading" v-if="loading">
<view class="skeleton" v-for="n in 3" :key="n">
<view class="sk-line w60"></view>
<view class="sk-line w40"></view>
<view class="sk-btn"></view>
</view>
<text class="loading-text">加载中...</text>
</view>
<view v-else>
<view class="vip-card" v-for="item in vipList" :key="item.id">
<view class="vip-head">
<view class="vip-icon">
<u-icon name="level" size="34" color="#2563EB"></u-icon>
</view>
<view class="vip-head-main">
<view class="vip-title-row">
<text class="vip-title">{{ item.name || '会员卡' }}</text>
<view class="tag" v-if="item.discountValue">
<text>{{ item.discountValue }}</text>
</view>
</view>
<view class="vip-desc">{{ formatVipMeta(item) }}</view>
</view>
</view>
<view class="vip-footer">
<view class="vf-left">
<template v-if="!isFree(item)">
<text class="vf-yen">¥</text>
<text class="vf-num">{{ item.price }}</text>
</template>
<template v-else>
<!-- 0元不展示价格 -->
<text class="vf-free">免费</text>
</template>
</view>
<view class="vf-btn" :class="[isFree(item) ? 'btn-free' : 'btn-buy']" @click.stop="openBuy(item)">
<text>{{ isFree(item) ? '领取' : '购买' }}</text>
</view>
</view>
</view>
<u-empty v-if="vipList.length === 0" mode="data" text="暂无可购买的会员卡" margin-top="120"></u-empty>
</view>
</view>
<!-- 购买弹窗 -->
<u-mask :show="showBuy" :z-index="100" />
<view class="sheet" v-if="showBuy">
<view class="sheet-hd">
<text class="sheet-title">{{ isFree(selectedVip) ? '确认领取' : '确认购买' }}</text>
<text class="sheet-close" @click="closeBuy">×</text>
</view>
<view class="sheet-card">
<view class="sc-top">
<view class="sc-name">
<text class="name">{{ selectedVip.name || '会员卡' }}</text>
<text class="badge" v-if="selectedVip.discountValue">{{ selectedVip.discountValue }}</text>
</view>
<view class="sc-price">
<template v-if="isFree(selectedVip)">
<text class="free">免费</text>
</template>
<template v-else>
<text class="yen">¥</text>
<text class="num">{{ selectedVip.price }}</text>
</template>
</view>
</view>
<view class="sc-sub">
{{ formatVipMeta(selectedVip) }}
</view>
<view class="sc-desc" v-if="selectedVip.description">
{{ selectedVip.description }}
</view>
</view>
<view class="sheet-actions">
<view class="btn ghost" @click="closeBuy">取消</view>
<view class="btn primary" @click="payNow">
<text>{{ paying ? '处理中...' : (isFree(selectedVip) ? '立即领取' : '立即支付') }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
bgc: { backgroundColor: "#F7F8FA" },
promotionId: '',
rawQ: '',
loading: false,
detail: {},
vipList: [],
areaId: '',
areaName: '',
channelId: '',
showBuy: false,
selectedVip: {},
2026-03-16 10:03:51 +08:00
paying: false,
redirectVipId: '' // 从登录页带回的 vipId用于自动重新发起购买
2026-01-28 18:00:39 +08:00
}
},
onLoad(option) {
const id = this.extractPromotionId(option || {})
if (!id) {
uni.showToast({ title: '缺少推广参数', icon: 'none' })
return
}
this.promotionId = String(id)
2026-03-16 10:03:51 +08:00
if (option && option.vipId) this.redirectVipId = String(option.vipId)
2026-01-28 18:00:39 +08:00
uni.setStorageSync('areaPromotionId', this.promotionId)
this.getDetail()
},
methods: {
isFree(vip) {
if (!vip) return false
const p = Number(vip.price)
return !isNaN(p) && p <= 0
},
showCounts(item) {
if (!item) return false
return (item.perUserCount || item.perUserCount === 0) || (item.totalCount || item.totalCount === 0)
},
// 从小程序跳转参数里提取推广ID兼容i/id/scene/q
extractPromotionId(option) {
if (option.i || option.id) return option.i || option.id
// scene 兼容:可能是 "i=2" 或直接 "2"
if (option.scene) {
const sc = decodeURIComponent(String(option.scene))
return this.getQueryValue(sc, 'i') || this.getQueryValue(sc, 'id') || sc
}
// 微信扫码进入常见q=encodeURIComponent(完整URL)
if (option.q) {
const decoded = decodeURIComponent(String(option.q))
this.rawQ = decoded
return (
this.getQueryValue(decoded, 'i') ||
this.getQueryValue(decoded, 'id') ||
this.getQueryValue(decoded, 'promotionId') ||
this.getQueryValue(decoded, 'areaPromotionId')
)
}
return ''
},
// 从 URL 或 querystring 中取参数
getQueryValue(urlOrQuery, key) {
if (!urlOrQuery) return ''
let q = String(urlOrQuery)
// 允许传入完整URL
if (q.indexOf('?') > -1) q = q.split('?')[1] || ''
if (q.indexOf('#') > -1) q = q.split('#')[0] || ''
if (!q) return ''
const parts = q.split('&')
for (let i = 0; i < parts.length; i++) {
const kv = parts[i].split('=')
if (!kv.length) continue
const k = decodeURIComponent(kv[0] || '').trim()
if (k !== key) continue
return decodeURIComponent(kv.slice(1).join('=') || '').trim()
}
return ''
},
copyPromotionId() {
if (!this.promotionId) return
uni.setClipboardData({
data: this.promotionId,
success: () => uni.showToast({ title: '已复制', icon: 'success' })
})
},
getDetail() {
if (!this.promotionId) return
this.loading = true
this.$u.get(`/app/areaPromotion/detail?id=${this.promotionId}`).then(res => {
this.loading = false
if (res.code !== 200) {
uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' })
return
}
const data = res.data || {}
this.detail = data
let list = []
if (Array.isArray(data)) list = data
else if (Array.isArray(data.list)) list = data.list
else if (Array.isArray(data.rows)) list = data.rows
else if (Array.isArray(data.vipList)) list = data.vipList
else if (Array.isArray(data.couponList)) list = data.couponList
else if (Array.isArray(data.data)) list = data.data
this.vipList = list || []
// areaId用于获取支付通道如果接口没给就从列表项兜底
this.areaId =
data.areaId ||
data.vipAreaId ||
(this.vipList[0] && (this.vipList[0].areaId || this.vipList[0].vipAreaId)) ||
''
// 运营区名称:用于展示
this.areaName =
data.areaName ||
data.vipAreaName ||
(this.vipList[0] && (this.vipList[0].areaName || this.vipList[0].vipAreaName)) ||
''
if (this.areaId) this.getChannelId()
2026-03-16 10:03:51 +08:00
// 从登录页带回:自动打开该会员卡并重新发起购买
if (this.redirectVipId) {
const vip = this.vipList.find(v => String(v.id) === String(this.redirectVipId))
const vid = this.redirectVipId
this.redirectVipId = ''
if (vip) {
this.openBuy(vip)
setTimeout(() => this.payNow(), 800)
}
}
2026-01-28 18:00:39 +08:00
}).catch(() => {
this.loading = false
uni.showToast({ title: '网络错误', icon: 'none' })
})
},
// 列表中展示的简短描述,避免拥挤
formatVipMeta(item) {
if (!item) return ''
const parts = []
if (item.validDays || item.validDays === 0) parts.push(`有效期${item.validDays}`)
if (item.limitTotal || item.limitTotal === 0) parts.push(`${item.limitTotal}`)
if (item.enableLimit) {
const r = item.limitRound
const c = item.limitCount
if ((r || r === 0) && (c || c === 0)) parts.push(`${r}${c}`)
} else if (item.enableLimit === false) {
parts.push('无频率限制')
}
if (item.perUserCount || item.perUserCount === 0) parts.push(`每人限购${item.perUserCount}`)
// 使用门槛minAmount0/空 => 无门槛
const min = Number(item.minAmount)
if (!isNaN(min) && min > 0) parts.push(`使用门槛¥${item.minAmount}`)
else parts.push('无门槛')
return parts.length ? parts.join(' · ') : '—'
},
getChannelId() {
if (!this.areaId) return
const appId = this.$store && this.$store.state ? this.$store.state.appid : ''
if (!appId) return
this.$u.get(`/app/channel/list?appId=${appId}&areaId=${this.areaId}&bstType=3&appType=1`).then(res => {
if (res.code === 200 && res.data && res.data.length) {
this.channelId = res.data[0].id
}
})
},
openBuy(item) {
this.selectedVip = item || {}
this.showBuy = true
// 弹出时尽量提前准备支付通道
if (!this.channelId && this.areaId) this.getChannelId()
},
closeBuy() {
this.showBuy = false
this.selectedVip = {}
},
2026-03-16 10:03:51 +08:00
// 静默登录(仅用 wx.login 的 code 换 token
silentLogin() {
return new Promise((resolve) => {
// #ifdef MP-WEIXIN
wx.login({
success: (res) => {
if (!res.code) {
resolve(false)
return
}
const data = { loginCode: res.code, appId: this.$store.state.appid }
this.$u.post('/wxLogin', data).then((loginRes) => {
if (loginRes.code === 200) {
uni.setStorageSync('token', loginRes.token)
resolve(true)
}else if(res.code == 401){
resolve(false)
} else {
resolve(false)
}
}).catch(() => resolve(false))
},
fail: () => resolve(false)
})
// #endif
// #ifndef MP-WEIXIN
resolve(false)
// #endif
})
},
// 跳转登录页,登录成功后带参数回到本页重新购买
goLoginWithRedirect() {
const backUrl = '/pages/tuihuang?i=' + encodeURIComponent(this.promotionId) + '&vipId=' + encodeURIComponent(this.selectedVip.id)
uni.navigateTo({
url: '/pages/login/login?redirect=' + encodeURIComponent(backUrl)
})
},
2026-01-28 18:00:39 +08:00
payNow() {
if (this.paying) return
if (!this.selectedVip || !this.selectedVip.id) {
uni.showToast({ title: '缺少会员卡信息', icon: 'none' })
return
}
if (!this.channelId) {
uni.showToast({ title: '支付通道未准备好,请稍后重试', icon: 'none' })
if (this.areaId) this.getChannelId()
return
}
const appId = this.$store && this.$store.state ? this.$store.state.appid : ''
if (!appId) {
uni.showToast({ title: '缺少appId配置', icon: 'none' })
return
}
this.paying = true
uni.showLoading({ title: '加载中...', mask: true })
const vip = this.selectedVip
const data = {
channelId: this.channelId,
appId,
appType: 1,
vipId: vip.id,
vip
}
2026-03-16 10:03:51 +08:00
const doPayResult = (res) => {
2026-01-28 18:00:39 +08:00
if (res.data && res.data.needPay) {
uni.requestPayment({
provider: 'wxpay',
timeStamp: res.data.payParams.timeStamp,
nonceStr: res.data.payParams.nonceStr,
package: res.data.payParams.packageVal,
signType: res.data.payParams.signType,
paySign: res.data.payParams.paySign,
success: () => {
this.$u.put(`/app/pay/refreshPayResult?no=${res.data.pay.no}`).then(() => {
this.paying = false
this.showBuy = false
uni.hideLoading()
uni.showToast({ title: '购买成功', icon: 'success', duration: 2000 })
}).catch(() => {
this.paying = false
uni.hideLoading()
uni.showToast({ title: '支付结果刷新失败', icon: 'none' })
})
},
fail: () => {
this.paying = false
uni.hideLoading()
uni.showToast({ title: '已取消支付', icon: 'none' })
}
})
} else {
this.paying = false
this.showBuy = false
uni.hideLoading()
uni.showToast({ title: '购买成功', icon: 'success', duration: 2000 })
}
2026-03-16 10:03:51 +08:00
}
this.$u.post(`/app/vipOrder`, data).then(res => {
if (res.code === 401) {
this.silentLogin().then((silentOk) => {
if (silentOk) {
// 静默登录成功,用新 token 再请求一次并直接调起支付
this.$u.post(`/app/vipOrder`, data).then(res2 => {
if (res2.code !== 200) {
this.paying = false
uni.hideLoading()
uni.showToast({ title: res2.msg || '下单失败', icon: 'none', duration: 5000 })
return
}
doPayResult(res2)
}).catch(() => {
this.paying = false
uni.hideLoading()
uni.showToast({ title: '网络错误', icon: 'none', duration: 5000 })
})
} else {
this.paying = false
uni.hideLoading()
this.goLoginWithRedirect()
}
})
return
}
if (res.code !== 200) {
this.paying = false
uni.hideLoading()
uni.showToast({ title: res.msg || '下单失败', icon: 'none' ,duration:5000})
return
}
doPayResult(res)
2026-01-28 18:00:39 +08:00
}).catch(() => {
this.paying = false
uni.hideLoading()
uni.showToast({ title: '网络错误', icon: 'none',duration:5000 })
})
}
}
}
</script>
<style lang="scss">
page {
background: #F7F8FA;
min-height: 100vh;
}
.page {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.hero {
margin: 18rpx 24rpx 8rpx;
border-radius: 28rpx;
overflow: hidden;
position: relative;
background: linear-gradient(135deg, #EAF2FF 0%, #F7FBFF 40%, #FFFFFF 100%);
border: 1rpx solid rgba(37, 99, 235, 0.12);
box-shadow: 0 18rpx 40rpx rgba(17, 24, 39, 0.08);
}
.hero-bg {
position: absolute;
inset: 0;
.b {
position: absolute;
border-radius: 50%;
filter: blur(40rpx);
opacity: 0.5;
}
.b1 { width: 360rpx; height: 360rpx; background: #60A5FA; top: -140rpx; right: -120rpx; }
.b2 { width: 280rpx; height: 280rpx; background: #34D399; bottom: -140rpx; left: -120rpx; opacity: 0.35; }
.b3 { width: 240rpx; height: 240rpx; background: #A78BFA; top: 40rpx; left: 120rpx; opacity: 0.22; }
}
.hero-content {
position: relative;
padding: 30rpx 28rpx 26rpx;
}
.hero-title {
font-size: 38rpx;
font-weight: 800;
color: #111827;
letter-spacing: 1rpx;
}
.hero-sub {
margin-top: 10rpx;
font-size: 26rpx;
color: #4B5563;
}
.hero-meta {
margin-top: 18rpx;
display: flex;
gap: 12rpx;
flex-wrap: wrap;
}
.meta-pill {
display: flex;
align-items: center;
gap: 10rpx;
padding: 12rpx 16rpx;
border-radius: 999rpx;
background: rgba(37, 99, 235, 0.08);
border: 1rpx solid rgba(37, 99, 235, 0.12);
.meta-text {
font-size: 24rpx;
color: #1F2937;
}
&.ghost {
background: rgba(255, 255, 255, 0.7);
}
}
.list {
padding: 14rpx 28rpx 90rpx;
}
.section-title {
display: flex;
align-items: baseline;
justify-content: space-between;
margin: 14rpx 6rpx 10rpx;
.st-main {
font-size: 30rpx;
font-weight: 700;
color: #111827;
}
.st-sub {
font-size: 22rpx;
color: #9CA3AF;
}
}
.vip-card {
background: linear-gradient(180deg, #FFFFFF 0%, #FBFDFF 100%);
border-radius: 28rpx;
padding: 32rpx 28rpx 26rpx;
margin-bottom: 22rpx;
box-shadow: 0 18rpx 44rpx rgba(17, 24, 39, 0.08);
border: 1rpx solid rgba(37, 99, 235, 0.08);
overflow: hidden;
position: relative;
&:active { transform: scale(0.99); }
}
.vip-card::after {
content: '';
position: absolute;
right: -120rpx;
top: -120rpx;
width: 240rpx;
height: 240rpx;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(66, 151, 243, 0.18), rgba(66, 151, 243, 0));
}
.vip-head {
display: flex;
gap: 18rpx;
align-items: center;
position: relative;
z-index: 1;
}
.vip-icon {
width: 84rpx;
height: 84rpx;
border-radius: 26rpx;
background: linear-gradient(135deg, rgba(37, 99, 235, 0.14) 0%, rgba(99, 102, 241, 0.12) 100%);
border: 1rpx solid rgba(37, 99, 235, 0.16);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.vip-left { display: none; }
.vip-right { display: none; }
.vip-head-main { flex: 1; min-width: 0; }
.vip-name-row { display: none; }
.vip-title-row {
display: flex;
align-items: center;
gap: 12rpx;
.tag {
padding: 4rpx 12rpx;
border-radius: 999rpx;
background: rgba(245, 158, 11, 0.12);
color: #B45309;
font-size: 22rpx;
font-weight: 600;
border: 1rpx solid rgba(245, 158, 11, 0.18);
}
}
.vip-title {
font-size: 36rpx;
font-weight: 900;
color: #0F172A;
max-width: 520rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
letter-spacing: 1rpx;
}
.vip-desc {
margin-top: 10rpx;
font-size: 26rpx;
color: #64748B;
line-height: 1.6;
word-break: break-all;
}
.vip-footer {
margin-top: 18rpx;
height: 96rpx;
border-radius: 22rpx;
background: #F8FAFC;
border: 1rpx solid rgba(15, 23, 42, 0.08);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 18rpx 0 22rpx;
position: relative;
z-index: 1;
}
.vf-left {
display: flex;
align-items: baseline;
min-width: 220rpx;
}
.vf-yen {
font-size: 24rpx;
font-weight: 900;
color: #EF4444;
margin-right: 6rpx;
}
.vf-num {
font-size: 44rpx;
font-weight: 900;
color: #EF4444;
font-family: 'DIN Alternate', sans-serif;
letter-spacing: 1rpx;
}
.vf-free {
font-size: 30rpx;
font-weight: 900;
color: #16A34A;
background: rgba(22, 163, 74, 0.10);
border: 1rpx solid rgba(22, 163, 74, 0.16);
padding: 10rpx 16rpx;
border-radius: 999rpx;
}
.vf-btn {
height: 72rpx;
min-width: 176rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
padding: 0 26rpx;
font-size: 30rpx;
font-weight: 900;
letter-spacing: 2rpx;
color: #FFFFFF;
box-shadow: 0 12rpx 24rpx rgba(37, 99, 235, 0.25);
}
.btn-buy {
background: linear-gradient(135deg, #3B82F6 0%, #2563EB 55%, #1D4ED8 100%);
}
.btn-free {
background: linear-gradient(135deg, #22C55E 0%, #16A34A 60%, #15803D 100%);
box-shadow: 0 12rpx 24rpx rgba(22, 163, 74, 0.22);
}
.vip-counts { display: none; }
.vip-chips {
margin-top: 16rpx;
display: flex;
gap: 12rpx;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
.count-pill { display: none; }
.chip {
display: flex;
align-items: center;
gap: 8rpx;
padding: 10rpx 14rpx;
border-radius: 999rpx;
background: rgba(2, 132, 199, 0.06);
border: 1rpx solid rgba(2, 132, 199, 0.12);
.chip-label {
font-size: 22rpx;
color: #475569;
}
.chip-val {
font-size: 22rpx;
font-weight: 800;
color: #0F172A;
}
}
.vip-note {
margin-top: 10rpx;
background: #F9FAFB;
border-radius: 14rpx;
padding: 14rpx 14rpx;
.note-label { font-size: 22rpx; color: #9CA3AF; }
.note-text {
font-size: 22rpx;
color: #4B5563;
line-clamp: 2;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.buy-btn { display: none; }
.buy-price { display: none; }
.buy-split { display: none; }
.buy-text { display: none; }
.vip-action {
margin-top: 18rpx;
height: 96rpx;
border-radius: 24rpx;
padding: 0 22rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(135deg, #3B82F6 0%, #2563EB 55%, #1D4ED8 100%);
box-shadow: 0 16rpx 34rpx rgba(37, 99, 235, 0.30);
position: relative;
overflow: hidden;
}
.vip-action::before {
content: '';
position: absolute;
left: -80rpx;
top: -80rpx;
width: 200rpx;
height: 200rpx;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0));
}
.va-left, .va-right {
position: relative;
z-index: 1;
display: flex;
align-items: baseline;
color: #fff;
}
.va-yen {
font-size: 24rpx;
font-weight: 900;
margin-right: 6rpx;
opacity: 0.95;
}
.va-num {
font-size: 42rpx;
font-weight: 900;
font-family: 'DIN Alternate', sans-serif;
letter-spacing: 1rpx;
}
.va-free {
font-size: 34rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.va-right {
gap: 10rpx;
align-items: center;
}
.va-text {
font-size: 30rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.loading {
padding: 10rpx 0 0;
.loading-text {
display: block;
text-align: center;
margin-top: 18rpx;
font-size: 24rpx;
color: #9CA3AF;
}
}
.skeleton {
background: #FFFFFF;
border-radius: 24rpx;
padding: 26rpx 24rpx;
margin-bottom: 18rpx;
box-shadow: 0 10rpx 28rpx rgba(17, 24, 39, 0.04);
border: 1rpx solid rgba(17, 24, 39, 0.06);
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: -40%;
width: 40%;
height: 100%;
background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0) 100%);
animation: sk 1.2s infinite;
}
.sk-line {
height: 26rpx;
border-radius: 999rpx;
background: #F3F4F6;
margin-bottom: 14rpx;
}
.w60 { width: 60%; }
.w40 { width: 40%; }
.sk-btn {
width: 160rpx;
height: 64rpx;
border-radius: 999rpx;
background: #E5E7EB;
margin-top: 10rpx;
}
}
@keyframes sk {
0% { left: -40%; }
100% { left: 120%; }
}
.sheet {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 101;
background: #FFFFFF;
border-radius: 32rpx 32rpx 0 0;
padding: 22rpx 24rpx 24rpx;
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
box-shadow: 0 -14rpx 36rpx rgba(17, 24, 39, 0.18);
}
.sheet-hd {
display: flex;
align-items: center;
justify-content: space-between;
.sheet-title {
font-size: 32rpx;
font-weight: 800;
color: #111827;
}
.sheet-close {
font-size: 52rpx;
color: #9CA3AF;
line-height: 1;
padding: 0 8rpx;
}
}
.sheet-card {
margin-top: 18rpx;
border-radius: 24rpx;
padding: 22rpx 22rpx 20rpx;
background: linear-gradient(180deg, #FFFFFF 0%, #F9FBFF 100%);
border: 1rpx solid rgba(66, 151, 243, 0.12);
.sc-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16rpx;
}
.sc-name {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
gap: 12rpx;
.name {
font-size: 32rpx;
font-weight: 800;
color: #111827;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.badge {
font-size: 22rpx;
color: #D97706;
background: rgba(217, 119, 6, 0.12);
padding: 4rpx 12rpx;
border-radius: 999rpx;
border: 1rpx solid rgba(217, 119, 6, 0.16);
}
}
.sc-price {
display: flex;
align-items: baseline;
color: #EF4444;
.yen { font-size: 22rpx; font-weight: 800; margin-right: 4rpx; }
.num { font-size: 44rpx; font-weight: 900; font-family: 'DIN Alternate', sans-serif; }
}
.sc-sub {
margin-top: 12rpx;
font-size: 26rpx;
color: #64748B;
line-height: 1.7;
word-break: break-all;
}
.sc-desc {
margin-top: 14rpx;
font-size: 24rpx;
color: #4B5563;
background: #F9FAFB;
border-radius: 16rpx;
padding: 14rpx 14rpx;
line-height: 1.6;
}
}
.sheet-actions {
margin-top: 18rpx;
display: flex;
gap: 16rpx;
.btn {
flex: 1;
height: 92rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
font-weight: 800;
}
.ghost {
background: #FFFFFF;
color: #374151;
border: 2rpx solid #E5E7EB;
}
.primary {
background: linear-gradient(135deg, #4297F3 0%, #2B76E5 100%);
color: #FFFFFF;
box-shadow: 0 10rpx 22rpx rgba(66, 151, 243, 0.28);
}
}
</style>