befly-admin 3.0.1

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 (57) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +4 -0
  3. package/LICENSE +201 -0
  4. package/README.md +143 -0
  5. package/index.html +13 -0
  6. package/libs/auto-routes-template.js +104 -0
  7. package/libs/autoRouter.ts +67 -0
  8. package/libs/icons.ts +543 -0
  9. package/package.json +42 -0
  10. package/public/logo.svg +106 -0
  11. package/src/App.vue +17 -0
  12. package/src/api/auth.ts +60 -0
  13. package/src/components/Icon.vue +41 -0
  14. package/src/env.d.ts +9 -0
  15. package/src/layouts/0.vue +207 -0
  16. package/src/layouts/1.vue +22 -0
  17. package/src/layouts/2.vue +166 -0
  18. package/src/main.ts +19 -0
  19. package/src/plugins/http.ts +94 -0
  20. package/src/plugins/router.ts +47 -0
  21. package/src/plugins/store.ts +19 -0
  22. package/src/styles/index.scss +198 -0
  23. package/src/styles/mixins.scss +98 -0
  24. package/src/styles/variables.scss +75 -0
  25. package/src/types/env.d.ts +23 -0
  26. package/src/util.ts +28 -0
  27. package/src/views/403/403.vue +33 -0
  28. package/src/views/admin/components/edit.vue +147 -0
  29. package/src/views/admin/components/role.vue +135 -0
  30. package/src/views/admin/index.vue +169 -0
  31. package/src/views/dict/components/edit.vue +156 -0
  32. package/src/views/dict/index.vue +159 -0
  33. package/src/views/index/components/AddonList.vue +125 -0
  34. package/src/views/index/components/EnvironmentInfo.vue +97 -0
  35. package/src/views/index/components/OperationLogs.vue +112 -0
  36. package/src/views/index/components/PerformanceMetrics.vue +148 -0
  37. package/src/views/index/components/QuickActions.vue +27 -0
  38. package/src/views/index/components/ServiceStatus.vue +193 -0
  39. package/src/views/index/components/SystemNotifications.vue +136 -0
  40. package/src/views/index/components/SystemOverview.vue +188 -0
  41. package/src/views/index/components/SystemResources.vue +104 -0
  42. package/src/views/index/components/UserInfo.vue +136 -0
  43. package/src/views/index/index.vue +62 -0
  44. package/src/views/login/index_1.vue +694 -0
  45. package/src/views/menu/components/edit.vue +150 -0
  46. package/src/views/menu/index.vue +168 -0
  47. package/src/views/news/detail/detail_2.vue +26 -0
  48. package/src/views/news/detail/index.vue +26 -0
  49. package/src/views/news/news.vue +26 -0
  50. package/src/views/role/components/api.vue +280 -0
  51. package/src/views/role/components/edit.vue +129 -0
  52. package/src/views/role/components/menu.vue +143 -0
  53. package/src/views/role/index.vue +179 -0
  54. package/src/views/user/user.vue +320 -0
  55. package/temp/router.js +71 -0
  56. package/tsconfig.json +34 -0
  57. package/vite.config.ts +100 -0
