修复跳转到告知书问题

This commit is contained in:
ywyonui 2025-08-07 10:12:04 +08:00
parent e392903194
commit e602f4c9bc
9 changed files with 508 additions and 701 deletions

View File

@ -16,7 +16,7 @@ import XsPicker from "@/pages/base/components/XsPicker/index.vue"
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
const { getGlobal } = useDataStore(); const { getGlobal } = useDataStore();
const { checkXqk, checkJlb, setXsPickerInitialized } = useUserStore(); const { checkXqk, checkJlb, checkJc, setXsPickerInitialized } = useUserStore();
const switchXs = (xs: any) => { const switchXs = (xs: any) => {
// //
@ -26,6 +26,8 @@ const switchXs = (xs: any) => {
checkXqk(); checkXqk();
} else if (getGlobal.type == 2) { } else if (getGlobal.type == 2) {
checkJlb(); checkJlb();
} else if (getGlobal.type == 3) {
checkJc();
} else { } else {
} }
} }

View File

@ -1,78 +1,75 @@
# JC模块 - 就餐标准管理 # 就餐模块业务逻辑调整说明
## 功能概述 ## 概述
参照选课的处理和业务逻辑,对就餐相关的业务逻辑进行了调整,实现了统一的状态管理和页面跳转逻辑。
JC模块是用于管理学生就餐标准的前端模块包含以下主要功能 ## 主要调整
### 1. 就餐标准列表 (`/pages/base/jc/index.vue`) ### 1. useUserStore中的checkJc方法完善
- 显示当前学期可用的就餐标准 - **位置**: `src/store/modules/user.ts`
- 支持学生选择器切换不同学生 - **功能**: 根据后端返回的type值进行页面跳转
- 展示就餐标准的基本信息(名称、价格、描述) - **逻辑**:
- `type = 1`: 未选择(返回可选标准列表)→ 跳转到报名页面 `/pages/base/jc/bm`
- `type = 2`: 待支付 → 跳转到支付页面 `/pages/base/jc/pay/index`
- `type = 3`: 已支付 → 跳转到已选择页面 `/pages/base/jc/index`
### 2. 就餐标准详情 (`/pages/base/jc/detail.vue`) ### 2. 页面整合
- 显示就餐标准的详细信息 - **删除页面**:
- 包含标准描述、使用说明、注意事项等 - `detail.vue` - 就餐标准详情页面
- 支持图片展示 - `bm-detail.vue` - 报名详情页面
- **整合到**: `index.vue` - 已选择就餐标准展示页面
### 3. 就餐标准报名 (`/pages/base/jc/bm.vue`) ### 3. index.vue页面重构
- 支持多选就餐标准 - **功能**: 展示已选择的就餐标准信息
- 实时计算总金额 - **包含内容**:
- 确认报名后跳转到支付页面 - 就餐标准基本信息(名称、价格、描述、有效期)
- 报名信息(报名时间、支付状态、订单号)
- 使用说明
- 注意事项
- **状态处理**:
- 有已选择标准:显示详细信息
- 无已选择标准:显示空状态,提供"立即报名"按钮
### 4. 支付流程 (`/pages/base/jc/pay/`) ### 4. bm.vue页面调整
- 支付页面 (`index.vue`):显示报名信息和支付倒计时 - **跳转逻辑**: 已报名时跳转到 `index.vue` 而不是已删除的 `bm-detail.vue`
- 支付等待页面 (`wait.vue`):处理支付回调
- 支付成功页面 (`success.vue`):支付成功提示
- 支付失败页面 (`fail.vue`):支付失败处理
### 5. 就餐记录 (`/pages/base/jc/record.vue`) ### 5. 后端接口对应
- 显示学生的就餐记录历史 - **接口**: `/mobile/jz/jc/getJcBzList`
- 包含就餐日期、状态、地点等信息 - **返回类型**: `AppJzXsJcVo`
- **type字段含义**:
- `1`: 未选择(返回可选标准列表)
- `2`: 待支付
- `3`: 已支付
## 组件结构 ## 页面跳转流程
### 核心组件 ### 通过launchPage的type=3进入
- `JcBzList` (`/pages/base/components/JcBzList/index.vue`):就餐标准列表组件 1. **useUserStore.toHome(type=3)** → 调用checkJc()
- `JcRecordList` (`/pages/base/components/JcRecordList/index.vue`):就餐记录列表组件 2. **checkJc()** → 调用后端接口获取就餐状态
3. **根据返回的type值跳转**:
- type=1 → `/pages/base/jc/bm` (报名页面)
- type=2 → `/pages/base/jc/pay/index` (支付页面)
- type=3 → `/pages/base/jc/index` (已选择页面)
### API接口 ### 页面间跳转
所有JC相关的API接口都集中在 `jcApi.ts` 文件中,包括: - **报名成功** → 跳转到支付页面
- 获取就餐标准列表 - **支付成功** → 跳转到已选择页面
- 获取就餐标准详情 - **查看详情** → 在已选择页面中展示完整信息
- 学生报名就餐标准
- 取消报名
- 发起支付
- 查询支付状态
- 获取就餐记录等
## 数据流 ## 技术实现
1. **报名流程** ### 前端
- 学生选择就餐标准 → 确认报名 → 跳转支付页面 → 完成支付 - **状态管理**: 使用Pinia store管理用户状态和就餐数据
- **页面路由**: 使用uni.reLaunch进行页面跳转
- **数据获取**: 通过API接口获取就餐状态和详情信息
2. **数据存储** ### 后端
- 使用 Pinia store 管理状态 - **状态检查**: 通过Redis缓存和数据库查询确定就餐状态
- 本地存储保存选中状态 - **类型定义**: 使用AppJzXsJcVo统一返回格式
- 支付数据通过 store 传递 - **业务逻辑**: 参照选课模块的实现方式
## 技术特点
- 采用 Vue 3 Composition API
- 使用 TypeScript 进行类型检查
- 响应式设计,支持移动端
- 模块化组件设计
- 统一的错误处理机制
## 注意事项 ## 注意事项
1. 确保后端接口返回的type值与前端期望一致
1. 支付倒计时功能需要后端提供倒计时接口 2. 页面跳转使用uni.reLaunch确保正确的页面栈管理
2. 多选功能需要合理处理数据同步 3. 数据获取失败时使用备用方案(跳转到报名页面)
3. 支付状态查询需要定时轮询 4. 删除的页面文件已清理,避免引用错误
4. 图片资源需要正确的路径处理
## 扩展建议
1. 可以添加就餐标准筛选功能
2. 支持按时间范围查询就餐记录
3. 添加就餐统计功能
4. 支持批量操作就餐标准

