소스 검색

提交代码5

pqy 4 달 전
부모
커밋
f78c54c268

+ 10 - 1
src/app/app-routing.module.ts

@@ -4,12 +4,21 @@ import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
 const routes: Routes = [
   {
     path: '',
+    redirectTo: 'login',
+    pathMatch: 'full',
+  },
+  {
+    path: 'login',
+    loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
+  },
+  {
+    path: 'tabs',
     loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
   },
   {
     path: 'ceramic-details',
     loadChildren: () => import('./ceramic-details/ceramic-details.module').then( m => m.CeramicDetailsPageModule)
-  }
+  },
 ];
 @NgModule({
   imports: [

+ 17 - 0
src/app/login/login-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { LoginPage } from './login.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: LoginPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class LoginPageRoutingModule {}

+ 21 - 0
src/app/login/login.module.ts

@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule , ReactiveFormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { LoginPageRoutingModule } from './login-routing.module';
+
+import { LoginPage } from './login.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    LoginPageRoutingModule,
+    ReactiveFormsModule, 
+  ],
+  declarations: [LoginPage]
+})
+export class LoginPageModule {}

+ 35 - 0
src/app/login/login.page.html

@@ -0,0 +1,35 @@
+<ion-header>
+  <ion-toolbar class="header-title">
+    <ion-title>窑忆</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="ion-padding content-custom">
+  <!-- 添加背景 -->
+  <div class="background-overlay"></div>
+  <form (ngSubmit)="onSubmit()" [formGroup]="loginForm" class="login-form">
+    <!-- 账号输入框 -->
+    <ion-item class="custom-item">
+      <ion-label position="floating" class="custom-label">账号</ion-label>
+      <ion-input type="text" formControlName="username" class="custom-input"></ion-input>
+    </ion-item>
+    <ion-text color="danger" *ngIf="loginForm.get('username')?.invalid && loginForm.get('username')?.touched">
+      请输入账号!
+    </ion-text>
+    <!-- 密码输入框 -->
+    <ion-item class="custom-item">
+      <ion-label position="floating" class="custom-label">密码</ion-label>
+      <ion-input type="password" formControlName="password" class="custom-input"></ion-input>
+    </ion-item>
+    <ion-text color="danger" *ngIf="loginForm.get('password')?.invalid && loginForm.get('password')?.touched">
+      请输入密码!
+    </ion-text>
+    <!-- 按钮组 -->
+    <div class="transparent-box">
+      <ion-button fill="outline" class="custom-button" (click)="guestLogin()">游客登录</ion-button>
+      <ion-button fill="outline" class="custom-button" (click)="register()">立即注册</ion-button>
+    </div>
+    <!-- 登录按钮 -->
+    <ion-button expand="block" type="submit" class="login-button" [disabled]="loginForm.invalid">登录</ion-button>
+  </form>
+</ion-content>

+ 92 - 0
src/app/login/login.page.scss

