Commit e2e5221c9f8b6256a09ff5b14d93aff79e0a0bf5

Authored by 刘淇
1 parent 83673de2

验证码登录

api/user.js
@@ -11,6 +11,14 @@ export const login = (params) => { @@ -11,6 +11,14 @@ export const login = (params) => {
11 }; 11 };
12 12
13 /** 13 /**
  14 + * 手机号验证码登录
  15 + * @returns {Promise}
  16 + */
  17 +export const smsLogin = (data) => {
  18 + return post('/admin-api/system/auth/sms-login',data)
  19 +}
  20 +
  21 +/**
14 * 获取用户信息 22 * 获取用户信息
15 * @returns {Promise} 23 * @returns {Promise}
16 */ 24 */
@@ -68,6 +76,15 @@ export const getUnreadCount = () => { @@ -68,6 +76,15 @@ export const getUnreadCount = () => {
68 } 76 }
69 77
70 78
  79 +/**
  80 + * 获取验证码
  81 + * @returns {Promise}
  82 + */
  83 +export const getSmsCode = (data) => {
  84 + return post('/admin-api/system/auth/send-sms-code',data)
  85 +}
  86 +
  87 +
71 88
72 89
73 90
manifest.json
@@ -54,7 +54,7 @@ @@ -54,7 +54,7 @@
54 }, 54 },
55 /* 小程序特有相关 */ 55 /* 小程序特有相关 */
56 "mp-weixin" : { 56 "mp-weixin" : {
57 - "appid" : "wxcb4cd34066b97d82", 57 + "appid" : "wx64368a9b9e799172",
58 "setting" : { 58 "setting" : {
59 "urlCheck" : false 59 "urlCheck" : false
60 }, 60 },
pages/index/index.vue
@@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
4 <view class="user-info-bar"> 4 <view class="user-info-bar">
5 <view class="user-info"> 5 <view class="user-info">
6 <view class="user-text"> 6 <view class="user-text">
7 - <view class="username">你好{{ userName }},欢迎登录</view>  
8 - <view class="login-desc">蓟城山水智慧园林养护平台</view> 7 + <view class="username">你好{{ userName }}</view>
  8 + <view class="login-desc">蓟城山水全域智能运营管理平台</view>
9 </view> 9 </view>
10 </view> 10 </view>
11 <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover"> 11 <view class="msg-icon" @click="handleMsgClick" hover-class="msg-icon--hover">
pages/login/index.vue
@@ -2,29 +2,20 @@ @@ -2,29 +2,20 @@
2 <view class="page-container"> 2 <view class="page-container">
3 <!-- 顶部标题区 --> 3 <!-- 顶部标题区 -->
4 <view class="top-title"> 4 <view class="top-title">
5 - <text class="welcome-text">你好,欢迎光临</text>  
6 -<!-- <text class="platform-name">全域智能运营管理平台</text>-->  
7 - <text class="platform-name">蓟城山水智慧园林养护平台</text>  
8 - 5 + <text class="welcome-text">你好,欢迎登录</text>
  6 + <text class="platform-name">蓟城山水全域智能运营管理平台</text>
9 </view> 7 </view>
10 8
11 <!-- 登录表单区域 --> 9 <!-- 登录表单区域 -->
12 <view class="login-form"> 10 <view class="login-form">
13 - <!-- uview-plus的Tabs组件 --> 11 + <!-- 登录方式切换 -->
14 <up-tabs 12 <up-tabs
15 :list="tabList" 13 :list="tabList"
16 line-width="55" 14 line-width="55"
17 line-height="2" 15 line-height="2"
18 @change="handleTabChange" 16 @change="handleTabChange"
19 - :activeStyle="{  
20 - color: '#3c9cff',  
21 - fontWeight:'500',  
22 - fontSize: '14px' // 补充单位,避免样式异常  
23 - }"  
24 - :inactiveStyle="{  
25 - color: '#606060',  
26 - fontSize: '14px' // 补充单位,避免样式异常  
27 - }" 17 + :activeStyle="{ color: '#3c9cff', fontWeight:'500', fontSize: '14px' }"
  18 + :inactiveStyle="{ color: '#606060', fontSize: '14px' }"
