0235711 vor 2 Tagen
Ursprung
Commit
f140c17c1d

+ 4 - 2
src/app/app.routes.ts

@@ -1,5 +1,7 @@
 import { Routes } from '@angular/router';
 
+// 登录页
+import { LoginPage } from './pages/auth/login/login';
 
 // 客服页面
 import { CustomerServiceLayout } from './pages/customer-service/customer-service-layout/customer-service-layout';
@@ -148,7 +150,7 @@ export const routes: Routes = [
     ]
   },
 
-  // 默认路由重定向到客服工作台
-  { path: '', redirectTo: '/customer-service/dashboard', pathMatch: 'full' },
+  // 默认路由重定向到登录页
+  { path: '', component: LoginPage, pathMatch: 'full' },
   { path: '**', redirectTo: '/customer-service/dashboard' }
 ];

+ 58 - 0
src/app/pages/auth/login/login.html

@@ -0,0 +1,58 @@
+<div class="login-container">
+  <div class="login-card">
+    <div class="brand">
+      <div class="logo"></div>
+      <h1>欢迎使用 YSS 平台</h1>
+      
+    </div>
+
+    <form (ngSubmit)="signIn()" class="form" autocomplete="off">
+      <div class="form-row">
+        <label for="username">账号</label>
+        <input id="username" type="text" [(ngModel)]="username" name="username" placeholder="请输入账号" />
+      </div>
+      <div class="form-row">
+        <label for="password">密码</label>
+        <input id="password" type="password" [(ngModel)]="password" name="password" placeholder="请输入密码" />
+      </div>
+      <button class="ios-primary" type="submit" [disabled]="loading">{{ loading ? '登录中…' : '登录' }}</button>
+      <div class="error" *ngIf="error">{{ error }}</div>
+    </form>
+
+    <div class="quick-roles">
+      <div class="divider"><span>快速进入</span></div>
+      <div class="roles-grid">
+        <button class="role-btn cs" (click)="goToRole('customer-service')">
+          <span class="icon">💬</span>
+          <span>客服</span>
+        </button>
+        <button class="role-btn designer" (click)="goToRole('designer')">
+          <span class="icon">🎨</span>
+          <span>设计师</span>
+        </button>
+        <button class="role-btn tl" (click)="goToRole('team-leader')">
+          <span class="icon">👑</span>
+          <span>组长</span>
+        </button>
+        <button class="role-btn finance" (click)="goToRole('finance')">
+          <span class="icon">💰</span>
+          <span>财务</span>
+        </button>
+        <button class="role-btn hr" (click)="goToRole('hr')">
+          <span class="icon">🧑‍💼</span>
+          <span>人事/行政</span>
+        </button>
+        <button class="role-btn admin" (click)="goToRole('admin')">
+          <span class="icon">🛡️</span>
+          <span>管理员</span>
+        </button>
+      </div>
+    </div>
+
+    <footer class="footer">
+      <span>© {{ currentYear }} YSS</span>
+      <a href="javascript:void(0)">隐私</a>
+      <a href="javascript:void(0)">条款</a>
+    </footer>
+  </div>
+</div>

+ 150 - 0
src/app/pages/auth/login/login.scss

