Commit 24c0bbe72df0d1e9611993ade850bf97b0e1f495
1 parent
53012a16
登录账号密码 加密存储
Showing
10 changed files
with
141 additions
and
106 deletions
package-lock.json
| @@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
| 6 | "": { | 6 | "": { |
| 7 | "dependencies": { | 7 | "dependencies": { |
| 8 | "clipboard": "^2.0.11", | 8 | "clipboard": "^2.0.11", |
| 9 | + "crypto-js": "^4.2.0", | ||
| 9 | "dayjs": "^1.11.19", | 10 | "dayjs": "^1.11.19", |
| 10 | "pinia-plugin-persistedstate": "^4.7.1" | 11 | "pinia-plugin-persistedstate": "^4.7.1" |
| 11 | }, | 12 | }, |
| @@ -85,6 +86,12 @@ | @@ -85,6 +86,12 @@ | ||
| 85 | "dev": true, | 86 | "dev": true, |
| 86 | "license": "MIT" | 87 | "license": "MIT" |
| 87 | }, | 88 | }, |
| 89 | + "node_modules/crypto-js": { | ||
| 90 | + "version": "4.2.0", | ||
| 91 | + "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz", | ||
| 92 | + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", | ||
| 93 | + "license": "MIT" | ||
| 94 | + }, | ||
| 88 | "node_modules/dayjs": { | 95 | "node_modules/dayjs": { |
| 89 | "version": "1.11.19", | 96 | "version": "1.11.19", |
| 90 | "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", | 97 | "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", |
package.json
pages-sub/daily/maintain-manage/road-detail-list.vue
| @@ -54,7 +54,7 @@ | @@ -54,7 +54,7 @@ | ||
| 54 | <!-- 自定义标题区域 --> | 54 | <!-- 自定义标题区域 --> |
| 55 | <template #head> | 55 | <template #head> |
| 56 | <view class="card-header"> | 56 | <view class="card-header"> |
| 57 | - <view class="common-card-title common-name">{{ item.planName || '无计划名称' }}{{ item.planName || '无计划名称' }}</view> | 57 | + <view class="common-card-title common-name">{{ item.planName || '无计划名称' }}</view> |
| 58 | <!-- 已失效标识 --> | 58 | <!-- 已失效标识 --> |
| 59 | <view v-show="item.finishState == 3 " class="common-invalid-tag">已失效</view> | 59 | <view v-show="item.finishState == 3 " class="common-invalid-tag">已失效</view> |
| 60 | <view v-show="item.finishState == 2 " class="common-finish-tag">已完成</view> | 60 | <view v-show="item.finishState == 2 " class="common-finish-tag">已完成</view> |
pages-sub/data/tree-archive/addTree.vue
| 1 | <template> | 1 | <template> |
| 2 | - <view class="container"> | 2 | + <view class="page-container"> |
| 3 | <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom> | 3 | <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom> |
| 4 | <up-form-item label="名称" prop="treetype" required> | 4 | <up-form-item label="名称" prop="treetype" required> |
| 5 | <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/> | 5 | <up-input v-model.trim="formData.treetype" placeholder="请输入名称" maxlength="30" border="none"/> |
| @@ -452,7 +452,7 @@ const submit = async () => { | @@ -452,7 +452,7 @@ const submit = async () => { | ||
| 452 | </script> | 452 | </script> |
| 453 | 453 | ||
| 454 | <style scoped lang="scss"> | 454 | <style scoped lang="scss"> |
| 455 | -.container { | 455 | +.page-container { |
| 456 | padding: 15px; | 456 | padding: 15px; |
| 457 | box-sizing: border-box; | 457 | box-sizing: border-box; |
| 458 | background: #fff; | 458 | background: #fff; |
pages-sub/data/tree-archive/logDetail.vue
| 1 | <template> | 1 | <template> |
| 2 | - <view class="container"> | 2 | + <view class="page-container"> |
| 3 | <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom> | 3 | <up-form :model="formData" ref="formRef" label-width="140rpx" border-bottom> |
| 4 | <up-form-item label="名称" prop="treetype" required> | 4 | <up-form-item label="名称" prop="treetype" required> |
| 5 | <up-input v-model="formData.treetype" placeholder="请输入名称" maxlength="30" border="none" readonly/> | 5 | <up-input v-model="formData.treetype" placeholder="请输入名称" maxlength="30" border="none" readonly/> |
| @@ -205,7 +205,7 @@ onReady(() => {}); | @@ -205,7 +205,7 @@ onReady(() => {}); | ||
| 205 | </script> | 205 | </script> |
| 206 | 206 | ||
| 207 | <style scoped lang="scss"> | 207 | <style scoped lang="scss"> |
| 208 | -.container { | 208 | +.page-container { |
| 209 | padding: 25rpx; | 209 | padding: 25rpx; |
| 210 | box-sizing: border-box; | 210 | box-sizing: border-box; |
| 211 | background: #fff; | 211 | background: #fff; |
pages-sub/data/tree-archive/treeRecord.vue
| 1 | <template> | 1 | <template> |
| 2 | <view class="container"> | 2 | <view class="container"> |
| 3 | + | ||
| 4 | + <!-- 内容区域 - 集成z-paging分页 --> | ||
| 5 | + <z-paging | ||
| 6 | + ref="pagingRef" | ||
| 7 | + v-model="rows" | ||
| 8 | + @query="fetchData" | ||
| 9 | + :auto-show-system-loading="true" | ||
| 10 | + > | ||
| 11 | + <!-- 空数据提示 --> | ||
| 12 | + <template #empty> | ||
| 13 | + <empty-view/> | ||
| 14 | + </template> | ||
| 3 | <!-- 空数据组件 --> | 15 | <!-- 空数据组件 --> |
| 4 | - <up-empty v-if="rows.length === 0" text="暂无数据" marginTop="100"></up-empty> | 16 | +<!-- <up-empty v-if="rows.length === 0" text="暂无数据" marginTop="100"></up-empty>--> |
| 5 | 17 | ||
| 6 | <!-- 树木列表:up-card重构 + 保留原始背景图写法 核心满足你的要求 --> | 18 | <!-- 树木列表:up-card重构 + 保留原始背景图写法 核心满足你的要求 --> |
| 7 | - <view class="record-wrap" v-else> | 19 | + <view class="record-wrap" > |
| 8 | <up-card | 20 | <up-card |
| 9 | v-for="i in rows" | 21 | v-for="i in rows" |
| 10 | - :key="i.id" | 22 | + :key="i.treenumber" |
| 11 | :border="false" | 23 | :border="false" |
| 12 | :show-head="false" | 24 | :show-head="false" |
| 13 | class="tree-card" | 25 | class="tree-card" |
| @@ -16,7 +28,6 @@ | @@ -16,7 +28,6 @@ | ||
| 16 | > | 28 | > |
| 17 | <template #body> | 29 | <template #body> |
| 18 | <view class="card-body-inner"> | 30 | <view class="card-body-inner"> |
| 19 | - <!-- ✅ 保留你的 原生背景图写法 完全没动 核心要求满足 --> | ||
| 20 | <view class="record-list-left" :style="`background-image: url(${i.treephoto});`"></view> | 31 | <view class="record-list-left" :style="`background-image: url(${i.treephoto});`"></view> |
| 21 | 32 | ||
| 22 | <view class="record-list-right"> | 33 | <view class="record-list-right"> |
| @@ -36,15 +47,9 @@ | @@ -36,15 +47,9 @@ | ||
| 36 | 树木编号:{{ i.treenumber }} | 47 | 树木编号:{{ i.treenumber }} |
| 37 | </view> | 48 | </view> |
| 38 | </template> | 49 | </template> |
| 39 | - | ||
| 40 | -<!-- <template #foot>--> | ||
| 41 | -<!-- <view class="treenumber-no">--> | ||
| 42 | -<!-- 树木编号:{{ i.treenumber }}--> | ||
| 43 | -<!-- </view>--> | ||
| 44 | -<!-- </template>--> | ||
| 45 | </up-card> | 50 | </up-card> |
| 46 | </view> | 51 | </view> |
| 47 | - | 52 | + </z-paging> |
| 48 | <!-- 底部新增按钮 --> | 53 | <!-- 底部新增按钮 --> |
| 49 | <view class="fixed-bottom-btn-wrap"> | 54 | <view class="fixed-bottom-btn-wrap"> |
| 50 | <up-button | 55 | <up-button |
| @@ -82,7 +87,8 @@ onLoad((options) => { | @@ -82,7 +87,8 @@ onLoad((options) => { | ||
| 82 | }) | 87 | }) |
| 83 | 88 | ||
| 84 | onShow(() => { | 89 | onShow(() => { |
| 85 | - treeRoadQuery() | 90 | + // 初始化分页数据 |
| 91 | + pagingRef.value?.reload() | ||
| 86 | }) | 92 | }) |
| 87 | 93 | ||
| 88 | 94 | ||
| @@ -99,10 +105,12 @@ const toAddTreePage = () => { | @@ -99,10 +105,12 @@ const toAddTreePage = () => { | ||
| 99 | }) | 105 | }) |
| 100 | } | 106 | } |
| 101 | 107 | ||
| 102 | -const treeRoadQuery = async () => { | ||
| 103 | - const res = await treeRoadReq( {road: roadId.value}) | 108 | +const pagingRef = ref(null) // z-paging实例 |
| 109 | +const fetchData = async (pageNo, pageSize) => { | ||
| 110 | + const res = await treeRoadReq( {road: roadId.value,pageNo, pageSize}) | ||
| 104 | console.log(res) | 111 | console.log(res) |
| 105 | - rows.value = res.list | 112 | + // rows.value = res.list |
| 113 | + pagingRef.value?.complete(res?.list || [], res?.total) | ||
| 106 | } | 114 | } |
| 107 | </script> | 115 | </script> |
| 108 | 116 |
pages/index/index.vue
| @@ -88,7 +88,7 @@ | @@ -88,7 +88,7 @@ | ||
| 88 | <view class="task-item__footer u-flex common-item-center common-justify-between" | 88 | <view class="task-item__footer u-flex common-item-center common-justify-between" |
| 89 | style="font-size: 13px; margin-top: 5px;"> | 89 | style="font-size: 13px; margin-top: 5px;"> |
| 90 | <view class="urgency-tag" > | 90 | <view class="urgency-tag" > |
| 91 | - 紧急程度: {{ item.pressingType }} | 91 | + 紧急程度: {{ item.pressingType||'--' }} |
| 92 | </view> | 92 | </view> |
| 93 | <view style="font-size: 13px;color: #333">{{ | 93 | <view style="font-size: 13px;color: #333">{{ |
| 94 | timeFormat(item.busiDateTime, 'yyyy-mm-dd hh:MM:ss') | 94 | timeFormat(item.busiDateTime, 'yyyy-mm-dd hh:MM:ss') |
| @@ -371,7 +371,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | @@ -371,7 +371,7 @@ $border-color: #e5e5e5; // 新增边框颜色变量 | ||
| 371 | .user-info-bar { | 371 | .user-info-bar { |
| 372 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 372 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 373 | background-size: 100% 100%; | 373 | background-size: 100% 100%; |
| 374 | - padding: 90px $spacing-lg 135px; | 374 | + padding: 80px $spacing-lg 135px; |
| 375 | display: flex; | 375 | display: flex; |
| 376 | justify-content: space-between; | 376 | justify-content: space-between; |
| 377 | align-items: center; | 377 | align-items: center; |
pages/login/index.vue
| 1 | <template> | 1 | <template> |
| 2 | - <view class="login-page"> | 2 | + <view class="page-container"> |
| 3 | <!-- 顶部标题区 --> | 3 | <!-- 顶部标题区 --> |
| 4 | <view class="top-title"> | 4 | <view class="top-title"> |
| 5 | <text class="welcome-text">你好,欢迎光临</text> | 5 | <text class="welcome-text">你好,欢迎光临</text> |
| @@ -112,27 +112,76 @@ | @@ -112,27 +112,76 @@ | ||
| 112 | import { ref, reactive, onMounted, nextTick } from 'vue'; | 112 | import { ref, reactive, onMounted, nextTick } from 'vue'; |
| 113 | import { useUserStore } from '@/pinia/user'; | 113 | import { useUserStore } from '@/pinia/user'; |
| 114 | import globalConfig from '@/common/config/global'; | 114 | import globalConfig from '@/common/config/global'; |
| 115 | +import CryptoJS from 'crypto-js'; | ||
| 116 | + | ||
| 117 | +// ========== 加密工具函数(建议抽离到 @/utils/encrypt.js) ========== | ||
| 118 | +// 密钥配置:生产环境建议从后端接口获取,不要硬编码 | ||
| 119 | +const CRYPTO_CONFIG = { | ||
| 120 | + key: CryptoJS.enc.Utf8.parse('jcsscrypto123abc'), // 16位key(AES-128) | ||
| 121 | + iv: CryptoJS.enc.Utf8.parse('abc123jcsscrypto'), // 16位iv | ||
| 122 | + mode: CryptoJS.mode.CBC, | ||
| 123 | + padding: CryptoJS.pad.Pkcs7 | ||
| 124 | +}; | ||
| 115 | 125 | ||
| 116 | -// 全局实例 & 基础状态 | 126 | +/** |
| 127 | + * AES加密 | ||
| 128 | + * @param {string} text 待加密文本 | ||
| 129 | + * @returns {string} 加密后的字符串 | ||
| 130 | + */ | ||
| 131 | +const aesEncrypt = (text) => { | ||
| 132 | + if (!text) return ''; | ||
| 133 | + try { | ||
| 134 | + return CryptoJS.AES.encrypt(text, CRYPTO_CONFIG.key, { | ||
| 135 | + iv: CRYPTO_CONFIG.iv, | ||
| 136 | + mode: CRYPTO_CONFIG.mode, | ||
| 137 | + padding: CRYPTO_CONFIG.padding | ||
| 138 | + }).toString(); | ||
| 139 | + } catch (err) { | ||
| 140 | + console.error('AES加密失败:', err); | ||
| 141 | + return ''; | ||
| 142 | + } | ||
| 143 | +}; | ||
| 144 | + | ||
| 145 | +/** | ||
| 146 | + * AES解密 | ||
| 147 | + * @param {string} encryptedText 加密后的字符串 | ||
| 148 | + * @returns {string} 解密后的原始文本 | ||
| 149 | + */ | ||
| 150 | +const aesDecrypt = (encryptedText) => { | ||
| 151 | + if (!encryptedText) return ''; | ||
| 152 | + try { | ||
| 153 | + const decryptObj = CryptoJS.AES.decrypt(encryptedText, CRYPTO_CONFIG.key, { | ||
| 154 | + iv: CRYPTO_CONFIG.iv, | ||
| 155 | + mode: CRYPTO_CONFIG.mode, | ||
| 156 | + padding: CRYPTO_CONFIG.padding | ||
| 157 | + }); | ||
| 158 | + return decryptObj.toString(CryptoJS.enc.Utf8); | ||
| 159 | + } catch (err) { | ||
| 160 | + console.error('AES解密失败:', err); | ||
| 161 | + return ''; | ||
| 162 | + } | ||
| 163 | +}; | ||
| 164 | + | ||
| 165 | +// ========== 业务逻辑 ========== | ||
| 117 | const userStore = useUserStore(); | 166 | const userStore = useUserStore(); |
| 118 | const loginFormRef = ref(null); | 167 | const loginFormRef = ref(null); |
| 119 | const isLoading = ref(false); | 168 | const isLoading = ref(false); |
| 120 | 169 | ||
| 121 | -// Tabs配置(name直接存储显示文本) | 170 | +// Tabs配置 |
| 122 | const tabList = ref([ | 171 | const tabList = ref([ |
| 123 | { name: '手机号登录' }, | 172 | { name: '手机号登录' }, |
| 124 | { name: '账号登录' } | 173 | { name: '账号登录' } |
| 125 | ]); | 174 | ]); |
| 126 | -const loginType = ref('手机号登录'); // 登录类型标识 | 175 | +const loginType = ref('手机号登录'); |
| 127 | 176 | ||
| 128 | -// 记住密码(默认选中) | 177 | +// 记住密码 |
| 129 | const rememberPwd = ref(true); | 178 | const rememberPwd = ref(true); |
| 130 | 179 | ||
| 131 | // 表单数据 | 180 | // 表单数据 |
| 132 | const form = reactive({ | 181 | const form = reactive({ |
| 133 | - account: '', // 账号 | ||
| 134 | - mobile: '', // 手机号 | ||
| 135 | - password: '' // 密码 | 182 | + account: '', |
| 183 | + mobile: '', | ||
| 184 | + password: '' | ||
| 136 | }); | 185 | }); |
| 137 | 186 | ||
| 138 | // 表单校验规则 | 187 | // 表单校验规则 |
| @@ -152,56 +201,19 @@ const loginFormRules = reactive({ | @@ -152,56 +201,19 @@ const loginFormRules = reactive({ | ||
| 152 | }); | 201 | }); |
| 153 | 202 | ||
| 154 | // Tabs切换事件 | 203 | // Tabs切换事件 |
| 155 | -const handleTabChange = ({name}) => { | ||
| 156 | - console.log(name) | 204 | +const handleTabChange = ({ name }) => { |
| 157 | if (isLoading.value) return; | 205 | if (isLoading.value) return; |
| 158 | loginType.value = name; | 206 | loginType.value = name; |
| 159 | - | ||
| 160 | - // 切换时清空另一类输入框 | ||
| 161 | if (name === '手机号登录') { | 207 | if (name === '手机号登录') { |
| 162 | form.account = ''; | 208 | form.account = ''; |
| 163 | } else { | 209 | } else { |
| 164 | form.mobile = ''; | 210 | form.mobile = ''; |
| 165 | } | 211 | } |
| 166 | - | ||
| 167 | - // 清空校验状态 | ||
| 168 | nextTick(() => { | 212 | nextTick(() => { |
| 169 | loginFormRef.value?.clearValidate(); | 213 | loginFormRef.value?.clearValidate(); |
| 170 | }); | 214 | }); |
| 171 | }; | 215 | }; |
| 172 | 216 | ||
| 173 | -// 生命周期 | ||
| 174 | -onMounted(() => { | ||
| 175 | - // 检查登录态 | ||
| 176 | - checkLoginStatus(); | ||
| 177 | - | ||
| 178 | - // 初始化表单校验规则 | ||
| 179 | - nextTick(() => { | ||
| 180 | - loginFormRef.value?.setRules(loginFormRules); | ||
| 181 | - }); | ||
| 182 | - | ||
| 183 | - // 读取缓存的账号/密码 | ||
| 184 | - if (rememberPwd.value) { | ||
| 185 | - try { | ||
| 186 | - const savedAccount = uni.getStorageSync('login_account') || ''; | ||
| 187 | - const savedMobile = uni.getStorageSync('login_mobile') || ''; | ||
| 188 | - const savedPwd = uni.getStorageSync('login_password') || ''; | ||
| 189 | - | ||
| 190 | - if (savedAccount) { | ||
| 191 | - form.account = savedAccount; | ||
| 192 | - loginType.value = '账号登录'; | ||
| 193 | - | ||
| 194 | - } else if (savedMobile) { | ||
| 195 | - form.mobile = savedMobile; | ||
| 196 | - loginType.value = '手机号登录'; | ||
| 197 | - } | ||
| 198 | - form.password = savedPwd; | ||
| 199 | - } catch (err) { | ||
| 200 | - console.warn('读取缓存密码失败:', err); | ||
| 201 | - } | ||
| 202 | - } | ||
| 203 | -}); | ||
| 204 | - | ||
| 205 | // 检查登录状态 | 217 | // 检查登录状态 |
| 206 | const checkLoginStatus = () => { | 218 | const checkLoginStatus = () => { |
| 207 | try { | 219 | try { |
| @@ -212,21 +224,19 @@ const checkLoginStatus = () => { | @@ -212,21 +224,19 @@ const checkLoginStatus = () => { | ||
| 212 | }); | 224 | }); |
| 213 | } | 225 | } |
| 214 | } catch (err) { | 226 | } catch (err) { |
| 215 | - console.warn('检查登录状态失败:', err); | 227 | + console.warn('检查登录状态失败:', err); |
| 216 | } | 228 | } |
| 217 | }; | 229 | }; |
| 218 | 230 | ||
| 219 | // 登录方法 | 231 | // 登录方法 |
| 220 | const handleLogin = async () => { | 232 | const handleLogin = async () => { |
| 221 | try { | 233 | try { |
| 222 | - // 表单校验 | ||
| 223 | await loginFormRef.value.validate(); | 234 | await loginFormRef.value.validate(); |
| 224 | isLoading.value = true; | 235 | isLoading.value = true; |
| 225 | 236 | ||
| 226 | // 组装登录参数 | 237 | // 组装登录参数 |
| 227 | const loginParams = { password: form.password }; | 238 | const loginParams = { password: form.password }; |
| 228 | if (loginType.value === '手机号登录') { | 239 | if (loginType.value === '手机号登录') { |
| 229 | - // loginParams.mobile = form.mobile; | ||
| 230 | loginParams.username = form.mobile; | 240 | loginParams.username = form.mobile; |
| 231 | } else { | 241 | } else { |
| 232 | loginParams.username = form.account; | 242 | loginParams.username = form.account; |
| @@ -235,19 +245,20 @@ const handleLogin = async () => { | @@ -235,19 +245,20 @@ const handleLogin = async () => { | ||
| 235 | // 执行登录 | 245 | // 执行登录 |
| 236 | await userStore.login(loginParams); | 246 | await userStore.login(loginParams); |
| 237 | 247 | ||
| 238 | - // 保存记住密码 | 248 | + // 保存数据:账号/密码 均用AES加密 |
| 239 | if (rememberPwd.value) { | 249 | if (rememberPwd.value) { |
| 240 | try { | 250 | try { |
| 241 | if (loginType.value === '手机号登录') { | 251 | if (loginType.value === '手机号登录') { |
| 242 | - uni.setStorageSync('login_mobile', form.mobile); | 252 | + uni.setStorageSync('login_mobile', aesEncrypt(form.mobile)); |
| 243 | uni.removeStorageSync('login_account'); | 253 | uni.removeStorageSync('login_account'); |
| 244 | } else { | 254 | } else { |
| 245 | - uni.setStorageSync('login_account', form.account); | 255 | + uni.setStorageSync('login_account', aesEncrypt(form.account)); |
| 246 | uni.removeStorageSync('login_mobile'); | 256 | uni.removeStorageSync('login_mobile'); |
| 247 | } | 257 | } |
| 248 | - uni.setStorageSync('login_password', form.password); | 258 | + // 密码加密存储(可解密还原) |
| 259 | + uni.setStorageSync('login_password', aesEncrypt(form.password)); | ||
| 249 | } catch (err) { | 260 | } catch (err) { |
| 250 | - console.warn('保存密码失败:', err); | 261 | + console.warn('保存登录信息失败:', err); |
| 251 | } | 262 | } |
| 252 | } else { | 263 | } else { |
| 253 | // 清除缓存 | 264 | // 清除缓存 |
| @@ -256,7 +267,7 @@ const handleLogin = async () => { | @@ -256,7 +267,7 @@ const handleLogin = async () => { | ||
| 256 | uni.removeStorageSync('login_password'); | 267 | uni.removeStorageSync('login_password'); |
| 257 | } | 268 | } |
| 258 | 269 | ||
| 259 | - // 登录成功提示+跳转 | 270 | + // 登录成功跳转 |
| 260 | uni.showToast({ title: '登录成功', icon: 'success', duration: 1000 }); | 271 | uni.showToast({ title: '登录成功', icon: 'success', duration: 1000 }); |
| 261 | setTimeout(() => { | 272 | setTimeout(() => { |
| 262 | uni.switchTab({ | 273 | uni.switchTab({ |
| @@ -265,9 +276,8 @@ const handleLogin = async () => { | @@ -265,9 +276,8 @@ const handleLogin = async () => { | ||
| 265 | }); | 276 | }); |
| 266 | }, 1000); | 277 | }, 1000); |
| 267 | } catch (err) { | 278 | } catch (err) { |
| 268 | - // 错误处理 | ||
| 269 | if (!Array.isArray(err)) { | 279 | if (!Array.isArray(err)) { |
| 270 | - console.error('登录失败:', err); | 280 | + console.error('登录失败:', err); |
| 271 | uni.showToast({ | 281 | uni.showToast({ |
| 272 | title: err.msg || '账号或密码错误,请重试', | 282 | title: err.msg || '账号或密码错误,请重试', |
| 273 | icon: 'none', | 283 | icon: 'none', |
| @@ -278,20 +288,39 @@ const handleLogin = async () => { | @@ -278,20 +288,39 @@ const handleLogin = async () => { | ||
| 278 | isLoading.value = false; | 288 | isLoading.value = false; |
| 279 | } | 289 | } |
| 280 | }; | 290 | }; |
| 291 | + | ||
| 292 | +// 生命周期 | ||
| 293 | +onMounted(() => { | ||
| 294 | + checkLoginStatus(); | ||
| 295 | + // 初始化表单规则 | ||
| 296 | + nextTick(() => { | ||
| 297 | + loginFormRef.value?.setRules(loginFormRules); | ||
| 298 | + }); | ||
| 299 | + // 读取缓存并解密填充 | ||
| 300 | + if (rememberPwd.value) { | ||
| 301 | + try { | ||
| 302 | + const savedAccount = aesDecrypt(uni.getStorageSync('login_account') || ''); | ||
| 303 | + const savedMobile = aesDecrypt(uni.getStorageSync('login_mobile') || ''); | ||
| 304 | + const savedPwd = aesDecrypt(uni.getStorageSync('login_password') || ''); | ||
| 305 | + | ||
| 306 | + if (savedAccount) { | ||
| 307 | + form.account = savedAccount; | ||
| 308 | + loginType.value = '账号登录'; | ||
| 309 | + } else if (savedMobile) { | ||
| 310 | + form.mobile = savedMobile; | ||
| 311 | + loginType.value = '手机号登录'; | ||
| 312 | + } | ||
| 313 | + // 密码解密后填充到输入框(可展示) | ||
| 314 | + form.password = savedPwd; | ||
| 315 | + } catch (err) { | ||
| 316 | + console.warn('读取缓存失败:', err); | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | +}); | ||
| 281 | </script> | 320 | </script> |
| 282 | 321 | ||
| 283 | <style scoped lang="scss"> | 322 | <style scoped lang="scss"> |
| 284 | -// 核心布局 | ||
| 285 | -.login-page { | ||
| 286 | - min-height: 100vh; | ||
| 287 | - padding: 0; | ||
| 288 | - box-sizing: border-box; | ||
| 289 | - overflow: hidden; | ||
| 290 | - position: relative; | ||
| 291 | - background: #f5f7fa; | ||
| 292 | -} | ||
| 293 | 323 | ||
| 294 | -// 顶部样式 | ||
| 295 | .top-title { | 324 | .top-title { |
| 296 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 325 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 297 | background-size: 100% 100%; | 326 | background-size: 100% 100%; |
| @@ -327,7 +356,6 @@ const handleLogin = async () => { | @@ -327,7 +356,6 @@ const handleLogin = async () => { | ||
| 327 | box-sizing: border-box; | 356 | box-sizing: border-box; |
| 328 | } | 357 | } |
| 329 | 358 | ||
| 330 | -// Tabs样式适配 | ||
| 331 | :deep(.u-tabs) { | 359 | :deep(.u-tabs) { |
| 332 | margin-bottom: 15px; | 360 | margin-bottom: 15px; |
| 333 | .u-tabs__content { | 361 | .u-tabs__content { |
| @@ -338,29 +366,24 @@ const handleLogin = async () => { | @@ -338,29 +366,24 @@ const handleLogin = async () => { | ||
| 338 | } | 366 | } |
| 339 | } | 367 | } |
| 340 | 368 | ||
| 341 | -// 表单间距 | ||
| 342 | :deep(.u-form-item) { | 369 | :deep(.u-form-item) { |
| 343 | margin-bottom: 10px; | 370 | margin-bottom: 10px; |
| 344 | position: relative; | 371 | position: relative; |
| 345 | } | 372 | } |
| 346 | 373 | ||
| 347 | -// 密码项样式 | ||
| 348 | .password-item { | 374 | .password-item { |
| 349 | position: relative; | 375 | position: relative; |
| 350 | margin-bottom: 5px !important; | 376 | margin-bottom: 5px !important; |
| 351 | } | 377 | } |
| 352 | 378 | ||
| 353 | -// ✅ 核心优化:记住密码完全居右对齐 | ||
| 354 | .remember-wrap { | 379 | .remember-wrap { |
| 355 | - //text-align: right; | ||
| 356 | - padding: 0; // 移除多余内边距 | 380 | + padding: 0; |
| 357 | margin: 10px 0 20px; | 381 | margin: 10px 0 20px; |
| 358 | 382 | ||
| 359 | :deep(.u-checkbox) { | 383 | :deep(.u-checkbox) { |
| 360 | font-size: 12px; | 384 | font-size: 12px; |
| 361 | color: #666; | 385 | color: #666; |
| 362 | justify-content: flex-end; | 386 | justify-content: flex-end; |
| 363 | - // 移除复选框的默认左边距,确保居右紧凑 | ||
| 364 | margin-left: 0 !important; | 387 | margin-left: 0 !important; |
| 365 | 388 | ||
| 366 | .u-checkbox__label { | 389 | .u-checkbox__label { |
| @@ -369,7 +392,6 @@ const handleLogin = async () => { | @@ -369,7 +392,6 @@ const handleLogin = async () => { | ||
| 369 | } | 392 | } |
| 370 | } | 393 | } |
| 371 | 394 | ||
| 372 | -// 登录按钮 | ||
| 373 | .login-btn { | 395 | .login-btn { |
| 374 | margin-top: 10px; | 396 | margin-top: 10px; |
| 375 | width: 100%; | 397 | width: 100%; |
| @@ -382,7 +404,6 @@ const handleLogin = async () => { | @@ -382,7 +404,6 @@ const handleLogin = async () => { | ||
| 382 | border-radius: 23px; | 404 | border-radius: 23px; |
| 383 | } | 405 | } |
| 384 | 406 | ||
| 385 | -// 版权信息 | ||
| 386 | .copyright { | 407 | .copyright { |
| 387 | width: 100%; | 408 | width: 100%; |
| 388 | text-align: center; | 409 | text-align: center; |
pages/mine/index.vue
| @@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
| 13 | <view class="user-info-content"> | 13 | <view class="user-info-content"> |
| 14 | <view class="user-name">{{ userStore.isLogin ? userInfo.username : '未登录' }}</view> | 14 | <view class="user-name">{{ userStore.isLogin ? userInfo.username : '未登录' }}</view> |
| 15 | <!-- <view class="user-phone">{{ userStore.isLogin ? userInfo.nickname : '--------' }}</view>--> | 15 | <!-- <view class="user-phone">{{ userStore.isLogin ? userInfo.nickname : '--------' }}</view>--> |
| 16 | - <view class="user-phone">上次登录时间{{ timeFormat(userInfo.loginDate)}}</view> | 16 | + <view class="user-phone">上次登录时间 {{ timeFormat(userInfo.loginDate)}}</view> |
| 17 | 17 | ||
| 18 | </view> | 18 | </view> |
| 19 | 19 | ||
| @@ -123,7 +123,7 @@ export default { | @@ -123,7 +123,7 @@ export default { | ||
| 123 | top: 0; | 123 | top: 0; |
| 124 | left: 0; | 124 | left: 0; |
| 125 | width: 100%; | 125 | width: 100%; |
| 126 | - height: 230px; | 126 | + height: 220px; |
| 127 | //height: 120px; | 127 | //height: 120px; |
| 128 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 128 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 129 | background-size: 100% 100%; | 129 | background-size: 100% 100%; |
| @@ -160,17 +160,15 @@ export default { | @@ -160,17 +160,15 @@ export default { | ||
| 160 | } | 160 | } |
| 161 | // 退出登录按钮 | 161 | // 退出登录按钮 |
| 162 | .logout-btn-wrap { | 162 | .logout-btn-wrap { |
| 163 | - margin: 100px 10px 10px; | 163 | + margin: 80px 15px 0; |
| 164 | } | 164 | } |
| 165 | // 登录按钮 | 165 | // 登录按钮 |
| 166 | .login-btn { | 166 | .login-btn { |
| 167 | - margin-top: 20px; | ||
| 168 | width: 100%; | 167 | width: 100%; |
| 169 | height: 44px; | 168 | height: 44px; |
| 170 | line-height: 44px; | 169 | line-height: 44px; |
| 171 | border-radius: 4px; | 170 | border-radius: 4px; |
| 172 | font-size: 16px; | 171 | font-size: 16px; |
| 173 | - | ||
| 174 | background: #0A86F4; | 172 | background: #0A86F4; |
| 175 | box-shadow: 0px 4px 6px 1px rgba(25,94,215,0.5); | 173 | box-shadow: 0px 4px 6px 1px rgba(25,94,215,0.5); |
| 176 | border-radius: 23px; | 174 | border-radius: 23px; |
pages/workbench/index.vue
| @@ -166,7 +166,7 @@ const handleMenuClick = (item: MenuItem) => { | @@ -166,7 +166,7 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 166 | top: 0; | 166 | top: 0; |
| 167 | left: 0; | 167 | left: 0; |
| 168 | width: 100%; | 168 | width: 100%; |
| 169 | - padding: 80px 15px 125px; | 169 | + padding: 80px 15px 115px; |
| 170 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; | 170 | background: url("https://img.jichengshanshui.com.cn:28207/appimg/bg.jpg") no-repeat; |
| 171 | background-size: 100% 100%; | 171 | background-size: 100% 100%; |
| 172 | z-index: 1; | 172 | z-index: 1; |
| @@ -176,7 +176,7 @@ const handleMenuClick = (item: MenuItem) => { | @@ -176,7 +176,7 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 176 | .welcome-text { | 176 | .welcome-text { |
| 177 | font-size: 16px; | 177 | font-size: 16px; |
| 178 | display: block; | 178 | display: block; |
| 179 | - margin-bottom: 10px; | 179 | + margin-bottom: 4px; |
| 180 | font-weight: 500; | 180 | font-weight: 500; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| @@ -191,7 +191,7 @@ const handleMenuClick = (item: MenuItem) => { | @@ -191,7 +191,7 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 191 | .content-wrap { | 191 | .content-wrap { |
| 192 | position: relative; | 192 | position: relative; |
| 193 | z-index: 2; | 193 | z-index: 2; |
| 194 | - padding: 135px 0 0; | 194 | + padding: 125px 0 0; |
| 195 | display: flex; | 195 | display: flex; |
| 196 | flex-direction: column; | 196 | flex-direction: column; |
| 197 | gap: 10px; | 197 | gap: 10px; |