28 ></up-tabs> 19 ></up-tabs>
29 20
30 <!-- 表单校验容器 --> 21 <!-- 表单校验容器 -->
@@ -34,71 +25,100 @@ @@ -34,71 +25,100 @@
34 ref="loginFormRef" 25 ref="loginFormRef"
35 labelWidth="0" 26 labelWidth="0"
36 > 27 >
37 - <!-- 手机号输入框 -->  
38 - <up-form-item v-if="loginType === '手机号登录'" prop="mobile">  
39 - <up-input  
40 - v-model="form.mobile"  
41 - border="surround"  
42 - clearable  
43 - maxlength="11"  
44 - input-align="left"  
45 - fontSize="14px"  
46 - :disabled="isLoading"  
47 - shape="circle"  
48 - placeholder="请输入手机号"  
49 - @blur="() => loginFormRef.validateField('mobile')"  
50 - />  
51 - </up-form-item>  
52 -  
53 - <!-- 账号输入框 -->  
54 - <up-form-item v-else prop="account">  
55 - <up-input  
56 - v-model="form.account"  
57 - border="surround"  
58 - clearable  
59 - maxlength="30"  
60 - input-align="left"  
61 - fontSize="14px"  
62 - :disabled="isLoading"  
63 - shape="circle"  
64 - placeholder="请输入账户"  
65 - @blur="() => loginFormRef.validateField('account')"  
66 - />  
67 - </up-form-item>  
68 -  
69 - <!-- 密码输入框 -->  
70 - <up-form-item  
71 - prop="password"  
72 - class="password-item"  
73 - >  
74 - <up-input  
75 - v-model="form.password"  
76 - placeholder="请输入密码"  
77 - maxlength="20"  
78 - border="surround"  
79 - clearable  
80 - input-align="left"  
81 - type="password"  
82 - :disabled="isLoading"  
83 - shape="circle"  
84 - @blur="() => loginFormRef.validateField('password')"  
85 - />  
86 - </up-form-item>  
87 -  
88 - <!-- 记住密码单独一行,居右显示 -->  
89 - <view class="remember-wrap">  
90 - <up-checkbox  
91 - :customStyle="{marginBottom: '8px'}"  
92 - label="记住密码"  
93 - name="agree"  
94 - size="14"  
95 - iconSize="14"  
96 - labelSize="14"  
97 - labelColor="'#3F3F3F'"  
98 - usedAlone  
99 - v-model:checked="rememberPwd"  
100 - >  
101 - </up-checkbox> 28 + <!-- ========== 手机号登录区域 ========== -->
  29 + <view v-if="loginType === '手机号登录'">
  30 + <!-- 手机号输入框 -->
  31 + <up-form-item prop="mobile">
  32 + <up-input
  33 + v-model="form.mobile"
  34 + border="surround"
  35 + maxlength="11"
  36 + input-align="left"
  37 + fontSize="14px"
  38 + :disabled="isLoading"
  39 + shape="circle"
  40 + placeholder="请输入手机号"
  41 + @blur="() => loginFormRef.validateField('mobile')"
  42 + />
  43 + </up-form-item>
  44 +
  45 + <!-- 验证码输入框 + 自定义倒计时按钮 -->
  46 + <up-form-item prop="code">
  47 + <up-input
  48 + v-model="form.code"
  49 + border="surround"
  50 + maxlength="4"
  51 + type="number"
  52 + input-align="left"
  53 + fontSize="14px"
  54 + :disabled="isLoading"
  55 + shape="circle"
  56 + placeholder="请输入验证码"
  57 + @blur="() => loginFormRef.validateField('code')"
  58 + >
  59 + <template #suffix>
  60 + <up-button
  61 + size="mini"
  62 + type="primary"
  63 + :disabled="isCodeBtnDisabled"
  64 + :loading="isCodeLoading"
  65 + @tap="getVerificationCode"
  66 + shape="circle"
  67 + >
  68 + {{ countdown > 0 ? `${countdown}秒后重新获取` : '获取验证码' }}
  69 + </up-button>
  70 + </template>
  71 + </up-input>
  72 + </up-form-item>
  73 + </view>
  74 +
  75 + <!-- ========== 账号登录区域 ========== -->
  76 + <view v-else>
  77 + <!-- 账号输入框 -->
  78 + <up-form-item prop="account">
  79 + <up-input
  80 + v-model="form.account"
  81 + border="surround"
  82 + maxlength="30"
  83 + input-align="left"
  84 + fontSize="14px"
  85 + :disabled="isLoading"
  86 + shape="circle"
  87 + placeholder="请输入账户"
  88 + @blur="() => loginFormRef.validateField('account')"
  89 + />
  90 + </up-form-item>
  91 +
  92 + <!-- 密码输入框 -->
  93 + <up-form-item prop="password" class="password-item">
  94 + <up-input
  95 + v-model="form.password"
  96 + placeholder="请输入密码"
  97 + maxlength="20"
  98 + border="surround"
  99 + input-align="left"
  100 + type="password"
  101 + :disabled="isLoading"
  102 + shape="circle"
  103 + @blur="() => loginFormRef.validateField('password')"
  104 + />
  105 + </up-form-item>
  106 +
  107 + <!-- 记住密码 -->
  108 + <view class="remember-wrap">
  109 + <up-checkbox
  110 + :customStyle="{marginBottom: '8px'}"
  111 + label="记住密码"
  112 + name="agree"
  113 + size="14"
  114 + iconSize="14"
  115 + labelSize="14"
  116 + labelColor="#3F3F3F"
  117 + usedAlone
  118 + v-model:checked="rememberPwd"
  119 + >
  120 + </up-checkbox>
  121 + </view>