View File

@ -1,295 +0,0 @@
<template>
<view class="bm-detail-page">
<!-- 页面头部 -->
<view class="page-header">
<view class="header-title">报名详情</view>
</view>
<!-- 学生信息 -->
<view class="info-card">
<view class="card-title">学生信息</view>
<view class="student-info">
<view class="info-item">
<text class="label">姓名</text>
<text class="value">{{ curXs.xm }}</text>
</view>
<view class="info-item">
<text class="label">班级</text>
<text class="value">{{ curXs.bjmc }}</text>
</view>
</view>
</view>
<!-- 报名信息 -->
<view class="info-card">
<view class="card-title">报名信息</view>
<view class="bm-info">
<view class="info-item">
<text class="label">报名时间</text>
<text class="value">{{ bmInfo.bmTime || '暂无' }}</text>
</view>
<view class="info-item">
<text class="label">报名状态</text>
<text class="value status-success">已报名</text>
</view>
</view>
</view>
<!-- 就餐标准详情 -->
<JcBzDetailCard
v-if="bmJcBzList.length > 0"
:jc-bz-list="bmJcBzList"
title="已报名的就餐标准"
:show-detail-btn="true"
:show-summary="false"
@detail="goToDetail"
/>
<!-- 支付信息 -->
<view class="info-card">
<view class="card-title">支付信息</view>
<view class="payment-info">
<view class="info-item">
<text class="label">缴费金额</text>
<text class="value total-price">¥{{ totalAmount }}</text>
</view>
<view class="info-item">
<text class="label">支付状态</text>
<text class="value" :class="paymentStatusClass">{{ paymentStatusText }}</text>
</view>
</view>
</view>
<!-- 底部操作 -->
<view class="bottom-actions">
<view class="action-btn primary" @click="goToPay" v-if="paymentStatus === 'unpaid'">
立即支付
</view>
<view class="action-btn secondary" @click="goBack">
返回
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import JcBzDetailCard from "@/pages/base/components/JcBzDetailCard/index.vue";
import { jcGetXsBmJcBzListApi } from "@/api/base/jcApi";
const { getCurXs } = useUserStore();
const { getData, setData } = useDataStore();
const curXs = computed(() => getCurXs);
//
const bmInfo = ref<any>({});
//
const bmJcBzList = ref<any[]>([]);
//
const paymentStatus = ref<string>('unpaid'); // unpaid, paid, expired
//
const totalAmount = computed(() => {
let total = 0;
bmJcBzList.value.forEach(jcBz => {
total += jcBz.jfje || 0;
});
return total;
});
//
const paymentStatusClass = computed(() => {
switch (paymentStatus.value) {
case 'paid':
return 'status-success';
case 'expired':
return 'status-error';
default:
return 'status-warning';
}
});
//
const paymentStatusText = computed(() => {
switch (paymentStatus.value) {
case 'paid':
return '已支付';
case 'expired':
return '已过期';
default:
return '待支付';
}
});
//
const getBmInfo = async () => {
try {
const res = await jcGetXsBmJcBzListApi({
xsId: curXs.value.id
});
if (res.resultCode === 1) {
bmJcBzList.value = res.result || [];
//
bmInfo.value = {
bmTime: new Date().toLocaleString(),
//
};
}
} catch (error) {
console.error('获取报名信息失败:', error);
}
};
//
const goToDetail = (jcBz: any) => {
setData({
...getData,
jcBzData: jcBz
});
uni.navigateTo({
url: '/pages/base/jc/detail'
});
};
//
const goToPay = () => {
// store
setData({
...getData,
xsId: curXs.value.id,
jcBzIds: bmJcBzList.value.map(item => item.id),
jcBzList: bmJcBzList.value,
totalJe: totalAmount.value,
});
uni.redirectTo({
url: '/pages/base/jc/pay/index'
});
};
//
const goBack = () => {
uni.navigateBack();
};
onMounted(() => {
getBmInfo();
});
</script>
<style lang="scss" scoped>
.bm-detail-page {
min-height: 100vh;
background-color: #f5f7fa;
padding-bottom: 100px;
}
.page-header {
background: linear-gradient(135deg, #4a90e2, #2879ff);
padding: 20px 15px;
color: #fff;
text-align: center;
.header-title {
font-size: 18px;
font-weight: bold;
}
}
.info-card {
margin: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 10px;
}
.student-info,
.bm-info,
.payment-info {
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 14px;
color: #666;
}
.value {
font-size: 14px;
color: #333;
font-weight: 500;
&.total-price {
color: #ff6b00;
font-weight: bold;
font-size: 16px;
}
&.status-success {
color: #52c41a;
}
&.status-warning {
color: #faad14;
}
&.status-error {
color: #ff4d4f;
}
}
}
}
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 15px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
display: flex;
gap: 15px;
.action-btn {
flex: 1;
height: 44px;
line-height: 44px;
text-align: center;
border-radius: 22px;
font-size: 16px;
&.primary {
background-color: #2879ff;
color: #fff;
}
&.secondary {
background-color: #fff;
color: #333;
border: 1px solid #ddd;
}
}
}
</style>

