39f68355 by lttnew

道馆页面更改

1 parent a4aa5d24
......@@ -5,7 +5,9 @@
"WebFetch(domain:minimax-algeng-chat-tts.oss-cn-wulanchabu.aliyuncs.com)",
"Bash(node -c pages/index/perfect.vue)",
"Bash(git checkout:*)",
"Bash(git restore:*)"
"Bash(git restore:*)",
"Bash(git:*)",
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); print\\('tabBar list:', [l['pagePath'] for l in d.get\\('tabBar',{}\\).get\\('list',[]\\)]\\)\")"
]
}
}
......
......@@ -11,22 +11,28 @@
baseUrl_api: '',
user: null
},
onLaunch: function(options) {
onLaunch: async function(options) {
console.log('App Launch', options);
this.globalData.baseUrl_api = config.baseUrl_api;
let userName = uni.getStorageSync('userName')
if (userName) {
getInfo().then(() => {
await getInfo(this)
this.globalData.isLogin = true
// 道馆用户跳转到道馆首页
const deptType = this.globalData.deptType
const userType = this.globalData.userType
// 道馆判断:deptType=6 或者 userType='4'
if (deptType == 6 || deptType == '6' || userType == '4') {
uni.reLaunch({
url: '/pages/index/home'
url: '/pages/index/daoGuanPerson'
})
}).catch(() => {
} else {
uni.reLaunch({
url: '/login/login'
})
url: '/pages/index/home'
})
}
return
}
......
......@@ -10,7 +10,13 @@ function pcLogin(data) {
params: data
}).then((res) => {
uni.setStorageSync('token', 'Bearer ' + res.data.token)
return new Promise((resolve) => {
pcLoginOpenId()
// 延迟一下确保 token 设置完成
setTimeout(() => {
resolve()
}, 100)
})
}).then(getInfo)
}
......@@ -98,13 +104,15 @@ function loginByPhone(phonenumber, code) {
}
// 获取用户详细信息
function getInfo() {
function getInfo(appInstance) {
console.log('getInfo 开始调用')
return request({
url: '/getInfo',
method: 'get'
}).then(res => {
console.log('getInfo 成功', res.data)
const userStore = useUserStore()
const app = getApp()
const app = appInstance || getApp()
const user = res.data.user
uni.setStorageSync('userName', user.userName)
......@@ -126,6 +134,12 @@ function getInfo() {
} else {
app.globalData.userType = '3'
}
console.log('getInfo跳转判断 - deptType:', deptType, 'userType:', app.globalData.userType)
// 道馆用户跳转道馆首页
return res
}).catch(err => {
console.error('getInfo 失败:', err)
})
}
......
<template>
<view class="dao-guan-tab-bar">
<image
v-if="currentIndex >= 0"
:src="config.baseUrl_api + '/fs/static/img/toolbar.png'"
class="dg-tab-bar-bg"
mode="scaleToFill"
:style="{ left: tabBgLeft }"
></image>
<view
v-for="(item, index) in tabs"
:key="index"
class="dg-tab-item"
:class="{ active: currentIndex === index }"
@click="handleClick(index, item.url)"
>
<image :src="currentIndex === index ? item.activeIcon : item.icon" class="dg-tab-icon"></image>
<text class="dg-tab-text">{{ item.text }}</text>
</view>
</view>
</template>
<script setup>
import config from '@/config.js'
import { computed, ref } from 'vue'
const props = defineProps({
currentIndex: {
type: Number,
default: 0
}
})
const emit = defineEmits(['switch'])
const switching = ref(false)
const tabs = [
{ text: '人员', url: '/pages/index/daoGuanPerson', icon: '/static/img/tool01.png', activeIcon: '/static/img/tool01_dwn.png' },
{ text: '订单', url: '/pages/index/daoGuanOrder', icon: '/static/img/tool02.png', activeIcon: '/static/img/tool02_dwn.png' },
{ text: '通知', url: '/pages/index/daoGuanNotice', icon: '/static/img/tool03.png', activeIcon: '/static/img/tool03_dwn.png' },
{ text: '级位', url: '/pages/index/daoGuanLevel', icon: '/static/img/tool04.png', activeIcon: '/static/img/tool04_dwn.png' },
{ text: '我的', url: '/pages/index/daoGuanMy', icon: '/static/img/tool05.png', activeIcon: '/static/img/tool05_dwn.png' },
]
const tabBgLeft = computed(() => {
if (props.currentIndex < 0) return '0'
return `${props.currentIndex * 20}%`
})
const handleClick = (index, url) => {
if (switching.value || index === props.currentIndex) return
switching.value = true
emit('switch', index, url)
uni.redirectTo({
url,
fail: () => {
switching.value = false
}
})
}
</script>
<style lang="scss" scoped>
.dao-guan-tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
display: flex;
justify-content: flex-start;
align-items: stretch;
padding-bottom: env(safe-area-inset-bottom);
z-index: 9999;
background-color: #d9d9d9;
overflow: hidden;
}
.dg-tab-bar-bg {
position: absolute;
top: 0rpx;
width: 20%;
height: calc(100% - 35rpx);
z-index: 0;
transition: left 0.3s ease;
pointer-events: none;
}
.dg-tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 20%;
height: 100%;
position: relative;
z-index: 1;
}
.dg-tab-icon {
width: 50rpx;
height: 50rpx;
margin-bottom: 4rpx;
}
.dg-tab-text {
font-size: 20rpx;
color: #666;
}
.dg-tab-item.active .dg-tab-text {
color: #C30D23;
font-weight: bold;
}
</style>
<template>
<view class="role-entry-page">
<!-- 全屏背景图 -->
<image :src="config.loginImage_api + '/fs/static/dg/bg@3x.png'" class="page-bg" mode="aspectFill"></image>
<image :src="config.baseUrl_api + '/fs/static/img/home_bg.png'" class="page-bg" mode="aspectFill"></image>
<!-- 顶部 Logo 区域 -->
<view class="header-wrapper">
<!-- <view class="header-wrapper">
<view class="logo-box">
<image :src="config.loginImage_api + '/fs/static/dg/ztx_b.svg'" class="logo" mode="aspectFit"></image>
</view>
</view>
</view> -->
<!-- 功能按钮区域 -->
<view class="btn-container">
<view @click="goToPage('/personal/addVip_per')">
<image :src="config.loginImage_api + '/fs/static/dg/btn01@3x.png'" class="btn-item"></image>
</view>
<view @click="goToPage('/personal/home')">
<image :src="config.loginImage_api + '/fs/static/dg/btn02@3x.png'" class="btn-item"></image>
<view
class="entry-btn"
:class="{ active: activeEntry === 'unit' }"
@click="goUnitLogin"
>
<image :src="activeEntry === 'unit' ? '/static/img/home_btn2.png' : '/static/img/home_btn.png'" class="btn-bg" mode="scaleToFill"></image>
<text>单位会员入口</text>
</view>
<view @click="goToPage('/login/loginC')">
<image :src="config.loginImage_api + '/fs/static/dg/btn03@3x.png'" class="btn-item"></image>
<view
class="entry-btn"
:class="{ active: activeEntry === 'personal' }"
@click="showLoginPopup"
>
<image :src="activeEntry === 'personal' ? '/static/img/home_btn2.png' : '/static/img/home_btn.png'" class="btn-bg" mode="scaleToFill"></image>
<text>个人会员入口</text>
</view>
</view>
<!-- 个人会员登录注册弹框 -->
<uni-popup ref="loginPopup" :mask-click="true" type="center" @change="handlePopupChange">
<view class="login-popup-content">
<!-- <view class="popup-title">请选择办理方式</view> -->
<view class="popup-btn active-btn" @click="goToPage('/personal/addVip_per')">
个人会员注册
</view>
<view class="popup-btn plain-btn" @click="goToPage('/personal/home')">
个人会员登录
</view>
</view>
</uni-popup>
</view>
</template>
......@@ -31,23 +51,46 @@ import {ref} from 'vue'
import {onShow} from '@dcloudio/uni-app'
import config from '@/config.js'
const loginPopup = ref(null)
const activeEntry = ref('')
onShow(() => {
uni.hideLoading();
})
const showLoginPopup = () => {
activeEntry.value = 'personal'
loginPopup.value.open()
}
const goUnitLogin = () => {
activeEntry.value = 'unit'
uni.navigateTo({
url: '/login/loginC',
})
}
const goToPage = (url) => {
loginPopup.value?.close()
uni.navigateTo({
url,
})
}
const handlePopupChange = (e) => {
if (!e.show && activeEntry.value === 'personal') {
activeEntry.value = ''
}
}
</script>
<style lang="scss" scoped>
.role-entry-page {
width: 100%;
min-height: 100vh;
position: relative;
padding: 20rpx;
padding: 20rpx 0;
box-sizing: border-box;
overflow: hidden;
}
.page-bg {
......@@ -71,13 +114,37 @@ const goToPage = (url) => {
flex-direction: column;
align-items: center;
margin: 0 auto;
padding-top: 1040rpx;
gap: 52rpx;
}
img, image {
width: 509rpx;
height: 117rpx;
margin: 40rpx 0;
.entry-btn {
position: relative;
width: 424rpx;
height: 86rpx;
display: flex;
align-items: center;
justify-content: center;
color: #2f2f2f;
font-size: 34rpx;
font-weight: 700;
letter-spacing: 2rpx;
}
}
.entry-btn.active {
color: #fff;
}
.btn-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.entry-btn text {
position: relative;
z-index: 1;
}
.loading-overlay {
......@@ -97,4 +164,47 @@ const goToPage = (url) => {
color: white;
font-size: 28rpx;
}
.login-popup-content {
width: 560rpx;
background: #fff;
border-radius: 28rpx;
padding: 42rpx 42rpx 50rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 20rpx 60rpx rgba(50, 0, 0, 0.25);
}
.popup-title {
margin-bottom: 22rpx;
color: #222;
font-size: 34rpx;
font-weight: 700;
}
.popup-btn {
position: relative;
width: 424rpx;
height: 86rpx;
line-height: 86rpx;
text-align: center;
border-radius: 16rpx;
font-size: 34rpx;
font-weight: 700;
margin: 18rpx 0;
box-sizing: border-box;
}
.active-btn {
background: linear-gradient(90deg, #ff9a00 0%, #e50012 100%);
color: #fff;
}
.plain-btn {
background: #fff;
color: #2f2f2f;
border: 4rpx solid #0b94d2;
box-shadow: inset 4rpx 0 0 #f7a100;
}
</style>
......
......@@ -118,25 +118,29 @@
<view class="popup-title">系统提示</view>
<view class="popup-content">
<view class="popup-text">尊敬的用户,您好!</view>
<view class="popup-text">在开始注册团体会员前,请您提前准备好以下材料,以便顺利完成申请</view>
<view class="popup-text">开始注册单位会员前,请提前准备以下材料(建议拍照或扫描,确保文字清晰、无遮挡)</view>
<view class="popup-item">
<text class="popup-num ml10">1.</text>
单位营业执照
</view>
<view class="popup-desc">请提供清晰的营业执照原件照片或扫描件(加盖公章更佳)</view>
<view class="popup-desc">原件照片或扫描件(加盖公章更佳)</view>
<view class="popup-item">
<text class="popup-tip">!</text>
<text class="popup-num"> 2.</text>
法人身份证正反面照片
法人身份证
</view>
<view class="popup-desc">请分别上传身份证正面及反面清晰照片</view>
<view class="popup-desc">确保信息完整、无遮挡、无模糊</view>
<view class="popup-desc">正、反面清晰照片(信息完整、无模糊)</view>
<!-- <view class="popup-desc">确保信息完整、无遮挡、无模糊</view> -->
<view class="popup-item">
<text class="popup-num ml10">3.</text>
机构照片
</view>
<view class="popup-desc">请提供体现单位实际经营或办公环境的照片1-2张</view>
<view class="popup-desc">如门头、办公场所、活动场地等(能展示机构真实存在即可)</view>
<view class="popup-desc">1–2张能体现单位真实经营或办公环境的照片,例如:门头、办公场所、活动场地等</view>
<!-- <view class="popup-desc">如门头、办公场所、活动场地等(能展示机构真实存在即可)</view> -->
<view class="popup-text">
<!-- <text class="popup-num ml10">3.</text> -->
感谢您的配合,祝您注册顺利!
</view>
</view>
<view class="popup-btns">
<view class="popup-btn cancel" @click="closeRegisterPopup">取消</view>
......@@ -151,7 +155,7 @@
import {onLoad, onReady} from '@dcloudio/uni-app';
import {ref, nextTick} from 'vue'
import config from '@/config.js'
import {getCodeImg, getSmsCodeImg, pcLogin, loginByPhone} from '@/common/login.js'
import {getCodeImg, getSmsCodeImg, pcLogin, loginByPhone, getInfo} from '@/common/login.js'
const isActive = ref(0)
const agree = ref(false)
......@@ -182,6 +186,14 @@ const countDown = ref({
const app = getApp()
function goHomeAfterLogin() {
const deptType = app.globalData.deptType
const userType = app.globalData.userType
uni.reLaunch({
url: (deptType == 6 || deptType == '6' || userType == '4') ? '/pages/index/daoGuanPerson' : '/pages/index/home'
})
}
onLoad(() => {
getCode()
if (uni.showShareMenu) {
......@@ -220,9 +232,12 @@ function login() {
}
if (loading.value) return;
loading.value = true
pcLogin(form.value).then((res) => {
pcLogin(form.value).then(() => {
app.globalData.isLogin = true
uni.redirectTo({url: '/pages/index/home'})
goHomeAfterLogin()
}).catch((err) => {
console.error('登录失败:', err)
uni.showToast({title: '登录失败', icon: 'none'})
}).finally(() => {
loading.value = false
})
......@@ -244,9 +259,8 @@ function login() {
loading.value = true
loginByPhone(form2.value.telNo, form2.value.code)
.then(() => {
loading.value = false
app.globalData.isLogin = true
uni.redirectTo({url: '/pages/index/home'})
goHomeAfterLogin()
}).finally(() => {
loading.value = false
})
......
......@@ -22,7 +22,7 @@
<!-- 温馨提示 -->
<view v-if="form.selfSelect == '1'" class="tip-box">
<text class="tip-text">温馨提示:
您可以自行录入考官信息,如果暂时没有合适的考官,也可以选择由省跆协指派(支持多选)进行考点申报,同时请尽快完成考点考官的认证。
您可以自行录入考官信息,如果暂时没有考官,可以选择由省跆协指派进行考点申报,请尽快完成考点考官的认证。
</text>
</view>
......
......@@ -48,8 +48,12 @@
<!-- 金额说明 -->
<view class="card notice-card">
<view class="notice-line">1. 请通过网上银行(网银)或银行柜台或手机银行向以下账号划转款项。</view>
<view class="notice-line">2. 转账金额与订单金额必须保持一致,不得多转、少转。</view>
<view class="danger-title">
<text>请使用认证机构下的账号进行对公转账。</text>
</view>
<view class="notice-line">1. 本订单将为您保留7天,请您及时支付;逾期未支付,订单将自动取消。</view>
<view class="notice-line">2. 请通过网上银行(网银)或银行柜台或手机银行向以下账号划转款项。</view>
<view class="notice-line">3. 转账金额与订单金额必须保持一致,不得多转、少转。</view>
</view>
<!-- 温馨提示 -->
......@@ -293,6 +297,14 @@ function handelClose() {
}
}
}
.danger-title{
display: flex;
align-items: center;
font-size: 28rpx;
color: #F56C6C;
font-weight: 600;
margin-bottom: 16rpx;
}
/* 温馨提示 */
.warning-card {
......
......@@ -20,6 +20,56 @@
}
},
{
"path": "pages/index/daoGuanPerson",
"style": {
"navigationBarTitleText": "人员",
"backgroundColor": "#ffffff",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/index/daoGuanLevel",
"style": {
"navigationBarTitleText": "级位",
"backgroundColor": "#ffffff",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/index/daoGuanOrder",
"style": {
"navigationBarTitleText": "订单",
"backgroundColor": "#ffffff",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/index/daoGuanNotice",
"style": {
"navigationBarTitleText": "通知",
"backgroundColor": "#ffffff",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/index/daoGuanMy",
"style": {
"navigationBarTitleText": "我的",
"backgroundColor": "#ffffff",
"navigationStyle": "custom",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#ffffff"
}
},
{
"path": "pages/webview/webview",
"style": {
"navigationBarTitleText": "中国跆拳道协会",
......@@ -189,6 +239,7 @@
}
}
],
"globalStyle": {
"navigationStyle": "default",
"navigationBarTextStyle": "black",
......
<template>
<view class="level-page">
<view class="hero">
<image class="hero-bg" :src="config.baseUrl_api + '/fs/static/img/top.png'" mode="aspectFill"></image>
<view class="hero-brand">
<view class="brand-cn">中国跆拳道协会</view>
<view class="brand-en">CHINESE TAEKWONDO ASSOCIATION</view>
</view>
</view>
<view class="content-panel">
<image class="panel-bg" :src="config.baseUrl_api + '/fs/static/img/red_bg2.png'" mode="scaleToFill"></image>
<view class="panel-content">
<view class="section-title">
<view class="title-cn">级位考试</view>
<view style="width:50rpx;height:8rpx;background:#C30D23;margin-top:20rpx;"></view>
<view class="title-en">LEVEL EXAM</view>
</view>
<view class="level-grid">
<image class="level-card" :src="config.baseUrl_api + '/fs/static/img/jw_btn01.png'" mode="aspectFit" @click="goPath('/level/addApply')"></image>
<image class="level-card" :src="config.baseUrl_api + '/fs/static/img/jw_btn02.png'" mode="aspectFit" @click="goPath('/personalVip/addChangeLevel')"></image>
<image class="level-card" :src="config.baseUrl_api + '/fs/static/img/jw_btn03.png'" mode="aspectFit" @click="goPath('/level/apply')"></image>
<image class="level-card" :src="config.baseUrl_api + '/fs/static/img/jw_btn04.png'" mode="aspectFit" @click="goPath('/personalVip/changeLevel')"></image>
</view>
</view>
</view>
<dao-guan-tab-bar :currentIndex="3" @switch="onTabSwitch" />
</view>
</template>
<script setup>
import config from '@/config.js'
import DaoGuanTabBar from '@/components/dao-guan-tab-bar.vue'
const onTabSwitch = () => {
// tab switch handled by component
}
const goPath = (url) => {
uni.navigateTo({ url })
}
</script>
<style lang="scss" scoped>
.level-page {
position: relative;
min-height: 100vh;
overflow-x: hidden;
background: #ededf0;
overflow-y: hidden;
// padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.hero {
position: relative;
height: 1060rpx;
overflow: hidden;
z-index: 1;
}
.hero-bg {
position: absolute;
left: 0;
top: -116px;
width: 100%;
height: 100%;
}
.hero-brand {
position: relative;
z-index: 1;
padding: calc(env(safe-area-inset-top) + 170rpx) 44rpx 0;
color: #fff;
}
.brand-cn {
font-size: 38rpx;
letter-spacing: 2rpx;
}
.brand-en {
margin-top: 8rpx;
color: rgba(255, 255, 255, 0.62);
font-size: 16rpx;
}
.content-panel {
position: relative;
z-index: 3;
min-height: 760rpx;
margin-top: -300rpx;
overflow: visible;
}
.panel-bg {
position: absolute;
left: 0;
top: -674rpx;
z-index:10;
width: 100%;
height: 1700rpx;
}
.panel-content {
position: relative;
z-index: 12;
padding: 0rpx 46rpx 40rpx;
}
.section-title {
margin-bottom: 30rpx;
}
.title-cn {
color: #2a2a2a;
font-size: 58rpx;
line-height: 1;
}
.title-en {
margin-top: 12rpx;
color: #9a9a9a;
font-size: 24rpx;
font-weight: 700;
}
.level-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14rpx 14rpx;
}
.level-card {
width: 100%;
height: 236rpx;
border-radius: 10rpx;
}
</style>
<template>
<view class="page dao-my-page">
<image class="page-red-bg" :src="config.baseUrl_api + '/fs/static/img/red_bg.png'" mode="scaleToFill"></image>
<view class="profile-hero">
<view class="hero-content">
<view class="profile-row">
<view class="avatar-wrap">
<image v-if="state.user.avatar" :src="state.user.avatar" mode="aspectFill"/>
<image v-else :src="config.baseUrl_api + '/fs/static/nodata.png'" mode="aspectFill"/>
</view>
<!-- <view class="profile-main">
<view class="name-row">
<view class="dept-name">{{ state.user?.dept?.deptName || deptInfo.deptName || memberInfo.name || '--' }}</view>
<view class="member-badge">
<text class="badge-star"></text>
<text>金牌会员</text>
</view>
</view>
<view class="id-row">
<text>ID:{{ memberInfo.menCode || memberInfo.memberCode || memberInfo.memberNo || state.user?.userName || '--' }}</text>
<text class="join-days">您已加入{{ joinDays }}</text>
</view>
</view> -->
</view>
<view class="motto">每一次的成功都不是偶然,是千锤百炼,永不放弃的结果。{{ state.user?.dept?.deptName || '龙威天成跆拳道馆' }},以专业铸就辉煌!</view>
<view class="edit-btn" @click="goPath('/myCenter/teamInfo')">修改资料</view>
</view>
</view>
<view class="menu-panel">
<image class="menu-bg" :src="config.baseUrl_api + '/fs/static/img/red_bg2.png'" mode="scaleToFill"></image>
<view class="menu-item" @click="goPath('/myCenter/teamInfo')">
<view class="menu-left">
<image class="menu-icon" :src="config.baseUrl_api + '/fs/static/img/user01.png'" mode="aspectFit"></image>
<text>单位信息</text>
</view>
<text class="arrow">></text>
</view>
<view class="menu-item" @click="goPath('/myCenter/auth')">
<view class="menu-left">
<image class="menu-icon" :src="config.baseUrl_api + '/fs/static/img/user02.png'" mode="aspectFit"></image>
<text>会员认证</text>
</view>
<text class="arrow">></text>
</view>
<view class="menu-item" @click="goPath('/myCenter/safe')">
<view class="menu-left">
<image class="menu-icon" :src="config.baseUrl_api + '/fs/static/img/user03.png'" mode="aspectFit"></image>
<text>账号安全</text>
</view>
<text class="arrow">></text>
</view>
</view>
<view class="logout-area">
<button class="logout-btn" @click="loginOut">退出登录</button>
</view>
<!-- 底部导航 -->
<dao-guan-tab-bar :currentIndex="4" @switch="onTabSwitch" />
</view>
</template>
<script setup>
import * as api from '@/common/api.js';
import * as loginServer from '@/common/login.js';
import config from '@/config.js'
import {
onLoad,
onShow,
onReady,
onPullDownRefresh
} from '@dcloudio/uni-app';
import {
ref, reactive, computed,
getCurrentInstance
} from 'vue';
import DaoGuanTabBar from '@/components/dao-guan-tab-bar.vue'
const {
proxy
} = getCurrentInstance()
const app = getApp();
const userType = ref('1')
const memberInfo = ref({})
const deptInfo = ref({})
let proId;
const svId = ref(null);
const numData = ref({});
const messageList = ref([])
const state = reactive({
user: {},
roleGroup: {},
postGroup: {}
})
const joinDays = computed(() => {
const value = memberInfo.value.createTime || memberInfo.value.joinTime || memberInfo.value.createDate
if (!value) return '--'
const start = new Date(String(value).replace(/-/g, '/')).getTime()
if (!start) return '--'
const days = Math.floor((Date.now() - start) / (24 * 60 * 60 * 1000)) + 1
return days > 0 ? days : 1
})
onShow(() => {
if (app.globalData.isLogin) {
init()
} else {
app.firstLoadCallback = () => {
init()
};
}
})
onLoad(option => {
if (option.scene) {
proId = decodeURIComponent(option.scene);
} else {
proId = option.proId;
}
if (uni.showShareMenu) {
uni.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
});
}
});
function loginOut() {
uni.showModal({
content: `确认退出吗?`,
success: function (res) {
if (res.confirm) {
loginServer.logout().finally(() => {
let path = '/login/login';
uni.reLaunch({
url: path
});
})
}
}
})
}
function getUser() {
api.getUserProfile().then((response) => {
state.user = response.data.user
if (state.user.avatar && state.user.avatar.indexOf('http') == -1) {
state.user.avatar = config.baseUrl_api + state.user.avatar
}
state.roleGroup = response.data.roleGroup
state.postGroup = response.data.postGroup
uni.hideLoading();
})
}
function init() {
uni.showLoading({
title: '加载中'
});
getUser()
loginServer.getMyOwnMemberInfo().then(res => {
userType.value = app.globalData.userType
memberInfo.value = app.globalData.memberInfo
deptInfo.value = app.globalData.dept || {}
uni.hideLoading();
})
}
function goPath(url) {
uni.navigateTo({
url: url
})
}
function onTabSwitch(index, url) {
// tab switch handled by component
}
</script>
<style lang="scss" scoped>
.dao-my-page {
position: relative;
min-height: 100vh;
width: 100vw;
overflow-x: hidden;
box-sizing: border-box;
padding-bottom: calc(210rpx + env(safe-area-inset-bottom));
background: #ededf0;
}
.page-red-bg {
position: absolute;
left: 0;
top: 0;
z-index: 0;
width: 100%;
height: 720rpx;
}
.profile-hero {
position: relative;
z-index: 1;
min-height: 460rpx;
}
.hero-content {
position: relative;
z-index: 2;
padding: calc(env(safe-area-inset-top) + 126rpx) 48rpx 0;
color: #fff;
}
.profile-row {
display: flex;
align-items: center;
}
.avatar-wrap {
width: 132rpx;
height: 132rpx;
padding: 6rpx;
flex: 0 0 132rpx;
overflow: hidden;
border-radius: 50%;
background: #fff;
box-shadow: 0 8rpx 26rpx rgba(65, 0, 0, 0.28);
image {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.profile-main {
flex: 1;
min-width: 0;
margin-left: 26rpx;
}
.name-row,
.id-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.dept-name {
max-width: 330rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 34rpx;
font-weight: 700;
}
.member-badge {
display: flex;
align-items: center;
gap: 8rpx;
color: #dba12d;
font-size: 24rpx;
white-space: nowrap;
}
.badge-star {
color: #f7c342;
font-size: 24rpx;
}
.id-row {
margin-top: 12rpx;
color: rgba(255, 255, 255, 0.86);
font-size: 26rpx;
}
.join-days {
margin-left: 18rpx;
white-space: nowrap;
}
.motto {
margin-top: 52rpx;
color: rgba(255, 255, 255, 0.86);
font-size: 24rpx;
line-height: 1.75;
}
.edit-btn {
width: 168rpx;
height: 48rpx;
margin: 34rpx 0 0 auto;
border-radius: 6rpx;
background: #fff;
color: #333;
font-size: 24rpx;
line-height: 48rpx;
text-align: center;
}
.menu-panel {
position: relative;
z-index: 2;
min-height: 560rpx;
margin-top: -72rpx;
padding: 178rpx 48rpx 0;
overflow: hidden;
}
.menu-bg {
position: absolute;
left: 0;
top: -400rpx;
z-index: 0;
width: 100%;
height: 1400rpx;
}
.menu-item {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
height: 132rpx;
color: #111;
font-size: 31rpx;
font-weight: 700;
}
.menu-left {
display: flex;
align-items: center;
}
.menu-icon {
width: 38rpx;
height: 38rpx;
margin-right: 18rpx;
}
.arrow {
color: #111;
font-size: 42rpx;
font-weight: 400;
line-height: 1;
}
.logout-area {
position: fixed;
left: 0;
right: 0;
bottom: calc(124rpx + env(safe-area-inset-bottom));
z-index: 9998;
padding: 16rpx 48rpx 18rpx;
background: transparent;
box-sizing: border-box;
}
.logout-btn {
height: 76rpx;
border-radius: 50rpx;
background: #ad181f;
color: #fff;
font-size: 28rpx;
line-height: 76rpx;
}
.logout-btn::after {
border: none;
}
</style>
<template>
<view class="notice-page">
<z-paging
ref="paging"
v-model="dataList"
class="notice-paging"
emptyViewImg="/static/nodata.png"
@query="queryList"
>
<template #top>
<view class="notice-hero">
<view class="hero-title-row">
<view>
<view class="hero-title">中国跆拳道协会</view>
<view class="hero-subtitle">CHINESE TAEKWONDO ASSOCIATION</view>
</view>
<view class="hero-page-title">
<text>协会资讯</text>
<view class="title-line"></view>
</view>
</view>
<view class="search-bar">
<uni-easyinput
v-model="query.keyword"
:input-border="false"
class="search-input"
placeholder="请输入公告标题"
placeholderStyle="font-size:28rpx;color:#999"
prefixIcon="search"
@confirm="handleSearch"
@clear="handleSearch"
/>
<view class="search-btn" @click="handleSearch">搜索</view>
</view>
</view>
<!-- <view class="notice-tabs">
<view class="tab-item active">公告</view>
</view> -->
</template>
<view class="notice-list">
<view v-for="item in dataList" :key="item.id || item.noteId" class="notice-item" @click="goDetail(item)">
<view class="notice-title">{{ item.name || item.title || '--' }}</view>
<view v-if="item.introduction || item.remark || item.content" class="notice-summary">
{{ item.introduction || item.remark || item.content }}
</view>
<image
v-if="getCover(item)"
:src="getCover(item)"
class="notice-cover"
mode="aspectFill"
/>
<view class="notice-meta">
<text>{{ formatDate(item.belongTime || item.createTime || item.publishTime) }}</text>
<!-- <view class="meta-right">
<text class="heart">♡</text>
<text>{{ item.readNum || item.views || item.likeNum || 0 }}</text>
<text class="more">...</text>
</view> -->
</view>
</view>
</view>
</z-paging>
<dao-guan-tab-bar :currentIndex="2" @switch="onTabSwitch" />
</view>
</template>
<script setup>
import { ref, reactive } from 'vue'
import * as api from '@/common/api.js'
import dayjs from 'dayjs'
import DaoGuanTabBar from '@/components/dao-guan-tab-bar.vue'
const dataList = ref([])
const paging = ref(null)
const query = reactive({
keyword: ''
})
const queryList = (pageNum, pageSize) => {
api.notice({
pageNum,
pageSize,
name: query.keyword
}).then(res => {
paging.value?.complete(res.rows || [])
}).catch(() => {
paging.value?.complete(false)
})
}
const handleSearch = () => {
paging.value?.reload()
}
const getCover = (item) => {
return item.coverUrl || item.cover || item.picUrl || item.imageUrl || item.imgUrl || ''
}
const formatDate = (value) => {
if (!value) return ''
return dayjs(value).format('YYYY-MM-DD')
}
const goDetail = (item) => {
uni.navigateTo({
url: `/pages/index/newsDetail?noteId=${item.noteId}`
})
}
const onTabSwitch = () => {
// tab switch handled by component
}
</script>
<style lang="scss" scoped>
.notice-page {
position: relative;
min-height: 100vh;
background: #ededf0;
box-sizing: border-box;
}
.notice-hero {
padding: calc(env(safe-area-inset-top) + 88rpx) 40rpx 34rpx;
background: linear-gradient(135deg, #b00005 0%, #760000 100%);
color: #fff;
}
.hero-title-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 42rpx;
}
.hero-title {
font-size: 38rpx;
letter-spacing: 2rpx;
}
.hero-subtitle {
margin-top: 8rpx;
color: rgba(255, 255, 255, 0.6);
font-size: 16rpx;
}
.hero-page-title {
color: #f4b536;
font-size: 36rpx;
font-weight: 500;
text-align: right;
}
.title-line {
width: 54rpx;
height: 6rpx;
margin: 14rpx 0 0 auto;
border-radius: 10rpx;
background: #f4b536;
}
.search-bar {
display: flex;
align-items: center;
height: 64rpx;
border-radius: 34rpx;
background: #fff;
overflow: hidden;
}
.search-input {
flex: 1;
:deep(.uni-easyinput__content) {
height: 64rpx;
padding: 0 18rpx 0 28rpx;
border-radius: 34rpx;
background: #fff;
}
}
.search-btn {
width: 88rpx;
height: 52rpx;
margin-right: 6rpx;
border-radius: 28rpx;
background: #c91c34;
color: #fff;
font-size: 24rpx;
font-weight: 700;
line-height: 52rpx;
text-align: center;
}
.notice-tabs {
display: flex;
align-items: center;
padding: 24rpx 40rpx 8rpx;
}
.tab-item {
position: relative;
margin-right: 54rpx;
padding-bottom: 14rpx;
color: #555;
font-size: 30rpx;
}
.tab-item.active {
color: #c30d23;
font-weight: 700;
}
.tab-item.active::after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
width: 52rpx;
height: 4rpx;
border-radius: 4rpx;
background: #c30d23;
transform: translateX(-50%);
}
.notice-paging {
height: 100vh;
background: #ededf0;
}
.notice-list {
padding: 0 10rpx calc(160rpx + env(safe-area-inset-bottom));
}
.notice-item {
margin-bottom: 34rpx;
background: #fff;
padding: 24rpx;
border-radius: 16rpx;
margin: 20rpx;
}
.notice-title {
color: #111;
font-size: 31rpx;
font-weight: 800;
line-height: 1.42;
}
.notice-summary {
display: -webkit-box;
margin-top: 12rpx;
overflow: hidden;
color: #666;
font-size: 25rpx;
line-height: 1.45;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.notice-cover {
width: 100%;
height: 330rpx;
margin-top: 14rpx;
border-radius: 0;
background: #ddd;
}
.notice-meta {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 10rpx;
color: #999;
font-size: 24rpx;
}
.meta-right {
display: flex;
align-items: center;
gap: 8rpx;
}
.heart {
font-size: 28rpx;
}
.more {
margin-left: 14rpx;
letter-spacing: 2rpx;
}
</style>
<template>
<view :class="{ 'no-scroll': isPopupOpen }" class="order-page">
<view class="order-hero">
<view class="hero-title-row">
<view>
<view class="hero-title">中国跆拳道协会</view>
<view class="hero-subtitle">CHINESE TAEKWONDO ASSOCIATION</view>
</view>
<view class="hero-page-title">
<text>订单管理</text>
<view class="title-line"></view>
</view>
</view>
<view class="search-bar">
<uni-easyinput
v-model="queryParams.wfCode"
:input-border="false"
class="search-input"
placeholder="输入订单编号/名称/单位/日期"
placeholderStyle="font-size:28rpx;color:#999"
prefixIcon="search"
@blur="handelSearch"
@clear="handelSearch">
</uni-easyinput>
<view class="add-btn" @click="handelSearch">
<text class="add-text">搜索</text>
</view>
</view>
</view>
<view class="tab-bar">
<view
v-for="(tab, index) in tabs"
:key="index"
:class="{ active: currentTab === tab.type }"
class="tab-item"
@click="switchTab(tab.type)"
>
{{ tab.name }}
</view>
</view>
<view class="status-filter">
<view
v-for="(tab, index) in statusTabs"
:key="index"
:class="{ active: currentStatus === tab.type }"
class="status-filter-item"
@click="switchStatus(tab.type)"
>
{{ tab.name }}
</view>
<!-- <view class="filter-entry">
<text class="filter-icon"></text>
<text>筛选</text>
</view> -->
</view>
<!-- 订单列表 -->
<scroll-view
:scroll-enabled="!isPopupOpen"
:show-scrollbar="false"
class="order-list-scroll"
lower-threshold="200"
scroll-y
@scrolltolower="loadMore"
>
<view class="order-list">
<!-- 有数据才循环 -->
<!-- <view v-if="list.length > 0">-->
<!-- <view-->
<!-- v-for="(item, index) in list"-->
<!-- :key="index"-->
<!-- class="order-card"-->
<!-- @click="goToDetail(item)"-->
<!-- >-->
<!-- &lt;!&ndash; 订单头部:日期 + 状态 &ndash;&gt;-->
<!-- <view class="card-header">-->
<!-- <view class="date">-->
<!-- &lt;!&ndash; <image :src="config.baseUrl_api + '/fs/static/calendar@2x.png'" mode="widthFix" style="width:30rpx;height:30rpx;"/> &ndash;&gt;-->
<!-- &lt;!&ndash; &ndash;&gt;-->
<!-- <text class="value text-primary">{{ item.wfCode || '——' }}</text>-->
<!-- </view>-->
<!-- <view class="status-tags">-->
<!-- &lt;!&ndash; <view-->
<!-- class="status-tag"-->
<!-- :class="{-->
<!-- success: item.payStatus == 1,-->
<!-- danger: item.payStatus == 2,-->
<!-- pending: item.payStatus == 0-->
<!-- }"-->
<!-- >-->
<!-- {{ getStatusText(item.payStatus) }}-->
<!-- </view> &ndash;&gt;-->
<!-- <view-->
<!-- :class="{-->
<!-- 'status-wait': item.auditStatus == 0,-->
<!-- 'status-pending': item.auditStatus == 1,-->
<!-- 'status-success': item.auditStatus == 2,-->
<!-- 'status-danger': item.auditStatus == 3-->
<!-- }"-->
<!-- class="status-tag ml-10"-->
<!-- >-->
<!-- {{ getAuditStatusText(item.auditStatus) }}-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- -->
<!-- &lt;!&ndash; 订单编号、缴费编号 &ndash;&gt;-->
<!-- <view class="info-row">-->
<!-- <text class="label">订单编号:</text>-->
<!-- <text class="value">{{ item.tradeNo || '——' }}</text>-->
<!-- </view>-->
<!-- <view v-if="item.orderName" class="info-row">-->
<!-- <text class="label">缴费名称:</text>-->
<!-- <text class="value">{{ item.orderName || '' }}</text>-->
<!-- </view>-->
<!-- &lt;!&ndash; <view class="info-row">-->
<!-- <text class="label">缴费编号:</text>-->
<!-- -->
<!-- </view> &ndash;&gt;-->
<!-- -->
<!-- &lt;!&ndash; 核心:前2tab仅展示缴费年限,后2tab仅展示人数合计 &ndash;&gt;-->
<!-- <view v-if="item.content" class="info-section flex f-j-s">-->
<!-- &lt;!&ndash; 个人/单位会员(仅缴费年限) &ndash;&gt;-->
<!-- <view v-if="currentTab === '0' || currentTab === '1'" class="single-info">-->
<!-- <view class="label">缴费年限:</view>-->
<!-- <view class="value">{{ item.content.yearCount || 0 }}</view>-->
<!-- </view>-->
<!-- &lt;!&ndash; 级位/段位考试(仅人数合计) &ndash;&gt;-->
<!-- <view v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'" class="single-info">-->
<!-- <view class="label">人数合计</view>-->
<!-- <view class="value">{{ item.content.personCount || 0 }}</view>-->
<!-- </view>-->
<!-- <view class="line"></view>-->
<!-- <view class="single-info">-->
<!-- <view class="label">订单状态</view>-->
<!-- <view :class="item.effect == 1 ? 'text-success' : 'text-warning'" class="value">-->
<!-- {{ item.effect == 1 ? '已生效' : '未生效' }}-->
<!-- </view>-->
<!-- </view>-->
<!-- <view class="line"></view>-->
<!-- <view class="single-info">-->
<!-- <view class="label">缴费状态</view>-->
<!-- <view-->
<!-- :class="{-->
<!-- 'text-primary': item.payStatus == 0,-->
<!-- 'text-success': item.payStatus == 1,-->
<!-- 'text-danger': item.payStatus == 2-->
<!-- }"-->
<!-- class="value"-->
<!-- >-->
<!-- {{ item.payStatus == 0 ? '待缴费' : item.payStatus == 1 ? '缴费成功' : '订单取消' }}-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- -->
<!-- &lt;!&ndash; 费用合计 + 缴费方式 &ndash;&gt;-->
<!-- <view class="price-section">-->
<!-- <view class="price-row">-->
<!-- <text class="price-label">费用合计</text>-->
<!-- <text class="price-value">¥{{ (Number(item.price) || 0).toFixed(2) }}</text>-->
<!-- </view>-->
<!-- <view class="price-row">-->
<!-- <text class="price-label">缴费方式</text>-->
<!-- <text class="price-value">{{ item.ziZhangBu ? '对公转账' : '民生付' }}</text>-->
<!-- </view>-->
<!-- </view>-->
<!-- -->
<!-- &lt;!&ndash; 按钮组:靠右紧凑展示 &ndash;&gt;-->
<!-- <view class="btn-group">-->
<!-- &lt;!&ndash; 已缴费:申请开票/已开票(需要审核通过才能开票) &ndash;&gt;-->
<!-- <template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2 &&item.price>0">-->
<!-- <button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice" @click="makeInvoiceFN(item)">-->
<!-- 开票-->
<!-- </button>-->
<!-- </template>-->
<!-- &lt;!&ndash; 已开票:查看发票 &ndash;&gt;-->
<!-- <template v-if="item.invoiceStatus == 1">-->
<!-- <button class="btn btn-invoice" @click.stop="viewInvoice(item)">查看发票</button>-->
<!-- </template>-->
<!-- &lt;!&ndash; 未缴费:去缴费 + 取消订单 &ndash;&gt;-->
<!-- &lt;!&ndash; <template v-if="item.payStatus == 0">-->
<!-- <button class="btn btn-cancel" @click="handleCancel(item)">取消订单</button>-->
<!-- <button class="btn btn-pay" @click="handlePay(item)">去缴费</button>-->
<!-- </template> &ndash;&gt;-->
<!-- </view>-->
<!-- </view>-->
<!-- </view>-->
<!-- 有数据才循环 -->
<view v-if="list.length > 0">
<view
v-for="(item, index) in list"
:key="index"
class="order-card-new"
@click="goToDetail(item)"
>
<!-- 订单头部:日期 + 状态 -->
<view class="card-header">
<view class="date">
<view class="data-header">
<text class="member-label">{{ getOrderLabel(item) }}·</text>
<text class="value ">{{ item.orderName || '——' }}</text>
</view>
<text :class="{
'status-wait': item.auditStatus == 0,
'status-pending': item.auditStatus == 1,
'status-success': item.auditStatus == 2,
'status-danger': item.auditStatus == 3
}"
class="status-tag ">{{ getAuditStatusText(item.auditStatus) }}
</text>
</view>
</view>
<!-- <view class="card-header code-row">
<view class="date">
<view class="data-header">
<text class="value">
<text class="tradeNo">订单编号:</text>
{{ item.tradeNo || '——' }}
</text>
</view>
</view>
</view> -->
<!-- <view class="card-header code-row">
<view class="date">
<view class="data-header">
<text class="value">
<text class="tradeNo">缴费编号:</text>
{{ item.wfCode || '——' }}
</text>
</view>
</view>
</view> -->
<view class="member-time">
<view class="label">
<text class="star"></text>
{{ `${filterTime(item.genTime)}${filterType(item.type)}` }}
</view>
<view class="price">
<view>¥{{ item.price || '0.00' }}</view>
<view v-if="item.type==0" class="person">共{{ item.content?.allPersonCount || 0 }}人</view>
<view v-if="item.type==1" class="person">共{{ item.content?.yearCount || 0 }}年</view>
<view v-if="item.type==2||item.type==3||item.type==4" class="person">共{{
item.content?.personCount || 0
}}人
</view>
</view>
</view>
<!-- 核心:前2tab仅展示缴费年限,后2tab仅展示人数合计 -->
<!-- <view v-if="item.content" class="info-section flex f-j-s">
<view v-if="currentTab === '0' || currentTab === '1'" class="single-info">
<view class="label">缴费年限:</view>
<view class="value">{{ item.content.yearCount || 0 }}</view>
</view>
<view v-if="currentTab === '2' || currentTab === '3' || currentTab === '4'" class="single-info">
<view class="label">人数合计</view>
<view class="value">{{ item.content.personCount || 0 }}</view>
</view>
<view class="line"></view>
<view class="single-info">
<view class="label">订单状态</view>
<view :class="item.effect == 1 ? 'text-success' : 'text-warning'" class="value">
{{ item.effect == 1 ? '已生效' : '未生效' }}
</view>
</view>
<view class="line"></view>
<view class="single-info">
<view class="label">缴费状态</view>
<view
:class="{
'text-primary': item.payStatus == 0,
'text-success': item.payStatus == 1,
'text-danger': item.payStatus == 2
}"
class="value"
>
{{ item.payStatus == 0 ? '待缴费' : item.payStatus == 1 ? '缴费成功' : '订单取消' }}
</view>
</view>
</view> -->
<!-- 按钮组:靠右紧凑展示 -->
<view class="btn-group">
<view>
<text class="more" @click.stop="goToDetail(item)">更多</text>
</view>
<view class="btn-flex">
<!-- 已缴费:申请开票/已开票(需要审核通过才能开票) -->
<template>
<button class="btn btn-info" @click.stop="goToDetail(item)">查看明细</button>
</template>
<!-- 已缴费:申请开票/已开票(需要审核通过才能开票) -->
<template v-if="item.payStatus == 1 && item.invoiceStatus != 1&& item.auditStatus == 2 &&item.price>0">
<button :disabled="item.invoiceStatus === 1" class="btn btn-view-invoice"
@click.stop="makeInvoiceFN(item)">
申请票据
</button>
</template>
<!-- 已申请票据:查看票据 -->
<template v-if="item.invoiceStatus == 1">
<button class="btn btn-invoice" @click.stop="viewInvoice(item)">查看票据</button>
</template>
<!-- 未缴费:去缴费 + 取消订单 -->
<!-- <template v-if="item.payStatus == 0">
<button class="btn btn-cancel" @click="handleCancel(item)">取消订单</button>
<button class="btn btn-pay" @click="handlePay(item)">去缴费</button>
</template> -->
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty">
<image :src="config.baseUrl_api + '/fs/static/nodata.png'" class="empty-img" mode="aspectFit"></image>
<text class="empty-text">暂无订单记录</text>
</view>
<!-- 加载/无更多提示 -->
<view v-if="loading" class="loading-tip">加载中...</view>
<view v-if="!loading && !hasMore && list.length" class="no-more">没有更多了</view>
</view>
</scroll-view>
<!-- 票据信息弹窗(级位/段位/越段考试) -->
<view v-if="showInvoicePopup" class="invoice-popup-mask" @click="closeInvoicePopup">
<view class="invoice-popup-content" @click.stop>
<view class="invoice-popup-header">
<text class="invoice-popup-title">票据信息</text>
<view class="invoice-popup-close" @click="closeInvoicePopup"></view>
</view>
<view class="invoice-popup-body">
<view class="invoice-info-list">
<view class="invoice-info-row">
<view class="invoice-info-label">票据类型</view>
<view :class="{ 'vat-type': invoiceData.invoiceType == 2 }" class="invoice-type-badge">
{{ invoiceData.invoiceType == 1 ? '普通票据' : '增值税专用票据' }}
</view>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">票据抬头</text>
<text class="invoice-info-value">{{ invoiceData.invoiceBuyerName || '—' }}</text>
</view>
<view v-if="invoiceData.invoiceBuyerTaxno" class="invoice-info-row">
<text class="invoice-info-label">纳税人识别号</text>
<text class="invoice-info-value">{{ invoiceData.invoiceBuyerTaxno }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">接收邮箱</text>
<text class="invoice-info-value">{{ invoiceData.invoicePushPhone || '—' }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">申请时间</text>
<text class="invoice-info-value">{{ invoiceData.invoiceTime || '—' }}</text>
</view>
<view class="invoice-info-row">
<text class="invoice-info-label">票据金额</text>
<text class="invoice-info-value">¥{{ invoiceData.price || '—' }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 票据Webview弹窗(个人/单位会员) -->
<view v-if="showInvoiceWebview" class="invoice-popup-mask" @click="closeInvoiceWebview">
<view class="invoice-webview-content" @click.stop>
<view class="invoice-popup-header">
<text class="invoice-popup-title">票据</text>
<view class="invoice-popup-close" @click="closeInvoiceWebview"></view>
</view>
<view class="invoice-webview-body">
<web-view :src="invoiceWebviewUrl"></web-view>
</view>
</view>
</view>
<!-- 自定义删除确认弹窗 -->
<view v-if="showDelPopup" class="popup-mask" @touchmove.stop.prevent @click.stop="closeDelPopup">
<view class="custom-modal" @click.stop>
<view class="modal-title">提示</view>
<view class="modal-content">{{ delModalContent }}</view>
<view class="modal-btns">
<button class="modal-btn-cancel" @click="closeDelPopup">取消</button>
<button class="modal-btn-confirm" @click="confirmDel">确定</button>
</view>
</view>
</view>
<!-- 自定义取消订单确认弹窗 -->
<view v-if="showCancelPopup" class="popup-mask" @touchmove.stop.prevent @click.stop="closeCancelPopup">
<view class="custom-modal" @click.stop>
<view class="modal-title">提示</view>
<view class="modal-content">{{ cancelModalContent }}</view>
<view class="modal-btns">
<button class="modal-btn-cancel" @click="closeCancelPopup">取消</button>
<button class="modal-btn-confirm" @click="confirmCancel">确定</button>
</view>
</view>
</view>
<!-- 底部导航 -->
<dao-guan-tab-bar :currentIndex="1" @switch="onTabSwitch" />
</view>
</template>
<script setup>
import {ref, reactive, onMounted, computed} from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app'
import * as api from '@/common/api.js'
import config from '@/config.js'
import dayjs from 'dayjs'
import DaoGuanTabBar from '@/components/dao-guan-tab-bar.vue'
// 获取deptType值(初始值为0,在onMounted中设置实际值)
const deptType = ref(0);
// 标签栏配置(根据deptType动态生成)
const tabs = computed(() => {
const dt = Number(deptType.value)
console.log('tabs computed, deptType:', deptType.value, 'dt:', dt, 'dt===6:', dt === 6, 'dt==6:', dt == 6);
if (dt === 6) {
console.log('返回3个tab: 个人会员、单位会员、级位考试');
return [
{name: '个人会员', type: '0', label: "会员"},
{name: '单位会员', type: '1', label: "单位"},
{name: '级位考试', type: '2', label: "级位"}
];
} else if (dt === 2) {
console.log('返回3个tab: 单位会员、段位考试、越段考试');
return [
// {name: '单位会员', type: '1'},
{name: '段位考试', type: '3', label: "段位"},
{name: '越段考试', type: '4', label: "越段"}
];
} else if (dt === 1) {
console.log('返回默认5个tab, dt值为:', dt);
return [
{name: '个人会员', type: '0', label: "会员"},
{name: '单位会员', type: '1', label: "单位"},
{name: '级位考试', type: '2', label: "级位"},
{name: '段位考试', type: '3', label: "段位"},
{name: '越段考试', type: '4', label: "越段"}
];
} else {
return [
{name: '单位会员', type: '1', label: "单位"},
];
}
});
const currentTab = ref('0');
const currentStatus = ref('');
const statusTabs = [
{ name: '全部', type: '' },
{ name: '待缴费', type: '0' },
{ name: '缴费成功', type: '1' },
{ name: '订单取消', type: '2' }
]
// 数据与分页配置
const list = ref([]);
const loading = ref(false);
const hasMore = ref(true);
const pageNum = ref(1);
const pageSize = ref(10);
// 查询参数
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
type: '0',
queryType: '1',
payStatus: ''
});
// 弹窗控制
const showDelPopup = ref(false);
const showCancelPopup = ref(false);
const isPopupOpen = ref(false);
const showInvoicePopup = ref(false);
const showInvoiceWebview = ref(false);
const invoiceWebviewUrl = ref('');
const invoiceData = ref({});
// 弹窗内容
const delModalContent = ref('');
const cancelModalContent = ref('');
// 当前操作的订单
const currentOrder = ref(null);
// 页面挂载初始化
onLoad((option) => {
// 获取app实例和deptType
const app = getApp();
deptType.value = Number(app.globalData?.deptType || 0);
const firstType = tabs.value[0]?.type ?? '0';
// currentTab.value = option.type || firstType;
// queryParams.type = option.type || firstType;
currentTab.value = firstType;
queryParams.type = firstType;
initData();
});
// 页面显示时刷新数据(从开票页面返回时)
onShow(() => {
// 如果有缓存的刷新标记,则刷新列表
if (needRefresh.value) {
needRefresh.value = false;
pageNum.value = 1;
list.value = [];
hasMore.value = true;
initData();
}
});
// 是否需要刷新
const needRefresh = ref(false);
// 上拉加载更多
const loadMore = () => {
console.log("触发上拉加载");
if (loading.value || !hasMore.value || isPopupOpen.value) return;
pageNum.value++;
initData();
};
// 切换标签
const switchTab = (type) => {
currentTab.value = type;
queryParams.type = type;
pageNum.value = 1;
list.value = [];
hasMore.value = true;
initData();
};
const switchStatus = (type) => {
currentStatus.value = type;
queryParams.payStatus = type;
pageNum.value = 1;
list.value = [];
hasMore.value = true;
initData();
};
// 状态文本映射
const getStatusText = (status) => {
const map = {
0: '待缴费',
1: '缴费成功',
2: '订单取消'
};
return map[status] || '';
};
// 审核状态文本映射
const getAuditStatusText = (status) => {
const map = {
0: '待提交',
1: '审核中',
2: '审核通过',
3: '审核拒绝'
};
return map[status] || '';
};
const getOrderLabel = (item) => {
const map = {
0: '会员',
1: '单位',
2: '级位',
3: '段位',
4: '越段'
}
return map[item.type] || tabs.value.find(t => t.type === currentTab.value)?.label || '订单'
}
const filterTime = (row) => {
if (!row) return ''
return dayjs(row).format('YYYY年MM月DD日')
}
const filterType = (row) => {
if (row == 0) return '个人会员缴费办理'
if (row == 1) return '单位会员缴费办理'
if (row == 2) return '级位考试办理'
if (row == 3) return '段位考试办理'
if (row == 4) return '越位考试办理'
}
// 数据请求核心方法
const initData = async () => {
loading.value = true;
queryParams.pageNum = pageNum.value;
try {
const res = await api.orderList(queryParams);
console.log("接口返回:", res);
if (!res || !res.rows || res.rows.length === 0) {
hasMore.value = false;
loading.value = false;
return;
}
// 安全解析content字段
res.rows.forEach(item => {
if (item.content) {
try {
item.content = JSON.parse(item.content);
} catch (e) {
item.content = null;
}
}
});
// 第一页覆盖,后面页数追加
if (pageNum.value === 1) {
list.value = res.rows;
} else {
list.value.push(...res.rows);
}
// 关键修复:只要返回条数 < pageSize 就说明没有更多了
hasMore.value = res.rows.length === pageSize.value;
} catch (e) {
console.error('订单加载异常:', e);
uni.showToast({title: '加载失败', icon: 'none'});
hasMore.value = false;
} finally {
loading.value = false;
}
};
const handelSearch = () => {
pageNum.value = 1
list.value = []
initData()
}
// 删除订单
const handleDelete = (item) => {
currentOrder.value = item;
delModalContent.value = `是否确认删除订单编号为"${item.tradeNo}"的订单?`;
showDelPopup.value = true;
isPopupOpen.value = true;
};
// 确认删除
const confirmDel = async () => {
if (!currentOrder.value) return;
try {
await api.deleteOrder(currentOrder.value.id);
uni.showToast({title: '删除成功', icon: 'success'});
pageNum.value = 1;
list.value = [];
await initData();
closeDelPopup();
} catch (e) {
uni.showToast({title: '删除失败', icon: 'error'});
}
};
const goToDetail = (item) => {
console.log("goToDetail:", item);
console.log("currentTab.value", currentTab.value);
const form = encodeURIComponent(JSON.stringify(item))
switch (currentTab.value) {
case '1':
uni.navigateTo({url: `/group/groupOrderDetail?form=${form}`});
break;
case '2':
case '3':
case '4':
uni.navigateTo({url: `/pages/rank/applyDetail?examId=${item.sourceId || item.id}&type=${queryParams.type}`});
break;
case '0':
default:
uni.navigateTo({url: `/personalVip/orderDetail?rangeId=${item.sourceId || item.id}&type=${queryParams.type}`});
break;
}
// uni.navigateTo({url: `/pages/rank/applyDetail?examId=${item.sourceId || item.id}&type=${queryParams.type}`});
}
// 关闭删除弹窗
const closeDelPopup = () => {
showDelPopup.value = false;
isPopupOpen.value = false;
currentOrder.value = null;
};
// 去缴费
const handlePay = async (item) => {
if (item.payStatus !== 0) return;
try {
await api.goPay({id: item.id});
uni.navigateTo({url: `/pages/pay/pay?orderId=${item.id}`});
} catch (e) {
uni.showToast({title: '发起支付失败', icon: 'none'});
}
};
// 申请开票
const makeInvoiceFN = (item) => {
// 开票条件:缴费成功 && 未开票 && 审核通过
// if (item.payStatus !== 1 || item.invoiceStatus === 1 || item.auditStatus !== 2) {
// return;
// }
// 设置刷新标记,从开票页面返回时刷新列表
needRefresh.value = true;
// 根据tab类型决定跳转页面:个人/单位会员开非税票,级位/段位/越段考试开增值税票
let url = '';
if (currentTab.value === '0' || currentTab.value === '1') {
// 个人/单位会员 - 非税开票
url = `/pages/invoice/applyFeisui?orderId=${item.id}&amount=${item.price}`;
} else {
// 级位/段位/越段考试 - 增值税开票
url = `/pages/invoice/apply?orderId=${item.id}&amount=${item.price}`;
}
uni.navigateTo({ url });
};
// 查看发票
const viewInvoice = (item) => {
// 个人/单位会员(type 0或1)直接跳转webview页面展示发票
if (item.type === 0 || item.type === '0' || item.type === 1 || item.type === '1') {
if (item.invoiceUrl) {
const encodedUrl = encodeURIComponent(item.invoiceUrl);
uni.navigateTo({
url: `/pages/webview/webview?url=${encodedUrl}`
});
} else {
uni.showToast({ title: '暂无发票', icon: 'none' });
}
return;
}
// 其他类型显示发票信息弹窗
invoiceData.value = {
invoiceType: item.invoiceType || 1,
invoiceBuyerName: item.invoiceTitle || item.invoiceBuyerName || '—',
invoiceBuyerTaxno: item.invoiceTaxno || item.invoiceBuyerTaxno || '',
invoicePushPhone: item.invoiceEmail || item.invoicePushPhone || '—',
price: item.price || '-',
invoiceTime: item.invoiceTime || '—'
};
showInvoicePopup.value = true;
isPopupOpen.value = true;
};
// 关闭发票弹窗
const closeInvoicePopup = () => {
showInvoicePopup.value = false;
isPopupOpen.value = false;
};
// 关闭发票Webview弹窗
const closeInvoiceWebview = () => {
showInvoiceWebview.value = false;
isPopupOpen.value = false;
};
// 取消订单
const handleCancel = (item) => {
currentOrder.value = item;
cancelModalContent.value = `是否确认取消订单编号为"${item.tradeNo}"的订单?`;
showCancelPopup.value = true;
isPopupOpen.value = true;
};
// 确认取消订单
const confirmCancel = async () => {
if (!currentOrder.value) return;
try {
await api.cancelPay(currentOrder.value.id);
uni.showToast({title: '取消成功', icon: 'success'});
pageNum.value = 1;
list.value = [];
await initData();
closeCancelPopup();
} catch (e) {
uni.showToast({title: '取消失败', icon: 'error'});
}
};
// 关闭取消订单弹窗
const closeCancelPopup = () => {
showCancelPopup.value = false;
isPopupOpen.value = false;
currentOrder.value = null;
};
// 底部导航切换
const onTabSwitch = (index, url) => {
// tab switch handled by component
};
</script>
<style lang="scss" scoped>
.order-page {
background: #ededf0;
height: 100vh;
display: flex;
flex-direction: column;
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
&.no-scroll {
overflow: hidden;
height: 100vh;
}
}
.order-hero {
flex-shrink: 0;
padding: calc(env(safe-area-inset-top) + 90rpx) 44rpx 34rpx;
background: linear-gradient(135deg, #b00005 0%, #7a0000 100%);
color: #fff;
}
.hero-title-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 42rpx;
}
.hero-title {
font-size: 38rpx;
letter-spacing: 2rpx;
}
.hero-subtitle {
margin-top: 8rpx;
color: rgba(255, 255, 255, 0.58);
font-size: 16rpx;
}
.hero-page-title {
color: #f4b536;
font-size: 36rpx;
font-weight: 500;
text-align: right;
}
.title-line {
width: 54rpx;
height: 6rpx;
margin: 14rpx 0 0 auto;
border-radius: 10rpx;
background: #f4b536;
}
/* 搜索栏 */
.search-bar {
display: flex;
align-items: center;
height: 64rpx;
padding: 0;
border-radius: 34rpx;
background-color: #ffffff;
overflow: hidden;
.search-input {
flex: 1;
margin-right: 0;
:deep(.uni-easyinput__content) {
border-radius: 34rpx;
background-color: #fff;
height: 64rpx;
padding: 0 18rpx 0 28rpx;
}
:deep(.uni-easyinput__content-input) {
font-size: 28rpx;
color: #333;
}
}
.add-btn {
display: flex;
align-items: center;
justify-content: center;
width: 88rpx;
height: 52rpx;
margin-right: 6rpx;
background-color: #c91c34;
border-radius: 28rpx;
font-size: 24rpx;
font-weight: 700;
color: #ffffff;
}
.add-icon {
font-size: 36rpx;
margin-right: 10rpx;
font-weight: bold;
}
}
// 标签栏样式
.tab-bar {
display: flex;
flex-shrink: 0;
padding: 22rpx 30rpx 4rpx;
background: #ededf0;
flex-shrink: 0;
.tab-item {
flex: none;
margin-right: 48rpx;
padding: 0 0 14rpx;
font-size: 30rpx;
color: #666;
position: relative;
font-weight: bold;
&.active {
color: #c30d23;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 72rpx;
height: 4rpx;
background: #c30d23;
border-radius: 2rpx;
}
}
}
}
.status-filter {
display: flex;
align-items: center;
flex-shrink: 0;
padding: 10rpx 26rpx 18rpx;
background: #ededf0;
}
.status-filter-item {
height: 48rpx;
line-height: 48rpx;
margin-right: 18rpx;
padding: 0 24rpx;
border-radius: 8rpx;
background: #fff;
color: #777;
font-size: 26rpx;
}
.status-filter-item.active {
color: #c30d23;
font-weight: 700;
}
.filter-entry {
margin-left: auto;
display: flex;
flex-direction: column;
align-items: center;
color: #999;
font-size: 20rpx;
}
.filter-icon {
font-size: 28rpx;
line-height: 22rpx;
}
// 滚动列表容器
.order-list-scroll {
flex: 1;
height: auto;
overflow: auto;
background: #ededf0;
}
// 订单列表
.order-list {
padding: 0 24rpx 34rpx;
.order-card {
background: #fff;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
border-radius: 12rpx;
display: flex;
flex-direction: column;
// border-top: 6rpx solid transparent;
// background-clip: padding-box, border-box;
// background-origin: padding-box, border-box;
// background-image: linear-gradient(#fff, #fff), linear-gradient(90deg, #FF755A, #F51722);
}
}
// 卡片头部
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10rpx;
// margin-bottom: 20rpx;
// border-bottom: 1rpx dashed #eee;
.date {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 26rpx;
.date-text {
color: #666;
}
}
.status-tags {
display: flex;
gap: 10rpx;
}
.status-tag {
font-size: 22rpx;
//padding: 6rpx 16rpx;
//border-radius: 20rpx;
&.success {
//background: #e6f7ef;
color: #52c41a;
}
&.danger {
//background: #fff1f0;
color: #ff4d4f;
}
&.pending {
//background: #f5f5f5;
color: #999;
}
&.ml-10 {
margin-left: 10rpx;
}
&.status-wait {
//background: #f0f5ff;
color: #597ef7;
//border: 1rpx solid rgba(89, 126, 247, 0.3);
}
&.status-pending {
//background: #fff7e6;
color: #faad14;
//border: 1rpx solid rgba(250, 173, 20, 0.3);
}
&.status-success {
//background: #e6f7ef;
color: #52c41a;
//border: 1rpx solid rgba(82, 196, 26, 0.3);
}
&.status-danger {
//background: #fff1f0;
color: #ff4d4f;
//border: 1rpx solid rgba(232, 52, 29, 0.3);
}
}
}
// 基础信息行
.info-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 26rpx;
.label {
color: #999;
flex-shrink: 0;
width: 140rpx;
}
.value {
color: #333;
word-break: break-all;
}
}
.info-section {
background: #f3f6fc;
display: flex;
align-items: center;
justify-content: space-between;
//margin: 20rpx 0;
padding: 0 20rpx;
min-height: 100rpx;
border-radius: 20rpx;
}
.line {
width: 1rpx;
height: 60rpx;
background: #e5e5e5;
flex-shrink: 0;
}
.single-info {
flex: 1;
padding: 16rpx 20rpx;
border-radius: 8rpx;
font-size: 26rpx;
text-align: center;
.label {
color: #999;
text-align: center;
}
.value {
color: #333;
font-weight: 500;
text-align: center;
margin-top: 10rpx;
&.text-primary {
color: #597ef7;
}
&.text-success {
color: #52c41a;
}
&.text-danger {
color: #ff4d4f;
}
&.text-warning {
color: #faad14;
}
}
}
// 费用合计
.price-section {
// border-top: 1rpx dashed #eee;
// padding-top: 16rpx;
margin-top: 8rpx;
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8rpx 0;
.price-label {
font-size: 26rpx;
color: #333;
}
.price-value {
font-size: 26rpx;
color: #666;
&.text-success {
color: #52c41a;
}
&.text-warning {
color: #faad14;
}
}
}
}
// 按钮组
.btn-group {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16rpx;
width: 100%;
margin-top: 16rpx;
.more {
color: #999;
font-size: 26rpx;
font-weight: 700;
}
.btn-flex {
display: flex;
justify-content: flex-end;
gap: 16rpx;
}
.btn {
// 固定宽度,所有按钮一样大
width: 126rpx;
height: 44rpx;
line-height: 44rpx;
padding: 0;
border-radius: 10rpx;
font-size: 24rpx;
white-space: nowrap;
font-weight: bold;
border: none;
background: transparent;
text-align: center;
margin: 0;
&::after {
border: none;
display: none; // 关键:隐藏伪元素
}
&.btn-delete {
background: #fff;
//color: #e4393c;
color: #c30d23;
border: 1rpx solid #c30d23;
}
&.btn-invoice {
background: #fff;
color: #c30d23;
border: 1rpx solid #c30d23;
}
&.btn-view-invoice {
//background: linear-gradient(90deg, #FF755A, #F51722);
color: #c30d23;
//border: none;
border: 1rpx solid #c30d23;
}
&.btn-info {
color: #444;
border: 1rpx solid #d7d7d7;
background: #fff;
}
&.btn-cancel {
background: #fff;
color: #666;
border: 1rpx solid #ccc;
}
&.btn-pay {
//background: linear-gradient(90deg, #FF755A, #F51722);
color: #c30d23;
//border: none;
border: 1rpx solid #c30d23;
}
&:disabled {
opacity: 0.6;
pointer-events: none;
}
}
}
// 加载/无更多提示
.loading-tip, .no-more {
text-align: center;
padding: 20rpx 0;
color: #999;
font-size: 26rpx;
}
// 弹窗遮罩层
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
// 自定义弹窗样式
.custom-modal {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
padding: 40rpx 30rpx;
box-sizing: border-box;
text-align: center;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
}
.modal-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 30rpx;
}
.modal-content {
font-size: 30rpx;
color: #666;
line-height: 1.6;
margin-bottom: 40rpx;
word-break: break-word;
}
.modal-btns {
display: flex;
justify-content: space-between;
gap: 20rpx;
}
.modal-btn-cancel {
flex: 1;
height: 80rpx;
line-height: 80rpx;
background: #f5f5f5;
color: #666;
border-radius: 40rpx;
font-size: 32rpx;
border: none;
margin: 0;
padding: 0;
&::after {
border: none;
}
}
.modal-btn-confirm {
flex: 1;
height: 80rpx;
line-height: 80rpx;
background: #C4121B;
color: #fff;
border-radius: 40rpx;
font-size: 32rpx;
border: none;
margin: 0;
padding: 0;
&::after {
border: none;
}
}
.code-text {
font-size: 28rpx;
font-weight: 600;
color: #e8341d;
letter-spacing: 1rpx;
}
// 发票弹窗样式
.invoice-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.invoice-popup-content {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
}
.invoice-webview-content {
width: 90%;
height: 85vh;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.invoice-webview-body {
flex: 1;
overflow: hidden;
}
.invoice-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #AD181F 0%, #E4393C 100%);
.invoice-popup-title {
font-size: 32rpx;
font-weight: 600;
color: #fff;
}
.invoice-popup-close {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
}
.invoice-popup-body {
padding: 30rpx;
}
.invoice-type-badge {
display: inline-flex;
align-items: center;
padding: 8rpx 24rpx;
background: linear-gradient(135deg, #FF755A 0%, #F51722 100%);
color: #fff;
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 500;
&.vat-type {
background: linear-gradient(135deg, #6aaaf2 0%, #178cd7 100%);
}
}
.invoice-info-list {
.invoice-info-row {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 24rpx 0;
border-bottom: 1rpx dashed #eee;
&:last-child {
border-bottom: none;
}
.invoice-info-label {
font-size: 26rpx;
color: #999;
flex-shrink: 0;
}
.invoice-info-value {
font-size: 26rpx;
color: #333;
text-align: right;
word-break: break-all;
max-width: 340rpx;
}
}
}
.order-card-new {
background: #fff;
margin-bottom: 22rpx;
padding: 22rpx 18rpx 18rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
border-radius: 18rpx;
display: flex;
flex-direction: column;
// 卡片头部
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 10rpx;
// margin-bottom: 20rpx;
// border-bottom: 1rpx dashed #eee;
.date {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8rpx;
font-size: 26rpx;
.data-header {
display: flex;
}
.member-label {
color: #c30d23;
font-size: 28rpx;
font-weight: bold;
}
.value {
color: #000;
font-size: 27rpx;
font-weight: bold;
max-width: 460rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.tradeNo {
color: #999;
font-size: 24rpx;
}
}
.date-text {
color: #666;
}
}
.status-tags {
display: flex;
gap: 10rpx;
}
.status-tag {
font-size: 24rpx;
color: #999;
//padding: 6rpx 16rpx;
//border-radius: 20rpx;
&.success {
//background: #e6f7ef;
color: #52c41a;
}
&.danger {
//background: #fff1f0;
color: #ff4d4f;
}
&.pending {
//background: #f5f5f5;
color: #999;
}
&.ml-10 {
margin-left: 10rpx;
}
&.status-wait {
//background: #f0f5ff;
color: #597ef7;
//border: 1rpx solid rgba(89, 126, 247, 0.3);
}
&.status-pending {
//background: #fff7e6;
color: #faad14;
//border: 1rpx solid rgba(250, 173, 20, 0.3);
}
&.status-success {
//background: #e6f7ef;
color: #52c41a;
//border: 1rpx solid rgba(82, 196, 26, 0.3);
}
&.status-danger {
//background: #fff1f0;
color: #ff4d4f;
//border: 1rpx solid rgba(232, 52, 29, 0.3);
}
}
}
.member-time {
width: 100%;
display: flex;
justify-content: space-between;
padding-bottom: 4rpx;
.label {
max-width: 480rpx;
color: #555;
font-size: 26rpx;
font-weight: 700;
line-height: 1.4;
.star {
color: #777;
font-size: 26rpx;
}
}
.price {
min-width: 130rpx;
color: #333;
font-size: 26rpx;
font-weight: 500;
text-align: right;
.person {
font-size: 24rpx;
color: #999;
text-align: right;
}
}
}
}
</style>
<template>
<view class="person-dashboard">
<view class="hero">
<image class="hero-bg" :src="config.baseUrl_api + '/fs/static/img/red_bg.png'" mode="aspectFill"></image>
<view class="hero-nav">
<view>
<view class="brand-cn">中国跆拳道协会</view>
<view class="brand-en">CHINESE TAEKWONDO ASSOCIATION</view>
</view>
<view class="page-title">
<text>人员管理</text>
<view class="title-line"></view>
</view>
</view>
<view class="member-card">
<view class="member-content">
<view class="member-left">
<view class="hello">您好!</view>
<view class="member-name">{{ memberName }}</view>
<view class="member-desc">您已经是中国跆拳道协会的金牌会员了</view>
<view class="name-line"></view>
</view>
<view class="level-box" @click="goPath('/myCenter/reviewList')">
<image class="star" :src="config.baseUrl_api + '/fs/static/img/star.png'" mode="aspectFit"></image>
<view class="level-title">晋级考点</view>
<view class="detail-btn">查看详情</view>
</view>
</view>
</view>
</view>
<view class="main-actions">
<image class="actions-bg" :src="config.baseUrl_api + '/fs/static/img/red_bg2.png'" mode="aspectFill"></image>
<view class="member-bottom">
<view class="info-item">
<view class="info-label">单位会员编号</view>
<view class="info-value">{{ memberInfo.memCode }}</view>
</view>
<view class="info-item">
<view class="info-label">有效期</view>
<view class="info-value">{{ validityDate }}</view>
</view>
</view>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn01.png'" mode="widthFix" @click="goPath('/myCenter/examPointApplyList')"></image>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn02.png'" mode="widthFix" @click="goAuthPayV2"></image>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn03.png'" mode="widthFix" @click="goPath('/personalVip/addVip')"></image>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn04.png'" mode="widthFix" @click="goPath('/personalVip/payment')"></image>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn05.png'" mode="widthFix" @click="goPath('/personalVip/list')"></image>
<image class="action-card" :src="config.baseUrl_api + '/fs/static/img/btn06.png'" mode="widthFix" @click="goPath('/personalVip/mobilize')"></image>
</view>
<dao-guan-tab-bar :currentIndex="0" @switch="onTabSwitch" />
<uni-popup ref="authPayPopup" :mask-click="false" type="center">
<view class="dialog-wrapper">
<view class="dialog-title">提示</view>
<view class="dialog-message">{{ authPayPopupMsg }}</view>
<view class="dialog-footer">
<button class="dialog-btn confirm" @click="closeAuthPayDialog">确定</button>
</view>
</view>
</uni-popup>
<uni-popup ref="examPointPopup" :mask-click="false" >
<view class="dialog-wrapper">
<view class="dialog-title">申请成为考点</view>
<view class="dialog-message">
<text>恭喜您成为中国跆拳道协会单位会员!</text>
<text>根据协会考点管理办法,需成为考点单位才能进行考级业务的办理。</text>
<text>是否现在申请成为考点。</text>
</view>
<view class="dialog-footer">
<button class="dialog-btn cancel" @click="closeExamPointDialog">取消</button>
<button class="dialog-btn confirm" @click="goExamPointApply">去申请</button>
</view>
<view class="no-display" @click="handleNoDisplay">不再显示</view>
</view>
</uni-popup>
<uni-popup ref="passwordTipPopup" :mask-click="false" type="center">
<view class="dialog-wrapper">
<view class="dialog-title">温馨提示</view>
<view class="dialog-message">密码长期未更新,请及时更新</view>
<view class="dialog-footer">
<button class="dialog-btn confirm" @click="closePasswordTipDialog">确定</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import config from '@/config.js'
import { computed, ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import * as api from '@/common/api.js'
import { getInfo } from '@/common/login'
import DaoGuanTabBar from '@/components/dao-guan-tab-bar.vue'
const app = getApp()
const userType = ref('1')
const memberInfo = ref({})
const deptInfo = ref({})
const numData = ref({})
const isBlack = ref(0)
const authPayDisabled = ref(true)
const authPayPopup = ref(null)
const authPayPopupMsg = ref('')
const passwordTipPopup = ref(null)
const examPointPopup = ref(null)
const showDirectlyForAuthPay = ref(false)
const directUnderFlagForAuthPay = ref(0)
const associateIdForAuthPay = ref(0)
const memberName = computed(() => {
return memberInfo.value.name || deptInfo.value.deptName || app.globalData?.dept?.deptName || '--'
})
const memberCode = computed(() => {
return memberInfo.value.menCode || memberInfo.value.memberCode || memberInfo.value.memberNo || '--'
})
const validityDate = computed(() => {
const value = memberInfo.value.validityDate || memberInfo.value.validityEndDate || memberInfo.value.expireTime
return value ? String(value).slice(0, 10) : '--'
})
onLoad((option) => {
const userName = uni.getStorageSync('userName')
if (!userName) {
app.globalData.isLogin = false
uni.reLaunch({ url: '/login/login' })
return
}
if (option.scene) {
decodeURIComponent(option.scene)
}
if (uni.showShareMenu) {
uni.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
})
}
})
onShow(() => {
if (app.globalData.isLogin) {
init()
} else {
app.firstLoadCallback = () => {
init()
}
}
})
function init() {
api.getMyOwnMemberInfo().then(res => {
const data = res && res.data ? res.data : {}
app.globalData.authenticationStatus = data.authenticationStatus
app.globalData.memberInfo = data.memberInfo || {}
app.globalData.dept = data.dept || app.globalData.dept || {}
app.globalData.isExam = data.memberInfo?.isPoints
userType.value = app.globalData.userType
memberInfo.value = data.memberInfo || app.globalData.memberInfo || {}
deptInfo.value = data.dept || app.globalData.dept || {}
updateAuthPayDisabled(data)
updateAuthPayRule(data)
handleAccountStatus()
checkDialogs()
})
api.getRemindCount().then(res => {
numData.value = res.data || {}
})
api.getBlack().then(res => {
isBlack.value = res.data
})
}
function handleAccountStatus() {
console.log('handleAccountStatus22',userType.value , app.globalData.authenticationStatus)
if (userType.value != '1' && app.globalData.authenticationStatus != '2' && app.globalData.authenticationStatus != '4') {
uni.navigateTo({ url: '/pages/index/perfect' })
return
}
if (app.globalData.authenticationStatus == '5') {
const content = app.globalData.genFlag == 1 ? '您的会员已过期' : '会员已过期,请及时续费'
uni.showModal({
title: '提示',
content,
success: (res) => {
if (res.confirm && app.globalData.genFlag != 1) {
uni.navigateTo({ url: '/myCenter/auth' })
}
}
})
return
}
if (app.globalData.authenticationStatus == '4') {
uni.showModal({
title: '提示',
content: '你的会员即将过期,将会影响你的业务,请及时续费'
})
}
if (app.globalData.memberInfo?.activeStatus == 0) {
uni.showModal({
content: '账号未激活,请前去激活',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/myCenter/auth' })
}
}
})
}
}
function goPath(path) {
if (isBlack.value == '1') {
uni.showModal({
title: '提示',
content: '您的账号已被拉黑,请联系中跆协!'
})
return
}
if (app.globalData.authenticationStatus == '5') {
if (app.globalData.genFlag == 1) {
uni.showModal({
title: '提示',
content: '您的会员已过期'
})
} else {
uni.showModal({
title: '提示',
content: '会员已过期,请及时续费',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/myCenter/auth' })
}
}
})
}
return
}
if (app.globalData.memberInfo?.activeStatus == 0) {
uni.showModal({
title: '提示',
content: '账号未激活,请前去激活',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/myCenter/auth' })
}
}
})
return
}
uni.navigateTo({ url: path })
}
function updateAuthPayDisabled(data = {}) {
const authStatus = data.authenticationStatus
const resultNoProvince = data.resultNoProvince2
if (authStatus == 0) {
authPayDisabled.value = false
} else if (authStatus == 1) {
authPayDisabled.value = true
} else {
const canPay = resultNoProvince === true || resultNoProvince == 1
authPayDisabled.value = !canPay
}
}
function updateAuthPayRule(data = {}) {
const memberInfoData = data.memberInfo || {}
const associateId = Number(memberInfoData.associateId || 0)
showDirectlyForAuthPay.value = !associateId
directUnderFlagForAuthPay.value = Number(memberInfoData.directUnderFlag || 0)
associateIdForAuthPay.value = associateId
}
function canAuthPayByAccountStatus() {
return showDirectlyForAuthPay.value &&
directUnderFlagForAuthPay.value === 0 &&
associateIdForAuthPay.value === 0
}
function showAuthPayDialog(message) {
authPayPopupMsg.value = message
authPayPopup.value?.open()
}
function closeAuthPayDialog() {
authPayPopup.value?.close()
}
function closePasswordTipDialog() {
passwordTipPopup.value?.close()
}
function goAuthPayV2() {
if (!canAuthPayByAccountStatus()) {
showAuthPayDialog('当前账号状态暂无法办理缴费业务')
return
}
if (authPayDisabled.value) {
showAuthPayDialog('您有一笔缴费正在审核中,请勿重复缴费。您可前往【认证详情】查看审核进度。')
return
}
goPath('/myCenter/perfect')
}
function closeExamPointDialog() {
examPointPopup.value?.close()
}
function goExamPointApply() {
closeExamPointDialog()
uni.navigateTo({ url: '/myCenter/examPointApplyList' })
}
async function handleNoDisplay() {
await api.noDisplay()
await getInfo()
closeExamPointDialog()
}
function checkDialogs() {
const user = app.globalData.userInfo || {}
const memberInfoData = app.globalData.memberInfo || {}
if (app.globalData.changePassFlag == '1' &&
app.globalData.memberInfo?.activeStatus == '1' &&
app.globalData.authenticationStatus == 2) {
const hasShown = uni.getStorageSync('passwordTipShown')
if (!hasShown) {
uni.setStorageSync('passwordTipShown', true)
passwordTipPopup.value?.open()
}
}
console.log('checkDialogs',app.globalData.memberInfo?.activeStatus,app.globalData.authenticationStatus,app.globalData.deptType,memberInfoData.isPoints)
if (app.globalData.memberInfo?.activeStatus == 1 &&
app.globalData.authenticationStatus == 2 &&
app.globalData.deptType == 6 &&
memberInfoData.isPoints == 1 ) {
examPointPopup.value?.open()
}
}
const onTabSwitch = () => {
// tab switch handled by component
}
</script>
<style lang="scss" scoped>
.person-dashboard {
min-height: 100vh;
background: #ededf0;
overflow: hidden;
}
.hero {
position: relative;
min-height: 610rpx;
padding: calc(env(safe-area-inset-top) + 62rpx) 28rpx 0;
overflow: hidden;
padding-top: 150rpx;
}
.hero-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.hero-nav {
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
align-items: flex-end;
padding: 0 18rpx;
color: #fff;
}
.brand-cn {
font-size: 38rpx;
letter-spacing: 2rpx;
}
.brand-en {
margin-top: 8rpx;
font-size: 16rpx;
opacity: 0.55;
}
.page-title {
color: #f4b536;
font-size: 34rpx;
font-weight: 500;
text-align: right;
}
.title-line {
width: 54rpx;
height: 6rpx;
margin: 12rpx 0 0 auto;
background: #f4b536;
border-radius: 10rpx;
}
.member-card {
position: relative;
z-index: 1;
height: 460rpx;
margin-top: 47rpx;
border-radius: 22rpx;
overflow: hidden;
background: #E2CECF;
}
.member-content {
position: relative;
z-index: 1;
display: flex;
justify-content: space-between;
padding: 48rpx 46rpx 0;
}
.hello {
color: #ff6d40;
font-size: 52rpx;
line-height: 1;
}
.member-name {
margin-top: 28rpx;
color: #181818;
font-size: 34rpx;
font-weight: 700;
}
.member-desc {
margin-top: 10rpx;
color: #8f817f;
font-size: 22rpx;
}
.name-line {
width: 112rpx;
height: 6rpx;
margin-top: 14rpx;
background: #111;
border-radius: 8rpx;
}
.level-box {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 8rpx;
}
.star {
width: 100rpx;
height: 82rpx;
}
.level-title {
margin-top: 12rpx;
color: #d99b1d;
font-size: 28rpx;
font-weight: 700;
}
.detail-btn {
margin-top: 8rpx;
padding: 4rpx 14rpx;
border-radius: 4rpx;
background: rgba(255, 255, 255, 0.8);
color: #5a5552;
font-size: 20rpx;
font-weight: 600;
}
.member-bottom {
position: relative;
z-index: 12;
display: flex;
gap: 76rpx;
grid-column: 1 / -1;
padding: 98rpx 46rpx 34rpx;
padding-bottom: 50px;
}
.info-label {
color: #e55f73;
font-size: 24rpx;
font-weight: 600;
}
.info-value {
margin-top: 6rpx;
color: #343434;
font-size: 25rpx;
}
.main-actions {
position: relative;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8rpx 10rpx;
margin-top: -255rpx;
padding: 0 28rpx 0;
overflow: hidden;
}
.actions-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 596rpx;
z-index: 9;
}
.action-card {
position: relative;
z-index: 10;
width: 100%;
border-radius: 8rpx;
}
.dialog-wrapper {
width: 610rpx;
padding: 42rpx 34rpx 32rpx;
border-radius: 22rpx;
background: #fff;
box-sizing: border-box;
}
.dialog-title {
text-align: center;
color: #222;
font-size: 34rpx;
font-weight: 700;
}
.dialog-message {
display: flex;
flex-direction: column;
gap: 10rpx;
margin-top: 26rpx;
color: #555;
font-size: 28rpx;
line-height: 1.6;
text-align: center;
}
.dialog-footer {
display: flex;
gap: 24rpx;
margin-top: 36rpx;
}
.dialog-btn {
flex: 1;
height: 76rpx;
border-radius: 40rpx;
font-size: 28rpx;
line-height: 76rpx;
}
.dialog-btn.cancel {
color: #666;
background: #f2f2f2;
}
.dialog-btn.confirm {
color: #fff;
background: #ad181f;
}
.no-display {
margin-top: 24rpx;
color: #999;
font-size: 24rpx;
text-align: center;
}
</style>
......@@ -553,12 +553,13 @@
<view class="dialog-wrapper exam-dialog">
<view class="dialog-close" @click="closeExamPointDialog"></view>
<view class="dialog-icon success-icon">
<uni-icons color="#29c490" size="48" type="checkmark"></uni-icons>
<uni-icons color="#ffffff" size="40" type="check"></uni-icons>
</view>
<view class="dialog-title">申请成为考点</view>
<view class="dialog-message">
<text>恭喜您成为中国跆拳道协会团体会员!</text>
<text>恭喜您成为中国跆拳道协会单位会员!</text>
<text>根据协会考点管理办法,需成为考点单位才能进行考级业务的办理。</text>
<text>是否现在申请成为考点?</text>
</view>
<view class="dialog-footer">
<button class="dialog-btn cancel" @click="closeExamPointDialog">取消</button>
......@@ -1049,11 +1050,11 @@ function checkDialogs() {
refreshCaptcha()
bindingPhonePopup.value.open()
}
console.log(99,app.globalData.memberInfo?.activeStatus,app.globalData.authenticationStatus,user.hintFlag,app.globalData.deptType,memberInfoData.isPoints,app.globalData.deptType)
// 申请考点条件: activeStatus=1 && authenticationStatus=2 && hintFlag=1 && deptType=6 && isPoints=1
if (app.globalData.memberInfo?.activeStatus == 1 &&
app.globalData.authenticationStatus == 2 &&
user.hintFlag == 1 &&
app.globalData.deptType == 6 &&
memberInfoData.isPoints == 1) {
examPointPopup.value.open()
......
......@@ -14,6 +14,7 @@
} from '../../store/modules/user';
const userStore = useUserStore()
const app = getApp()
onShow(() => {
let user = userStore.user
......@@ -25,13 +26,23 @@
}
let userName = uni.getStorageSync('userName')
console.log('userName', userName)
console.log('app.globalData.userType ', app.globalData.userType )
if (userName) {
if(app.globalData.userType == 4){
uni.reLaunch({
url: '/pages/index/daoGuanPerson'
})
return
}else{
uni.reLaunch({
url: '/pages/index/home'
})
return
}
}
let webUserName = uni.getStorageSync('webUserName')
if (webUserName) {
uni.reLaunch({
......
......@@ -204,7 +204,6 @@ import {
import config from "/config.js";
import {
wxLogin,
logout,
getWebInfo
} from '@/common/login.js';
import {useUserStore} from "@/store/modules/user.js";
......@@ -238,6 +237,9 @@ const showConfirm = ref(false)
let hasOpenedBindPopup = false
onShow(() => {
// 重置绑定弹框标志,确保每次进入页面都能正确弹出
hasOpenedBindPopup = false
let webUserName = uni.getStorageSync('webUserName')
if (!webUserName) {
// 登录后需要等待数据加载完成
......@@ -245,7 +247,13 @@ onShow(() => {
getWebInfo().then(() => {
// 数据加载完成后检查是否需要弹出绑定框
checkAndOpenBindPopup()
}).catch(() => {
// getWebInfo 失败时也检查一下
checkAndOpenBindPopup()
})
}).catch(() => {
// wxLogin 失败时也检查一下
checkAndOpenBindPopup()
})
} else {
// 已登录,直接检查
......@@ -516,14 +524,23 @@ const cancelLogout = () => {
};
// 确认退出登录
const confirmLogout = () => {
// 调用退出登录接口
logout().then(() => {
const confirmLogout = async () => {
showConfirm.value = false
uni.showLoading({ title: '退出中...', mask: true })
// 调用解绑接口
await to(unbindUser())
// 清除缓存和用户信息
uni.removeStorageSync('webUserName')
userStore.setPerInfo(null)
userStore.setUser(null)
// 跳转到登录页
uni.reLaunch({
url: '/login/login'
})
});
};
</script>
......
<template>
<view class="change-level-page">
<view class="page-hero">
<view class="hero-title-row">
<view>
<view class="searchbar">
<uni-easyinput placeholderStyle="font-size:30rpx" :input-border="false" prefixIcon="search"
v-model="queryParams.code" placeholder="搜索变更单号" @blur="getList" @clear="getList">
</uni-easyinput>
<view class="invertedbtn-red" @click="goAdd">+ 新建级位变更</view>
<view class="hero-title">中国跆拳道协会</view>
<view class="hero-subtitle">CHINESE TAEKWONDO ASSOCIATION</view>
</view>
<view class="hero-page-title">
<text>级位变更审核</text>
<view class="title-line"></view>
</view>
</view>
<view class="appList">
<view class="appItem" v-for="(item,index) in list" :key="index">
<view class="status" @click="goDetail(item)">
<view>
<text v-if="item.status == 0" class="text-warning">待提交</text>
<text v-if="item.status == 1" class="text-primary">审核中</text>
<text v-if="item.status == 2" class="text-success">审核通过</text>
<text v-if="item.status == 3" class="text-danger">审核拒绝</text>
<text v-if="item.status == 4" class="text-warning">已撤回</text>
<view class="search-row">
<view class="search-box">
<uni-easyinput
v-model="queryParams.code"
:input-border="false"
class="search-input"
placeholder="输入变更单号"
placeholderStyle="font-size:28rpx;color:#999"
prefixIcon="search"
@confirm="getList"
@clear="getList"
/>
<view class="search-btn" @click="getList">搜索</view>
</view>
<view class="add-btn" @click="goAdd">
<text class="add-icon"></text>
<text>新建变更</text>
</view>
</view>
</view>
<view class="name mt0" @click="goDetail(item)">
<text class="text-primary">{{item.code}}</text>-{{item.shenMemName}}
<view class="status-tabs">
<view
v-for="tab in statusTabs"
:key="tab.value"
class="status-tab"
:class="{ active: activeStatus === tab.value }"
@click="switchStatus(tab.value)"
>
{{ tab.label }}
</view>
<view class="flexbox" @click="goDetail(item)">
<view>
变更人数
<view>
<text class="text-danger">{{item.count}}</text>
</view>
<scroll-view scroll-y class="list-scroll">
<view class="card-list">
<view v-for="item in list" :key="item.id" class="change-card">
<view class="card-top">
<view class="code-line">编号:{{ item.code || '--' }}</view>
<view class="status-text" :class="statusClass(item.status)">{{ statusText(item.status) }}</view>
</view>
<view class="w50">
提交时间
<view>{{item.commitTime||'--'}}</view>
<view class="org-name">{{ item.shenMemName || '--' }}</view>
<view class="info-grid">
<view class="info-cell">
<view class="info-label">变更人数</view>
<view class="info-value">{{ item.count || 0 }}</view>
</view>
<view class="info-cell time-cell">
<view class="info-label">变更时间</view>
<view class="info-value">{{ item.commitTime || '--' }}</view>
</view>
<view class="func" v-if="(item.status==0||item.status==3||item.status==4)">
<button @click="handleUpdate(item)">编辑</button>
<button @click="commitFN(item)">提交审核</button>
<button @click="handleDelete(item)">删除</button>
</view>
<view class="card-actions">
<view class="detail-link" @click="goDetail(item)">查看详情&gt;</view>
<view class="action-buttons">
<button class="action-btn delete" :disabled="!canEdit(item)" @click.stop="handleDelete(item)">删除</button>
<button class="action-btn edit" :disabled="!canEdit(item)" @click.stop="handleUpdate(item)">编辑</button>
<button class="action-btn submit" :disabled="!canEdit(item)" @click.stop="commitFN(item)">提交审核</button>
</view>
</view>
</view>
<view class="nodata" v-if="list.length==0">
<view v-if="list.length === 0" class="nodata">
<image mode="aspectFit" :src="config.baseUrl_api + '/fs/static/nodata.png'"></image>
<text>暂无数据</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import * as api from '@/common/api.js'
import config from '@/config.js'
import {
ref
} from 'vue'
import {
onLoad,
onShow
} from '@dcloudio/uni-app'
const app = getApp();
const queryParams = ref({
code:''
})
const list = ref([])
const total = ref(0)
onShow(()=>{
import * as api from '@/common/api.js'
import config from '@/config.js'
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
const queryParams = ref({
code: '',
status: ''
})
const list = ref([])
const total = ref(0)
const activeStatus = ref('')
const statusTabs = [
{ label: '全部', value: '' },
{ label: '待提交', value: '0' },
{ label: '审核中', value: '1' },
{ label: '已通过', value: '2' },
{ label: '已拒绝', value: '3' }
]
onShow(() => {
getList()
})
function goAdd(){
let path = `/personalVip/addChangeLevel`
uni.navigateTo({
url: path
});
})
function switchStatus(status) {
activeStatus.value = status
queryParams.value.status = status
getList()
}
function statusText(status) {
const map = {
0: '待提交',
1: '审核中',
2: '已通过',
3: '已拒绝',
4: '已撤回'
}
return map[status] || ''
}
function statusClass(status) {
const map = {
0: 'pending',
1: 'reviewing',
2: 'passed',
3: 'rejected',
4: 'pending'
}
function getList(){
return map[status] || ''
}
function canEdit(item) {
return item.status == 0 || item.status == 3 || item.status == 4
}
function goAdd() {
uni.navigateTo({
url: '/personalVip/addChangeLevel'
})
}
function getList() {
uni.showLoading({
title:'加载中'
title: '加载中'
})
api.getChangelevelList(queryParams.value).then(res=>{
list.value = res.rows
total.value = res.total
api.getChangelevelList(queryParams.value).then(res => {
list.value = res.rows || []
total.value = res.total || 0
}).finally(() => {
uni.hideLoading()
})
}
function goDetail(item){
let path = `/personalVip/changeLevelDetail?rangeId=${item.id}`
}
function goDetail(item) {
uni.navigateTo({
url: path
});
}
function handleUpdate(item){
// 编辑
let path = `/personalVip/addChangeLevel?rangeId=${item.id}`
url: `/personalVip/changeLevelDetail?rangeId=${item.id}`
})
}
function handleUpdate(item) {
uni.navigateTo({
url: path
});
}
function commitFN(row){
url: `/personalVip/addChangeLevel?rangeId=${item.id}`
})
}
function commitFN(row) {
uni.showModal({
title: '提示',
content: `确定提交吗`,
content: '确定提交吗',
success: function(res) {
if (res.confirm) {
api.commitLevelChange(row.id).then(Response=>{
api.commitLevelChange(row.id).then(() => {
uni.showToast({
icon:"none",
title:'提交成功!'
icon: 'none',
title: '提交成功!'
})
getList()
})
}
}
})
}
function handleDelete(row){
}
function handleDelete(row) {
uni.showModal({
title: '提示',
content: `确定删除吗`,
content: '确定删除吗',
success: function(res) {
if (res.confirm) {
api.levelModRangeDelete([row.id]).then(Response=>{
api.levelModRangeDelete([row.id]).then(() => {
uni.showToast({
icon:"none",
title:'删除成功!'
icon: 'none',
title: '删除成功!'
})
getList()
})
}
}
})
}
}
</script>
<style lang='scss' scoped>
.searchbar {
<style lang="scss" scoped>
.change-level-page {
min-height: 100vh;
background: #ededf0;
overflow: hidden;
}
.page-hero {
padding: calc(env(safe-area-inset-top) + 88rpx) 36rpx 28rpx;
background: linear-gradient(135deg, #b00005 0%, #760000 100%);
color: #fff;
}
.hero-title-row {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 40rpx;
}
.hero-title {
font-size: 38rpx;
letter-spacing: 2rpx;
}
.hero-subtitle {
margin-top: 8rpx;
color: rgba(255, 255, 255, 0.62);
font-size: 16rpx;
}
.hero-page-title {
color: #f4b536;
font-size: 36rpx;
font-weight: 500;
text-align: right;
}
.title-line {
width: 54rpx;
height: 6rpx;
margin: 14rpx 0 0 auto;
border-radius: 10rpx;
background: #f4b536;
}
.search-row {
display: flex;
align-items: center;
padding: 25rpx;
box-sizing: border-box;
gap: 16rpx;
}
:deep(.uni-easyinput .uni-easyinput__content) {
border-radius: 35rpx;
border: none;
height: 70rpx;
.search-box {
flex: 1;
display: flex;
align-items: center;
height: 64rpx;
overflow: hidden;
border-radius: 34rpx;
background: #fff;
}
.search-input {
flex: 1;
:deep(.uni-easyinput__content) {
height: 64rpx;
padding: 0 12rpx 0 22rpx;
border-radius: 34rpx;
background: #fff;
}
}
.search-btn {
width: 88rpx;
height: 52rpx;
margin-right: 6rpx;
border-radius: 28rpx;
background: #c91c34;
color: #fff;
font-size: 24rpx;
font-weight: 700;
line-height: 52rpx;
text-align: center;
}
.add-btn {
display: flex;
align-items: center;
justify-content: center;
width: 194rpx;
height: 64rpx;
border-radius: 34rpx;
background: #fff;
color: #222;
font-size: 28rpx;
font-weight: 700;
}
.add-icon {
margin-right: 6rpx;
color: #c30d23;
font-size: 40rpx;
line-height: 1;
}
.status-tabs {
display: flex;
align-items: center;
justify-content: space-around;
height: 92rpx;
background: #fff;
}
.status-tab {
position: relative;
height: 92rpx;
color: #666;
font-size: 30rpx;
font-weight: 700;
line-height: 92rpx;
}
.status-tab.active {
color: #c30d23;
}
:deep(.uni-easyinput__content-input) {
.status-tab.active::after {
content: '';
position: absolute;
left: 50%;
bottom: 18rpx;
width: 54rpx;
height: 4rpx;
border-radius: 4rpx;
background: #c30d23;
transform: translateX(-50%);
}
.list-scroll {
height: calc(100vh - 364rpx);
}
.card-list {
padding: 22rpx 30rpx 40rpx;
}
.change-card {
margin-bottom: 20rpx;
padding: 20rpx 20rpx 18rpx;
border-radius: 18rpx;
background: #fff;
}
.card-top {
display: flex;
align-items: center;
justify-content: space-between;
}
.code-line {
color: #0076ce;
font-size: 26rpx;
}
}
.status-text {
font-size: 26rpx;
font-weight: 700;
}
.status-text.pending {
color: #f0a000;
}
.status-text.reviewing {
color: #0076ce;
}
.invertedbtn-red {
border-radius: 50px;
background-color: #fff;
.status-text.passed {
color: #28a745;
}
.status-text.rejected {
color: #e60012;
}
.org-name {
margin-top: 8rpx;
color: #333;
font-size: 30rpx;
padding: 10rpx 20rpx;
}
}
.info-grid {
display: grid;
grid-template-columns: 1fr 1.5fr;
margin-top: 18rpx;
}
.info-label {
color: #999;
font-size: 24rpx;
}
.info-value {
margin-top: 4rpx;
color: #555;
font-size: 34rpx;
line-height: 1.2;
}
.time-cell .info-value {
font-size: 32rpx;
}
.card-actions {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
}
.detail-link {
color: #333;
font-size: 24rpx;
font-weight: 700;
}
.action-buttons {
display: flex;
align-items: center;
gap: 16rpx;
}
.action-btn {
width: 112rpx;
height: 42rpx;
margin: 0;
padding: 0;
border-radius: 10rpx;
background: #fff;
font-size: 24rpx;
line-height: 42rpx;
&::after {
border: none;
}
}
.action-btn.delete {
color: #e60012;
border: 1rpx solid #e60012;
}
.action-btn.edit {
color: #333;
border: 1rpx solid #999;
}
.action-btn.submit {
color: #0076ce;
border: 1rpx solid #0076ce;
}
.action-btn[disabled] {
color: #c9c9c9;
border-color: #e1e1e1;
background: #fff;
}
.nodata {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120rpx;
color: #999;
font-size: 28rpx;
image {
width: 220rpx;
height: 220rpx;
margin-bottom: 20rpx;
}
}
</style>
......
<template>
<view class="hasfixedbottom">
<view class="searchbar">
<uni-easyinput placeholderStyle="font-size:30rpx" :input-border="false" prefixIcon="search"
v-model="queryParams.perName" placeholder="搜索姓名或证件号码" @blur="getList" @clear="getList">
</uni-easyinput>
<view class="invertedbtn-red" @click="goVipList">+ 添加会员</view>
<view class="invertedbtn-red" @click="showAddPopup">+ 添加</view>
<view class="invertedbtn-red" @click="goVipList">+ 在线选择</view>
</view>
<view style="padding:0 20rpx">
......@@ -58,6 +60,33 @@
</view>
</view>
</uni-popup>
<!-- 添加会员弹框 -->
<uni-popup ref="addPopup" type="center">
<view class="add-popup">
<view class="popup-title">添加会员</view>
<view class="popup-form">
<view class="form-item">
<view class="form-label">证件类型</view>
<view class="form-input">
<picker :value="idcListIndex" :range="idcList" range-key="label" @change="onIdcTypeChange">
<view class="picker-value">{{ idcList[idcListIndex]?.label }}</view>
</picker>
</view>
</view>
<view class="form-item">
<view class="form-label">证件号</view>
<view class="form-input">
<input v-model="addForm.idcCode" placeholder="请输入证件号" placeholder-class="placeholder-class"/>
</view>
</view>
</view>
<view class="popup-btns">
<view class="popup-btn cancel" @click="closeAddPopup">取消</view>
<view class="popup-btn confirm" @click="confirmAdd">确定</view>
</view>
</view>
</uni-popup>
</view>
</template>
......@@ -78,7 +107,18 @@
const nowYear = ref(1)
const nowItem = ref({})
const pickView = ref(null)
const addPopup = ref(null)
const visible = ref(true)
const addForm = ref({
idcCode: '',
idType: '0'
})
const idcList = ref([
{ label: '身份证', value: '0' },
{ label: '来往大陆(内地)通行证', value: '1' },
{ label: '香港身份证', value: '5' }
])
const idcListIndex = ref(0)
const yearlist = ref([{
text: '一年',
value: 1
......@@ -197,6 +237,50 @@
url: `/myCenter/payOrder?rangeId=${queryParams.value.rangeId}`
})
}
// 显示添加弹框
function showAddPopup() {
addForm.value.idcCode = ''
idcListIndex.value = 0
addPopup.value.open()
}
// 关闭添加弹框
function closeAddPopup() {
addPopup.value.close()
}
// 证件类型选择
function onIdcTypeChange(e) {
idcListIndex.value = e.detail.value
addForm.value.idType = idcList.value[idcListIndex.value].value
}
// 确认添加
async function confirmAdd() {
if (!addForm.value.idcCode) {
uni.showToast({ title: '请输入证件号', icon: 'none' })
return
}
if (!queryParams.value.rangeId) {
uni.showToast({ title: '缺少rangeId', icon: 'none' })
return
}
try {
await api.memberInsertPersons({
rangeId: queryParams.value.rangeId,
year: 1,
idcCode: addForm.value.idcCode
})
uni.showToast({ title: '添加成功', icon: 'success' })
closeAddPopup()
getList()
getCount()
} catch (e) {
console.error(e)
}
}
</script>
<style scoped lang="scss">
......@@ -301,4 +385,86 @@
font-size: 32rpx;
border: none;
}
/* 添加会员弹框 */
.add-popup {
width: 600rpx;
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
}
.popup-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
text-align: center;
padding: 40rpx 30rpx 20rpx;
}
.popup-form {
padding: 20rpx 30rpx 40rpx;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.form-item:last-child {
margin-bottom: 0;
}
.form-label {
width: 140rpx;
font-size: 28rpx;
color: #333;
flex-shrink: 0;
}
.form-input {
flex: 1;
background: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx 24rpx;
}
.form-input input {
font-size: 28rpx;
color: #333;
width: 100%;
}
.picker-value {
font-size: 28rpx;
color: #333;
}
.placeholder-class {
color: #999;
}
.popup-btns {
display: flex;
border-top: 1rpx solid #eee;
}
.popup-btn {
flex: 1;
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 30rpx;
}
.popup-btn.cancel {
color: #666;
border-right: 1rpx solid #eee;
}
.popup-btn.confirm {
color: #E60012;
font-weight: 500;
}
</style>
\ No newline at end of file
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!