102 </view> 122 </view>
103 </up-form> 123 </up-form>
104 124
@@ -121,10 +141,12 @@ @@ -121,10 +141,12 @@
121 </template> 141 </template>
122 142
123 <script setup> 143 <script setup>
124 -import { ref, reactive, onMounted, nextTick } from 'vue'; 144 +import { ref, reactive, onMounted, nextTick, computed, onUnmounted} from 'vue';
  145 +import {onShow} from '@dcloudio/uni-app'
125 import { useUserStore } from '@/pinia/user'; 146 import { useUserStore } from '@/pinia/user';
126 import globalConfig from '@/common/config/global'; 147 import globalConfig from '@/common/config/global';
127 import CryptoJS from 'crypto-js'; 148 import CryptoJS from 'crypto-js';
  149 +import { getSmsCode } from "@/api/user";
128 150
129 // ========== 加密工具函数 ========== 151 // ========== 加密工具函数 ==========
130 const CRYPTO_CONFIG = { 152 const CRYPTO_CONFIG = {
@@ -163,26 +185,101 @@ const aesDecrypt = (encryptedText) =&gt; { @@ -163,26 +185,101 @@ const aesDecrypt = (encryptedText) =&gt; {
163 } 185 }
164 }; 186 };
165 187
166 -// ========== 业务逻辑 ========== 188 +// ========== 核心业务逻辑 ==========
167 const userStore = useUserStore(); 189 const userStore = useUserStore();
168 const loginFormRef = ref(null); 190 const loginFormRef = ref(null);
169 const isLoading = ref(false); 191 const isLoading = ref(false);
  192 +const isCodeLoading = ref(false); // 验证码按钮独立loading状态
170 193
171 -// Tabs配置  
172 -const tabList = ref([  
173 - { name: '手机号登录' },  
174 - { name: '账号登录' }  
175 -]); 194 +// 登录方式切换
  195 +const tabList = ref([{ name: '手机号登录' }, { name: '账号登录' }]);