@@ -0,0 +1,92 @@
+.header-title {
+  font-family: "FangSong", serif; /* 使用仿宋体 */
+  font-weight: bold; /* 加粗 */
+  text-align: center; /* 居中 */
+}
+/* 背景覆盖层 */
+.background-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: url('/assets/images/login-background.jpg') no-repeat center center;
+  background-size: cover;
+}
+/* 页面整体内容 */
+.content-custom {
+  font-family: 'FangSong', serif; /* 仿宋字体 */
+  color: #2c2c2c;
+}
+/* 输入框样式 */
+.custom-item {
+  margin-top: 60px;
+  --background: rgba(255, 255, 255, 0.7); /* 半透明背景 */
+  --border-radius: 10px;
+  --ion-item-background: transparent; /* 背景透明 */
+  border: 1px solid rgba(0, 0, 0, 0.1);
+  margin-bottom: 15px;
+}
+.custom-label {
+  margin-top: 30px;
+  font-family: 'FangSong', serif; /* 仿宋字体 */
+  font-size: 20px;
+  color: #333;
+}
+.custom-input {
+  font-family: 'FangSong', serif; /* 仿宋字体 */
+  font-size: 20px;
+  color: #2c2c2c;
+  background: transparent; /* 保持背景透明 */
+}
+/* 按钮样式 */
+.custom-button {
+  font-family: 'FangSong', serif;
+  color: #2c2c2c;
+  --background: rgba(255, 255, 255, 0.8);
+  --border-color: #2c2c2c;
+  --border-radius: 20px;
+  margin: 10px;
+}
+/* 登录按钮 */
+.login-button {
+  font-family: 'FangSong', serif;
+  --background: #8c7b75; /* 古风棕色 */
+  color: #fff;
+  border-radius: 10px;
+  margin-top: 40px;
+  font-size: 18px;
+  --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+/* 按钮组样式 */
+.transparent-box {
+  display: flex; /* 使用 Flexbox 布局 */
+  justify-content: space-between; /* 按钮间距自动分布 */
+  align-items: center; /* 按钮垂直居中 */
+  font-size: 24px; /* 调整按钮字体大小 */
+  margin: 20px 10px; /* 上下留白 */
+  padding: 10px 0; /* 内部留白 */
+  background: rgba(255, 255, 255, 0.8); /* 半透明白色背景 */
+  border-radius: 5px; /* 圆角设计 */
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+}
+/* 按钮样式 */
+.transparent-box ion-button {
+  font-family: 'FangSong', serif; /* 使用仿宋字体 */
+  color: #2c2c2c; /* 按钮文字颜色 */
+  --background: url('/assets/images/button-background1.png') no-repeat center center;
+  width: 120px; /* 按钮的宽度 */
+  height: 50px; /* 按钮的高度 */
+  --border-radius: 20px; /* 圆角按钮 */
+  --border-color: #8c7b75; /* 边框颜色为古风棕色 */
+  --padding-start: 15px; /* 按钮左右内边距 */
+  --padding-end: 15px;
+  --border-width: 1px; /* 边框宽度 */
+  text-align: center; /* 按钮文字居中 */
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+  transition: all 0.3s ease; /* 添加交互动画 */
+}
+/* 鼠标悬停效果 */
+.transparent-box ion-button:hover {
+  transform: scale(1.05); /* 微微放大 */
+}

+ 17 - 0
src/app/login/login.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { LoginPage } from './login.page';
+
+describe('LoginPage', () => {
+  let component: LoginPage;
+  let fixture: ComponentFixture<LoginPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 62 - 0
src/app/login/login.page.ts

@@ -0,0 +1,62 @@
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { HttpClient } from '@angular/common/http';
+import { Router } from '@angular/router';
+import { login } from 'src/app/service/api'; // 引入 login 方法
+
+@Component({
+  selector: 'app-login',
+  templateUrl: './login.page.html',
+  styleUrls: ['./login.page.scss'],
+})
+export class LoginPage {
+  loginForm: FormGroup;
+
+  constructor(
+    private fb: FormBuilder,
+    private http: HttpClient,
+    private router: Router
+  ) {
+    this.loginForm = this.fb.group({
+      username: ['', Validators.required],
+      password: ['', Validators.required],
+    });
+  }
+  // 登录提交
+  async onSubmit() { 
+    if (this.loginForm.valid) {
+      const formData = this.loginForm.value;
+      try {
+        const response = await login(formData); // 调用后端 API 进行登录
+        if (response.success) {
+          localStorage.setItem('userToken', response.token);// 存储用户 Token
+          localStorage.setItem('user', JSON.stringify(response.data)); // 存储用户数据
+          localStorage.removeItem('guest'); // 移除游客标志
+          console.log('用户登录成功,数据已存储');
+          this.router.navigate(['/tabs/tab1']); // 跳转到目标页面
+        } else {
+          alert('登录失败,请检查账号和密码!');
+        }
+      } catch (error) {
+        console.error('登录请求失败:', error);
+        alert('登录请求失败,请稍后再试!');
+      }
+    } else {
+      alert('请填写完整的表单!');
+    }
+  }
+  // 游客登录
+  guestLogin() {
+    console.log('游客登录');
+    // 清除可能存在的用户登录数据
+    localStorage.removeItem('user');
+    localStorage.setItem('guest', 'true'); // 标记为游客身份
+    this.router.navigate(['/tabs/tab1']); // 跳转到首页
+  }
+  // 注册逻辑
+  register() {
+    console.log('立即注册');
+    this.router.navigate(['/register']); // 跳转到注册页面
+  }
+}
+  

+ 18 - 0
src/app/service/api.ts

@@ -2,6 +2,7 @@ import axios from 'axios';
 
 const BASE_URL = 'http://127.0.0.1:5000';
 const BASE_URL_2='http://127.0.0.1:3001';
+const BASE_URL_3='http://127.0.0.1:8080';
 // 调用 Flask 进行图像识别
 export const predict = async (data: any) => {
   try {
@@ -24,4 +25,21 @@ export const queryRAGKnowledge = async (query: string) => {
     console.error('查询 RAG 知识库时发生错误:', error);
     throw error;
   }
+};
+// 登录请求方法
+export const login = async (formData: { username: string; password: string }) => {
+  try {
+    const response = await axios.post(`${BASE_URL_3}/api/login`, formData);
+    // 检查后端返回的响应结果
+    if (response.data.success) {
+      console.log('登录成功');
+      return response.data;
+    } else {
+      console.error('登录失败:', response.data.message);
+      throw new Error(response.data.message || '登录失败,请检查账号和密码!');
+    }
+  } catch (error) {
+    console.error('登录请求时发生错误:', error);
+    throw error;
+  }
 };

+ 28 - 0
src/app/service/auth.guard.ts

@@ -0,0 +1,28 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
+
+@Injectable({
+  providedIn: 'root', // 服务全局注入
+})
+export class AuthGuard implements CanActivate {
+  constructor(private router: Router) {}
+  /**
+   * 检查路由激活条件
+   * @param route 当前路由
+   * @param state 当前路由状态
+   * @returns 是否允许访问或者重定向路由
+   */
+  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree {
+    const isLoggedIn = !!localStorage.getItem('userToken'); // 检查是否存在登录的Token
+
+    if (!isLoggedIn) {
+      // 未登录时,跳转到登录页面,并记录目标路由
+      console.warn('用户未登录,跳转到登录页面');
+      return this.router.createUrlTree(['/login'], {
+        queryParams: { redirect: state.url },
+      });
+    }
+    // 已登录允许访问
+    return true;
+  }
+}

+ 13 - 1
src/app/tab1/tab1.page.scss

@@ -167,12 +167,24 @@ swiper {
   left: 2%;
   width: 410px;
   height: 410px;
-  background: rgb(196, 191, 191 ,0.8); /* 半透明灰色背景 */
+  background-image: url('/assets/images/KG-item-background.jpg');/* 设置背景图片 */
+  background-size: cover; /* 让背景图片覆盖整个容器 */
+  background-position: center; /* 背景图片居中 */
   display: flex; /* 使用 Flexbox 布局 */
   justify-content: center; /* 水平居中 */
   align-items: center; /* 垂直居中 */
   border-radius: 20%;
 }
+.square-box::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(255, 255, 255, 0.2); /* 半透明白色 */
+  border-radius: 20%;
+}
 .node-details {
   position: absolute;
   width: 200px;

+ 1 - 1
src/app/tab2/tab2.page.html

@@ -1,6 +1,6 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title class="header-title">窑忆</ion-title>
+    <ion-title class="header-title">智能AI</ion-title>
   </ion-toolbar>
 </ion-header>
 

+ 20 - 0
src/app/tab2/tab2.page.ts

@@ -17,6 +17,8 @@ export class Tab2Page {
     );
     return currentConversation ? currentConversation.messages : [];
   }
+  isGuest: boolean = false; // 是否为游客
+  userAvatar: string = ''; // 用户头像
   userMessage: string = '';  // 用户输入的消息
   responseMsg: string = ''; // 实时消息内容
   isComplete: boolean = true; // 消息是否生成完成
@@ -45,6 +47,24 @@ export class Tab2Page {
     this.userMessage = ''; // 清空输入框
   }
   ngOnInit() {
+    // 检查是否为游客登录
+    const guestFlag = localStorage.getItem('guest');
+    this.isGuest = guestFlag === 'true';
+    if (this.isGuest) {
+      // 如果是游客,设置默认头像
+      this.userAvatar = '/assets/images/user.jpg';
+    } else {
+      // 如果是用户登录,从 localStorage 中获取用户头像
+      const storedUserData = localStorage.getItem('user');
+      if (storedUserData) {
+        const user = JSON.parse(storedUserData);
+        this.userAvatar = user.avatar || '/assets/images/user.jpg'; // 如果用户没有上传头像,使用默认头像
+      } else {
+        console.error('用户数据未找到,请确保已登录!');
+        this.userAvatar = '/assets/images/user.jpg';
+      }
+    }
+
     // 初始化时检查是否有对话
     if (this.conversations.length === 0) {
       this.createNewChat(); // 自动创建默认对话

+ 1 - 1
src/app/tab3/tab3.page.html

@@ -1,6 +1,6 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title class="header-title">窑忆</ion-title>
+    <ion-title class="header-title">陶瓷识别</ion-title>
   </ion-toolbar>
 </ion-header>
 

+ 8 - 7
src/app/tab4/tab4.page.html

@@ -1,20 +1,23 @@
 <ion-header [translucent]="true">
   <ion-toolbar>
-    <ion-title class="header-title">窑忆</ion-title>
+    <ion-title class="header-title">个人中心</ion-title>
   </ion-toolbar>
 </ion-header>
 
 <ion-content [fullscreen]="true" style="--background: url('/assets/images/background.jpg') no-repeat center/cover;">
   <div class="box"></div>
-  <div class="user">
+  <div class="user" *ngIf="user">
     <div class="face">
-      <img src="assets/images/user.jpg" class="img" alt="User Avatar">
+      <!-- 动态绑定用户头像 -->
+      <img [src]="user.avatar" class="img" alt="User Avatar">
     </div>
     <div class="username">
-      pqyyyyy
+      <!-- 动态绑定用户名 -->
+      {{ user.username }}
     </div>
     <div class="id">
-      ID:14945
+      <!-- 动态绑定用户 ID -->
+      ID: {{ user.id }}
     </div>
   </div>
   <ion-list>
@@ -24,14 +27,12 @@
       <ion-label>意见反馈</ion-label>
       <ion-icon name="chevron-forward" slot="end"></ion-icon>
     </ion-item>
-
     <!-- 第二项:设置 -->
     <ion-item (click)="onItemClick({ id: '2', icon: 'settings-outline', title: '设置' })" lines="none">
       <ion-icon name="settings-outline" slot="start" class="setfont"></ion-icon>
       <ion-label>设置</ion-label>
       <ion-icon name="chevron-forward" slot="end"></ion-icon>
     </ion-item>
-
     <!-- 第三项:关于我们 -->
     <ion-item (click)="onItemClick({ id: '3', icon: 'information-circle-outline', title: '关于我们' })" lines="none">
       <ion-icon name="information-circle-outline" slot="start" class="setfont"></ion-icon>

+ 2 - 2
src/app/tab4/tab4.page.scss

@@ -12,7 +12,7 @@
   border-radius: 50%;
   background:
     radial-gradient(circle, rgba(255, 255, 255, 0.8) 60%, rgba(255, 255, 255, 0) 100%) ,
-    url('/assets/images/1.jpg') no-repeat center/cover;
+    url('/assets/images/user-background.jpg') no-repeat center/cover;
 }
 /* 用户信息区域 */
 .user {
@@ -29,7 +29,7 @@
   position: relative;
   width: 120px;
   height: 120px;
-  background: url('/assets/images/2.jpg') no-repeat center/cover;
+  background: url('/assets/images/avatar-frame.jpg') no-repeat center/cover;
   border-radius: 50%;
   margin: 0px;
   display: flex;

+ 36 - 7
src/app/tab4/tab4.page.ts

@@ -1,15 +1,44 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 
 @Component({
   selector: 'app-tab4',
-  templateUrl: 'tab4.page.html',
-  styleUrls: ['tab4.page.scss']
+  templateUrl: './tab4.page.html',
+  styleUrls: ['./tab4.page.scss'],
 })
-export class Tab4Page {
+export class Tab4Page implements OnInit {
+  user: any; // 存储用户数据
+  isGuest: boolean = false; // 标识是否为游客登录
+
+  constructor() {}
+
+  ngOnInit() {
+    // 检查是否为游客登录
+    const guestFlag = localStorage.getItem('guest');
+    this.isGuest = guestFlag === 'true'; // 如果是游客登录,设置标志
+    if (this.isGuest) {
+      console.log('当前为游客登录模式,不加载用户数据');
+      // 为游客设置默认数据
+      this.user = {
+        avatar: '/assets/images/user.jpg', // 默认头像路径
+        username: '游客', // 默认用户名
+        id: '0000', // 默认ID
+      };
+    } else {
+      // 加载用户数据
+      const storedUserData = localStorage.getItem('user');
+      if (storedUserData) {
+        this.user = JSON.parse(storedUserData);
+        console.log('成功加载用户数据:', this.user);
+      } else {
+        console.error('用户数据未找到,请确保已登录!');
+      }
+    }
+  }
   onItemClick(item: any) {
+    if (this.isGuest) {
+      alert('游客登录无权访问此操作!');
+      return;
+    }
     console.log('Clicked item:', item);
-    // Handle the click action for the item
   }
-  constructor() {}
-
 }

+ 12 - 11
src/app/tabs/tabs-routing.module.ts

@@ -1,43 +1,44 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { TabsPage } from './tabs.page';
+import { AuthGuard } from 'src/app/service/auth.guard';
 
 const routes: Routes = [
   {
-    path: 'tabs',
+    path: '',
     component: TabsPage,
     children: [
       {
         path: 'tab1',
-        loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule)
+        loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule),
+        canActivate: [AuthGuard],
       },
       {
         path: 'tab2',
-        loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule)
+        loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule),
+        canActivate: [AuthGuard], 
       },
       {
         path: 'tab3',
-        loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule)
+        loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule),
+        canActivate: [AuthGuard],
       },
       {
         path: 'tab4',
-        loadChildren: () => import('../tab4/tab4.module').then(m => m.Tab4PageModule)
+        loadChildren: () => import('../tab4/tab4.module').then(m => m.Tab4PageModule),
+        canActivate: [AuthGuard],
       },
       {
         path: '',
-        redirectTo: '/tabs/tab1',
+        redirectTo: 'tab1',
         pathMatch: 'full'
       }
     ]
   },
-  {
-    path: '',
-    redirectTo: '/tabs/tab1',
-    pathMatch: 'full'
-  }
 ];
 
 @NgModule({
   imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
 })
 export class TabsPageRoutingModule {}

BIN
src/assets/images/KG-item-background.jpg


+ 0 - 0
src/assets/images/2.jpg → src/assets/images/avatar-frame.jpg


BIN
src/assets/images/button-background2(1).png


BIN
src/assets/images/login-background.jpg


+ 0 - 0
src/assets/images/1.jpg → src/assets/images/user-background.jpg