修复已知问题
This commit is contained in:
parent
fa666d934e
commit
f1e7589344
328
DATABASE_SETUP.md
Normal file
328
DATABASE_SETUP.md
Normal file
@ -0,0 +1,328 @@
|
||||
# 智慧教室管理系统数据库与API配置指南
|
||||
|
||||
本文档详细介绍如何在宝塔面板上配置MySQL数据库和Node.js API服务,以支持智慧教室管理系统的HarmonyOS应用。
|
||||
|
||||
## 一、宝塔面板安装与配置
|
||||
|
||||
### 1.1 安装宝塔面板
|
||||
|
||||
如果尚未安装宝塔面板,请参考[宝塔官方文档](https://www.bt.cn/bbs/thread-19376-1-1.html)进行安装。
|
||||
|
||||
### 1.2 安装必要软件
|
||||
|
||||
在宝塔面板中安装以下软件:
|
||||
- Nginx (用于Web服务器)
|
||||
- MySQL (用于数据库)
|
||||
- PHP (如需使用phpMyAdmin)
|
||||
- Node.js (用于API服务)
|
||||
|
||||
## 二、MySQL数据库配置
|
||||
|
||||
### 2.1 创建数据库
|
||||
|
||||
1. 在宝塔面板左侧找到"数据库"选项
|
||||
2. 点击"添加数据库"
|
||||
3. 填写以下信息:
|
||||
- 数据库名称:`hongm`
|
||||
- 用户名:`hongm`
|
||||
- 密码:`JsKJeG7CX2WnyArt` (或自定义安全密码)
|
||||
- 访问权限:选择"所有人" (开发环境)或指定IP (生产环境)
|
||||
4. 点击"提交"创建数据库
|
||||
|
||||
### 2.2 导入数据结构
|
||||
|
||||
1. 在数据库列表找到刚创建的`hongm`数据库
|
||||
2. 点击"管理"进入phpMyAdmin
|
||||
3. 选择"SQL"标签
|
||||
4. 复制并执行以下SQL语句:
|
||||
|
||||
```sql
|
||||
-- 创建用户信息表
|
||||
CREATE TABLE IF NOT EXISTS UserText (
|
||||
account VARCHAR(20) PRIMARY KEY,
|
||||
nickname VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
phone VARCHAR(20),
|
||||
photo VARCHAR(255) DEFAULT NULL,
|
||||
category VARCHAR(20) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建用户密码表
|
||||
CREATE TABLE IF NOT EXISTS UserPassword (
|
||||
account VARCHAR(20) PRIMARY KEY,
|
||||
password VARCHAR(50) NOT NULL,
|
||||
FOREIGN KEY (account) REFERENCES UserText(account)
|
||||
);
|
||||
|
||||
-- 插入示例用户数据
|
||||
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
|
||||
('2', '张三', 'student@qq.com', '17267383831', 'student'),
|
||||
('9222', '李华', 'student123@qq.com', '12345678901', 'student'),
|
||||
('0', '教师demo', 'teach@qq.com', NULL, 'teacher');
|
||||
|
||||
-- 插入密码(所有密码都是'1')
|
||||
INSERT INTO UserPassword (account, password) VALUES
|
||||
('2', '1'),
|
||||
('9222', '1'),
|
||||
('0', '1');
|
||||
```
|
||||
|
||||
## 三、Node.js API服务配置
|
||||
|
||||
### 3.1 创建网站
|
||||
|
||||
1. 在宝塔面板左侧找到"网站"选项
|
||||
2. 点击"添加站点"
|
||||
3. 填写以下信息:
|
||||
- 域名:`api.yourdomain.com` (或您的服务器IP地址)
|
||||
- 备注:`智慧教室管理系统API服务`
|
||||
- PHP版本:纯静态
|
||||
4. 点击"提交"创建网站
|
||||
|
||||
### 3.2 上传API服务代码
|
||||
|
||||
1. 在网站目录中创建`app.js`文件
|
||||
2. 将以下代码复制到`app.js`中:
|
||||
|
||||
```javascript
|
||||
const express = require('express');
|
||||
const mysql = require('mysql2');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
// 启用CORS和JSON解析
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// 数据库连接配置
|
||||
const dbConfig = {
|
||||
host: 'localhost',
|
||||
port: 3306, // MySQL默认端口,根据您的配置可能需要修改
|
||||
user: 'hongm',
|
||||
password: 'JsKJeG7CX2WnyArt',
|
||||
database: 'hongm'
|
||||
};
|
||||
|
||||
// 创建数据库连接池
|
||||
const pool = mysql.createPool(dbConfig);
|
||||
const promisePool = pool.promise();
|
||||
|
||||
// 验证用户登录API
|
||||
app.post('/api/login', async (req, res) => {
|
||||
try {
|
||||
const { account, password } = req.body;
|
||||
|
||||
// 查询密码
|
||||
const [passwordRows] = await promisePool.query(
|
||||
'SELECT password FROM UserPassword WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (passwordRows.length === 0) {
|
||||
return res.status(401).json({ success: false, message: '账号不存在' });
|
||||
}
|
||||
|
||||
const storedPassword = passwordRows[0].password;
|
||||
|
||||
if (password !== storedPassword) {
|
||||
return res.status(401).json({ success: false, message: '密码错误' });
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
const [userRows] = await promisePool.query(
|
||||
'SELECT * FROM UserText WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (userRows.length === 0) {
|
||||
return res.status(401).json({ success: false, message: '用户信息不存在' });
|
||||
}
|
||||
|
||||
const user = userRows[0];
|
||||
|
||||
// 返回成功登录信息和用户数据
|
||||
res.json({
|
||||
success: true,
|
||||
message: '登录成功',
|
||||
user: {
|
||||
account: user.account,
|
||||
nickname: user.nickname,
|
||||
email: user.email,
|
||||
phone: user.phone,
|
||||
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
|
||||
category: user.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('登录错误:', error);
|
||||
res.status(500).json({ success: false, message: '服务器错误' });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取用户信息API
|
||||
app.get('/api/user/:account', async (req, res) => {
|
||||
try {
|
||||
const { account } = req.params;
|
||||
|
||||
// 查询用户信息
|
||||
const [userRows] = await promisePool.query(
|
||||
'SELECT * FROM UserText WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (userRows.length === 0) {
|
||||
return res.status(404).json({ success: false, message: '用户不存在' });
|
||||
}
|
||||
|
||||
const user = userRows[0];
|
||||
|
||||
// 返回用户数据
|
||||
res.json({
|
||||
success: true,
|
||||
user: {
|
||||
account: user.account,
|
||||
nickname: user.nickname,
|
||||
email: user.email,
|
||||
phone: user.phone,
|
||||
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
|
||||
category: user.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取用户信息错误:', error);
|
||||
res.status(500).json({ success: false, message: '服务器错误' });
|
||||
}
|
||||
});
|
||||
|
||||
// 更新用户邮箱API
|
||||
app.put('/api/user/:account/email', async (req, res) => {
|
||||
try {
|
||||
const { account } = req.params;
|
||||
const { email } = req.body;
|
||||
|
||||
// 验证邮箱格式
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return res.status(400).json({ success: false, message: '邮箱格式无效' });
|
||||
}
|
||||
|
||||
// 更新邮箱
|
||||
const [result] = await promisePool.query(
|
||||
'UPDATE UserText SET email = ? WHERE account = ?',
|
||||
[email, account]
|
||||
);
|
||||
|
||||
if (result.affectedRows === 0) {
|
||||
return res.status(404).json({ success: false, message: '用户不存在' });
|
||||
}
|
||||
|
||||
res.json({ success: true, message: '邮箱更新成功' });
|
||||
} catch (error) {
|
||||
console.error('更新邮箱错误:', error);
|
||||
res.status(500).json({ success: false, message: '服务器错误' });
|
||||
}
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
app.listen(port, () => {
|
||||
console.log(`API服务运行在端口 ${port}`);
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 安装依赖并启动服务
|
||||
|
||||
1. 通过SSH连接到服务器,进入网站目录:
|
||||
```bash
|
||||
cd /www/wwwroot/your_website_directory
|
||||
```
|
||||
|
||||
2. 初始化项目并安装依赖:
|
||||
```bash
|
||||
npm init -y
|
||||
npm install express mysql2 cors body-parser
|
||||
```
|
||||
|
||||
3. 安装PM2并启动服务:
|
||||
```bash
|
||||
npm install -g pm2
|
||||
pm2 start app.js --name "classmg-api"
|
||||
pm2 startup
|
||||
pm2 save
|
||||
```
|
||||
|
||||
### 3.4 配置Nginx反向代理 (可选)
|
||||
|
||||
为了使API更安全和易于访问,可以配置Nginx反向代理:
|
||||
|
||||
1. 在宝塔面板中,找到您的网站,点击"设置"
|
||||
2. 点击"反向代理",添加新的反向代理
|
||||
3. 填写以下信息:
|
||||
- 名称:`API反向代理`
|
||||
- 目标URL:`http://127.0.0.1:3000`
|
||||
- 发送域名:勾选
|
||||
4. 点击"提交"
|
||||
|
||||
现在您可以通过 `http://your_domain/api/` 访问API服务。
|
||||
|
||||
### 3.5 配置防火墙
|
||||
|
||||
确保服务器防火墙允许访问3000端口(如果直接使用Node.js)或80/443端口(如果使用Nginx反向代理)。
|
||||
|
||||
## 四、HarmonyOS应用配置
|
||||
|
||||
### 4.1 修改API地址
|
||||
|
||||
在HarmonyOS应用的`DatabaseService.ets`文件中,确保API地址正确配置:
|
||||
|
||||
```typescript
|
||||
// 定义API服务配置
|
||||
const API_CONFIG: ApiConfig = {
|
||||
// 修改为您的实际服务器地址
|
||||
baseUrl: 'http://your_server_ip:3000/api',
|
||||
timeout: 10000
|
||||
};
|
||||
```
|
||||
|
||||
### 4.2 构建并测试应用
|
||||
|
||||
1. 在DevEco Studio中构建应用
|
||||
2. 在模拟器或真机上测试登录功能
|
||||
3. 尝试使用示例账号登录:
|
||||
- 账号:`2`或`9222`或`0`
|
||||
- 密码:`1`
|
||||
|
||||
## 五、生产环境安全建议
|
||||
|
||||
1. 启用HTTPS:为API服务配置SSL证书
|
||||
2. 实现更安全的密码存储:使用加盐哈希而不是明文存储
|
||||
3. 添加JWT认证:实现令牌机制进行会话管理
|
||||
4. 限制请求频率:防止暴力破解攻击
|
||||
5. 日志记录:记录所有API调用以便审计
|
||||
6. 定期备份数据库:防止数据丢失
|
||||
|
||||
## 六、故障排除
|
||||
|
||||
### 6.1 API连接问题
|
||||
|
||||
如果应用无法连接到API:
|
||||
1. 检查服务器IP和端口是否正确
|
||||
2. 确认防火墙设置允许连接
|
||||
3. 验证API服务是否正在运行 (`pm2 status`)
|
||||
4. 检查服务器日志 (`pm2 logs classmg-api`)
|
||||
|
||||
### 6.2 数据库问题
|
||||
|
||||
如果API服务无法连接到数据库:
|
||||
1. 确认数据库用户名和密码正确
|
||||
2. 检查数据库用户权限
|
||||
3. 验证数据库服务是否正在运行
|
||||
|
||||
### 6.3 降级模式
|
||||
|
||||
应用内已实现降级模式,当无法连接到API服务时,将使用本地模拟数据:
|
||||
- 账号:`2`、`9222`、`0`
|
||||
- 密码:`1`
|
81
README.md
Normal file
81
README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# 智慧教室管理系统
|
||||
|
||||
## 项目概述
|
||||
|
||||
智慧教室管理系统是一款基于HarmonyOS/ArkTS开发的应用,旨在提供智能化的教室管理解决方案。系统包含多个功能模块,用于管理教室状态、课程信息和用户权限等。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- **用户管理**: 支持教师和学生两种用户类型,提供差异化的功能体验
|
||||
- **教室监控**: 实时监控教室温度、湿度、人数等数据
|
||||
- **课程数据**: 展示课程信息、教师信息和学生出勤情况
|
||||
- **个人设置**: 用户可以修改个人信息和系统偏好设置
|
||||
|
||||
## 技术架构
|
||||
|
||||
- **前端**: HarmonyOS/ArkTS
|
||||
- **后端**: MySQL数据库
|
||||
- **通信**: HTTP API
|
||||
|
||||
## 系统要求
|
||||
|
||||
- HarmonyOS设备
|
||||
- 网络连接
|
||||
- 支持HarmonyOS 3.0及以上版本
|
||||
|
||||
## 数据库配置
|
||||
|
||||
系统使用MySQL数据库存储用户信息和系统数据。数据库配置如下:
|
||||
|
||||
- **数据库地址**: 139.155.155.67:25342
|
||||
- **数据库名**: hongm
|
||||
- **用户名**: hongm
|
||||
- **密码**: JsKJeG7CX2WnyArt
|
||||
|
||||
### 数据库初始化
|
||||
|
||||
导入`database_setup.sql`文件以创建必要的表和初始数据。主要的数据表包括:
|
||||
|
||||
- **UserText**: 存储用户个人信息
|
||||
- **UserPassword**: 存储用户登录信息
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 登录系统
|
||||
|
||||
1. 在登录页面输入您的账号和密码(默认密码: 1)
|
||||
2. 系统会根据账号自动识别用户类型:
|
||||
- 账号包含"2"的识别为学生
|
||||
- 账号包含"0"的识别为教师
|
||||
3. 登录成功后将显示过渡页面,然后进入系统主页
|
||||
|
||||
### 系统导航
|
||||
|
||||
系统包含三个主要页面:
|
||||
|
||||
- **首页**: 显示教室监控和综合上课数据
|
||||
- **上课**: 提供课程相关功能
|
||||
- **设置**: 管理个人信息和系统设置
|
||||
|
||||
### 个人信息设置
|
||||
|
||||
在设置页面,用户可以查看和修改个人信息:
|
||||
|
||||
- **头像**: 显示用户头像
|
||||
- **账号**: 显示当前登录账号(不可修改)
|
||||
- **昵称**: 显示用户昵称(不可修改)
|
||||
- **邮箱**: 可修改,需符合邮箱格式
|
||||
- **电话**: 显示联系电话(不可修改)
|
||||
|
||||
## 开发信息
|
||||
|
||||
- **开发团队**: 922213102班鸿蒙第一组
|
||||
- **版本**: 1.0.0
|
||||
|
||||
## 备注
|
||||
|
||||
- 用户密码默认为"1"
|
||||
- 系统现有示例用户:
|
||||
- 学生: 账号"2",昵称"张三"
|
||||
- 学生: 账号"9222",昵称"李华"
|
||||
- 教师: 账号"0",昵称"教师demo"
|
255
api-app.js
Normal file
255
api-app.js
Normal file
@ -0,0 +1,255 @@
|
||||
const express = require('express');
|
||||
const mysql = require('mysql2');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 创建Express应用
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
// 启用CORS和JSON解析
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// 创建logs目录(如果不存在)
|
||||
const logsDir = path.join(__dirname, 'logs');
|
||||
if (!fs.existsSync(logsDir)) {
|
||||
fs.mkdirSync(logsDir);
|
||||
}
|
||||
|
||||
// 简单日志函数
|
||||
function logToFile(message) {
|
||||
const logFile = path.join(logsDir, 'api.log');
|
||||
const timestamp = new Date().toISOString();
|
||||
fs.appendFileSync(logFile, `${timestamp} - ${message}\n`);
|
||||
console.log(`${timestamp} - ${message}`);
|
||||
}
|
||||
|
||||
// 数据库连接配置
|
||||
const dbConfig = {
|
||||
host: 'localhost',
|
||||
port: 25342, // 修改为正确的MySQL端口
|
||||
user: 'hongm', // 替换为您的数据库用户名
|
||||
password: 'hongm', // 替换为您的数据库密码
|
||||
database: 'hongm',
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
};
|
||||
|
||||
// 创建数据库连接池
|
||||
const pool = mysql.createPool(dbConfig);
|
||||
const promisePool = pool.promise();
|
||||
|
||||
// 检查数据库连接
|
||||
async function checkDbConnection() {
|
||||
try {
|
||||
const connection = await promisePool.getConnection();
|
||||
logToFile('数据库连接成功');
|
||||
connection.release();
|
||||
return true;
|
||||
} catch (error) {
|
||||
logToFile(`数据库连接失败: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 在启动时测试数据库连接
|
||||
checkDbConnection();
|
||||
|
||||
// 创建一个简单的首页响应
|
||||
app.get('/', (req, res) => {
|
||||
res.send('智能教室管理系统API服务正在运行');
|
||||
});
|
||||
|
||||
// 创建一个图片目录路由
|
||||
app.use('/images', express.static(path.join(__dirname, 'images')));
|
||||
|
||||
// API: 用户登录验证
|
||||
app.post('/api/login', async (req, res) => {
|
||||
try {
|
||||
logToFile(`收到登录请求: ${JSON.stringify(req.body)}`);
|
||||
const { account, password } = req.body;
|
||||
|
||||
if (!account || !password) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '账号和密码不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询密码
|
||||
const [passwordRows] = await promisePool.query(
|
||||
'SELECT password FROM UserPassword WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (passwordRows.length === 0) {
|
||||
logToFile(`登录失败: 账号 ${account} 不存在`);
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '账号不存在'
|
||||
});
|
||||
}
|
||||
|
||||
const storedPassword = passwordRows[0].password;
|
||||
|
||||
if (password !== storedPassword) {
|
||||
logToFile(`登录失败: 账号 ${account} 密码错误`);
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
const [userRows] = await promisePool.query(
|
||||
'SELECT * FROM UserText WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (userRows.length === 0) {
|
||||
logToFile(`登录异常: 账号 ${account} 存在但用户信息丢失`);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: '用户信息不存在'
|
||||
});
|
||||
}
|
||||
|
||||
const user = userRows[0];
|
||||
logToFile(`登录成功: 账号 ${account}, 用户名 ${user.nickname}`);
|
||||
|
||||
// 返回成功登录信息和用户数据
|
||||
res.json({
|
||||
success: true,
|
||||
message: '登录成功',
|
||||
user: {
|
||||
account: user.account,
|
||||
nickname: user.nickname,
|
||||
email: user.email || '',
|
||||
phone: user.phone || '',
|
||||
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
|
||||
category: user.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logToFile(`登录错误: ${error.message}`);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API: 获取用户信息
|
||||
app.get('/api/user/:account', async (req, res) => {
|
||||
try {
|
||||
const { account } = req.params;
|
||||
logToFile(`获取用户信息请求: 账号 ${account}`);
|
||||
|
||||
// 查询用户信息
|
||||
const [userRows] = await promisePool.query(
|
||||
'SELECT * FROM UserText WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (userRows.length === 0) {
|
||||
logToFile(`获取用户信息失败: 账号 ${account} 不存在`);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
});
|
||||
}
|
||||
|
||||
const user = userRows[0];
|
||||
logToFile(`获取用户信息成功: 账号 ${account}, 用户名 ${user.nickname}`);
|
||||
|
||||
// 返回用户数据
|
||||
res.json({
|
||||
success: true,
|
||||
user: {
|
||||
account: user.account,
|
||||
nickname: user.nickname,
|
||||
email: user.email || '',
|
||||
phone: user.phone || '',
|
||||
photo: user.photo || 'http://139.155.155.67:2342/images/default_avatar.png',
|
||||
category: user.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logToFile(`获取用户信息错误: ${error.message}`);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// API: 更新用户邮箱
|
||||
app.put('/api/user/:account/email', async (req, res) => {
|
||||
try {
|
||||
const { account } = req.params;
|
||||
const { email } = req.body;
|
||||
logToFile(`更新用户邮箱请求: 账号 ${account}, 新邮箱 ${email}`);
|
||||
|
||||
// 验证邮箱格式
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
logToFile(`更新邮箱失败: 邮箱格式无效 ${email}`);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '邮箱格式无效'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
const [userCheck] = await promisePool.query(
|
||||
'SELECT account FROM UserText WHERE account = ?',
|
||||
[account]
|
||||
);
|
||||
|
||||
if (userCheck.length === 0) {
|
||||
logToFile(`更新邮箱失败: 账号 ${account} 不存在`);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '用户不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新邮箱
|
||||
const [result] = await promisePool.query(
|
||||
'UPDATE UserText SET email = ? WHERE account = ?',
|
||||
[email, account]
|
||||
);
|
||||
|
||||
if (result.affectedRows === 0) {
|
||||
logToFile(`更新邮箱失败: 账号 ${account} 无法更新`);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: '更新邮箱失败'
|
||||
});
|
||||
}
|
||||
|
||||
logToFile(`更新邮箱成功: 账号 ${account}, 新邮箱 ${email}`);
|
||||
res.json({
|
||||
success: true,
|
||||
message: '邮箱更新成功'
|
||||
});
|
||||
} catch (error) {
|
||||
logToFile(`更新邮箱错误: ${error.message}`);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
app.listen(port, () => {
|
||||
logToFile(`API服务运行在端口 ${port}`);
|
||||
});
|
16
api-package.json
Normal file
16
api-package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "classmg-api",
|
||||
"version": "1.0.0",
|
||||
"description": "智能教室管理系统API服务",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
"dev": "nodemon app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"mysql2": "^3.6.1"
|
||||
}
|
||||
}
|
11
build-script.sh
Executable file
11
build-script.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Cleaning project..."
|
||||
rm -rf build/
|
||||
rm -rf build-profile/
|
||||
|
||||
echo "Building project..."
|
||||
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js --mode module -p module=entry@default -p product=default -p requiredDeviceType=phone clean
|
||||
|
||||
echo "Rebuilding project..."
|
||||
/Applications/DevEco-Studio.app/Contents/tools/node/bin/node /Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js --mode module -p module=entry@default -p product=default -p requiredDeviceType=phone assembleHap --analyze=normal --parallel --incremental --daemon
|
33
database_setup.sql
Normal file
33
database_setup.sql
Normal file
@ -0,0 +1,33 @@
|
||||
-- Create the UserText table to store user information
|
||||
CREATE TABLE IF NOT EXISTS UserText (
|
||||
account VARCHAR(20) PRIMARY KEY,
|
||||
nickname VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
phone VARCHAR(20),
|
||||
photo VARCHAR(255) DEFAULT NULL,
|
||||
category VARCHAR(20) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Create the UserPassword table to store user account and password
|
||||
CREATE TABLE IF NOT EXISTS UserPassword (
|
||||
account VARCHAR(20) PRIMARY KEY,
|
||||
password VARCHAR(50) NOT NULL,
|
||||
FOREIGN KEY (account) REFERENCES UserText(account)
|
||||
);
|
||||
|
||||
-- Insert sample users as specified in the requirements
|
||||
-- Student users
|
||||
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
|
||||
('2', '张三', 'student@qq.com', '17267383831', 'student'),
|
||||
('9222', '李华', 'student123@qq.com', '12345678901', 'student');
|
||||
|
||||
-- Teacher user
|
||||
INSERT INTO UserText (account, nickname, email, phone, category) VALUES
|
||||
('0', '教师demo', 'teach@qq.com', NULL, 'teacher');
|
||||
|
||||
-- Insert passwords (all passwords are '1')
|
||||
INSERT INTO UserPassword (account, password) VALUES
|
||||
('2', '1'),
|
||||
('9222', '1'),
|
||||
('0', '1');
|
415
entry/src/main/ets/common/DatabaseService.ets
Normal file
415
entry/src/main/ets/common/DatabaseService.ets
Normal file
@ -0,0 +1,415 @@
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import prompt from '@ohos.promptAction';
|
||||
import http from '@ohos.net.http';
|
||||
|
||||
// User model for storing user information from database
|
||||
export class UserModel {
|
||||
account: string = '';
|
||||
nickname: string = '';
|
||||
email: string = '';
|
||||
phone: string = '';
|
||||
photo: string = '';
|
||||
category: string = '';
|
||||
|
||||
constructor(account: string = '',
|
||||
nickname: string = '',
|
||||
email: string = '',
|
||||
phone: string = '',
|
||||
photo: string = '',
|
||||
category: string = '') {
|
||||
this.account = account;
|
||||
this.nickname = nickname;
|
||||
this.email = email;
|
||||
this.phone = phone;
|
||||
this.photo = photo || 'http://139.155.155.67:2342/images/default_avatar.png'; // Default photo URL
|
||||
this.category = category;
|
||||
}
|
||||
}
|
||||
|
||||
// API响应接口定义
|
||||
export interface ApiResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
user?: UserInfo;
|
||||
}
|
||||
|
||||
// User info returned from API
|
||||
export interface UserInfo {
|
||||
account: string;
|
||||
nickname: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
photo?: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
// API服务配置
|
||||
export interface ApiConfig {
|
||||
baseUrl: string;
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
// 定义API服务配置
|
||||
const API_CONFIG: ApiConfig = {
|
||||
// 修改为用户实际使用的服务器地址
|
||||
baseUrl: 'http://139.155.155.67:2342/api',
|
||||
timeout: 10000 // 10秒超时
|
||||
};
|
||||
|
||||
// 修正JSON响应类型处理
|
||||
export type JsonResponse = string | object | ArrayBuffer;
|
||||
|
||||
// 真实API实现 - 连接到后端服务
|
||||
export class DatabaseService {
|
||||
private static instance: DatabaseService;
|
||||
// 缓存用户数据,减少API调用
|
||||
private userCache: Map<string, UserModel> = new Map();
|
||||
// 缓存验证结果
|
||||
private authCache: Map<string, boolean> = new Map();
|
||||
|
||||
// Callbacks for user data changes
|
||||
private userDataChangeCallbacks: (() => void)[] = [];
|
||||
|
||||
private constructor() {
|
||||
// 初始化缓存
|
||||
hilog.info(0, 'ClassMG', 'Database Service Initialized');
|
||||
}
|
||||
|
||||
public static getInstance(): DatabaseService {
|
||||
if (!DatabaseService.instance) {
|
||||
DatabaseService.instance = new DatabaseService();
|
||||
}
|
||||
return DatabaseService.instance;
|
||||
}
|
||||
|
||||
// 创建HTTP请求客户端
|
||||
private createHttpClient(): http.HttpRequest {
|
||||
let httpRequest = http.createHttp();
|
||||
httpRequest.on('headersReceive', (header) => {
|
||||
hilog.debug(0, 'ClassMG', `Headers received: ${JSON.stringify(header)}`);
|
||||
});
|
||||
return httpRequest;
|
||||
}
|
||||
|
||||
// 执行登录验证
|
||||
public async validateUser(account: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
// 生成缓存键
|
||||
const cacheKey = `${account}:${password}`;
|
||||
|
||||
// 检查缓存
|
||||
if (this.authCache.has(cacheKey)) {
|
||||
const cachedResult = this.authCache.get(cacheKey);
|
||||
return cachedResult === true; // 确保返回布尔值
|
||||
}
|
||||
|
||||
// 创建HTTP客户端
|
||||
const httpRequest = this.createHttpClient();
|
||||
|
||||
// 准备登录数据
|
||||
let loginData = {} as Record<string, string>;
|
||||
loginData.account = account;
|
||||
loginData.password = password;
|
||||
|
||||
// 发送登录请求
|
||||
const response = await httpRequest.request(
|
||||
`${API_CONFIG.baseUrl}/login`,
|
||||
{
|
||||
method: http.RequestMethod.POST,
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
extraData: JSON.stringify(loginData),
|
||||
connectTimeout: API_CONFIG.timeout,
|
||||
readTimeout: API_CONFIG.timeout
|
||||
}
|
||||
);
|
||||
|
||||
// 释放HTTP客户端
|
||||
httpRequest.destroy();
|
||||
|
||||
// 检查响应
|
||||
if (response.responseCode === 200) {
|
||||
// 使用显式类型转换处理JSON结果
|
||||
const jsonString: string = response.result ? response.result.toString() : '{}';
|
||||
try {
|
||||
const result: ApiResponse = JSON.parse(jsonString) as ApiResponse;
|
||||
|
||||
// 缓存结果
|
||||
const isValid = result.success === true;
|
||||
this.authCache.set(cacheKey, isValid);
|
||||
|
||||
// 如果登录成功且有用户数据,缓存用户信息
|
||||
if (isValid && result.user) {
|
||||
const user = new UserModel(
|
||||
result.user.account,
|
||||
result.user.nickname,
|
||||
result.user.email,
|
||||
result.user.phone,
|
||||
result.user.photo,
|
||||
result.user.category
|
||||
);
|
||||
this.userCache.set(account, user);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
} catch (parseError) {
|
||||
const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError);
|
||||
hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 默认返回false
|
||||
return false;
|
||||
} catch (error) {
|
||||
hilog.error(0, 'ClassMG', `Error validating user: ${error instanceof Error ? error.message : String(error)}`);
|
||||
|
||||
// 临时降级到模拟验证 - 仅用于开发/测试
|
||||
if (account === '2' || account === '9222' || account === '0') {
|
||||
return password === '1';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户类别
|
||||
public getUserCategory(account: string): string {
|
||||
// 检查缓存
|
||||
if (this.userCache.has(account)) {
|
||||
const user = this.userCache.get(account);
|
||||
return user ? user.category : '';
|
||||
}
|
||||
|
||||
// 降级逻辑 - 当API不可用时
|
||||
if (account.includes('2')) {
|
||||
return 'student';
|
||||
} else if (account.includes('0')) {
|
||||
return 'teacher';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// 获取用户昵称
|
||||
public getUserNickname(account: string): string {
|
||||
// 检查缓存
|
||||
if (this.userCache.has(account)) {
|
||||
const user = this.userCache.get(account);
|
||||
return user ? user.nickname : '';
|
||||
}
|
||||
|
||||
// 默认值 - 仅用于开发/测试
|
||||
if (account === '2') return '张三';
|
||||
if (account === '9222') return '李华';
|
||||
if (account === '0') return '教师demo';
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// 异步获取用户信息
|
||||
public async getUserByAccountAsync(account: string): Promise<UserModel | null> {
|
||||
try {
|
||||
// 检查缓存
|
||||
if (this.userCache.has(account)) {
|
||||
const cachedUser = this.userCache.get(account);
|
||||
return cachedUser || null;
|
||||
}
|
||||
|
||||
// 创建HTTP客户端
|
||||
const httpRequest = this.createHttpClient();
|
||||
|
||||
// 发送获取用户信息请求
|
||||
const response = await httpRequest.request(
|
||||
`${API_CONFIG.baseUrl}/user/${account}`,
|
||||
{
|
||||
method: http.RequestMethod.GET,
|
||||
connectTimeout: API_CONFIG.timeout,
|
||||
readTimeout: API_CONFIG.timeout
|
||||
}
|
||||
);
|
||||
|
||||
// 释放HTTP客户端
|
||||
httpRequest.destroy();
|
||||
|
||||
// 检查响应
|
||||
if (response.responseCode === 200) {
|
||||
// 使用显式类型转换处理JSON结果
|
||||
const jsonString: string = response.result ? response.result.toString() : '{}';
|
||||
try {
|
||||
const result: ApiResponse = JSON.parse(jsonString) as ApiResponse;
|
||||
|
||||
if (result.success && result.user) {
|
||||
const user = new UserModel(
|
||||
result.user.account,
|
||||
result.user.nickname,
|
||||
result.user.email,
|
||||
result.user.phone,
|
||||
result.user.photo,
|
||||
result.user.category
|
||||
);
|
||||
|
||||
// 更新缓存
|
||||
this.userCache.set(account, user);
|
||||
|
||||
return user;
|
||||
}
|
||||
} catch (parseError) {
|
||||
const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError);
|
||||
hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
hilog.error(0, 'ClassMG', `Error getting user: ${error instanceof Error ? error.message : String(error)}`);
|
||||
|
||||
// 降级到模拟数据 - 仅用于开发/测试
|
||||
if (account === '2') {
|
||||
return new UserModel('2', '张三', 'student@qq.com', '17267383831', '', 'student');
|
||||
} else if (account === '9222') {
|
||||
return new UserModel('9222', '李华', 'student123@qq.com', '12345678901', '', 'student');
|
||||
} else if (account === '0') {
|
||||
return new UserModel('0', '教师demo', 'teach@qq.com', '', '', 'teacher');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 为了兼容性保留的同步方法 - 使用缓存
|
||||
public getUserByAccount(account: string): UserModel | null {
|
||||
// 检查缓存
|
||||
if (this.userCache.has(account)) {
|
||||
const cachedUser = this.userCache.get(account);
|
||||
return cachedUser || null;
|
||||
}
|
||||
|
||||
// 如果缓存中没有,返回默认模拟数据
|
||||
if (account === '2') {
|
||||
return new UserModel('2', '张三', 'student@qq.com', '17267383831', '', 'student');
|
||||
} else if (account === '9222') {
|
||||
return new UserModel('9222', '李华', 'student123@qq.com', '12345678901', '', 'student');
|
||||
} else if (account === '0') {
|
||||
return new UserModel('0', '教师demo', 'teach@qq.com', '', '', 'teacher');
|
||||
}
|
||||
|
||||
// 触发异步加载并返回null
|
||||
this.getUserByAccountAsync(account)
|
||||
.then((user: UserModel | null) => {
|
||||
if (user) {
|
||||
this.notifyUserDataChange();
|
||||
}
|
||||
})
|
||||
.catch((error: Error | string | Object) => {
|
||||
const errorMsg: string = error instanceof Error ? error.message : String(error);
|
||||
hilog.error(0, 'ClassMG', `Error in async user fetch: ${errorMsg}`);
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新用户邮箱 - 连接API
|
||||
public async updateUserEmail(account: string, newEmail: string): Promise<boolean> {
|
||||
try {
|
||||
// 验证邮箱格式
|
||||
if (!this.validateEmailFormat(newEmail)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建HTTP客户端
|
||||
const httpRequest = this.createHttpClient();
|
||||
|
||||
// 准备更新数据
|
||||
let updateData = {} as Record<string, string>;
|
||||
updateData.email = newEmail;
|
||||
|
||||
// 发送更新请求
|
||||
const response = await httpRequest.request(
|
||||
`${API_CONFIG.baseUrl}/user/${account}/email`,
|
||||
{
|
||||
method: http.RequestMethod.PUT,
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
extraData: JSON.stringify(updateData),
|
||||
connectTimeout: API_CONFIG.timeout,
|
||||
readTimeout: API_CONFIG.timeout
|
||||
}
|
||||
);
|
||||
|
||||
// 释放HTTP客户端
|
||||
httpRequest.destroy();
|
||||
|
||||
// 检查响应
|
||||
if (response.responseCode === 200) {
|
||||
// 使用显式类型转换处理JSON结果
|
||||
const jsonString: string = response.result ? response.result.toString() : '{}';
|
||||
try {
|
||||
const result: ApiResponse = JSON.parse(jsonString) as ApiResponse;
|
||||
|
||||
if (result.success) {
|
||||
// 更新本地缓存
|
||||
if (this.userCache.has(account)) {
|
||||
const user = this.userCache.get(account);
|
||||
if (user) {
|
||||
user.email = newEmail;
|
||||
this.userCache.set(account, user);
|
||||
}
|
||||
}
|
||||
|
||||
// 通知更新
|
||||
this.notifyUserDataChange();
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (parseError) {
|
||||
const errorMsg: string = parseError instanceof Error ? parseError.message : String(parseError);
|
||||
hilog.error(0, 'ClassMG', `Error parsing JSON response: ${errorMsg}`);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
const errorMessage: string = error instanceof Error ? error.message : String(error);
|
||||
hilog.error(0, 'ClassMG', `Error updating user email: ${errorMessage}`);
|
||||
|
||||
// 降级到本地更新 - 仅用于开发/测试
|
||||
if (this.userCache.has(account)) {
|
||||
const user = this.userCache.get(account);
|
||||
if (user) {
|
||||
user.email = newEmail;
|
||||
this.userCache.set(account, user);
|
||||
this.notifyUserDataChange();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册数据变化回调
|
||||
public registerUserDataChangeCallback(callback: () => void): void {
|
||||
this.userDataChangeCallbacks.push(callback);
|
||||
}
|
||||
|
||||
// 通知所有回调
|
||||
private notifyUserDataChange(): void {
|
||||
this.userDataChangeCallbacks.forEach(callback => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// 验证邮箱格式
|
||||
public validateEmailFormat(email: string): boolean {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
public clearCache(): void {
|
||||
this.userCache.clear();
|
||||
this.authCache.clear();
|
||||
}
|
||||
}
|
260
entry/src/main/ets/pages/TransitionPage.ets
Normal file
260
entry/src/main/ets/pages/TransitionPage.ets
Normal file
@ -0,0 +1,260 @@
|
||||
import { router } from '@kit.ArkUI';
|
||||
import settingsService from '../common/SettingsService';
|
||||
import logManager, { LogCategory, LogEventType } from '../common/logtext';
|
||||
|
||||
// 粒子信息类型
|
||||
class ParticleInfo {
|
||||
x: number = 0;
|
||||
y: number = 0;
|
||||
size: number = 0;
|
||||
opacity: number = 0;
|
||||
speed: number = 0;
|
||||
color: string = '#ffffff';
|
||||
|
||||
constructor(x: number = 0,
|
||||
y: number = 0,
|
||||
size: number = 0,
|
||||
opacity: number = 0,
|
||||
speed: number = 0,
|
||||
color: string = '#ffffff') {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.size = size;
|
||||
this.opacity = opacity;
|
||||
this.speed = speed;
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
struct TransitionPage {
|
||||
@State userName: string = '';
|
||||
@State welcomeMessage: string = '欢迎使用智慧教室管理系统';
|
||||
@State contentOpacity: number = 0;
|
||||
@State scaleValue: number = 0.8;
|
||||
@State rotationAngle: number = -10;
|
||||
@State translateYValue: number = 100;
|
||||
|
||||
// 为粒子动画定义状态
|
||||
@State particles: ParticleInfo[] = [];
|
||||
private particleCount: number = 30; // 降低粒子数量避免性能问题
|
||||
private particleTimer: number = 0;
|
||||
private bgColorStop: number = 0;
|
||||
@State backgroundGradient: string = 'linear-gradient(45deg, #4c71f2, #42a7f0)';
|
||||
|
||||
// 创建粒子
|
||||
private createParticles() {
|
||||
let newParticles: ParticleInfo[] = [];
|
||||
for (let i = 0; i < this.particleCount; i++) {
|
||||
let particle = new ParticleInfo(
|
||||
Math.random() * 100, // x位置 (百分比)
|
||||
Math.random() * 100, // y位置 (百分比)
|
||||
Math.random() * 8 + 2, // 粒子大小
|
||||
Math.random() * 0.6 + 0.1, // 初始透明度
|
||||
Math.random() * 0.5 + 0.1, // 移动速度
|
||||
i % 3 === 0 ? '#ffffff' : (i % 3 === 1 ? '#aaccff' : '#77aaff') // 随机颜色
|
||||
);
|
||||
newParticles.push(particle);
|
||||
}
|
||||
this.particles = newParticles;
|
||||
}
|
||||
|
||||
// 更新粒子动画
|
||||
private updateParticles() {
|
||||
this.particleTimer = setInterval(() => {
|
||||
let updatedParticles: ParticleInfo[] = [];
|
||||
|
||||
for (let i = 0; i < this.particles.length; i++) {
|
||||
let p = this.particles[i];
|
||||
// 更新位置
|
||||
let newY = p.y - p.speed;
|
||||
if (newY < -5) {
|
||||
newY = 105; // 当粒子移出屏幕顶部时,从底部重新进入
|
||||
p.x = Math.random() * 100;
|
||||
}
|
||||
|
||||
let newOpacity = p.opacity + (Math.random() * 0.1 - 0.05);
|
||||
// 确保透明度在合理范围内
|
||||
if (newOpacity < 0.1) newOpacity = 0.1;
|
||||
if (newOpacity > 0.8) newOpacity = 0.8;
|
||||
|
||||
// 创建更新后的粒子对象
|
||||
let updatedParticle = new ParticleInfo(
|
||||
p.x,
|
||||
newY,
|
||||
p.size,
|
||||
newOpacity,
|
||||
p.speed,
|
||||
p.color
|
||||
);
|
||||
|
||||
updatedParticles.push(updatedParticle);
|
||||
}
|
||||
|
||||
this.particles = updatedParticles;
|
||||
|
||||
// 更新背景渐变
|
||||
this.bgColorStop = (this.bgColorStop + 0.005) % 1;
|
||||
const hue1 = Math.floor((220 + Math.sin(this.bgColorStop * Math.PI * 2) * 20) % 360);
|
||||
const hue2 = Math.floor((190 + Math.cos(this.bgColorStop * Math.PI * 2) * 20) % 360);
|
||||
this.backgroundGradient = `linear-gradient(45deg, hsl(${hue1}, 70%, 60%), hsl(${hue2}, 70%, 60%))`;
|
||||
|
||||
}, 50);
|
||||
}
|
||||
|
||||
aboutToAppear() {
|
||||
// 设置用户名称
|
||||
this.userName = settingsService.getUserNickname();
|
||||
|
||||
// 创建并开始粒子动画
|
||||
this.createParticles();
|
||||
this.updateParticles();
|
||||
|
||||
// 第一阶段动画:元素进入
|
||||
animateTo({ duration: 1000, curve: Curve.Ease }, () => {
|
||||
this.contentOpacity = 1;
|
||||
this.scaleValue = 1;
|
||||
this.rotationAngle = 0;
|
||||
this.translateYValue = 0;
|
||||
});
|
||||
|
||||
// 3秒后开始退出动画
|
||||
setTimeout(() => {
|
||||
// 第二阶段动画:放大效果
|
||||
animateTo({ duration: 300, curve: Curve.EaseIn }, () => {
|
||||
this.scaleValue = 1.1;
|
||||
this.rotationAngle = 0;
|
||||
});
|
||||
|
||||
// 第三阶段动画:淡出效果
|
||||
setTimeout(() => {
|
||||
animateTo({ duration: 700, curve: Curve.EaseOut }, () => {
|
||||
this.contentOpacity = 0;
|
||||
this.scaleValue = 0.5;
|
||||
this.translateYValue = -50;
|
||||
});
|
||||
|
||||
// 等待动画结束后跳转
|
||||
setTimeout(() => {
|
||||
// 停止粒子动画定时器
|
||||
clearInterval(this.particleTimer);
|
||||
router.pushUrl({
|
||||
url: 'pages/HomePage'
|
||||
});
|
||||
}, 700);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
|
||||
// 记录过渡
|
||||
this.Log_Event('aboutToAppear');
|
||||
}
|
||||
|
||||
aboutToDisappear() {
|
||||
// 清除粒子动画定时器
|
||||
if (this.particleTimer) {
|
||||
clearInterval(this.particleTimer);
|
||||
}
|
||||
this.Log_Event('aboutToDisappear');
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
// 粒子动画层
|
||||
Stack() {
|
||||
// 渐变背景
|
||||
Column()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundImage(this.backgroundGradient)
|
||||
|
||||
// 粒子效果
|
||||
ForEach(this.particles, (item: ParticleInfo) => {
|
||||
Circle()
|
||||
.fill(item.color)
|
||||
.width(item.size)
|
||||
.height(item.size)
|
||||
.position({ x: `${item.x}%`, y: `${item.y}%` })
|
||||
.opacity(item.opacity)
|
||||
})
|
||||
|
||||
// 内容层
|
||||
Column() {
|
||||
Image('http://139.155.155.67:2342/images/default_avatar.png')
|
||||
.width(120)
|
||||
.height(120)
|
||||
.borderRadius(60)
|
||||
.margin({ bottom: 40 })
|
||||
|
||||
Text(this.userName)
|
||||
.fontSize(42)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.fontColor('#FFFFFF')
|
||||
.margin({ bottom: 20 })
|
||||
|
||||
Text(this.welcomeMessage)
|
||||
.fontSize(26)
|
||||
.fontColor('rgba(255, 255, 255, 0.9)')
|
||||
|
||||
Row()
|
||||
.width(150)
|
||||
.height(4)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.borderRadius(2)
|
||||
.margin({ top: 30 })
|
||||
.opacity(0.7)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.opacity(this.contentOpacity)
|
||||
.scale({ x: this.scaleValue, y: this.scaleValue })
|
||||
.rotate({ angle: this.rotationAngle })
|
||||
.translate({ y: this.translateYValue })
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
|
||||
// 页面生命周期方法
|
||||
onPageShow(): void {
|
||||
this.Log_Event('onPageShow');
|
||||
}
|
||||
|
||||
onPageHide(): void {
|
||||
this.Log_Event('onPageHide');
|
||||
}
|
||||
|
||||
onBackPress(): boolean {
|
||||
this.Log_Event('onBackPress');
|
||||
return true; // 防止返回
|
||||
}
|
||||
|
||||
// 日志记录方法
|
||||
public Log_Event(eventName: string): void {
|
||||
// 根据事件名称选择合适的日志事件类型
|
||||
switch (eventName) {
|
||||
case 'aboutToAppear':
|
||||
logManager.info(LogCategory.USER, LogEventType.PAGE_APPEAR, 'TransitionPage');
|
||||
break;
|
||||
case 'aboutToDisappear':
|
||||
logManager.info(LogCategory.USER, LogEventType.PAGE_DISAPPEAR, 'TransitionPage');
|
||||
break;
|
||||
case 'onPageShow':
|
||||
logManager.info(LogCategory.USER, LogEventType.PAGE_SHOW, 'TransitionPage');
|
||||
break;
|
||||
case 'onPageHide':
|
||||
logManager.info(LogCategory.USER, LogEventType.PAGE_HIDE, 'TransitionPage');
|
||||
break;
|
||||
case 'onBackPress':
|
||||
logManager.info(LogCategory.USER, LogEventType.PAGE_BACK, 'TransitionPage');
|
||||
break;
|
||||
default:
|
||||
logManager.info(LogCategory.USER, LogEventType.SYSTEM_INFO, `TransitionPage: ${eventName}`);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user