活动按钮状态流转

This commit is contained in:
2026-03-25 15:53:37 +08:00
commit 37346e790f
2762 changed files with 240282 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 填写报名信息 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<page-meta :page-style="'overflow:' + (pageShow ? 'hidden' : 'visible')"></page-meta>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar :showBack="true" title="填写报名信息"></title-bar>
<!-- 内容区 -->
<view class="container-main" v-if="loadEnd">
<activity-apply ref="activityApply" :show-data="applyField" @onChange="pageChange"></activity-apply>
</view>
<!-- 底部按钮 -->
<view class="container-footer">
<view class="footer-btn" @click="heandleSubmit()">立即报名</view>
<view class="safe-padding"></view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
import activityApply from "@/pages/component/activity/apply.vue"
export default {
components: {
activityApply,
},
data() {
return {
// 页面是否阻止滚动
pageShow: false,
// 加载完成
loadEnd: false,
// 活动id
activityId: null,
// 入会字段
applyField: [],
// 用户手机号
userMobile: "",
}
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
})
},
onLoad(option) {
this.activityId = option.id
uni.showLoading({
title: "加载中"
})
this.getUserMobile()
this.getApplyField(() => {
this.loadEnd = true
uni.hideLoading()
})
},
methods: {
// 改变页面滚动状态
pageChange(state) {
this.pageShow = state
},
// 获取用户手机号
getUserMobile() {
this.$util.request("login.getMobile").then(res => {
if (res.code == 1) {
this.userMobile = res.data.mobile || ""
var index = this.applyField.findIndex(item => {
if (item.type == "number" && item.field == "mobile") return true
})
if (index > -1 && !this.applyField[index].value) {
this.applyField[index].value = this.userMobile
}
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
console.error('获取用户手机号 ', error)
})
},
// 获取报名字段
getApplyField(fn) {
this.$util.request("activity.field", {
id: this.activityId,
}).then(res => {
if (fn) fn()
if (res.code == 1) {
let list = res.data
list.forEach((item) => {
if (item.type == "checkbox") {
item.value = []
} else if (item.type == "image") {
item.value = []
} else if (item.type == "map") {
item.value = {
latitude: "",
longitude: "",
name: "",
address: ""
}
} else if (item.type == "number" && item.field == "mobile") {
item.value = this.userMobile || ""
} else {
item.value = ""
}
});
this.applyField = list
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
if (fn) fn()
console.error('获取报名字段 ', error)
})
},
// 提交申请
heandleSubmit() {
uni.showLoading({
title: "加载中",
mask: true
})
this.$refs.activityApply.getApplyField((data) => {
let fileList = []
for (let i in data) {
// 判断必填项是否为空
if (data[i].required == 1) {
let isEmpty = false
if (data[i].type == "checkbox") {
if (!data[i].value.length) isEmpty = true
} else if (data[i].type == "image") {
if (!data[i].value.length) isEmpty = true
} else if (data[i].type == "map") {
if (!data[i].value.address) isEmpty = true
} else {
if (!data[i].value && data[i].value !== 0) isEmpty = true
}
if (isEmpty) {
uni.hideLoading()
uni.showToast({
icon: "none",
title: data[i].label + "不能为空"
})
return
}
}
// 判断手机号是否合规
if (data[i].type == "number" && data[i].field == "mobile") {
if (!this.$util.validation("phone", data[i].value)) {
uni.hideLoading()
uni.showToast({
icon: "none",
title: "请输入正确的手机号"
})
return
}
}
// 设置字段值格式
if (data[i].type == "number") {
data[i].value = (data[i].value || data[i].value === 0) ? Number(data[i].value) : data[i].value
} else if (data[i].type == "checkbox") {
data[i].value = data[i].value.join()
} else if (data[i].type == "image") {
for (let j in data[i].value) {
fileList.push({
index: i,
number: j,
value: data[i].value[j]
})
}
} else if (data[i].type == "video" && data[i].value) {
fileList.push({
index: i,
value: data[i].value
})
}
}
if (fileList.length) {
this.uploadFiles(fileList, (files) => {
for (let i in fileList) {
if (data[fileList[i].index].type == "image") {
data[fileList[i].index].value[fileList[i].number] = files[i]
} else if (data[fileList[i].index].type == "video") {
data[fileList[i].index].value = files[i]
}
}
this.submitEvent(data)
})
} else {
this.submitEvent(data)
}
})
},
// 上传文件
uploadFiles(list, fn) {
this.$util.uploadFileMultiple(list.map(item => item.value)).then(result => {
fn(result)
}).catch(error => {
console.error('上传文件 ', error)
})
},
// 提交事件
submitEvent(formData) {
for (let i in formData) {
if (formData[i].type == "image") {
formData[i].value = formData[i].value.join()
}
}
this.$store.commit("app/setActivityField", JSON.stringify(formData))
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/order?id=" + this.activityId
})
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
padding: 32rpx 32rpx 144rpx;
}
.container-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 96;
background: #ffffff;
border-top: 1rpx solid #F6F7FB;
padding: 12rpx 24rpx;
.footer-btn {
color: #ffffff;
font-size: 32rpx;
line-height: 44rpx;
padding: 22rpx 24rpx;
border-radius: 16rpx;
background: var(--theme-color);
text-align: center;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,826 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 活动详情 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar :showBack="true" title="活动详情"></title-bar>
<!-- 内容区 -->
<view class="container-main">
<block v-if="loadEnd">
<!-- 轮播图 -->
<carousel :show-data="activityInfo.image_list" height="320rpx" radius="10rpx" bottom="24rpx" right="24rpx"></carousel>
<!-- 活动信息 -->
<view class="main-info">
<view class="info-header flex align-items-center" v-if="activityInfo.activity_state == 1">
<image class="header-bg" src="/static/activity/time_bg.png" mode="aspectFill"></image>
<image class="header-icon" src="/static/activity/time.png" mode="aspectFit"></image>
<view class="header-box flex-item flex align-items-center">
<view class="text flex-item">距离报名结束还有</view>
<view class="cell">{{countdown.day}}</view>
<view class="text"></view>
<view class="cell">{{countdown.hours}}</view>
<view class="text"></view>
<view class="cell">{{countdown.minutes}}</view>
<view class="text"></view>
<view class="cell">{{countdown.seconds}}</view>
<view class="text"></view>
</view>
</view>
<view class="info-main">
<view class="main-title">{{activityInfo.name}}</view>
<view class="main-row flex align-items-center">
<view class="price" v-if="parseFloat(activityInfo.fees || 0) > 0"><text></text>{{activityInfo.fees}}</view>
<view class="price" v-else>免费</view>
<view class="label">
<text class="type-1" v-if="activityInfo.activity_state == 1">报名中</text>
<text class="type-2" v-else-if="activityInfo.activity_state == 2">进行中</text>
<text class="type-3" v-else-if="activityInfo.activity_state == 3">已结束</text>
</view>
<view class="label">
<text v-if="activityInfo.organizing_method == 1">线上活动</text>
<text v-else-if="activityInfo.organizing_method == 2">线下活动</text>
</view>
</view>
<view class="main-label" v-if="activityInfo.points_status == 1">参加活动可得{{activityInfo.points || 0}}积分</view>
<view class="main-column flex align-items-center">
<view class="column-icon" :style="{'background-image': 'url('+ iconTime +')'}" v-if="iconTime"></view>
<view class="column-text flex-item">{{activityInfo.time_frame}}</view>
</view>
<view class="main-column flex align-items-start" v-if="activityInfo.organizing_method == 2 && activityInfo.address">
<view class="column-icon" :style="{'background-image': 'url('+ iconLocation +')'}" v-if="iconLocation"></view>
<view class="column-text flex-item">{{activityInfo.address}}</view>
<view class="column-navigation flex align-items-center" @click="toNavigation()">
<view class="icon" :style="{'background-image': 'url('+ iconNavigation +')'}" v-if="iconNavigation"></view>
<text class="text">导航</text>
</view>
</view>
</view>
</view>
<!-- 已报名 -->
<view class="main-record flex justify-content-between align-items-center" v-if="activityInfo.apply_count">
<view class="record-bubble">已报名{{activityInfo.apply_count}}</view>
<view class="record-list flex">
<view class="list-item" v-for="(item, index) in activityInfo.apply_list" :key="index">
<image :src="item.member_avatar" mode="aspectFill"></image>
</view>
<view class="list-item" v-if="parseInt(activityInfo.apply_count || 0) > 9">
<view class="item-more flex justify-content-around align-items-center">
<view class="point"></view>
<view class="point"></view>
<view class="point"></view>
</view>
</view>
</view>
</view>
<!-- 活动介绍 -->
<view class="main-content">
<mp-html :content="activityInfo.content"></mp-html>
</view>
<!-- 底部按钮 -->
<view class="main-footer">
<view class="flex justify-content-between align-items-center">
<view class="footer-menu flex">
<!-- #ifdef MP-WEIXIN -->
<button type="default" open-type="share" class="menu-btn">
<image class="icon" src="/static/share.png" mode="aspectFit"></image>
<view class="text">分享</view>
</button>
<!-- #endif -->
<view class="menu-btn" @click="onContact">
<image class="icon" src="/static/phone.png" mode="aspectFit"></image>
<view class="text">联系</view>
</view>
</view>
<!-- 用户退款中 -->
<block v-if="activityInfo.refund == 1 && activityInfo.pay_state == 3">
<view class="footer-btn flex-item disabled">退款中</view>
</block>
<!-- 用户已报名 -->
<block v-else-if="activityInfo.apply_status == 1">
<view class="footer-btn flex-item" :class="{disabled: activityInfo.activity_state != 1}" @click="handleApply()">已报名</view>
</block>
<!-- 用户未报名 -->
<block v-else>
<!-- 活动报名中 -->
<block v-if="activityInfo.activity_state == 1">
<!-- 存在人数限制 -->
<block v-if="activityInfo.apply_limit_number || activityInfo.apply_limit_number === 0">
<!-- 有剩余名额 -->
<block v-if="parseInt(activityInfo.apply_limit_number) > 0">
<view class="footer-btn flex-item flex flex-center" @click="handleApply()" v-if="!token || userMobile">
<text>立即报名</text>
<text style="font-size: 24rpx;">剩余{{activityInfo.apply_limit_number}}个名额</text>
</view>
<button class="footer-btn flex-item clear flex flex-center" open-type="getPhoneNumber" @getphonenumber="bindPhoneNumber" v-else>
<text>立即报名</text>
<text style="font-size: 24rpx;">剩余{{activityInfo.apply_limit_number}}个名额</text>
</button>
</block>
<!-- 无剩余名额 -->
<view class="footer-btn flex-item disabled" v-else>
<text>无法报名</text>
<text style="font-size: 24rpx;">剩余0个名额</text>
</view>
</block>
<!-- 不存在人数限制 -->
<block v-else>
<view class="footer-btn flex-item flex flex-center" @click="handleApply()" v-if="!token || userMobile">立即报名</view>
<button class="footer-btn flex-item clear flex flex-center" open-type="getPhoneNumber" @getphonenumber="bindPhoneNumber" v-else>立即报名</button>
</block>
</block>
<!-- 活动报名结束进行中/已结束 -->
<view class="footer-btn flex-item disabled" v-else>报名已结束</view>
</block>
</view>
<view class="safe-padding"></view>
</view>
</block>
<view class="main-login" v-else-if="showLogin">
<image class="login-image" :src="loginImg" mode="aspectFit"></image>
<view class="login-tips">小程序需要登录注册才能使用相关功能请登录后查看该页面</view>
<view class="login-btn" @click="toLogin()">前往登录</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
import carousel from "@/pages/component/carousel/carousel.vue"
import svgData from "@/common/svg.js"
// #ifdef H5
import wx from 'weixin-js-sdk';
// #endif
export default {
components: {
carousel,
},
data() {
return {
// 加载完成
loadEnd: false,
// 活动id
activityId: null,
// 活动详情
activityInfo: {},
// 活动剩余时间计时器
activityInterval: null,
// 活动倒计时
countdown: {
day: 0,
hours: 0,
minutes: 0,
seconds: 0,
},
// 是否显示登录提示
showLogin: false,
}
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
iconTime: state => {
return svgData.svgToUrl("time", state.app.themeColor)
},
iconLocation: state => {
return svgData.svgToUrl("location", state.app.themeColor)
},
iconNavigation: state => {
return svgData.svgToUrl("navigation", state.app.themeColor)
},
loginImg: state => state.app.loginImg,
token: state => state.user.token,
userMobile: state => state.user.mobile,
})
},
onLoad(option) {
this.activityId = option.id || option.scene
uni.showLoading({
title: "加载中"
})
this.getActivity(() => {
this.loadEnd = true
uni.hideLoading()
// #ifdef H5
this.initConfig()
// #endif
})
},
onShow() {
if (this.loadEnd) this.getActivity()
},
onShareAppMessage() {
return {
title: this.activityInfo.name,
path: '/pagesActivity/index/details?id=' + this.activityId,
imageUrl: this.activityInfo.image_list[0],
}
},
onShareTimeline() {
return {
title: this.activityInfo.name,
path: '/pagesActivity/index/details?id=' + this.activityId,
imageUrl: this.activityInfo.image_list[0],
}
},
onUnload() {
clearInterval(this.activityInterval)
},
methods: {
// 获取活动详情
getActivity(fn) {
this.$util.request("activity.details", {
id: this.activityId
}).then(res => {
if (res.code == 1) {
if (res.data.activity_auth == 2) {
this.getMemberState(1, () => {
if (fn) fn()
this.activityInfo = res.data
this.activityInfo.time_frame = this.getTimeFrame(res.data.start_time, res.data.end_time)
if (this.activityInfo.images) this.activityInfo.image_list = this.activityInfo.images.split(",")
else this.activityInfo.image_list = []
this.getCountdown()
})
} else {
if (fn) fn()
this.activityInfo = res.data
this.activityInfo.time_frame = this.getTimeFrame(res.data.start_time, res.data.end_time)
if (this.activityInfo.images) this.activityInfo.image_list = this.activityInfo.images.split(",")
else this.activityInfo.image_list = []
this.getCountdown()
}
} else {
if (fn) fn()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
if (error == 401) {
this.showLogin = true
} else {
console.error('获取活动详情 ', error)
}
})
},
// 获取会员状态
getMemberState(type, fn) {
this.$util.request("member.state").then(res => {
if (res.code == 1) {
if (res.data.state.state == 6) {
fn()
} else if (res.data.state.state == -1) {
uni.hideLoading()
uni.showModal({
title: "系统提示",
content: type == 2 ? "该活动需成为会员后可报名" : "此页面需成为会员后可查看!",
confirmColor: this.themeColor,
confirmText: "去加入",
success: (res) => {
if (res.confirm) {
this.$util.toPage({
mode: 1,
path: "/pages/member/apply/index"
})
}
}
})
} else {
uni.hideLoading()
uni.showModal({
title: "系统提示",
content: type == 2 ? "该活动需成为会员后可报名" : "此页面需成为会员后可查看!",
confirmColor: this.themeColor,
confirmText: "前往查看",
success: (res) => {
if (res.confirm) {
uni.switchTab({
url: "/pages/mine/index"
})
}
}
})
}
} else {
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('获取会员状态 ', error)
})
},
// 获取活动剩余时间
getCountdown() {
let nowTime = new Date().getTime()
this.countdown = this.$util.getTimeDifference(nowTime, this.activityInfo.apply_time * 1000)
if (this.countdown.day == 0 && this.countdown.hours == 0 && this.countdown.minutes == 0 && this.countdown.seconds == 0) {
this.activityInfo.activity_state = 2
clearInterval(this.activityInterval)
} else {
this.activityInterval = setInterval(() => {
let nowTime = new Date().getTime()
this.countdown = this.$util.getTimeDifference(nowTime, this.activityInfo.apply_time * 1000)
if (this.countdown.day == 0 && this.countdown.hours == 0 && this.countdown.minutes == 0 && this.countdown.seconds == 0) {
this.activityInfo.activity_state = 2
clearInterval(this.activityInterval)
}
}, 1000);
}
},
// 获取时间范围
getTimeFrame(start, end) {
let startTime = this.$util.formatDate(start, "object")
let endTime = this.$util.formatDate(end, "object")
let startResult = `${startTime.year}-${startTime.month}-${startTime.day} ${startTime.hours}:${startTime.minutes}`
let endResult = `${endTime.year}-${endTime.month}-${endTime.day} ${endTime.hours}:${endTime.minutes}`
return startResult + "—" + endResult
},
// 跳转地图导航
toNavigation() {
this.$util.toPage({
mode: 7,
address: {
latitude: this.activityInfo.latitude,
longitude: this.activityInfo.longitude,
address: this.activityInfo.address,
},
})
},
// #ifdef H5
// 微信公众号初始化方法
initConfig() {
this.$util.request("main.WeChatConfig", {
url: location.href.split('#')[0]
}).then(res => {
if (res.code == 1) {
wx.config({
debug: false,
appId: res.data.appId,
timestamp: Number(res.data.timestamp),
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ["updateAppMessageShareData", "updateTimelineShareData", "wx-open-launch-weapp"],
openTagList: ["updateAppMessageShareData", "updateTimelineShareData", 'wx-open-launch-weapp'],
})
wx.ready(() => {
wx.updateAppMessageShareData({
title: this.activityInfo.name,
desc: "",
link: window.location.href,
imgUrl: this.activityInfo.image_list[0],
});
wx.updateTimelineShareData({
title: this.activityInfo.name,
link: window.location.href,
imgUrl: this.activityInfo.image_list[0],
});
});
} else {
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('通过config接口注入权限验证配置 ', error)
})
},
// #endif
// 联系
onContact() {
this.$util.toPage({
mode: 6,
phone: this.activityInfo.mobile,
})
},
// 立即报名
handleApply() {
if (!uni.getStorageSync("token")) {
uni.navigateTo({
url: "/pages/login/index",
animationType: "fade-in"
})
return
}
if (this.activityInfo.non_member_registration_status == 2) {
uni.showLoading({
title: "加载中",
mask: true,
})
this.getMemberState(2, () => {
uni.hideLoading()
this.toApplication()
})
} else {
this.toApplication()
}
},
// 跳转报名页面
toApplication() {
if (this.activityInfo.apply_status == 1) {
uni.showModal({
content: "您已报名此活动,是否前往查看?",
confirmColor: this.themeColor,
confirmText: "前往查看",
success: (res) => {
if (res.confirm) {
this.$util.toPage({
mode: 2,
path: `/pagesActivity/order/details?id=${this.activityInfo.apply_id}&activity_id=${this.activityId}`
})
}
}
})
} else {
if (this.activityInfo.apply_field_state == 1 && this.activityInfo.apply_info_fill_state != 1) {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/apply?id=" + this.activityId
})
} else {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/order?id=" + this.activityId
})
}
}
},
// 绑定手机号
bindPhoneNumber(e) {
if (e.detail.errMsg == "getPhoneNumber:ok") {
uni.showLoading({
mask: true,
title: "加载中",
})
uni.login({
provider: 'weixin',
success: loginRes => {
let data = e.detail
data.code = loginRes.code
this.$util.request("login.bindMobile", data).then(res => {
uni.hideLoading()
if (res.code == 1) {
this.$store.commit('user/updateMobile', res.data.phoneNumber)
this.handleApply()
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('获取用户手机号码 ', error)
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
icon: "none",
title: "授权手机号失败,请重试"
})
}
});
} else {
uni.showToast({
title: '获取手机号失败,请重新获取',
icon: 'none'
})
}
},
// 前往登录
toLogin() {
uni.redirectTo({
url: `/pagesActivity/index/details?id=${this.activityId}`,
})
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
padding: 32rpx 32rpx 144rpx;
.main-info {
border-radius: 16rpx;
background: #ffffff;
overflow: hidden;
margin-top: 32rpx;
.info-header {
background: linear-gradient(134.71deg, var(--theme-color) -1.001%, #ffffff 300%);
padding: 24rpx 32rpx;
position: relative;
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.header-icon {
width: 48rpx;
height: 48rpx;
position: relative;
z-index: 1;
}
.header-box {
position: relative;
z-index: 1;
.text {
color: #ffffff;
font-size: 24rpx;
line-height: 34rpx;
margin-left: 8rpx;
}
.cell {
color: #ffffff;
font-size: 24rpx;
height: 48rpx;
line-height: 48rpx;
padding: 0 8rpx;
min-width: 48rpx;
border-radius: 4rpx;
backdrop-filter: blur(20rpx);
background: rgba(255, 255, 255, 0.4);
margin-left: 8rpx;
text-align: center;
}
}
}
.info-main {
padding: 32rpx;
.main-title {
color: #5A5B6E;
font-size: 32rpx;
font-weight: 600;
line-height: 44rpx;
}
.main-row {
margin-top: 24rpx;
.price {
color: var(--theme-color);
font-size: 40rpx;
font-weight: 600;
line-height: 50rpx;
text {
font-size: 22rpx
}
}
.label {
margin-left: 16rpx;
text {
display: block;
color: var(--theme-color);
font-size: 24rpx;
line-height: 34rpx;
padding: 6rpx 14rpx;
border: 2rpx solid var(--theme-color);
border-radius: 4rpx;
}
.type-1 {
color: #FFA820;
border-color: #FFA820;
}
.type-2 {
color: #00AE84;
border-color: #00AE84;
}
.type-3 {
color: #E60012;
border-color: #E60012;
}
}
}
.main-label {
margin-top: 24rpx;
color: #FF8112;
font-size: 24rpx;
line-height: 36rpx;
padding: 14rpx 24rpx;
border-radius: 16rpx;
background: #FFF9EF;
}
.main-column {
margin-top: 24rpx;
.column-icon {
width: 32rpx;
height: 40rpx;
background-size: 32rpx 40rpx;
}
.column-text {
margin-left: 10rpx;
color: #666666;
font-size: 28rpx;
line-height: 40rpx;
}
.column-navigation {
margin-left: 16rpx;
.icon {
width: 32rpx;
height: 32rpx;
background-size: 32rpx;
}
.text {
margin-left: 8rpx;
color: var(--theme-color);
font-size: 28rpx;
line-height: 40rpx;
}
}
}
}
}
.main-record {
padding: 12rpx 32rpx;
border-radius: 16rpx;
background: #ffffff;
margin-top: 32rpx;
.record-bubble {
color: #ffffff;
font-size: 20rpx;
line-height: 28rpx;
padding: 8rpx 16rpx;
background: var(--theme-color);
border-radius: 8rpx;
display: flex;
align-items: center;
position: relative;
&::after {
content: "";
display: block;
position: absolute;
top: 50%;
right: -10rpx;
transform: translateY(-50%);
width: 0;
height: 0;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
border-left: 12rpx solid var(--theme-color);
}
}
.record-list {
padding: 16rpx;
width: 452rpx;
border-radius: 8rpx;
// background: #F6F7FB;
.list-item {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
overflow: hidden;
margin-left: -7rpx;
border: 2rpx solid #ffffff;
.item-more {
width: 100%;
height: 100%;
background: var(--theme-color);
padding: 0 6rpx;
.point {
width: 6rpx;
height: 6rpx;
background: #ffffff;
border-radius: 50%;
}
}
}
}
}
.main-content {
padding: 32rpx;
border-radius: 16rpx;
background: #ffffff;
color: #5A5B6E;
font-size: 28rpx;
line-height: 48rpx;
margin-top: 32rpx;
}
.main-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
padding: 12rpx 32rpx 12rpx 48rpx;
background: #ffffff;
border-top: 1rpx solid #F6F7FB;
.footer-menu {
.menu-btn {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 32rpx;
background: transparent;
padding: 0;
.icon {
width: 52rpx;
height: 52rpx;
}
.text {
color: #5A5B6E;
font-size: 20rpx;
line-height: 28rpx;
}
}
}
.footer-btn {
color: #ffffff;
font-size: 32rpx;
line-height: 44rpx;
padding: 22rpx 24rpx;
border-radius: 16rpx;
background: var(--theme-color);
text-align: center;
&.disabled {
background: #8D929C;
}
}
}
.main-login {
padding: 64rpx 28rpx 0;
.login-image {
width: 100%;
height: 500rpx;
}
.login-tips {
color: #585858;
font-size: 36rpx;
line-height: 50rpx;
margin-top: 48rpx;
text-align: center;
}
.login-btn {
margin-top: 56rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 28rpx;
border-radius: 16rpx;
text-align: center;
background: var(--theme-color);
color: #ffffff;
}
}
}
}
</style>

View File

@@ -0,0 +1,835 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 活动详情 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar :showBack="true" title="活动详情"></title-bar>
<!-- 内容区 -->
<view class="container-main">
<block v-if="loadEnd">
<!-- 轮播图 -->
<carousel :show-data="activityInfo.image_list" height="320rpx" radius="10rpx" bottom="24rpx" right="24rpx"></carousel>
<!-- 活动信息 -->
<view class="main-info">
<view class="info-header flex align-items-center" v-if="activityInfo.activity_state == 1">
<image class="header-bg" src="/static/activity/time_bg.png" mode="aspectFill"></image>
<image class="header-icon" src="/static/activity/time.png" mode="aspectFit"></image>
<view class="header-box flex-item flex align-items-center">
<view class="text flex-item">距离报名结束还有</view>
<view class="cell">{{countdown.day}}</view>
<view class="text"></view>
<view class="cell">{{countdown.hours}}</view>
<view class="text"></view>
<view class="cell">{{countdown.minutes}}</view>
<view class="text"></view>
<view class="cell">{{countdown.seconds}}</view>
<view class="text"></view>
</view>
</view>
<view class="info-main">
<view class="main-title">{{activityInfo.name}}</view>
<view class="main-row flex align-items-center">
<view class="price" v-if="parseFloat(activityInfo.fees || 0) > 0"><text></text>{{activityInfo.fees}}</view>
<view class="price" v-else>免费</view>
<view class="label">
<text class="type-1" v-if="activityInfo.activity_state == 1">报名中</text>
<text class="type-2" v-else-if="activityInfo.activity_state == 2">进行中</text>
<text class="type-3" v-else-if="activityInfo.activity_state == 3">已结束</text>
</view>
<view class="label">
<text v-if="activityInfo.organizing_method == 1">线上活动</text>
<text v-else-if="activityInfo.organizing_method == 2">线下活动</text>
</view>
</view>
<view class="main-label" v-if="activityInfo.points_status == 1">参加活动可得{{activityInfo.points || 0}}积分</view>
<view class="main-column flex align-items-center">
<view class="column-icon" :style="{'background-image': 'url('+ iconTime +')'}" v-if="iconTime"></view>
<view class="column-text flex-item">{{activityInfo.time_frame}}</view>
</view>
<view class="main-column flex align-items-start" v-if="activityInfo.organizing_method == 2 && activityInfo.address">
<view class="column-icon" :style="{'background-image': 'url('+ iconLocation +')'}" v-if="iconLocation"></view>
<view class="column-text flex-item">{{activityInfo.address}}</view>
<view class="column-navigation flex align-items-center" @click="toNavigation()">
<view class="icon" :style="{'background-image': 'url('+ iconNavigation +')'}" v-if="iconNavigation"></view>
<text class="text">导航</text>
</view>
</view>
</view>
</view>
<!-- 已报名 -->
<view class="main-record flex justify-content-between align-items-center" v-if="activityInfo.apply_count">
<view class="record-bubble">已报名{{activityInfo.apply_count}}</view>
<view class="record-list flex">
<view class="list-item" v-for="(item, index) in activityInfo.apply_list" :key="index">
<image :src="item.member_avatar" mode="aspectFill"></image>
</view>
<view class="list-item" v-if="parseInt(activityInfo.apply_count || 0) > 9">
<view class="item-more flex justify-content-around align-items-center">
<view class="point"></view>
<view class="point"></view>
<view class="point"></view>
</view>
</view>
</view>
</view>
<!-- 活动介绍 -->
<view class="main-content">
<mp-html :content="activityInfo.content"></mp-html>
</view>
<!-- 底部按钮 -->
<view class="main-footer">
<view class="flex justify-content-between align-items-center">
<view class="footer-menu flex">
<!-- #ifdef MP-WEIXIN -->
<button type="default" open-type="share" class="menu-btn">
<image class="icon" src="/static/share.png" mode="aspectFit"></image>
<view class="text">分享</view>
</button>
<!-- #endif -->
<view class="menu-btn" @click="onContact">
<image class="icon" src="/static/phone.png" mode="aspectFit"></image>
<view class="text">联系</view>
</view>
</view>
<!-- 用户退款中 -->
<block v-if="activityInfo.refund == 1 && activityInfo.pay_state == 3">
<view class="footer-btn flex-item disabled">退款中</view>
</block>
<!-- 用户已报名 -->
<block v-else-if="activityInfo.apply_status == 1">
<view class="footer-btn flex-item" :class="{disabled: activityInfo.activity_state != 1}" @click="handleApply()">已报名</view>
</block>
<!-- 用户未报名 -->
<block v-else>
<!-- 活动报名中 -->
<block v-if="activityInfo.activity_state == 1">
<!-- 存在人数限制 -->
<block v-if="activityInfo.apply_limit_number || activityInfo.apply_limit_number === 0">
<!-- 有剩余名额 -->
<block v-if="parseInt(activityInfo.apply_limit_number) > 0">
<view class="footer-btn flex-item flex flex-center" @click="handleApply()" v-if="!token || userMobile">
<text>立即报名</text>
<text style="font-size: 24rpx;">剩余{{activityInfo.apply_limit_number}}个名额</text>
</view>
<button class="footer-btn flex-item clear flex flex-center" open-type="getPhoneNumber" @getphonenumber="bindPhoneNumber" v-else>
<text>立即报名</text>
<text style="font-size: 24rpx;">剩余{{activityInfo.apply_limit_number}}个名额</text>
</button>
</block>
<!-- 无剩余名额 -->
<view class="footer-btn flex-item disabled" v-else>
<text>无法报名</text>
<text style="font-size: 24rpx;">剩余0个名额</text>
</view>
</block>
<!-- 不存在人数限制 -->
<block v-else>
<view class="footer-btn flex-item flex flex-center" @click="handleApply()" v-if="!token || userMobile">立即报名</view>
<button class="footer-btn flex-item clear flex flex-center" open-type="getPhoneNumber" @getphonenumber="bindPhoneNumber" v-else>立即报名</button>
</block>
</block>
<!-- 活动报名结束进行中/已结束 -->
<view class="footer-btn flex-item disabled" v-else>报名已结束</view>
</block>
</view>
<view class="safe-padding"></view>
</view>
</block>
<view class="main-login" v-else-if="showLogin">
<image class="login-image" :src="loginImg" mode="aspectFit"></image>
<view class="login-tips">小程序需要登录注册才能使用相关功能请登录后查看该页面</view>
<view class="login-btn" @click="toLogin()">前往登录</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
import carousel from "@/pages/component/carousel/carousel.vue"
import svgData from "@/common/svg.js"
// #ifdef H5
import wx from 'weixin-js-sdk';
// #endif
export default {
components: {
carousel,
},
data() {
return {
// 加载完成
loadEnd: false,
// 活动id
activityId: null,
// 活动详情
activityInfo: {},
// 活动剩余时间计时器
activityInterval: null,
// 活动倒计时
countdown: {
day: 0,
hours: 0,
minutes: 0,
seconds: 0,
},
// 是否显示登录提示
showLogin: false,
//活动状态
activityStatus:1
}
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
iconTime: state => {
return svgData.svgToUrl("time", state.app.themeColor)
},
iconLocation: state => {
return svgData.svgToUrl("location", state.app.themeColor)
},
iconNavigation: state => {
return svgData.svgToUrl("navigation", state.app.themeColor)
},
loginImg: state => state.app.loginImg,
token: state => state.user.token,
userMobile: state => state.user.mobile,
})
},
onLoad(option) {
this.activityId = option.id || option.scene
uni.showLoading({
title: "加载中"
})
this.getActivity(() => {
this.loadEnd = true
uni.hideLoading()
// #ifdef H5
this.initConfig()
// #endif
})
},
onShow() {
if (this.loadEnd) this.getActivity()
},
onShareAppMessage() {
return {
title: this.activityInfo.name,
path: '/pagesActivity/index/details?id=' + this.activityId,
imageUrl: this.activityInfo.image_list[0],
}
},
onShareTimeline() {
return {
title: this.activityInfo.name,
path: '/pagesActivity/index/details?id=' + this.activityId,
imageUrl: this.activityInfo.image_list[0],
}
},
onUnload() {
clearInterval(this.activityInterval)
},
methods: {
// 获取活动详情
getActivity(fn) {
this.$util.request("activity.details", {
id: this.activityId
}).then(res => {
if (res.code == 1) {
if (res.data.activity_auth == 2) {
this.getMemberState(1, () => {
if (fn) fn()
this.activityInfo = res.data
this.activityInfo.time_frame = this.getTimeFrame(res.data.start_time, res.data.end_time)
if (this.activityInfo.images) this.activityInfo.image_list = this.activityInfo.images.split(",")
else this.activityInfo.image_list = []
this.getCountdown()
})
} else {
if (fn) fn()
this.activityInfo = res.data
this.activityInfo.time_frame = this.getTimeFrame(res.data.start_time, res.data.end_time)
if (this.activityInfo.images) this.activityInfo.image_list = this.activityInfo.images.split(",")
else this.activityInfo.image_list = []
this.getCountdown()
}
} else {
if (fn) fn()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
if (error == 401) {
this.showLogin = true
} else {
console.error('获取活动详情 ', error)
}
})
},
// 获取会员状态
getMemberState(type, fn) {
this.$util.request("member.state").then(res => {
if (res.code == 1) {
if (res.data.state.state == 6) {
fn()
} else if (res.data.state.state == -1) {
uni.hideLoading()
uni.showModal({
title: "系统提示",
content: type == 2 ? "该活动需成为会员后可报名" : "此页面需成为会员后可查看!",
confirmColor: this.themeColor,
confirmText: "去加入",
success: (res) => {
if (res.confirm) {
this.$util.toPage({
mode: 1,
path: "/pages/member/apply/index"
})
}
}
})
} else {
uni.hideLoading()
uni.showModal({
title: "系统提示",
content: type == 2 ? "该活动需成为会员后可报名" : "此页面需成为会员后可查看!",
confirmColor: this.themeColor,
confirmText: "前往查看",
success: (res) => {
if (res.confirm) {
uni.switchTab({
url: "/pages/mine/index"
})
}
}
})
}
} else {
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('获取会员状态 ', error)
})
},
// 获取活动剩余时间
getCountdown() {
let nowTime = new Date().getTime()
this.countdown = this.$util.getTimeDifference(nowTime, this.activityInfo.apply_time * 1000)
if (this.countdown.day == 0 && this.countdown.hours == 0 && this.countdown.minutes == 0 && this.countdown.seconds == 0) {
this.activityInfo.activity_state = 2
clearInterval(this.activityInterval)
} else {
this.activityInterval = setInterval(() => {
let nowTime = new Date().getTime()
this.countdown = this.$util.getTimeDifference(nowTime, this.activityInfo.apply_time * 1000)
if (this.countdown.day == 0 && this.countdown.hours == 0 && this.countdown.minutes == 0 && this.countdown.seconds == 0) {
this.activityInfo.activity_state = 2
clearInterval(this.activityInterval)
}
}, 1000);
}
},
// 获取时间范围
getTimeFrame(start, end) {
let startTime = this.$util.formatDate(start, "object")
let endTime = this.$util.formatDate(end, "object")
let startResult = `${startTime.year}-${startTime.month}-${startTime.day} ${startTime.hours}:${startTime.minutes}`
let endResult = `${endTime.year}-${endTime.month}-${endTime.day} ${endTime.hours}:${endTime.minutes}`
return startResult + "—" + endResult
},
// 跳转地图导航
toNavigation() {
this.$util.toPage({
mode: 7,
address: {
latitude: this.activityInfo.latitude,
longitude: this.activityInfo.longitude,
address: this.activityInfo.address,
},
})
},
// #ifdef H5
// 微信公众号初始化方法
initConfig() {
this.$util.request("main.WeChatConfig", {
url: location.href.split('#')[0]
}).then(res => {
if (res.code == 1) {
wx.config({
debug: false,
appId: res.data.appId,
timestamp: Number(res.data.timestamp),
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ["updateAppMessageShareData", "updateTimelineShareData", "wx-open-launch-weapp"],
openTagList: ["updateAppMessageShareData", "updateTimelineShareData", 'wx-open-launch-weapp'],
})
wx.ready(() => {
wx.updateAppMessageShareData({
title: this.activityInfo.name,
desc: "",
link: window.location.href,
imgUrl: this.activityInfo.image_list[0],
});
wx.updateTimelineShareData({
title: this.activityInfo.name,
link: window.location.href,
imgUrl: this.activityInfo.image_list[0],
});
});
} else {
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('通过config接口注入权限验证配置 ', error)
})
},
// #endif
// 联系
onContact() {
this.$util.toPage({
mode: 6,
phone: this.activityInfo.mobile,
})
},
// 立即报名
handleApply() {
if (!uni.getStorageSync("token")) {
uni.navigateTo({
url: "/pages/login/index",
animationType: "fade-in"
})
return
}
if (this.activityInfo.non_member_registration_status == 2) {
uni.showLoading({
title: "加载中",
mask: true,
})
this.getMemberState(2, () => {
uni.hideLoading()
this.toApplication()
})
} else {
this.toApplication()
}
},
// 跳转报名页面
toApplication() {
if (this.activityInfo.apply_status == 1) {
uni.showModal({
content: "您已报名此活动,是否前往查看?",
confirmColor: this.themeColor,
confirmText: "前往查看",
success: (res) => {
if (res.confirm) {
this.$util.toPage({
mode: 2,
path: `/pagesActivity/order/details?id=${this.activityInfo.apply_id}&activity_id=${this.activityId}`
})
}
}
})
} else {
if (this.activityInfo.apply_field_state == 1 && this.activityInfo.apply_info_fill_state != 1) {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/apply?id=" + this.activityId
})
} else {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/order?id=" + this.activityId
})
}
}
},
// 绑定手机号
bindPhoneNumber(e) {
if (e.detail.errMsg == "getPhoneNumber:ok") {
uni.showLoading({
mask: true,
title: "加载中",
})
uni.login({
provider: 'weixin',
success: loginRes => {
let data = e.detail
data.code = loginRes.code
this.$util.request("login.bindMobile", data).then(res => {
uni.hideLoading()
if (res.code == 1) {
this.$store.commit('user/updateMobile', res.data.phoneNumber)
this.handleApply()
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('获取用户手机号码 ', error)
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
icon: "none",
title: "授权手机号失败,请重试"
})
}
});
} else {
uni.showToast({
title: '获取手机号失败,请重新获取',
icon: 'none'
})
}
},
// 前往登录
toLogin() {
uni.redirectTo({
url: `/pagesActivity/index/details?id=${this.activityId}`,
})
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
padding: 32rpx 32rpx 144rpx;
.main-info {
border-radius: 16rpx;
background: #ffffff;
overflow: hidden;
margin-top: 32rpx;
.info-header {
background: linear-gradient(134.71deg, var(--theme-color) -1.001%, #ffffff 300%);
padding: 24rpx 32rpx;
position: relative;
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.header-icon {
width: 48rpx;
height: 48rpx;
position: relative;
z-index: 1;
}
.header-box {
position: relative;
z-index: 1;
.text {
color: #ffffff;
font-size: 24rpx;
line-height: 34rpx;
margin-left: 8rpx;
}
.cell {
color: #ffffff;
font-size: 24rpx;
height: 48rpx;
line-height: 48rpx;
padding: 0 8rpx;
min-width: 48rpx;
border-radius: 4rpx;
backdrop-filter: blur(20rpx);
background: rgba(255, 255, 255, 0.4);
margin-left: 8rpx;
text-align: center;
}
}
}
.info-main {
padding: 32rpx;
.main-title {
color: #5A5B6E;
font-size: 32rpx;
font-weight: 600;
line-height: 44rpx;
}
.main-row {
margin-top: 24rpx;
.price {
color: var(--theme-color);
font-size: 40rpx;
font-weight: 600;
line-height: 50rpx;
text {
font-size: 22rpx
}
}
.label {
margin-left: 16rpx;
text {
display: block;
color: var(--theme-color);
font-size: 24rpx;
line-height: 34rpx;
padding: 6rpx 14rpx;
border: 2rpx solid var(--theme-color);
border-radius: 4rpx;
}
.type-1 {
color: #FFA820;
border-color: #FFA820;
}
.type-2 {
color: #00AE84;
border-color: #00AE84;
}
.type-3 {
color: #E60012;
border-color: #E60012;
}
}
}
.main-label {
margin-top: 24rpx;
color: #FF8112;
font-size: 24rpx;
line-height: 36rpx;
padding: 14rpx 24rpx;
border-radius: 16rpx;
background: #FFF9EF;
}
.main-column {
margin-top: 24rpx;
.column-icon {
width: 32rpx;
height: 40rpx;
background-size: 32rpx 40rpx;
}
.column-text {
margin-left: 10rpx;
color: #666666;
font-size: 28rpx;
line-height: 40rpx;
}
.column-navigation {
margin-left: 16rpx;
.icon {
width: 32rpx;
height: 32rpx;
background-size: 32rpx;
}
.text {
margin-left: 8rpx;
color: var(--theme-color);
font-size: 28rpx;
line-height: 40rpx;
}
}
}
}
}
.main-record {
padding: 12rpx 32rpx;
border-radius: 16rpx;
background: #ffffff;
margin-top: 32rpx;
.record-bubble {
color: #ffffff;
font-size: 20rpx;
line-height: 28rpx;
padding: 8rpx 16rpx;
background: var(--theme-color);
border-radius: 8rpx;
display: flex;
align-items: center;
position: relative;
&::after {
content: "";
display: block;
position: absolute;
top: 50%;
right: -10rpx;
transform: translateY(-50%);
width: 0;
height: 0;
border-top: 12rpx solid transparent;
border-bottom: 12rpx solid transparent;
border-left: 12rpx solid var(--theme-color);
}
}
.record-list {
padding: 16rpx;
width: 452rpx;
border-radius: 8rpx;
// background: #F6F7FB;
.list-item {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
overflow: hidden;
margin-left: -7rpx;
border: 2rpx solid #ffffff;
.item-more {
width: 100%;
height: 100%;
background: var(--theme-color);
padding: 0 6rpx;
.point {
width: 6rpx;
height: 6rpx;
background: #ffffff;
border-radius: 50%;
}
}
}
}
}
.main-content {
padding: 32rpx;
border-radius: 16rpx;
background: #ffffff;
color: #5A5B6E;
font-size: 28rpx;
line-height: 48rpx;
margin-top: 32rpx;
}
.main-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
padding: 12rpx 32rpx 12rpx 48rpx;
background: #ffffff;
border-top: 1rpx solid #F6F7FB;
.footer-menu {
.menu-btn {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 32rpx;
background: transparent;
padding: 0;
.icon {
width: 52rpx;
height: 52rpx;
}
.text {
color: #5A5B6E;
font-size: 20rpx;
line-height: 28rpx;
}
}
}
.footer-btn {
color: #ffffff;
font-size: 32rpx;
line-height: 44rpx;
padding: 22rpx 24rpx;
border-radius: 16rpx;
background: var(--theme-color);
text-align: center;
&.disabled {
background: #8D929C;
}
}
}
.main-login {
padding: 64rpx 28rpx 0;
.login-image {
width: 100%;
height: 500rpx;
}
.login-tips {
color: #585858;
font-size: 36rpx;
line-height: 50rpx;
margin-top: 48rpx;
text-align: center;
}
.login-btn {
margin-top: 56rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 28rpx;
border-radius: 16rpx;
text-align: center;
background: var(--theme-color);
color: #ffffff;
}
}
}
}
</style>

