xto-fronted 0.4.72 → 0.4.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/assets/404-CrBiLyvr.js +1 -0
  2. package/dist/assets/404-Cw_4ZCL6.css +1 -0
  3. package/dist/assets/_plugin-vue_export-helper-DlAUqK2U.js +1 -0
  4. package/dist/assets/index-9XqWtCGs.js +1 -0
  5. package/dist/assets/index-BBGKF0l-.js +1 -0
  6. package/dist/assets/index-BRR97dc6.js +1 -0
  7. package/dist/assets/index-BlRslYYI.css +1 -0
  8. package/dist/assets/index-BudArKxR.css +1 -0
  9. package/dist/assets/index-CFhWBbxk.css +1 -0
  10. package/dist/assets/index-CRJFI4iz.js +2 -0
  11. package/dist/assets/index-CmknWHK0.js +1 -0
  12. package/dist/assets/index-D7epoF1g.js +1 -0
  13. package/dist/assets/index-D9XBi20Y.css +1 -0
  14. package/dist/assets/index-DkkuYBgT.css +1 -0
  15. package/dist/assets/index-O9wpAB3Q.js +1 -0
  16. package/dist/assets/index-_aYxnqzF.css +1 -0
  17. package/dist/assets/index-rfC7s9zA.js +1 -0
  18. package/dist/assets/index-vfvEFrCH.css +1 -0
  19. package/dist/assets/vendor-42ANG6Sg.js +6 -0
  20. package/dist/assets/vue-vendor-D8SAgT8u.js +29 -0
  21. package/dist/assets/xto-base-BD3OH0V6.js +1 -0
  22. package/dist/assets/xto-base-CojW9IFO.css +1 -0
  23. package/dist/assets/xto-business--V1F5Gwb.css +1 -0
  24. package/dist/assets/xto-core-Boim7B0B.js +1 -0
  25. package/dist/assets/xto-data-CnAQAQH2.css +1 -0
  26. package/dist/assets/xto-data-DuG2QJrr.js +1 -0
  27. package/dist/assets/xto-feedback-C-ESp-Y1.css +1 -0
  28. package/dist/assets/xto-feedback-XZPSQcJN.js +1 -0
  29. package/dist/assets/xto-form-BmTGLIm_.js +1 -0
  30. package/dist/assets/xto-form-CrsyAjyr.css +1 -0
  31. package/dist/assets/xto-layout-D1stVnJI.css +1 -0
  32. package/dist/assets/xto-navigation-BRzSCpAw.css +1 -0
  33. package/dist/assets/xto-navigation-nPmjRi-J.js +1 -0
  34. package/dist/index.html +28 -0
  35. package/package.json +1 -1
  36. package/src/App.vue +30 -1
  37. package/src/assets/styles/_root.scss +141 -97
  38. package/src/assets/styles/_variables.scss +44 -19
  39. package/src/assets/styles/index.scss +267 -42
  40. package/src/views/dashboard/index.vue +417 -155
  41. package/src/views/error/403.vue +251 -56
  42. package/src/views/error/404.vue +253 -56
  43. package/src/views/login/index.vue +586 -194
  44. package/src/views/system/menu/index.vue +403 -94
  45. package/src/views/system/role/index.vue +348 -69
  46. package/src/views/system/user/index.vue +402 -73
  47. package/dist/App.vue.d.ts +0 -2
  48. package/dist/api/auth.d.ts +0 -8
  49. package/dist/api/system.d.ts +0 -16
  50. package/dist/api/user.d.ts +0 -13
  51. package/dist/components/Layout/Footer.vue.d.ts +0 -2
  52. package/dist/components/Layout/Header.vue.d.ts +0 -5
  53. package/dist/components/Layout/MixTopMenu.vue.d.ts +0 -5
  54. package/dist/components/Layout/Sidebar.vue.d.ts +0 -11
  55. package/dist/components/Layout/SidebarMenuItem.vue.d.ts +0 -5
  56. package/dist/components/Layout/Tabs.vue.d.ts +0 -2
  57. package/dist/components/Layout/TopMenu.vue.d.ts +0 -5
  58. package/dist/components/Layout/index.vue.d.ts +0 -2
  59. package/dist/composables/useApp.d.ts +0 -29
  60. package/dist/composables/useAuth.d.ts +0 -6
  61. package/dist/composables/useForm.d.ts +0 -20
  62. package/dist/composables/useTable.d.ts +0 -29
  63. package/dist/directives/permission.d.ts +0 -4
  64. package/dist/enums/index.d.ts +0 -32
  65. package/dist/index-48G2-eqi.js +0 -475
  66. package/dist/index-BhtcPIRK.js +0 -3169
  67. package/dist/index-CZKP8fOP.js +0 -372
  68. package/dist/index-JouxgO84.js +0 -142
  69. package/dist/index-qlKXxfT9.js +0 -345
  70. package/dist/index.d.ts +0 -54
  71. package/dist/index.es.js +0 -91
  72. package/dist/index.umd.js +0 -1
  73. package/dist/main.d.ts +0 -0
  74. package/dist/router/dynamicRoutes.d.ts +0 -30
  75. package/dist/router/guards.d.ts +0 -17
  76. package/dist/router/index.d.ts +0 -6
  77. package/dist/router/layoutRoute.d.ts +0 -18
  78. package/dist/router/staticRoutes.d.ts +0 -2
  79. package/dist/stores/app.d.ts +0 -93
  80. package/dist/stores/auth.d.ts +0 -41
  81. package/dist/stores/index.d.ts +0 -9
  82. package/dist/stores/menu.d.ts +0 -77
  83. package/dist/stores/user.d.ts +0 -92
  84. package/dist/style.css +0 -1
  85. package/dist/utils/auth.d.ts +0 -27
  86. package/dist/utils/config.d.ts +0 -30
  87. package/dist/utils/permission.d.ts +0 -18
  88. package/dist/utils/request.d.ts +0 -24
  89. package/dist/utils/storage.d.ts +0 -24
  90. package/dist/views/dashboard/index.vue.d.ts +0 -2
  91. package/dist/views/error/403.vue.d.ts +0 -2
  92. package/dist/views/error/404.vue.d.ts +0 -2
  93. package/dist/views/login/index.vue.d.ts +0 -4
  94. package/dist/views/system/menu/index.vue.d.ts +0 -4
  95. package/dist/views/system/role/index.vue.d.ts +0 -4
  96. package/dist/views/system/user/index.vue.d.ts +0 -4
