登录页优化

This commit is contained in:
2026-01-15 20:42:27 +08:00
parent 812fade238
commit d0d2f260df
6 changed files with 432 additions and 306 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

@@ -11,6 +11,7 @@ export default {
login: 'Login',
version: 'Version',
expirationtime: 'Expiration Time',
assistant: 'TK high-value fan assistant',
},
hostsList: {
// 顶部筛选
@@ -26,7 +27,7 @@ export default {
reset: 'Reset',
start: 'Start',
end: 'End',
// 第二行筛选
selectCountry: 'Select Country',
bigBrotherId: 'Big Brother ID',
@@ -36,7 +37,7 @@ export default {
openTikTok: 'Open TikTok Login',
currentNetwork: 'Current Network',
runningTime: 'Running Time',
// 表格列
id: 'Id',
hostId: 'Host ID',
@@ -49,7 +50,7 @@ export default {
followerCount: 'Followers',
followingCount: 'Following',
createTime: 'Created Time',
// 更多筛选弹窗
time: 'Time',
startTime: 'Start Time',
@@ -67,7 +68,7 @@ export default {
descending: 'Descending',
confirm: 'Confirm',
cancel: 'Cancel',
// 指定直播间弹窗
cancelSpecify: 'Cancel Specify Rooms',
specifyReset: 'Reset',
@@ -75,25 +76,25 @@ export default {
specifyStart: 'Start',
enterRoomIds: 'Enter room IDs, separate multiple IDs with Enter key',
enterRoomId: 'Please enter room ID',
// 网络问题弹窗
networkFailed:
'Network connection failed, unable to access the network. Please check network settings.',
// 复制提示
noContentToCopy: 'No content to copy',
copySuccess: 'Copied successfully',
copyFailed: 'Copy failed',
// 任务状态 loading
stopping: 'Stopping...',
starting: 'Starting...',
// 获取国家失败弹窗
pleaseEnterCountryName: 'Please enter the country name in Chinese',
getCountryFailed: 'Failed to get country',
},
// ==== 新增:国家名称国际化 ====
countries: {
AD: "Andorra",

View File

@@ -11,6 +11,8 @@ export default {
login: '登录',
version: '版本号',
expirationtime: '过期时间',
assistant: 'TK高价值粉丝助手',
},
hostsList: {
filterPrivateUsers: '过滤隐私用户',
@@ -57,7 +59,7 @@ export default {
specifyStart: '开始',
networkFailed: '网络连接失败,无法访问网络,请查看网络设置。',
enterRoomIds: '请输入直播间id多个id用回车键隔开',
// ==== 新增:表格列、排序使用 ====
userId: '用户id',
level: '等级',
@@ -68,24 +70,24 @@ export default {
followerCount: '粉丝数',
followingCount: '关注数',
createTime: '创建时间',
// ==== 新增:复制提示 ====
noContentToCopy: '无内容可复制',
copySuccess: '复制成功',
copyFailed: '复制失败',
// ==== 新增:获取国家失败弹窗 ====
pleaseEnterCountryName: '请输入要获取的国家',
getCountryFailed: '获取国家失败',
// ==== 新增loading 提示 ====
stopping: '正在停止...',
starting: '正在启动...',
// ==== 新增单个直播间id校验提示 ====
enterRoomId: '请输入直播间id',
},
// ==== 新增:国家名称国际化 ====
countries: {
AD: "安道尔",
@@ -343,5 +345,5 @@ export default {
ZM: "赞比亚",
ZW: "津巴布韦"
}
}

View File

@@ -1,123 +1,125 @@
<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>{{ $t('common.networkSettings') }}</span>
</div>
<!-- <div class="setup-item center-justify"> -->
<div class="">
<el-select
v-model="currentLanguage"
:placeholder="$t('common.language')"
@change="changeLanguage"
size="small"
style="width: 120px;"
>
<el-option
:label="$t('common.simplifiedChinese')"
value="zh-CN"
/>
<el-option
:label="$t('common.english')"
value="en-US"
/>
</el-select>
</div>
</div>
<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">{{ $t('common.networkSettings') }}</span>
</span>
</el-dropdown>
</div>
<div class="center-line" style="margin-top: 40px">
<!-- logo -->
<div class="logo">
<!-- <div class="center-justify" style="height: 80px; width: 300px;">
<img style="height: 100%;" src="@/assets/logotext.png">
</div> -->
<!-- 语言选择 el-select但外观套进 setup-item -->
<div class="setup-item setup-item--dropdown">
<el-dropdown trigger="click" @command="changeLanguage">
<span class="lang-dropdown">
<span class="lang-dropdown__label">{{ currentLanguageLabel }}</span>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="zh-CN">
{{ $t('common.simplifiedChinese') }}
</el-dropdown-item>
<el-dropdown-item command="en-US">
{{ $t('common.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">
<!-- 你原本 logo 是空的这里给你留一个位 -->
<!-- 想放logo就取消注释 -->
<img class="logo-image" src="@/assets/logo2.png" alt="" />
<span class="logo-title">{{ $t('common.assistant') }}</span>
</div>
<!-- From -->
<div class="from">
<div class="from-title center-justify">
<div>{{ $t('common.accountLogin') }}</div>
</div>
<div class="welcome">{{ $t('common.accountLogin') }}</div>
<div class="from">
<div class="from-input">
<el-form label-position="left" label-width="100px" :model="formData">
<div class="field-label">{{ $t('common.tenantName') }}</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.tenantName"
:placeholder="$t('common.tenantName')"
clearable
@keyup.enter="onSubmit"
/>
<el-input style="height: 25px" v-model="formData.tenantName" :placeholder="$t('common.tenantName')"
clearable @keyup.enter="onSubmit" />
</div>
<div class="field-label">{{ $t('common.account') }}</div>
<div class="from-input-item1">
<img src="@/assets/username.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.userId"
:placeholder="$t('common.account')"
clearable
@keyup.enter="onSubmit"
/>
<el-input style="height: 25px" v-model="formData.userId" :placeholder="$t('common.account')" clearable
@keyup.enter="onSubmit" />
</div>
<div class="field-label">{{ $t('common.password') }}</div>
<div class="from-input-item1">
<img src="@/assets/password.png" alt="" />
<el-input
style="height: 25px"
v-model="formData.password"
type="password"
:placeholder="$t('common.password')"
show-password
@keyup.enter="onSubmit"
/>
<el-input style="height: 25px" v-model="formData.password" type="password"
:placeholder="$t('common.password')" show-password @keyup.enter="onSubmit" />
</div>
<div class="from-input-item">
<el-button
class="loginButton"
color="#8f7ee7"
type="primary"
@click="onSubmit"
>{{ $t('common.login') }}</el-button
>
<el-button class="loginButton" type="primary" :loading="isSubmitting" @click="onSubmit">
{{ $t('common.login') }}
</el-button>
</div>
</el-form>
</div>
</div>
<div class="version">{{ $t('common.version') }}{{ version }}</div>
</div>
<!-- 右侧插画 -->
<div class="auth-right">
<img src="../assets/logoBg2.png" class="illustration" alt="" />
</div>
<div class="version center-justify">{{ $t('common.version') }}{{ version }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ref, onMounted, computed } from "vue";
import { useRouter } from "vue-router";
import { useI18n } from 'vue-i18n';
import { useI18n } from "vue-i18n";
import { login, rentgetloginID } from "@/api/account";
import { getToken, setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
import { ElLoading } from "element-plus";
import { setToken, setUser, setUserPass, getUserPass } from "@/utils/storage";
import { usePythonBridge } from "@/utils/pythonBridge";
import { ElMessage } from 'element-plus';
import { tokenStore,UserStore } from '@/stores/notice'
import { tokenStore, UserStore } from "@/stores/notice";
const router = useRouter();
const { locale, t } = useI18n();
const tokenCache = tokenStore();
const userCache = UserStore();
const { t, locale } = useI18n();
const tokenCache = tokenStore()
const userCache = UserStore()
const { getVersion } = usePythonBridge();
let version = ref("0.0.0");
// 当前语言
const currentLanguage = ref(localStorage.getItem('language') || 'zh-CN');
const currentLanguage = ref(localStorage.getItem("language") || "zh-CN");
const currentLanguageLabel = computed(() =>
currentLanguage.value === "zh-CN"
? t("common.simplifiedChinese")
: t("common.english")
);
const formData = ref({
tenantName: "",
userId: "",
password: "",
});
onMounted(() => {
setTimeout(() => {
@@ -128,217 +130,73 @@ onMounted(() => {
}, 500);
});
async function getpassword(){
const res = await getUserPass();
formData.value = {
tenantName: res == null ? "" : res.tenantName,
userId: res == null ? "" : res.userId,
password: res == null ? "" : res.password,
};
async function getpassword() {
const res = await getUserPass();
formData.value = {
tenantName: res == null ? "" : res.tenantName,
userId: res == null ? "" : res.userId,
password: res == null ? "" : res.password,
};
}
// 切换语言
const changeLanguage = (lang) => {
locale.value = lang;
currentLanguage.value = lang;
localStorage.setItem('language', lang);
// 重新加载页面以应用Element Plus语言包
localStorage.setItem("language", lang);
window.location.reload();
};
const router = useRouter();
const formData = ref({});
const isSubmitting = ref(false);
const onSubmit = () => {
const loading = ElLoading.service({
lock: true,
text: "Loading",
background: "rgba(0, 0, 0, 0.7)",
});
rentgetloginID({
name: formData.value.tenantName,
})
.then((res) => {
console.log(res);
if (isSubmitting.value) return;
isSubmitting.value = true;
rentgetloginID({ name: formData.value.tenantName })
.then((tenantId) => {
login({
username: formData.value.userId,
tenantId: res,
tenantId: tenantId,
password: formData.value.password,
})
.then((res) => {
loading.close();
console.log("123123123123",res);
isSubmitting.value = false;
setToken(res.tokenValue);
tokenCache.setToken(res.tokenValue)
userCache.setUser(res)
tokenCache.setToken(res.tokenValue);
userCache.setUser(res);
setUser(res);
setUserPass(formData.value);
router.push("/nav");
})
.catch((err) => {
loading.close();
.catch(() => {
isSubmitting.value = false;
});
})
.catch((err) => {
loading.close();
isSubmitting.value = false;
console.log(err);
});
};
</script>
<style lang="less">
/* ✅ 全屏自适应 */
.main {
width: 1600px;
height: 900px;
width: 100vw;
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: 1600px;
height: 900px;
.right {
box-sizing: border-box;
position: relative;
width: 1600px;
height: 900px;
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;
align-items: center;
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;
height: 80px;
}
.from {
width: 420px;
// height: 320px;
color: @bg-color;
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: @bg-color;
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: @bg-color;
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: @bg-color;
line-height: 37px;
}
}
.from-input-item1 {
display: flex;
width: 359px;
height: 50px;
background: @bg-color-light-light;
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;
}
.center-justify {
@@ -347,17 +205,268 @@ const onSubmit = () => {
align-items: center;
}
.center-align {
/* 外壳:自适应 */
.auth-shell {
width: min(1320px, 92vw);
height: min(760px, 86vh);
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-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-text {
font-size: 13px;
font-weight: 600;
color: #2b3347;
letter-spacing: 0.2px;
}
/* el-select 这块不要小箭头重复setup-item 自带 after 箭头,所以把 after 去掉 */
.setup-item--select::after {
display: none;
}
/* 主卡片 */
.auth-card {
width: min(1200px, 92vw);
height: min(680px, 80vh);
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: clamp(20px, 3vw, 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-bottom: -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: 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;
}
/* 输入框胶囊 */
.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;
}
/* 按钮同款 */
.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);
}
.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));
}
/* 小屏:上下 */
@media (max-width: 900px) {
.auth-shell {
width: 94vw;
height: auto;
padding: 16px 0;
}
.top-actions {
top: 10px;
right: 10px;
}
.auth-card {
flex-direction: column;
height: auto;
}
.auth-left,
.auth-right {
width: 100%;
}
.auth-right {
min-height: 240px;
}
}
/* element-plus 变量 */
.el-input__wrapper {
--el-input-focus-border-color: rgba(255, 255, 0, 0);
--el-menu-hover-bg-color: rgba(255, 255, 0, 0);
@@ -365,16 +474,54 @@ const onSubmit = () => {
</style>
<style scoped lang="less">
/* 输入框内层去默认阴影 */
::v-deep(.el-input__wrapper) {
background-color: rgba(255, 0, 0, 0);
background-color: transparent;
box-shadow: none;
}
/* 输入文字/placeholder 同款深色 */
::v-deep(.el-input__inner) {
color: #fff;
color: #2b3347;
}
::v-deep(.el-input__inner::placeholder) {
color: @bg-color;
color: #9aa1b3;
}
/* 语言下拉:去边框、透明底,融入 setup-item */
::v-deep(.lang-select .el-select__wrapper) {
background: transparent;
box-shadow: none;
border: none;
padding: 0 8px;
min-height: 24px;
/* 或者 height: 24px */
}
::v-deep(.lang-select .el-select__selected-item) {
color: #2b3347;
font-weight: 600;
font-size: 13px;
line-height: 1.2;
}
::v-deep(.lang-select .el-select__caret) {
color: #9aa1b3;
}
.lang-dropdown {
display: inline-flex;
align-items: center;
padding: 0 8px;
min-height: 24px;
min-width: 80px;
}
.lang-dropdown__label {
color: #2b3347;
font-weight: 600;
font-size: 13px;
line-height: 1.2;
}
</style>