hookehuyr

feat(router): 添加周期选择功能及页面

实现周期选择功能,包括:
1. 添加周期选择路由和页面组件
2. 在路由守卫中检查是否需要周期选择
3. 添加周期选择API调用和逻辑处理
4. 实现周期选择确认后的跳转逻辑
...@@ -36,6 +36,12 @@ export default [{ ...@@ -36,6 +36,12 @@ export default [{
36 title: '未授权页面', 36 title: '未授权页面',
37 } 37 }
38 }, { 38 }, {
39 + path: '/cycle-selection',
40 + component: () => import('@/views/cycle-selection.vue'),
41 + meta: {
42 + title: '选择周期',
43 + }
44 +}, {
39 path: '/test', 45 path: '/test',
40 component: () => import('@/views/test.vue'), 46 component: () => import('@/views/test.vue'),
41 meta: { 47 meta: {
......
1 /* 1 /*
2 * @Date: 2022-05-26 13:57:28 2 * @Date: 2022-05-26 13:57:28
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2024-12-23 18:26:41 4 + * @LastEditTime: 2025-09-08 15:52:35
5 * @FilePath: /data-table/src/router.js 5 * @FilePath: /data-table/src/router.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -12,6 +12,7 @@ import generateRoutes from './utils/generateRoute' ...@@ -12,6 +12,7 @@ import generateRoutes from './utils/generateRoute'
12 import { showConfirmDialog, Loading } from "vant"; 12 import { showConfirmDialog, Loading } from "vant";
13 import Cookies from 'js-cookie'; 13 import Cookies from 'js-cookie';
14 import { styleColor } from "@/constant.js"; 14 import { styleColor } from "@/constant.js";
15 +import { getCycleListAPI } from '@/api/cycle';
15 16
16 // TAG: 路由配置表 17 // TAG: 路由配置表
17 /** 18 /**
...@@ -37,7 +38,43 @@ const router = createRouter({ ...@@ -37,7 +38,43 @@ const router = createRouter({
37 /** 38 /**
38 * generateRoute 负责把后台返回数据拼接成项目需要的路由结构,动态添加到路由表里面 39 * generateRoute 负责把后台返回数据拼接成项目需要的路由结构,动态添加到路由表里面
39 */ 40 */
40 -router.beforeEach((to, from, next) => { 41 +/**
42 + * 检查是否需要周期选择
43 + * @param {Object} to 目标路由
44 + * @returns {Promise<boolean>} 是否需要周期选择
45 + */
46 +const checkCycleSelection = async (to) => {
47 + // 如果已经在周期选择页面,不需要再检查
48 + if (to.path === '/cycle-selection') {
49 + return false;
50 + }
51 +
52 + // 如果没有表单代码,不需要周期选择
53 + if (!to.query.code) {
54 + return false;
55 + }
56 +
57 + // 如果用户已经选择过周期,不需要再选择
58 + if (to.query.cycle_selected === '1') {
59 + return false;
60 + }
61 +
62 + // 如果是预览模式,不需要周期选择
63 + if (to.query.model === 'preview') {
64 + return false;
65 + }
66 +
67 + try {
68 + const { data } = await getCycleListAPI({ form_code: to.query.code });
69 + // 如果需要周期选择且有周期列表
70 + return data.is_cycle && data.cycle_list && data.cycle_list.length > 0;
71 + } catch (error) {
72 + console.error('检查周期选择失败:', error);
73 + return false;
74 + }
75 +};
76 +
77 +router.beforeEach(async (to, from, next) => {
41 // 使用404为中转页面,避免动态路由没有渲染出来,控制台报警告问题 78 // 使用404为中转页面,避免动态路由没有渲染出来,控制台报警告问题
42 if (to.path == '/404' && to.redirectedFrom != undefined) { 79 if (to.path == '/404' && to.redirectedFrom != undefined) {
43 // 模拟异步操作 80 // 模拟异步操作
...@@ -51,6 +88,23 @@ router.beforeEach((to, from, next) => { ...@@ -51,6 +88,23 @@ router.beforeEach((to, from, next) => {
51 next({ ...to.redirectedFrom, replace: true }); 88 next({ ...to.redirectedFrom, replace: true });
52 }, 1000); 89 }, 1000);
53 } else { 90 } else {
91 + // 检查是否需要周期选择
92 + const needsCycleSelection = await checkCycleSelection(to);
93 + if (needsCycleSelection) {
94 + // 保存目标路由到sessionStorage
95 + sessionStorage.setItem('cycle_target_route', JSON.stringify({
96 + path: to.path,
97 + query: to.query,
98 + params: to.params
99 + }));
100 + // 跳转到周期选择页面
101 + next({
102 + path: '/cycle-selection',
103 + query: { code: to.query.code }
104 + });
105 + return;
106 + }
107 +
54 if (to.query.page_type === 'add' || to.query.page_type === undefined) { // 表单为新增状态, 检查是否有未完成的表单信息 108 if (to.query.page_type === 'add' || to.query.page_type === undefined) { // 表单为新增状态, 检查是否有未完成的表单信息
55 const existingCookie = Cookies.get(to.query.code); 109 const existingCookie = Cookies.get(to.query.code);
56 if (existingCookie && to.query.force_back !== '1') { 110 if (existingCookie && to.query.force_back !== '1') {
......
1 +<template>
2 + <div class="cycle-selection-page">
3 + <van-config-provider :theme-vars="themeVars">
4 + <div class="cycle-popup">
5 + <div class="popup-header">
6 + <h3>选择周期</h3>
7 + </div>
8 + <div class="popup-content">
9 + <van-radio-group v-model="selectedCycle">
10 + <div v-for="cycle in cycleList" :key="cycle.id" class="cycle-item">
11 + <van-radio :name="cycle.id">
12 + <div class="cycle-info">
13 + <div class="cycle-title">{{ cycle.title }}</div>
14 + <div class="cycle-time">
15 + <div v-if="cycle.start_date">活动开始时间: {{ cycle.start_date }}</div>
16 + <div v-if="cycle.end_date">活动结束时间: {{ cycle.end_date }}</div>
17 + <div v-if="cycle.reg_begin_time">报名开始时间: {{ cycle.reg_begin_time }}</div>
18 + <div v-if="cycle.reg_end_time">报名结束时间: {{ cycle.reg_end_time }}</div>
19 + </div>
20 + </div>
21 + </van-radio>
22 + </div>
23 + </van-radio-group>
24 + </div>
25 + <div class="popup-footer">
26 + <van-button type="primary" class="confirm-btn" @click="confirmCycleSelection" :disabled="!selectedCycle">
27 + 确认选择
28 + </van-button>
29 + </div>
30 + </div>
31 + </van-config-provider>
32 + </div>
33 +</template>
34 +
35 +<script setup>
36 +import { ref, onMounted, nextTick } from 'vue';
37 +import { useRouter, useRoute } from 'vue-router';
38 +import { getCycleListAPI } from '@/api/cycle';
39 +import { styleColor } from '@/constant.js';
40 +
41 +const $router = useRouter();
42 +const $route = useRoute();
43 +
44 +// 主题配置
45 +const themeVars = {
46 + // 主色调配置
47 + radioCheckedIconColor: styleColor.baseColor,
48 + buttonPrimaryBackground: styleColor.baseColor,
49 + buttonPrimaryBorderColor: styleColor.baseColor,
50 +};
51 +
52 +// 周期选择相关变量
53 +const cycleList = ref([]);
54 +const selectedCycle = ref('');
55 +
56 +/**
57 + * 动态计算弹窗内容区域高度
58 + */
59 +const calculatePopupContentHeight = () => {
60 + nextTick(() => {
61 + const popupElement = document.querySelector('.cycle-popup');
62 + const headerElement = document.querySelector('.popup-header');
63 + const footerElement = document.querySelector('.popup-footer');
64 + const contentElement = document.querySelector('.popup-content');
65 +
66 + if (popupElement && headerElement && footerElement && contentElement) {
67 + const popupHeight = popupElement.offsetHeight;
68 + const headerHeight = headerElement.offsetHeight;
69 + const footerHeight = footerElement.offsetHeight;
70 + const padding = 100; // 上下padding和margin的总和
71 +
72 + const contentHeight = popupHeight - headerHeight - footerHeight - padding;
73 + contentElement.style.height = `${contentHeight}px`;
74 + }
75 + });
76 +};
77 +
78 +/**
79 + * 获取周期列表
80 + * @param {string} form_code 表单唯一标识
81 + */
82 +const getCycleList = async (form_code) => {
83 + try {
84 + const { data } = await getCycleListAPI({ form_code });
85 + if (data.is_cycle) {
86 + if (!data.cycle_list || data.cycle_list.length === 0) {
87 + // 如果is_cycle是true但cycle_list为空,跳转到停用页面
88 + $router.push("/stop?status=disable");
89 + return;
90 + }
91 + // 设置周期列表
92 + cycleList.value = data.cycle_list;
93 + // 计算高度
94 + calculatePopupContentHeight();
95 + } else {
96 + // 如果不需要周期选择,直接跳转到目标页面
97 + const targetRoute = sessionStorage.getItem('cycle_target_route');
98 + if (targetRoute) {
99 + sessionStorage.removeItem('cycle_target_route');
100 + $router.replace(JSON.parse(targetRoute));
101 + } else {
102 + $router.replace('/');
103 + }
104 + }
105 + } catch (error) {
106 + console.error('获取周期列表失败:', error);
107 + }
108 +};
109 +
110 +/**
111 + * 确认选择周期
112 + */
113 +const confirmCycleSelection = () => {
114 + if (selectedCycle.value) {
115 + // 获取目标路由
116 + const targetRoute = sessionStorage.getItem('cycle_target_route');
117 + if (targetRoute) {
118 + const route = JSON.parse(targetRoute);
119 + // 添加周期参数
120 + route.query = {
121 + ...route.query,
122 + x_cycle: selectedCycle.value,
123 + cycle_selected: '1'
124 + };
125 + // 清除临时存储
126 + sessionStorage.removeItem('cycle_target_route');
127 + // 跳转到目标页面
128 + $router.replace(route);
129 + } else {
130 + // 如果没有目标路由,跳转到首页
131 + $router.replace({
132 + path: '/',
133 + query: {
134 + x_cycle: selectedCycle.value,
135 + cycle_selected: '1'
136 + }
137 + });
138 + }
139 + }
140 +};
141 +
142 +onMounted(() => {
143 + const form_code = $route.query.code;
144 + if (form_code) {
145 + getCycleList(form_code);
146 + } else {
147 + console.error('缺少表单代码参数');
148 + $router.replace('/');
149 + }
150 +});
151 +</script>
152 +
153 +<style lang="less" scoped>
154 +.cycle-selection-page {
155 + width: 100vw;
156 + height: 100vh;
157 + background-color: rgba(0, 0, 0, 0.5);
158 + display: flex;
159 + align-items: center;
160 + justify-content: center;
161 +}
162 +
163 +.cycle-popup {
164 + width: 100%;
165 + max-width: 500px;
166 + height: 70vh;
167 + background: white;
168 + border-radius: 12px;
169 + display: flex;
170 + flex-direction: column;
171 + overflow: hidden;
172 +}
173 +
174 +.popup-header {
175 + padding: 20px;
176 + text-align: center;
177 + border-bottom: 1px solid #eee;
178 + flex-shrink: 0;
179 +
180 + h3 {
181 + margin: 0;
182 + font-size: 18px;
183 + font-weight: 600;
184 + color: #333;
185 + }
186 +}
187 +
188 +.popup-content {
189 + flex: 1;
190 + overflow-y: auto;
191 + padding: 0 20px;
192 + min-height: 0;
193 +}
194 +
195 +.cycle-item {
196 + padding: 15px 0;
197 + border-bottom: 1px solid #f5f5f5;
198 +
199 + &:last-child {
200 + border-bottom: none;
201 + }
202 +}
203 +
204 +.cycle-info {
205 + margin-left: 10px;
206 +}
207 +
208 +.cycle-title {
209 + font-size: 16px;
210 + font-weight: 500;
211 + color: #333;
212 + margin-bottom: 8px;
213 +}
214 +
215 +.cycle-time {
216 + font-size: 14px;
217 + color: #666;
218 + line-height: 1.5;
219 +
220 + div {
221 + margin-bottom: 4px;
222 +
223 + &:last-child {
224 + margin-bottom: 0;
225 + }
226 + }
227 +}
228 +
229 +.popup-footer {
230 + padding: 20px;
231 + border-top: 1px solid #eee;
232 + flex-shrink: 0;
233 +}
234 +
235 +.confirm-btn {
236 + width: 100%;
237 + height: 44px;
238 + border-radius: 8px;
239 + font-size: 16px;
240 + font-weight: 500;
241 +}
242 +</style>