176 const loginType = ref('手机号登录'); 196 const loginType = ref('手机号登录');
177 197
178 -// 记住密码 198 +// 记住密码(仅账号登录生效)
179 const rememberPwd = ref(true); 199 const rememberPwd = ref(true);
180 200
181 // 表单数据 201 // 表单数据
182 const form = reactive({ 202 const form = reactive({
183 account: '', // 账号 203 account: '', // 账号
184 mobile: '', // 手机号 204 mobile: '', // 手机号
185 - password: '' // 密码 205 + password: '', // 密码
  206 + code: '' // 验证码
  207 +});
  208 +
  209 +// 验证码倒计时相关
  210 +const countdown = ref(0);
  211 +let countdownTimer = null; // 倒计时定时器
  212 +
  213 +// 临时缓存KEY
  214 +const TEMP_FORM_KEY = 'login_temp_form_data';
  215 +
  216 +// ========== 方案二核心:保存/恢复表单数据 ==========
  217 +// 保存表单数据到本地缓存
  218 +const saveFormData = () => {
  219 + try {
  220 + const tempData = {
  221 + form: { ...form },
  222 + loginType: loginType.value,
  223 + countdown: countdown.value,
  224 + rememberPwd: rememberPwd.value
  225 + };
  226 + // 加密存储,提升安全性
  227 + const encryptData = aesEncrypt(JSON.stringify(tempData));
  228 + uni.setStorageSync(TEMP_FORM_KEY, encryptData);
  229 + } catch (err) {
  230 + console.warn('保存表单数据失败:', err);
  231 + }
  232 +};
  233 +
  234 +// 从本地缓存恢复表单数据
  235 +const restoreFormData = () => {
  236 + try {
  237 + const encryptData = uni.getStorageSync(TEMP_FORM_KEY);
  238 + if (!encryptData) return;
  239 +
  240 + const tempData = JSON.parse(aesDecrypt(encryptData));
  241 + // 恢复表单数据
  242 + if (tempData.form) {
  243 + Object.assign(form, tempData.form);
  244 + }
  245 + // 恢复登录类型
  246 + // if (tempData.loginType) {
  247 + // loginType.value = tempData.loginType;
  248 + // }
  249 + // 恢复记住密码状态
  250 + if (tempData.rememberPwd !== undefined) {
  251 + rememberPwd.value = tempData.rememberPwd;
  252 + }
  253 + // 恢复倒计时
  254 + if (tempData.countdown && tempData.countdown > 0) {
  255 + countdown.value = tempData.countdown;
  256 + // 重启倒计时定时器
  257 + startCountdown(countdown.value);
  258 + }
  259 +
  260 + // 恢复记住密码的缓存数据(原有逻辑)
  261 + if (rememberPwd.value && loginType.value === '账号登录') {
  262 + const savedAccount = aesDecrypt(uni.getStorageSync('login_account') || '');
  263 + const savedPwd = aesDecrypt(uni.getStorageSync('login_password') || '');
  264 + if (savedAccount) {
  265 + form.account = savedAccount;
  266 + form.password = savedPwd;
  267 + }
  268 + }
  269 + } catch (err) {
  270 + console.warn('恢复表单数据失败:', err);
  271 + }
  272 +};
  273 +
  274 +// 校验手机号格式
  275 +const isMobileValid = computed(() => {
  276 + const mobileReg = /^1[3-9]\d{9}$/;
  277 + return mobileReg.test(form.mobile);
  278 +});
  279 +
  280 +// 统一计算验证码按钮禁用状态
  281 +const isCodeBtnDisabled = computed(() => {
  282 + return countdown.value > 0 || isLoading.value || isCodeLoading.value || !isMobileValid.value;
186 }); 283 });
187 284
188 // 表单校验规则 285 // 表单校验规则
@@ -198,20 +295,81 @@ const loginFormRules = reactive({ @@ -198,20 +295,81 @@ const loginFormRules = reactive({
198 password: [ 295 password: [
199 { type: 'string', required: true, message: '请输入登录密码', trigger: ['change', 'blur'] }, 296 { type: 'string', required: true, message: '请输入登录密码', trigger: ['change', 'blur'] },
200 { type: 'string', min: 3, max: 20, message: '密码长度为3-20个字符', trigger: ['change', 'blur'] } 297 { type: 'string', min: 3, max: 20, message: '密码长度为3-20个字符', trigger: ['change', 'blur'] }
  298 + ],
  299 + code: [
  300 + { type: 'string', required: true, message: '请输入验证码', trigger: ['change', 'blur'] },
  301 + { type: 'string', len: 4, message: '验证码长度4位', trigger: ['change', 'blur'] }
201 ] 302 ]
202 }); 303 });
203 304
204 -// Tabs切换事件:移除清空输入框的逻辑 305 +// 切换登录方式
205 const handleTabChange = ({ name }) => { 306 const handleTabChange = ({ name }) => {
206 if (isLoading.value) return; 307 if (isLoading.value) return;
207 - console.log('切换到:', name); 308 +
208 loginType.value = name; 309 loginType.value = name;
209 - // 只清空校验状态,不清除输入框内容 310 + // 清空校验状态和无关输入框
210 nextTick(() => { 311 nextTick(() => {
211 loginFormRef.value?.clearValidate(); 312 loginFormRef.value?.clearValidate();
212 }); 313 });
213 }; 314 };
214 315
  316 +// 开始验证码倒计时
  317 +const startCountdown = (seconds = 60) => {
  318 + countdown.value = seconds;
  319 +
  320 + // 清除已有定时器,避免重复
  321 + if (countdownTimer) clearInterval(countdownTimer);
  322 +
  323 + countdownTimer = setInterval(() => {
  324 + if (countdown.value <= 0) {
  325 + clearCountdown();
  326 + return;
  327 + }
  328 + countdown.value--;
  329 + }, 1000);
  330 +};
  331 +
  332 +// 清除倒计时
  333 +const clearCountdown = () => {
  334 + countdown.value = 0;
  335 + if (countdownTimer) {
  336 + clearInterval(countdownTimer);
  337 + countdownTimer = null;
  338 + }
  339 +};
  340 +
  341 +// 获取验证码
  342 +const getVerificationCode = async () => {
  343 + // 双重校验:按钮禁用状态
  344 + if (isCodeBtnDisabled.value) {
  345 + console.warn('验证码按钮已禁用,不执行请求');
  346 + return;
  347 + }
  348 +
  349 + try {
  350 + isCodeLoading.value = true; // 按钮进入loading
  351 + uni.showLoading({ title: '发送验证码中...', mask: true }); // 遮罩防止重复点击
  352 +
  353 + const postData = {
  354 + mobile: form.mobile,
  355 + scene: 21
  356 + };
  357 + // 调用验证码接口
  358 + const res = await getSmsCode(postData);
  359 +
  360 + uni.hideLoading();
  361 + uni.$u.toast('验证码已发送,请注意查收');
  362 + startCountdown(); // 仅接口成功时开始倒计时
  363 + } catch (err) {
  364 + // 捕获接口异常
  365 + uni.hideLoading();
  366 + console.error('获取验证码失败:', err);
  367 + uni.$u.toast(err.msg || '发送验证码失败,请重试');
  368 + } finally {
  369 + isCodeLoading.value = false; // 结束loading状态
  370 + }
  371 +};
  372 +
