321
This commit is contained in:
parent
23b5ff20a2
commit
d6895456ff
3
App.vue
3
App.vue
|
|
@ -2,6 +2,9 @@
|
|||
export default {
|
||||
onLaunch: function() {
|
||||
console.log('App Launch')
|
||||
// 本次小程序启动期间是否已展示过首页广告弹窗
|
||||
this.globalData = this.globalData || {};
|
||||
this.globalData.hasShownHomeAd = false;
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
|
|
|
|||
|
|
@ -72,4 +72,46 @@ export const getShareConfig = async (path) => {
|
|||
path: path,
|
||||
imageUrl: config.shareImage
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取悬浮球配置
|
||||
* @returns {Object|null} 返回浮动配置,失败时返回null {
|
||||
"enabled": true,
|
||||
"image": "https://admin-1308826010.cos.ap-shanghai.myqcloud.com/upload/20251006/20251006174940_1752.jpg",
|
||||
"popupImage": "https://admin-1308826010.cos.ap-shanghai.myqcloud.com/upload/20251006/20251006175053_9954.jpg"
|
||||
}
|
||||
*/
|
||||
export const getFloatConfig = async () => {
|
||||
if (configData.value == null) {
|
||||
return null;
|
||||
}
|
||||
var config = configData.value.config;
|
||||
var floatConfig = config.floatConfig;
|
||||
if (!floatConfig || !floatConfig.enabled) {
|
||||
return null;
|
||||
}
|
||||
return floatConfig;
|
||||
}
|
||||
export const getPopupShow = ref(true);
|
||||
/**
|
||||
* 获取首页弹窗配置
|
||||
* @returns {Object|null} 返回弹窗配置,失败时返回null {
|
||||
"enabled": true,
|
||||
"image": "https://admin-1308826010.cos.ap-shanghai.myqcloud.com/upload/20251006/20251006151324_4800.jpg"
|
||||
}
|
||||
*/
|
||||
export const getPopupConfig = async () => {
|
||||
if (configData.value == null) {
|
||||
return null;
|
||||
}
|
||||
var config = configData.value.config;
|
||||
var popupConfig = config.popupConfig;
|
||||
if (!popupConfig || !popupConfig.enabled) {
|
||||
return null;
|
||||
}
|
||||
return popupConfig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
2
components.d.ts
vendored
2
components.d.ts
vendored
|
|
@ -11,6 +11,8 @@ declare module 'vue' {
|
|||
CardContainer: typeof import('./components/com/appointment/card-container.vue')['default']
|
||||
Container: typeof import('./components/com/page/container.vue')['default']
|
||||
ContainerBase: typeof import('./components/com/page/container-base.vue')['default']
|
||||
FloatBall: typeof import('./components/com/page/float-ball.vue')['default']
|
||||
HomeAdPopup: typeof import('./components/com/page/home-ad-popup.vue')['default']
|
||||
LabelField: typeof import('./components/com/appointment/label-field.vue')['default']
|
||||
LabelSlectField: typeof import('./components/com/appointment/label-slect-field.vue')['default']
|
||||
MahjongCard: typeof import('./components/index/MahjongCard.vue')['default']
|
||||
|
|
|
|||
71
components/com/page/float-ball.vue
Normal file
71
components/com/page/float-ball.vue
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<!-- 仅在有配置时显示悬浮球 -->
|
||||
<view v-if="floatConfig && floatConfig.image" class="float-ball" @click="openPopup">
|
||||
<image class="float-ball__img" :src="floatConfig.image" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 弹窗:居中展示图片,可关闭 -->
|
||||
<up-popup v-model:show="showPopup" mode="center" :closeable="true" :overlay="true" :safeAreaInsetBottom="false">
|
||||
<view class="popup-content">
|
||||
<image v-if="floatConfig && floatConfig.popupImage" class="popup-image" :src="floatConfig.popupImage" mode="widthFix" :show-menu-by-longpress="true"></image>
|
||||
</view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { getFloatConfig, getConfigData } from '@/common/server/config';
|
||||
|
||||
const floatConfig = ref(null);
|
||||
const showPopup = ref(false);
|
||||
|
||||
const loadConfig = async () => {
|
||||
// 确保配置已预加载
|
||||
await getConfigData();
|
||||
floatConfig.value = await getFloatConfig();
|
||||
};
|
||||
|
||||
const openPopup = () => {
|
||||
showPopup.value = true;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await loadConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.float-ball {
|
||||
position: fixed;
|
||||
right: 24rpx;
|
||||
bottom: 180rpx;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
z-index: 10080;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.15);
|
||||
background: rgba(255,255,255,0.9);
|
||||
}
|
||||
|
||||
.float-ball__img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
max-width: 640rpx;
|
||||
max-height: 80vh;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.popup-image {
|
||||
width: 640rpx;
|
||||
border-radius: 16rpx;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
54
components/com/page/home-ad-popup.vue
Normal file
54
components/com/page/home-ad-popup.vue
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<up-popup v-model:show="show" mode="center" :closeable="true" :overlay="true" :safeAreaInsetBottom="false">
|
||||
<view class="popup-content" @click.stop>
|
||||
<image v-if="popup && popup.image" class="popup-image" :src="popup.image" mode="widthFix"></image>
|
||||
</view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { getPopupConfig, getConfigData, getPopupShow } from '@/common/server/config';
|
||||
|
||||
const show = ref(false);
|
||||
const popup = ref(null);
|
||||
|
||||
const shouldShowOncePerLaunch = () => {
|
||||
try {
|
||||
const app = getApp();
|
||||
if (!app || !app.globalData) return true;
|
||||
if (app.globalData.hasShownHomeAd) return false;
|
||||
app.globalData.hasShownHomeAd = true;
|
||||
return true;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
await getConfigData();
|
||||
popup.value = await getPopupConfig();
|
||||
if (popup.value && popup.value.image && getPopupShow.value) {
|
||||
show.value = true;
|
||||
getPopupShow.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popup-content {
|
||||
max-width: 640rpx;
|
||||
max-height: 80vh;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.popup-image {
|
||||
width: 640rpx;
|
||||
border-radius: 16rpx;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -238,8 +238,8 @@ const handleJoin = () => {
|
|||
}
|
||||
|
||||
.grid-item {
|
||||
width: 320rpx;
|
||||
height: 460rpx;
|
||||
width: 300rpx;
|
||||
height: 405rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: -4rpx 12rpx 7rpx 0rpx rgba(0, 0, 0, 0.25);
|
||||
border-radius: 27rpx;
|
||||
|
|
@ -248,7 +248,7 @@ const handleJoin = () => {
|
|||
|
||||
// ==================== 麻将桌区域样式 ====================
|
||||
.mahjong-table-section {
|
||||
height: 251rpx;
|
||||
height: 230rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ const handleJoin = () => {
|
|||
.info-text {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 20rpx;
|
||||
font-size:18rpx;
|
||||
color: #575757;
|
||||
text-align: left;
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -57,22 +57,28 @@
|
|||
|
||||
<card-container marginTop="30rpx">
|
||||
<label-slect-field label="是否禁烟">
|
||||
<com-appointment-radio-select :options="smokingOptions" v-model="reservationInfo.is_smoking" />
|
||||
<view style="height: 61rpx;display: flex;justify-content: left;align-items: center;">
|
||||
<com-appointment-radio-select :options="smokingOptions" v-model="reservationInfo.is_smoking" />
|
||||
</view>
|
||||
</label-slect-field>
|
||||
<view :style="{ height: lineHeight }"></view>
|
||||
<label-slect-field label="性别">
|
||||
<com-appointment-radio-select :options="genderOptions" v-model="reservationInfo.gender_limit" />
|
||||
<view style="height: 61rpx;display: flex;justify-content: left;align-items: center;">
|
||||
<com-appointment-radio-select :options="genderOptions" v-model="reservationInfo.gender_limit" />
|
||||
</view>
|
||||
</label-slect-field>
|
||||
<view :style="{ height: lineHeight }"></view>
|
||||
<label-slect-field label="年龄范围">
|
||||
<view @click="openAgePicker" class="clickable-row">
|
||||
<view @click="openAgePicker"
|
||||
style="height: 61rpx;display: flex;justify-content: left;align-items: center;"
|
||||
class="clickable-row">
|
||||
{{ getAgeRangeText() }}
|
||||
</view>
|
||||
</label-slect-field>
|
||||
<view :style="{ height: lineHeight }"></view>
|
||||
<label-slect-field label="信誉">
|
||||
<view class="flex-center-row">
|
||||
<text class="inline-label">大于等于</text>
|
||||
<view class="flex-center-row" style="height: 61rpx;">
|
||||
<text class="inline-label" style="margin-top: 12rpx;">大于等于</text>
|
||||
<view class="counter-container">
|
||||
<view @click="decrement" :disabled="currentValue <= 0">
|
||||
<uni-icons type="minus" size="24" color="#00AC4E" />
|
||||
|
|
@ -97,13 +103,18 @@
|
|||
<text class="note-text">鸽子费(保证金),参与人需缴纳鸽子费。若有参与者在预约后没有赴约,其鸽子费由在场的所有人平分。组局成功或失败后鸽子费将全额返还。</text>
|
||||
</card-container>
|
||||
|
||||
<view class="center submit-button" @click="submitReservation">
|
||||
<text style="margin: 20rpx; color: white;">发起预约</text>
|
||||
<view class="action-row">
|
||||
<view class="center reset-button" @click="resetForm">
|
||||
<text style="margin: 20rpx;">重置</text>
|
||||
</view>
|
||||
<view class="center submit-button action-submit" @click="submitReservation">
|
||||
<text style="margin: 20rpx; color: white;">发起预约</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="center note-container" @click="tipsShow">
|
||||
|
||||
<text class="muted-text">组局成功后,发起者可通过店员领取线下红包</text>
|
||||
<!-- <text class="muted-text">组局成功后,发起者可通过店员领取线下红包</text> -->
|
||||
</view>
|
||||
|
||||
|
||||
|
|
@ -126,7 +137,7 @@
|
|||
class="center evaluate-btn" :data-item="reservation">
|
||||
<text class="evaluate-btn-text">分享给好友</text>
|
||||
</button>
|
||||
|
||||
|
||||
<view
|
||||
style="width: 50%;height: 100%;background-color: #f7f7f7;display: flex;align-items: center;justify-content: center;"
|
||||
@click="submitPopupRef.close();">
|
||||
|
|
@ -705,7 +716,7 @@ onLoad(async () => {
|
|||
}
|
||||
})
|
||||
onShow(async () => {
|
||||
resetForm();
|
||||
// resetForm();
|
||||
})
|
||||
// 年龄列构建与显示/文案
|
||||
const buildAgeColumns = () => {
|
||||
|
|
@ -806,6 +817,28 @@ const onAgePickerConfirm = (e) => {
|
|||
background-color: #00AC4E;
|
||||
}
|
||||
|
||||
/* 操作区一行布局:重置 + 发起 */
|
||||
.action-row {
|
||||
width: 90%;
|
||||
margin: 30rpx auto 0;
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.reset-button {
|
||||
flex: 0 0 30%;
|
||||
height: 88rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #FFFFFF;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.action-submit {
|
||||
flex: 1 1 auto;
|
||||
height: 88rpx;
|
||||
margin: 0; /* 覆盖原有 submit-button 的外边距,使其与 reset 同行 */
|
||||
}
|
||||
|
||||
/* 备注容器及文本 */
|
||||
.note-container {
|
||||
width: 90%;
|
||||
|
|
|
|||
|
|
@ -5,20 +5,23 @@
|
|||
:loading-more-enabled="true" :auto="true" :empty-view-text="'暂无麻将局数据'" :empty-view-img="'/static/empty.png'"
|
||||
:refresher-threshold="80" :loading-more-threshold="50" style="flex: 1; width: 95%; margin-top: 10rpx;">
|
||||
<template #top>
|
||||
|
||||
<view style="z-index: 100;position: sticky;top :0;">
|
||||
<view :style="{ height: statusBarHeight + 'px' }" style="background-color: #fff;"
|
||||
class="status-bar">
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</template>
|
||||
<template #empty>
|
||||
<com-index-NoEmpty></com-index-NoEmpty>
|
||||
</template>
|
||||
|
||||
<view style="z-index: 100;margin-top:10rpx;height:532rpx;">
|
||||
<view style="z-index: 100;margin-top:10rpx;height:330rpx;"
|
||||
v-if="homeData != null && homeData.advertList != null && homeData.advertList.length > 0">
|
||||
<view style="width: 100%;width:100%;display:flex;justify-content: center;"
|
||||
v-if="homeData != null && homeData.advertList != null && homeData.advertList.length > 0">
|
||||
<view class="" style="width:97%; height: 386rpx;border-radius: 25rpx;">
|
||||
<view class="" style="width:97%; height: 320rpx;border-radius: 25rpx;">
|
||||
<swiper class="img-swiper" :indicator-dots="false" circular autoplay interval="3000">
|
||||
<swiper-item v-for="(item, index) in homeData.advertList" :key="index">
|
||||
<image :src="item.imageUrl" mode="scaleToFill" class="slide-img"
|
||||
|
|
@ -28,8 +31,12 @@
|
|||
</swiper>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 10rpx;"></view>
|
||||
<view style="height:10rpx;"></view>
|
||||
</view>
|
||||
<up-notice-bar
|
||||
v-if="configData != null && configData.config != null && configData.config.homeAnnouncement != null && configData.config.homeAnnouncement.length > 0"
|
||||
:text="configData.config.homeAnnouncement" mode="closable" :speed="20"></up-notice-bar>
|
||||
<view style="height:15rpx;"></view>
|
||||
<view class="grid-container">
|
||||
<MahjongCard v-for="(item, index) in dataList" :key="index" :item="item" @click="openPop"
|
||||
@join="handleJoin" />
|
||||
|
|
@ -40,6 +47,12 @@
|
|||
<!-- 预约信息弹窗组件 -->
|
||||
<ReservationPopup ref="reservationPopup" @openUserPop="openUserPop" @refreshList="refreshData" />
|
||||
|
||||
<!-- 悬浮球组件(右下角) -->
|
||||
<com-page-float-ball />
|
||||
|
||||
<!-- 首页广告弹窗(每次启动仅显示一次) -->
|
||||
<com-page-home-ad-popup />
|
||||
|
||||
|
||||
|
||||
<uni-popup ref="userInfo_popup" type="center">
|
||||
|
|
@ -101,6 +114,8 @@ import { onShareAppMessage } from '@dcloudio/uni-app';
|
|||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import MahjongCard from '@/components/index/MahjongCard.vue'
|
||||
import ReservationPopup from '@/components/com/index/ReservationPopup.vue'
|
||||
import ComPageFloatBall from '@/components/com/page/float-ball.vue'
|
||||
import ComPageHomeAdPopup from '@/components/com/page/home-ad-popup.vue'
|
||||
import {
|
||||
homeData,
|
||||
preloadHomeData,
|
||||
|
|
@ -244,6 +259,9 @@ const handleJoin = (item) => {
|
|||
reservationPopup.value.close()
|
||||
}
|
||||
|
||||
// 注册组件
|
||||
defineExpose({})
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 初始化模拟数据
|
||||
|
|
@ -286,7 +304,7 @@ onLoad(async (option) => {
|
|||
|
||||
.img-swiper {
|
||||
width: 100%;
|
||||
height: 386rpx;
|
||||
height: 320rpx;
|
||||
}
|
||||
|
||||
.slide-img {
|
||||
|
|
@ -300,9 +318,9 @@ onLoad(async (option) => {
|
|||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
/* 2列,每列宽度相等 */
|
||||
gap: 20rpx;
|
||||
gap: 25rpx;
|
||||
/* 网格间距(行列间距相同) */
|
||||
padding: 0 15rpx;
|
||||
padding: 0 25rpx;
|
||||
}
|
||||
|
||||
/* 网格项内容 */
|
||||
|
|
|
|||
|
|
@ -344,8 +344,8 @@
|
|||
|
||||
<style lang="scss" scoped>
|
||||
$u-popup-flex:1 !default;
|
||||
$u-popup-content-background-color: #fff !default;
|
||||
|
||||
// $u-popup-content-background-color: #fff !default;
|
||||
$u-popup-content-background-color: transparent !default;
|
||||
.u-popup {
|
||||
flex: $u-popup-flex;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user