@@ -1,195 +1,587 @@
1
- <script setup lang="ts">
2
- import { ref, reactive } from 'vue'
3
- import { useRouter, useRoute } from 'vue-router'
4
- import { Button, Icon } from '@xto/base'
5
- import { Form, FormItem, Input, Checkbox } from '@xto/form'
6
- import { Message } from '@xto/feedback'
7
- import { login } from '@/api/auth'
8
- import { setTokenInfo } from '@/utils/auth'
9
- import { getAppId, getClientId } from '@/utils/config'
10
-
11
- const router = useRouter()
12
- const route = useRoute()
13
-
14
- const loading = ref(false)
15
- const rememberMe = ref(false)
16
-
17
- const formData = reactive({
18
- uid: '',
19
- password: ''
20
- })
21
-
22
- const rules: Record<string, any[]> = {
23
- uid: [
24
- { required: true, message: '请输入用户名', trigger: 'blur' }
25
- ],
26
- password: [
27
- { required: true, message: '请输入密码', trigger: 'blur' },
28
- { min: 6, message: '密码长度至少6位', trigger: 'blur' }
29
- ]
30
- }
31
-
32
- const formRef = ref()
33
-
34
- // 登录
35
- const handleLogin = async () => {
36
- try {
37
- await formRef.value?.validate()
38
- loading.value = true
39
-
40
- // 调用登录 API
41
- const result = await login({
42
- appId: getAppId(),
43
- clientId: getClientId(),
44
- uid: formData.uid,
45
- password: formData.password,
46
- code: true
47
- })
48
-
49
- // 保存 token
50
- setTokenInfo(result)
51
-
52
- Message.success('登录成功')
53
-
54
- // 获取重定向地址
55
- const redirect = route.query.redirect as string || '/'
56
-
57
- // 跳转到目标页面(路由守卫会自动获取用户信息和菜单)
58
- router.push(redirect)
59
- } catch (error) {
60
- console.error('登录失败:', error)
61
- } finally {
62
- loading.value = false
63
- }
64
- }
65
- </script>
66
-
67
- <template>
68
- <div class="login">
69
- <div class="login__container">
70
- <div class="login__header">
71
- <img src="/vite.svg" alt="Logo" class="login__logo" />
72
- <h1 class="login__title">Xto Demo</h1>
73
- <p class="login__subtitle">后台管理系统</p>
74
- </div>
75
-
76
- <Form
77
- ref="formRef"
78
- :model="formData"
79
- :rules="rules"
80
- class="login__form"
81
- label-width="0"
82
- >
83
- <FormItem prop="uid">
84
- <Input
85
- v-model="formData.uid"
86
- placeholder="用户名"
87
- size="large"
88
- >
89
- <template #prefix>
90
- <Icon name="user" :size="18" />
91
- </template>
92
- </Input>
93
- </FormItem>
94
-
95
- <FormItem prop="password">
96
- <Input
97
- v-model="formData.password"
98
- type="password"
99
- placeholder="密码"
100
- size="large"
101
- show-password
102
- @keyup.enter="handleLogin"
103
- >
104
- <template #prefix>
105
- <Icon name="lock" :size="18" />
106
- </template>
107
- </Input>
108
- </FormItem>
109
-
110
- <FormItem>
111
- <Checkbox v-model="rememberMe">记住我</Checkbox>
112
- </FormItem>
113
-
114
- <FormItem>
115
- <Button
116
- type="primary"
117
- size="large"
118
- :loading="loading"
119
- class="login__submit"
120
- @click="handleLogin"
121
- >
122
- 登录
123
- </Button>
124
- </FormItem>
125
- </Form>
126
-
127
- <div class="login__footer">
128
- <p>请输入您的用户名和密码</p>
129
- </div>
130
- </div>
131
- </div>
132
- </template>
133
-
134
- <style lang="scss" scoped>
135
- .login {
136
- width: 100%;
137
- min-height: 100vh;
138
- display: flex;
139
- align-items: center;
140
- justify-content: flex-end;
141
- padding-right: 15%;
142
- background: linear-gradient(135deg, var(--color-primary-light-9) 0%, var(--color-primary-light-7) 100%);
143
-
144
- &__container {
145
- width: 400px;
146
- padding: 40px;
147
- background-color: var(--bg-color);
148
- border-radius: var(--border-radius-large);
149
- box-shadow: var(--box-shadow-dark);
150
- }
151
-
152
- &__header {
153
- text-align: center;
154
- margin-bottom: 30px;
155
- }
156
-
157
- &__logo {
158
- width: 60px;
159
- height: 60px;
160
- }
161
-
162
- &__title {
163
- font-size: 28px;
164
- font-weight: 600;
165
- color: var(--color-primary);
166
- margin: 15px 0 5px;
167
- }
168
-
169
- &__subtitle {
170
- font-size: 14px;
171
- color: var(--color-text-secondary);
172
- }
173
-
174
- &__form {
175
- :deep(.x-form-item) {
176
- margin-bottom: 20px;
177
- }
178
-
179
- :deep(.x-input__prefix) {
180
- margin-right: 8px;
181
- }
182
- }
183
-
184
- &__submit {
185
- width: 100%;
186
- }
187
-
188
- &__footer {
189
- text-align: center;
190
- margin-top: 20px;
191
- font-size: 12px;
192
- color: var(--color-text-placeholder);
193
- }
194
- }
1
+ <script setup lang="ts">
2
+ import { ref, reactive } from 'vue'
3
+ import { useRouter, useRoute } from 'vue-router'
4
+ import { Button, Icon } from '@xto/base'
5
+ import { Form, FormItem, Input, Checkbox } from '@xto/form'
6
+ import { Message } from '@xto/feedback'
7
+ import { login } from '@/api/auth'
8
+ import { setTokenInfo } from '@/utils/auth'
9
+ import { getAppId, getClientId } from '@/utils/config'
10
+
11
+ const router = useRouter()
12
+ const route = useRoute()
13
+
14
+ const loading = ref(false)
15
+ const rememberMe = ref(false)
16
+
17
+ const formData = reactive({
18
+ uid: '',
19
+ password: ''
20
+ })
21
+
22
+ const rules: Record<string, any[]> = {
23
+ uid: [
24
+ { required: true, message: '请输入用户名', trigger: 'blur' }
25
+ ],
26
+ password: [
27
+ { required: true, message: '请输入密码', trigger: 'blur' },
28
+ { min: 6, message: '密码长度至少6位', trigger: 'blur' }
29
+ ]
30
+ }
31
+
32
+ const formRef = ref()
33
+
34
+ // 登录
35
+ const handleLogin = async () => {
36
+ try {
37
+ await formRef.value?.validate()
38
+ loading.value = true
39
+
40
+ // 调用登录 API
41
+ const result = await login({
42
+ appId: getAppId(),
43
+ clientId: getClientId(),
44
+ uid: formData.uid,
45
+ password: formData.password,
46
+ code: true
47
+ })
48
+
49
+ // 保存 token
50
+ setTokenInfo(result)
51
+
52
+ Message.success('登录成功')
53
+
54
+ // 获取重定向地址
55
+ const redirect = route.query.redirect as string || '/'
56
+
57
+ // 跳转到目标页面(路由守卫会自动获取用户信息和菜单)
58
+ router.push(redirect)
59
+ } catch (error) {
60
+ console.error('登录失败:', error)
61
+ } finally {
62
+ loading.value = false
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <template>
68
+ <div class="login-page">
69
+ <!-- 左侧品牌区域 -->
70
+ <div class="login-brand">
71
+ <div class="brand-content">
72
+ <!-- Logo -->
73
+ <div class="brand-logo">
74
+ <div class="logo-icon">
75
+ <svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
76
+ <rect width="48" height="48" rx="12" fill="currentColor"/>
77
+ <path d="M14 24L20 30L34 16" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
78
+ </svg>
79
+ </div>
80
+ <span class="logo-text">XTO</span>
81
+ </div>
82
+
83
+ <!-- 标语 -->
84
+ <div class="brand-slogan">
85
+ <h1>企业级后台管理解决方案</h1>
86
+ <p>开箱即用的中后台前端/设计解决方案</p>
87
+ </div>
88
+
89
+ <!-- 特性列表 -->
90
+ <div class="brand-features">
91
+ <div class="feature-item">
92
+ <div class="feature-icon">
93
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
94
+ <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
95
+ </svg>
96
+ </div>
97
+ <div class="feature-text">
98
+ <h4>高性能</h4>
99
+ <p>极致的渲染性能与响应速度</p>
100
+ </div>
101
+ </div>
102
+ <div class="feature-item">
103
+ <div class="feature-icon">
104
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
105
+ <rect x="3" y="3" width="18" height="18" rx="2"/>
106
+ <path d="M9 9h6M9 12h6M9 15h4"/>
107
+ </svg>
108
+ </div>
109
+ <div class="feature-text">
110
+ <h4>丰富组件</h4>
111
+ <p>60+ 高质量业务组件</p>
112
+ </div>
113
+ </div>
114
+ <div class="feature-item">
115
+ <div class="feature-icon">
116
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
117
+ <circle cx="12" cy="12" r="10"/>
118
+ <path d="M12 6v6l4 2"/>
119
+ </svg>
120
+ </div>
121
+ <div class="feature-text">
122
+ <h4>持续更新</h4>
123
+ <p>活跃的社区与快速迭代</p>
124
+ </div>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- 底部装饰 -->
129
+ <div class="brand-decoration">
130
+ <div class="decoration-line"></div>
131
+ <div class="decoration-dots">
132
+ <span></span><span></span><span></span>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <!-- 背景装饰 -->
138
+ <div class="brand-bg">
139
+ <div class="bg-grid"></div>
140
+ <div class="bg-glow"></div>
141
+ <div class="bg-pattern"></div>
142
+ </div>
143
+ </div>
144
+
145
+ <!-- 右侧登录区域 -->
146
+ <div class="login-form-section">
147
+ <div class="form-container">
148
+ <!-- 头部 -->
149
+ <div class="form-header">
150
+ <h2>欢迎登录</h2>
151
+ <p>请输入您的账户信息</p>
152
+ </div>
153
+
154
+ <!-- 表单 -->
155
+ <Form
156
+ ref="formRef"
157
+ :model="formData"
158
+ :rules="rules"
159
+ class="login-form"
160
+ label-width="0"
161
+ >
162
+ <FormItem prop="uid">
163
+ <div class="input-wrapper">
164
+ <label class="input-label">用户名</label>
165
+ <Input
166
+ v-model="formData.uid"
167
+ placeholder="请输入用户名"
168
+ size="large"
169
+ >
170
+ <template #prefix>
171
+ <Icon name="user" :size="18" />
172
+ </template>
173
+ </Input>
174
+ </div>
175
+ </FormItem>
176
+
177
+ <FormItem prop="password">
178
+ <div class="input-wrapper">
179
+ <label class="input-label">密码</label>
180
+ <Input
181
+ v-model="formData.password"
182
+ type="password"
183
+ placeholder="请输入密码"
184
+ size="large"
185
+ show-password
186
+ @keyup.enter="handleLogin"
187
+ >
188
+ <template #prefix>
189
+ <Icon name="lock" :size="18" />
190
+ </template>
191
+ </Input>
192
+ </div>
193
+ </FormItem>
194
+
195
+ <div class="form-options">
196
+ <Checkbox v-model="rememberMe">记住我</Checkbox>
197
+ <a href="#" class="forgot-link">忘记密码?</a>
198
+ </div>
199
+
200
+ <FormItem>
201
+ <Button
202
+ type="primary"
203
+ size="large"
204
+ :loading="loading"
205
+ class="login-btn"
206
+ @click="handleLogin"
207
+ >
208
+ <span v-if="!loading">登 录</span>
209
+ <span v-else>登录中...</span>
210
+ </Button>
211
+ </FormItem>
212
+ </Form>
213
+
214
+ <!-- 底部 -->
215
+ <div class="form-footer">
216
+ <p>其他登录方式</p>
217
+ <div class="social-login">
218
+ <button class="social-btn" title="企业微信">
219
+ <svg viewBox="0 0 24 24" fill="currentColor">
220
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
221
+ </svg>
222
+ </button>
223
+ <button class="social-btn" title="钉钉">
224
+ <svg viewBox="0 0 24 24" fill="currentColor">
225
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 00-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.74-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/>
226
+ </svg>
227
+ </button>
228
+ <button class="social-btn" title="飞书">
229
+ <svg viewBox="0 0 24 24" fill="currentColor">
230
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
231
+ </svg>
232
+ </button>
233
+ </div>
234
+ </div>
235
+ </div>
236
+
237
+ <!-- 版权信息 -->
238
+ <div class="copyright">
239
+ <p>© 2024 XTO Team. All rights reserved.</p>
240
+ </div>
241
+ </div>
242
+ </div>
243
+ </template>
244
+
245
+ <style lang="scss" scoped>
246
+ .login-page {
247
+ width: 100%;
248
+ min-height: 100vh;
249
+ display: flex;
250
+ background: var(--bg-color-page);
251
+ }
252
+
253
+ // 左侧品牌区域
254
+ .login-brand {
255
+ flex: 1;
256
+ position: relative;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ background: linear-gradient(135deg, #002c8c 0%, #0958d9 50%, #1677ff 100%);
261
+ overflow: hidden;
262
+
263
+ @media (max-width: 992px) {
264
+ display: none;
265
+ }
266
+
267
+ .brand-content {
268
+ position: relative;
269
+ z-index: 2;
270
+ padding: 60px;
271
+ max-width: 520px;
272
+ color: #fff;
273
+ }
274
+
275
+ .brand-logo {
276
+ display: flex;
277
+ align-items: center;
278
+ gap: 12px;
279
+ margin-bottom: 48px;
280
+
281
+ .logo-icon {
282
+ width: 48px;
283
+ height: 48px;
284
+ color: #fff;
285
+ }
286
+
287
+ .logo-text {
288
+ font-size: 28px;
289
+ font-weight: 700;
290
+ letter-spacing: 2px;
291
+ }
292
+ }
293
+
294
+ .brand-slogan {
295
+ margin-bottom: 48px;
296
+
297
+ h1 {
298
+ font-size: 32px;
299
+ font-weight: 600;
300
+ line-height: 1.3;
301
+ margin-bottom: 12px;
302
+ }
303
+
304
+ p {
305
+ font-size: 16px;
306
+ opacity: 0.8;
307
+ line-height: 1.6;
308
+ }
309
+ }
310
+
311
+ .brand-features {
312
+ display: flex;
313
+ flex-direction: column;
314
+ gap: 24px;
315
+
316
+ .feature-item {
317
+ display: flex;
318
+ align-items: flex-start;
319
+ gap: 16px;
320
+ padding: 16px;
321
+ background: rgba(255, 255, 255, 0.1);
322
+ border-radius: var(--border-radius-large);
323
+ backdrop-filter: blur(10px);
324
+ transition: all 0.3s ease;
325
+
326
+ &:hover {
327
+ background: rgba(255, 255, 255, 0.15);
328
+ transform: translateX(8px);
329
+ }
330
+ }
331
+
332
+ .feature-icon {
333
+ width: 40px;
334
+ height: 40px;
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: center;
338
+ background: rgba(255, 255, 255, 0.2);
339
+ border-radius: var(--border-radius-base);
340
+ flex-shrink: 0;
341
+
342
+ svg {
343
+ width: 20px;
344
+ height: 20px;
345
+ }
346
+ }
347
+
348
+ .feature-text {
349
+ h4 {
350
+ font-size: 15px;
351
+ font-weight: 600;
352
+ margin-bottom: 4px;
353
+ }
354
+
355
+ p {
356
+ font-size: 13px;
357
+ opacity: 0.8;
358
+ }
359
+ }
360
+ }
361
+
362
+ .brand-decoration {
363
+ position: absolute;
364
+ bottom: 60px;
365
+ left: 60px;
366
+ right: 60px;
367
+ display: flex;
368
+ align-items: center;
369
+ gap: 16px;
370
+
371
+ .decoration-line {
372
+ flex: 1;
373
+ height: 1px;
374
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0.3), transparent);
375
+ }
376
+
377
+ .decoration-dots {
378
+ display: flex;
379
+ gap: 8px;
380
+
381
+ span {
382
+ width: 6px;
383
+ height: 6px;
384
+ background: rgba(255, 255, 255, 0.5);
385
+ border-radius: 50%;
386
+ }
387
+ }
388
+ }
389
+
390
+ // 背景装饰
391
+ .brand-bg {
392
+ position: absolute;
393
+ inset: 0;
394
+ z-index: 1;
395
+ overflow: hidden;
396
+
397
+ .bg-grid {
398
+ position: absolute;
399
+ inset: 0;
400
+ background-image:
401
+ linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
402
+ linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
403
+ background-size: 60px 60px;
404
+ }
405
+
406
+ .bg-glow {
407
+ position: absolute;
408
+ top: -20%;
409
+ right: -10%;
410
+ width: 60%;
411
+ height: 60%;
412
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
413
+ animation: glow-pulse 8s ease-in-out infinite;
414
+ }
415
+
416
+ .bg-pattern {
417
+ position: absolute;
418
+ bottom: -10%;
419
+ left: -10%;
420
+ width: 40%;
421
+ height: 40%;
422
+ background: radial-gradient(circle, rgba(255, 255, 255, 0.08) 0%, transparent 60%);
423
+ }
424
+ }
425
+ }
426
+
427
+ @keyframes glow-pulse {
428
+ 0%, 100% { opacity: 0.6; transform: scale(1); }
429
+ 50% { opacity: 1; transform: scale(1.1); }
430
+ }
431
+
432
+ // 右侧登录区域
433
+ .login-form-section {
434
+ width: 500px;
435
+ min-height: 100vh;
436
+ display: flex;
437
+ flex-direction: column;
438
+ justify-content: center;
439
+ padding: 60px 80px;
440
+ background: var(--bg-color);
441
+
442
+ @media (max-width: 768px) {
443
+ width: 100%;
444
+ padding: 40px 24px;
445
+ }
446
+
447
+ .form-container {
448
+ width: 100%;
449
+ max-width: 360px;
450
+ margin: 0 auto;
451
+ }
452
+
453
+ .form-header {
454
+ margin-bottom: 40px;
455
+
456
+ h2 {
457
+ font-size: 28px;
458
+ font-weight: 600;
459
+ color: var(--color-text-primary);
460
+ margin-bottom: 8px;
461
+ }
462
+
463
+ p {
464
+ font-size: 14px;
465
+ color: var(--color-text-secondary);
466
+ }
467
+ }
468
+
469
+ .login-form {
470
+ .input-wrapper {
471
+ margin-bottom: 4px;
472
+ }
473
+
474
+ .input-label {
475
+ display: block;
476
+ font-size: 14px;
477
+ font-weight: 500;
478
+ color: var(--color-text-regular);
479
+ margin-bottom: 8px;
480
+ }
481
+
482
+ :deep(.x-form-item) {
483
+ margin-bottom: 24px;
484
+ }
485
+
486
+ :deep(.x-input) {
487
+ --x-input-border-radius: 8px;
488
+ }
489
+
490
+ :deep(.x-input__prefix) {
491
+ margin-right: 12px;
492
+ color: var(--color-text-placeholder);
493
+ }
494
+ }
495
+
496
+ .form-options {
497
+ display: flex;
498
+ justify-content: space-between;
499
+ align-items: center;
500
+ margin-bottom: 24px;
501
+
502
+ .forgot-link {
503
+ font-size: 14px;
504
+ color: var(--color-primary);
505
+ text-decoration: none;
506
+ transition: color 0.2s;
507
+
508
+ &:hover {
509
+ color: var(--color-primary-dark-1);
510
+ }
511
+ }
512
+ }
513
+
514
+ .login-btn {
515
+ width: 100%;
516
+ height: 44px;
517
+ font-size: 16px;
518
+ font-weight: 500;
519
+ border-radius: 8px;
520
+ letter-spacing: 4px;
521
+ transition: all 0.3s ease;
522
+
523
+ &:hover:not(:disabled) {
524
+ transform: translateY(-2px);
525
+ box-shadow: 0 4px 12px rgba(22, 119, 255, 0.4);
526
+ }
527
+
528
+ &:active:not(:disabled) {
529
+ transform: translateY(0);
530
+ }
531
+ }
532
+
533
+ .form-footer {
534
+ margin-top: 32px;
535
+ padding-top: 24px;
536
+ border-top: 1px solid var(--color-border-lighter);
537
+ text-align: center;
538
+
539
+ p {
540
+ font-size: 13px;
541
+ color: var(--color-text-placeholder);
542
+ margin-bottom: 16px;
543
+ }
544
+
545
+ .social-login {
546
+ display: flex;
547
+ justify-content: center;
548
+ gap: 16px;
549
+ }
550
+
551
+ .social-btn {
552
+ width: 44px;
553
+ height: 44px;
554
+ display: flex;
555
+ align-items: center;
556
+ justify-content: center;
557
+ background: var(--color-fill);
558
+ border: none;
559
+ border-radius: 50%;
560
+ cursor: pointer;
561
+ transition: all 0.2s ease;
562
+ color: var(--color-text-secondary);
563
+
564
+ svg {
565
+ width: 20px;
566
+ height: 20px;
567
+ }
568
+
569
+ &:hover {
570
+ background: var(--color-primary-light-6);
571
+ color: var(--color-primary);
572
+ transform: translateY(-2px);
573
+ }
574
+ }
575
+ }
576
+
577
+ .copyright {
578
+ margin-top: 48px;
579
+ text-align: center;
580
+
581
+ p {
582
+ font-size: 12px;
583
+ color: var(--color-text-placeholder);
584
+ }
585
+ }
586
+ }
195
587
  </style>