215 // 检查登录状态 373 // 检查登录状态
216 const checkLoginStatus = () => { 374 const checkLoginStatus = () => {
217 try { 375 try {
@@ -226,45 +384,56 @@ const checkLoginStatus = () =&gt; { @@ -226,45 +384,56 @@ const checkLoginStatus = () =&gt; {
226 } 384 }
227 }; 385 };
228 386
229 -// 登录方法 387 +// 登录处理
230 const handleLogin = async () => { 388 const handleLogin = async () => {
231 try { 389 try {
232 - await loginFormRef.value.validate(); 390 + // 根据登录类型校验对应字段
  391 + const validateFields = loginType.value === '手机号登录'
  392 + ? ['mobile', 'code']
  393 + : ['account', 'password'];
  394 +
  395 + // 执行表单校验
  396 + const validateRes = await loginFormRef.value.validate(validateFields);
  397 + if (!validateRes) {
  398 + return;
  399 + }
  400 +
233 isLoading.value = true; 401 isLoading.value = true;
234 402
235 // 组装登录参数 403 // 组装登录参数
236 - const loginParams = { password: form.password }; 404 + const loginParams = {};
237 if (loginType.value === '手机号登录') { 405 if (loginType.value === '手机号登录') {
238 - loginParams.username = form.mobile; 406 + loginParams.mobile = form.mobile;
  407 + loginParams.code = form.code;
  408 + loginParams.type = 'sms';
239 } else { 409 } else {
240 loginParams.username = form.account; 410 loginParams.username = form.account;
  411 + loginParams.password = form.password;
  412 + loginParams.type = 'password';
241 } 413 }
242 414
243 // 执行登录 415 // 执行登录
244 await userStore.login(loginParams); 416 await userStore.login(loginParams);
245 417
246 - // 保存数据:账号/密码 均用AES加密  
247 - if (rememberPwd.value) { 418 + // 保存登录信息(仅账号登录且勾选记住密码)
  419 + if (loginType.value === '账号登录' && rememberPwd.value) {
248 try { 420 try {
249 - console.log('123+'+loginType.value)  
250 - if (loginType.value === '手机号登录') {  
251 - uni.setStorageSync('login_mobile', aesEncrypt(form.mobile));  
252 - uni.removeStorageSync('login_account'); // 确保只存一种类型  
253 - } else {  
254 - uni.setStorageSync('login_account', aesEncrypt(form.account));  
255 - uni.removeStorageSync('login_mobile'); // 确保只存一种类型  
256 - } 421 + uni.setStorageSync('login_account', aesEncrypt(form.account));
257 uni.setStorageSync('login_password', aesEncrypt(form.password)); 422 uni.setStorageSync('login_password', aesEncrypt(form.password));
  423 + uni.removeStorageSync('login_mobile');
258 } catch (err) { 424 } catch (err) {
259 console.warn('保存登录信息失败:', err); 425 console.warn('保存登录信息失败:', err);
260 } 426 }
261 } else { 427 } else {
262 - // 清除所有缓存 428 + // 清除缓存
263 uni.removeStorageSync('login_account'); 429 uni.removeStorageSync('login_account');
264 uni.removeStorageSync('login_mobile'); 430 uni.removeStorageSync('login_mobile');
265 uni.removeStorageSync('login_password'); 431 uni.removeStorageSync('login_password');
266 } 432 }
267 433
  434 + // 登录成功后清除临时表单缓存
  435 + uni.removeStorageSync(TEMP_FORM_KEY);
  436 +
268 // 登录成功跳转 437 // 登录成功跳转
269 uni.showToast({ title: '登录成功', icon: 'success', duration: 1000 }); 438 uni.showToast({ title: '登录成功', icon: 'success', duration: 1000 });
270 setTimeout(() => { 439 setTimeout(() => {
@@ -274,58 +443,50 @@ const handleLogin = async () =&gt; { @@ -274,58 +443,50 @@ const handleLogin = async () =&gt; {
274 }); 443 });
275 }, 1000); 444 }, 1000);
276 } catch (err) { 445 } catch (err) {
277 - if (!Array.isArray(err)) {  
278 - console.error('登录失败:', err);  
279 - uni.showToast({  
280 - title: err.msg || '账号或密码错误,请重试',  
281 - icon: 'none',  
282 - duration: 1000  
283 - });  
284 - } 446 + console.error('登录失败:', err);
  447 + uni.showToast({
  448 + title: err.msg || (loginType.value === '手机号登录' ? '验证码错误' : '账号或密码错误'),
  449 + icon: 'none',
  450 + duration: 1000
  451 + });
285 } finally { 452 } finally {
286 isLoading.value = false; 453 isLoading.value = false;
287 } 454 }
288 }; 455 };
289 456
290 -// 生命周期 457 +// ========== 小程序生命周期适配(方案二核心) ==========
  458 +// 页面显示时(切回页面)恢复数据
  459 +onShow(() => {
  460 + restoreFormData();
  461 +});
  462 +
  463 +// 页面卸载时保存数据
  464 +onUnmounted(() => {
  465 + clearCountdown();
  466 + saveFormData();
  467 +});
  468 +
  469 +// 页面挂载时初始化
