用户登录功能
This commit is contained in:
13
.env.example
Normal file
13
.env.example
Normal file
@@ -0,0 +1,13 @@
|
||||
# Flask配置
|
||||
SECRET_KEY=your-secret-key-here-please-change-this
|
||||
FLASK_ENV=production
|
||||
|
||||
# OpenAI API配置(可选,用于AI润色功能)
|
||||
OPENAI_API_KEY=
|
||||
|
||||
# 默认用户配置
|
||||
# Docker首次启动时会自动创建此用户
|
||||
DEFAULT_USERNAME=admin
|
||||
DEFAULT_PASSWORD=admin123
|
||||
|
||||
# 注意: 生产环境请务必修改默认密码!
|
||||
@@ -24,10 +24,14 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
||||
# 复制项目文件
|
||||
COPY backend/ /app/backend/
|
||||
COPY frontend/ /app/frontend/
|
||||
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
|
||||
|
||||
# 创建数据目录
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# 设置启动脚本权限
|
||||
RUN chmod +x /app/docker-entrypoint.sh
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5000
|
||||
|
||||
@@ -35,4 +39,4 @@ EXPOSE 5000
|
||||
WORKDIR /app/backend
|
||||
|
||||
# 启动应用
|
||||
CMD ["python", "app.py"]
|
||||
CMD ["/app/docker-entrypoint.sh"]
|
||||
|
||||
82
README.md
82
README.md
@@ -4,12 +4,14 @@
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🔐 **用户认证**: 密码登录保护,确保数据安全
|
||||
- ✅ **任务管理**: 添加、编辑、删除工作任务
|
||||
- ⏱️ **时间追踪**: 点击开始/停止计时,自动记录任务时长
|
||||
- 📊 **时间统计**: 查看每日、每周的工作时间统计
|
||||
- 🤖 **AI润色**: 使用AI优化任务描述,让工作记录更专业
|
||||
- 📱 **响应式设计**: 支持桌面和移动设备
|
||||
- 💾 **本地存储**: 数据存储在本地SQLite数据库中
|
||||
- 🐳 **Docker支持**: 一键部署,开箱即用
|
||||
|
||||
## 技术栈
|
||||
|
||||
@@ -20,12 +22,40 @@
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 环境要求
|
||||
### 方法一: 使用Docker (推荐)
|
||||
|
||||
```bash
|
||||
# 1. 克隆项目
|
||||
git clone <repository-url>
|
||||
cd worklist
|
||||
|
||||
# 2. (可选) 配置环境变量
|
||||
cp .env.example .env
|
||||
# 编辑 .env 文件,设置自定义用户名和密码
|
||||
|
||||
# 3. 启动Docker容器
|
||||
docker-compose up -d
|
||||
|
||||
# 4. 访问应用
|
||||
# 打开浏览器访问 http://localhost:5000
|
||||
# 默认用户名: admin
|
||||
# 默认密码: admin123
|
||||
```
|
||||
|
||||
**首次启动说明:**
|
||||
- Docker会自动创建数据库和默认用户
|
||||
- 默认用户名: `admin`, 默认密码: `admin123`
|
||||
- 可通过环境变量 `DEFAULT_USERNAME` 和 `DEFAULT_PASSWORD` 自定义
|
||||
- 登录后请立即修改密码
|
||||
|
||||
### 方法二: 本地运行
|
||||
|
||||
#### 1. 环境要求
|
||||
|
||||
- Python 3.7 或更高版本
|
||||
- 现代浏览器 (Chrome, Firefox, Safari, Edge)
|
||||
|
||||
### 2. 安装和运行
|
||||
#### 2. 安装和运行
|
||||
|
||||
#### 方法一:使用启动脚本(推荐)
|
||||
|
||||
@@ -44,30 +74,64 @@ python start.py
|
||||
- 启动服务器
|
||||
- 自动打开浏览器
|
||||
|
||||
**首次使用需创建用户:**
|
||||
```bash
|
||||
# 运行用户创建脚本
|
||||
python create_user.py
|
||||
```
|
||||
|
||||
#### 方法二:手动启动
|
||||
|
||||
```bash
|
||||
# 1. 安装Python依赖
|
||||
pip install -r backend/requirements.txt
|
||||
|
||||
# 2. 启动服务器
|
||||
# 2. 创建初始用户
|
||||
python create_user.py
|
||||
|
||||
# 3. 启动服务器
|
||||
cd backend
|
||||
python app.py
|
||||
|
||||
# 3. 在浏览器中访问
|
||||
# 4. 在浏览器中访问
|
||||
# http://localhost:5000
|
||||
```
|
||||
|
||||
### 3. 配置AI润色功能(可选)
|
||||
### 3. 配置说明
|
||||
|
||||
如需使用AI润色功能,请:
|
||||
#### 环境变量配置
|
||||
|
||||
1. 获取OpenAI API密钥
|
||||
2. 编辑 `backend/.env` 文件
|
||||
3. 设置 `OPENAI_API_KEY=your_api_key_here`
|
||||
#### 环境变量配置
|
||||
|
||||
创建 `.env` 文件 (可复制 `.env.example`):
|
||||
|
||||
```bash
|
||||
# Flask配置
|
||||
SECRET_KEY=your-secret-key-here-please-change-this
|
||||
|
||||
# OpenAI API配置(可选,用于AI润色功能)
|
||||
OPENAI_API_KEY=your_openai_api_key
|
||||
|
||||
# Docker首次启动时的默认用户(仅Docker部署时有效)
|
||||
DEFAULT_USERNAME=admin
|
||||
DEFAULT_PASSWORD=admin123
|
||||
```
|
||||
|
||||
**安全建议:**
|
||||
- 生产环境务必修改 `SECRET_KEY`
|
||||
- 修改默认用户名和密码
|
||||
- 登录后立即在系统中修改密码
|
||||
|
||||
#### AI润色功能(可选)
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 登录系统
|
||||
|
||||
1. 首次访问会显示登录页面
|
||||
2. 输入用户名和密码
|
||||
3. 登录成功后进入主界面
|
||||
|
||||
### 基本操作
|
||||
|
||||
1. **添加任务**
|
||||
|
||||
@@ -8,13 +8,18 @@ def create_app():
|
||||
app = Flask(__name__)
|
||||
|
||||
# 配置
|
||||
app.config['SECRET_KEY'] = 'your-secret-key-here'
|
||||
app.config['SECRET_KEY'] = 'your-secret-key-here-change-in-production'
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///worklist.db'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
# Session配置
|
||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = 86400 # 24小时
|
||||
|
||||
# 初始化扩展
|
||||
db.init_app(app)
|
||||
CORS(app) # 允许跨域请求
|
||||
CORS(app, supports_credentials=True) # 允许跨域请求并支持凭证
|
||||
|
||||
# 注册蓝图
|
||||
app.register_blueprint(api, url_prefix='/api')
|
||||
|
||||
@@ -1,9 +1,34 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from datetime import datetime
|
||||
from sqlalchemy import func
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
class User(db.Model):
|
||||
"""用户模型"""
|
||||
__tablename__ = 'users'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
password_hash = db.Column(db.String(200), nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
def set_password(self, password):
|
||||
"""设置密码(哈希加密)"""
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
"""验证密码"""
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'username': self.username,
|
||||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
|
||||
class Task(db.Model):
|
||||
"""任务模型"""
|
||||
__tablename__ = 'tasks'
|
||||
|
||||
@@ -1,11 +1,81 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask import Blueprint, request, jsonify, session
|
||||
from datetime import datetime, timedelta
|
||||
from models import db, Task, TimeRecord
|
||||
from models import db, Task, TimeRecord, User
|
||||
from ai_service import ai_service
|
||||
import json
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
|
||||
# 认证API
|
||||
@api.route('/auth/login', methods=['POST'])
|
||||
def login():
|
||||
"""用户登录"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or 'username' not in data or 'password' not in data:
|
||||
return jsonify({'error': '用户名和密码不能为空'}), 400
|
||||
|
||||
username = data['username']
|
||||
password = data['password']
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
||||
if user and user.check_password(password):
|
||||
# 登录成功,设置session
|
||||
session['user_id'] = user.id
|
||||
session['username'] = user.username
|
||||
return jsonify({
|
||||
'message': '登录成功',
|
||||
'user': user.to_dict()
|
||||
})
|
||||
else:
|
||||
return jsonify({'error': '用户名或密码错误'}), 401
|
||||
|
||||
@api.route('/auth/logout', methods=['POST'])
|
||||
def logout():
|
||||
"""用户登出"""
|
||||
session.clear()
|
||||
return jsonify({'message': '登出成功'})
|
||||
|
||||
@api.route('/auth/check', methods=['GET'])
|
||||
def check_auth():
|
||||
"""检查登录状态"""
|
||||
if 'user_id' in session:
|
||||
user = User.query.get(session['user_id'])
|
||||
if user:
|
||||
return jsonify({
|
||||
'authenticated': True,
|
||||
'user': user.to_dict()
|
||||
})
|
||||
return jsonify({'authenticated': False}), 401
|
||||
|
||||
@api.route('/auth/register', methods=['POST'])
|
||||
def register():
|
||||
"""用户注册(可选,用于创建初始用户)"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or 'username' not in data or 'password' not in data:
|
||||
return jsonify({'error': '用户名和密码不能为空'}), 400
|
||||
|
||||
username = data['username']
|
||||
password = data['password']
|
||||
|
||||
# 检查用户是否已存在
|
||||
if User.query.filter_by(username=username).first():
|
||||
return jsonify({'error': '用户名已存在'}), 400
|
||||
|
||||
# 创建新用户
|
||||
user = User(username=username)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'message': '注册成功',
|
||||
'user': user.to_dict()
|
||||
}), 201
|
||||
|
||||
# 任务管理API
|
||||
@api.route('/tasks', methods=['GET'])
|
||||
def get_tasks():
|
||||
|
||||
38
create_user.py
Normal file
38
create_user.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
创建初始用户脚本
|
||||
用于创建管理员账户
|
||||
"""
|
||||
|
||||
from backend.app import create_app
|
||||
from backend.models import db, User
|
||||
|
||||
def create_initial_user():
|
||||
"""创建初始用户"""
|
||||
app = create_app()
|
||||
|
||||
with app.app_context():
|
||||
# 检查是否已有用户
|
||||
existing_user = User.query.first()
|
||||
|
||||
if existing_user:
|
||||
print(f"用户已存在: {existing_user.username}")
|
||||
return
|
||||
|
||||
# 创建默认管理员用户
|
||||
username = input("请输入用户名 (默认: admin): ").strip() or "admin"
|
||||
password = input("请输入密码 (默认: admin123): ").strip() or "admin123"
|
||||
|
||||
user = User(username=username)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
print(f"用户创建成功!")
|
||||
print(f"用户名: {username}")
|
||||
print(f"请妥善保管密码!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_initial_user()
|
||||
@@ -14,6 +14,9 @@ services:
|
||||
- PYTHONUNBUFFERED=1
|
||||
- SECRET_KEY=${SECRET_KEY:-your-secret-key-here}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||
# 默认用户配置
|
||||
- DEFAULT_USERNAME=${DEFAULT_USERNAME:-admin}
|
||||
- DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-admin123}
|
||||
volumes:
|
||||
# 持久化数据库
|
||||
- ./data:/app/data
|
||||
|
||||
44
docker-entrypoint.sh
Normal file
44
docker-entrypoint.sh
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "正在启动工作任务管理系统..."
|
||||
|
||||
# 切换到backend目录
|
||||
cd /app/backend
|
||||
|
||||
# 等待数据库初始化
|
||||
echo "初始化数据库..."
|
||||
python -c "
|
||||
from app import create_app
|
||||
from models import db, User
|
||||
import os
|
||||
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
# 创建所有表
|
||||
db.create_all()
|
||||
|
||||
# 检查是否已有用户
|
||||
existing_user = User.query.first()
|
||||
|
||||
if not existing_user:
|
||||
# 从环境变量获取默认用户信息
|
||||
default_username = os.getenv('DEFAULT_USERNAME', 'admin')
|
||||
default_password = os.getenv('DEFAULT_PASSWORD', 'admin123')
|
||||
|
||||
# 创建默认用户
|
||||
user = User(username=default_username)
|
||||
user.set_password(default_password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
print(f'已创建默认用户: {default_username}')
|
||||
print(f'默认密码: {default_password}')
|
||||
print('请登录后立即修改密码!')
|
||||
else:
|
||||
print('用户已存在,跳过初始化')
|
||||
"
|
||||
|
||||
echo "启动Flask应用..."
|
||||
# 启动应用
|
||||
exec python app.py
|
||||
@@ -812,3 +812,98 @@ body {
|
||||
from { transform: translateX(-100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* 登录页面样式 */
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
animation: modalSlideIn 0.5s ease;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.login-header i {
|
||||
font-size: 3rem;
|
||||
color: #667eea;
|
||||
margin-bottom: 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
color: #2d3748;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.login-form .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.login-form label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.login-form label i {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #fed7d7;
|
||||
color: #c53030;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #fc8181;
|
||||
}
|
||||
|
||||
/* 用户信息显示 */
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #4a5568;
|
||||
font-weight: 600;
|
||||
padding: 8px 16px;
|
||||
background: #f7fafc;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.user-info i {
|
||||
color: #667eea;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,44 @@
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 登录页面 -->
|
||||
<div id="loginPage" class="login-page">
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<i class="fas fa-tasks"></i>
|
||||
<h1>工作任务管理系统</h1>
|
||||
</div>
|
||||
<form id="loginForm" class="login-form">
|
||||
<div class="form-group">
|
||||
<label for="loginUsername">
|
||||
<i class="fas fa-user"></i> 用户名
|
||||
</label>
|
||||
<input type="text" id="loginUsername" name="username" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="loginPassword">
|
||||
<i class="fas fa-lock"></i> 密码
|
||||
</label>
|
||||
<input type="password" id="loginPassword" name="password" required>
|
||||
</div>
|
||||
<div id="loginError" class="error-message" style="display: none;"></div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-sign-in-alt"></i> 登录
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主应用页面 -->
|
||||
<div id="mainApp" class="container" style="display: none;">
|
||||
<!-- 头部 -->
|
||||
<header class="header">
|
||||
<h1><i class="fas fa-tasks"></i> 工作任务管理系统</h1>
|
||||
<div class="header-actions">
|
||||
<span class="user-info">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<span id="currentUsername"></span>
|
||||
</span>
|
||||
<button id="addTaskBtn" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> 添加任务
|
||||
</button>
|
||||
@@ -22,6 +55,9 @@
|
||||
<button id="timeHistoryBtn" class="btn btn-secondary">
|
||||
<i class="fas fa-history"></i> 时间历史
|
||||
</button>
|
||||
<button id="logoutBtn" class="btn btn-outline">
|
||||
<i class="fas fa-sign-out-alt"></i> 退出
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class WorkListAPI {
|
||||
async request(endpoint, options = {}) {
|
||||
const url = `${this.baseURL}${endpoint}`;
|
||||
const config = {
|
||||
credentials: 'same-origin', // 包含cookies以支持session
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
@@ -30,6 +31,31 @@ class WorkListAPI {
|
||||
}
|
||||
}
|
||||
|
||||
// 认证API
|
||||
async login(username, password) {
|
||||
return this.request('/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
}
|
||||
|
||||
async logout() {
|
||||
return this.request('/auth/logout', {
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
async checkAuth() {
|
||||
return this.request('/auth/check');
|
||||
}
|
||||
|
||||
async register(username, password) {
|
||||
return this.request('/auth/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
}
|
||||
|
||||
// 任务管理API
|
||||
async getTasks() {
|
||||
return this.request('/tasks');
|
||||
|
||||
@@ -5,18 +5,111 @@ class WorkListApp {
|
||||
this.currentEditingTask = null;
|
||||
this.currentFilter = 'all';
|
||||
this.excludedStatuses = new Set();
|
||||
this.currentUser = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化应用
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.loadTasks();
|
||||
this.setupDatePicker();
|
||||
async init() {
|
||||
// 先检查登录状态
|
||||
await this.checkAuthStatus();
|
||||
|
||||
// 如果已登录,绑定事件并加载任务
|
||||
if (this.currentUser) {
|
||||
this.bindEvents();
|
||||
this.loadTasks();
|
||||
this.setupDatePicker();
|
||||
}
|
||||
}
|
||||
|
||||
// 检查认证状态
|
||||
async checkAuthStatus() {
|
||||
try {
|
||||
const response = await api.checkAuth();
|
||||
if (response.authenticated) {
|
||||
this.currentUser = response.user;
|
||||
this.showMainApp();
|
||||
} else {
|
||||
this.showLoginPage();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('未登录:', error);
|
||||
this.showLoginPage();
|
||||
}
|
||||
}
|
||||
|
||||
// 显示登录页面
|
||||
showLoginPage() {
|
||||
document.getElementById('loginPage').style.display = 'flex';
|
||||
document.getElementById('mainApp').style.display = 'none';
|
||||
|
||||
// 绑定登录表单事件
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
await this.handleLogin();
|
||||
});
|
||||
}
|
||||
|
||||
// 显示主应用页面
|
||||
showMainApp() {
|
||||
document.getElementById('loginPage').style.display = 'none';
|
||||
document.getElementById('mainApp').style.display = 'block';
|
||||
|
||||
// 显示用户名
|
||||
if (this.currentUser) {
|
||||
document.getElementById('currentUsername').textContent = this.currentUser.username;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理登录
|
||||
async handleLogin() {
|
||||
const username = document.getElementById('loginUsername').value;
|
||||
const password = document.getElementById('loginPassword').value;
|
||||
const errorDiv = document.getElementById('loginError');
|
||||
|
||||
try {
|
||||
errorDiv.style.display = 'none';
|
||||
const response = await api.login(username, password);
|
||||
|
||||
if (response.user) {
|
||||
this.currentUser = response.user;
|
||||
this.showMainApp();
|
||||
|
||||
// 绑定事件并加载任务
|
||||
this.bindEvents();
|
||||
this.loadTasks();
|
||||
this.setupDatePicker();
|
||||
}
|
||||
} catch (error) {
|
||||
errorDiv.textContent = error.message || '登录失败,请检查用户名和密码';
|
||||
errorDiv.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// 处理登出
|
||||
async handleLogout() {
|
||||
try {
|
||||
await api.logout();
|
||||
this.currentUser = null;
|
||||
this.tasks = [];
|
||||
this.showLoginPage();
|
||||
} catch (error) {
|
||||
console.error('登出失败:', error);
|
||||
alert('登出失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
// 登出按钮
|
||||
const logoutBtn = document.getElementById('logoutBtn');
|
||||
if (logoutBtn && !logoutBtn.hasAttribute('data-bound')) {
|
||||
logoutBtn.addEventListener('click', () => {
|
||||
this.handleLogout();
|
||||
});
|
||||
logoutBtn.setAttribute('data-bound', 'true');
|
||||
}
|
||||
// 添加任务按钮
|
||||
document.getElementById('addTaskBtn').addEventListener('click', () => {
|
||||
this.showTaskModal();
|
||||
|
||||
Reference in New Issue
Block a user