4cf9d409 by lttnew

我的订单

1 parent e97c4b0a
......@@ -349,6 +349,14 @@ export function addSelectPageList(query) {
})
}
// 获取个人技术认证详情
export function getPersonTecDetails(type, personId) {
return request({
url: `/person/technology/getPersonTecDetails/${type}/${personId}`,
method: 'get'
})
}
export function fillAuditLog(ids) {
return request({
url: `/person/paymentRange/fillAuditLog/${ids}`,
......@@ -1441,7 +1449,57 @@ export function bindUser(data) {
export function unbindUser() {
return request({
url: `/person/info/unbindUser`,
url: `/person/info/unBindUser`,
method: 'post'
})
}
/**
* 订单列表
* @param params
* @returns {*}
*/
export function orderList(params) {
return request({
url: `/common/order/list`,
method: 'get',
params
})
}
/**
* 删除订单
* @param params
* @returns {*}
*/
export function deleteOrder(id) {
return request({
url: `/common/order/${id}`,
method: 'delete'
})
}
/**
* 取消订单
* @param params
* @returns {*}
*/
export function cancelPay(id) {
return request({
url: `/person/paymentRangeNew/cancelPay/${id}`,
method: 'post'
})
}
export const outputInvoiceNo = (data) => {
return request({
url: `/common/order/outputInvoiceNo/${data.id}`,
method: 'post',
params: data
})
}
export function getAssoPers(perId) {
return request({
url: '/person/info/getRoleListByPerId/' + perId,
method: 'get'
})
}
\ No newline at end of file
......
......@@ -47,6 +47,7 @@ function logout() {
uni.removeStorageSync('token')
uni.removeStorageSync('userName')
uni.removeStorageSync('webUserName')
uni.removeStorageSync('openId')
userStore.setUser(null)
app.globalData.isLogin = false
......
// dev
const baseUrl_api = 'http://192.168.1.189:8787'
const baseUrl_api = 'http://192.168.1.137:8787'
const payUrl = 'https://wxpay.cmbc.com.cn/mobilePlatform/appserver/lcbpPay.do'
// prod
......
<template>
<view class="page">
<view class="bgbg">
<view class="flex">
<view class="imgbox">
<image v-if="state.user.avatar" :src="state.user.avatar"/>
<image v-else src="@/static/nodata.png"/>
</view>
<view class="flex">
<view class="imgbox">
<image v-if="state.user.avatar" :src="state.user.avatar"/>
<image v-else src="@/static/nodata.png"/>
</view>
<text class="name">{{ state.user.userName }}</text>
</view>
</view>
<view class="rMainBox">
<uni-list :border="false" class="myList">
<uni-list-item thumb="/static/user_icon01.png" title="团体信息" showArrow clickable @click="goPath('/myCenter/teamInfo')">
</uni-list-item>
<uni-list-item thumb="/static/user_icon02.png" title="会员认证" showArrow clickable @click="goPath('/myCenter/auth')">
</uni-list-item>
<!-- <uni-list-item thumb="/static/user_icon03.png" v-show="userType==2" title="账户信息" showArrow clickable>
</uni-list-item> -->
<uni-list-item thumb="/static/user_icon03.png" title="账号安全" showArrow clickable @click="goPath('/myCenter/safe')">
</uni-list-item>
</uni-list>
</view>
<view class="fixedBottom" style="background: transparent;box-shadow: none;">
<button @click="loginOut" class="btn btn-red" style="border-radius: 50px;">退出登录</button>
</view>
<view class="rMainBox">
<uni-list :border="false" class="myList">
<uni-list-item thumb="/static/user_icon01.png" title="团体信息" showArrow clickable @click="goPath('/myCenter/teamInfo')">
</uni-list-item>
<uni-list-item thumb="/static/user_icon02.png" title="会员认证" showArrow clickable @click="goPath('/myCenter/auth')">
</uni-list-item>
<!-- <uni-list-item thumb="/static/user_icon03.png" v-show="userType==2" title="账户信息" showArrow clickable>
</uni-list-item> -->
<uni-list-item thumb="/static/user_icon03.png" title="账号安全" showArrow clickable @click="goPath('/myCenter/safe')">
</uni-list-item>
<uni-list-item thumb="/static/user_icon03.png" title="我的订单" showArrow clickable @click="goPath('/myCenter/order')">
</uni-list-item>
</uni-list>
</view>
<view class="fixedBottom" style="background: transparent;box-shadow: none;">
<button @click="loginOut" class="btn btn-red" style="border-radius: 50px;">退出登录</button>
</view>
</view>
</template>
......@@ -60,11 +62,11 @@ let proId;
const svId = ref(null);
const numData = ref({});
const messageList = ref([])
const state = reactive({
user: {},
roleGroup: {},
postGroup: {}
const messageList = ref([])
const state = reactive({
user: {},
roleGroup: {},
postGroup: {}
})
onShow(() => {
if (app.globalData.isLogin) {
......@@ -80,12 +82,12 @@ onLoad(option => {
proId = decodeURIComponent(option.scene);
} else {
proId = option.proId;
}
}
if(uni.showShareMenu){
uni.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
});
});
}
});
......@@ -104,21 +106,21 @@ function loginOut() {
}
})
}
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 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
......@@ -126,31 +128,31 @@ function init() {
uni.hideLoading();
})
}
function goPath(url){
uni.navigateTo({
url:url
})
function goPath(url){
uni.navigateTo({
url:url
})
}
</script>
<style scope lang="scss">
<style scope lang="scss">
.uni-list:after{display: none;}
.page {
width: 100vw;
overflow: hidden;
}
.bgbg{
.flex{align-items: center;}
height: 280rpx;padding:30rpx;
.name{margin-left: 20rpx;
font-size: 36rpx;}
.imgbox{width: 120rpx;
height: 120rpx;overflow: hidden;
// background: #C7C7CD;
// border: 4rpx solid #FFFFFF;
border-radius: 50%;
image{height: 120rpx;width: 120rpx;object-fit: cover;}
}
.bgbg{
.flex{align-items: center;}
height: 280rpx;padding:30rpx;
.name{margin-left: 20rpx;
font-size: 36rpx;}
.imgbox{width: 120rpx;
height: 120rpx;overflow: hidden;
// background: #C7C7CD;
// border: 4rpx solid #FFFFFF;
border-radius: 50%;
image{height: 120rpx;width: 120rpx;object-fit: cover;}
}
}
.loginOutIcon {
position: relative;
......@@ -219,11 +221,11 @@ function goPath(url){
margin: 30rpx 0 20rpx;
padding: 0 20rpx 0;
}
}
.rMainBox {position: relative;top:-120rpx;
box-sizing: border-box;padding: 20rpx 20rpx;
background-color: #fff;
border-radius: 15rpx;
margin: 25rpx;overflow: hidden;
}
.rMainBox {position: relative;top:-120rpx;
box-sizing: border-box;padding: 20rpx 20rpx;
background-color: #fff;
border-radius: 15rpx;
margin: 25rpx;overflow: hidden;
}
</style>
\ No newline at end of file
......
<template>
<view class="order-page" :class="{ 'no-scroll': isPopupOpen }">
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: currentTab === index }"
@click="switchTab(index)"
>
{{ tab.name }}
</view>
</view>
<!-- 订单列表 -->
<scroll-view
scroll-y
class="order-list-scroll"
:enhanced="true"
:show-scrollbar="false"
:scroll-enabled="!isPopupOpen"
>
<view class="order-list">
<!-- 有数据才循环 -->
<view v-if="list.length > 0">
<view
v-for="(item, index) in list"
:key="index"
class="order-card"
>
<!-- 订单头部:日期 + 状态 -->
<view class="card-header">
<view class="date">
<image src="/static/calendar@2x.png" v-if="item.payTime" mode="widthFix" style="width:30rpx;height:30rpx;"/>
<text class="date-text" v-if="item.payTime">{{ item.payTime }}</text>
</view>
<view
class="status-tag"
:class="{
success: item.payStatus == 1,
danger: item.payStatus == 2,
pending: item.payStatus == 0
}"
>
{{ getStatusText(item.payStatus) }}
</view>
</view>
<!-- 订单编号、缴费编号 -->
<view class="info-row">
<text class="label">订单编号:</text>
<text class="value">{{ item.tradeNo || '——' }}</text>
</view>
<view class="info-row">
<text class="label">缴费编号:</text>
<text class="value">{{ item.wfCode || '——' }}</text>
</view>
<!-- 核心:前2tab仅展示缴费年限,后2tab仅展示人数合计 -->
<view class="info-section flex f-j-s" v-if="item.content">
<!-- 个人/单位会员(仅缴费年限) -->
<view v-if="currentTab === 0 || currentTab === 1" class="single-info">
<view class="label">缴费年限:</view>
<view class="value">{{ item.content.yearCount || 0 }}</view>
</view>
<view class="line"></view>
<!-- 级位/段位考试(仅人数合计) -->
<view v-if="currentTab === 2 || currentTab === 3" class="single-info">
<view class="label">人数合计</view>
<view class="value">{{ item.content.allPersonCount || 0 }}</view>
</view>
<view class="single-info">
<view class="label">缴费方式</view>
<view class="value">民生付</view>
</view>
</view>
<!-- 费用合计 -->
<view class="total-row">
<text class="label">费用合计:</text>
<text class="amount">¥{{ (Number(item.price) || 0).toFixed(2) }}</text>
</view>
<!-- 按钮组:靠右紧凑展示 -->
<view class="btn-group">
<button class="btn btn-delete" @click="handleDelete(item)">删除</button>
<!-- 已缴费:申请开票/已开票 -->
<template v-if="item.payStatus == 1">
<button class="btn btn-invoice" @click="makeInvoiceFN(item)" :disabled="item.invoiceStatus === 1">
{{ item.invoiceStatus === 1 ? '已开票' : '申请开票' }}
</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 v-else class="empty">
<text class="empty-text">暂无订单记录</text>
</view>
<!-- 加载/无更多提示 -->
<view class="loading-tip" v-if="loading">加载中...</view>
<view class="no-more" v-if="!loading && !hasMore && list.length">没有更多了</view>
</view>
</scroll-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>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { onReachBottom, } from '@dcloudio/uni-app'
import * as api from '@/common/api.js'
// 获取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' },
{ name: '单位会员', type: '1' },
{ name: '级位考试', type: '2' }
];
} else if (dt === 2) {
console.log('返回3个tab: 单位会员、段位考试、越段考试');
return [
{ name: '单位会员', type: '1' },
{ name: '段位考试', type: '3' },
{ name: '越段考试', type: '4' }
];
} else {
console.log('返回默认5个tab, dt值为:', dt);
return [
{ name: '个人会员', type: '0' },
{ name: '单位会员', type: '1' },
{ name: '级位考试', type: '2' },
{ name: '段位考试', type: '3' },
{ name: '越段考试', type: '4' }
];
}
});
const currentTab = ref(0);
// 数据与分页配置
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 delModalContent = ref('');
const cancelModalContent = ref('');
// 当前操作的订单
const currentOrder = ref(null);
// 页面挂载初始化
onMounted(() => {
// 获取app实例和deptType
const app = getApp();
console.log('onMounted - app:', app);
console.log('onMounted - app.globalData:', app.globalData);
deptType.value = Number(app.globalData?.deptType || 0);
console.log('onMounted - deptType.value:', deptType.value, typeof deptType.value);
initData();
});
// 小程序原生触底加载
onReachBottom(() => {
if (!loading.value && hasMore.value && !isPopupOpen.value) {
pageNum.value++;
initData();
}
});
// 切换标签
const switchTab = (index) => {
currentTab.value = index;
queryParams.type = tabs.value[index].type;
pageNum.value = 1;
list.value = [];
hasMore.value = true;
initData();
};
// 状态文本映射
const getStatusText = (status) => {
const map = {
0: '待缴费',
1: '缴费成功',
2: '订单取消'
};
return map[status] || '未知状态';
};
// 数据请求核心方法
const initData = async () => {
loading.value = true;
queryParams.pageNum = pageNum.value;
try {
const res = await api.orderList(queryParams);
if (!res || !res.rows) {
list.value = [];
hasMore.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);
}
// 判断是否还有更多数据
hasMore.value = list.value.length < (res.total || 0);
} catch (e) {
console.error('订单加载异常:', e);
uni.showToast({ title: '加载失败', icon: 'none' });
} finally {
loading.value = false;
}
};
// 删除订单
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 = [];
initData();
closeDelPopup();
} catch (e) {
uni.showToast({ title: '删除失败', icon: 'error' });
}
};
// 关闭删除弹窗
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) return;
uni.navigateTo({ url: `/pages/invoice/apply?orderId=${item.id}amount=${item.price}` });
};
// 取消订单
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 = [];
initData();
closeCancelPopup();
} catch (e) {
uni.showToast({ title: '取消失败', icon: 'error' });
}
};
// 关闭取消订单弹窗
const closeCancelPopup = () => {
showCancelPopup.value = false;
isPopupOpen.value = false;
currentOrder.value = null;
};
</script>
<style lang="scss" scoped>
.order-page {
background: #f5f7fa;
min-height: 100vh;
display: flex;
flex-direction: column;
&.no-scroll {
overflow: hidden;
height: 100vh;
}
}
// 标签栏样式
.tab-bar {
display: flex;
background: #fff;
border-bottom: 1rpx solid #eee;
flex-shrink: 0;
.tab-item {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #666;
position: relative;
&.active {
color: #e4393c;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background: linear-gradient(90deg, #FF755A, #F51722);
border-radius: 2rpx;
}
}
}
}
// 滚动列表容器
.order-list-scroll {
flex: 1;
height: 0;
}
// 订单列表
.order-list {
padding: 20rpx;
.order-card {
background: #fff;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
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: 20rpx;
margin-bottom: 20rpx;
border-bottom: 1rpx dashed #eee;
.date {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 26rpx;
.date-text {
color: #666;
}
}
.status-tag {
font-size: 24rpx;
padding: 6rpx 16rpx;
border-radius: 20rpx;
&.success {
background: #e6f7ef;
color: #52c41a;
}
&.danger {
background: #fff1f0;
color: #ff4d4f;
}
&.pending {
background: #f5f5f5;
color: #999;
}
}
}
// 基础信息行
.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;
padding: 0 40rpx;
justify-content: space-around;
margin: 20rpx 0;
}
.line{
width: 1rpx;
height: 90%;
background: #eee;
}
.single-info {
padding: 16rpx 20rpx;
border-radius: 8rpx;
font-size: 26rpx;
// border-right: 1rpx solid #eee;
.label {
color: #999;
text-align: center;
}
.value {
color: #333;
font-weight: 500;
text-align: center;
margin-top: 10rpx;
}
}
// 费用合计
.total-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 0 30rpx;
padding: 15rpx 0;
font-size: 28rpx;
border-bottom: 1rpx dashed #eee;
.label {
color: #333;
}
.amount {
color: #EB6100;
font-weight: 600;
font-size: 32rpx;
}
}
// 按钮组
.btn-group {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16rpx;
width: 100%;
.btn {
padding: 12rpx 32rpx;
border-radius: 40rpx;
font-size: 24rpx;
line-height: 1.5;
white-space: nowrap;
display: inline-block;
margin: 0;
border: none;
width: 80px;
background: transparent;
&::after {
border: none;
}
&.btn-delete {
background: #fff;
color: #e4393c;
border: 1rpx solid #e4393c;
}
&.btn-invoice {
background: #fff;
color: #e4393c;
border: 1rpx solid #e4393c;
}
&.btn-cancel {
background: #fff;
color: #666;
border: 1rpx solid #ccc;
}
&.btn-pay {
background: linear-gradient(90deg, #FF755A, #F51722);
color: #fff;
border: none;
}
&:disabled {
opacity: 0.6;
pointer-events: none;
}
}
}
// 空状态
.empty {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 0;
.empty-text {
color: #999;
font-size: 28rpx;
}
}
// 加载/无更多提示
.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;
}
}
</style>
\ No newline at end of file
......@@ -13,11 +13,25 @@
}
},
{
"path": "pages/webview/webview",
"style": {
"navigationBarTitleText": "中国跆拳道协会",
"enablePullDownRefresh": false
}
},
{
"path": "pages/index/msgList",
"style": {
"navigationBarTitleText": "待办列表",
"enablePullDownRefresh": false
}
},
{
"path": "pages/invoice/apply",
"style": {
"navigationBarTitleText": "开具发票",
"enablePullDownRefresh": false
}
}, {
"path": "pages/rank/approval",
"style": {
......@@ -165,12 +179,40 @@
}
},
{
"path": "home",
"style": {
"navigationBarTitleText": "个人会员中心",
"enablePullDownRefresh": false
}
"path": "home",
"style": {
"navigationBarTitleText": "个人会员中心",
"enablePullDownRefresh": false
}
},
{
"path": "personInfo",
"style": {
"navigationBarTitleText": "人员信息",
"enablePullDownRefresh": false
}
},
{
"path": "memberInfo",
"style": {
"navigationBarTitleText": "个人会员信息",
"enablePullDownRefresh": false
}
},
{
"path": "levelRecord",
"style": {
"navigationBarTitleText": "级位记录",
"enablePullDownRefresh": false
}
},
{
"path": "order",
"style": {
"navigationBarTitleText": "我的订单",
"enablePullDownRefresh": false
}
}
]
},
{
......@@ -684,6 +726,13 @@
}
},
{
"path": "order",
"style": {
"navigationBarTitleText": "我的订单",
"enablePullDownRefresh": false
}
},
{
"path": "reviewList",
"style": {
"navigationBarTitleText": "审核详情",
......
<template>
<view class="invoice-apply">
<view class="content">
<!-- 发票类型 -->
<view class="form-item">
<text class="label">发票类型</text>
<text class="value">{{ form.invoiceType === '2' ? '普通发票(企业)' : '普通发票(个人)' }}</text>
</view>
<!-- 发票抬头 -->
<view class="form-item">
<text class="label">发票抬头</text>
<input
class="input"
v-model="form.name"
placeholder="请输入公司全称或个人姓名"
placeholder-style="color: #999;"
/>
</view>
<!-- 纳税人识别号(企业才显示) -->
<view class="form-item" v-if="form.invoiceType === '2'">
<text class="label">纳税人识别号</text>
<input
class="input"
v-model="form.taxno"
placeholder="请输入纳税人识别号"
placeholder-style="color: #999;"
maxlength="20"
/>
</view>
<!-- 开票金额 -->
<view class="form-item">
<text class="label">开票金额</text>
<text class="amount">¥ {{ (Number(form.amount)).toFixed(2) }}</text>
</view>
<!-- 接收方式 -->
<view class="form-item">
<text class="label">接收方式</text>
<text class="value">电子邮箱</text>
</view>
<!-- 接收邮箱 -->
<view class="form-item">
<text class="label">接收邮箱</text>
<input
class="input"
v-model="form.email"
placeholder="请输入接收发票的邮箱(必填)"
placeholder-style="color: #999;"
/>
</view>
</view>
<view class="hint">电子发票将在3-5个工作日内发送至该邮箱</view>
<!-- 提交按钮 -->
<view class="btn-wrap" @click="submitInvoice">
<view class="submit-btn">提交申请</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { outputInvoiceNo } from '@/common/api.js'; // 与PC端接口一致
// 表单数据(与PC端字段完全对齐)
const form = reactive({
invoiceType: '1', // 1=个人 2=企业
name: '', // 发票抬头
taxno: '', // 纳税人识别号
email: '', // 邮箱
amount: 0, // 金额
id: '' // 订单ID
});
// 页面加载(接收PC端传来的参数)
onLoad((options) => {
if (options.id) {
form.id = options.id;
form.amount = options.amount;
console.log(33,form.amount);
}
if (options.invoiceType) {
form.invoiceType = options.invoiceType;
}
// getOrderInfo();
});
// 获取订单金额
// const getOrderInfo = async () => {
// try {
// // 这里替换成你真实获取订单金额的接口
// = 1500;
// } catch (error) {
// uni.showToast({ title: '获取订单信息失败', icon: 'none' });
// }
// };
// 提交发票申请(与PC逻辑完全一致)
const submitInvoice = async () => {
// 1. PC端逻辑:个人不允许开票
if (form.invoiceType === '1') {
return uni.showToast({ title: '暂不支持个人开票', icon: 'none' });
}
// 2. 抬头校验
if (!form.name) {
return uni.showToast({ title: '请输入发票抬头', icon: 'none' });
}
// 3. 企业必须填纳税人识别号
if (form.invoiceType === '2' && !form.taxno) {
return uni.showToast({ title: '请输入纳税人识别号', icon: 'none' });
}
// 4. 纳税人识别号格式校验(同PC)
const taxReg = /^[A-Z0-9]{15}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
if (form.invoiceType === '2' && !taxReg.test(form.taxno)) {
return uni.showToast({ title: '纳税人识别号格式不正确', icon: 'none' });
}
// 5. 邮箱校验
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!form.email) {
return uni.showToast({ title: '请输入接收邮箱', icon: 'none' });
}
if (!emailReg.test(form.email)) {
return uni.showToast({ title: '请输入正确的邮箱地址', icon: 'none' });
}
try {
// 调用PC端同一个接口:outputInvoiceNo
await outputInvoiceNo(form);
uni.showToast({
title: '发票申请提交成功!',
icon: 'success',
duration: 2000
});
setTimeout(() => {
uni.navigateBack();
}, 2000);
} catch (error) {
uni.showToast({ title: '提交失败,请检查信息', icon: 'none' });
}
};
</script>
<style lang="scss" scoped>
.invoice-apply {
min-height: 100vh;
background: #f5f7fa;
.content {
padding: 20rpx;
.form-item {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
padding: 24rpx;
border-bottom: 1rpx solid #eee;
.label {
font-size: 28rpx;
color: #333;
}
.input {
width: 80%;
font-size: 28rpx;
color: #333;
padding: 16rpx 0;
text-align: right;
}
.value {
font-size: 28rpx;
color: #333;
}
.amount {
font-size: 28rpx;
color: #e4393c;
font-weight: 500;
}
}
}
.hint {
font-size: 26rpx;
color: #B6BCC0;
margin-top: 20rpx;
text-align: center;
}
.btn-wrap {
width: 100%;
background-color: #fff;
padding: 30rpx;
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
.submit-btn {
height: 70rpx;
line-height: 70rpx;
border-radius: 35rpx;
width: 90%;
margin: 0 auto;
background: #AD181F;
color: #fff;
font-size: 28rpx;
text-align: center;
}
}
</style>
\ No newline at end of file
<template>
<view class="container">
<web-view :src="url" @message="handleMessage"></web-view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app'
const url = ref('');
onLoad((options) => {
if (options.url) {
url.value = decodeURIComponent(options.url);
}
});
const handleMessage = (event) => {
console.log('收到消息:', event.detail.data);
};
</script>
<style scoped>
.container {
width: 100%;
height: 100vh;
}
</style>
\ No newline at end of file
......@@ -6,22 +6,24 @@
</image>
<!-- 绑定/解绑学员 -->
<view v-if="!isBound" class="bind-student" @click="handleBindAction">
<text>{{ isBound ? '解绑' : '绑定学员' }}</text>
<image class="arrow-icon" :src="config.baseUrl_api + '/fs/static/slices/bd@2x.png'" mode="aspectFit">
</image>
<view class="bind-student" @click="handleBindAction">
<text>{{ isBound ? '解绑学员' : '绑定学员' }}</text>
<image src="/static/bd@2x.png" class="bind-icon" mode="aspectFit"></image>
</view>
<!-- 用户信息 -->
<view class="user-section">
<view class="user-top">
<view class="avatar-wrap">
<image class="avatar" :src="config.baseUrl_api + '/fs/static/slices/tx@2x.png'"
mode="aspectFill">
</image>
</view>
<view class="member-id">{{ userInfo.userName }}</view>
<view class="avatar-wrap" @click="showLogoutConfirm">
<!-- <image class="avatar" v-if="perInfo.photo" :src="config.baseUrl_api + perInfo.photo"
mode="aspectFill">
</image> -->
<image class="avatar" :src="config.baseUrl_api + '/fs/static/slices/tx@2x.png'"
mode="aspectFill">
</image>
</view>
<view class="member-id">注册会员{{ userInfo.userName }}</view>
</view>
<view class="user-bottom">
<view class="user-name">{{ perInfo?.perName }}</view>
<view class="card-info">
......@@ -32,7 +34,7 @@
</view>
<!-- 已过期印章 -->
<image v-if="perInfo?.perValidDateFlag && perInfo?.perValidDateFlag!='1'" class="expired-stamp"
<image v-if="perInfo?.perValidDateFlag == 0" class="expired-stamp"
:src="config.baseUrl_api + '/fs/static/slices/end@2x.png'" mode="aspectFit">
</image>
</view>
......@@ -41,55 +43,122 @@
<view class="func-card">
<view class="func-list">
<view class="func-item" @click="goToAuth">
<image class="func-icon" :src="config.baseUrl_api + '/fs/static/slices/btn01@2x.png'"
mode="aspectFit">
</image>
<text class="func-text">参赛能力认证</text>
<view class="func-icon">
<image src="/static/btn01@2x.png"></image>
<!-- <uni-icons type="contact" size="40" color="#1E88E5"></uni-icons> -->
</view>
<text class="func-text">人员信息</text>
</view>
<view class="func-item" @click="goToScore">
<image class="func-icon" :src="config.baseUrl_api + '/fs/static/slices/btn02@2x.png'"
mode="aspectFit">
</image>
<text class="func-text">成绩查询</text>
<view class="func-icon">
<image src="/static/btn02@2x.png"></image>
<!-- <uni-icons type="person" size="40" color="#1E88E5"></uni-icons> -->
</view>
<text class="func-text">个人会员</text>
</view>
<view class="func-item" @click="goToRecord">
<image class="func-icon" :src="config.baseUrl_api + '/fs/static/slices/btn03@2x.png'"
mode="aspectFit">
</image>
<text class="func-text">参赛记录</text>
<view class="func-icon">
<image src="/static/btn03@2x.png"></image>
<!-- <uni-icons type="list" size="40" color="#1E88E5"></uni-icons> -->
</view>
<text class="func-text">级位记录</text>
</view>
</view>
</view>
<!-- 人员信息区域 -->
<view class="info-section">
<view class="section-title">
<view class="title-bar"></view>
<text class="title-text">人员信息</text>
<!-- 查询功能区域 -->
<view class="query-section">
<view class="query-item" @click="goToOrder">
<view class="query-item-left">
<image src="/static/user_icon01@2x.png" class="query-item-icon"></image>
<text class="query-item-text">我的订单</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="info-card">
<view class="info-item">
<text class="item-label">姓名</text>
<text class="item-value">{{ perInfo?.perName }}</text>
<view class="query-item" @click="goToWebView(1)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">单位会员查询</text>
</view>
<view class="info-item">
<text class="item-label">证件类型</text>
<text class="item-value">{{ perInfo?.idcTypeStr }}</text>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(2)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">个人会员查询</text>
</view>
<view class="info-item">
<text class="item-label">身份证号</text>
<text class="item-value">{{ perInfo?.perIdcCode }}</text>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(3)">
<view class="query-item-left">
<image src="/static/user_icon02@2x.png" class="query-item-icon"></image>
<text class="query-item-text">旧版级位证书查询</text>
</view>
<view class="info-item">
<text class="item-label">生日</text>
<text class="item-value">{{ perInfo?.birth }}</text>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(4)">
<view class="query-item-left">
<image src="/static/user_icon02@2x.png" class="query-item-icon"></image>
<text class="query-item-text">新版级位证书查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(5)">
<view class="query-item-left">
<image src="/static/user_icon02@2x.png" class="query-item-icon"></image>
<text class="query-item-text">级位记录查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(6)">
<view class="query-item-left">
<image src="/static/user_icon02@2x.png" class="query-item-icon"></image>
<text class="query-item-text">国际段位证书查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(7)">
<view class="query-item-left">
<image src="/static/user_icon02@2x.png" class="query-item-icon"></image>
<text class="query-item-text">中国段位证书查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(8)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">级位考官查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(9)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">段位考官查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(10)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">大众教练员查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(11)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">大众裁判员查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
<view class="query-item" @click="goToWebView(12)">
<view class="query-item-left">
<image src="/static/user_icon03@2x.png" class="query-item-icon"></image>
<text class="query-item-text">培训讲师查询</text>
</view>
<uni-icons type="arrowright" size="20" color="#999"></uni-icons>
</view>
</view>
<!-- 退出登录按钮 -->
<view class="logout-section">
<view class="logout-btn" @click="handleLogout">退出登录</view>
</view>
<!-- 绑定学员弹框 -->
......@@ -117,6 +186,20 @@
</view>
</view>
</uni-popup>
<!-- 退出登录确认框 -->
<view v-if="showConfirm" class="confirm-mask" @click.stop="cancelLogout">
<view class="confirm-box" @click.stop>
<view class="confirm-content">
<view class="confirm-title">退出登录</view>
<view class="confirm-text">确定要退出登录吗?</view>
</view>
<view class="confirm-buttons">
<view class="confirm-btn cancel" @click="cancelLogout">取消</view>
<view class="confirm-btn confirm" @click="confirmLogout">确定</view>
</view>
</view>
</view>
</view>
</template>
......@@ -146,6 +229,8 @@
const userStore = useUserStore()
const userInfo = computed(() => userStore.user)
const perInfo = computed(() => userStore.perInfo)
console.log(222,userInfo.value)
console.log(333,perInfo.value)
// 是否已绑定学员
const isBound = computed(() => {
......@@ -154,10 +239,11 @@
})
const bindPopup = ref(null)
const bindForm = ref({
name: '',
idcCode: ''
})
const bindForm = ref({
name: '',
idcCode: ''
})
const showConfirm = ref(false)
onMounted(() => {
let webUserName = uni.getStorageSync('webUserName')
......@@ -255,10 +341,6 @@
uni.hideLoading()
if (err) {
// uni.showToast({
// title: '绑定失败',
// icon: 'none'
// })
return
}
......@@ -267,37 +349,69 @@
icon: 'success'
})
closeBindPopup()
// 刷新用户信息
getWebInfo()
}
// 返回上一页
const goBack = () => {
uni.navigateBack()
}
const goToAuth = () => {
console.log("参赛能力认证");
uni.navigateTo({
url: '/personal/personInfo'
});
};
const goToScore = () => {
console.log("成绩查询");
uni.navigateTo({
url: '/personal/memberInfo'
});
};
const goToWebView = (type) => {
const url = "https://member.taekwondo.org.cn/#/authAccurate?type=" + type
uni.navigateTo({
url: "/pages/webview/webview?url=" + encodeURIComponent(url)
});
};
// 导航到级位记录
const goToRecord = () => {
console.log("参赛记录");
uni.navigateTo({
url: '/personal/levelRecord'
});
};
// 退出登录
const handleLogout = () => {
uni.showModal({
content: `确认退出吗?`,
success: function(res) {
if (res.confirm) {
logout().then(() => {
uni.reLaunch({
url: '/login/login'
})
})
}
}
})
}
// 导航到我的订单
const goToOrder = () => {
uni.navigateTo({
url: '/personal/order'
});
};
// 显示退出登录确认框
const showLogoutConfirm = () => {
showConfirm.value = true;
};
// 取消退出登录
const cancelLogout = () => {
showConfirm.value = false;
};
// 确认退出登录
const confirmLogout = () => {
// 调用退出登录接口
logout().then(() => {
// 跳转到登录页
uni.reLaunch({
url: '/login/login'
})
});
};
</script>
<style lang="scss" scoped>
......@@ -307,6 +421,38 @@
padding-bottom: 40rpx;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background: #ffffff;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.nav-right {
width: 44rpx;
}
/* 会员卡 */
.member-card {
position: relative;
......@@ -333,19 +479,19 @@
gap: 8rpx;
z-index: 10;
padding: 10rpx 16rpx;
background: rgba(255, 255, 255, 0.5);
// background: rgba(255, 255, 255, 0.5);
border-radius: 30rpx;
}
.bind-student text {
font-size: 26rpx;
color: #8b7355;
}
font-size: 26rpx;
color: #5c4b37;
}
.arrow-icon {
width: 28rpx;
height: 28rpx;
}
.bind-icon {
width: 45rpx;
height: 45rpx;
}
.user-section {
position: relative;
......@@ -373,7 +519,7 @@
.member-id {
font-size: 26rpx;
color: #8b7355;
color: #503000;
}
.user-bottom {
......@@ -394,8 +540,8 @@
}
.info-row {
font-size: 24rpx;
color: #8b7355;
font-size: 28rpx;
color: #503000;
}
/* 已过期印章 */
......@@ -435,6 +581,15 @@
.func-icon {
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
// background: #f0f7ff;
// border-radius: 16rpx;
image{
width: 72rpx;
height: 72rpx;
}
}
.func-text {
......@@ -442,59 +597,43 @@
color: #333;
}
/* 人员信息区域 */
.info-section {
/* 查询功能区域 */
.query-section {
margin: 0 30rpx;
background: #ffffff;
border-radius: 20rpx;
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.section-title {
.query-item {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 20rpx;
}
.title-bar {
width: 6rpx;
height: 32rpx;
background: #e6c560;
border-radius: 3rpx;
}
.title-text {
font-size: 30rpx;
font-weight: 500;
color: #333;
justify-content: space-between;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.info-card {
background: #ffffff;
border-radius: 20rpx;
padding: 0 32rpx;
.query-item:last-child {
border-bottom: none;
}
.info-item {
.query-item-left {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f5f5f5;
gap: 16rpx;
}
.info-item:last-child {
border-bottom: none;
.query-item-icon {
width: 44rpx;
height: 48rpx;
}
.item-label {
.query-item-text {
font-size: 28rpx;
color: #333;
}
.item-value {
font-size: 28rpx;
color: #999;
}
/* 绑定学员弹框 */
.bind-popup {
width: 600rpx;
......@@ -572,18 +711,66 @@
font-weight: 500;
}
/* 退出登录 */
.logout-section {
margin: 60rpx 30rpx 40rpx;
/* 退出登录确认框 */
.confirm-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.logout-btn {
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 30rpx;
color: #999;
.confirm-box {
width: 600rpx;
background: #ffffff;
border-radius: 20rpx;
overflow: hidden;
}
.confirm-content {
padding: 40rpx;
text-align: center;
}
.confirm-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
margin-bottom: 20rpx;
}
.confirm-text {
font-size: 28rpx;
color: #666;
margin-top: 30rpx;
color: #C40F18;
}
.confirm-buttons {
display: flex;
border-top: 1rpx solid #eee;
}
.confirm-btn {
flex: 1;
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 30rpx;
}
.confirm-btn.cancel {
color: #666;
border-right: 1rpx solid #eee;
}
.confirm-btn.confirm {
color: #C40F18;
font-weight: 500;
}
</style>
\ No newline at end of file
......
<template>
<view class="page-container">
<!-- 表格头部 -->
<view class="table-header">
<view class="table-cell table-cell-1">序号</view>
<view class="table-cell table-cell-2">级位</view>
<view class="table-cell table-cell-3">级位号</view>
<view class="table-cell table-cell-4">获得证书时间</view>
<view class="table-cell table-cell-5">操作</view>
</view>
<!-- 表格内容 -->
<view v-if="loading" class="loading-container">
<uni-icons type="spinner" size="40" color="#409eff" class="loading-icon" />
<view class="loading-text">加载中...</view>
</view>
<view v-else class="table-content">
<view class="table-row" v-for="(item, index) in levelRecords" :key="index">
<view class="table-cell table-cell-1">{{ index + 1 }}</view>
<view class="table-cell table-cell-2">{{ szToHz(item.level) }}</view>
<view class="table-cell table-cell-3">{{ item.certCode }}</view>
<view class="table-cell table-cell-4">{{ item.createTime ? item.createTime.substring(0, 10) : '--' }}</view>
<view class="table-cell table-cell-5">
<view class="change-record-btn" @click="showChangeRecord(item)" v-if="item.remark?.length>0">变更记录</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="levelRecords.length === 0 && !loading" class="empty-state">
<uni-icons type="empty" size="80" color="#ccc" />
<view class="empty-text">暂无级位记录</view>
</view>
</view>
<!-- 变更记录弹窗 -->
<uni-popup ref="changeRecordPopup" type="center">
<view class="popup-content">
<view class="popup-header">
<view class="popup-title">级位变更</view>
<view class="popup-close" @click="closeChangeRecord">
<uni-icons type="close" size="24" color="#fff" />
</view>
</view>
<view class="popup-body">
<view v-if="currentChangeRecord" class="change-details">
<view class="change-item">
<view class="change-label">变更名称:{{ currentChangeRecord.modName || '等级变更' }}</view>
<!-- <view class="change-value"></view> -->
</view>
<view class="change-item">
<view class="change-label">考级级别:{{ szToHz(currentChangeRecord.oldJi) }}</view>
<!-- <view class="change-value"></view> -->
</view>
<view class="change-item">
<view class="change-label">变更后级位:{{ szToHz(currentChangeRecord.newJi) }}</view>
<!-- <view class="change-value"></view> -->
</view>
<view class="change-item">
<view class="change-label">变更时间:{{ parseTime(currentChangeRecord.modTime) }}</view>
<!-- <view class="change-value">/view> -->
</view>
</view>
<!-- 空状态 -->
<view v-else class="empty-state">
<uni-icons type="empty" size="60" color="#ccc" />
<view class="empty-text">暂无变更记录</view>
</view>
</view>
<!-- <view class="popup-footer">
<button class="confirm-btn" @click="closeChangeRecord">确定</button>
</view> -->
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useUserStore } from '../store/modules/user';
import { getAssoPers } from '@/common/api.js';
import { getPersonTecDetails } from '@/common/api.js';
// 级位记录数据
const levelRecords = ref([]);
const loading = ref(false);
const perId = ref('');
const userStore = useUserStore();
// 变更记录相关
const changeRecordPopup = ref(null);
const currentChangeRecord = ref(null);
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 显示变更记录
const showChangeRecord = (item) => {
// remark已经在getLevelRecords中解析过了,直接使用
if (item.remark) {
currentChangeRecord.value = Array.isArray(item.remark) ? item.remark[0] : item.remark;
} else {
currentChangeRecord.value = null;
}
changeRecordPopup.value.open();
};
// 关闭变更记录
const closeChangeRecord = () => {
changeRecordPopup.value.close();
};
// 获取级位记录数据
const getLevelRecords = async () => {
loading.value = true;
try {
const res = await getPersonTecDetails(0, perId.value); // 0表示级位
levelRecords.value = res.data || [];
// 处理数据
levelRecords.value.forEach(item => {
item.level = item.level * 1;
if (item.remark) {
try {
item.remark = JSON.parse(item.remark);
} catch (e) {
console.error('解析remark失败:', e);
}
}
});
} catch (error) {
console.error('获取级位记录失败:', error);
uni.showToast({
title: '获取级位记录失败',
icon: 'none'
});
} finally {
loading.value = false;
}
};
// 数字转汉字
const szToHz = (num) => {
const arr = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
return arr[num] || num;
};
// 时间格式化
const parseTime = (time) => {
if (!time) return '--';
const date = new Date(time);
return date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0');
};
onMounted(async () => {
// 获取perId
const userInfo = userStore.user;
if (userInfo && userInfo.perId) {
perId.value = userInfo.perId;
getLevelRecords();
} else {
// 如果userInfo中没有perId,尝试通过getAssoPers获取
try {
const res = await getAssoPers(userInfo?.id || '');
perId.value = res.data[10] || '';
if (perId.value) {
getLevelRecords();
} else {
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
} catch (error) {
console.error('获取perId失败:', error);
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
}
});
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f5f5f5;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background: #ffffff;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.nav-right {
width: 44rpx;
}
/* 表格头部 */
.table-header {
display: flex;
background: linear-gradient(135deg, #ff6b6b, #ff9f43);
padding: 0 30rpx;
position: sticky;
// top: 88rpx;
z-index: 90;
}
/* 表格内容 */
.table-content {
background: #ffffff;
margin: 20rpx 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
/* 表格行 */
.table-row {
display: flex;
padding: 0 30rpx;
border-bottom: 1rpx solid #f0f0f0;
transition: all 0.3s ease;
}
.table-row:last-child {
border-bottom: none;
}
.table-row:hover {
background: #fff9f0;
}
/* 表格单元格 */
.table-cell {
padding: 24rpx 0;
font-size: 26rpx;
color: #333;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.table-header .table-cell {
color: #ffffff;
font-weight: 500;
}
/* 单元格宽度 */
.table-cell-1 {
width: 80rpx;
}
.table-cell-2 {
width: 120rpx;
}
.table-cell-3 {
flex: 1;
padding: 0 10rpx;
}
.table-cell-4 {
width: 200rpx;
}
.table-cell-5 {
width: 150rpx;
}
/* 变更记录按钮 */
.change-record-btn {
background: linear-gradient(135deg, #ff6b6b, #ff9f43);
color: #ffffff;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.change-record-btn:hover {
transform: scale(1.05);
box-shadow: 0 4rpx 8rpx rgba(255, 107, 107, 0.3);
}
.change-record-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
/* 弹窗 */
.popup-content {
width: 300px;
background: #ffffff;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
background: linear-gradient(135deg, #ff6b6b, #ff9f43);
}
.popup-title {
font-size: 32rpx;
font-weight: 500;
color: #ffffff;
}
.popup-close {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.popup-close uni-icons {
color: #ffffff;
}
.popup-body {
padding: 40rpx;
}
/* 变更记录详情 */
.change-details {
width: 100%;
}
.change-item {
margin-bottom: 30rpx;
}
.change-item:last-child {
margin-bottom: 0;
}
.change-label {
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
font-weight: 500;
}
.change-value {
font-size: 28rpx;
color: #333;
padding: 16rpx 24rpx;
background: #fff9f0;
border-radius: 12rpx;
border-left: 4rpx solid #ff6b6b;
}
.popup-footer {
padding: 0 30rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 80rpx;
background: linear-gradient(135deg, #ff6b6b, #ff9f43);
color: #ffffff;
border: none;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
}
.confirm-btn:hover {
transform: translateY(-2rpx);
box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3);
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 500rpx;
}
.loading-icon {
animation: spin 1s linear infinite;
color: #ff6b6b;
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #ff6b6b;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 响应式调整 */
@media (max-width: 375px) {
.table-header,
.table-row {
padding: 0 20rpx;
}
.table-cell {
font-size: 24rpx;
padding: 20rpx 0;
}
.table-cell-1 {
width: 60rpx;
}
.table-cell-2 {
width: 100rpx;
}
.table-cell-4 {
width: 160rpx;
}
.table-cell-5 {
width: 120rpx;
}
.change-record-btn {
font-size: 22rpx;
padding: 6rpx 12rpx;
}
.popup-content {
width: 300px;
max-width: 450rpx;
}
.popup-header,
.popup-body,
.popup-footer {
padding: 24rpx 32rpx;
}
.change-label {
font-size: 28rpx;
}
.change-value {
font-size: 26rpx;
padding: 12rpx 20rpx;
}
.change-item {
margin-bottom: 20rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="page-container">
<!-- 信息展示区域 -->
<view v-if="loading" class="loading-container">
<uni-icons type="spinner" size="40" color="#409eff" class="loading-icon" />
<view class="loading-text">加载中...</view>
</view>
<view v-else class="info-card">
<view class="info-item">
<view class="info-label">姓名</view>
<view class="info-value">{{ form?.name || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">证件类型</view>
<view class="info-value">{{ getCertType(form?.idcType) }}</view>
</view>
<view class="info-item">
<view class="info-label">证件号</view>
<view class="info-value">{{ form?.idcCode || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">性别</view>
<view class="info-value">{{ getGender(form?.sex) }}</view>
</view>
<view class="info-item">
<view class="info-label">会员编号</view>
<view class="info-value">{{ form?.perCode || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">所属一级协会</view>
<view class="info-value">{{ form?.topAssName || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">所属地区协会</view>
<view class="info-value">{{ form?.areaAssName || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">注册单位会员</view>
<view class="info-value">{{ form?.memName || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">缴费日期</view>
<view class="info-value">{{ form?.payDate || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">注册时间</view>
<view class="info-value">{{ form?.createTime || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">出生日期</view>
<view class="info-value">{{ form?.birth || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">手机号码</view>
<view class="info-value">{{ form?.phone || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">地址</view>
<view class="info-value">{{ form?.site || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">详细地址</view>
<view class="info-value">{{ form?.address || '--' }}</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useUserStore } from '../store/modules/user';
import { getAssoPers, getInfo } from '@/common/api.js';
const userStore = useUserStore();
const form = ref({});
const loading = ref(false);
const perId = ref('');
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 获取证件类型
const getCertType = (type) => {
switch (type) {
case '0':
case '1':
return '身份证';
case '2':
return '护照';
case '3':
return '军官证';
case '4':
return '港澳通行证';
case '5':
return '台湾通行证';
default:
return '--';
}
};
// 获取性别
const getGender = (gender) => {
switch (gender) {
case '0':
case '1':
return '男';
case '2':
return '女';
default:
return '--';
}
};
// 获取个人会员信息
const getMemberInfo = async () => {
loading.value = true;
try {
const res = await getInfo(perId.value);
form.value = res.data;
// 处理数据
form.value.topAssName = form.value?.ancestorNameList?.[0] || '--';
form.value.areaAssName = form.value?.ancestorNameList?.[1] || '--';
form.value.memName = form.value.memName || '--';
form.value.payDate = form.value.payDate ? form.value.payDate.substring(0, 10) : '--';
form.value.createTime = form.value.createTime ? form.value.createTime.substring(0, 10) : '--';
form.value.birth = form.value.birth ? form.value.birth.substring(0, 10) : '--';
form.value.site = form.value.site || '--';
form.value.address = form.value.address || '--';
} catch (error) {
console.error('获取个人会员信息失败:', error);
uni.showToast({
title: '获取个人会员信息失败',
icon: 'none'
});
} finally {
loading.value = false;
}
};
onMounted(async () => {
// 获取perId
const userInfo = userStore.user;
if (userInfo && userInfo.perId) {
perId.value = userInfo.perId;
getMemberInfo();
} else {
// 如果userInfo中没有perId,尝试通过getAssoPers获取
try {
const res = await getAssoPers(userInfo?.id || '');
perId.value = res.data[10] || '';
if (perId.value) {
getMemberInfo();
} else {
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
} catch (error) {
console.error('获取perId失败:', error);
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
});
}
}
});
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 30rpx;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background: #ffffff;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.nav-right {
width: 44rpx;
}
/* 信息卡片 */
.info-card {
margin: 20rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
overflow: hidden;
}
/* 信息项 */
.info-item {
display: flex;
align-items: center;
padding: 30rpx 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.info-item:last-child {
border-bottom: none;
}
/* 标签 */
.info-label {
font-size: 28rpx;
color: #666666;
width: 200rpx;
flex-shrink: 0;
}
/* 值 */
.info-value {
font-size: 28rpx;
color: #333333;
flex: 1;
padding-left: 20rpx;
text-align: left;
word-break: break-all;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 500rpx;
}
.loading-icon {
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #666;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 响应式调整 */
@media (max-width: 375px) {
.info-label {
width: 160rpx;
font-size: 26rpx;
}
.info-value {
font-size: 26rpx;
padding-left: 16rpx;
}
.info-item {
padding: 24rpx 20rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="order-page" :class="{ 'no-scroll': isPopupOpen }">
<!-- 订单列表 -->
<scroll-view
scroll-y
class="order-list-scroll"
:enhanced="true"
:show-scrollbar="false"
:scroll-enabled="!isPopupOpen"
>
<view class="order-list">
<!-- 有数据才循环 -->
<view v-if="list.length > 0">
<view
v-for="(item, index) in list"
:key="index"
class="order-card"
>
<!-- 订单头部:日期 + 状态 -->
<view class="card-header">
<view class="date">
<image src="/static/calendar@2x.png" v-if="item.payTime" mode="widthFix" style="width:30rpx;height:30rpx;"/>
<text class="date-text" v-if="item.payTime">{{ item.payTime }}</text>
</view>
<view
class="status-tag"
:class="{
success: item.payStatus == 1,
danger: item.payStatus == 2,
pending: item.payStatus == 0
}"
>
{{ getStatusText(item.payStatus) }}
</view>
</view>
<!-- 订单编号、缴费编号 -->
<view class="info-row">
<text class="label">订单编号:</text>
<text class="value">{{ item.tradeNo || '——' }}</text>
</view>
<view class="info-row">
<text class="label">缴费编号:</text>
<text class="value">{{ item.wfCode || '——' }}</text>
</view>
<!-- 核心:个人会员仅展示缴费年限 -->
<view class="info-section flex f-j-s" v-if="item.content">
<view class="single-info">
<view class="label">缴费年限:</view>
<view class="value">{{ item.content.yearCount || 0 }}</view>
</view>
<view class="line"></view>
<view class="single-info">
<view class="label">缴费方式</view>
<view class="value">民生付</view>
</view>
</view>
<!-- 费用合计 -->
<view class="total-row">
<text class="label">费用合计:</text>
<text class="amount">¥{{ (Number(item.price) || 0).toFixed(2) }}</text>
</view>
<!-- 按钮组:靠右紧凑展示 -->
<view class="btn-group">
<button class="btn btn-delete" @click="handleDelete(item)">删除</button>
<!-- 已缴费:申请开票/已开票 -->
<template v-if="item.payStatus == 1">
<button class="btn btn-invoice" @click="makeInvoiceFN(item)" :disabled="item.invoiceStatus === 1">
{{ item.invoiceStatus === 1 ? '已开票' : '申请开票' }}
</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 v-else class="empty">
<text class="empty-text">暂无订单记录</text>
</view>
<!-- 加载/无更多提示 -->
<view class="loading-tip" v-if="loading">加载中...</view>
<view class="no-more" v-if="!loading && !hasMore && list.length">没有更多了</view>
</view>
</scroll-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>
</view>
</template>
<script setup>
import { ref, reactive, onMounted,computed } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app'
import { useUserStore } from "../store/modules/user";
import * as api from '@/common/api.js'
const userStore = useUserStore()
// 数据与分页配置
const list = ref([]);
const loading = ref(false);
const hasMore = ref(true);
const pageNum = ref(1);
const pageSize = ref(10);
const userInfo = computed(() => userStore.user)
// 查询参数(固定为个人会员类型)
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
type: '0', // 0表示个人会员
queryType: '1',
// payStatus: '',
// perId: ''
});
// 弹窗控制
const showDelPopup = ref(false);
const showCancelPopup = ref(false);
const isPopupOpen = ref(false);
// 弹窗内容
const delModalContent = ref('');
const cancelModalContent = ref('');
// 当前操作的订单
const currentOrder = ref(null);
// 页面挂载初始化
onMounted(() => {
// 获取用户信息
// if (userInfo.value && userInfo.value.perId) {
// queryParams.perId = userInfo.value.perId;
initData();
// } else {
// uni.showToast({ title: '获取用户信息失败', icon: 'none' });
// }
});
// 小程序原生触底加载
onReachBottom(() => {
if (!loading.value && hasMore.value && !isPopupOpen.value) {
pageNum.value++;
initData();
}
});
// 状态文本映射
const getStatusText = (status) => {
const map = {
0: '待缴费',
1: '缴费成功',
2: '订单取消'
};
return map[status] || '未知状态';
};
// 数据请求核心方法
const initData = async () => {
loading.value = true;
queryParams.pageNum = pageNum.value;
try {
const res = await api.orderList(queryParams);
if (!res || !res.rows) {
list.value = [];
hasMore.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);
}
// 判断是否还有更多数据
hasMore.value = list.value.length < (res.total || 0);
} catch (e) {
console.error('订单加载异常:', e);
uni.showToast({ title: '加载失败', icon: 'none' });
} finally {
loading.value = false;
}
};
// 删除订单
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 = [];
initData();
closeDelPopup();
} catch (e) {
uni.showToast({ title: '删除失败', icon: 'error' });
}
};
// 关闭删除弹窗
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) => {
uni.navigateTo({ url: `/pages/invoice/apply?orderId=${item.id}amount=${item.price}` });
};
// 取消订单
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 = [];
initData();
closeCancelPopup();
} catch (e) {
uni.showToast({ title: '取消失败', icon: 'error' });
}
};
// 关闭取消订单弹窗
const closeCancelPopup = () => {
showCancelPopup.value = false;
isPopupOpen.value = false;
currentOrder.value = null;
};
</script>
<style lang="scss" scoped>
.order-page {
background: #f5f7fa;
min-height: 100vh;
display: flex;
flex-direction: column;
&.no-scroll {
overflow: hidden;
height: 100vh;
}
}
// 滚动列表容器
.order-list-scroll {
flex: 1;
height: 0;
}
// 订单列表
.order-list {
padding: 20rpx;
.order-card {
background: #fff;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
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: 20rpx;
margin-bottom: 20rpx;
border-bottom: 1rpx dashed #eee;
.date {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 26rpx;
.date-text {
color: #666;
}
}
.status-tag {
font-size: 24rpx;
padding: 6rpx 16rpx;
border-radius: 20rpx;
&.success {
background: #e6f7ef;
color: #52c41a;
}
&.danger {
background: #fff1f0;
color: #ff4d4f;
}
&.pending {
background: #f5f5f5;
color: #999;
}
}
}
// 基础信息行
.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;
padding: 0 40rpx;
justify-content: space-around;
margin: 20rpx 0;
}
.line{
width: 1rpx;
height: 90%;
background: #eee;
}
.single-info {
padding: 16rpx 20rpx;
border-radius: 8rpx;
font-size: 26rpx;
.label {
color: #999;
text-align: center;
}
.value {
color: #333;
font-weight: 500;
text-align: center;
margin-top: 10rpx;
}
}
// 费用合计
.total-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 0 30rpx;
padding: 15rpx 0;
font-size: 28rpx;
border-bottom: 1rpx dashed #eee;
.label {
color: #333;
}
.amount {
color: #EB6100;
font-weight: 600;
font-size: 32rpx;
}
}
// 按钮组
.btn-group {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16rpx;
width: 100%;
.btn {
padding: 12rpx 32rpx;
border-radius: 40rpx;
font-size: 24rpx;
line-height: 1.5;
white-space: nowrap;
display: inline-block;
margin: 0;
border: none;
width: 80px;
background: transparent;
&::after {
border: none;
}
&.btn-delete {
background: #fff;
color: #e4393c;
border: 1rpx solid #e4393c;
}
&.btn-invoice {
background: #fff;
color: #e4393c;
border: 1rpx solid #e4393c;
}
&.btn-cancel {
background: #fff;
color: #666;
border: 1rpx solid #ccc;
}
&.btn-pay {
background: linear-gradient(90deg, #FF755A, #F51722);
color: #fff;
border: none;
}
&:disabled {
opacity: 0.6;
pointer-events: none;
}
}
}
// 空状态
.empty {
display: flex;
justify-content: center;
align-items: center;
padding: 120rpx 0;
.empty-text {
color: #999;
font-size: 28rpx;
}
}
// 加载/无更多提示
.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;
}
}
</style>
\ No newline at end of file
<template>
<view class="page-container">
<!-- 信息展示区域 -->
<view class="info-section">
<view class="info-item">
<view class="info-label">姓名</view>
<view class="info-value">{{ perInfo?.perName || '' }}</view>
</view>
<!-- <view class="info-item">
<view class="info-label">证件类型</view>
<view class="info-value">{{ getCertType(perInfo?.idcType) }}</view>
</view> -->
<view class="info-item">
<view class="info-label">身份证号</view>
<view class="info-value">{{ perInfo?.perIdcCode || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">生日</view>
<view class="info-value">{{ perInfo?.birth || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">会员卡号</view>
<view class="info-value">{{ perInfo?.perCode || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">会员有效期</view>
<view class="info-value">{{ perInfo?.perValidDate || '--' }}</view>
</view>
<!-- <view class="info-item">
<view class="info-label">性别</view>
<view class="info-value">{{ getGender(perInfo?.gender) }}</view>
</view>
<view class="info-item">
<view class="info-label">民族</view>
<view class="info-value">{{ perInfo?.nation || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">联系电话</view>
<view class="info-value">{{ perInfo?.phone || '--' }}</view>
</view>
<view class="info-item">
<view class="info-label">邮箱</view>
<view class="info-value">{{ perInfo?.email || '--' }}</view>
</view> -->
<!-- <view class="info-item">
<view class="info-label">地址</view>
<view class="info-value">{{ perInfo?.address || '--' }}</view>
</view> -->
</view>
</view>
</template>
<script setup>
import { computed, onMounted } from 'vue';
import { useUserStore } from '../store/modules/user';
const userStore = useUserStore();
const perInfo = computed(() => userStore.perInfo);
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 获取证件类型
const getCertType = (type) => {
switch (type) {
case '1':
return '身份证';
case '2':
return '护照';
case '3':
return '军官证';
case '4':
return '港澳通行证';
case '5':
return '台湾通行证';
default:
return '--';
}
};
// 获取性别
const getGender = (gender) => {
switch (gender) {
case '1':
return '男';
case '2':
return '女';
default:
return '--';
}
};
onMounted(() => {
// 可以在这里添加额外的初始化逻辑
});
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f5f5f5;
}
/* 导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background: #ffffff;
padding: 0 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 32rpx;
font-weight: 500;
color: #333;
}
.nav-right {
width: 44rpx;
}
/* 信息展示区域 */
.info-section {
margin: 20rpx;
background: #ffffff;
}
.info-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-size: 28rpx;
color: #666;
width: 150rpx;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
text-align: right;
padding-left: 30rpx;
}
</style>
\ No newline at end of file
static/bd@2x.png

711 Bytes

Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!