登录页优化

This commit is contained in:
2026-01-16 15:22:14 +08:00
parent ec47d9011e
commit 9afbe92a18
4 changed files with 413 additions and 272 deletions

BIN
src/assets/logo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
src/assets/logoBg2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View File

@@ -1,131 +1,157 @@
<template>
<div class="main">
<div class="container">
<div class="right">
<img src="../assets/logoBg.png" class="background-video" alt="">
<!-- 设置 -->
<div class="center-align">
<div></div>
<div class="setup">
<div class="setup-item center-justify">
<div></div>
<span>
网络设置
<div class="viewport center-justify">
<div class="auth-shell ">
<!-- 顶部操作区网络/语言 -->
<!-- <div class="top-actions">
<div class="setup-item center-justify">
<el-dropdown>
<span class="el-dropdown-link">
<span class="setup-text">网络设置</span>
</span>
</div>
<div class="setup-item center-justify">
<div></div>
<span>
简体中文
</el-dropdown>
</div>
<div class="setup-item center-justify">
<el-dropdown>
<span class="el-dropdown-link">
<span class="setup-text">简体中文</span>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>中文</el-dropdown-item>
<el-dropdown-item>English</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div> -->
<!-- 主卡片 -->
<div class="auth-card">
<!-- 左侧登录区 -->
<div class="auth-left">
<div class="logo center-justify">
<!-- 你原来这里是 logotext.png也可以换成 logo2.png -->
<img class="logo-image" src="@/assets/logo2.png" alt="" />
<span class="logo-title">IOS自动化平台</span>
</div>
<div class="welcome">账号登录</div>
<div class="from">
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="field-label">租户号</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input style="height: 25px" v-model="formData.tenantName" placeholder="租户号"
clearable @keyup.enter="onSubmit" />
</div>
<div class="field-label">账号</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input style="height: 25px" v-model="formData.userId" placeholder="账号"
clearable @keyup.enter="onSubmit" />
</div>
<div class="field-label">密码</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="" />
<el-input style="height: 25px" v-model="formData.password" type="password"
placeholder="密码" show-password @keyup.enter="onSubmit" />
</div>
<div class="from-input-item">
<el-button class="loginButton" color="#4f6ef7" type="primary"
:loading="isSubmitting" :disabled="isSubmitting" @click="onSubmit">
登录
</el-button>
</div>
</el-form>
</div>
</div>
<div class="version">版本号{{ version }}</div>
</div>
<!-- 右侧插画区 -->
<div class="auth-right">
<!-- 你原来是 logoBg.png这里保持同款右侧插画用法 -->
<img src="../assets/logoBg2.png" class="illustration" alt="" />
</div>
</div>
<div class="center-line" style="margin-top: 40px;">
<!-- logo -->
<div class="logo">
<div class="center-justify" style="height: 80px; width: 300px;">
<!-- <img style="margin-right: 20px;height: 100%;" src="@/assets/logo.png"> -->
<img style="height: 100%;" src="@/assets/logotext.png">
</div>
</div>
<!-- From -->
<div class="from">
<div class="from-title center-justify">
<div>账号登陆</div>
</div>
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="from-input-item1">
<img src="@/assets/username.png" alt="">
<el-input style="height: 25px;" v-model="formData.tenantName" placeholder="租户号"
clearable @keyup.enter="onSubmit" />
</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="">
<el-input style="height: 25px;" v-model="formData.userId" placeholder="账号" clearable
@keyup.enter="onSubmit" />
</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="">
<el-input style="height: 25px; " v-model="formData.password" type="password"
placeholder="密码" show-password @keyup.enter="onSubmit" />
</div>
<div class="from-input-item">
<el-button class="loginButton" color="#8f7ee7" type="primary"
@click="onSubmit">登录</el-button>
</div>
</el-form>
</div>
</div>
</div>
<div class="version center-justify ">版本号{{ version }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { login, getIdByName } from '@/api/account';
import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/stores/storage';
import { ElLoading, ElMessage } from 'element-plus';
import { passToken } from '@/api/ios';
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useRouter } from 'vue-router'
import { login, getIdByName } from '@/api/account'
import { setToken, setUser, setUserPass, getUserPass } from '@/stores/storage'
import { passToken } from '@/api/ios'
let version = ref('3.6.0');
let version = ref('3.6.2')
const scale = ref(1)
const resize = () => {
const sw = window.innerWidth / 1600
const sh = window.innerHeight / 900
scale.value = Math.min(sw, sh)
const el = document.querySelector('.viewport')
if (el) el.style.transform = `scale(${scale.value})`
}
onMounted(() => {
resize()
window.addEventListener('resize', resize)
})
const router = useRouter();
onBeforeUnmount(() => {
window.removeEventListener('resize', resize)
})
const router = useRouter()
const isSubmitting = ref(false)
const formData = ref({
tenantName: getUserPass() == null ? '' : getUserPass().tenantName,
userId: getUserPass() == null ? '' : getUserPass().userId,
password: getUserPass() == null ? '' : getUserPass().password,
});
password: getUserPass() == null ? '' : getUserPass().password
})
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
});
setUserPass(formData.value);
getIdByName(formData.value.tenantName).then((tenantId) => {
console.log("tenantId", tenantId)
login({
tenantId: Number(tenantId),
username: formData.value.userId,
password: formData.value.password,
}).then((res) => {
loading.close();
console.log(res)
setToken(res.tokenValue);
setUser(res);
setTimeout(() => {
passToken({ data: res, })
}, 10000)
if (isSubmitting.value) return
isSubmitting.value = true
router.push('/Video');
}).catch((err) => {
loading.close();
}).finally((err) => {
// loading.close();
setUserPass(formData.value)
getIdByName(formData.value.tenantName)
.then((tenantId) => {
login({
tenantId: Number(tenantId),
username: formData.value.userId,
password: formData.value.password
})
.then((res) => {
isSubmitting.value = false
setToken(res.tokenValue)
setUser(res)
setTimeout(() => {
passToken({ data: res })
}, 10000)
router.push('/Video')
})
.catch(() => {
isSubmitting.value = false
})
})
})
};
.catch(() => {
isSubmitting.value = false
})
}
</script>
<style lang="less">
@@ -134,153 +160,23 @@ const onSubmit = () => {
height: 100vh;
overflow: hidden;
box-sizing: border-box;
background: linear-gradient(180deg, #f3f5f9 0%, #eef1f5 100%);
display: flex;
align-items: center;
justify-content: center;
font-family: "Source Han Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
/* 页面无法选中 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
.container {
display: flex;
box-sizing: border-box;
width: 100vw;
height: 100vh;
.right {
box-sizing: border-box;
position: relative;
width: 100vw;
height: 100vh;
padding: 20px 40px 20px 50px;
border-left: 3px solid #23516e;
position: relative;
/* 添加 position: relative */
overflow: hidden;
/* 防止内容溢出 */
.version {
color: #fff;
position: absolute;
font-size: 20px;
bottom: 20px;
left: calc(50% - 50px);
// box-sizing: border-box;
// width: 1600px;
}
.background-video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
/* 确保视频在内容之下 */
}
.setup {
display: flex;
color: #fff;
.setup-item {
padding: 10px 6px;
display: flex;
div {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rgb(255, 255, 255);
margin-right: 5px;
}
}
}
.logo {
padding: 20px 0;
}
.from {
width: 420px;
// height: 320px;
color: #022b4e;
background-color: #ffffff44;
border-radius: 20px;
border: 1px solid #fff;
padding: 32px;
box-sizing: border-box;
.from-title {
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 24px;
color: #022b4e;
line-height: 37px;
div {
font-size: 20px;
font-weight: 600;
// border-bottom: 4px solid #1db97d;
}
}
.from-input {
width: 100%;
padding: 15px 0;
.from-input-item {
display: flex;
padding: 8px 0;
.from-input-item-title {
color: #022b4e;
font-size: 18px;
font-weight: 500;
width: 80px;
height: 50px;
}
.loginButton {
width: 359px;
height: 50px;
background: #FFFFFF;
border-radius: 24px;
border: 1px solid #FFFFFF;
font-family: Source Han Sans SC;
font-weight: 500;
font-size: 18px;
color: #022b4e;
line-height: 37px;
}
}
.from-input-item1 {
display: flex;
width: 359px;
height: 50px;
background: #022b4e1c;
border-radius: 24px;
border: 1px solid #FFFFFF;
padding: 12px 25px 13px 25px;
box-sizing: border-box;
margin-bottom: 16px;
}
}
}
}
}
}
.center-line {
display: flex;
flex-direction: column;
align-items: center;
/* 设计稿基准尺寸1600x900 */
.viewport {
width: 1600px;
height: 900px;
transform-origin: center center;
}
.center-justify {
@@ -289,37 +185,248 @@ const onSubmit = () => {
align-items: center;
}
.center-align {
.auth-shell {
width: 1320px;
height: 760px;
position: relative;
display: flex;
justify-content: space-between;
}
.center-flex {
display: flex;
justify-content: center;
align-items: center;
justify-content: center;
}
.auth-shell::before,
.auth-shell::after {
content: "";
position: absolute;
border-radius: 50%;
filter: blur(0);
}
.auth-shell::before {
width: 420px;
height: 420px;
background: radial-gradient(circle, rgba(214, 226, 255, 0.7), rgba(214, 226, 255, 0));
left: -80px;
top: -40px;
}
.auth-shell::after {
width: 360px;
height: 360px;
background: radial-gradient(circle, rgba(255, 236, 214, 0.6), rgba(255, 236, 214, 0));
right: -40px;
bottom: -60px;
}
.top-actions {
position: absolute;
top: 50px;
right: 80px;
display: flex;
gap: 16px;
z-index: 2;
}
.setup-item {
background: rgba(255, 255, 255, 0.95);
border: 1px solid #e1e6f2;
border-radius: 16px;
padding: 8px 12px 8px 14px;
box-shadow: 0 8px 18px rgba(40, 57, 108, 0.12);
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
display: inline-flex;
align-items: center;
gap: 6px;
}
.setup-text {
font-size: 13px;
font-weight: 600;
color: #2b3347;
letter-spacing: 0.2px;
}
.setup-item::after {
content: "";
width: 6px;
height: 6px;
border-right: 2px solid #9aa1b3;
border-bottom: 2px solid #9aa1b3;
transform: rotate(45deg);
margin-top: -2px;
}
.setup-item:hover {
transform: translateY(-1px);
border-color: #c9d2f4;
box-shadow: 0 12px 24px rgba(40, 57, 108, 0.16);
}
.setup-item:active {
transform: translateY(0);
}
.auth-card {
width: 1200px;
height: 680px;
display: flex;
background: #ffffff;
border-radius: 28px;
overflow: hidden;
box-shadow: 0 24px 60px rgba(40, 57, 108, 0.16);
position: relative;
z-index: 1;
}
.auth-left {
width: 42%;
padding: 48px 56px;
display: flex;
flex-direction: column;
justify-content: center;
}
.logo {
display: flex;
align-items: center;
gap: 16px;
}
.logo-image {
height: 200px;
margin-bottom: -20px;
}
.logo-title {
font-size: 22px;
font-weight: 600;
color: #2b3347;
letter-spacing: 2px;
position: relative;
padding-left: 14px;
margin-top: 30px;
}
.logo-title::before {
content: "";
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 4px;
border-radius: 2px;
background: linear-gradient(180deg, #4f6ef7, #5b7bff);
}
.welcome {
margin-top: 14px;
font-size: 24px;
font-weight: 600;
color: #2b3347;
margin-bottom: 8px;
}
.from {
width: 100%;
color: #2b3347;
background-color: transparent;
border-radius: 0;
border: none;
padding: 0;
box-sizing: border-box;
}
.from-input {
width: 100%;
padding: 8px 0 0 0;
}
.field-label {
font-size: 13px;
color: #6b7280;
margin: 12px 0 6px;
}
.from-input-item {
display: flex;
padding: 16px 0 0 0;
}
.loginButton {
width: 100%;
height: 48px;
background: linear-gradient(135deg, #4f6ef7, #5b7bff);
border-radius: 14px;
border: none;
font-size: 16px;
font-weight: 600;
color: #ffffff;
box-shadow: 0 12px 24px rgba(79, 110, 247, 0.28);
}
.from-input-item1 {
display: flex;
align-items: center;
width: 100%;
height: 48px;
background: #f6f8fc;
border-radius: 12px;
border: 1px solid #e6eaf2;
padding: 10px 14px;
box-sizing: border-box;
margin-bottom: 4px;
gap: 10px;
}
.from-input-item1 img {
width: 18px;
height: 18px;
opacity: 0.6;
}
.version {
margin-top: 24px;
font-size: 12px;
color: #9aa1b3;
}
.auth-right {
width: 58%;
background: linear-gradient(160deg, #f8f9fc 0%, #f0f3f9 100%);
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.illustration {
width: 90%;
height: auto;
max-height: 88%;
object-fit: contain;
filter: drop-shadow(0 30px 60px rgba(40, 57, 108, 0.25));
}
/* Element Plus 输入框外层去掉 focus 边框效果 */
.el-input__wrapper {
--el-input-focus-border-color: rgba(255, 255, 0, 0);
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
}
</style>
<style scoped lang="less">
::v-deep(.el-input__wrapper) {
background-color: rgba(255, 0, 0, 0);
background-color: transparent;
box-shadow: none;
}
::v-deep(.el-input__inner) {
color: #fff;
color: #2b3347;
}
::v-deep(.el-input__inner::placeholder) {
color: #022b4e;
color: #9aa1b3;
}
</style>

View File

@@ -304,18 +304,12 @@ const normalizeSseLimit = (val, fallback) => {
watch(sseCrawlerEnabled, v => {
localStorage.setItem('SSE_CRAWLER_ENABLED', JSON.stringify(v))
if (v && sseCrawlerRemaining.value <= 0) {
sseCrawlerEnabled.value = false
ElMessage.warning('请先设置监听爬虫条数')
}
})
watch(sseBossEnabled, v => {
localStorage.setItem('SSE_BOSS_ENABLED', JSON.stringify(v))
if (v && sseBossRemaining.value <= 0) {
sseBossEnabled.value = false
ElMessage.warning('请先设置监听大哥条数')
}
})
watch(sseCrawlerLimit, v => {
@@ -362,9 +356,15 @@ const bumpSseCount = (type) => {
const remainingRef = isCrawler ? sseCrawlerRemaining : sseBossRemaining
if (remainingRef.value <= 0) {
if (isCrawler) {
sseCrawlerEnabled.value = false
if (sseCrawlerEnabled.value) {
sseCrawlerEnabled.value = false
window.electronAPI.toggleMq('crawler', false)
}
} else {
sseBossEnabled.value = false
if (sseBossEnabled.value) {
sseBossEnabled.value = false
window.electronAPI.toggleMq('boss', false)
}
}
return false
}
@@ -373,8 +373,10 @@ const bumpSseCount = (type) => {
if (remainingRef.value <= 0) {
if (isCrawler) {
sseCrawlerEnabled.value = false
window.electronAPI.toggleMq('crawler', false)
} else {
sseBossEnabled.value = false
window.electronAPI.toggleMq('boss', false)
}
ElMessage.info(`监听${isCrawler ? '爬虫' : '大哥'}条数已用完,已自动关闭`)
}
@@ -814,7 +816,11 @@ onMounted(async () => {
console.log("sse过滤的数据", getHostfilters())
//MQ链接
window.electronAPI.startMq(userdata.tenantId, userdata.id)
window.electronAPI.startMq(userdata.tenantId, userdata.id).then(() => {
// 初始化时同步状态
window.electronAPI.toggleMq('crawler', sseCrawlerEnabled.value)
window.electronAPI.toggleMq('boss', sseBossEnabled.value)
})
// 初始化时获取设备列表
@@ -1771,12 +1777,40 @@ const markPendingForOffline = (ids) => {
};
const onToggleCrawler = (val) => {
if (val) sseBossEnabled.value = false;
};
// 🔒 想打开,但剩余为 0直接拦截
if (val === true && sseCrawlerRemaining.value <= 0) {
ElMessage.warning('请先设置监听爬虫条数')
// 立即回滚(但这是“主动控制”,不是 watch 回滚)
sseCrawlerEnabled.value = false
return
}
// 互斥逻辑
if (val) {
sseBossEnabled.value = false
window.electronAPI.toggleMq('boss', false)
}
// 真正同步给后端 / MQ
window.electronAPI.toggleMq('crawler', val)
}
const onToggleBoss = (val) => {
if (val) sseCrawlerEnabled.value = false;
};
if (val === true && sseBossRemaining.value <= 0) {
ElMessage.warning('请先设置监听大哥条数')
sseBossEnabled.value = false
return
}
if (val) {
sseCrawlerEnabled.value = false
window.electronAPI.toggleMq('crawler', false)
}
window.electronAPI.toggleMq('boss', val)
}
function timestampToTime(timestamp_ms) {