This commit is contained in:
zpc 2025-10-06 23:24:50 +08:00
parent 23b5ff20a2
commit d6895456ff
9 changed files with 246 additions and 23 deletions

View File

@ -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')

View File

@ -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
View File

@ -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']

View 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>

View 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>

View File

@ -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;

View File

@ -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%;

View File

@ -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;
}
/* 网格项内容 */

View File

@ -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;