hookehuyr

feat(登录): 添加戒子身份验证功能及相关页面

实现戒子通过身份证号登录的功能,包括:
1. 新增登录页面LoginID.vue
2. 修改api接口路径和参数
3. 添加戒子信息查询接口
4. 创建戒子详情页StudentInfo.vue
5. 暂时禁用路由鉴权逻辑
1 /* 1 /*
2 * @Date: 2023-08-24 09:42:27 2 * @Date: 2023-08-24 09:42:27
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-11-04 21:18:22 4 + * @LastEditTime: 2025-11-12 14:53:22
5 * @FilePath: /stdj_h5/src/api/index.js 5 * @FilePath: /stdj_h5/src/api/index.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -11,7 +11,8 @@ const Api = { ...@@ -11,7 +11,8 @@ const Api = {
11 GET_HOME: '/srv/?a=home', 11 GET_HOME: '/srv/?a=home',
12 GET_ARTICLE: '/srv/?a=get_article', 12 GET_ARTICLE: '/srv/?a=get_article',
13 GET_ARTICLE_FILE: '/srv/?a=get_article&t=file', 13 GET_ARTICLE_FILE: '/srv/?a=get_article&t=file',
14 - POST_LOGIN: '/srv/?a=login', 14 + POST_LOGIN: '/srv/?a=user',
15 + GET_USER_INFO: '/srv/?a=user',
15 }; 16 };
16 17
17 /** 18 /**
...@@ -90,13 +91,26 @@ export const getArticleDetailAPI = (params) => fn(fetch.get(Api.GET_ARTICLE, par ...@@ -90,13 +91,26 @@ export const getArticleDetailAPI = (params) => fn(fetch.get(Api.GET_ARTICLE, par
90 91
91 /** 92 /**
92 * @description: 登录接口 93 * @description: 登录接口
93 - * @param {String} phone 手机号 94 + * @param {String} mobile 手机号
94 - * @param {String} code 验证码 95 + * @param {String} mobile_code 验证码
96 + * @param {String} idcard 身份证号
95 * @returns {Object} data 97 * @returns {Object} data
96 - * @property string data.token 登录凭证 98 + * @property string data.id 用户id(后续接口要转的值)
97 - * @property object data.user 用户信息 99 + * @property string data.nickname 法名
98 - * @property string data.user.id 用户id
99 - * @property string data.user.nickname 昵称
100 - * @property string data.user.avatar 头像
101 */ 100 */
102 export const loginAPI = (params) => fn(fetch.post(Api.POST_LOGIN, params)); 101 export const loginAPI = (params) => fn(fetch.post(Api.POST_LOGIN, params));
102 +
103 +/**
104 + * @description: 戒子信息查询
105 + * @param {String} i 用户id
106 + * @returns {Object} data
107 + * @property string data.id 用户id
108 + * @property string data.name 姓名
109 + * @property string data.nickname 法名
110 + * @property string data.courtesy_name 字号
111 + * @property string data.introduction 简介
112 + * @property string data.group_title 堂口
113 + * @property string data.avatar[{name,url}] 证件照
114 + * @property string data.photo[{name,url}] 其它照片
115 + */
116 +export const getUserInfoAPI = (params) => fn(fetch.get(Api.GET_USER_INFO, params));
......
1 /* 1 /*
2 * @Date: 2025-10-30 10:29:15 2 * @Date: 2025-10-30 10:29:15
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-11-11 22:10:22 4 + * @LastEditTime: 2025-11-12 10:06:07
5 * @FilePath: /stdj_h5/src/router/index.js 5 * @FilePath: /stdj_h5/src/router/index.js
6 * @Description: 文件描述 6 * @Description: 文件描述
7 */ 7 */
...@@ -51,6 +51,11 @@ const routes = [ ...@@ -51,6 +51,11 @@ const routes = [
51 component: () => import('../views/Login.vue') 51 component: () => import('../views/Login.vue')
52 }, 52 },
53 { 53 {
54 + path: '/jz_login',
55 + name: '登录-戒子',
56 + component: () => import('../views/LoginID.vue')
57 + },
58 + {
54 path: '/volunteers', 59 path: '/volunteers',
55 name: '义工', 60 name: '义工',
56 component: () => import('../views/Volunteers.vue') 61 component: () => import('../views/Volunteers.vue')
...@@ -101,24 +106,24 @@ router.beforeEach((to, from, next) => { ...@@ -101,24 +106,24 @@ router.beforeEach((to, from, next) => {
101 * 访问控制:除首页('/')与登录页外,其余页面均需登录 106 * 访问控制:除首页('/')与登录页外,其余页面均需登录
102 * 说明:优先读取 Cookie 中的 token;若不存在则回退读取本地存储 token,避免重复登录。 107 * 说明:优先读取 Cookie 中的 token;若不存在则回退读取本地存储 token,避免重复登录。
103 */ 108 */
104 - const token_cookie = Cookies.get('token-stdj') 109 + // const token_cookie = Cookies.get('token-stdj')
105 - const is_login = !!(token_cookie) 110 + // const is_login = !!(token_cookie)
106 111
107 - // 白名单:无需登录校验的路径 112 + // // 白名单:无需登录校验的路径
108 - const white_list = ['/', '/login'] 113 + // const white_list = ['/', '/login']
109 - const need_auth = !white_list.includes(to.path) 114 + // const need_auth = !white_list.includes(to.path)
110 115
111 - if (need_auth && !is_login) { 116 + // if (need_auth && !is_login) {
112 - // 在重定向前关闭或重置上一跳的loading,避免计数残留 117 + // // 在重定向前关闭或重置上一跳的loading,避免计数残留
113 - try { 118 + // try {
114 - const loading = useLoadingStore() 119 + // const loading = useLoadingStore()
115 - loading.reset() 120 + // loading.reset()
116 - } catch (e) { 121 + // } catch (e) {
117 - void e 122 + // void e
118 - } 123 + // }
119 - next({ name: '登录', query: { redirect: to.fullPath } }) 124 + // next({ name: '登录', query: { redirect: to.fullPath } })
120 - return 125 + // return
121 - } 126 + // }
122 127
123 next() 128 next()
124 }) 129 })
......
1 +<!--
2 + * @Date: 2025-11-10 18:08:59
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-11-12 14:51:18
5 + * @FilePath: /stdj_h5/src/views/LoginID.vue
6 + * @Description: 登录页
7 +-->
8 +<template>
9 + <div class="login-page">
10 + <!-- 顶部LOGO标题 -->
11 + <div class="logo-title">
12 + <img class="logo-img" src="https://cdn.ipadbiz.cn/stdj/images/logo@2x.png" alt="Logo">
13 + </div>
14 +
15 + <!-- 戒子身份验证容器 -->
16 + <div class="auth-card">
17 + <div class="card-title">戒子身份验证</div>
18 +
19 + <!-- 证件号码 -->
20 + <div class="form-item">
21 + <div class="input-with-icon phone">
22 + <input class="input" type="text" placeholder="请输入证件号码" v-model="id_card" maxlength="18" />
23 + </div>
24 + </div>
25 +
26 + <!-- 身份证验证按钮 -->
27 + <div class="form-item">
28 + <div class="btn primary" :class="{ disabled: id_login_disabled }" @click="on_click_login_by_id">立即查询</div>
29 + </div>
30 + </div>
31 + </div>
32 +</template>
33 +
34 +<script setup>
35 +import { ref, computed, onMounted } from 'vue'
36 +import { useRoute, useRouter } from 'vue-router'
37 +import { useTitle } from '@vueuse/core'
38 +import { loginAPI } from '@/api/index.js'
39 +import { showFailToast, showSuccessToast } from 'vant'
40 +import Cookies from 'js-cookie'
41 +
42 +useTitle('戒子身份验证')
43 +
44 +const route = useRoute()
45 +const router = useRouter()
46 +
47 +// 表单状态
48 +const logging = ref(false)
49 +const id_card = ref('')
50 +
51 +/**
52 + * 身份证登录按钮禁用条件
53 + * 说明:登录进行中或输入内容为空时不可操作
54 + */
55 +const id_login_disabled = computed(function () {
56 + return (
57 + logging.value ||
58 + !is_not_empty(id_card.value)
59 + )
60 +})
61 +
62 +/**
63 + * 校验输入是否非空
64 + * 说明:仅校验输入值是否为空字符串(去除首尾空格)
65 + * @param {string} v 输入内容
66 + * @returns {boolean} 是否非空
67 + */
68 +const is_not_empty = function (v) {
69 + return String(v || '').trim().length > 0
70 +}
71 +
72 +/**
73 + * 身份证登录
74 + * 说明:仅凭身份证号进行登录
75 + * @returns {Promise<void>}
76 + */
77 +const on_login_by_id = async function () {
78 + if (logging.value) return
79 + if (!is_not_empty(id_card.value)) {
80 + showFailToast('请输入证件号码')
81 + return
82 + }
83 + try {
84 + logging.value = true
85 + const { code, data } = await loginAPI({ idcard: id_card.value })
86 + if (code) {
87 + Cookies.set('token-stdj', data.id, { expires: 1 })
88 + showSuccessToast('登录成功')
89 + setTimeout(() => {
90 + // 直接跳转到戒子信息页
91 + router.replace({ path: '/studentInfo' })
92 + }, 1000)
93 + }
94 + } catch (e) {
95 + showFailToast('网络异常,请稍后重试')
96 + } finally {
97 + logging.value = false
98 + }
99 +}
100 +
101 +/**
102 + * 点击身份证登录包装函数
103 + * 说明:在禁用态下不触发真实登录逻辑
104 + * @returns {void}
105 + */
106 +const on_click_login_by_id = function () {
107 + if (id_login_disabled.value) return
108 + on_login_by_id()
109 +}
110 +
111 +onMounted(() => {
112 + // 初始化时,检查是否已登录(通过token判断)
113 + const token = Cookies.get('token-stdj')
114 + if (token) {
115 + // 已登录,直接跳转到戒子信息页
116 + router.replace({ path: '/studentInfo' })
117 + }
118 +})
119 +</script>
120 +
121 +<style lang="less" scoped>
122 +// 页面背景与布局
123 +.login-page {
124 + min-height: 100vh;
125 + background-color: #FCF8F1; // 整个页面背景色
126 + display: flex;
127 + flex-direction: column;
128 + align-items: center;
129 + padding: 6rem 1rem;
130 + background-image: url('https://cdn.ipadbiz.cn/stdj/images/bg002@2x.png');
131 + background-size: cover;
132 + background-position: center;
133 +}
134 +
135 +// 顶部Logo标题
136 +.logo-title {
137 + margin-bottom: 2rem;
138 +}
139 +
140 +.logo-img {
141 + height: 5.5rem;
142 + object-fit: contain;
143 + margin-top: 1rem;
144 +}
145 +
146 +// 验证容器
147 +.auth-card {
148 + width: 100%;
149 + max-width: 26rem;
150 + background-color: rgba(174, 155, 99, 0.14); // 容器背景色
151 + border-radius: 0.75rem;
152 + padding: 1rem;
153 + box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.06);
154 +}
155 +
156 +.card-title {
157 + text-align: center;
158 + color: #432C0E;
159 + font-size: 1.125rem;
160 + font-weight: 700;
161 + margin-top: 0.75rem;
162 + margin-bottom: 1.25rem;
163 +}
164 +
165 +// 表单项布局
166 +.form-item {
167 + margin-top: 1.25rem;
168 + margin-bottom: 0.75rem;
169 +}
170 +
171 +// 输入框:带左侧图标
172 +.input-with-icon {
173 + position: relative;
174 +}
175 +
176 +.input-with-icon::before {
177 + content: '';
178 + position: absolute;
179 + left: 0.75rem;
180 + top: 50%;
181 + width: 1rem;
182 + height: 1rem;
183 + background-size: contain;
184 + background-position: center;
185 + background-repeat: no-repeat;
186 + transform: translateY(-50%);
187 +}
188 +
189 +.input-with-icon.phone::before {
190 + background-image: url('https://cdn.ipadbiz.cn/stdj/images/%E8%BA%AB%E4%BB%BD@2x.png');
191 +}
192 +
193 +
194 +
195 +.input {
196 + width: 100%;
197 + height: 2.5rem;
198 + border-radius: 0.5rem;
199 + border: none;
200 + background-color: #FFFFFF;
201 + padding: 0 0.75rem 0 2.25rem; // 为左侧图标预留空间
202 + box-shadow: inset 0 0 0 0.0625rem rgba(0, 0, 0, 0.08);
203 +}
204 +
205 +.input::placeholder {
206 + color: #999999;
207 +}
208 +
209 +// 按钮样式
210 +.btn {
211 + height: 2.5rem;
212 + padding: 0 0.75rem;
213 + border: none;
214 + border-radius: 0.5rem;
215 + background-color: #A67939; // 获取验证码与立即验证背景色
216 + color: #FFFFFF;
217 + font-weight: 600;
218 + text-align: center;
219 + line-height: 2.5rem;
220 +}
221 +
222 +.btn.primary {
223 + width: 100%;
224 +}
225 +
226 +.btn.disabled {
227 + // 不可操作态:明显的灰显与禁止样式
228 + opacity: 0.5;
229 + cursor: not-allowed;
230 + pointer-events: none;
231 + filter: grayscale(30%);
232 + box-shadow: none;
233 +}
234 +</style>
1 <!-- 1 <!--
2 * @Date: 2025-11-10 18:12:23 2 * @Date: 2025-11-10 18:12:23
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-11-11 13:04:06 4 + * @LastEditTime: 2025-11-12 15:14:14
5 * @FilePath: /stdj_h5/src/views/StudentInfo.vue 5 * @FilePath: /stdj_h5/src/views/StudentInfo.vue
6 * @Description: 戒子详情页 6 * @Description: 戒子详情页
7 --> 7 -->
...@@ -9,74 +9,61 @@ ...@@ -9,74 +9,61 @@
9 <div class="masters-detail-container"> 9 <div class="masters-detail-container">
10 <section class="single-list"> 10 <section class="single-list">
11 <div class="item-card"> 11 <div class="item-card">
12 - <img :src="article_item.src" :alt="article_item.title" class="item-image" /> 12 + <img :src="student_info.src" :alt="student_info.title" class="item-image" />
13 <div class="item-content"> 13 <div class="item-content">
14 - <div class="item-role">{{ article_item.role }}</div> 14 + <div class="item-role">{{ student_info.role }}</div>
15 - <div class="item-name" v-html="formatNameWithSuperscript(article_item.name)"></div> 15 + <div class="item-name" v-html="formatNameWithSuperscript(student_info.name)"></div>
16 - <div class="item-desc" v-html="article_item.desc"></div> 16 + <div class="item-group-title">{{ student_info.group_title }}</div>
17 + <div class="item-desc" v-html="student_info.desc"></div>
17 </div> 18 </div>
18 </div> 19 </div>
19 </section> 20 </section>
20 <!-- 标题图片 --> 21 <!-- 标题图片 -->
21 <div class="common-title"> 22 <div class="common-title">
22 - <img src="https://cdn.ipadbiz.cn/stdj/images/%E7%9B%B8%E5%85%B3%E7%85%A7%E7%89%87@2x.png" alt="相关照片" class="title-image"> 23 + <img src="https://cdn.ipadbiz.cn/stdj/images/%E7%9B%B8%E5%85%B3%E7%85%A7%E7%89%87@2x.png" alt="相关照片"
24 + class="title-image">
23 </div> 25 </div>
24 <!-- 下载提示:参考首页滑动提示样式 --> 26 <!-- 下载提示:参考首页滑动提示样式 -->
25 - <div class="download-hint"> 27 + <div v-if="student_info?.photo?.length" class="download-hint">
26 <span class="download-icon">⬇</span> 28 <span class="download-icon">⬇</span>
27 <span class="download-text">点击查看大图 长按图片下载</span> 29 <span class="download-text">点击查看大图 长按图片下载</span>
28 </div> 30 </div>
29 <!-- 瀑布流内容 --> 31 <!-- 瀑布流内容 -->
30 - <div class="waterfall-content"> 32 + <div v-if="student_info?.photo?.length" class="waterfall-content">
31 - <van-list
32 - v-model:loading="loading"
33 - :finished="finished"
34 - finished-text="没有更多了"
35 - @load="onLoad"
36 - >
37 <div class="waterfall-container"> 33 <div class="waterfall-container">
38 <div class="waterfall-column" v-for="(column, index) in columns" :key="index"> 34 <div class="waterfall-column" v-for="(column, index) in columns" :key="index">
39 - <div 35 + <div class="waterfall-item" v-for="item in column" :key="item.id" @click="onImageClick(item)">
40 - class="waterfall-item"
41 - v-for="item in column"
42 - :key="item.id"
43 - @click="onImageClick(item)"
44 - >
45 <div class="image-wrapper"> 36 <div class="image-wrapper">
46 - <img 37 + <img :src="item.src" :alt="item.title" :style="{ height: item.height + 'px' }" @load="onImageLoad"
47 - :src="item.src" 38 + @error="onImageError" />
48 - :alt="item.title"
49 - :style="{ height: item.height + 'px' }"
50 - @load="onImageLoad"
51 - @error="onImageError"
52 - />
53 - <!-- <div class="image-overlay">
54 - <span class="image-title">{{ item.title }}</span>
55 - </div> -->
56 </div> 39 </div>
57 </div> 40 </div>
58 </div> 41 </div>
59 </div> 42 </div>
60 - </van-list> 43 + </div>
44 + <div v-else>
45 + <div class="empty-message">暂无相关照片</div>
61 </div> 46 </div>
62 </div> 47 </div>
63 </template> 48 </template>
64 49
65 <script setup> 50 <script setup>
66 -import { ref, onMounted } from 'vue' 51 +import { ref, onMounted, reactive } from 'vue'
67 import { useTitle } from '@vueuse/core'; 52 import { useTitle } from '@vueuse/core';
68 import { useRoute, useRouter } from 'vue-router' 53 import { useRoute, useRouter } from 'vue-router'
69 import { showImagePreview } from 'vant' 54 import { showImagePreview } from 'vant'
55 +import Cookies from 'js-cookie'
70 56
71 // 导入接口 57 // 导入接口
72 -import { getArticleDetailAPI, getImgStreamAPI } from '@/api/index.js' 58 +import { getUserInfoAPI } from '@/api/index.js'
73 59
74 useTitle('戒子详情') 60 useTitle('戒子详情')
75 61
76 const route = useRoute() 62 const route = useRoute()
77 const router = useRouter() 63 const router = useRouter()
78 64
79 -const article_item = ref({}) 65 +const info_id = ref('');
66 +const student_info = ref({})
80 67
81 /** 68 /**
82 * 为name字段的第一个文字添加上标效果 69 * 为name字段的第一个文字添加上标效果
...@@ -93,21 +80,33 @@ const formatNameWithSuperscript = (name) => { ...@@ -93,21 +80,33 @@ const formatNameWithSuperscript = (name) => {
93 } 80 }
94 81
95 /** 82 /**
96 - * 加载文章详情 83 + * 加载详情
97 */ 84 */
98 -const loadArticleDetail = async () => { 85 +const loadUserInfo = async () => {
99 try { 86 try {
100 - // const articleId = route.params.id 87 + const { code, data } = await getUserInfoAPI({ i: info_id.value })
101 - const articleId = '3662153'
102 - const { code, data } = await getArticleDetailAPI({ i: articleId })
103 if (code) { 88 if (code) {
104 // 遍历data对象,将每个元素转换为新的对象格式 89 // 遍历data对象,将每个元素转换为新的对象格式
105 - article_item.value = { 90 + student_info.value = {
106 id: data.id, 91 id: data.id,
107 - role: data.post_excerpt, 92 + role: data.name,
108 - name: data.post_title, 93 + name: data.nickname,
109 - src: data?.file_list?.photo?.value, 94 + src: data?.avatar[0].url,
110 - desc: data.post_content 95 + desc: data.introduction,
96 + courtesy_name: data.courtesy_name,
97 + group_title: data.group_title,
98 + photo: data.photo,
99 + }
100 + // 处理图片数据
101 + if (student_info.value.photo?.length) {
102 + student_info.value.photo = student_info.value.photo.map(item => ({
103 + id: item.id,
104 + src: item.url,
105 + title: item.name,
106 + height: Math.floor(Math.random() * 200) + 200
107 + }))
108 + allImages.value = student_info.value.photo
109 + distributeImages(student_info.value.photo)
111 } 110 }
112 } 111 }
113 } catch (error) { 112 } catch (error) {
...@@ -115,57 +114,10 @@ const loadArticleDetail = async () => { ...@@ -115,57 +114,10 @@ const loadArticleDetail = async () => {
115 } 114 }
116 } 115 }
117 116
118 -// const i = ref(router.currentRoute.value.query.i)
119 -const i = ref('3680502')
120 -
121 // 响应式数据 117 // 响应式数据
122 -const loading = ref(false)
123 -const finished = ref(false)
124 -const currentPage = ref(0) // API使用0作为起始页码
125 -const pageSize = 10
126 const allImages = ref([]) 118 const allImages = ref([])
127 const columns = reactive([[], []]) 119 const columns = reactive([[], []])
128 120
129 -// 加载数据
130 -const onLoad = async () => {
131 - loading.value = true
132 -
133 - try {
134 - // 调用正式接口
135 - const { code, data } = await getImgStreamAPI({
136 - i: i.value,
137 - page: currentPage.value,
138 - limit: pageSize
139 - })
140 -
141 - if (code) {
142 - const newData = data.list.map(item => ({
143 - id: item.id,
144 - src: item.value, // 修改为src,用于ImagePreview
145 - title: item.name,
146 - description: item.description,
147 - date: item.post_date,
148 - height: Math.floor(Math.random() * 200) + 200 // 随机高度用于瀑布流布局
149 - }))
150 -
151 - if (newData.length === 0) {
152 - finished.value = true
153 - } else {
154 - allImages.value.push(...newData)
155 - distributeImages(newData)
156 - currentPage.value++
157 - }
158 - } else {
159 - finished.value = true
160 - }
161 - } catch (error) {
162 - console.error('加载数据失败:', error)
163 - finished.value = true
164 - } finally {
165 - loading.value = false
166 - }
167 -}
168 -
169 // 分配图片到两列 121 // 分配图片到两列
170 const distributeImages = (images) => { 122 const distributeImages = (images) => {
171 images.forEach(image => { 123 images.forEach(image => {
...@@ -188,17 +140,8 @@ const distributeImages = (images) => { ...@@ -188,17 +140,8 @@ const distributeImages = (images) => {
188 */ 140 */
189 // 图片点击事件 - 仅预览当前单张图片 141 // 图片点击事件 - 仅预览当前单张图片
190 const onImageClick = (item) => { 142 const onImageClick = (item) => {
191 - console.log('点击图片:', item)
192 -
193 - // 获取当前点击图片在所有图片中的索引
194 - const currentIndex = allImages.value.findIndex(img => img.id === item.id)
195 -
196 - // 提取所有图片的src用于预览
197 - const images = allImages.value.map(img => img.src)
198 -
199 - // 仅预览当前单张图片,禁用索引与循环
200 showImagePreview({ 143 showImagePreview({
201 - images: [images[currentIndex]], 144 + images: [item.src],
202 startPosition: 0, 145 startPosition: 0,
203 showIndex: false, 146 showIndex: false,
204 loop: false, 147 loop: false,
...@@ -208,7 +151,7 @@ const onImageClick = (item) => { ...@@ -208,7 +151,7 @@ const onImageClick = (item) => {
208 151
209 // 图片加载成功 152 // 图片加载成功
210 const onImageLoad = (event) => { 153 const onImageLoad = (event) => {
211 - console.log('图片加载成功:', event.target.src) 154 + // console.log('图片加载成功:', event.target.src)
212 } 155 }
213 156
214 // 图片加载失败 157 // 图片加载失败
...@@ -219,13 +162,20 @@ const onImageError = (event) => { ...@@ -219,13 +162,20 @@ const onImageError = (event) => {
219 } 162 }
220 163
221 onMounted(() => { 164 onMounted(() => {
222 - loadArticleDetail() 165 + // 检查是否已登录(通过token判断)
223 - // 初始加载第一页数据 166 + const token = Cookies.get('token-stdj')
224 - onLoad() 167 + if (!token) {
168 + // 未登录,跳转到登录页
169 + router.replace({ path: '/jz_login' })
170 + return
171 + }
172 + //
173 + info_id.value = token
174 + //
175 + loadUserInfo()
225 }) 176 })
226 </script> 177 </script>
227 178
228 -
229 <style scoped> 179 <style scoped>
230 /* 通用标题样式 */ 180 /* 通用标题样式 */
231 .common-title { 181 .common-title {
...@@ -241,6 +191,14 @@ onMounted(() => { ...@@ -241,6 +191,14 @@ onMounted(() => {
241 height: auto; 191 height: auto;
242 } 192 }
243 193
194 +.empty-message {
195 + text-align: center;
196 + color: #999999;
197 + font-size: 1rem;
198 + margin-top: 2rem;
199 +}
200 +
201 +
244 /* 下载提示样式(参考首页 swipe-hint) */ 202 /* 下载提示样式(参考首页 swipe-hint) */
245 .download-hint { 203 .download-hint {
246 display: flex; 204 display: flex;
...@@ -252,11 +210,13 @@ onMounted(() => { ...@@ -252,11 +210,13 @@ onMounted(() => {
252 text-shadow: 0 1px 1px rgba(0, 0, 0, 0.08); 210 text-shadow: 0 1px 1px rgba(0, 0, 0, 0.08);
253 font-weight: 700; 211 font-weight: 700;
254 } 212 }
213 +
255 .download-icon { 214 .download-icon {
256 font-size: 0.875rem; 215 font-size: 0.875rem;
257 line-height: 1; 216 line-height: 1;
258 filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.15)); 217 filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.15));
259 } 218 }
219 +
260 .download-text { 220 .download-text {
261 font-size: 0.875rem; 221 font-size: 0.875rem;
262 font-weight: 700; 222 font-weight: 700;
...@@ -268,6 +228,7 @@ onMounted(() => { ...@@ -268,6 +228,7 @@ onMounted(() => {
268 .download-hint { 228 .download-hint {
269 margin-bottom: 1.25rem; 229 margin-bottom: 1.25rem;
270 } 230 }
231 +
271 .download-icon, 232 .download-icon,
272 .download-text { 233 .download-text {
273 font-size: 0.8rem; 234 font-size: 0.8rem;
...@@ -323,6 +284,12 @@ onMounted(() => { ...@@ -323,6 +284,12 @@ onMounted(() => {
323 margin-top: 0.25rem; 284 margin-top: 0.25rem;
324 } 285 }
325 286
287 +.item-group-title {
288 + font-size: 1rem;
289 + font-weight: 500;
290 + margin-top: 0.25rem;
291 +}
292 +
326 .item-desc { 293 .item-desc {
327 margin-top: 0.5rem; 294 margin-top: 0.5rem;
328 } 295 }
...@@ -447,6 +414,7 @@ onMounted(() => { ...@@ -447,6 +414,7 @@ onMounted(() => {
447 0% { 414 0% {
448 background-position: 200% 0; 415 background-position: 200% 0;
449 } 416 }
417 +
450 100% { 418 100% {
451 background-position: -200% 0; 419 background-position: -200% 0;
452 } 420 }
......