620 lines
21 KiB
Vue
620 lines
21 KiB
Vue
<template>
|
||
<view class="ticket-page">
|
||
<!-- 顶部返回按钮 -->
|
||
<view class="top-nav-left-icon pf" style="top: 44px;" v-show="!seatSelectorVisible">
|
||
<view class="icon back-icon round cp" @click="goBack">‹</view>
|
||
</view>
|
||
|
||
<!-- Header Banner - 使用头部组件 -->
|
||
<ticket-header
|
||
:poster-url="goodsData.images"
|
||
:title="goodsData.title"
|
||
:tag="goodsData.simple_desc"
|
||
:collected="collected"
|
||
:peer-goods="peerGoods"
|
||
:current-goods-id="goodsId"
|
||
:current-goods-timestamp="goodsData.batch_number_expire || 0"
|
||
:current-goods-title="goodsData.title"
|
||
:session-meta="sessionMeta"
|
||
@collect="toggleCollect"
|
||
@switch-date="handleSwitchDate"
|
||
/>
|
||
|
||
<!-- Main Content -->
|
||
<view class="main-content">
|
||
<!-- Location Card - 使用场馆卡片组件 -->
|
||
<venue-card
|
||
:venue="currentVenue"
|
||
:is-nearest="true"
|
||
@nav="openAmap"
|
||
@click="openPopup('venue')"
|
||
/>
|
||
|
||
<!-- Service Header -->
|
||
<view class="service-header" @click="openPopup('service')">
|
||
<view class="service-tags-wrapper">
|
||
<view class="service-tags">
|
||
<view class="tag-item" v-for="(item, index) in services" :key="index">
|
||
<text :class="item.status === 'success' ? 'icon-success' : 'icon-warning'">
|
||
{{ item.status === 'success' ? '✔' : '!' }}
|
||
</text>
|
||
{{ item.title }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="service-more">></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Section: 演出详情 -->
|
||
<view class="section-card mt-10">
|
||
<view class="section-header">
|
||
<text class="section-title">演出详情</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Section: 购票须知 -->
|
||
<view class="section-card mt-10">
|
||
<view class="section-header">
|
||
<text class="section-title">购票须知</text>
|
||
<view class="section-more" @click="openPopup('notice', 'buy')">全部 ></view>
|
||
</view>
|
||
<view class="notice-grid">
|
||
<view class="notice-item" v-for="(item, index) in buyNotices.slice(0, 4)" :key="index">
|
||
<view class="notice-title"><view class="dot"></view>{{ item.title }}</view>
|
||
<view class="notice-desc">{{ item.desc }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Section: 观演须知 -->
|
||
<view class="section-card mt-10" :style="{ marginBottom: '90px' }">
|
||
<view class="section-header">
|
||
<text class="section-title">观演须知</text>
|
||
<view class="section-more" @click="openPopup('notice', 'watch')">全部 ></view>
|
||
</view>
|
||
<view class="notice-grid">
|
||
<view class="notice-item" v-for="(item, index) in watchNotices.slice(0, 4)" :key="index">
|
||
<view class="notice-title"><view class="dot"></view>{{ item.title }}</view>
|
||
<view class="notice-desc">{{ item.desc }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Bottom Bar -->
|
||
<view class="bottom-bar">
|
||
<!-- 没选座时显示客服 -->
|
||
<view class="customer-service" v-if="confirmedSeats.length === 0">
|
||
<iconfont name="icon-customer-service" size="50rpx" color="#666"></iconfont>
|
||
<text>客服</text>
|
||
</view>
|
||
|
||
<!-- 选完座显示合计 -->
|
||
<view class="selected-info" v-else>
|
||
<view class="info-top" @click="seatSelectorVisible = true">已选 {{ confirmedSeats.length }} 座 <text class="reselect-text">重新选座 ></text></view>
|
||
<view class="info-bottom">合计 <text class="price-text">¥{{ totalConfirmedPrice }}</text></view>
|
||
</view>
|
||
|
||
<!-- 可购票时显示正常按钮 -->
|
||
<button
|
||
class="buy-button"
|
||
v-if="hasAvailableSession"
|
||
@click="confirmedSeats.length === 0 ? openTicketPopup() : goToOrder()">
|
||
{{ confirmedSeats.length === 0 ? '立即购票' : '提交订单' }}
|
||
</button>
|
||
<!-- 无可用场次时显示停售按钮 -->
|
||
<button class="buy-button buy-button-disabled" v-else>停止售票</button>
|
||
</view>
|
||
|
||
<!-- Popup Container - 使用弹窗组件 -->
|
||
<ticket-popup
|
||
:visible="popupVisible"
|
||
:type="popupType"
|
||
:services="services"
|
||
:venues="venues"
|
||
:buy-notices="buyNotices"
|
||
:watch-notices="watchNotices"
|
||
:default-tab="noticeActiveTab"
|
||
:tree-data="treeData"
|
||
:seat-templates="seatTemplates"
|
||
:session-meta="sessionMeta"
|
||
@close="closePopup"
|
||
@nav="openAmap"
|
||
@confirm-buy="handleConfirmBuy"
|
||
/>
|
||
|
||
<!-- Seat Selector (全屏遮罩选座) -->
|
||
<vr-seat-selector
|
||
:visible="seatSelectorVisible"
|
||
:selection="currentSelection"
|
||
:tree-data="treeData"
|
||
:seat-templates="seatTemplates"
|
||
@close="seatSelectorVisible = false"
|
||
@confirm="handleConfirmSeats"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 引入子组件
|
||
import TicketHeader from './components/ticket-header/index.vue';
|
||
import VenueCard from './components/venue-card/index.vue';
|
||
import TicketPopup from './components/ticket-popup/index.vue';
|
||
import VrSeatSelector from './components/vr-seat-selector/index.vue';
|
||
import iconfont from '@/components/iconfont/iconfont.vue';
|
||
|
||
export default {
|
||
name: 'GoodsVrTicket',
|
||
// 注册子组件
|
||
components: {
|
||
TicketHeader,
|
||
VenueCard,
|
||
TicketPopup,
|
||
VrSeatSelector,
|
||
iconfont
|
||
},
|
||
data() {
|
||
return {
|
||
// App 实例
|
||
app: null,
|
||
// 商品ID
|
||
goodsId: null,
|
||
// 商品数据
|
||
goodsData: {},
|
||
// VR 配置数据
|
||
vrConfig: [],
|
||
// 场馆列表
|
||
venues: [],
|
||
// 当前选中的场馆
|
||
currentVenue: {},
|
||
// 收藏状态
|
||
collected: false,
|
||
// 弹窗状态
|
||
popupVisible: false,
|
||
popupType: 'service',
|
||
noticeActiveTab: 'buy',
|
||
// 当前选中的4维规格
|
||
currentSelection: null,
|
||
// 是否显示座位选择器
|
||
seatSelectorVisible: false,
|
||
// 已确认选中的座位
|
||
confirmedSeats: [],
|
||
// Tree API 数据
|
||
treeData: null,
|
||
seatTemplates: null,
|
||
// 同场次关联商品(用于多日期切换)
|
||
peerGoods: [],
|
||
// 场次元数据(用于停售控制)
|
||
sessionMeta: [],
|
||
// 服务说明数据 (可从 API 获取)
|
||
defaultServices: [
|
||
{ title: '电子票', desc: '现场验票时,请观演人出示APP票夹中的电子票二维码验票入场,不支持截屏。', status: 'success' },
|
||
{ title: '不支持转赠', desc: '本场演出实行实名制购票观演政策,下单时需填写观演人信息。购票完成后,观演人信息不可更改,需携带购票时填写的证件验证入场观演。', status: 'warning' },
|
||
{ title: '不支持退换票', desc: '票品为有价票券,非普通商品,其背后承载的文化服务具有时效性,稀缺性等特征,不支持退换。', status: 'warning' }
|
||
],
|
||
// 购票须知默认值
|
||
defaultBuyNotices: [
|
||
{ title: '实名购票', desc: '本场演出实行实名制购票观演政策,一证一票。' },
|
||
{ title: '演出时长以现场为准', desc: '受现场不可控因素影响,实际演出时长以现场为准。' },
|
||
{ title: '定时购说明', desc: '使用定时购服务后,将在开票时为您自动购票,不保证购票成功,也不影响开票后的正常购票,使用需扣减票价等额90%的秀豆积分。' },
|
||
{ title: '候补说明', desc: '使用候补服务后,将按照订单出票时间先后顺序候补,不保证一定成功,候补服务费一般为订单费用的5.5%。' }
|
||
],
|
||
// 观演须知默认值
|
||
defaultWatchNotices: [
|
||
{ title: '站席观演', desc: '场地内不设座位,均为站席观演。' },
|
||
{ title: '禁止携带物品', desc: '由于安保和版权的原因,大多数演出、展览及比赛场所禁止携带食品、饮料、专业摄录设备、打火机等物品。' },
|
||
{ title: '退换政策', desc: '票品为有价票券,非普通商品,其背后承载的文化服务具有时效性,稀缺性等特征,不支持退换。' }
|
||
]
|
||
};
|
||
},
|
||
computed: {
|
||
// 根据当前选中的 tab 动态返回对应的须知列表
|
||
currentNoticeList() {
|
||
return this.noticeActiveTab === 'buy' ? this.buyNotices : this.watchNotices;
|
||
},
|
||
// 购票须知:优先使用场馆配置,为空时回退默认值
|
||
buyNotices() {
|
||
const venueNotices = this.currentVenue?.notices?.buy;
|
||
if (venueNotices && venueNotices.length > 0) {
|
||
return venueNotices;
|
||
}
|
||
return this.defaultBuyNotices;
|
||
},
|
||
// 观演须知:优先使用场馆配置,为空时回退默认值
|
||
watchNotices() {
|
||
const venueNotices = this.currentVenue?.notices?.watch;
|
||
if (venueNotices && venueNotices.length > 0) {
|
||
return venueNotices;
|
||
}
|
||
return this.defaultWatchNotices;
|
||
},
|
||
// 服务说明:优先使用场馆配置,为空时回退默认值
|
||
services() {
|
||
const venueServices = this.currentVenue?.notices?.service;
|
||
if (venueServices && venueServices.length > 0) {
|
||
return venueServices;
|
||
}
|
||
return this.defaultServices;
|
||
},
|
||
totalConfirmedPrice() {
|
||
return this.confirmedSeats.reduce((sum, seat) => sum + Number(seat.price || 0), 0).toFixed(2);
|
||
},
|
||
// 是否有可用场次(未被停售)
|
||
hasAvailableSession() {
|
||
const now = Math.floor(Date.now() / 1000);
|
||
// 如果 sessionMeta 为空,视为有可用场次(保持向后兼容)
|
||
if (!this.sessionMeta || this.sessionMeta.length === 0) {
|
||
return true;
|
||
}
|
||
// 只要有一个场次的 batch_expire_ts > now,就认为有可用场次
|
||
return this.sessionMeta.some(session => session.batch_expire_ts > now);
|
||
}
|
||
},
|
||
methods: {
|
||
// 返回上一页
|
||
goBack() {
|
||
const pages = getCurrentPages();
|
||
if (pages.length > 1) {
|
||
// 有历史页面,返回上一页
|
||
uni.navigateBack();
|
||
} else {
|
||
// 无历史页面,返回首页
|
||
uni.reLaunch({
|
||
url: '/pages/index/index'
|
||
});
|
||
}
|
||
},
|
||
// 切换日期(点击 peer_goods 中的商品)
|
||
handleSwitchDate(goodsId) {
|
||
console.log('[VR Ticket] Switch to goods:', goodsId);
|
||
// 重新加载目标商品页面
|
||
uni.redirectTo({
|
||
url: `/pages/goods-vr-ticket/goods-vr-ticket?id=${goodsId}`
|
||
});
|
||
},
|
||
|
||
onLoad(params) {
|
||
// 获取 app 实例
|
||
const app = getApp();
|
||
this.app = app;
|
||
|
||
// 获取页面参数
|
||
if (params && params.id) {
|
||
this.goodsId = params.id;
|
||
}
|
||
|
||
// 尝试从缓存获取商品数据
|
||
var goods = app.globalData.goods_data_cache_handle(this.goodsId);
|
||
if (goods != null) {
|
||
console.log('[VR Ticket] goods from cache:', goods);
|
||
this.handleGoodsData(goods);
|
||
this.loadTreeData();
|
||
} else {
|
||
// 请求 API 获取商品详情
|
||
this.loadGoodsDetail();
|
||
}
|
||
},
|
||
|
||
// 加载商品详情
|
||
loadGoodsDetail() {
|
||
var self = this;
|
||
uni.request({
|
||
url: this.app.globalData.get_request_url('detail', 'goods'),
|
||
method: 'POST',
|
||
data: { id: this.goodsId },
|
||
dataType: 'json',
|
||
success: function(res) {
|
||
if (res.data.code == 0) {
|
||
var goods = res.data.data.goods;
|
||
console.log('[VR Ticket] goods from API:', goods);
|
||
self.handleGoodsData(goods);
|
||
// 更新商品缓存,确保 user_is_favor 等字段同步
|
||
self.app.globalData.goods_data_cache_handle(goods.id, goods);
|
||
self.loadTreeData();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理商品数据
|
||
handleGoodsData(goods) {
|
||
// 存储商品数据
|
||
this.goodsData = goods;
|
||
|
||
// 初始化收藏状态(从商品数据获取)
|
||
this.collected = (goods.user_is_favor == 1);
|
||
|
||
// 解析 vr_goods_config
|
||
var vrConfig = goods.vr_goods_config;
|
||
if (typeof vrConfig === 'string') {
|
||
try {
|
||
vrConfig = JSON.parse(vrConfig);
|
||
} catch (e) {
|
||
vrConfig = [];
|
||
}
|
||
}
|
||
this.vrConfig = vrConfig;
|
||
|
||
console.log('[VR Ticket] vrConfig parsed:', vrConfig);
|
||
},
|
||
|
||
// 加载 VR Ticket 层级树数据
|
||
loadTreeData() {
|
||
var self = this;
|
||
uni.request({
|
||
url: this.app.globalData.get_request_url('tree', 'goods', 'vr_ticket', 'goods_id=' + this.goodsId + '&group_by=session,venue,room,section'),
|
||
method: 'GET',
|
||
dataType: 'json',
|
||
success: function(res) {
|
||
if (res.data.code == 0) {
|
||
console.log('[VR Ticket] Tree API success:', res.data.data);
|
||
self.handleTreeData(res.data.data);
|
||
} else {
|
||
uni.showToast({ title: res.data.msg || '获取票务数据失败', icon: 'none' });
|
||
}
|
||
},
|
||
fail: function() {
|
||
uni.showToast({ title: '网络错误,获取票务数据失败', icon: 'none' });
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理 Tree API 数据
|
||
handleTreeData(apiData) {
|
||
this.treeData = apiData.tree;
|
||
this.seatTemplates = apiData.seat_templates;
|
||
|
||
// 提取会话元数据和同场次商品
|
||
this.sessionMeta = apiData.session_meta || [];
|
||
this.peerGoods = apiData.peer_goods || [];
|
||
|
||
// 提取场馆数据
|
||
const venuesMap = {};
|
||
Object.values(apiData.seat_templates || {}).forEach(template => {
|
||
if (template.seat_map && template.seat_map.venue) {
|
||
venuesMap[template.seat_map.venue.name] = template.seat_map.venue;
|
||
}
|
||
});
|
||
this.venues = Object.values(venuesMap);
|
||
if (this.venues.length > 0) {
|
||
this.findClosestVenue();
|
||
}
|
||
},
|
||
|
||
// 找出距离最近的场馆
|
||
findClosestVenue() {
|
||
// 模拟当前用户坐标 (例如: 北京市朝阳区)
|
||
// 在实际项目中,可以使用 uni.getLocation 获取真实经纬度
|
||
const currentLoc = { lat: 39.9042, lng: 116.4074 };
|
||
|
||
let closest = this.venues[0];
|
||
let minDistance = Infinity;
|
||
|
||
this.venues.forEach(venue => {
|
||
if (venue.location && venue.location.lat && venue.location.lng) {
|
||
const lat = parseFloat(venue.location.lat);
|
||
const lng = parseFloat(venue.location.lng);
|
||
|
||
// 使用 Haversine 公式计算距离
|
||
const distance = this.calculateDistance(currentLoc.lat, currentLoc.lng, lat, lng);
|
||
|
||
if (distance < minDistance) {
|
||
minDistance = distance;
|
||
closest = venue;
|
||
}
|
||
}
|
||
});
|
||
|
||
this.currentVenue = closest || this.venues[0];
|
||
},
|
||
|
||
// Haversine 距离计算 (单位:米)
|
||
calculateDistance(lat1, lng1, lat2, lng2) {
|
||
const radLat1 = lat1 * Math.PI / 180.0;
|
||
const radLat2 = lat2 * Math.PI / 180.0;
|
||
const a = radLat1 - radLat2;
|
||
const b = (lng1 - lng2) * Math.PI / 180.0;
|
||
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
|
||
return s * 6378137.0;
|
||
},
|
||
|
||
// 导航到高德地图
|
||
openAmap(venue) {
|
||
const { location, name } = venue;
|
||
if (!location || !location.lat || !location.lng) {
|
||
uni.showToast({
|
||
title: '该场馆暂无位置信息',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
// 使用 uni.openLocation 打开地图导航
|
||
uni.openLocation({
|
||
latitude: parseFloat(location.lat),
|
||
longitude: parseFloat(location.lng),
|
||
name: name,
|
||
address: venue.address,
|
||
fail: () => {
|
||
// 如果失败,使用高德 URL Scheme
|
||
const url = `https://uri.amap.com/marker?position=${location.lng},${location.lat}&name=${encodeURIComponent(name)}`;
|
||
// #ifdef H5
|
||
window.open(url, '_blank');
|
||
// #endif
|
||
// #ifndef H5
|
||
plus.runtime.openWeb(url);
|
||
// #endif
|
||
}
|
||
});
|
||
},
|
||
|
||
// 切换收藏状态
|
||
toggleCollect() {
|
||
var self = this;
|
||
// 登录检查
|
||
var user = this.app.globalData.get_user_info(this, 'toggleCollect');
|
||
if (user == false) {
|
||
return;
|
||
}
|
||
|
||
uni.showLoading({
|
||
title: '处理中...',
|
||
});
|
||
|
||
uni.request({
|
||
url: this.app.globalData.get_request_url('favor', 'goods'),
|
||
method: 'POST',
|
||
data: {
|
||
id: this.goodsId,
|
||
},
|
||
dataType: 'json',
|
||
success: function(res) {
|
||
uni.hideLoading();
|
||
if (res.data.code == 0) {
|
||
// 更新收藏状态
|
||
self.collected = res.data.data.status == 1;
|
||
// 更新商品缓存,确保数据一致性
|
||
var goods = self.app.globalData.goods_data_cache_handle(self.goodsId);
|
||
if (goods) {
|
||
goods.user_is_favor = res.data.data.status;
|
||
self.app.globalData.goods_data_cache_handle(self.goodsId, goods);
|
||
}
|
||
self.app.globalData.showToast(res.data.msg, 'success');
|
||
} else {
|
||
if (self.app.globalData.is_login_check(res.data, self, 'toggleCollect')) {
|
||
self.app.globalData.showToast(res.data.msg);
|
||
}
|
||
}
|
||
},
|
||
fail: function() {
|
||
uni.hideLoading();
|
||
self.app.globalData.showToast('网络错误,请重试');
|
||
}
|
||
});
|
||
},
|
||
|
||
// 打开购票弹窗
|
||
openTicketPopup() {
|
||
this.popupType = 'buy';
|
||
this.popupVisible = true;
|
||
},
|
||
|
||
// 打开弹窗:type 区分是服务还是须知,tab 区分须知的默认选中项
|
||
openPopup(type, tab = 'buy') {
|
||
this.popupType = type;
|
||
if (type === 'notice') {
|
||
this.noticeActiveTab = tab;
|
||
}
|
||
this.popupVisible = true;
|
||
},
|
||
|
||
// 关闭弹窗
|
||
closePopup() {
|
||
this.popupVisible = false;
|
||
},
|
||
|
||
// 确认购买/进入选座
|
||
handleConfirmBuy(selection) {
|
||
console.log('[VR Ticket] Confirm buy with selection:', selection);
|
||
this.currentSelection = selection;
|
||
this.closePopup();
|
||
// 显示座位选择器
|
||
this.seatSelectorVisible = true;
|
||
},
|
||
|
||
// 确认选座(选座完毕直接跳转到订单页)
|
||
handleConfirmSeats(seats) {
|
||
console.log('[VR Ticket] Confirmed seats:', seats);
|
||
this.confirmedSeats = seats;
|
||
this.seatSelectorVisible = false;
|
||
// 直接跳转到订单确认页
|
||
this.goToOrder();
|
||
},
|
||
|
||
// 跳转到订单提交页
|
||
goToOrder() {
|
||
// 验证登录状态
|
||
var user = this.app.globalData.get_user_info(this, 'goToOrder');
|
||
if (user == false) {
|
||
return;
|
||
}
|
||
|
||
// 验证已选座位
|
||
if (this.confirmedSeats.length === 0) {
|
||
this.app.globalData.showToast('请先选择座位');
|
||
return;
|
||
}
|
||
|
||
// 获取当前选中 session 的 session_datetime
|
||
const sessionItem = this.sessionMeta.find(m => m.session === this.currentSelection?.session);
|
||
const sessionDatetime = sessionItem?.session_datetime || '';
|
||
|
||
// 获取海报URL(支持 images 为字符串或数组两种格式)
|
||
let posterImg = '';
|
||
if (typeof this.goodsData.images === 'string') {
|
||
posterImg = this.goodsData.images || '';
|
||
} else if (Array.isArray(this.goodsData.images) && this.goodsData.images.length > 0) {
|
||
posterImg = this.goodsData.images[0] || '';
|
||
}
|
||
|
||
// 组装跳转参数
|
||
const params = {
|
||
goodsId: this.goodsId,
|
||
seats: encodeURIComponent(JSON.stringify(this.confirmedSeats)),
|
||
selection: encodeURIComponent(JSON.stringify(this.currentSelection)),
|
||
buyNotices: encodeURIComponent(JSON.stringify(this.buyNotices)),
|
||
services: encodeURIComponent(JSON.stringify(this.services)),
|
||
batchExpire: this.goodsData.batch_number_expire || '',
|
||
sessionDatetime: sessionDatetime,
|
||
posterUrl: posterImg,
|
||
venueAddress: this.currentVenue?.address || ''
|
||
};
|
||
|
||
// 拼接 URL 参数
|
||
let url = '/pages/goods-vr-ticket-order/goods-vr-ticket-order?';
|
||
Object.keys(params).forEach(key => {
|
||
url += `${key}=${params[key]}&`;
|
||
});
|
||
|
||
console.log('[VR Ticket] Navigate to order confirm:', url);
|
||
|
||
uni.navigateTo({
|
||
url: url.slice(0, -1) // 移除末尾的 &
|
||
});
|
||
}
|
||
},
|
||
// 下拉刷新
|
||
onPullDownRefresh() {
|
||
console.log('[VR Ticket] Pull down to refresh');
|
||
var self = this;
|
||
// 重新加载商品详情和树数据
|
||
uni.request({
|
||
url: this.app.globalData.get_request_url('detail', 'goods'),
|
||
method: 'POST',
|
||
data: { id: this.goodsId },
|
||
dataType: 'json',
|
||
success: function(res) {
|
||
if (res.data.code == 0) {
|
||
var goods = res.data.data.goods;
|
||
console.log('[VR Ticket] Refresh goods from API:', goods);
|
||
self.handleGoodsData(goods);
|
||
// 重新加载 Tree 数据(刷新座位库存)
|
||
self.loadTreeData();
|
||
} else {
|
||
uni.showToast({ title: res.data.msg || '刷新失败', icon: 'none' });
|
||
}
|
||
// 停止下拉刷新动画
|
||
uni.stopPullDownRefresh();
|
||
},
|
||
fail: function() {
|
||
uni.showToast({ title: '网络错误,刷新失败', icon: 'none' });
|
||
uni.stopPullDownRefresh();
|
||
}
|
||
});
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
@import "./goods-vr-ticket.css";
|
||
</style>
|