af-mobile-client-vue3 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "af-mobile-client-vue3",
3
3
  "type": "module",
4
- "version": "1.3.5",
4
+ "version": "1.3.7",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "description": "Vue + Vite component lib",
7
7
  "engines": {
@@ -0,0 +1,17 @@
1
+ import { post } from '@af-mobile-client-vue3/services/restTools'
2
+
3
+ const userApi = {
4
+ // 修改密码
5
+ modifyPassword: '/af-system/user/modifypwd',
6
+ }
7
+
8
+ /**
9
+ * 修改密码
10
+ * @param data 修改密码参数
11
+ * @returns Promise
12
+ */
13
+ export function modifyPassword(data: any) {
14
+ return post(userApi.modifyPassword, JSON.stringify(data))
15
+ }
16
+
17
+ export { userApi }
@@ -0,0 +1,346 @@
1
+ <script setup lang="ts">
2
+ import { modifyPassword } from '@af-mobile-client-vue3/services/api/user'
3
+ import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
4
+ import { showDialog, showToast, Button as vanButton, Field as vanField, Form as vanForm, Icon as vanIcon, Popup as vanPopup } from 'vant'
5
+ import { reactive, ref, watch } from 'vue'
6
+ import { useRouter } from 'vue-router'
7
+
8
+ // 定义 props 和 emits
9
+ const props = defineProps<{
10
+ visible: boolean
11
+ }>()
12
+
13
+ const emit = defineEmits<{
14
+ (event: 'update:visible', value: boolean): void
15
+ }>()
16
+
17
+ const router = useRouter()
18
+ const userStore = useUserStore()
19
+
20
+ // 表单引用
21
+ const formRef = ref()
22
+
23
+ // 表单数据
24
+ const formData = reactive({
25
+ oldPassword: '',
26
+ newPassword: '',
27
+ confirmPassword: '',
28
+ })
29
+
30
+ // 密码显示状态
31
+ const showOldPassword = ref(false)
32
+ const showNewPassword = ref(false)
33
+ const showConfirmPassword = ref(false)
34
+
35
+ // 加载状态
36
+ const loading = ref(false)
37
+
38
+ // 密码强度验证正则
39
+ const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
40
+
41
+ // 验证密码强度
42
+ function validatePasswordStrength(password: string): boolean {
43
+ return strongPasswordRegex.test(password)
44
+ }
45
+
46
+ // 验证确认密码
47
+ function validateConfirmPassword(value: string): boolean {
48
+ return value === formData.newPassword
49
+ }
50
+
51
+ // 表单验证规则
52
+ const rules = {
53
+ oldPassword: [
54
+ { required: true, message: '请输入旧密码' },
55
+ ],
56
+ newPassword: [
57
+ { required: true, message: '请输入新密码' },
58
+ {
59
+ validator: (value: string) => {
60
+ if (value && !validatePasswordStrength(value)) {
61
+ return '新密码必须包含大小写字母、数字和特殊字符,且长度不少于8位'
62
+ }
63
+ return true
64
+ },
65
+ },
66
+ ],
67
+ confirmPassword: [
68
+ { required: true, message: '请再次输入新密码' },
69
+ {
70
+ validator: (value: string) => {
71
+ if (value && !validateConfirmPassword(value)) {
72
+ return '两次密码输入不一致'
73
+ }
74
+ return true
75
+ },
76
+ },
77
+ ],
78
+ }
79
+
80
+ // 提交表单
81
+ async function handleSubmit() {
82
+ try {
83
+ loading.value = true
84
+
85
+ // 验证表单
86
+ await formRef.value?.validate()
87
+
88
+ // 调用修改密码API
89
+ const result = await handleModifyPassword({ data: {
90
+ ename: userStore.getUserInfo().username,
91
+ password: formData.oldPassword,
92
+ newpassword: formData.newPassword,
93
+ affirmpassword: formData.confirmPassword,
94
+ } })
95
+ if (result.success) {
96
+ showDialog({
97
+ title: '成功',
98
+ message: '修改密码成功,请重新登录',
99
+ confirmButtonText: '立即重新登录',
100
+ }).then(() => {
101
+ // 退出登录
102
+ userStore.logout()
103
+ // 跳转到登录页
104
+ router.push('/login')
105
+ })
106
+ }
107
+ else {
108
+ showToast(result.message || '修改密码失败')
109
+ }
110
+ }
111
+ catch (error) {
112
+ console.error('修改密码失败:', error)
113
+ showToast('修改密码失败,请检查输入信息')
114
+ }
115
+ finally {
116
+ loading.value = false
117
+ }
118
+ }
119
+
120
+ // 关闭弹窗
121
+ function closeModal() {
122
+ emit('update:visible', false)
123
+ }
124
+
125
+ // 修改密码API调用
126
+ async function handleModifyPassword(data: any) {
127
+ try {
128
+ const res = await modifyPassword(data)
129
+ if (res && res === 'true') {
130
+ return {
131
+ success: true,
132
+ message: '修改成功',
133
+ }
134
+ }
135
+ else {
136
+ return {
137
+ success: false,
138
+ message: '修改密码失败,请检查原始密码是否正确!',
139
+ }
140
+ }
141
+ }
142
+ catch (error: any) {
143
+ return {
144
+ success: false,
145
+ message: error?.message || '修改密码失败',
146
+ }
147
+ }
148
+ }
149
+
150
+ // 监听visible变化,重置表单
151
+ watch(() => props.visible, (newVal) => {
152
+ if (!newVal) {
153
+ // 弹窗关闭时重置表单
154
+ formData.oldPassword = ''
155
+ formData.newPassword = ''
156
+ formData.confirmPassword = ''
157
+ showOldPassword.value = false
158
+ showNewPassword.value = false
159
+ showConfirmPassword.value = false
160
+ }
161
+ })
162
+ </script>
163
+
164
+ <template>
165
+ <van-popup
166
+ :show="props.visible"
167
+ position="center"
168
+ :close-on-click-overlay="false"
169
+ round
170
+ class="modify-password-popup"
171
+ @update:show="emit('update:visible', $event)"
172
+ >
173
+ <div class="popup-content">
174
+ <!-- 标题 -->
175
+ <div class="popup-header">
176
+ <h3 class="popup-title">
177
+ 修改密码
178
+ </h3>
179
+ <van-icon name="cross" class="close-icon" @click="closeModal" />
180
+ </div>
181
+
182
+ <!-- 表单 -->
183
+ <van-form ref="formRef" class="password-form" @submit="handleSubmit">
184
+ <!-- 旧密码 -->
185
+ <van-field
186
+ v-model="formData.oldPassword"
187
+ name="oldPassword"
188
+ label="旧密码"
189
+ placeholder="请输入旧密码"
190
+ :type="showOldPassword ? 'text' : 'password'"
191
+ :rules="rules.oldPassword"
192
+ @click-right-icon="showOldPassword = !showOldPassword"
193
+ >
194
+ <template #right-icon>
195
+ <van-icon
196
+ :name="showOldPassword ? 'eye-o' : 'closed-eye'"
197
+ color="#666"
198
+ />
199
+ </template>
200
+ </van-field>
201
+
202
+ <!-- 新密码 -->
203
+ <van-field
204
+ v-model="formData.newPassword"
205
+ name="newPassword"
206
+ label="新密码"
207
+ placeholder="请输入新密码"
208
+ :type="showNewPassword ? 'text' : 'password'"
209
+ :rules="rules.newPassword"
210
+ @click-right-icon="showNewPassword = !showNewPassword"
211
+ >
212
+ <template #right-icon>
213
+ <van-icon
214
+ :name="showNewPassword ? 'eye-o' : 'closed-eye'"
215
+ color="#666"
216
+ />
217
+ </template>
218
+ </van-field>
219
+
220
+ <!-- 确认新密码 -->
221
+ <van-field
222
+ v-model="formData.confirmPassword"
223
+ name="confirmPassword"
224
+ label="确认新密码"
225
+ placeholder="请再次输入新密码"
226
+ :type="showConfirmPassword ? 'text' : 'password'"
227
+ :rules="rules.confirmPassword"
228
+ @click-right-icon="showConfirmPassword = !showConfirmPassword"
229
+ >
230
+ <template #right-icon>
231
+ <van-icon
232
+ :name="showConfirmPassword ? 'eye-o' : 'closed-eye'"
233
+ color="#666"
234
+ />
235
+ </template>
236
+ </van-field>
237
+
238
+ <!-- 按钮组 -->
239
+ <div class="form-buttons">
240
+ <van-button
241
+ plain
242
+ type="default"
243
+ size="large"
244
+ class="cancel-btn"
245
+ @click="closeModal"
246
+ >
247
+ 取消
248
+ </van-button>
249
+ <van-button
250
+ type="primary"
251
+ size="large"
252
+ native-type="submit"
253
+ :loading="loading"
254
+ class="submit-btn"
255
+ >
256
+ 确认修改
257
+ </van-button>
258
+ </div>
259
+ </van-form>
260
+ </div>
261
+ </van-popup>
262
+ </template>
263
+
264
+ <style scoped lang="less">
265
+ .modify-password-popup {
266
+ width: 90%;
267
+ max-width: 400px;
268
+
269
+ .popup-content {
270
+ padding: 20px;
271
+
272
+ .popup-header {
273
+ display: flex;
274
+ justify-content: space-between;
275
+ align-items: center;
276
+ margin-bottom: 20px;
277
+
278
+ .popup-title {
279
+ font-size: 18px;
280
+ font-weight: 600;
281
+ color: #333;
282
+ margin: 0;
283
+ }
284
+
285
+ .close-icon {
286
+ font-size: 20px;
287
+ color: #999;
288
+ cursor: pointer;
289
+ padding: 4px;
290
+
291
+ &:hover {
292
+ color: #666;
293
+ }
294
+ }
295
+ }
296
+
297
+ .password-form {
298
+ .form-buttons {
299
+ display: flex;
300
+ gap: 12px;
301
+ margin-top: 24px;
302
+
303
+ .cancel-btn,
304
+ .submit-btn {
305
+ flex: 1;
306
+ height: 44px;
307
+ border-radius: 8px;
308
+ font-size: 16px;
309
+ }
310
+
311
+ .cancel-btn {
312
+ border-color: #ddd;
313
+ color: #666;
314
+ }
315
+
316
+ .submit-btn {
317
+ background-color: #3b82f6;
318
+ border-color: #3b82f6;
319
+ }
320
+ }
321
+ }
322
+ }
323
+ }
324
+
325
+ // 覆盖vant默认样式
326
+ :deep(.van-field) {
327
+ padding: 12px 0;
328
+
329
+ .van-field__label {
330
+ color: #333;
331
+ font-weight: 500;
332
+ }
333
+
334
+ .van-field__control {
335
+ color: #333;
336
+ }
337
+
338
+ .van-field__right-icon {
339
+ padding: 4px;
340
+ }
341
+ }
342
+
343
+ :deep(.van-popup) {
344
+ border-radius: 12px;
345
+ }
346
+ </style>
@@ -1,230 +1,337 @@
1
1
  <script setup lang="ts">
2
+ import useSettingStore from '@af-mobile-client-vue3/stores/modules/setting'
2
3
  import useUserStore from '@af-mobile-client-vue3/stores/modules/user'
3
- import {
4
- Cell as VanCell,
5
- Col as VanCol,
6
- List as VanList,
7
- Row as VanRow,
8
- } from 'vant/es'
4
+ import { Dialog as vanDialog, Icon as vanIcon } from 'vant'
5
+ import { ref } from 'vue'
6
+ import { useRouter } from 'vue-router'
7
+ import ModifyPassword from './comm/ModifyPassword.vue'
9
8
 
9
+ const router = useRouter()
10
10
  const username = useUserStore().getUserInfo().name
11
- const fullnames = useUserStore().getLogin().f.resources.parentname
11
+ const fullnames = useUserStore().getLogin().f.resources.fullnames
12
12
 
13
- async function exit_login() {
14
- await useUserStore().logout()
13
+ // 修改密码弹窗显示状态
14
+ const showModifyPassword = ref(false)
15
+ // 退出登录确认弹窗显示状态
16
+ const showLogoutConfirm = ref(false)
17
+
18
+ function exit_login() {
19
+ showLogoutConfirm.value = true
20
+ }
21
+
22
+ function confirmLogout() {
23
+ useUserStore().logout()
24
+ showLogoutConfirm.value = false
15
25
  }
26
+
27
+ function cancelLogout() {
28
+ showLogoutConfirm.value = false
29
+ }
30
+
31
+ function goToAttendance() {
32
+ router.push('/user/attendance')
33
+ }
34
+
35
+ function openModifyPassword() {
36
+ showModifyPassword.value = true
37
+ }
38
+
39
+ const webMobileConfig = useSettingStore().getSetting()
16
40
  </script>
17
41
 
18
42
  <template>
19
- <main class="my_main h-full w-full">
20
- <div class="header">
21
- <div class="style_back">
22
- <div class="circle circle1" />
23
- <div class="circle circle2" />
43
+ <main class="my_main">
44
+ <!-- 顶部导航栏 -->
45
+ <nav class="header_nav">
46
+ <div class="header_content">
47
+ <h1 class="main_title">
48
+ 我的
49
+ </h1>
50
+ <!-- <div class="setting_section"> -->
51
+ <!-- <van-icon name="setting-o" class="setting-icon" /> -->
52
+ <!-- </div> -->
24
53
  </div>
25
- <div class="header_main">
26
- <div class="user_nav">
27
- <img alt="setting_icon" class="user_setting_icon" src="../../../assets/img/user/my/setting.png">
28
- </div>
29
- <div class="user_content">
30
- <VanRow :gutter="18">
31
- <VanCol>
32
- <img alt="default-user-profile" class="default_user_profile" src="../../../assets/common/default-user-profile.png">
33
- </VanCol>
34
- <VanCol class="user_info_col">
35
- <p class="user_nickname">
36
- {{ username }}
37
- </p>
38
- <p class="user_remark">
39
- {{ fullnames }}
40
- </p>
41
- </VanCol>
42
- </VanRow>
43
- </div>
44
- </div>
45
- </div>
54
+ </nav>
55
+
56
+ <!-- 主要内容区域 -->
46
57
  <div class="content">
47
- <div class="core">
48
- <VanRow class="core_row">
49
- <VanCol>
50
- <p class="core_value">
51
- 24
58
+ <!-- 用户信息卡片 -->
59
+ <div class="user-card">
60
+ <div class="user-info">
61
+ <div class="avatar-wrap">
62
+ <img alt="default-user-profile" class="avatar" src="../../../assets/common/default-user-profile.png">
63
+ <div class="status-dot" />
64
+ </div>
65
+ <div class="user-detail">
66
+ <h2 class="username">
67
+ {{ username }}
68
+ </h2>
69
+ <p class="user-role">
70
+ {{ fullnames }}
52
71
  </p>
53
- <p class="core_title">
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <!-- 工作统计 -->
77
+ <div class="stats-card">
78
+ <div class="stats-grid">
79
+ <div class="stat-item">
80
+ <div class="stat-value">
81
+ 0
82
+ </div>
83
+ <div class="stat-label">
54
84
  待办任务
55
- </p>
56
- </VanCol>
57
- <VanCol>
58
- <p class="core_value">
59
- 2
60
- </p>
61
- <p class="core_title">
85
+ </div>
86
+ </div>
87
+ <div class="stat-item">
88
+ <div class="stat-value">
89
+ 0
90
+ </div>
91
+ <div class="stat-label">
62
92
  待办流程
63
- </p>
64
- </VanCol>
65
- <VanCol>
66
- <p class="core_value">
67
- 7
68
- </p>
69
- <p class="core_title">
93
+ </div>
94
+ </div>
95
+ <div class="stat-item">
96
+ <div class="stat-value">
97
+ 0
98
+ </div>
99
+ <div class="stat-label">
70
100
  新消息
71
- </p>
72
- </VanCol>
73
- </VanRow>
101
+ </div>
102
+ </div>
103
+ </div>
74
104
  </div>
75
- <div class="setting">
76
- <VanList>
77
- <VanCell is-link @click="exit_login">
78
- <template #title>
79
- <VanRow :gutter="12" class="setting_row">
80
- <VanCol>
81
- <img alt="exit_login_icon" class="setting_icon" src="../../../assets/img/user/my/exit-login.png">
82
- </VanCol>
83
- <VanCol class="setting_name_col">
84
- <span class="setting_name_text">退出登录</span>
85
- </VanCol>
86
- </VanRow>
87
- </template>
88
- <template #right-icon>
89
- <img alt="setting_arrow_icon" class="setting_arrow_icon" src="../../../assets/img/user/my/setting-arrow.png">
90
- </template>
91
- </VanCell>
92
- </VanList>
105
+
106
+ <!-- 功能列表 -->
107
+ <div class="menu-card">
108
+ <div class="menu-section">
109
+ <h3 class="menu-title">
110
+ 系统功能
111
+ </h3>
112
+ <div class="menu-list">
113
+ <div v-if="webMobileConfig?.isAttendance" class="menu-item" @click="goToAttendance">
114
+ <div class="menu-item-content">
115
+ <van-icon name="user-o" class="menu-icon" />
116
+ <span class="menu-text">考勤管理</span>
117
+ </div>
118
+ <van-icon name="arrow" class="menu-arrow" />
119
+ </div>
120
+ <div class="menu-item" @click="openModifyPassword">
121
+ <div class="menu-item-content">
122
+ <van-icon name="lock" class="menu-icon" />
123
+ <span class="menu-text">修改密码</span>
124
+ </div>
125
+ <van-icon name="arrow" class="menu-arrow" />
126
+ </div>
127
+ <div class="menu-item" @click="exit_login">
128
+ <div class="menu-item-content">
129
+ <van-icon name="sign" class="menu-icon logout-icon" />
130
+ <span class="menu-text">退出登录</span>
131
+ </div>
132
+ <van-icon name="arrow" class="menu-arrow" />
133
+ </div>
134
+ </div>
135
+ </div>
93
136
  </div>
94
137
  </div>
138
+
139
+ <!-- 修改密码弹窗 -->
140
+ <ModifyPassword
141
+ v-model:visible="showModifyPassword"
142
+ />
143
+
144
+ <!-- 退出登录确认弹窗 -->
145
+ <van-dialog
146
+ v-model:show="showLogoutConfirm"
147
+ title="确认退出"
148
+ message="确定要退出登录吗?"
149
+ show-cancel-button
150
+ confirm-button-text="确认退出"
151
+ cancel-button-text="取消"
152
+ @confirm="confirmLogout"
153
+ @cancel="cancelLogout"
154
+ />
95
155
  </main>
96
156
  </template>
97
157
 
98
158
  <style scoped lang="less">
99
159
  .my_main {
100
- .header {
160
+ min-height: 100vh;
161
+ background-color: #f7f8fa;
162
+
163
+ // 顶部导航栏
164
+ .header_nav {
165
+ background-color: #fff;
166
+ padding: 0 16px;
167
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
101
168
  position: relative;
102
- height: 200px;
103
- background-color: #336bf1;
104
- overflow: hidden;
105
- .style_back {
106
- .circle {
107
- position: absolute;
108
- border-radius: 50%;
109
- }
110
- .circle1 {
111
- background-color: #3f7afa;
112
- width: 265px;
113
- height: 265px;
114
- top: -80px;
115
- left: -50px;
116
- z-index: 3;
117
- }
118
- .circle2 {
119
- background-color: #3c75f9;
120
- width: 300px;
121
- height: 300px;
122
- top: -80px;
123
- left: 10px;
124
- z-index: 2;
125
- }
126
- }
127
- .header_main {
128
- padding: var(--base-interval-1);
129
- .user_nav {
130
- padding: 0 var(--base-interval-0);
131
- color: #fff;
132
- position: relative;
133
- z-index: 4;
134
- font-size: 26px;
135
- text-align: right;
136
- .user_setting_icon {
137
- width: 26px;
138
- height: 26px;
139
- }
169
+ z-index: 50;
170
+
171
+ .header_content {
172
+ display: flex;
173
+ justify-content: space-between;
174
+ align-items: center;
175
+ height: 56px;
176
+
177
+ .main_title {
178
+ font-size: 1.25rem;
179
+ font-weight: 600;
180
+ color: #333;
181
+ margin: 0;
140
182
  }
141
- .user_content {
142
- padding: 0 35px 0 35px;
143
- color: #fff;
144
- position: relative;
145
- z-index: 4;
146
- .default_user_profile {
147
- border-radius: 50%;
148
- width: 76px;
149
- height: 76px;
150
- box-shadow:
151
- 0 10px 20px rgba(0, 0, 0, 0.05),
152
- 0 6px 6px rgba(0, 0, 0, 0.05);
153
- }
154
- .van-row {
155
- flex-wrap: nowrap;
156
- align-items: center;
157
- .user_info_col {
158
- flex-grow: 1;
159
- p {
160
- margin: 0;
161
- }
162
- .user_nickname {
163
- font-size: 20px;
164
- font-weight: bold;
165
- margin-bottom: 4px;
166
- }
167
- .user_remark {
168
- font-size: 14px;
169
- }
170
- }
183
+
184
+ .setting_section {
185
+ padding: 8px;
186
+ cursor: pointer;
187
+
188
+ .setting-icon {
189
+ font-size: 20px;
190
+ color: #666;
171
191
  }
172
192
  }
173
193
  }
174
194
  }
195
+
175
196
  .content {
176
- padding: var(--base-interval-1);
177
- .core {
178
- position: relative;
179
- bottom: 50px;
180
- padding: 16px 0;
197
+ padding: 16px;
198
+
199
+ // 用户信息卡片
200
+ .user-card {
181
201
  background-color: #fff;
182
- z-index: 4;
183
- margin: 0 16px;
184
- border-radius: 4px;
185
- box-shadow:
186
- 0 10px 20px rgba(0, 0, 0, 0.05),
187
- 0 6px 6px rgba(0, 0, 0, 0.05);
188
- .core_row {
189
- text-align: center;
190
- .van-col {
191
- flex-grow: 1;
192
- p {
193
- margin: 8px 0;
202
+ border-radius: 8px;
203
+ padding: 16px;
204
+ margin-bottom: 16px;
205
+
206
+ .user-info {
207
+ display: flex;
208
+ align-items: center;
209
+
210
+ .avatar-wrap {
211
+ position: relative;
212
+ margin-right: 16px;
213
+
214
+ .avatar {
215
+ width: 80px;
216
+ height: 80px;
217
+ border-radius: 50%;
218
+ object-fit: cover;
194
219
  }
195
- .core_value {
220
+
221
+ .status-dot {
222
+ position: absolute;
223
+ bottom: 2px;
224
+ right: 2px;
225
+ width: 16px;
226
+ height: 16px;
227
+ border-radius: 50%;
228
+ background-color: #10b981;
229
+ border: 2px solid #fff;
230
+ }
231
+ }
232
+
233
+ .user-detail {
234
+ .username {
196
235
  font-size: 20px;
197
- font-weight: bold;
236
+ font-weight: 600;
237
+ color: #333;
238
+ margin: 0 0 4px 0;
198
239
  }
199
- .core_title {
240
+
241
+ .user-role {
200
242
  font-size: 14px;
201
- color: #999;
243
+ color: #666;
244
+ margin: 0;
202
245
  }
203
246
  }
204
247
  }
205
248
  }
206
- .setting {
207
- .setting_row {
208
- align-items: center;
209
- .setting_icon {
210
- width: 28px;
211
- height: 28px;
212
- vertical-align: middle;
213
- }
214
- .setting_name_col {
215
- flex-grow: 1;
216
- .setting_name_text {
217
- font-size: 16px;
218
- font-weight: bold;
249
+
250
+ // 工作统计卡片
251
+ .stats-card {
252
+ background-color: #fff;
253
+ border-radius: 8px;
254
+ padding: 16px;
255
+ margin-bottom: 16px;
256
+
257
+ .stats-grid {
258
+ display: grid;
259
+ grid-template-columns: repeat(3, 1fr);
260
+ gap: 16px;
261
+ text-align: center;
262
+
263
+ .stat-item {
264
+ .stat-value {
265
+ font-size: 24px;
266
+ font-weight: 600;
267
+ color: #3b82f6;
268
+ margin-bottom: 4px;
269
+ }
270
+
271
+ .stat-label {
272
+ font-size: 14px;
273
+ color: #666;
219
274
  }
220
275
  }
221
276
  }
222
- .van-cell {
223
- align-items: center;
224
- .setting_arrow_icon {
225
- width: 12px;
226
- height: 12px;
227
- vertical-align: middle;
277
+ }
278
+
279
+ // 功能列表卡片
280
+ .menu-card {
281
+ background-color: #fff;
282
+ border-radius: 8px;
283
+ overflow: hidden;
284
+
285
+ .menu-section {
286
+ padding: 16px;
287
+
288
+ .menu-title {
289
+ font-size: 14px;
290
+ font-weight: 500;
291
+ color: #666;
292
+ margin-bottom: 12px;
293
+ }
294
+
295
+ .menu-list {
296
+ .menu-item {
297
+ display: flex;
298
+ align-items: center;
299
+ justify-content: space-between;
300
+ padding: 12px;
301
+ background-color: #f7f8fa;
302
+ border-radius: 8px;
303
+ margin-bottom: 8px;
304
+ cursor: pointer;
305
+
306
+ &:last-child {
307
+ margin-bottom: 0;
308
+ }
309
+
310
+ .menu-item-content {
311
+ display: flex;
312
+ align-items: center;
313
+ gap: 12px;
314
+
315
+ .menu-icon {
316
+ font-size: 24px;
317
+ color: #3b82f6;
318
+
319
+ &.logout-icon {
320
+ color: #ef4444;
321
+ }
322
+ }
323
+
324
+ .menu-text {
325
+ font-size: 16px;
326
+ color: #333;
327
+ }
328
+ }
329
+
330
+ .menu-arrow {
331
+ color: #999;
332
+ font-size: 16px;
333
+ }
334
+ }
228
335
  }
229
336
  }
230
337
  }