This commit is contained in:
lcj
2026-07-13 11:15:59 +08:00
parent 45a74578fc
commit ee0825c1e6
7 changed files with 225 additions and 18 deletions
+165 -5
View File
@@ -35,7 +35,7 @@
<!-- 预约提醒横幅 -->
<view v-if="scenic.needReservation" class="notice-banner">
<view class="icon-wrap icon-alert" />
<text class="notice-text">需预约后出行,未预约无法入园</text>
<rich-text class="notice-text" :nodes="scenic.needReservation" />
</view>
<!-- 特色标签行 -->
@@ -132,7 +132,26 @@
<rich-text class="notice-html" :nodes="scenic.bookingNoticeHtml" />
</view>
<view v-if="activeTab === 'intro'" class="content-section">
<text class="intro-text">{{ scenic.introduction }}</text>
<rich-text class="notice-html" :nodes="scenic.introduction" />
</view>
</view>
<!-- 店铺公告弹窗 -->
<view v-if="noticeVisible" class="notice-overlay" @click="noticeVisible = false">
<view class="notice-popup" @click.stop>
<view class="notice-popup-accent" />
<view class="notice-popup-header">
<view class="notice-popup-icon" />
<text class="notice-popup-title">{{ storeNotice.title }}</text>
</view>
<scroll-view class="notice-popup-body" scroll-y>
<rich-text :nodes="storeNotice.content" />
</scroll-view>
<view class="notice-popup-footer">
<view class="notice-popup-btn" @click="noticeVisible = false">
<text>我知道了</text>
</view>
</view>
</view>
</view>
@@ -143,14 +162,14 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { onLoad, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import { post, getToken } from '../../../api/request'
interface NoticeItem { title: string; content: string }
interface CardBenefit { type: string; cardName: string; skuName: string; remainingCount: number; freeCount: number; bookingDeadline: string; cardModelId: string; resourceId: string; goodsId: string }
interface ScenicDetail {
id: number; name: string; price: number; freeCount: number; level: string
hours: string; needReservation: boolean; bookingDeadline: string
hours: string; needReservation: string; bookingDeadline: string
phone: string; address: string; tags: string[]; images: string[]
introduction: string; bookingNotice: NoticeItem[]; bookingNoticeHtml: string; cards: CardBenefit[]
scenicId: number; shopId: number; mainPic: string; status: number
@@ -160,6 +179,9 @@ const scenic = ref<ScenicDetail>({ tags: [], images: [], cards: [], bookingNotic
const activeTab = ref<'notice' | 'intro'>('notice')
const currentSlide = ref(0)
const loading = ref(true)
const currentShopId = ref('')
const storeNotice = ref<{ title: string; content: string }>({ title: '', content: '' })
const noticeVisible = ref(false)
function onSwiperChange(e: any) {
currentSlide.value = e.detail.current
@@ -171,6 +193,7 @@ const statusLabel = computed(() => ['', '正常运营', '审核中', '暂停运
onLoad((options) => {
const shopId = options?.shopId || options?.id || ''
currentShopId.value = shopId
if (!shopId) {
uni.showToast({ title: '缺少景区参数', icon: 'none' })
uni.navigateBack()
@@ -178,8 +201,26 @@ onLoad((options) => {
}
uni.setNavigationBarTitle({ title: '景区详情' })
fetchDetail(String(shopId))
fetchShopNotice(String(shopId))
})
async function fetchShopNotice(shopId: string) {
try {
const res = await post<any>('/mp-api/mpShopNotice/queryShopNoticePage', {
shopId,
pageNo: 1,
pageSize: 5,
})
const list = res?.list || res?.data?.list || []
if (list.length > 0) {
storeNotice.value = { title: list[0].title || '', content: list[0].content || '' }
noticeVisible.value = true
}
} catch {
// silently fail
}
}
async function fetchDetail(shopId: string) {
loading.value = true
try {
@@ -220,6 +261,18 @@ async function fetchDetail(shopId: string) {
}
}
onShareAppMessage(() => ({
title: scenic.value.name || '景区详情',
path: `/packages/content/pages/scenic-detail?shopId=${currentShopId.value}`,
imageUrl: scenic.value.images?.[0] || '',
}))
onShareTimeline(() => ({
title: scenic.value.name || '景区详情',
query: `shopId=${currentShopId.value}`,
imageUrl: scenic.value.images?.[0] || '',
}))
function onHoursClick() {
uni.showToast({ title: '营业时间仅供参考', icon: 'none' })
}
@@ -575,7 +628,7 @@ $icon-crown: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' w
.deadline-text {
margin-left: auto;
font-size: $font-size-xs;
font-size: $font-size-sm;
color: $uv-tips-color;
}
@@ -705,6 +758,113 @@ $icon-crown: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' w
display: block;
}
/* ====== 店铺公告弹窗 ====== */
.notice-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(4rpx);
z-index: $z-modal;
display: flex;
align-items: center;
justify-content: center;
animation: overlayIn 250ms ease;
}
@keyframes overlayIn {
from { opacity: 0; }
to { opacity: 1; }
}
.notice-popup {
width: 620rpx;
max-height: 70vh;
background: $color-card;
border-radius: $radius-xl;
box-shadow: $shadow-xl;
overflow: hidden;
display: flex;
flex-direction: column;
animation: popupIn 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes popupIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.notice-popup-accent {
height: 6rpx;
background: $gradient-accent;
flex-shrink: 0;
}
.notice-popup-header {
display: flex;
flex-direction: column;
align-items: center;
padding: $spacing-6 $spacing-5 0;
flex-shrink: 0;
}
.notice-popup-icon {
width: 48rpx;
height: 48rpx;
flex-shrink: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 24 24' fill='none' stroke='%233c9cff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9'/%3E%3Cpath d='M13.73 21a2 2 0 0 1-3.46 0'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
margin-bottom: $spacing-2;
}
.notice-popup-title {
font-size: $font-size-lg;
font-weight: $font-weight-bold;
color: $uv-main-color;
font-family: $font-family-display;
line-height: 1.4;
text-align: center;
}
.notice-popup-body {
padding: $spacing-3 $spacing-5 $spacing-3;
font-size: $font-size-sm;
color: $uv-tips-color;
line-height: 1.9;
max-height: 45vh;
overflow-y: auto;
}
.notice-popup-footer {
padding: 0 $spacing-5 $spacing-5;
flex-shrink: 0;
}
.notice-popup-btn {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
background: $gradient-accent;
border-radius: $radius-full;
transition: opacity $transition-fast;
text {
font-size: $font-size-base;
color: #fff;
font-weight: $font-weight-medium;
}
&:active { opacity: 0.85; }
}
/* ====== 底部安全区 ====== */
.safe-bottom {
height: calc(40rpx + env(safe-area-inset-bottom));
+25 -6
View File
@@ -97,7 +97,7 @@
<view class="name-price-row">
<text class="scenic-name">{{ scenic.name }}</text>
<view class="price-group">
<text class="price-symbol">¥</text>
<text class="price-symbol">¥</text>
<text class="price-value">{{ scenic.price }}</text>
</view>
</view>
@@ -122,7 +122,7 @@
<text>全年还剩{{ scenic.remainingCount }}次</text>
</view>
<view v-else-if="scenic.freeCount > 0" class="remaining-tag">
<text>全年可用{{ scenic.freeCount }}次</text>
<text>全年最多可用{{ scenic.freeCount }}次</text>
</view>
<view class="benefit-tag" :class="scenic.needReservation ? 'warning' : 'success'">
<text>{{ scenic.needReservation ? '需预约' : '免预约' }}</text>
@@ -132,7 +132,7 @@
<!-- 温馨提示 -->
<view v-if="scenic.tips" class="tips-bar">
<view class="tips-icon">!</view>
<text class="tips-text">{{ scenic.tips }}</text>
<rich-text class="tips-text" :nodes="scenic.tips" />
</view>
</view>
</view>
@@ -195,7 +195,7 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { onLoad, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import { post } from '../../../api/request'
interface ScenicItem {
@@ -592,7 +592,12 @@ function clearSearch() {
}
function goBack() {
uni.navigateBack()
const pages = getCurrentPages()
if (pages.length <= 1) {
uni.switchTab({ url: '/pages/index/index' })
} else {
uni.navigateBack()
}
}
function onCardClick(scenic: ScenicItem) {
@@ -600,6 +605,18 @@ function onCardClick(scenic: ScenicItem) {
url: `/packages/content/pages/scenic-detail?shopId=${scenic.shopId}`,
})
}
onShareAppMessage(() => ({
title: navTitle.value,
path: '/packages/content/pages/scenic-list',
imageUrl: 'https://aliyunoss.hn12301.net/sd/aizaipingxiang.png',
}))
onShareTimeline(() => ({
title: navTitle.value,
query: '',
imageUrl: 'https://aliyunoss.hn12301.net/sd/aizaipingxiang.png',
}))
</script>
<style lang="scss" scoped>
@@ -997,6 +1014,7 @@ function onCardClick(scenic: ScenicItem) {
font-size: $font-size-xl;
color: $uv-error;
font-weight: $font-weight-bold;
margin-left: 5rpx;
}
/* 营业时间 + 营业状态 同行 */
@@ -1173,7 +1191,8 @@ function onCardClick(scenic: ScenicItem) {
.tips-text {
font-size: $font-size-sm;
color: $uv-content-color;
line-height: 1.5;
line-height: 1.6;
display: block;
}
/* ====== uni-popup 弹窗内容 ====== */