feat: 登录页面布局

This commit is contained in:
duanshuwen
2025-11-27 22:12:03 +08:00
parent 62efe40359
commit aaed2abfd8
9 changed files with 77 additions and 83 deletions

56
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@remixicon/vue": "^4.7.0",
"axios": "^1.12.2", "axios": "^1.12.2",
"bytenode": "^1.5.7", "bytenode": "^1.5.7",
"crypto": "^1.0.1", "crypto": "^1.0.1",
@@ -29,9 +30,6 @@
"@electron-forge/plugin-fuses": "^7.10.2", "@electron-forge/plugin-fuses": "^7.10.2",
"@electron-forge/plugin-vite": "^7.10.2", "@electron-forge/plugin-vite": "^7.10.2",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@iconify-json/ri": "^1.2.6",
"@iconify-json/tdesign": "^1.2.10",
"@iconify/vue": "^5.0.0",
"@tailwindcss/postcss": "^4.1.14", "@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/vite": "^4.0.0", "@tailwindcss/vite": "^4.0.0",
"@types/electron-squirrel-startup": "^1.0.2", "@types/electron-squirrel-startup": "^1.0.2",
@@ -1487,49 +1485,6 @@
"dev": true, "dev": true,
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/@iconify-json/ri": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@iconify-json/ri/-/ri-1.2.6.tgz",
"integrity": "sha512-tGXRmXtb8oFu8DNg9MsS1pywKFgs9QZ4U6LBzUamBHaw3ePSiPd7ouE64gzHzfEcR16hgVaXoUa+XxD3BB0XOg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/tdesign": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@iconify-json/tdesign/-/tdesign-1.2.10.tgz",
"integrity": "sha512-qt96LW9buYHGjRZsXla5SVFwDX9d11K0iEjgYoNm2EfL9Rh6yGvCN5h9XnejAK4mvmpI/Q1JM+IEyXXlBhigkg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
"dev": true,
"license": "MIT"
},
"node_modules/@iconify/vue": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-5.0.0.tgz",
"integrity": "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@iconify/types": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/cyberalien"
},
"peerDependencies": {
"vue": ">=3"
}
},
"node_modules/@inquirer/checkbox": { "node_modules/@inquirer/checkbox": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-3.0.1.tgz",
@@ -1930,6 +1885,15 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/@remixicon/vue": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@remixicon/vue/-/vue-4.7.0.tgz",
"integrity": "sha512-kwl1BIEnNoZ5IZ4NJZPOQmA2PfxKlOmogWcJBStuDynoyYpsYSvU22ZkerpdofgjsLS/FiLKhJ/ddpP8U8znVw==",
"license": "Apache-2.0",
"peerDependencies": {
"vue": ">= 3"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.53.3", "version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",

View File

@@ -30,9 +30,6 @@
"@electron-forge/plugin-fuses": "^7.10.2", "@electron-forge/plugin-fuses": "^7.10.2",
"@electron-forge/plugin-vite": "^7.10.2", "@electron-forge/plugin-vite": "^7.10.2",
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@iconify-json/ri": "^1.2.6",
"@iconify-json/tdesign": "^1.2.10",
"@iconify/vue": "^5.0.0",
"@tailwindcss/postcss": "^4.1.14", "@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/vite": "^4.0.0", "@tailwindcss/vite": "^4.0.0",
"@types/electron-squirrel-startup": "^1.0.2", "@types/electron-squirrel-startup": "^1.0.2",
@@ -52,6 +49,7 @@
"vue-eslint-parser": "^10.2.0" "vue-eslint-parser": "^10.2.0"
}, },
"dependencies": { "dependencies": {
"@remixicon/vue": "^4.7.0",
"axios": "^1.12.2", "axios": "^1.12.2",
"bytenode": "^1.5.7", "bytenode": "^1.5.7",
"crypto": "^1.0.1", "crypto": "^1.0.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -24,8 +24,8 @@ class AppMain {
private createWindow(options?: { frameless?: boolean; route?: string }): BrowserWindow { private createWindow(options?: { frameless?: boolean; route?: string }): BrowserWindow {
const frameless = !!options?.frameless const frameless = !!options?.frameless
const win = new BrowserWindow({ const win = new BrowserWindow({
width: 900, width: 1440,
height: 670, height: 900,
autoHideMenuBar: true, autoHideMenuBar: true,
frame: frameless ? false : true, frame: frameless ? false : true,
// @ts-ignore // @ts-ignore

View File

@@ -3,16 +3,10 @@ import { createApp } from "vue";
import { createPinia } from "pinia"; import { createPinia } from "pinia";
import router from "./router"; import router from "./router";
import App from "./App.vue"; import App from "./App.vue";
import { addCollection } from '@iconify/vue'
import { icons as tdesignIcons } from '@iconify-json/tdesign'
import { icons as riIcons } from '@iconify-json/ri'
// 创建 Vue 应用实例 // 创建 Vue 应用实例
const app = createApp(App); const app = createApp(App);
addCollection(tdesignIcons)
addCollection(riIcons)
// 使用 Pinia 状态管理 // 使用 Pinia 状态管理
app.use(createPinia()); app.use(createPinia());

View File

@@ -1,23 +1,27 @@
<template> <template>
<div <div
class="min-h-screen p-[8px] login-bg" class="h-screen box-border p-[8px] login-bg flex items-center justify-center"
> >
<div class="w-full h-full max-w-md bg-white rounded-2xl shadow-xl p-8"> <div class="w-[836px] h-full bg-white rounded-2xl p-[32px] flex flex-col">
<div class="flex items-center"> <div class="flex items-center">
<span class="w-[48px] h-[48px] rounded-2xl bg-blue-500 box-border p-[12px] text-white flex items-center justify-items-center text-[24px] font-bold"> <img class="w-[48px] h-[48px]" src="@assets/images/login/blue_logo.png" />
N
</span>
<span class="ml-auto text-[14px] text-gray-600">没有账号</span> <span class="ml-auto text-[14px] text-gray-600">没有账号</span>
<button class="bg-sky-50 rounded-[8px] text-[14px] text-sky-600 px-[12px] py-[6px]">注册</button> <button class="bg-sky-50 rounded-[8px] text-[14px] text-sky-600 px-[12px] py-[6px] focus-visible:outline-none">注册</button>
</div> </div>
<div class="space-y-6"> <div class="flex flex-col items-center justify-center mb-[24px] box-border pt-[108px]">
<div> <img class="w-[80px] h-[80px] mb-[12px]" src="@assets/images/login/user_icon.png" />
<label class="block text-sm font-medium text-gray-700 mb-2">账号</label> <div class="text-[24px] font-500 text-gray-800 line-height-[32px] mb-[4px]">登录</div>
<div class="border rounded flex items-center focus:outline-none focus:ring-2 focus:ring-indigo-400 gap-2"> <div class="text-[16px] text-gray-500 line-height-[24px]">24小时在岗从不打烊的数字员工</div>
<Icon icon="tdesign:user-1-filled" :width="20" :height="20" /> </div>
<div class="w-[392px] ml-auto mr-auto">
<div class="font-[14px] text-gray-700 mb-2">账号</div>
<div class="border rounded-[10px] flex items-center gap-2 box-border px-[12px] py-[10px]">
<RiUser3Fill size="20px" color="#99A0AE" />
<input <input
class="flex-1 px-3 py-2" class="flex-1 focus-visible:outline-none"
type="text" type="text"
v-model.trim="form.account" v-model.trim="form.account"
placeholder="请输入账号" placeholder="请输入账号"
@@ -25,13 +29,11 @@
/> />
</div> </div>
<p v-if="errors.account" class="mt-1 text-xs text-red-500">{{ errors.account }}</p> <p v-if="errors.account" class="mt-1 text-xs text-red-500">{{ errors.account }}</p>
</div> <div class="font-[14px] text-gray-700 mb-[8px] mt-[12px]">密码</div>
<div> <div class="flex items-center gap-2 border rounded-[10px] box-border px-[12px] py-[10px]">
<label class="block text-sm font-medium text-gray-700 mb-2">密码</label> <RiKey2Fill size="20px" color="#99A0AE" />
<div class="flex items-center gap-2 border rounded focus:outline-none focus:ring-2 focus:ring-indigo-400">
<Icon icon="ri:key-2-fill" :width="20" :height="20" />
<input <input
class="flex-1 px-3 py-2 " class="flex-1 focus-visible:outline-none"
:type="showPwd ? 'text' : 'password'" :type="showPwd ? 'text' : 'password'"
v-model.trim="form.password" v-model.trim="form.password"
placeholder="请输入密码" placeholder="请输入密码"
@@ -39,13 +41,49 @@
/> />
</div> </div>
<p v-if="errors.password" class="mt-1 text-xs text-red-500">{{ errors.password }}</p> <p v-if="errors.password" class="mt-1 text-xs text-red-500">{{ errors.password }}</p>
<!-- 记住密码|忘记密码 -->
<div class="flex items-center justify-between mb-[24px] mt-[24px]">
<div class="flex items-center gap-2">
<input
type="checkbox"
v-model="showPwd"
class="w-[14px] h-[14px] rounded-[4px]"
/>
<span class="text-[14px] text-gray-600">记住密码</span>
</div> </div>
<span class="text-[14px] text-sky-600">忘记密码</span>
</div>
<!-- 登录按钮 -->
<button <button
class="w-full py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 disabled:bg-indigo-300" class="w-full py-2 bg-blue-600 text-white rounded-[8px] hover:bg-blue-700 disabled:bg-blue-300"
@click="onSubmit" @click="onSubmit"
>{{ loading ? '登录中…' : '登录' }}</button> >
{{ loading ? '登录中…' : '登录' }}
</button>
<!-- 同意协议 -->
<div class="flex items-center justify-center gap-2 mt-[24px]">
<input
type="checkbox"
v-model="form.agreement"
class="w-[14px] h-[14px] rounded-[4px]"
/>
<span class="text-[14px] text-gray-600">我已同意</span>
<span class="text-[14px] text-sky-600">使用协议</span>
<span class="text-[14px] text-gray-600"></span>
<span class="text-[14px] text-sky-600">隐私协议</span>
</div> </div>
</div> </div>
<!-- Copy Right -->
<div class="text-[14px] text-gray-500 text-center mt-[24px] mt-auto">
© 2025 智能体智能科技有限公司 版权所有
</div>
</div>
<img class="w-[570px]" src="@assets/images/login/logo.png" />
</div> </div>
</template> </template>
@@ -53,10 +91,10 @@
import { ref, reactive } from "vue"; import { ref, reactive } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { login as apiLogin } from "@/renderer/api/login"; import { login as apiLogin } from "@/renderer/api/login";
import { Icon } from '@iconify/vue'; import { RiUser3Fill , RiKey2Fill} from '@remixicon/vue'
const router = useRouter(); const router = useRouter();
const form = reactive({ account: "", password: "" }); const form = reactive({ account: "", password: "", agreement: "" });
const errors = reactive<{ account?: string; password?: string }>({}); const errors = reactive<{ account?: string; password?: string }>({});
const loading = ref(false); const loading = ref(false);
const showPwd = ref(false); const showPwd = ref(false);