agri-app/pages/login.vue

354 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="normal-login-container">
<view class="logo-content align-center justify-center flex">
<image style="width: 100rpx;height: 100rpx;" :src="globalConfig.appInfo.logo" mode="widthFix">
</image>
<text class="title">智能农业移动端登录</text>
</view>
<view class="login-form-content">
<view class="input-item flex align-center">
<view class="iconfont icon-user icon"></view>
<input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
</view>
<view class="input-item flex align-center" style="position: relative;">
<view class="iconfont icon-password icon"></view>
<input v-if="!isShowPwd"
v-model="loginForm.password" type="password" class="input" placeholder="请输入密码" maxlength="20" />
<input v-else v-model="loginForm.password" type="text" class="input" placeholder="请输入密码" maxlength="20" />
<!-- 替换为 uni-icons 密码图标 -->
<uni-icons
class="pwd-icon"
:type="isShowPwd ? 'eye-filled' : 'eye'"
size="18"
color="#666"
@click="isShowPwd = !isShowPwd"
></uni-icons>
</view>
<view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
<view class="iconfont icon-code icon"></view>
<input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
<view class="login-code">
<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
</view>
</view>
<!-- ========== 新增uni-data-checkbox 记住密码(可正常勾选) ========== -->
<view style="margin: 10px 0 0 10px; text-align: left;">
<uni-data-checkbox
multiple
v-model="value"
:localdata="data"
size="14"
color="#007aff"
@change="onRememberChange"
></uni-data-checkbox>
</view>
<!-- ========== 新增结束 ========== -->
<view class="action-btn">
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
</view>
<view class="reg text-center" v-if="register">
<text class="text-grey1">没有账号?</text>
<text @click="handleUserRegister" class="text-blue">立即注册</text>
</view>
<view class="xieyi text-center">
<text class="text-grey1">登录即代表同意</text>
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
</view>
</view>
<view class="icp ">
<view class="icp-one">
<view>
主办单位北京小策技术有限公司
</view>
<view>
版权所有北京小策技术有限公司
</view>
<view>
ICP备案/许可证号京ICP备2026002112号-2X
</view>
</view>
</view>
</view>
</template>
<script>
import { getCodeImg } from '@/api/login'
import { getToken } from '@/utils/auth'
import UniIcons from "../uni_modules/uni-icons/components/uni-icons/uni-icons.vue";
// ========== 新增引入uni-data-checkbox ==========
import UniDataCheckbox from "@/uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue";
import CryptoJS from 'crypto-js'
export default {
// ========== 新增:注册组件 ==========
components: {UniIcons, UniDataCheckbox},
data() {
return {
isShowPwd: false,
codeUrl: "",
captchaEnabled: true,
// 用户注册开关
register: true,
data:[{value: 0, text: '记住密码'}],
globalConfig: getApp().globalData.config,
loginForm: {
username: "",
password: "",
code: "",
uuid: ""
},
value:[0],
// ========== 新增:记住密码相关数据 ==========
remember: true, // 记住密码选中状态
STORAGE_KEY: 'agri_login_pwd' // 存储密码的key
}
},
created() {
this.getCode()
},
onLoad() {
//#ifdef H5
if (getToken()) {
this.$tab.reLaunch('/pages/control/index')
}
//#endif
// ========== 新增:页面加载时自动回填密码 ==========
this.loadSavedPwd()
},
methods: {
// ========== 新增uni-data-checkbox 状态切换 ==========
onRememberChange(e) {
this.remember = false;
if (e.detail.value && e.detail.value===0) {
this.remember = true ;
}
},
// ========== 新增:密码加密存储核心方法 ==========
// 生成加密密钥(绑定设备信息)
getEncryptKey() {
try {
const sys = uni.getSystemInfoSync()
const appId = uni.getAccountInfoSync()?.miniProgram?.appId || 'agri-default'
return CryptoJS.MD5(sys.deviceId + appId + 'agri-pwd-key').toString()
} catch (e) {
return 'agri-safe-key-2026'
}
},
// 加密密码
encryptPwd(pwd) {
return CryptoJS.AES.encrypt(pwd, this.getEncryptKey()).toString()
},
// 解密密码
decryptPwd(encryptedStr) {
try {
const bytes = CryptoJS.AES.decrypt(encryptedStr, this.getEncryptKey())
return bytes.toString(CryptoJS.enc.Utf8) || ''
} catch (e) {
return ''
}
},
// 加载本地存储的密码并回填
loadSavedPwd() {
try {
const savedData = uni.getStorageSync(this.STORAGE_KEY)
if (!savedData) return
const data = JSON.parse(savedData)
if (data.username && data.encryptedPwd) {
this.loginForm.username = data.username
this.loginForm.password = this.decryptPwd(data.encryptedPwd)
this.remember = true // 回填密码时自动选中
}
} catch (e) {
console.error('加载记住密码失败:', e)
}
},
// 保存密码到本地(加密)
savePwd() {
if (!this.remember) {
uni.removeStorageSync(this.STORAGE_KEY)
return
}
const saveData = {
username: this.loginForm.username,
encryptedPwd: this.encryptPwd(this.loginForm.password),
saveTime: Date.now()
}
uni.setStorageSync(this.STORAGE_KEY, JSON.stringify(saveData))
},
// ========== 原有方法(完全未修改) ==========
// 用户注册
handleUserRegister() {
this.$tab.redirectTo(`/pages/register`)
},
// 隐私协议
handlePrivacy() {
let site = this.globalConfig.appInfo.agreements[0]
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
},
// 用户协议
handleUserAgrement() {
let site = this.globalConfig.appInfo.agreements[1]
this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
},
// 获取图形验证码
getCode() {
getCodeImg().then(res => {
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
if (this.captchaEnabled) {
this.codeUrl = 'data:image/gif;base64,' + res.img
this.loginForm.uuid = res.uuid
}
})
},
// 登录方法
async handleLogin() {
if (this.loginForm.username === "") {
this.$modal.msgError("请输入账号")
} else if (this.loginForm.password === "") {
this.$modal.msgError("请输入密码")
} else if (this.loginForm.code === "" && this.captchaEnabled) {
this.$modal.msgError("请输入验证码")
} else {
this.$modal.loading("登录中,请耐心等待...")
this.pwdLogin()
}
},
// 密码登录
async pwdLogin() {
this.$store.dispatch('Login', this.loginForm).then(() => {
this.$modal.closeLoading()
// ========== 新增:登录成功后保存密码 ==========
this.savePwd()
this.loginSuccess()
}).catch(() => {
if (this.captchaEnabled) {
this.getCode()
}
})
},
// 登录成功后,处理函数
loginSuccess(result) {
// 设置用户信息
this.$store.dispatch('GetInfo').then(res => {
// ========== 原有MQTT初始化逻辑最小改动 ==========
// 1. 获取全局App实例
const app = getApp()
// 2. 获取登录后的token从auth工具获取
const token = getToken()
// 3. 订阅列表:暂时写死(后续可改为从后端接口获取)
// 示例:按用户名/用户ID生成专属订阅列表
// 864865085016294 十方北棚
// 864536071808560 七方北棚
// 864865085008135 八方北棚
// 4. 调用App.vue的loginSuccess方法初始化MQTT
app.loginSuccess(token)
// ========== 原有逻辑保留 ==========
this.$tab.reLaunch('/pages/control/index')
})
}
}
}
</script>
<style lang="scss" scoped>
page {
background-color: #ffffff;
}
.pwd-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
}
.normal-login-container {
width: 100%;
.logo-content {
width: 100%;
font-size: 21px;
text-align: center;
padding-top: 15%;
image {
border-radius: 4px;
}
.title {
margin-left: 10px;
}
}
.login-form-content {
text-align: center;
margin: 20px auto;
margin-top: 15%;
width: 80%;
.input-item {
margin: 20px auto;
background-color: #f5f6f7;
height: 45px;
border-radius: 20px;
.icon {
font-size: 38rpx;
margin-left: 10px;
color: #999;
}
.input,
.input:focus,
.input:active,
.input:hover {
font-size: 14px;
line-height: 20px;
background-color: #f5f6f7; /* 输入框背景色(和页面背景区分) */
text-align: left;
padding-left: 15px;
border: none;
outline: none !important;
box-shadow: none;
}
}
.login-btn {
margin-top: 40px;
height: 45px;
}
.reg {
margin-top: 15px;
}
.xieyi {
color: #333;
margin-top: 20px;
}
.login-code {
height: 38px;
float: right;
.login-code-img {
height: 38px;
position: absolute;
margin-left: 10px;
width: 200rpx;
}
}
}
}
.icp { position: fixed; bottom: 0; width: 100%; background: #fff; padding: 12px 0; box-shadow: 0 -2px 4px rgba(0,0,0,0.1); }
.icp-one { max-width: 800px; margin: 0 auto; text-align: center; font-size: 12px; color: #666; }
</style>