View File

@@ -0,0 +1,329 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 活动列表 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar title="活动列表"></title-bar>
<!-- 内容区 -->
<view class="container-main">
<block v-if="loadEnd">
<!-- 顶部导航 -->
<view class="main-header" :style="{top: titleBarHeight + 'px'}">
<view class="header-search" @click="toSearch()">
<view class="search-input flex align-items-center">
<image class="input-icon" src="/static/search.png" mode="aspectFit"></image>
<text class="input-text flex-item">请输入关键字搜索</text>
</view>
</view>
<view class="header-filter">
<view class="filter-item" v-for="(item, index) in filterList" :key="index" @click="changeFilter(item.state)">
<view class="text" :class="{active: selectFilter == item.state}">{{item.name}}</view>
</view>
</view>
</view>
<!-- 活动列表 -->
<view class="main-list">
<activity-item :show-data="activityList" v-if="activityList.length"></activity-item>
<empty top="30%" title="暂无相关内容~" v-else></empty>
</view>
</block>
<view class="main-login" v-else-if="showLogin">
<image class="login-image" :src="loginImg" mode="aspectFit"></image>
<view class="login-tips">小程序需要登录注册才能使用相关功能请登录后查看该页面</view>
<view class="login-btn" @click="toLogin()">前往登录</view>
</view>
</view>
<!-- 底部导航 -->
<tab-bar></tab-bar>
</view>
</template>
<script>
import { mapState } from "vuex"
import activityItem from "@/pages/component/activity/index.vue"
// #ifdef H5
import wx from 'weixin-js-sdk';
// #endif
export default {
components: {
activityItem,
},
data() {
return {
// 加载完成
loadEnd: false,
// 标题栏高度
titleBarHeight: 0,
// 筛选列表
filterList: [{
name: "全部",
state: "",
},
{
name: "报名中",
state: 1,
},
{
name: "进行中",
state: 2,
},
{
name: "已结束",
state: 3,
},
],
// 已选筛选
selectFilter: "",
// 活动列表
activityList: [],
// 分类查询参数
page: 1,
limit: 20,
hasMore: false,
// 是否显示登录提示
showLogin: false,
}
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
shareImage: state => state.app.shareImage,
shareTitle: state => state.app.shareTitle,
loginImg: state => state.app.loginImg,
})
},
mounted() {
// #ifdef MP-WEIXIN
let statusBarHeight = uni.getSystemInfoSync().statusBarHeight
let menuButtonInfo = uni.getMenuButtonBoundingClientRect()
this.titleBarHeight = statusBarHeight + (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height
// #endif
},
onLoad() {
uni.showLoading({
title: "加载中"
})
this.getActivityList(() => {
uni.hideLoading()
this.loadEnd = true
})
// #ifdef H5
this.initConfig()
// #endif
},
onPullDownRefresh() {
this.page = 1
this.getActivityList(() => {
uni.stopPullDownRefresh()
})
},
onReachBottom() {
if (this.hasMore) {
this.page++
this.getActivityList()
}
},
onShareAppMessage() {
return {
title: this.shareTitle,
imageUrl: this.shareImage,
}
},
onShareTimeline() {
return {
title: this.shareTitle,
imageUrl: this.shareImage,
}
},
methods: {
// #ifdef H5
// 微信公众号初始化方法
initConfig() {
this.$util.request("main.WeChatConfig", {
url: location.href.split('#')[0]
}).then(res => {
if (res.code == 1) {
wx.config({
debug: false,
appId: res.data.appId,
timestamp: Number(res.data.timestamp),
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ["updateAppMessageShareData", "updateTimelineShareData"],
openTagList: ["updateAppMessageShareData", "updateTimelineShareData"],
})
wx.ready(() => {
wx.updateAppMessageShareData({
title: this.shareTitle,
desc: "",
link: window.location.href,
imgUrl: this.shareImage,
});
wx.updateTimelineShareData({
title: this.shareTitle,
link: window.location.href,
imgUrl: this.shareImage,
});
});
} else {
uni.hideLoading()
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('通过config接口注入权限验证配置 ', error)
})
},
// #endif
// 跳转搜索
toSearch() {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/search/index"
})
},
// 更换筛选
changeFilter(state) {
this.selectFilter = state
this.getActivityList()
},
// 获取活动列表
getActivityList(fn) {
this.$util.request("activity.list", {
page: this.page,
limit: this.limit,
state: this.selectFilter
}).then(res => {
if (fn) fn()
if (res.code == 1) {
let list = res.data.data
this.hasMore = this.page < res.data.total / this.limit ? true : false
this.activityList = this.page == 1 ? list : [...this.activityList, ...list];
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
if (error == 401) {
this.showLogin = true
} else {
console.error('获取活动列表 ', error)
}
})
},
// 前往登录
toLogin() {
uni.redirectTo({
url: "/pagesActivity/index/index",
})
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
.main-header {
position: sticky;
top: 0;
z-index: 99;
background: #ffffff;
.header-search {
padding: 16rpx 32rpx;
.search-input {
padding: 20rpx 32rpx;
background: #F9F9F9;
border-radius: 10rpx;
.input-icon {
width: 40rpx;
height: 40rpx;
}
.input-text {
margin-left: 16rpx;
color: #8D929C;
font-size: 28rpx;
line-height: 40rpx;
}
}
}
.header-filter {
display: flex;
.filter-item {
flex: 1;
padding: 0 32rpx;
display: inline-flex;
justify-content: center;
.text {
padding: 36rpx 0;
color: #5A5B6E;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
border-bottom: 4rpx solid transparent;
&.active {
color: var(--theme-color);
border-color: var(--theme-color);
}
}
}
}
}
.main-list {
padding: 32rpx;
}
.main-login {
padding: 96rpx 60rpx 0;
.login-image {
width: 100%;
height: 500rpx;
}
.login-tips {
color: #585858;
font-size: 36rpx;
line-height: 50rpx;
margin-top: 48rpx;
text-align: center;
}
.login-btn {
margin-top: 56rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 28rpx;
border-radius: 16rpx;
text-align: center;
background: var(--theme-color);
color: #ffffff;
}
}
}
}
</style>

View File

View File

@@ -0,0 +1,443 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 订单详情 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar :showBack="true" title="订单详情"></title-bar>
<!-- 内容区 -->
<view class="container-main" v-if="loadEnd">
<view class="main-card flex">
<image class="card-avatar" :src="activityInfo.image" mode="aspectFill"></image>
<view class="card-box flex-item flex-direction-column justify-content-between">
<view class="box-title text-ellipsis-more">{{activityInfo.name}}</view>
<view class="box-label flex">
<view class="label">
<text class="type-1" v-if="activityInfo.state == 1">报名中</text>
<text class="type-2" v-else-if="activityInfo.state == 2">进行中</text>
<text class="type-3" v-else-if="activityInfo.state == 3">已结束</text>
</view>
<view class="label">
<text v-if="activityInfo.organizing_method == 1">线上活动</text>
<text v-else-if="activityInfo.organizing_method == 2">线下活动</text>
</view>
</view>
</view>
</view>
<view class="main-info">
<view class="info-title">活动信息</view>
<view class="info-main">
<view class="main-item">
<view class="title">活动时间</view>
<text class="value">{{activityInfo.time_frame}}</text>
</view>
<view class="main-item">
<view class="title">联系信息</view>
<text class="value">{{activityInfo.contacts}} {{activityInfo.mobile}}</text>
</view>
<view class="main-item">
<view class="title">支付金额</view>
<text class="value" :style="{color: themeColor}" v-if="parseFloat(activityInfo.fees) > 0">{{activityInfo.fees}}</text>
<text class="value" :style="{color: themeColor}" v-else>免费</text>
</view>
</view>
</view>
<view class="main-footer">
<view class="flex justify-content-between align-items-center">
<view class="footer-price flex align-items-center" v-if="parseFloat(activityInfo.fees) > 0">
<view class="unit"></view>
<view class="number">{{activityInfo.fees}}</view>
</view>
<view class="footer-btn flex-item" @click="handleSubmit">{{parseFloat(activityInfo.fees) > 0 ? "立即支付" : "立即报名"}}</view>
</view>
<view class="safe-padding"></view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
// #ifdef H5
import wx from 'weixin-js-sdk';
// #endif
export default {
data() {
return {
// 加载完成
loadEnd: false,
// 活动id
activityId: null,
// 活动详情
activityInfo: {},
}
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
subscribeId: state => state.app.subscribeNotifyIds.applet_activity_apply,
})
},
onLoad(option) {
this.activityId = option.id
uni.showLoading({
title: "加载中"
})
this.getActivity(() => {
this.loadEnd = true
uni.hideLoading()
})
// #ifdef H5
this.initConfig()
// #endif
},
methods: {
// 获取活动详情
getActivity(fn) {
this.$util.request("activity.details", {
id: this.activityId
}).then(res => {
if (fn) fn()
if (res.code == 1) {
this.activityInfo = res.data
this.activityInfo.time_frame = this.getTimeFrame(res.data.start_time, res.data.end_time)
if (this.activityInfo.images) this.activityInfo.image = this.activityInfo.images.split(",")[0]
else this.activityInfo.image_list = ""
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
console.error('获取活动详情 ', error)
})
},
// 获取时间范围
getTimeFrame(start, end) {
let startTime = this.$util.formatDate(start, "object")
let endTime = this.$util.formatDate(end, "object")
let startResult = `${String(startTime.year).slice(2)}/${startTime.month}/${startTime.day} ${startTime.hours}:${startTime.minutes}`
let endResult = `${String(endTime.year).slice(2)}/${endTime.month}/${endTime.day} ${endTime.hours}:${endTime.minutes}`
return startResult + "~" + endResult
},
// 提交报名
async handleSubmit() {
uni.showLoading({
title: "加载中",
mask: true
})
this.subscribeMessage(() => {
var formData = { activity_id: this.activityId }
if (this.activityInfo.apply_field_state == 1) formData.data = this.$store.state.app.activityField
this.$util.request("activity.submit", formData).then(res => {
if (res.code == 1) {
if (res.data) {
this.onPayment(res.data)
} else {
uni.reLaunch({
url: "/pagesActivity/index/success?freeType=1",
success: () => {
uni.hideLoading()
}
})
}
} else {
uni.showToast({
title: res.msg,
icon: 'none',
duration: 2000
})
}
}).catch(error => {
console.error('活动报名 ', error)
})
})
},
// #ifdef H5
// 微信公众号初始化方法
initConfig() {
this.$util.request("main.WeChatConfig", {
url: location.href.split('#')[0]
}).then(res => {
if (res.code == 1) {
wx.config({
debug: false,
appId: res.data.appId,
timestamp: Number(res.data.timestamp),
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ['chooseWXPay']
})
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
console.error('通过config接口注入权限验证配置 ', error)
})
},
// #endif
// 立即支付
onPayment(data) {
// #ifdef MP-WEIXIN
uni.requestPayment({
provider: "wxpay",
...data,
success: () => {
uni.hideLoading()
uni.reLaunch({
url: "/pagesActivity/index/success"
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
title: '支付已取消',
icon: 'none'
})
}
})
// #endif
// #ifdef H5
wx.ready(() => {
uni.hideLoading()
wx.chooseWXPay({
timestamp: data.timeStamp,
package: data.package,
nonceStr: data.nonceStr,
signType: data.signType,
paySign: data.paySign,
success: (res) => {
uni.hideLoading()
if (res.errMsg == "chooseWXPay:ok") {
uni.reLaunch({
url: "/pagesActivity/index/success"
})
} else {
uni.showToast({
title: '支付失败',
icon: 'error'
})
}
},
fail: () => {
uni.hideLoading();
uni.showToast({
title: '支付已取消',
icon: 'none'
})
},
});
});
// #endif
},
// 订阅消息
subscribeMessage(fn, number = 0) {
// #ifdef MP-WEIXIN
uni.requestSubscribeMessage({
tmplIds: this.subscribeId ? [this.subscribeId] : [],
success: () => {
fn()
},
fail: (error) => {
if (error.errCode == 20004) {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '请前往设置打开接受通知',
confirmColor: this.themeColor,
confirmText: '继续报名',
success: (res) => {
if (res.confirm) {
fn()
}
},
})
} else if (error.errCode) {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '消息订阅失败,无法接收到活动通知,错误码:' + error.errCode,
confirmColor: this.themeColor,
confirmText: '继续报名',
success: (res) => {
if (res.confirm) {
fn()
}
},
})
} else if (++number > 3) {
this.subscribeMessage(fn, number)
} else {
fn()
}
}
})
// #endif
// #ifndef MP-WEIXIN
fn()
// #endif
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
padding: 32rpx;
.main-card {
border-radius: 10rpx;
background: #ffffff;
padding: 32rpx;
.card-avatar {
width: 200rpx;
height: 160rpx;
border-radius: 16rpx;
}
.card-box {
margin-left: 32rpx;
.box-title {
color: #5A5B6E;
font-size: 28rpx;
font-weight: 600;
line-height: 40rpx;
}
.box-label {
margin-top: 16rpx;
.label {
margin-right: 16rpx;
text {
display: block;
color: var(--theme-color);
font-size: 24rpx;
line-height: 34rpx;
padding: 6rpx 14rpx;
border: 2rpx solid var(--theme-color);
border-radius: 4rpx;
}
.type-1 {
color: #FFA820;
border-color: #FFA820;
}
.type-2 {
color: #00AE84;
border-color: #00AE84;
}
.type-3 {
color: #E60012;
border-color: #E60012;
}
}
}
}
}
.main-info {
border-radius: 10rpx;
background: #ffffff;
padding: 24rpx 32rpx 32rpx;
margin-top: 32rpx;
.info-title {
color: #5A5B6E;
font-size: 32rpx;
font-weight: 600;
line-height: 44rpx;
}
.info-main {
margin-top: 32rpx;
.main-item {
margin-top: 48rpx;
display: flex;
overflow: hidden;
&:first-child {
margin-top: 0;
}
.title {
color: #5A5B6E;
font-size: 28rpx;
line-height: 40rpx;
}
.value {
margin-left: 32rpx;
color: #8D929C;
font-size: 28rpx;
line-height: 40rpx;
flex: 1;
word-break: break-all;
display: flex;
justify-content: flex-end;
}
}
}
}
.main-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
padding: 12rpx 32rpx;
background: #ffffff;
border-top: 1rpx solid #F6F7FB;
.footer-price {
margin-right: 40rpx;
.unit {
color: var(--theme-color);
font-size: 32rpx;
line-height: 44rpx;
}
.number {
margin-left: 16rpx;
color: var(--theme-color);
font-size: 40rpx;
font-weight: 600;
line-height: 56rpx;
}
}
.footer-btn {
color: #ffffff;
font-size: 32rpx;
line-height: 44rpx;
padding: 22rpx 24rpx;
border-radius: 16rpx;
background: var(--theme-color);
text-align: center;
}
}
}
}
</style>