View File

@ -171,9 +171,9 @@ const confirmBm = async () => {
content: '您已经报名了该就餐标准,是否查看报名详情?', content: '您已经报名了该就餐标准,是否查看报名详情?',
success: (modalRes) => { success: (modalRes) => {
if (modalRes.confirm) { if (modalRes.confirm) {
// //
uni.redirectTo({ uni.redirectTo({
url: '/pages/base/jc/bm-detail' url: '/pages/base/jc/index'
}); });
} else { } else {
// //

View File

@ -1,285 +0,0 @@
<template>
<BasicLayout>
<view class="jc-detail">
<!-- 就餐标准信息卡片 -->
<view class="info-card">
<view class="card-title">就餐标准信息</view>
<view class="divider"></view>
<view class="jc-info">
<image
class="jc-image"
:src="jcDetail.lxtp"
mode="aspectFill"
></image>
<view class="jc-content">
<view class="jc-name">{{ jcDetail.bzmc }}</view>
<view class="jc-price">
价格<text class="price-value">¥{{ jcDetail.jfje }}</text>
</view>
<view class="jc-desc">{{ jcDetail.bzms || '暂无描述' }}</view>
<view class="jc-time">有效期{{ jcDetail.yxq || '暂无有效期信息' }}</view>
</view>
</view>
</view>
<!-- 就餐标准详情 -->
<view class="info-card">
<view class="card-title">标准详情</view>
<view class="divider"></view>
<view class="content-section">
<text>{{ jcDetail.bzms || "暂无详细信息" }}</text>
</view>
</view>
<!-- 使用说明 -->
<view class="info-card">
<view class="card-title">使用说明</view>
<view class="divider"></view>
<view class="content-section">
<template v-if="usageInstructions && usageInstructions.length > 0">
<view
v-for="(instruction, index) in usageInstructions"
:key="index"
class="instruction-item"
>
<text>{{ instruction }}</text>
</view>
</template>
<template v-else>
<view class="empty-data">
<u-icon name="info-circle" color="#C8C9CC" size="18"></u-icon>
<text>暂无使用说明</text>
</view>
</template>
</view>
</view>
<!-- 注意事项 -->
<view class="info-card">
<view class="card-title">注意事项</view>
<view class="divider"></view>
<view class="content-section">
<template v-if="precautions && precautions.length > 0">
<view
v-for="(precaution, index) in precautions"
:key="index"
class="precaution-item"
>
<text>{{ precaution }}</text>
</view>
</template>
<template v-else>
<view class="empty-data">
<u-icon name="info-circle" color="#C8C9CC" size="18"></u-icon>
<text>暂无注意事项</text>
</view>
</template>
</view>
</view>
</view>
<template #bottom>
<view class="white-bg-color py-5">
<view class="flex-row items-center pb-10 pt-5">
<u-button
text="返回"
class="ml-15 mr-7"
:plain="true"
@click="navigateBack"
/>
</view>
</view>
</template>
</BasicLayout>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { navigateBack } from "@/utils/uniapp";
import { useDataStore } from "@/store/modules/data";
import { storeToRefs } from "pinia";
import { imagUrl } from "@/utils";
import { jcGetJcBzDetailApi } from "@/api/base/jcApi";
//
interface JcBzData {
id?: string;
bzmc?: string;
jfje?: number;
bzms?: string;
yxq?: string;
lxtp?: string;
[key: string]: any; //
}
const useData = useDataStore();
const { jcBzData } = storeToRefs(useData);
// 使 -
const usageInstructions = ref<string[]>([]);
// -
const precautions = ref<string[]>([]);
const jcBzDetail = jcBzData.value as JcBzData;
if (jcBzDetail && jcBzDetail.id) {
jcGetJcBzDetailApi({
jcBzId: jcBzDetail.id,
}).then((res) => {
if (res.resultCode == 1) {
//
console.log('就餐标准详情:', res.result);
}
});
}
//
const jcDetail = computed(() => {
const data = (jcBzData.value as JcBzData) || {};
return {
id: data.id || "",
bzmc: data.bzmc || "暂无标准名称",
jfje: data.jfje || 0,
bzms: data.bzms || "暂无描述",
yxq: data.yxq || "暂无有效期信息",
lxtp: imagUrl(data.lxtp || ''), // 使imagUrl
};
});
// 使
const parseInstructions = () => {
const data = (jcBzData.value as JcBzData) || {};
const bzms = data.bzms || '';
// 使
// 使
if (bzms) {
//
// 使""
const parts = bzms.split('注意事项:');
if (parts.length > 1) {
usageInstructions.value = parts[0].split('\n').filter(item => item.trim());
precautions.value = parts[1].split('\n').filter(item => item.trim());
} else {
usageInstructions.value = bzms.split('\n').filter(item => item.trim());
}
}
};
onMounted(() => {
parseInstructions();
});
</script>
<style lang="scss" scoped>
.jc-detail {
background-color: #f5f7fa;
}
.info-card {
margin: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.divider {
height: 1px;
background-color: #eee;
margin-bottom: 15px;
}
}
.jc-info {
display: flex;
.jc-image {
width: 120px;
height: 138px;
border-radius: 8px;
margin-right: 15px;
}
.jc-content {
flex: 1;
.jc-name {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.jc-price {
font-size: 14px;
color: #666;
margin-bottom: 8px;
.price-value {
color: #ff6b00;
font-weight: bold;
}
}
.jc-desc {
font-size: 14px;
color: #666;
margin-bottom: 8px;
line-height: 1.4;
}
.jc-time {
font-size: 14px;
color: #666;
}
}
}
.content-section {
font-size: 14px;
color: #666;
line-height: 1.6;
.instruction-item,
.precaution-item {
margin-bottom: 10px;
padding-left: 15px;
position: relative;
&:before {
content: "•";
position: absolute;
left: 0;
color: #2879ff;
}
&:last-child {
margin-bottom: 0;
}
}
.empty-data {
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #909399;
font-size: 14px;
text {
margin-left: 8px;
}
}
}
</style>

View File

@ -1,6 +1,7 @@
<template> <template>
<view class="jc-page"> <BasicLayout>
<!-- 就餐信息头部 - 固定部分 --> <view class="jc-index">
<!-- 就餐信息头部 -->
<view class="selection-header"> <view class="selection-header">
<view class="header-content"> <view class="header-content">
<!-- 学生选择部分 --> <!-- 学生选择部分 -->
@ -8,35 +9,206 @@
</view> </view>
</view> </view>
<!-- 可滚动的内容区域 --> <!-- 已选择的就餐标准信息 -->
<view class="scrollable-content"> <view class="selected-jc-info" v-if="selectedJcBz">
<JcBzList :xs-id="curXs.id" /> <view class="info-card">
<view class="card-title">已选择的就餐标准</view>
<view class="divider"></view>
<view class="jc-info">
<image
class="jc-image"
:src="selectedJcBz.lxtp"
mode="aspectFill"
></image>
<view class="jc-content">
<view class="jc-name">{{ selectedJcBz.bzmc }}</view>
<view class="jc-price">
价格<text class="price-value">¥{{ selectedJcBz.jfje }}</text>
</view>
<view class="jc-desc">{{ selectedJcBz.bzms || '暂无描述' }}</view>
<view class="jc-time">有效期{{ selectedJcBz.yxq || '暂无有效期信息' }}</view>
</view> </view>
</view> </view>
</view>
<!-- 报名信息 -->
<view class="info-card" v-if="bmInfo">
<view class="card-title">报名信息</view>
<view class="divider"></view>
<view class="bm-info">
<view class="bm-item">
<text class="label">报名时间</text>
<text class="value">{{ bmInfo.bmTime }}</text>
</view>
<view class="bm-item">
<text class="label">支付状态</text>
<text class="value status-paid">已支付</text>
</view>
<view class="bm-item">
<text class="label">订单号</text>
<text class="value">{{ bmInfo.orderNumber || '暂无' }}</text>
</view>
</view>
</view>
<!-- 使用说明 -->
<view class="info-card">
<view class="card-title">使用说明</view>
<view class="divider"></view>
<view class="content-section">
<template v-if="usageInstructions && usageInstructions.length > 0">
<view
v-for="(instruction, index) in usageInstructions"
:key="index"
class="instruction-item"
>
<text>{{ instruction }}</text>
</view>
</template>
<template v-else>
<view class="empty-data">
<u-icon name="info-circle" color="#C8C9CC" size="18"></u-icon>
<text>暂无使用说明</text>
</view>
</template>
</view>
</view>
<!-- 注意事项 -->
<view class="info-card">
<view class="card-title">注意事项</view>
<view class="divider"></view>
<view class="content-section">
<template v-if="precautions && precautions.length > 0">
<view
v-for="(precaution, index) in precautions"
:key="index"
class="precaution-item"
>
<text>{{ precaution }}</text>
</view>
</template>
<template v-else>
<view class="empty-data">
<u-icon name="info-circle" color="#C8C9CC" size="18"></u-icon>
<text>暂无注意事项</text>
</view>
</template>
</view>
</view>
</view>
<!-- 未选择就餐标准时的提示 -->
<view class="no-selection" v-else>
<view class="empty-state">
<u-icon name="info-circle" color="#C8C9CC" size="48"></u-icon>
<text class="empty-text">暂未选择就餐标准</text>
<u-button
text="立即报名"
type="primary"
@click="goToBm"
class="bm-button"
/>
</view>
</view>
</view>
</BasicLayout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import XsPicker from "@/pages/base/components/XsPicker/index.vue" import { ref, onMounted, computed } from "vue";
import JcBzList from "@/pages/base/components/JcBzList/index.vue"
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import { storeToRefs } from "pinia";
import { imagUrl } from "@/utils";
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
import { jcGetXsBmJcBzListApi } from "@/api/base/jcApi";
const { getCurXs } = useUserStore(); const { getCurXs } = useUserStore();
const { getData, setData } = useDataStore();
const curXs = computed(() => getCurXs); const curXs = computed(() => getCurXs);
// //
onBeforeUnmount(() => { const selectedJcBz = ref<any>(null);
//
const bmInfo = ref<any>(null);
// 使
const usageInstructions = ref<string[]>([]);
//
const precautions = ref<string[]>([]);
//
const getSelectedJcInfo = async () => {
try {
const res = await jcGetXsBmJcBzListApi({
xsId: curXs.value.id
});
if (res.resultCode === 1 && res.result && res.result.length > 0) {
//
const jcQd = res.result[0];
selectedJcBz.value = {
id: jcQd.bzId,
bzmc: jcQd.bzmc || '就餐标准',
jfje: jcQd.jfje || 0,
bzms: jcQd.bzms || '暂无描述',
yxq: jcQd.yxq || '暂无有效期信息',
lxtp: imagUrl(jcQd.lxtp || ''),
};
//
bmInfo.value = {
bmTime: jcQd.createdTime ? new Date(jcQd.createdTime).toLocaleString() : new Date().toLocaleString(),
orderNumber: jcQd.zfptId || '暂无',
status: jcQd.jfzt === 'B' ? '已支付' : '待支付'
};
// 使
parseInstructions();
}
} catch (error) {
console.error('获取已选择就餐标准失败:', error);
}
};
// 使
const parseInstructions = () => {
const bzms = selectedJcBz.value?.bzms || '';
if (bzms) {
//
const parts = bzms.split('注意事项:');
if (parts.length > 1) {
usageInstructions.value = parts[0].split('\n').filter((item: string) => item.trim());
precautions.value = parts[1].split('\n').filter((item: string) => item.trim());
} else {
usageInstructions.value = bzms.split('\n').filter((item: string) => item.trim());
}
}
};
//
const goToBm = () => {
uni.reLaunch({
url: '/pages/base/jc/bm'
});
};
onMounted(() => {
getSelectedJcInfo();
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.jc-page { .jc-index {
min-height: 100%; min-height: 100vh;
background-color: #f5f7fa; background-color: #f5f7fa;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
} }
.selection-header { .selection-header {
@ -58,10 +230,160 @@ onBeforeUnmount(() => {
} }
} }
// .selected-jc-info {
.scrollable-content { padding: 15px;
}
.info-card {
margin-bottom: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.divider {
height: 1px;
background-color: #eee;
margin-bottom: 15px;
}
}
.jc-info {
display: flex;
.jc-image {
width: 120px;
height: 138px;
border-radius: 8px;
margin-right: 15px;
}
.jc-content {
flex: 1; flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch; // iOS .jc-name {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.jc-price {
font-size: 14px;
color: #666;
margin-bottom: 8px;
.price-value {
color: #ff6b00;
font-weight: bold;
}
}
.jc-desc {
font-size: 14px;
color: #666;
margin-bottom: 8px;
line-height: 1.4;
}
.jc-time {
font-size: 14px;
color: #666;
}
}
}
.bm-info {
.bm-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
.label {
font-size: 14px;
color: #666;
}
.value {
font-size: 14px;
color: #333;
&.status-paid {
color: #52c41a;
font-weight: bold;
}
}
}
}
.content-section {
font-size: 14px;
color: #666;
line-height: 1.6;
.instruction-item,
.precaution-item {
margin-bottom: 10px;
padding-left: 15px;
position: relative;
&:before {
content: "•";
position: absolute;
left: 0;
color: #2879ff;
}
&:last-child {
margin-bottom: 0;
}
}
.empty-data {
display: flex;
align-items: center;
justify-content: center;
padding: 20px 0;
color: #909399;
font-size: 14px;
text {
margin-left: 8px;
}
}
}
.no-selection {
display: flex;
align-items: center;
justify-content: center;
min-height: 60vh;
.empty-state {
text-align: center;
.empty-text {
display: block;
margin: 20px 0;
color: #909399;
font-size: 16px;
}
.bm-button {
margin-top: 20px;
}
}
} }
</style> </style>

View File

@ -22,6 +22,12 @@ const { setGlobal } = useDataStore();
const userStore = useUserStore(); const userStore = useUserStore();
const isShow = ref(true); const isShow = ref(true);
const toLogin = () => {
uni.reLaunch({
url: "/pages/system/login/login",
});
};
onLoad(async (data: any) => { onLoad(async (data: any) => {
setGlobal(data); setGlobal(data);
if (data && data.openId) { if (data && data.openId) {
@ -42,20 +48,12 @@ onLoad(async (data: any) => {
} }
// //
userStore.toHome(data.type); userStore.toHome(data.type);
} else { return;
uni.reLaunch({
url: "/pages/system/login/login",
});
} }
} catch (err) { } catch (err) {
uni.reLaunch({ toLogin();
url: "/pages/system/login/login",
});
} }
} else {
uni.reLaunch({
url: "/pages/system/login/login",
});
} }
toLogin();
}); });
</script> </script>

View File

@ -1,6 +1,7 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { authenticationApi, loginCode, loginPass, weChatLogin, checkOpenId } from "@/api/system/login"; import { authenticationApi, loginCode, loginPass, weChatLogin, checkOpenId } from "@/api/system/login";
import { checkXsXkByTypeApi, xsKxApi } from "@/api/base/xkApi"; import { checkXsXkByTypeApi, xsKxApi } from "@/api/base/xkApi";
import { jcGetJcBzListApi } from "@/api/base/jcApi";
import { AUTH_KEY } from "@/config"; import { AUTH_KEY } from "@/config";
import { imagUrl } from "@/utils"; import { imagUrl } from "@/utils";
import { useWebSocket } from '@/utils/webSocket/webSocket' import { useWebSocket } from '@/utils/webSocket/webSocket'
@ -228,6 +229,15 @@ export const useUserStore = defineStore({
url: "/pages/base/home/xsXz", url: "/pages/base/home/xsXz",
}); });
} }
} else if (type == 3) {
// 俱乐部逻辑: 判断当前学生列表
if (this.userdata.xsList && this.userdata.xsList.length == 1) {
this.checkJc();
} else if (this.userdata.xsList && this.userdata.xsList.length > 1) {
uni.reLaunch({
url: "/pages/base/home/xsXz",
});
}
} else { } else {
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/home/index", url: "/pages/base/home/index",
@ -267,12 +277,11 @@ export const useUserStore = defineStore({
default: default:
// 默认跳转到告知书页面 // 默认跳转到告知书页面
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/xqk", url: "/pages/base/gzs/xkXqk",
}); });
return; return;
} }
} }
// 接口调用失败,使用备用方案 // 接口调用失败,使用备用方案
this.checkXqkFallback(); this.checkXqkFallback();
} catch (error) { } catch (error) {
@ -303,12 +312,12 @@ export const useUserStore = defineStore({
} }
} }
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/xqk", url: "/pages/base/gzs/xkXqk",
}); });
} catch (error) { } catch (error) {
console.error("备用方案也失败:", error); console.error("备用方案也失败:", error);
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/xqk", url: "/pages/base/gzs/xkXqk",
}); });
} }
}, },
@ -345,7 +354,7 @@ export const useUserStore = defineStore({
default: default:
// 默认跳转到告知书页面 // 默认跳转到告知书页面
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/jlb", url: "/pages/base/gzs/xkJlb",
}); });
return; return;
} }
@ -382,12 +391,71 @@ export const useUserStore = defineStore({
} }
} }
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/jlb", url: "/pages/base/gzs/xkJlb",
}); });
} catch (error) { } catch (error) {
console.error("备用方案也失败:", error); console.error("备用方案也失败:", error);
uni.reLaunch({ uni.reLaunch({
url: "/pages/base/gzs/jlb", url: "/pages/base/gzs/xkJlb",
});
}
},
// 就餐查询 - 优化版本
async checkJc() {
try {
// 使用getJcBzList接口获取详细就餐信息
const res = await jcGetJcBzListApi({
xsId: this.curXs.id
});
if (res.resultCode === 1) {
const data = res.result;
const type = data.type;
switch (type) {
case 1: // 未选择(返回可选标准列表)- 跳转到报名页面
uni.reLaunch({
url: "/pages/base/jc/bm",
});
return;
case 2: // 待支付 - 跳转到支付页面
uni.reLaunch({
url: "/pages/base/jc/pay/index",
});
return;
case 3: // 已支付 - 跳转到已选择页面
uni.reLaunch({
url: "/pages/base/jc/index",
});
return;
default:
// 默认跳转到报名页面
uni.reLaunch({
url: "/pages/base/gzs/jc",
});
return;
}
}
// 接口调用失败,使用备用方案
this.checkJcFallback();
} catch (error) {
console.error("查询就餐状态失败:", error);
// 异常情况,使用备用方案
this.checkJcFallback();
}
},
// 就餐备用检查方案
async checkJcFallback() {
try {
// 备用方案:直接跳转到报名页面
uni.reLaunch({
url: "/pages/base/gzs/jc",
});
} catch (error) {
console.error("备用方案也失败:", error);
uni.reLaunch({
url: "/pages/base/gzs/jc",
}); });
} }
}, },