@@ -0,0 +1,694 @@
1
+ <template>
2
+ <div class="auth-container" :class="{ 'sign-up-mode': $Data.isSignUp }">
3
+ <!-- 左侧欢迎区域 -->
4
+ <div class="left-panel">
5
+ <div class="panel-content" v-if="!$Data.isSignUp">
6
+ <h2>你好,朋友!</h2>
7
+ <p>填写个人信息,开始使用</p>
8
+ <button class="toggle-btn" @click="$Method.toggleMode">注册账号</button>
9
+ </div>
10
+ <div class="panel-content" v-else>
11
+ <h2>欢迎回来!</h2>
12
+ <p>使用您的账号登录</p>
13
+ <button class="toggle-btn" @click="$Method.toggleMode">立即登录</button>
14
+ </div>
15
+ </div>
16
+
17
+ <!-- 右侧表单区域 -->
18
+ <div class="right-panel">
19
+ <!-- 登录表单 -->
20
+ <div class="form-container sign-in-form" :class="{ active: !$Data.isSignUp }">
21
+ <h2 class="form-title">登录到 Befly</h2>
22
+
23
+ <!-- 登录方式切换 -->
24
+ <div class="login-type-tabs">
25
+ <button type="button" class="tab-btn" :class="{ active: $Data.loginType === 'email' }" @click="$Method.switchLoginType('email')">邮箱登录</button>
26
+ <button type="button" class="tab-btn" :class="{ active: $Data.loginType === 'phone' }" @click="$Method.switchLoginType('phone')">手机登录</button>
27
+ <button type="button" class="tab-btn" :class="{ active: $Data.loginType === 'qrcode' }" @click="$Method.switchLoginType('qrcode')">扫码登录</button>
28
+ </div>
29
+
30
+ <!-- 邮箱登录 -->
31
+ <t-form v-if="$Data.loginType === 'email'" :data="$Data.loginForm.email" :rules="$Data.loginRules.email" :ref="(el) => ($Form.emailForm = el)" @submit="$Method.handleLogin" class="login-form" :required-mark="false" show-error-message label-align="left" label-width="70px">
32
+ <t-form-item name="email" label="邮箱">
33
+ <t-input v-model="$Data.loginForm.email.email" placeholder="请输入邮箱" size="large" clearable>
34
+ <template #prefix-icon>
35
+ <mail-icon />
36
+ </template>
37
+ </t-input>
38
+ </t-form-item>
39
+
40
+ <t-form-item name="password" label="密码">
41
+ <t-input v-model="$Data.loginForm.email.password" type="password" placeholder="请输入密码" size="large" clearable>
42
+ <template #prefix-icon>
43
+ <lock-on-icon />
44
+ </template>
45
+ </t-input>
46
+ </t-form-item>
47
+
48
+ <div class="form-footer">
49
+ <a href="#" class="forgot-password">忘记密码?</a>
50
+ </div>
51
+
52
+ <t-button theme="primary" type="submit" class="auth-btn" size="large" :loading="$Data.loginLoading"> 登录 </t-button>
53
+ </t-form>
54
+
55
+ <!-- 手机登录 -->
56
+ <t-form v-if="$Data.loginType === 'phone'" :data="$Data.loginForm.phone" :rules="$Data.loginRules.phone" :ref="(el) => ($Form.phoneForm = el)" @submit="$Method.handleLogin" class="login-form" :required-mark="false" show-error-message label-align="left" label-width="70px">
57
+ <t-form-item name="phone" label="手机号">
58
+ <t-input v-model="$Data.loginForm.phone.phone" placeholder="请输入手机号" size="large" clearable>
59
+ <template #prefix-icon>
60
+ <mobile-icon />
61
+ </template>
62
+ </t-input>
63
+ </t-form-item>
64
+
65
+ <t-form-item name="code" label="验证码">
66
+ <t-input v-model="$Data.loginForm.phone.code" placeholder="请输入验证码" size="large" clearable>
67
+ <template #prefix-icon>
68
+ <secured-icon />
69
+ </template>
70
+ <template #suffix>
71
+ <t-button variant="text" :disabled="$Data.codeCountdown > 0" @click="$Method.sendCode">
72
+ {{ $Data.codeCountdown > 0 ? `${$Data.codeCountdown}秒后重试` : '发送验证码' }}
73
+ </t-button>
74
+ </template>
75
+ </t-input>
76
+ </t-form-item>
77
+
78
+ <t-button theme="primary" type="submit" class="auth-btn" size="large" :loading="$Data.loginLoading"> 登录 </t-button>
79
+ </t-form>
80
+
81
+ <!-- 扫码登录 -->
82
+ <div v-if="$Data.loginType === 'qrcode'" class="qrcode-container">
83
+ <div class="qrcode-box">
84
+ <t-qrcode :value="$Data.qrcodeValue" :size="200" :status="$Data.qrcodeStatus" @refresh="$Method.refreshQrcode" />
85
+ <p class="qrcode-tip">{{ $Data.qrcodeTip }}</p>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- 注册表单 -->
91
+ <div class="form-container sign-up-form" :class="{ active: $Data.isSignUp }">
92
+ <h2 class="form-title">注册账号</h2>
93
+
94
+ <t-form :data="$Data.registerForm" :rules="$Data.registerRules" :ref="(el) => ($Form.registerForm = el)" @submit="$Method.handleRegister" class="login-form" :required-mark="false" show-error-message label-align="left" label-width="70px">
95
+ <t-form-item name="name" label="姓名">
96
+ <t-input v-model="$Data.registerForm.name" placeholder="请输入姓名" size="large" clearable>
97
+ <template #prefix-icon>
98
+ <user-icon />
99
+ </template>
100
+ </t-input>
101
+ </t-form-item>
102
+
103
+ <t-form-item name="email" label="邮箱">
104
+ <t-input v-model="$Data.registerForm.email" placeholder="请输入邮箱" size="large" clearable>
105
+ <template #prefix-icon>
106
+ <mail-icon />
107
+ </template>
108
+ </t-input>
109
+ </t-form-item>
110
+
111
+ <t-form-item name="password" label="密码">
112
+ <t-input v-model="$Data.registerForm.password" type="password" placeholder="请输入密码" size="large" clearable>
113
+ <template #prefix-icon>
114
+ <lock-on-icon />
115
+ </template>
116
+ </t-input>
117
+ </t-form-item>
118
+
119
+ <t-button theme="primary" type="submit" class="auth-btn" size="large" :loading="$Data.registerLoading"> 注册 </t-button>
120
+ </t-form>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </template>
125
+
126
+ <script setup>
127
+ const router = useRouter();
128
+
129
+ // 表单引用定义
130
+ const $Form = $ref({
131
+ emailForm: null,
132
+ phoneForm: null,
133
+ registerForm: null
134
+ });
135
+
136
+ // 数据定义
137
+ const $Data = $ref({
138
+ loginLoading: false,
139
+ registerLoading: false,
140
+ isSignUp: false,
141
+ loginType: 'email',
142
+ codeCountdown: 0,
143
+ qrcodeValue: '',
144
+ qrcodeStatus: 'loading',
145
+ qrcodeTip: '二维码加载中...',
146
+ loginForm: {
147
+ email: {
148
+ email: '',
149
+ password: ''
150
+ },
151
+ phone: {
152
+ phone: '',
153
+ code: ''
154
+ }
155
+ },
156
+ loginRules: {
157
+ email: {
158
+ email: [{ required: true, message: '请输入邮箱', type: 'error' }],
159
+ password: [{ required: true, message: '请输入密码', type: 'error' }]
160
+ },
161
+ phone: {
162
+ phone: [{ required: true, message: '请输入手机号', type: 'error' }],
163
+ code: [{ required: true, message: '请输入验证码', type: 'error' }]
164
+ }
165
+ },
166
+ registerForm: {
167
+ name: '',
168
+ email: '',
169
+ password: ''
170
+ },
171
+ registerRules: {
172
+ name: [{ required: true, message: '请输入姓名', type: 'error' }],
173
+ email: [{ required: true, message: '请输入邮箱', type: 'error' }],
174
+ password: [{ required: true, message: '请输入密码', type: 'error' }]
175
+ }
176
+ });
177
+
178
+ // 方法定义
179
+ const $Method = {
180
+ // 切换登录/注册模式
181
+ toggleMode() {
182
+ $Data.isSignUp = !$Data.isSignUp;
183
+ },
184
+
185
+ // 切换登录方式
186
+ switchLoginType(type: 'email' | 'phone' | 'qrcode') {
187
+ $Data.loginType = type;
188
+ // 如果切换到二维码登录,生成二维码
189
+ if (type === 'qrcode') {
190
+ $Method.generateQrcode();
191
+ }
192
+ },
193
+
194
+ // 生成二维码
195
+ generateQrcode() {
196
+ $Data.qrcodeStatus = 'loading';
197
+ $Data.qrcodeTip = '二维码加载中...';
198
+
199
+ // TODO: 调用生成二维码接口
200
+ // const res = await generateQrcodeApi();
201
+
202
+ // 模拟生成二维码
203
+ setTimeout(() => {
204
+ // 生成一个唯一的二维码标识
205
+ const qrcodeId = `qrcode_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
206
+ $Data.qrcodeValue = `https://befly.com/qrcode/scan?id=${qrcodeId}`;
207
+ $Data.qrcodeStatus = 'active';
208
+ $Data.qrcodeTip = '请使用手机扫描二维码登录';
209
+
210
+ // 开始轮询检查扫码状态
211
+ $Method.checkQrcodeStatus();
212
+ }, 1000);
213
+ },
214
+
215
+ // 刷新二维码
216
+ refreshQrcode() {
217
+ $Method.generateQrcode();
218
+ },
219
+
220
+ // 检查二维码状态
221
+ checkQrcodeStatus() {
222
+ // TODO: 实现轮询检查二维码扫描状态
223
+ // 这里模拟扫码状态变化
224
+ setTimeout(() => {
225
+ // 模拟扫码成功
226
+ // $Data.qrcodeStatus = 'scanned';
227
+ // $Data.qrcodeTip = '扫描成功,请在手机上确认登录';
228
+ // 模拟登录成功
229
+ // setTimeout(() => {
230
+ // localStorage.setItem('token', 'mock-token');
231
+ // MessagePlugin.success('登录成功');
232
+ // router.push('/dashboard');
233
+ // }, 2000);
234
+ }, 5000);
235
+
236
+ // 模拟二维码过期
237
+ setTimeout(() => {
238
+ if ($Data.qrcodeStatus === 'active') {
239
+ $Data.qrcodeStatus = 'expired';
240
+ $Data.qrcodeTip = '二维码已过期,请点击刷新';
241
+ }
242
+ }, 30000); // 30秒后过期
243
+ },
244
+
245
+ // 发送验证码
246
+ async sendCode() {
247
+ if (!$Data.loginForm.phone.phone) {
248
+ MessagePlugin.warning('请先输入手机号');
249
+ return;
250
+ }
251
+
252
+ try {
253
+ await $Http('/addon/admin/sendSmsCode', { phone: $Data.loginForm.phone.phone });
254
+
255
+ MessagePlugin.success('验证码已发送');
256
+ $Data.codeCountdown = 60;
257
+
258
+ const timer = setInterval(() => {
259
+ $Data.codeCountdown--;
260
+ if ($Data.codeCountdown <= 0) {
261
+ clearInterval(timer);
262
+ }
263
+ }, 1000);
264
+ } catch (error) {
265
+ // 错误已经在 request 拦截器中处理
266
+ }
267
+ },
268
+
269
+ // 处理登录
270
+ async handleLogin() {
271
+ let valid = false;
272
+ let formData = null;
273
+
274
+ if ($Data.loginType === 'email') {
275
+ valid = await $Form.emailForm.validate();
276
+ formData = $Data.loginForm.email;
277
+ } else if ($Data.loginType === 'phone') {
278
+ valid = await $Form.phoneForm.validate();
279
+ formData = $Data.loginForm.phone;
280
+ }
281
+
282
+ if (!valid) return;
283
+
284
+ $Data.loginLoading = true;
285
+
286
+ try {
287
+ const res = await $Http('/addon/admin/login', formData);
288
+
289
+ // 先保存 token
290
+ localStorage.setItem('token', res.data.token);
291
+
292
+ // 如果返回用户信息,也可以存储
293
+ if (res.data.userInfo) {
294
+ localStorage.setItem('userInfo', JSON.stringify(res.data.userInfo));
295
+ }
296
+
297
+ MessagePlugin.success('登录成功');
298
+
299
+ // 跳转到首页,路由守卫会自动加载菜单
300
+ await router.push('/');
301
+ } catch (error) {
302
+ // 错误已经在 request 拦截器中处理
303
+ } finally {
304
+ $Data.loginLoading = false;
305
+ }
306
+ },
307
+
308
+ // 处理注册
309
+ async handleRegister() {
310
+ const valid = await $Form.registerForm.validate();
311
+ if (!valid) return;
312
+
313
+ $Data.registerLoading = true;
314
+
315
+ try {
316
+ await $Http('/addon/admin/register', $Data.registerForm);
317
+ MessagePlugin.success('注册成功,请登录');
318
+ $Data.isSignUp = false;
319
+
320
+ // 清空注册表单
321
+ $Data.registerForm.name = '';
322
+ $Data.registerForm.email = '';
323
+ $Data.registerForm.password = '';
324
+ } catch (error) {
325
+ // 错误已经在 request 拦截器中处理
326
+ } finally {
327
+ $Data.registerLoading = false;
328
+ }
329
+ }
330
+ };
331
+ </script>
332
+
333
+ <style scoped lang="scss">
334
+ .auth-container {
335
+ display: flex;
336
+ width: 100%;
337
+ min-height: 100vh;
338
+ overflow: hidden;
339
+ position: relative;
340
+ background: #fff;
341
+ }
342
+
343
+ // 青色滑动背景块
344
+ .left-panel {
345
+ position: absolute;
346
+ top: 0;
347
+ left: 0;
348
+ width: 50%;
349
+ height: 100%;
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: center;
353
+ background: linear-gradient(135deg, #48b19f 0%, #3a9d8f 100%);
354
+ color: #fff;
355
+ z-index: 5;
356
+ transition: transform 0.5s ease-in-out;
357
+
358
+ .panel-content {
359
+ text-align: center;
360
+ padding: 2rem;
361
+ max-width: 400px;
362
+
363
+ h2 {
364
+ font-size: 2rem;
365
+ font-weight: 600;
366
+ margin-bottom: 1rem;
367
+ }
368
+
369
+ p {
370
+ font-size: 1rem;
371
+ line-height: 1.6;
372
+ margin-bottom: 2rem;
373
+ opacity: 0.9;
374
+ }
375
+
376
+ .toggle-btn {
377
+ padding: 0.8rem 3rem;
378
+ border: 2px solid #fff;
379
+ background: transparent;
380
+ color: #fff;
381
+ border-radius: 25px;
382
+ font-size: 0.9rem;
383
+ font-weight: 600;
384
+ cursor: pointer;
385
+ transition: all 0.3s;
386
+
387
+ &:hover {
388
+ background: #fff;
389
+ color: #48b19f;
390
+ }
391
+ }
392
+ }
393
+ }
394
+
395
+ // 淡入动画
396
+ @keyframes fadeInUp {
397
+ from {
398
+ opacity: 0;
399
+ transform: translateY(20px);
400
+ }
401
+ to {
402
+ opacity: 1;
403
+ transform: translateY(0);
404
+ }
405
+ }
406
+
407
+ // 表单区域容器(全屏背景)
408
+ .right-panel {
409
+ position: absolute;
410
+ width: 100%;
411
+ height: 100%;
412
+ top: 0;
413
+ left: 0;
414
+ z-index: 1;
415
+ }
416
+
417
+ // 注册模式下青色块移动到右侧
418
+ .auth-container.sign-up-mode {
419
+ .left-panel {
420
+ transform: translateX(100%);
421
+ }
422
+ }
423
+
424
+ // 表单容器(跟随颜色区域滑动)
425
+ .form-container {
426
+ position: absolute;
427
+ width: 50%;
428
+ height: 100%;
429
+ top: 0;
430
+ display: flex;
431
+ flex-direction: column;
432
+ align-items: center;
433
+ justify-content: center;
434
+ padding: 3rem 2rem;
435
+ opacity: 0;
436
+ pointer-events: none;
437
+ transition: all 0.5s ease-in-out;
438
+
439
+ &.active {
440
+ opacity: 1;
441
+ pointer-events: all;
442
+ }
443
+ }
444
+
445
+ // 登录模式:登录表单在右侧
446
+ .auth-container:not(.sign-up-mode) {
447
+ .sign-in-form {
448
+ right: 0;
449
+ }
450
+
451
+ .sign-up-form {
452
+ left: 0;
453
+ }
454
+ }
455
+
456
+ // 注册模式:注册表单在左侧,登录表单在右侧
457
+ .auth-container.sign-up-mode {
458
+ .sign-in-form {
459
+ right: -50%;
460
+ }
461
+
462
+ .sign-up-form {
463
+ left: 0;
464
+ }
465
+ }
466
+
467
+ .form-title {
468
+ font-size: 1.8rem;
469
+ color: #333;
470
+ margin-bottom: 1.5rem;
471
+ font-weight: 600;
472
+ text-align: center;
473
+ width: 100%;
474
+ }
475
+
476
+ // 登录方式切换标签
477
+ .login-type-tabs {
478
+ display: flex;
479
+ justify-content: center;
480
+ gap: 0;
481
+ margin-bottom: 2rem;
482
+ border-bottom: 2px solid #f0f0f0;
483
+ width: 100%;
484
+ max-width: 450px;
485
+
486
+ .tab-btn {
487
+ padding: 0.75rem 1.2rem;
488
+ border: none;
489
+ background: transparent;
490
+ color: #666;
491
+ font-size: 0.875rem;
492
+ cursor: pointer;
493
+ position: relative;
494
+ transition: all 0.3s;
495
+
496
+ &::after {
497
+ content: '';
498
+ position: absolute;
499
+ bottom: -2px;
500
+ left: 0;
501
+ width: 100%;
502
+ height: 2px;
503
+ background: #48b19f;
504
+ transform: scaleX(0);
505
+ transition: transform 0.3s;
506
+ }
507
+
508
+ &.active {
509
+ color: #48b19f;
510
+ font-weight: 600;
511
+
512
+ &::after {
513
+ transform: scaleX(1);
514
+ }
515
+ }
516
+
517
+ &:hover {
518
+ color: #48b19f;
519
+ }
520
+ }
521
+ }
522
+
523
+ .login-form {
524
+ width: 100%;
525
+ max-width: 450px;
526
+ }
527
+
528
+ .t-form-item {
529
+ width: 100%;
530
+ margin-bottom: 1.2rem;
531
+
532
+ :deep(.t-form__controls) {
533
+ width: 100%;
534
+ }
535
+
536
+ :deep(.t-input) {
537
+ width: 100%;
538
+ background: #f8f9fa;
539
+ border: 1px solid #e0e0e0;
540
+ border-radius: 6px;
541
+ transition: all 0.3s;
542
+
543
+ &:hover {
544
+ border-color: #48b19f;
545
+ }
546
+
547
+ &:focus-within {
548
+ border-color: #48b19f;
549
+ background: #fff;
550
+ }
551
+
552
+ input {
553
+ padding: 0.75rem 1rem;
554
+ }
555
+ }
556
+ }
557
+
558
+ .form-footer {
559
+ width: 100%;
560
+ display: flex;
561
+ justify-content: flex-end;
562
+ margin-bottom: 1rem;
563
+ }
564
+
565
+ .forgot-password {
566
+ font-size: 0.8rem;
567
+ color: #888;
568
+ text-decoration: none;
569
+
570
+ &:hover {
571
+ color: #48b19f;
572
+ }
573
+ }
574
+
575
+ .auth-btn {
576
+ width: 100% !important;
577
+ max-width: 100%;
578
+ height: 44px;
579
+ border-radius: 6px;
580
+ background: #48b19f;
581
+ border: none;
582
+ font-size: 0.95rem;
583
+ font-weight: 600;
584
+ margin-top: 0.5rem;
585
+ transition: all 0.3s;
586
+
587
+ &:hover {
588
+ background: #3a9d8f;
589
+ transform: translateY(-1px);
590
+ box-shadow: 0 3px 10px rgba(72, 177, 159, 0.3);
591
+ }
592
+
593
+ :deep(.t-button__text) {
594
+ color: #fff;
595
+ }
596
+ }
597
+
598
+ // 二维码容器
599
+ .qrcode-container {
600
+ display: flex;
601
+ align-items: center;
602
+ justify-content: center;
603
+ min-height: 350px;
604
+ padding: 2rem 0;
605
+ width: 100%;
606
+ max-width: 450px;
607
+ }
608
+
609
+ .qrcode-box {
610
+ text-align: center;
611
+ display: flex;
612
+ flex-direction: column;
613
+ align-items: center;
614
+ gap: 1.5rem;
615
+
616
+ :deep(.t-qrcode) {
617
+ margin: 0 auto;
618
+ }
619
+
620
+ .qrcode-tip {
621
+ font-size: 0.9rem;
622
+ color: #666;
623
+ margin-top: 0.5rem;
624
+ }
625
+ }
626
+
627
+ // 响应式设计
628
+ @media (max-width: 968px) {
629
+ .auth-container {
630
+ flex-direction: column;
631
+ }
632
+
633
+ .left-panel,
634
+ .right-panel {
635
+ flex: none;
636
+ width: 100%;
637
+ }
638
+
639
+ .left-panel {
640
+ order: 1 !important;
641
+ min-height: 200px;
642
+
643
+ .panel-content {
644
+ h2 {
645
+ font-size: 1.5rem;
646
+ }
647
+
648
+ p {
649
+ font-size: 0.9rem;
650
+ }
651
+ }
652
+ }
653
+
654
+ .right-panel {
655
+ order: 2 !important;
656
+ min-height: 500px;
657
+ }
658
+
659
+ .form-container {
660
+ width: 100%;
661
+ padding: 2rem 1rem;
662
+ position: static;
663
+ }
664
+ }
665
+
666
+ @media (max-width: 576px) {
667
+ .left-panel .panel-content {
668
+ padding: 1.5rem;
669
+
670
+ h2 {
671
+ font-size: 1.3rem;
672
+ }
673
+
674
+ p {
675
+ font-size: 0.85rem;
676
+ margin-bottom: 1rem;
677
+ }
678
+ }
679
+
680
+ .form-title {
681
+ font-size: 1.5rem;
682
+ margin-bottom: 1.5rem;
683
+ }
684
+
685
+ .login-type-tabs {
686
+ gap: 0.2rem;
687
+
688
+ .tab-btn {
689
+ padding: 0.6rem 1rem;
690
+ font-size: 0.8rem;
691
+ }
692
+ }
693
+ }
694
+ </style>