View File

@@ -0,0 +1,137 @@
<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 支付成功 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{'--theme-color': themeColor}">
<!-- 标题栏 -->
<title-bar :showBack="true" title="支付成功"></title-bar>
<!-- 内容区 -->
<view class="container-main" v-if="loadEnd">
<view class="main-image">
<image class="icon" src="/static/check.png" mode="aspectFit"></image>
</view>
<view class="main-title" v-if="freeType == 1">报名成功</view>
<view class="main-title" v-else>支付成功</view>
<view class="main-subtitle">请前往个人中心查看活动详情</view>
<view class="main-btn" @click="toOrder">前往查看</view>
<view class="main-back" @click="toIndex">返回首页</view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
export default {
data() {
return {
// 加载完成
loadEnd: false,
// 支付类型
payType: null,
// 是否免费
freeType: null
}
},
onLoad(option) {
this.payType = option.type
this.freeType = option.freeType
this.$nextTick(() => {
this.loadEnd = true
})
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
})
},
methods: {
// 跳转我的活动
toOrder() {
// #ifdef MP-WEIXIN
this.$util.toPage({
mode: 2,
path: "/pagesActivity/order/index"
})
// #endif
// #ifndef MP-WEIXIN
uni.switchTab({
url: "/pages/mine/index"
})
// #endif
},
// 返回首页
toIndex() {
uni.switchTab({
url: "/pages/index/index"
})
},
}
}
</script>
<style lang="scss">
page {
background: #ffffff;
}
.container {
.container-main {
padding: 144rpx 48rpx 32rpx;
.main-image {
width: 200rpx;
height: 200rpx;
margin: 0 auto;
padding: 48rpx;
background: var(--theme-color);
border-radius: 50%;
}
.main-title {
color: #333;
font-size: 36rpx;
font-weight: 600;
line-height: 50rpx;
margin-top: 48rpx;
text-align: center;
}
.main-subtitle {
color: #999;
font-size: 24rpx;
line-height: 34rpx;
margin-top: 24rpx;
text-align: center;
}
.main-btn {
color: #FFF;
font-size: 32rpx;
line-height: 44rpx;
padding: 34rpx;
border-radius: 16rpx;
text-align: center;
margin-top: 48rpx;
background: var(--theme-color);
}
.main-back {
color: #979797;
font-size: 32rpx;
line-height: 44rpx;
padding: 32rpx;
text-align: center;
margin-top: 16rpx;
}
}
}
</style>