291 onMounted(() => { 470 onMounted(() => {
292 checkLoginStatus(); 471 checkLoginStatus();
  472 +
293 // 初始化表单规则 473 // 初始化表单规则
294 nextTick(() => { 474 nextTick(() => {
295 loginFormRef.value?.setRules(loginFormRules); 475 loginFormRef.value?.setRules(loginFormRules);
  476 + // 首次挂载也恢复一次数据
  477 + restoreFormData();
296 }); 478 });
297 - // 读取缓存并解密填充:修复逻辑漏洞,确保只填充当前登录类型的输入框  
298 - if (rememberPwd.value) {  
299 - try {  
300 - const savedAccount = aesDecrypt(uni.getStorageSync('login_account') || '');  
301 - const savedMobile = aesDecrypt(uni.getStorageSync('login_mobile') || '');  
302 - const savedPwd = aesDecrypt(uni.getStorageSync('login_password') || '');  
303 -  
304 - // 核心修正:只填充对应类型的输入框,另一类置空  
305 - if (savedAccount) {  
306 - form.account = savedAccount;  
307 - form.mobile = ''; // 清空手机号输入框  
308 - // loginType.value = '账号登录';  
309 - } else if (savedMobile) {  
310 - form.mobile = savedMobile;  
311 - form.account = ''; // 清空账号输入框  
312 - // loginType.value = '手机号登录';  
313 - }  
314 - form.password = savedPwd;  
315 - } catch (err) {  
316 - console.warn('读取缓存失败:', err);  
317 - }  
318 - }  
319 }); 479 });
320 </script> 480 </script>
321 481
322 <style scoped lang="scss"> 482 <style scoped lang="scss">
323 -.page-container{  
324 - height:100vh; 483 +.page-container {
  484 + height: 100vh;
325 background: url("https://img.jichengshanshui.com.cn:28207/appimg/loginbg.jpg") no-repeat; 485 background: url("https://img.jichengshanshui.com.cn:28207/appimg/loginbg.jpg") no-repeat;
326 background-position: bottom center; 486 background-position: bottom center;
327 background-size: 100% 240px; 487 background-size: 100% 240px;
328 } 488 }
  489 +