@@ -0,0 +1,150 @@
+@use 'sass:math';
+
+$bg: #f2f2f7;
+$text: #1c1c1e;
+$muted: #8e8e93;
+$primary: #007aff;
+$card: #ffffff;
+$border: #e5e5ea;
+$shadow: 0 8px 30px rgba(0,0,0,0.06);
+
+.login-container {
+  min-height: 100vh;
+  display: grid;
+  place-items: center;
+  background: linear-gradient(180deg, #f5f7fb, #eef1f7);
+  padding: 24px;
+}
+
+.login-card {
+  width: min(960px, 100%);
+  background: $card;
+  border-radius: 24px;
+  border: 1px solid $border;
+  box-shadow: $shadow;
+  padding: clamp(20px, 4vw, 40px);
+  display: grid;
+  grid-template-rows: auto auto auto auto;
+  gap: 24px;
+}
+
+.brand {
+  text-align: center;
+  .logo {
+    font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji', 'Segoe UI Emoji';
+    font-size: 42px;
+    margin-bottom: 4px;
+  }
+  h1 { font-size: clamp(20px, 2vw, 26px); color: $text; margin: 0; }
+  p { color: $muted; margin: 6px 0 0; }
+}
+
+.form {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 16px 20px;
+
+  .form-row {
+    display: grid;
+    gap: 8px;
+
+    label { color: $text; font-weight: 600; }
+    input {
+      height: 44px;
+      border: 1px solid $border;
+      border-radius: 12px;
+      padding: 0 14px;
+      font-size: 16px;
+      outline: none;
+      background: $card;
+      transition: box-shadow .2s ease, border-color .2s ease;
+
+      &:focus {
+        border-color: $primary;
+        box-shadow: 0 0 0 4px color-mix(in srgb, $primary 12%, transparent);
+      }
+    }
+  }
+
+  .ios-primary {
+    grid-column: 1 / -1;
+    height: 46px;
+    border: none;
+    border-radius: 14px;
+    background: $primary;
+    color: white;
+    font-weight: 600;
+    letter-spacing: .3px;
+    cursor: pointer;
+    transition: transform .04s ease, filter .2s ease;
+
+    &:active { transform: scale(.998); }
+    &:disabled { filter: grayscale(.2) opacity(.8); cursor: not-allowed; }
+  }
+}
+
+.quick-roles {
+  .divider {
+    display: grid;
+    grid-template-columns: 1fr auto 1fr;
+    align-items: center;
+    gap: 12px;
+    color: $muted;
+
+    &::before, &::after {
+      content: '';
+      height: 1px;
+      background: $border;
+      display: block;
+    }
+
+    span { padding: 2px 8px; border-radius: 999px; background: #f7f7fa; font-size: 12px; }
+  }
+
+  .roles-grid {
+    margin-top: 12px;
+    display: grid;
+    grid-template-columns: repeat(6, 1fr);
+    gap: 10px;
+
+    .role-btn {
+      height: 64px;
+      border-radius: 14px;
+      border: 1px solid $border;
+      background: white;
+      display: grid;
+      grid-template-columns: auto 1fr;
+      align-items: center;
+      gap: 8px;
+      padding: 0 12px;
+      cursor: pointer;
+      transition: box-shadow .2s ease, transform .04s ease, border-color .2s ease;
+
+      .icon { font-size: 18px; }
+      span:last-child { font-weight: 600; color: $text; }
+
+      &:hover { border-color: color-mix(in srgb, $primary 20%, $border); box-shadow: 0 6px 18px rgba(0,0,0,.04); }
+      &:active { transform: translateY(1px); }
+    }
+  }
+}
+
+.footer {
+  display: flex;
+  gap: 12px;
+  align-items: center;
+  justify-content: center;
+  color: $muted;
+  font-size: 12px;
+
+  a { color: inherit; text-decoration: none; }
+}
+
+@media (max-width: 920px) {
+  .form { grid-template-columns: 1fr; }
+  .roles-grid { grid-template-columns: repeat(3, 1fr) !important; }
+}
+
+@media (max-width: 520px) {
+  .roles-grid { grid-template-columns: repeat(2, 1fr) !important; }
+}

+ 55 - 0
src/app/pages/auth/login/login.ts

@@ -0,0 +1,55 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Router, RouterModule } from '@angular/router';
+
+@Component({
+  selector: 'app-login',
+  standalone: true,
+  imports: [CommonModule, FormsModule, RouterModule],
+  templateUrl: './login.html',
+  styleUrls: ['./login.scss']
+})
+export class LoginPage {
+  username = '';
+  password = '';
+  loading = false;
+  error = '';
+  currentYear = new Date().getFullYear();
+
+  constructor(private router: Router) {}
+
+  async signIn() {
+    this.error = '';
+    this.loading = true;
+    try {
+      // 这里保留为演示登录流程的占位,不破坏现有业务逻辑
+      await new Promise(r => setTimeout(r, 400));
+      // 登录成功后可跳转到常用角色,实际项目中可根据权限返回跳转
+      await this.router.navigateByUrl('/customer-service/dashboard');
+    } catch (e) {
+      this.error = '登录失败,请重试';
+    } finally {
+      this.loading = false;
+    }
+  }
+
+  goToRole(role: 'customer-service' | 'designer' | 'team-leader' | 'finance' | 'hr' | 'admin') {
+    const map: Record<string, string> = {
+      'customer-service': '/customer-service/dashboard',
+      'designer': '/designer/dashboard',
+      'team-leader': '/team-leader/dashboard',
+      'finance': '/finance/dashboard',
+      'hr': '/hr/assets',
+      'admin': '/admin/dashboard'
+    };
+
+    // iOS 风格过渡动画(与项目内其他页面行为一致)
+    document.body.classList.add('ios-page-transition');
+    setTimeout(() => {
+      this.router.navigateByUrl(map[role]).finally(() => {
+        setTimeout(() => document.body.classList.remove('ios-page-transition'), 300);
+      });
+    }, 100);
+  }
+}