feat: 抖音核销接入小泥人API、公告详情页、景区预约人选人等多处优化
抖音核销(douyin-verify): - 对接小泥人 OAuth2 token 与 voucher-list 券码接口,按 code 分组展示订单券号及可激活次数 - 支持手动输入券码验证券码;核销流程改为先调后端 sendAndBind 发卡绑定,再验证券码 - 购卡须知改为富文本渲染,新增无可核销订单提示弹窗、客服电话拨打 - 增加 product_id 过滤与 per-page=100,验证券码命中后仅展示不弹提示 公告模块: - 首页/公告列表点击跳转到新增公告详情页(notice-detail),展示标题、发布时间、富文本内容及视频 - 公告列表卡片改为标题+发布时间+查看详情按钮,时间与按钮同排 - 富文本支持内嵌 video 解析播放;content 为空不显示分隔线;标题取公告 title 而非类型 首页与价格: - 首页大卡价格/划线价/立省改为商品详情接口动态获取 - 热门景区列表扩展至8条,支持优惠/折扣标签与默认占位图 预约/景区: - 预约页改为选择预约人(可选/已预约置灰),预约参数改用预约人信息 - 景区列表与详情支持优惠类型、折扣、特惠价展示,富文本图片防溢出 - 景区列表移除自定义导航改用原生导航 订单/绑卡: - 订单列表/详情增加抖音核销单号(thirdOrderNo)展示,核销订单隐藏金额与退款入口 - 绑卡页移除人脸可选开关,照片必传改为拍照 - 其他:图片域名统一为 hn12301,tabBar'旅游卡'改名,登录页支持 redirect 回跳
This commit is contained in:
@@ -15,22 +15,43 @@
|
||||
<text class="price-label">价格:</text>
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ buyPrice }}</text>
|
||||
<text class="price-original">¥3800+</text>
|
||||
<text v-if="showPrice" class="price-original">¥{{ showPrice }}</text>
|
||||
</view>
|
||||
<view class="btn-buy" :class="{ 'btn-buy--disabled': paying }" @tap="handleBuy">
|
||||
<text class="btn-buy__text">{{ paying ? '支付中...' : '立即购买' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 购卡须知弹窗:内容为富文本 -->
|
||||
<view v-if="showNotice" class="modal-mask" @tap="showNotice = false">
|
||||
<view class="modal-panel" @tap.stop>
|
||||
<text class="modal-title">购卡须知</text>
|
||||
<scroll-view scroll-y class="modal-scroll">
|
||||
<rich-text class="modal-html" :nodes="noticeHtml" />
|
||||
</scroll-view>
|
||||
<view class="modal-divider" />
|
||||
<view class="modal-footer">
|
||||
<view class="modal-foot-btn" @tap="showNotice = false">
|
||||
<text class="modal-foot-text">取消</text>
|
||||
</view>
|
||||
<view class="modal-foot-split" />
|
||||
<view class="modal-foot-btn" @tap="confirmBuy">
|
||||
<text class="modal-foot-text modal-foot-text--red">确认购买</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onLoad, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import { post, getToken } from '../../../api/request'
|
||||
import { BASE_URL } from '../../../config'
|
||||
import { useUserStore } from '../../../store/user'
|
||||
import { createOrder, prepay } from '../../../api/order'
|
||||
import type { BannerItem } from '../../../api/types'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -39,10 +60,42 @@ const cardModleId = ref('')
|
||||
const mainPic = ref('')
|
||||
const introduction = ref('')
|
||||
const buyPrice = ref('0')
|
||||
const showPrice = ref('')
|
||||
const skuId = ref('')
|
||||
const loading = ref(true)
|
||||
const paying = ref(false)
|
||||
const shareTitle = ref('爱上萍乡旅游年卡')
|
||||
const showNotice = ref(false)
|
||||
const noticeContent = ref('')
|
||||
|
||||
/** 富文本 HTML 清洗:给所有 img 加 max-width:100%,防止超出容器边界 */
|
||||
function fixRichHtml(html: string): string {
|
||||
if (!html) return ''
|
||||
const style = 'max-width:100%;height:auto;display:block;'
|
||||
return html.replace(/<img(?![^>]*max-width)[^>]*>/gi, (tag: string) => {
|
||||
if (/style="[^"]*"/i.test(tag)) {
|
||||
return tag.replace(/style="([^"]*)"/i, (_: string, s: string) => `style="${s};${style}"`)
|
||||
}
|
||||
return tag.replace(/<img/i, `<img style="${style}"`)
|
||||
})
|
||||
}
|
||||
|
||||
const noticeHtml = computed(() => fixRichHtml(noticeContent.value))
|
||||
|
||||
/** 获取购卡须知富文本(type = CARD_PURCHASE_NOTICE) */
|
||||
async function fetchPurchaseNotice() {
|
||||
try {
|
||||
const res = await post<BannerItem[]>('/mp-api/mpContent/queryList', {
|
||||
channel: 'pingxiang',
|
||||
type: 'CARD_PURCHASE_NOTICE',
|
||||
})
|
||||
const list = res || []
|
||||
const item = list.find((i) => i.type === 'CARD_PURCHASE_NOTICE') || list[0]
|
||||
noticeContent.value = item?.content || ''
|
||||
} catch {
|
||||
noticeContent.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((query) => {
|
||||
goodsId.value = (query?.goodsId as string) || '821926819630853'
|
||||
@@ -59,7 +112,7 @@ interface CardModelDetail {
|
||||
name: string
|
||||
goods: { id: string; mainPic: string }
|
||||
goodsInfo: { introduction: string }
|
||||
sku: Array<{ id: string; buyPrice: number }>
|
||||
sku: Array<{ id: string; buyPrice: number; showPrice: number }>
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -73,6 +126,8 @@ onMounted(async () => {
|
||||
skuId.value = sku?.id || ''
|
||||
const price = sku?.buyPrice || 0
|
||||
buyPrice.value = (price / 100).toFixed(2)
|
||||
const showPriceValue = sku?.showPrice || 0
|
||||
showPrice.value = showPriceValue ? (showPriceValue / 100).toFixed(2) : ''
|
||||
} catch {
|
||||
// 静默失败
|
||||
} finally {
|
||||
@@ -97,8 +152,15 @@ async function handleBuy() {
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
return
|
||||
}
|
||||
if (paying.value) return
|
||||
showNotice.value = true
|
||||
fetchPurchaseNotice()
|
||||
}
|
||||
|
||||
async function confirmBuy() {
|
||||
if (paying.value) return
|
||||
paying.value = true
|
||||
showNotice.value = false
|
||||
|
||||
try {
|
||||
const mobile = (uni.getStorageSync('phone') as string) || ''
|
||||
@@ -276,4 +338,71 @@ $bar-height: 120rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 购卡须知弹窗 ===== */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: $z-modal;
|
||||
background: rgba(120, 120, 120, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-panel {
|
||||
width: 662rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.modal-title {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-family: $font-family-display;
|
||||
font-size: $font-size-lg;
|
||||
font-weight: $font-weight-bold;
|
||||
color: $uv-main-color;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.modal-scroll {
|
||||
height: 700rpx;
|
||||
}
|
||||
.modal-html {
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-size: $font-size-sm;
|
||||
color: $uv-content-color;
|
||||
line-height: 1.7;
|
||||
word-break: break-all;
|
||||
}
|
||||
.modal-divider {
|
||||
height: 1rpx;
|
||||
background: $uv-border-color;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.modal-foot-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-foot-split {
|
||||
width: 1rpx;
|
||||
height: 48rpx;
|
||||
background: $uv-border-color;
|
||||
}
|
||||
.modal-foot-text {
|
||||
font-size: $font-size-lg;
|
||||
color: $uv-main-color;
|
||||
&--red {
|
||||
color: #ff383c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user