329 .top-title { 490 .top-title {
330 background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; 491 background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat;
331 background-size: 100% 100%; 492 background-size: 100% 100%;
@@ -361,9 +522,11 @@ onMounted(() =&gt; { @@ -361,9 +522,11 @@ onMounted(() =&gt; {
361 522
362 :deep(.u-tabs) { 523 :deep(.u-tabs) {
363 margin-bottom: 15px; 524 margin-bottom: 15px;
  525 +
364 .u-tabs__content { 526 .u-tabs__content {
365 height: auto !important; 527 height: auto !important;
366 } 528 }
  529 +
367 .u-tab-item { 530 .u-tab-item {
368 padding: 0 10px; 531 padding: 0 10px;
369 } 532 }
@@ -415,4 +578,15 @@ onMounted(() =&gt; { @@ -415,4 +578,15 @@ onMounted(() =&gt; {
415 position: fixed; 578 position: fixed;
416 bottom: 60px; 579 bottom: 60px;
417 } 580 }
  581 +
  582 +// 验证码按钮样式优化
  583 +:deep(.u-input__suffix) {
  584 + padding-right: 10rpx;
  585 +}
  586 +
  587 +:deep(.u-button--mini) {
  588 + height: 60rpx;
  589 + line-height: 60rpx;
  590 + padding: 0 15rpx;
  591 +}
418 </style> 592 </style>
419 \ No newline at end of file 593 \ No newline at end of file
pages/workbench/index.vue
@@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
10 10
11 <!-- 蓝色装饰块 --> 11 <!-- 蓝色装饰块 -->
12 <view class="blue-decor-block" v-show="!loading"> 12 <view class="blue-decor-block" v-show="!loading">
13 - <text class="welcome-text u-line-1">你好{{ userInfo?.user?.nickname || '' }},欢迎登录</text>  
14 - <text class="platform-name">蓟城山水智慧园林养护平台</text> 13 + <text class="welcome-text u-line-1">你好{{ userInfo?.user?.nickname || '' }}</text>
  14 + <text class="platform-name">蓟城山水全域智能运营管理平台</text>
15 </view> 15 </view>
16 16
17 <!-- 内容容器 --> 17 <!-- 内容容器 -->
pinia/user.js
@@ -2,7 +2,7 @@ import { defineStore } from &#39;pinia&#39;; @@ -2,7 +2,7 @@ import { defineStore } from &#39;pinia&#39;;
2 import cache from '@/common/utils/cache'; 2 import cache from '@/common/utils/cache';
3 import globalConfig from '@/common/config/global'; 3 import globalConfig from '@/common/config/global';
4 // 新增:导入 refreshToken 接口 4 // 新增:导入 refreshToken 接口
5 -import { login, getUserInfo, logout, moduleList, getSimpleDictDataList, refreshToken } from '@/api/user'; 5 +import { login,smsLogin, getUserInfo, logout, moduleList, getSimpleDictDataList, refreshToken } from '@/api/user';
6 6
7 // 新增:定义刷新Token的定时器标识(避免重复创建定时器) 7 // 新增:定义刷新Token的定时器标识(避免重复创建定时器)
8 let refreshTokenTimer = null; 8 let refreshTokenTimer = null;
@@ -41,7 +41,14 @@ export const useUserStore = defineStore(&#39;user&#39;, { @@ -41,7 +41,14 @@ export const useUserStore = defineStore(&#39;user&#39;, {
41 actions: { 41 actions: {
42 async login(params) { 42 async login(params) {
43 try { 43 try {
44 - const res = await login(params); 44 + // smsLogin
  45 + let res
  46 + if(params.type=='sms'){
  47 + res = await smsLogin(params);
  48 + }else{
  49 + res = await login(params);
  50 + }
  51 +
45 // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token) 52 // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token)
46 const { accessToken, expiresTime, userId, refreshToken } = res; 53 const { accessToken, expiresTime, userId, refreshToken } = res;
47 54