354 lines
11 KiB
Vue
354 lines
11 KiB
Vue
<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>
|