index.vue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. <template>
  2. <zzx-navbar :scrollable="true" :back="true" title="详情"></zzx-navbar>
  3. <view class="detail-header">
  4. <image class="header-bg" :src="bannerList[0]" mode="">
  5. </image>
  6. <view class="back-icon" :style="{ paddingTop: (statusBarHeight + 10) + 'px' }" @click="RouterUtils.back()">
  7. <zzx-icon name="back"></zzx-icon>
  8. </view>
  9. <view :style="{ height: (statusBarHeight + 70) + 'px' }"></view>
  10. <scroll-view class="header-swiper content" scroll-x="true" :show-scrollbar="false">
  11. <view class="swiper-inner">
  12. <block v-if="detailInfo.video">
  13. <video v-for="(item, index) in videoList" :key="index" :src="item"></video>
  14. </block>
  15. <image v-for="(item, index) in bannerList" :key="index" :src="item" mode=""></image>
  16. </view>
  17. </scroll-view>
  18. </view>
  19. <view :style="{ height: (statusBarHeight + 34) + 'px' }"></view>
  20. <view class="header-info">
  21. <view class="venue-name">
  22. <view>
  23. <view class="name">{{ detailInfo.name }}</view>
  24. <view class="open-status-info">
  25. <view class="open-status">{{ detailInfo.runStatus ? '营业中' : '歇业中' }}</view>
  26. <view class="open-time-info">{{ detailInfo?.startTime }}-{{ detailInfo?.endTime }}</view>
  27. </view>
  28. </view>
  29. <view class="star">
  30. <uni-rate :readonly="true" :allowHalf="true" size="16" :value="detailInfo.goodRate" />
  31. <text>{{ detailInfo.goodRate || '0.0' }}</text>
  32. </view>
  33. </view>
  34. <view class="open-time" v-if="teachingDay">
  35. <view class="time">
  36. <view>教学日 <text style="margin-right: 14rpx;" v-for="(item, index) in teachingDay.data" :key="index">{{
  37. item.startTime + '-' + item.endTime || '--' }}</text></view>
  38. <view class="">非教学日 <text style="margin-right: 14rpx;" v-for="(item, index) in noTeachingDay.data"
  39. :key="index">{{ item.startTime + '-' + item.endTime || '--' }}</text></view>
  40. </view>
  41. </view>
  42. <view class="venues-tags">
  43. <view class="tags-box">
  44. <view class="tags" v-for="(item, index) in detailInfo.facilityInfo" :key="index">{{ item }}</view>
  45. </view>
  46. <!-- <view class="more-info" @click="RouterUtils.to_page('/pages/index/basisInfo/index')">
  47. <text>信息/设备设施</text>
  48. <zzx-icon name="ashRight" size="10"></zzx-icon>
  49. </view> -->
  50. </view>
  51. <view class="venues-address">
  52. <view class="address">
  53. <zzx-icon name="location" size="14"></zzx-icon>
  54. <text>{{ detailInfo.address }}</text>
  55. </view>
  56. <view class="nav-phone">
  57. <view class="nav" @click="open_map">
  58. <zzx-icon name="navigation" size="14"></zzx-icon>
  59. <view class="">导航</view>
  60. </view>
  61. <view class="phone" @click="open_phone">
  62. <zzx-icon name="phone" size="14"></zzx-icon>
  63. <view class="">电话</view>
  64. </view>
  65. </view>
  66. </view>
  67. </view>
  68. <view class="content">
  69. <uv-sticky offset-top="74">
  70. <view class="detail-select">
  71. <view :class="sel_index === index ? 'select-text' : 'notsel-text'" v-for="(item, index) in selectList"
  72. :key="index" @click="sel_tab(index)">
  73. <text>{{ item }}</text>
  74. </view>
  75. </view>
  76. </uv-sticky>
  77. <view id="detail">
  78. <view class="venue-select-card">
  79. <view class="v-caed-header">
  80. <view class="v-left">
  81. <zzx-icon name="venue-icon1" size="14"></zzx-icon>
  82. <view style="margin-bottom: 10rpx;">包场</view>
  83. </view>
  84. <view class="" v-if="charteredList">
  85. 开场前{{ charteredList.earlyRefundTime }}分钟随时退
  86. </view>
  87. <view class="v-left" @click="checkedVr">
  88. <zzx-icon name="venue-icon2" size="14"></zzx-icon>
  89. <view style="margin-bottom: 10rpx;">VR实景</view>
  90. </view>
  91. </view>
  92. <view class="v-select-infocard">
  93. <view class="select-btn">
  94. <view :class="selChartered === index ? 'distance' : 'score'"
  95. v-for="(item, index) in allCategoryList" :key="item.id"
  96. @click="selectChartered(item, index)">{{ item.name }}</view>
  97. </view>
  98. <view class="info-card-list">
  99. <scroll-view scroll-x="true" class="scroll-view_H">
  100. <view class="item-card scroll-view-item_H" v-for="(item, index) in charteredList?.timeSlot"
  101. :key="item.id" @click="open_popup(item, index)">
  102. <view class="today">{{ item.dateLabel }}</view>
  103. <view class="time">最早{{ item.startTime }}可订</view>
  104. <view class="price" v-if="item.sellingPrice">¥{{ item.sellingPrice?.toFixed(2) }}起
  105. </view>
  106. </view>
  107. <view class="not-data" v-if="charteredList?.timeSlot?.length < 1">暂无可选时间</view>
  108. </scroll-view>
  109. </view>
  110. </view>
  111. <view class="not-data" v-if="charteredList?.length < 1">暂无场地数据</view>
  112. </view>
  113. <view class="venue-card">
  114. <view class="card-title">
  115. <zzx-icon name="venue-icon3" size="14"></zzx-icon>
  116. <view class="">无固定场</view>
  117. </view>
  118. <view class="item-card" v-for="(item, index) in detailInfo.placeInfoMsgVO" :key="item.id">
  119. <image :src="item.cover" mode=""></image>
  120. <view class="venue-info">
  121. <view class="info-title">
  122. <view class="title textHidden">{{ item.name }}</view>
  123. <view class="sales">年售{{ item.sales }}</view>
  124. </view>
  125. <!-- <view class="type">
  126. 篮球、足球、羽毛球
  127. </view> -->
  128. <view class="price-info">
  129. <view class="price">
  130. <view class="" v-if="item.sellingPrice">¥{{ item?.sellingPrice?.toFixed(2) }}</view>
  131. <view class="" v-if="item.originalPrice">¥{{ item?.originalPrice?.toFixed(2) }}</view>
  132. </view>
  133. <view class="price-btn"
  134. @click="RouterUtils.to_page(`/pages/index/gymPay/index?type=1&placeId=${item.id}`)">抢购
  135. </view>
  136. </view>
  137. </view>
  138. <view class="card-tips">
  139. <view class="item-tips" @click="buyTips(item)">
  140. <text>购买须知</text>
  141. <zzx-icon name="ashRight" size="10"></zzx-icon>
  142. </view>
  143. <view class="item-tips" @click="serveTips(item)">
  144. <text>服务保障</text>
  145. <zzx-icon name="ashRight" size="10"></zzx-icon>
  146. </view>
  147. <!-- <view class="item-tips">
  148. <text>用户评价</text>
  149. <zzx-icon name="ashRight" size="10"></zzx-icon>
  150. </view> -->
  151. </view>
  152. </view>
  153. <view class="not-data" v-if="detailInfo.placeInfoMsgVO?.length < 1">暂无场地数据</view>
  154. </view>
  155. </view>
  156. <view class="course-card" id="notice">
  157. <view class="course-tips">
  158. <view class="">!</view>
  159. <view class="">全平台每种类型的运动课程只可免费试听一次</view>
  160. </view>
  161. <view class="select-btn">
  162. <view :class="sel_btn === index ? 'distance' : 'score'" v-for="(item, index) in allCourseCategoryList"
  163. :key="item.id" @click="selectallCategory(item, index)">{{ item.name }}</view>
  164. </view>
  165. <block v-if="!courseLoading">
  166. <view class="venue-card" v-for="item in courseList" :key="item.id" @click="toCOurseDetail">
  167. <image :src="item.cover" mode=""></image>
  168. <view class="venue-info">
  169. <view class="info-title">
  170. <view class="title textHidden">{{ item.name }}</view>
  171. <view class="sales">年售{{ item.salesYear }}</view>
  172. </view>
  173. <view class="type">
  174. {{ item.address }} {{ item.km || '--' }}km
  175. </view>
  176. <view class="price">
  177. <view class="" v-if="item?.sellingPrice">¥{{ item?.sellingPrice?.toFixed(2) }}</view>
  178. <view class="" v-if="item?.originalPrice">¥{{ item?.originalPrice?.toFixed(2) }}</view>
  179. </view>
  180. <view class="course-count">
  181. {{ item.period }}课时 {{ item.startTime }}-{{ item.endTime }}
  182. </view>
  183. <view class="price-info">
  184. <view class="sale">
  185. 已售{{ item.sales }} {{ item.goodRate }}%好评
  186. </view>
  187. <view class="price-btn"
  188. @click="RouterUtils.to_page(`/pages/index/courseDetail/index?id=${item.id}&type=2`)">
  189. {{ item.hasDiscount ? '免费试听' : '立即购买' }}
  190. </view>
  191. </view>
  192. </view>
  193. </view>
  194. </block>
  195. <loading v-else />
  196. <view class="not-data" v-if="courseList?.length == 0 && !courseLoading">暂无数据</view>
  197. <!-- <view class="more">查看更多</view> -->
  198. </view>
  199. <view class="instructor-card" id="schedule">
  200. <view class="card-title" @click="RouterUtils.to_page('/pages/index/allInstructor/index')">
  201. <view class="">教练({{ detailInfo.instructorVOList?.length }})</view>
  202. <zzx-icon name="right" size="12"></zzx-icon>
  203. </view>
  204. <scroll-view class="header-swiper content" scroll-x="true" :show-scrollbar="false">
  205. <view class="swiper-box">
  206. <view class="swiper-inner" v-for="item in detailInfo.instructorVOList" :key="item.id"
  207. @click="RouterUtils.to_page(`/pages/index/instructorDetail/index?id=${item.id}`)">
  208. <view class="header-img">
  209. <image :src="item.avatar" mode=""></image>
  210. <image src="/src/static/badge.png" mode=""></image>
  211. </view>
  212. <view class="instructor-name textHidden">{{ item.name }}</view>
  213. <view class="instructor-specialty textHidden">
  214. <text v-for="(category, index) in item.category" :key="index">{{ category }},</text>
  215. </view>
  216. </view>
  217. </view>
  218. </scroll-view>
  219. </view>
  220. <view class="appraise-card" id="appraise">
  221. <view class="appraise-title">
  222. <view class="title">评价</view>
  223. <view class="comments">
  224. <view class="a-star">
  225. <zzx-icon name="star" size="10"></zzx-icon>
  226. <text v-if="appraiseList?.averageScore">{{ appraiseList?.averageScore?.toFixed(1) }}</text>
  227. </view>
  228. <view class="a-text">| {{ appraiseList.scoreNum }}人评论</view>
  229. </view>
  230. </view>
  231. <view class="appraise-info" v-for="item in appraiseList.records" :key="item.id">
  232. <view class="a-user-info">
  233. <view class="info">
  234. <image :src="item.avatar" mode=""></image>
  235. <view class="name">{{ item.username }}</view>
  236. </view>
  237. <view class="time">{{ DateUtils.formatDateToMMDD(item.createTime) }}</view>
  238. </view>
  239. <view class="a-score">
  240. <text>{{ item.score?.toFixed(1) }}</text>
  241. <uni-rate :readonly="true" size="16" :value="item.score" />
  242. </view>
  243. <view class="a-content">
  244. {{ item.evaluateContent }}
  245. </view>
  246. <scroll-view class="scroll-view_H" scroll-x="true" :show-scrollbar="false" v-if="item.images != ''">
  247. <view class="scroll-view-item_H uni-bg-red" v-for="(img, idx) in item.images.split(',')" :key="idx">
  248. <image @click="_previewImage(item.images.split(','), img)" :src="img" mode=""></image>
  249. </view>
  250. </scroll-view>
  251. </view>
  252. </view>
  253. </view>
  254. <view style="position: relative;z-index: 99999;">
  255. <selPopup ref="openPopup" :listData="placedata" :itemList="selItems" :deteObj="dateIndex"
  256. :deteSelIndex="selIndex" @changes="onchange" />
  257. <uni-popup ref="placeInfoPopup" :safe-area="false" type="bottom">
  258. <view class="buyTips-box">
  259. <view class="buyTips-title">购买须知</view>
  260. <view class="rich-box" v-if="buyTipsObj">
  261. <rich-text :nodes="buyTipsObj.reminder"></rich-text>
  262. </view>
  263. </view>
  264. </uni-popup>
  265. <uni-popup ref="salesPopup" :safe-area="false" type="bottom">
  266. <view class="buyTips-box">
  267. <view class="buyTips-title">服务保障</view>
  268. <view class="rich-box">{{ salesTipsText }}</view>
  269. </view>
  270. </uni-popup>
  271. </view>
  272. </template>
  273. <script lang="ts" setup>
  274. import { ref, onMounted, getCurrentInstance, nextTick } from 'vue';
  275. import { RouterUtils, DateUtils, _previewImage, TipsUtils } from '@/utils/util';
  276. import { http } from '@/utils/http'
  277. import zzxNavbar from '@/components/zzx-navbar/zzx-navbar.vue';
  278. import { onLoad, onPageScroll, onReachBottom } from '@dcloudio/uni-app';
  279. import loading from '@/components/zzx-loading/zzx-loading.vue';
  280. import selPopup from './components/popup.vue';
  281. import { useCacheStore } from '@/stores/cache'
  282. const cache = useCacheStore()
  283. const placeInfoPopup = ref()
  284. const statusBarHeight = ref(0);
  285. const sectionTops = ref<number[]>([]);
  286. const isScrollingByTab = ref(false);
  287. const scrollTimer = ref<any>(null);
  288. const selectList = ref(['场地', '课程', '教练', '评价']);
  289. const sel_index = ref(0);
  290. const sel_btn = ref(0);
  291. const instance = getCurrentInstance();
  292. onLoad((option) => {
  293. listId.value = option.id
  294. appraiseFormData.value.siteId = option.id
  295. })
  296. onReachBottom(() => {
  297. appraiseFormData.value.pageNo++
  298. getFindByOrderPage()
  299. })
  300. onMounted(() => {
  301. get_navheight()
  302. nextTick(() => {
  303. setTimeout(() => getSectionsTop(), 300);
  304. });
  305. get_placeInfo()
  306. get_allCategory()
  307. get_allCourseCategory()
  308. getFindByOrderPage()
  309. })
  310. const get_navheight = () => {
  311. const systemInfo = uni.getSystemInfoSync();
  312. statusBarHeight.value = systemInfo.statusBarHeight || 0;
  313. }
  314. // 打开选场弹窗
  315. const dateIndex = ref()
  316. const selIndex = ref()
  317. const openPopup = ref(null);
  318. const open_popup = async (e, i) => {
  319. selIndex.value = i
  320. dateIndex.value = e
  321. get_placeInfoDetail(categoryId.value, i)
  322. await nextTick()
  323. openPopup.value?.opens()
  324. console.log(openPopup.value, 'openPopup');
  325. }
  326. // vr
  327. const checkedVr = () => {
  328. if (detailInfo.value.vr) {
  329. RouterUtils.to_page(`/pages/index/vr/index?vrImg=${detailInfo.value.vr}`)
  330. } else {
  331. TipsUtils.tips_toast('场馆暂未上传VR图片')
  332. }
  333. }
  334. // 获取所有模块的位置信息
  335. const getSectionsTop = () => {
  336. const ids = ['detail', 'notice', 'schedule', 'appraise'];
  337. const query = uni.createSelectorQuery().in(instance.proxy);
  338. ids.forEach(id => {
  339. query.select(`#${id}`).boundingClientRect();
  340. });
  341. query.exec((rects) => {
  342. // 添加空值检查
  343. sectionTops.value = rects
  344. .filter((rect: any) => rect !== null)
  345. .map((rect: any) => rect.top - 130);
  346. })
  347. };
  348. //滚动事件
  349. onPageScroll((e) => {
  350. if (isScrollingByTab.value) return;
  351. if (scrollTimer.value) return;
  352. scrollTimer.value = setTimeout(() => {
  353. updateActiveTab(e.scrollTop);
  354. scrollTimer.value = null;
  355. }, 100);
  356. });
  357. // 根据滚动位置更新激活的Tab
  358. const updateActiveTab = (scrollTop: number) => {
  359. const offset = 130;
  360. const scrollPosition = scrollTop + offset;
  361. let activeIndex = 0;
  362. if (!sectionTops.value || !Array.isArray(sectionTops.value)) {
  363. return
  364. }
  365. for (let i = 0; i < sectionTops.value.length; i++) {
  366. if (scrollPosition >= sectionTops.value[i]) {
  367. activeIndex = i;
  368. } else {
  369. break; // 模块位置已排序,可提前结束
  370. }
  371. }
  372. if (sel_index.value !== activeIndex) {
  373. sel_index.value = activeIndex;
  374. }
  375. };
  376. const sel_tab = async (i: number) => {
  377. isScrollingByTab.value = true; // 标记为Tab点击触发的滚动
  378. sel_index.value = i;
  379. const ids = ['detail', 'notice', 'schedule', 'appraise'];
  380. const id = ids[i];
  381. await scrollToTop();
  382. await nextTick();
  383. const query = uni.createSelectorQuery().in(instance.proxy);
  384. query.select(`#${id}`).boundingClientRect(data => {
  385. if (!data) return;
  386. uni.pageScrollTo({
  387. scrollTop: data.top - 130,
  388. duration: 500,
  389. complete: () => {
  390. setTimeout(() => {
  391. isScrollingByTab.value = false;
  392. }, 300);
  393. }
  394. });
  395. }).exec();
  396. }
  397. // 滚动到顶部
  398. const scrollToTop = () => {
  399. return new Promise(resolve => {
  400. uni.pageScrollTo({
  401. scrollTop: 0,
  402. duration: 0,
  403. success: resolve
  404. });
  405. });
  406. }
  407. // 打开地图
  408. const open_map = () => {
  409. uni.openLocation({
  410. latitude: detailInfo.value.latitude || 0,
  411. longitude: detailInfo.value.longitude || 0,
  412. name: detailInfo.value.name,
  413. address: detailInfo.value.address,
  414. success: function () {
  415. console.log('success');
  416. }
  417. });
  418. }
  419. // 拨打电话
  420. const open_phone = () => {
  421. uni.makePhoneCall({
  422. phoneNumber: detailInfo.value.phone
  423. });
  424. }
  425. // 详情信息
  426. const listId = ref(0);
  427. const detailInfo = ref({})
  428. const bannerList = ref([])
  429. const videoList = ref([])
  430. const noTeachingDay = ref()
  431. const teachingDay = ref()
  432. const get_placeInfo = () => {
  433. http.get('/detail/getPlaceInfo', { data: { id: listId.value }, loading: true }).then((res) => {
  434. const startIndex = res.result.cover ? res.result.cover.indexOf('"') + 1 : 0;
  435. const endIndex = res.result.cover ? res.result.cover.lastIndexOf('') : 0;
  436. bannerList.value = res.result.cover ? res.result.cover.slice(startIndex, endIndex).split(',') : [];
  437. let videoIndex = res.result.video ? res.result.video.indexOf('"') + 1 : 0;
  438. let videoEndIndex = res.result.video ? res.result.video.lastIndexOf('') : 0;
  439. videoList.value = res.result.video ? res.result.video.slice(videoIndex, videoEndIndex).split(',') : [];
  440. detailInfo.value = res.result
  441. noTeachingDay.value = JSON.parse(res.result.noTeachingDay)
  442. teachingDay.value = JSON.parse(res.result.teachingDay)
  443. })
  444. }
  445. // 场地分类
  446. const allCategoryList = ref([])
  447. const categoryId = ref()
  448. const get_allCategory = () => {
  449. http.get('/detail/getAllCategory', { data: { id: listId.value } }).then((res) => {
  450. if (!res.result || res.result.length == 0) return
  451. categoryId.value = res.result[0].id
  452. allCategoryList.value = res.result
  453. get_placeInfoNoFixation(categoryId.value)
  454. })
  455. }
  456. // 课程分类
  457. const allCourseCategoryList = ref([])
  458. const categoryCourseId = ref()
  459. const get_allCourseCategory = () => {
  460. http.get('/detail/getAllCourseCategory', { data: { id: listId.value } }).then((res) => {
  461. categoryCourseId.value = res.result[0].id
  462. allCourseCategoryList.value = res.result
  463. get_courseList(categoryCourseId.value)
  464. })
  465. }
  466. // 课程列表
  467. const courseList = ref([])
  468. const courseLoading = ref(false)
  469. const get_courseList = (categoryId: string) => {
  470. courseLoading.value = true
  471. http.get('/detail/courseInfoVOList', { data: { categoryId: categoryId, id: listId.value, longitude: cache.get('LON') || 0, latitude: cache.get('LAT') || 0 } }).then((res) => {
  472. courseLoading.value = false
  473. courseList.value = res.result
  474. })
  475. }
  476. // 课程切换
  477. const selectallCategory = (e, i) => {
  478. sel_btn.value = i
  479. get_courseList(e.id)
  480. }
  481. // 包场切换
  482. const selChartered = ref(0)
  483. const selectChartered = (e, i) => {
  484. console.log(e, i, '包场切换');
  485. categoryId.value = e.id
  486. selChartered.value = i
  487. get_placeInfoNoFixation(e.id)
  488. get_placeInfoDetail(e.id, '')
  489. }
  490. // 包场信息
  491. const charteredList = ref([])
  492. const get_placeInfoNoFixation = (categoryId) => {
  493. http.get('/detail/getPlaceInfoNoFixation', { data: { categoryId: categoryId, id: listId.value }, loading: true }).then((res) => {
  494. charteredList.value = res.result
  495. })
  496. }
  497. const buyTipsObj = ref()
  498. const buyTips = (e) => {
  499. placeInfoPopup.value.open()
  500. buyTipsObj.value = e
  501. }
  502. const salesPopup = ref()
  503. const salesTipsText = ref('')
  504. const serveTips = (e) => {
  505. salesPopup.value.open()
  506. if (e.sales === 0) {
  507. salesTipsText.value = '未消费随时退款,过期未消费自动退款。'
  508. } else if (e.sales === 2) {
  509. salesTipsText.value = '不支退款,请慎重考虑后购买。'
  510. }
  511. }
  512. const placedata = ref()
  513. const selItems = ref()
  514. const get_placeInfoDetail = (catId: any, dateIndex: any) => {
  515. http.get('/stadium/getPlaceInfo', { data: { siteId: listId.value, categoryId: catId }, loading: true }).then((res) => {
  516. placedata.value = res.result
  517. selItems.value = res.result?.stadiumConcertsVOList[dateIndex]?.concertsVOList
  518. })
  519. }
  520. const onchange = (e, i) => {
  521. selItems.value = e?.concertsVOList
  522. selIndex.value = i
  523. }
  524. // 获取评价
  525. const appraiseFormData = ref({
  526. siteId: null,
  527. pageNo: 1,
  528. pageSize: 10
  529. })
  530. const appraiseList = ref([])
  531. const getFindByOrderPage = () => {
  532. http.get('/my/evaluate/findByOrderPage', { data: appraiseFormData.value, loading: true }).then((res) => {
  533. if (appraiseFormData.value.pageNo == 1) {
  534. appraiseList.value = res.result
  535. } else {
  536. appraiseList.value = [...appraiseList.value, ...res.result]
  537. }
  538. })
  539. }
  540. </script>
  541. <style lang="less" scoped>
  542. .detail-header {
  543. position: relative;
  544. .header-bg {
  545. position: absolute;
  546. width: 100%;
  547. height: 432rpx;
  548. z-index: -100;
  549. }
  550. .back-icon {
  551. position: absolute;
  552. left: 20rpx;
  553. z-index: 9999;
  554. }
  555. .header-swiper {
  556. position: absolute;
  557. width: 740rpx;
  558. white-space: nowrap;
  559. overflow: hidden;
  560. /* 隐藏滚动条 */
  561. /deep/ ::-webkit-scrollbar {
  562. display: none;
  563. width: 0 !important;
  564. height: 0 !important;
  565. background: transparent;
  566. }
  567. .swiper-inner {
  568. display: inline-flex;
  569. align-items: center;
  570. gap: 14rpx;
  571. height: 100%;
  572. &>video,
  573. &>image {
  574. width: 220rpx;
  575. height: 126rpx;
  576. border-radius: 16rpx;
  577. flex-shrink: 0;
  578. }
  579. }
  580. }
  581. }
  582. .header-info {
  583. padding: 20rpx;
  584. background: #FFFFFF;
  585. border-radius: 16rpx 16rpx 0rpx 0rpx;
  586. .venue-name {
  587. display: flex;
  588. align-items: center;
  589. justify-content: space-between;
  590. .open-status-info {
  591. margin-top: 20rpx;
  592. display: flex;
  593. align-items: center;
  594. gap: 20rpx;
  595. .open-status {
  596. font-size: 24rpx;
  597. color: #4DD951;
  598. }
  599. .open-time-info {
  600. font-size: 24rpx;
  601. color: #AAAAAA;
  602. }
  603. }
  604. .name {
  605. font-weight: 800;
  606. font-size: 32rpx;
  607. color: #222222;
  608. }
  609. .star {
  610. display: flex;
  611. align-items: center;
  612. gap: 10rpx;
  613. font-size: 28rpx;
  614. color: #FDD143;
  615. }
  616. }
  617. .open-time {
  618. margin-top: 20rpx;
  619. .time {
  620. font-size: 24rpx;
  621. color: #AAAAAA;
  622. &>view {
  623. margin-top: 20rpx;
  624. }
  625. }
  626. }
  627. .venues-tags {
  628. display: flex;
  629. align-items: center;
  630. justify-content: space-between;
  631. margin-top: 20rpx;
  632. .tags-box {
  633. display: flex;
  634. flex-wrap: wrap;
  635. gap: 10rpx;
  636. .tags {
  637. padding: 8rpx 16rpx;
  638. font-size: 24rpx;
  639. color: #222222;
  640. background: #F5F5F5;
  641. border-radius: 8rpx;
  642. }
  643. }
  644. .more-info {
  645. display: flex;
  646. align-items: center;
  647. font-size: 24rpx;
  648. gap: 6rpx;
  649. color: #AAAAAA;
  650. }
  651. }
  652. .venues-address {
  653. display: flex;
  654. align-items: center;
  655. justify-content: space-between;
  656. margin-top: 20rpx;
  657. background: #F9F9F9;
  658. border-radius: 16rpx;
  659. padding: 20rpx;
  660. .address {
  661. display: flex;
  662. align-items: center;
  663. gap: 10rpx;
  664. font-weight: bold;
  665. font-size: 24rpx;
  666. color: #222222;
  667. }
  668. .nav-phone {
  669. display: flex;
  670. align-items: center;
  671. gap: 20rpx;
  672. text-align: center;
  673. .nav,
  674. .phone {
  675. font-size: 22rpx;
  676. color: #222222;
  677. }
  678. }
  679. }
  680. }
  681. .detail-select {
  682. display: flex;
  683. align-items: center;
  684. gap: 60rpx;
  685. margin-top: 20rpx;
  686. height: 80rpx;
  687. background-color: #F6F6F6;
  688. .select-text,
  689. .notsel-text {
  690. position: relative;
  691. transition: all 0.3s ease;
  692. }
  693. .select-text {
  694. font-weight: 800;
  695. font-size: 32rpx;
  696. color: #222222;
  697. position: relative;
  698. }
  699. .notsel-text {
  700. font-size: 32rpx;
  701. color: #AAAAAA;
  702. }
  703. .select-text::after {
  704. position: absolute;
  705. content: '';
  706. width: 40rpx;
  707. height: 20rpx;
  708. background-color: #C8FF0C;
  709. border-radius: 4rpx;
  710. left: 050%;
  711. transform: translateX(-50%);
  712. bottom: -16rpx;
  713. transition: all 0.3s ease;
  714. opacity: 1;
  715. }
  716. .notsel-text::after {
  717. content: '';
  718. position: absolute;
  719. left: 50%;
  720. bottom: -16rpx;
  721. width: 0;
  722. height: 20rpx;
  723. background-color: #C8FF0C;
  724. border-radius: 4rpx;
  725. transform: translateX(-50%);
  726. opacity: 0;
  727. transition: all 0.3s ease;
  728. }
  729. }
  730. .venue-select-card {
  731. padding: 20rpx;
  732. background: linear-gradient(180deg, #FCFFF1 0%, #FFFFFF 100%);
  733. border-radius: 32rpx;
  734. .v-caed-header {
  735. display: flex;
  736. align-items: center;
  737. justify-content: space-around;
  738. height: 80rpx;
  739. font-size: 24rpx;
  740. color: #AAAAAA;
  741. border-bottom: 2rpx solid #F0F0F0;
  742. .v-left {
  743. display: flex;
  744. align-items: center;
  745. gap: 10rpx;
  746. }
  747. }
  748. .v-select-infocard {
  749. .select-btn {
  750. display: flex;
  751. align-items: center;
  752. gap: 20rpx;
  753. margin-top: 20rpx;
  754. .distance {
  755. width: 92rpx;
  756. height: 36rpx;
  757. background: #C8FF0C;
  758. border-radius: 8rpx;
  759. font-size: 24rpx;
  760. color: #222222;
  761. line-height: 34rpx;
  762. text-align: center;
  763. }
  764. .score {
  765. width: 92rpx;
  766. height: 36rpx;
  767. background: #F5F5F5;
  768. border-radius: 8rpx;
  769. font-size: 24rpx;
  770. color: #AAAAAA;
  771. line-height: 34rpx;
  772. text-align: center;
  773. }
  774. }
  775. .info-card-list {
  776. margin-top: 20rpx;
  777. .scroll-view_H {
  778. white-space: nowrap;
  779. width: 100%;
  780. .item-card {
  781. width: 200rpx;
  782. height: 132rpx;
  783. padding: 20rpx;
  784. background: #F6F6F6;
  785. border-radius: 16rpx;
  786. display: inline-block;
  787. margin-right: 16rpx;
  788. .today {
  789. font-size: 24rpx;
  790. color: #222222;
  791. }
  792. .time {
  793. margin-top: 12rpx;
  794. font-size: 24rpx;
  795. color: #222222;
  796. }
  797. .price {
  798. margin-top: 12rpx;
  799. font-weight: bold;
  800. font-size: 28rpx;
  801. color: #FB5B5B;
  802. }
  803. }
  804. }
  805. }
  806. }
  807. }
  808. .venue-card {
  809. padding: 20rpx;
  810. background: #FFFFFF;
  811. border-radius: 32rpx;
  812. margin-top: 20rpx;
  813. .card-title {
  814. display: flex;
  815. align-items: center;
  816. gap: 10rpx;
  817. font-size: 24rpx;
  818. color: #AAAAAA;
  819. &>view {
  820. margin-bottom: 10rpx;
  821. }
  822. }
  823. .item-card {
  824. margin-top: 20rpx;
  825. display: flex;
  826. align-items: center;
  827. flex-wrap: wrap;
  828. gap: 20rpx;
  829. &>image {
  830. width: 200rpx;
  831. height: 200rpx;
  832. border-radius: 32rpx;
  833. }
  834. .venue-info {
  835. width: 430rpx;
  836. .info-title {
  837. display: flex;
  838. align-items: center;
  839. justify-content: space-between;
  840. .title {
  841. width: 340rpx;
  842. font-weight: 800;
  843. font-size: 32rpx;
  844. color: #222222;
  845. }
  846. .sales {
  847. font-size: 22rpx;
  848. color: #AAAAAA;
  849. }
  850. }
  851. .type {
  852. margin-top: 16rpx;
  853. font-size: 24rpx;
  854. color: #AAAAAA;
  855. }
  856. .price-info {
  857. display: flex;
  858. align-items: center;
  859. justify-content: space-between;
  860. margin-top: 16rpx;
  861. .price {
  862. display: flex;
  863. align-items: center;
  864. gap: 20rpx;
  865. &>view:nth-child(1) {
  866. font-weight: bold;
  867. font-size: 28rpx;
  868. color: #FB5B5B;
  869. }
  870. &>view:nth-child(2) {
  871. font-size: 22rpx;
  872. color: #AAAAAA;
  873. text-decoration: line-through;
  874. }
  875. }
  876. .price-btn {
  877. width: 152rpx;
  878. height: 48rpx;
  879. background: #C8FF0C;
  880. border-radius: 8rpx;
  881. font-weight: bold;
  882. font-size: 28rpx;
  883. color: #222222;
  884. text-align: center;
  885. line-height: 48rpx;
  886. }
  887. }
  888. }
  889. .card-tips {
  890. display: flex;
  891. align-items: center;
  892. gap: 40rpx;
  893. flex-basis: 100%;
  894. height: 60rpx;
  895. border-bottom: 1rpx solid #F0F0F0;
  896. .item-tips {
  897. display: flex;
  898. align-items: center;
  899. gap: 10rpx;
  900. font-size: 22rpx;
  901. color: #222222;
  902. }
  903. }
  904. }
  905. }
  906. .course-card {
  907. padding: 20rpx;
  908. background: #FFFFFF;
  909. border-radius: 32rpx;
  910. margin-top: 20rpx;
  911. .course-tips {
  912. display: flex;
  913. align-items: center;
  914. gap: 10rpx;
  915. font-size: 24rpx;
  916. color: #999999;
  917. &>view:first-child {
  918. width: 32rpx;
  919. height: 32rpx;
  920. background: #FFA347;
  921. border-radius: 50%;
  922. display: flex;
  923. justify-content: center;
  924. align-items: center;
  925. color: #FFFFFF;
  926. }
  927. }
  928. .select-btn {
  929. display: flex;
  930. align-items: center;
  931. gap: 20rpx;
  932. margin-top: 20rpx;
  933. .distance {
  934. width: 92rpx;
  935. height: 36rpx;
  936. background: #C8FF0C;
  937. border-radius: 8rpx;
  938. font-size: 24rpx;
  939. color: #222222;
  940. line-height: 34rpx;
  941. text-align: center;
  942. }
  943. .score {
  944. width: 92rpx;
  945. height: 36rpx;
  946. background: #F5F5F5;
  947. border-radius: 8rpx;
  948. font-size: 24rpx;
  949. color: #AAAAAA;
  950. line-height: 34rpx;
  951. text-align: center;
  952. }
  953. }
  954. .venue-card {
  955. display: flex;
  956. align-items: center;
  957. gap: 20rpx;
  958. margin-top: 20rpx;
  959. &>image {
  960. width: 200rpx;
  961. height: 200rpx;
  962. border-radius: 32rpx;
  963. }
  964. .venue-info {
  965. width: 430rpx;
  966. height: 220rpx;
  967. border-bottom: 1rpx solid #F0F0F0;
  968. padding-bottom: 10rpx;
  969. .info-title {
  970. display: flex;
  971. align-items: center;
  972. justify-content: space-between;
  973. .title {
  974. width: 340rpx;
  975. font-weight: 800;
  976. font-size: 32rpx;
  977. color: #222222;
  978. }
  979. .sales {
  980. font-size: 22rpx;
  981. color: #AAAAAA;
  982. }
  983. }
  984. .type {
  985. margin-top: 16rpx;
  986. font-size: 24rpx;
  987. color: #AAAAAA;
  988. }
  989. .price {
  990. display: flex;
  991. align-items: center;
  992. gap: 20rpx;
  993. margin-top: 16rpx;
  994. &>view:nth-child(1) {
  995. font-weight: bold;
  996. font-size: 28rpx;
  997. color: #FB5B5B;
  998. }
  999. &>view:nth-child(2) {
  1000. font-size: 22rpx;
  1001. color: #AAAAAA;
  1002. text-decoration: line-through;
  1003. }
  1004. }
  1005. .course-count {
  1006. font-size: 22rpx;
  1007. color: #AAAAAA;
  1008. }
  1009. .price-info {
  1010. display: flex;
  1011. align-items: center;
  1012. justify-content: space-between;
  1013. .sale {
  1014. font-size: 24rpx;
  1015. color: #AAAAAA;
  1016. }
  1017. .price-btn {
  1018. width: 152rpx;
  1019. height: 48rpx;
  1020. background: #C8FF0C;
  1021. border-radius: 8rpx;
  1022. font-weight: bold;
  1023. font-size: 28rpx;
  1024. color: #222222;
  1025. text-align: center;
  1026. line-height: 48rpx;
  1027. }
  1028. }
  1029. }
  1030. }
  1031. .more {
  1032. margin: auto;
  1033. width: 100rpx;
  1034. margin-top: 20rpx;
  1035. text-align: center;
  1036. font-size: 24rpx;
  1037. color: #CCCCCC;
  1038. position: relative;
  1039. }
  1040. .more::after {
  1041. position: absolute;
  1042. bottom: -10rpx;
  1043. left: 25rpx;
  1044. content: '';
  1045. width: 38rpx;
  1046. height: 4rpx;
  1047. border-radius: 2rpx;
  1048. background-color: #CCCCCC;
  1049. }
  1050. }
  1051. .instructor-card {
  1052. padding: 20rpx;
  1053. background: #FFFFFF;
  1054. border-radius: 32rpx;
  1055. margin-top: 20rpx;
  1056. overflow: hidden;
  1057. .card-title {
  1058. display: flex;
  1059. align-items: center;
  1060. justify-content: space-between;
  1061. font-weight: 800;
  1062. font-size: 32rpx;
  1063. color: #222222;
  1064. }
  1065. .header-swiper {
  1066. margin-top: 24rpx;
  1067. width: 700rpx;
  1068. white-space: nowrap;
  1069. overflow: hidden;
  1070. /* 隐藏滚动条 */
  1071. /deep/ ::-webkit-scrollbar {
  1072. display: none;
  1073. width: 0 !important;
  1074. height: 0 !important;
  1075. background: transparent;
  1076. }
  1077. .swiper-box {
  1078. display: flex;
  1079. align-items: center;
  1080. gap: 44rpx;
  1081. .swiper-inner {
  1082. width: 100rpx;
  1083. text-align: center;
  1084. .header-img {
  1085. position: relative;
  1086. image {
  1087. width: 100rpx;
  1088. height: 100rpx;
  1089. border-radius: 50%;
  1090. &:nth-child(2) {
  1091. position: absolute;
  1092. bottom: 0;
  1093. right: 0;
  1094. width: 36rpx;
  1095. height: 36rpx;
  1096. }
  1097. }
  1098. }
  1099. .instructor-name {
  1100. width: 120rpx;
  1101. font-size: 28rpx;
  1102. font-weight: 600;
  1103. }
  1104. .instructor-specialty {
  1105. width: 120rpx;
  1106. font-size: 22rpx;
  1107. color: #AAAAAA;
  1108. }
  1109. }
  1110. }
  1111. }
  1112. }
  1113. .appraise-card {
  1114. padding: 20rpx;
  1115. background: #FFFFFF;
  1116. border-radius: 32rpx;
  1117. margin-top: 20rpx;
  1118. .appraise-title {
  1119. display: flex;
  1120. align-items: center;
  1121. justify-content: space-between;
  1122. .title {
  1123. font-weight: 800;
  1124. font-size: 32rpx;
  1125. color: #222222;
  1126. }
  1127. .comments {
  1128. display: flex;
  1129. align-items: center;
  1130. gap: 16rpx;
  1131. .a-star {
  1132. font-size: 24rpx;
  1133. color: #FDD143;
  1134. }
  1135. .a-text {
  1136. font-size: 24rpx;
  1137. color: #AAAAAA;
  1138. }
  1139. }
  1140. }
  1141. .appraise-info {
  1142. margin-top: 20rpx;
  1143. .a-user-info {
  1144. display: flex;
  1145. align-items: center;
  1146. justify-content: space-between;
  1147. .info {
  1148. display: flex;
  1149. align-items: center;
  1150. gap: 20rpx;
  1151. &>image {
  1152. width: 60rpx;
  1153. height: 60rpx;
  1154. border-radius: 50%;
  1155. }
  1156. .name {
  1157. font-weight: bold;
  1158. font-size: 24rpx;
  1159. color: #222222;
  1160. }
  1161. }
  1162. .time {
  1163. font-size: 24rpx;
  1164. color: #AAAAAA;
  1165. }
  1166. }
  1167. .a-score {
  1168. font-size: 24rpx;
  1169. color: #AAAAAA;
  1170. display: flex;
  1171. align-items: center;
  1172. gap: 20rpx;
  1173. margin-top: 20rpx;
  1174. }
  1175. .a-content {
  1176. margin-top: 20rpx;
  1177. font-size: 28rpx;
  1178. color: #222222;
  1179. }
  1180. .scroll-view_H {
  1181. white-space: nowrap;
  1182. width: 100%;
  1183. height: 220rpx;
  1184. margin-top: 20rpx;
  1185. border-bottom: 1rpx solid #F0F0F0;
  1186. .scroll-view-item_H {
  1187. display: inline-block;
  1188. text-align: center;
  1189. margin-right: 14rpx;
  1190. &>image {
  1191. width: 202rpx;
  1192. height: 200rpx;
  1193. background: #D8D8D8;
  1194. border-radius: 32rpx;
  1195. }
  1196. }
  1197. }
  1198. }
  1199. }
  1200. .buyTips-box {
  1201. background: #F6F6F6;
  1202. border-radius: 32rpx 32rpx 0rpx 0rpx;
  1203. max-height: 1200rpx;
  1204. overflow: auto;
  1205. padding: 20rpx;
  1206. .buyTips-title {
  1207. text-align: center;
  1208. font-size: 28rpx;
  1209. font-weight: bold;
  1210. }
  1211. .rich-box {
  1212. margin-top: 20rpx;
  1213. }
  1214. }
  1215. </style>