Files
2026-03-25 15:53:37 +08:00

757 lines
19 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="component-activity" :style="{'--theme-color': themeColor}">
<view class="activity-item" :class="{special: showType == 1}" v-for="item in showData" :key="item.id"
@click="toDetails(item)">
<view class="item-header flex justify-content-between align-items-center" v-if="showType == 2">
<view class="header-number">订单号{{item.order_no}}</view>
<view class="header-status">
<text style="color: #FF626E;" v-if="item.pay_state == 1">待付款</text>
<block v-else-if="item.pay_state == 2">
<text style="color: #FF9100;" v-if="item.activity_state == 1">报名中</text>
<text v-else-if="item.activity_state == 2">进行中</text>
<text style="color: #666666;" v-else-if="item.activity_state == 3">已结束</text>
</block>
<text style="color: #FF9100;" v-else-if="item.pay_state == 3">退款中</text>
<text style="color: #666666;" v-else-if="item.pay_state == 4">已退款</text>
<text style="color: #FF626E;" v-else-if="item.pay_state == 5">已驳回</text>
</view>
</view>
<view class="item-info flex">
<image class="info-image" :src="item.images" mode="aspectFill"></image>
<view class="info-box flex-item">
<view class="box-title text-ellipsis">{{item.name}}</view>
<view class="box-tag flex align-items-center">
<view class="icon" :style="{'background-image': 'url('+ iconTime +')'}" v-if="iconTime"></view>
<text class="text flex-item text-ellipsis">{{item.start_time}} | {{item.week}}</text>
</view>
<view class="box-tag flex align-items-center" v-if="item.organizing_method == 1">
<view class="icon" :style="{'background-image': 'url('+ iconNetwork +')'}" v-if="iconNetwork">
</view>
<text class="text flex-item text-ellipsis">报名成功后查看</text>
</view>
<view class="box-tag flex align-items-center" v-else-if="item.organizing_method == 2">
<view class="icon" :style="{'background-image': 'url('+ iconLocation +')'}" v-if="iconLocation">
</view>
<text class="text flex-item text-ellipsis">{{item.address}}</text>
</view>
</view>
</view>
<view class="item-footer flex align-items-center" v-if="showType == 2">
<view class="footer-label">
<text v-if="item.organizing_method == 1">线上活动</text>
<text v-else-if="item.organizing_method == 2">线下活动</text>
</view>
<view class="footer-price flex-item">
<block v-if="item.fees > 0">
<text></text>{{item.fees}}
</block>
<block v-else>免费</block>
</view>
<view class="footer-btn flex">
<!-- 报名中 -->
<block v-if="item.activity_state == 1">
<!-- 待付款 -->
<block v-if="item.pay_state == 1">
<view class="btn" style="background: #FF626E;" @click.stop="toCancel(item.id)">取消参加</view>
<view class="btn" @click.stop="toPayment(item.activity_id)">去支付</view>
</block>
<!-- 已付款 -->
<block v-else-if="item.pay_state == 2">
<view style="background: #FF626E;" class="btn" @click.stop="handleCancel(item.id)"
v-if="parseFloat(item.fees) == 0">取消参加</view>
<view style="background: #FF626E;" class="btn"
@click.stop="handleRefund(item.id,item.activity_id)" v-else-if="item.refund == 1">申请退款
</view>
<view class="btn" style="background: #FFB656;" @click.stop="showWebsite(item.url)"
v-if="item.organizing_method == 1">线上地址</view>
<!-- && item.is_verifying == 1 -->
<view class="btn" :class="{disabled: getButtonConfig(item).disabled}"
:style="{backgroundColor: getButtonConfig(item).bgColor}"
@click.stop="handleButtonClick(item, getButtonConfig(item))"
v-else-if="item.organizing_method == 2 ">
<text>{{getButtonConfig(item).text}}</text>
</view>
</block>
<!-- 已驳回 -->
<block v-else-if="item.pay_state == 5">
<view class="btn" style="background: #FF626E;">驳回原因</view>
</block>
</block>
<!-- 进行中 -->
<block v-else-if="item.pay_state == 2">
<!-- 线上 -->
<view class="btn" style="background: #FFB656;" @click.stop="showWebsite(item.url)"
v-if="item.organizing_method == 1">线上地址</view>
<!--线下-->
<view class="btn" :class="{disabled: getButtonConfig(item).disabled}"
:style="{backgroundColor: getButtonConfig(item).bgColor }"
@click.stop="handleButtonClick(item, getButtonConfig(item))"
v-else-if="item.organizing_method == 2">
{{getButtonConfig(item).text}}
</view>
</block>
<!-- 已结束 -->
<block v-if="item.activity_state == 3 && item.pay_state == 2">
<view class="btn" @click.stop="showCertificate(item.activity_id, item.id)">参会证书</view>
</block>
</view>
</view>
</view>
<!-- 参会凭证 -->
<activity-poster ref="activityPoster" @onChange="pageChange"></activity-poster>
<!-- 参会证书 -->
<activity-certificate ref="activityCertificate" @onChange="pageChange"></activity-certificate>
</view>
</template>
<script>
import {
mapState
} from "vuex"
import activityPoster from "@/pages/component/activity/poster.vue"
import activityCertificate from "@/pages/component/activity/certificate.vue"
import svgData from "@/common/svg.js"
// #ifdef H5
import wx from 'weixin-js-sdk';
// #endif
export default {
name: "activityIndex",
props: ["showData", "showType"],
components: {
activityPoster,
activityCertificate,
},
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)
},
iconNetwork: state => {
return svgData.svgToUrl("network", state.app.themeColor)
}
})
},
methods: {
// 获取按钮配置
getButtonConfig(item) {
//只有报名以后才会显示出来
// 默认配置
const config = {
text: "已报名",
disabled: false,
needPhone: false,
}
// 根据签到状态和活动状态返回不同配置
if (item.actitity_state_trans == 1) {
return {
text: "已报名",
disabled: false,
needPhone: false,
}
}
if (item.actitity_state_trans === 2) {
return {
text: "活动未开始",
disabled: true,
needPhone: false,
bgColor:'#8d929c'
}
}
if (item.actitity_state_trans == 3 && item.check_in_status == 'unchecked_in') {
return {
text: '扫码签到',
disabled: false,
needPhone: false
}
}
if (item.actitity_state_trans == 3 && item.check_in_status == 'checked_in') {
return {
text: "已签到",
disabled: true,
needPhone: false,
bgColor: '#52c41a'
}
}
if (item.actitity_state_trans == 4) {
return {
text: '活动已结束',
disabled: true,
needPhone: false,
bgColor:'#8d929c'
}
}
return config
},
// 统一处理按钮点击
handleButtonClick(item) {
// 根据签到状态和活动状态返回不同配置
if (item.actitity_state_trans == 1) {
this.showToast('您已报名!')
return
}
if (item.actitity_state_trans === 2) {
this.showToast('活动尚未开始,请在活动开始后前来签到!')
return
}
if (item.actitity_state_trans == 3 && item.check_in_status == 'unchecked_in') {
this.scanQRCode(item)
}
if (item.actitity_state_trans == 3 && item.check_in_status == 'checked_in') {
this.showToast('您已签到成功!')
return
}
if (item.actitity_state_trans == 4) {
this.showToast('本次活动已结束,无法进行签到或报名!')
return
}
},
// 扫码签到
scanQRCode(item) {
// 调起摄像头扫码
uni.scanCode({
scanType: ['barCode', 'qrCode','wxCode'],
success: (res) => {
let path = this.$util.parseURLParams(decodeURIComponent(res.path))
if(!path.scene && !path.op) {
uni.showToast({
title: '请扫描签到码 !',
icon: 'none',
duration: 3000 // 设置显示时间为3000毫秒3秒
})
return
}
let id = path.scene.split('=')[1]
if(id!==item.activity_id) {//name
uni.showToast({
title:`二维码不匹配:当前扫码入口属于【活动-${item.name}`,
icon: 'none',
duration: 3000 // 设置显示时间为3000毫秒3秒
});
return
}
// 验证并签到
this.verifyAndSign(id)
},
fail: (err) => {
if (err.errMsg !== 'scanCode:fail cancel') {
// 非取消操作才提示错误
this.showToast('扫码失败,请重试')
}
}
})
},
// 验证并签到
verifyAndSign(id) {
uni.showLoading({
title: '签到中'
})
// 调用签到接口
this.$util.request('activity.code', {
id: id, // 扫描到的二维码内容
}).then(res => {
uni.hideLoading()
if (res.code == 1) {
// 签到成功
this.showToast('签到成功')
setTimeout(()=>{
this.$emit("getOrderList");
},500)
} else {
this.showToast(res.msg || '签到失败')
}
}).catch(error => {
uni.hideLoading()
console.error('签到失败', error)
this.showToast('签到失败,请重试')
})
},
// 显示提示信息
showToast(message) {
uni.showToast({
title: message,
icon: 'none',
duration: 2000
})
},
// 改变页面滚动状态
pageChange(state) {
this.$emit("onChange", state)
},
// 申请退款
handleRefund(applyId, activity_id) {
uni.showModal({
content: "确认申请退款此活动?",
confirmColor: "#FF626E",
confirmText: "确认退款",
cancelColor: "#999999",
cancelText: "取消退款",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: "加载中",
mask: true
})
this.$util.request("activity.applyRefund", {
activity_id: activity_id,
apply_id: applyId
}).then(res => {
uni.hideLoading()
if (res.code == 1) {
uni.redirectTo({
url: "/pagesActivity/order/success"
})
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('申请退款 ', error)
})
}
}
})
},
// 未支付取消参加
toCancel(applyId) {
uni.showModal({
content: "确认取消参加此活动?",
confirmColor: "#FF626E",
confirmText: "确认取消",
cancelColor: "#999999",
cancelText: "我再想想",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: "加载中",
mask: true
})
this.$util.request("activity.applyDel", {
id: applyId
}).then(res => {
uni.hideLoading()
if (res.code == 1) {
uni.showToast({
title: "取消成功",
icon: "success",
mask: true,
duration: 1500
})
this.$emit("getOrderList");
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('取消参加 ', error)
})
}
}
})
},
// 去支付
toPayment(id) {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/order?id=" + id
})
},
// 取消参加
handleCancel(applyId) {
uni.showModal({
content: "确认取消参加此活动?",
confirmColor: "#FF626E",
confirmText: "确认取消",
cancelColor: "#999999",
cancelText: "我再想想",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: "加载中",
mask: true
})
this.$util.request("activity.applyCancel", {
id: applyId
}).then(res => {
uni.hideLoading()
if (res.code == 1) {
uni.showToast({
title: "取消成功",
icon: "success",
mask: true,
duration: 1500
})
this.$emit("getOrderList");
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('取消参加 ', error)
})
}
}
})
},
// 线上地址
showWebsite(url) {
if (url) {
uni.showModal({
content: url,
confirmColor: this.themeColor,
confirmText: "复制链接",
cancelColor: "#999999",
cancelText: "关闭页面",
success: (res) => {
if (res.confirm) {
this.$util.toPage({
mode: 8,
content: url
})
}
}
})
} else {
uni.showToast({
icon: "none",
title: "暂无线上地址,请稍后再试"
})
}
},
// 跳转详情
toDetails(item) {
if (this.showType == 2) {
this.$util.toPage({
mode: 1,
path: `/pagesActivity/order/details?id=${item.id}&activity_id=${item.activity_id}`
})
} else if (this.showType == 3) {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/verification/details?id=" + item.id
})
} else {
if (item.activity_auth == 2) {
if (!uni.getStorageSync("token")) {
uni.showModal({
title: "系统提示",
content: "该活动为会员专属,请登录后查看",
confirmColor: this.themeColor,
confirmText: "前往登录",
success: (res) => {
if (res.confirm) {
uni.navigateTo({
url: "/pages/login/index"
})
}
}
})
return
}
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/details?id=" + item.id
})
} else {
this.$util.toPage({
mode: 1,
path: "/pagesActivity/index/details?id=" + item.id
})
}
}
},
// 获取位置权限
getAuthSetting(fn) {
// #ifdef MP-WEIXIN
uni.getSetting({
success: (setting) => {
if (setting.authSetting && setting.authSetting.hasOwnProperty("scope.userLocation")) {
if (setting.authSetting["scope.userLocation"]) {
if (fn) fn()
} else {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '请重新授权获取您的地理位置,否则部分功能将无法使用',
confirmColor: this.themeColor,
confirmText: "授权",
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (result) => {
if (result.authSetting[
"scope.userLocation"]) {
if (fn) fn()
} else {
uni.showToast({
title: '请确定已打开定位权限',
icon: "none",
duration: 2000,
});
}
},
fail: () => {
uni.showToast({
title: '位置获取失败',
icon: 'none',
duration: 2000,
})
}
})
} else {
uni.showToast({
title: '请授权获取您的地理位置,否则部分功能将无法使用',
icon: 'none',
duration: 2000,
})
}
},
fail: () => {
if (fn) fn()
}
})
}
} else {
if (fn) fn()
}
},
fail: () => {
uni.showToast({
title: '位置获取失败',
icon: 'none',
duration: 2000,
})
}
})
// #endif
// #ifdef H5
if (fn) fn()
// #endif
},
// 获取当前地址
getLocation(fn) {
// #ifdef MP-WEIXIN
uni.getLocation({
type: 'wgs84',
geocode: true,
success: (res) => {
fn({
latitude: res.latitude,
longitude: res.longitude,
})
},
fail: () => {
fn()
}
});
// #endif
// #ifdef H5
wx.ready(() => {
wx.getLocation({
type: 'wgs84',
success: (res) => {
fn({
latitude: res.latitude,
longitude: res.longitude,
})
},
fail: (err) => {
fn()
},
cancel: () => {
fn()
}
});
})
// #endif
},
// 截取地址栏参数
getUrlParam(url) {
const query = url.split("?")[1] || "";
const params = {};
query.split("&").forEach((pair) => {
const [key, value] = pair.split("=");
if (key) {
params[key] = decodeURIComponent(value || "");
}
});
return params;
},
// 参会证书
showCertificate(activityId, applyId) {
this.$refs.activityCertificate.getPoster(activityId, applyId)
},
},
}
</script>
<style lang="scss">
.component-activity {
.activity-item {
margin-top: 32rpx;
background: #ffffff;
border-radius: 10rpx;
padding: 32rpx;
&:first-child {
margin-top: 0;
}
&.special {
background: transparent;
border-radius: 0;
padding: 0;
}
.item-header {
padding-bottom: 32rpx;
border-bottom: 1rpx solid #E4E4E4;
margin-bottom: 32rpx;
.header-number {
color: #999999;
font-size: 28rpx;
line-height: 40rpx;
}
.header-status {
color: var(--theme-color);
font-size: 28rpx;
line-height: 40rpx;
}
}
.item-info {
.info-image {
width: 220rpx;
height: 160rpx;
border-radius: 16rpx;
}
.info-box {
margin-left: 32rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.box-title {
color: #5A5B6E;
font-size: 28rpx;
font-weight: 600;
line-height: 40rpx;
}
.box-tag {
.icon {
width: 32rpx;
height: 32rpx;
background-size: 32rpx;
margin-right: 10rpx;
}
.text {
color: #8D929C;
font-size: 24rpx;
line-height: 34rpx;
}
}
}
}
.item-footer {
margin-top: 32rpx;
.footer-label {
color: var(--theme-color);
font-size: 24rpx;
line-height: 34rpx;
padding: 8rpx 16rpx;
border-radius: 8rpx;
background: #F6F7FB;
}
.footer-price {
margin-left: 16rpx;
color: var(--theme-color);
font-size: 36rpx;
font-weight: 600;
line-height: 50rpx;
text {
font-size: 22rpx;
}
}
.footer-btn {
margin-left: 16rpx;
.btn {
color: #ffffff;
font-size: 28rpx;
line-height: 40rpx;
padding: 14rpx 24rpx;
border-radius: 8rpx;
background: var(--theme-color);
margin-left: 12rpx;
&.disabled {
// opacity: 0.6;
cursor: not-allowed;
}
&:first-child {
margin-left: 0;
}
}
}
}
}
}
</style>