diff --git a/Html/__pycache__/backboardManager.cpython-310.pyc b/Html/__pycache__/backboardManager.cpython-310.pyc index 38c5348..1b96bd0 100644 Binary files a/Html/__pycache__/backboardManager.cpython-310.pyc and b/Html/__pycache__/backboardManager.cpython-310.pyc differ diff --git a/Html/app.py b/Html/app.py index 87475c2..d0c7ba3 100644 --- a/Html/app.py +++ b/Html/app.py @@ -1,4 +1,4 @@ -from flask import Flask, session, request, jsonify, render_template, send_from_directory +from flask import Flask, redirect, session, request, jsonify, render_template, send_from_directory, url_for from flask_cors import CORS from flask_socketio import SocketIO, join_room, emit, Namespace import markdown @@ -14,13 +14,13 @@ sys.path.insert(0, current_dir) from AlgoriAgent.projects.algoriAgent.agent_manager import AgentManager app = Flask(__name__) -# socketio = SocketIO(app, cors_allowed_origins="*",ping_timeout=60, ping_interval=5) -socketio = SocketIO(app, cors_allowed_origins="http://localhost:9888") # 设置跨域支持 +socketio = SocketIO(app, cors_allowed_origins="*",ping_timeout=60, ping_interval=5) +# socketio = SocketIO(app, cors_allowed_origins="http://localhost:9888") # 设置跨域支持 import logging app.secret_key = 'cakebaker' app.logger.setLevel(logging.DEBUG) # 配置 CORS -CORS(app, resources={r"/*": {"origins": "http://localhost:9888"},}, supports_credentials=True) +CORS(app, resources={r"/*": {"origins": "http://127.0.0.1:9090"},}, supports_credentials=True) userid_recorder = {} # user_id&path -> session['user_id'] @@ -46,6 +46,7 @@ class VSCodeNamespace(Namespace): path = dataconfig.get('path') print(f"User {user_id} connected with path: {path}") useruuid = user_id2uuid[user_id] + join_room(useruuid, namespace='/vscode') if backboard_manager.get_backboard(useruuid) == None: backboard_manager.add_backboard(useruuid, path) @@ -94,9 +95,13 @@ def realtime_response(config, realtime_action): ''' Agent and Chat ''' -agent_manager = AgentManager() - +agent_manager = AgentManager(app = app, socketio = socketio) +user_threads = {} +import threading +from concurrent.futures import ThreadPoolExecutor, as_completed +# 初始化线程池 +executor = ThreadPoolExecutor(max_workers=10) class AgentNamespace(Namespace): def on_login(self, data): data = json.loads(data) @@ -112,27 +117,48 @@ class AgentNamespace(Namespace): markdown = fmd.read() markdown_prompts = fmdp.read() score_prompts = fsp.read() - user_uuid, agent = agent_manager.new_agent(markdown, markdown_prompts, score_prompts, id=user_uuid) + user_uuid, agent = agent_manager.new_agent(markdown, markdown_prompts, score_prompts, id=user_uuid, root_path=f'{user}/{folder_name}') print(user_uuid) - join_room(user_uuid) # 将该用户加入以user_id为名的room + join_room(user_uuid, namespace='/agent') # 将该用户加入以user_id为名的room backboard_manager.add_backboard(user_uuid, folder_name) + def on_language(self, language): + id = session.get('user_id') + agent_manager.change_language(id, language) def on_message(self, data): print(f"Message from client: {data}") id = session.get('user_id') + if (type(data)==str): + data = json.loads(data) print(id) - # 构造回复消息 - res = agent_manager.invoke(id, data, backboard_manager.get_backboard(id).get_info_prompt()) - reply = f"AI: {res.content['speak']}" - # 将回复发送回前端 - emit('message', reply) + if data['type'] == 'text': + res = agent_manager.invoke(id, data['data'], backboard_manager.get_backboard(id).get_info_prompt()) + print('=*='*20) + print(res.content) + reply = f"AI: {res.content['speak']}" + with app.app_context(): + emit('message',reply, room=id, namespace='/agent') + emit('request_function',res.content['function'], room=id, namespace='/agent') + # for res in agent_manager.invoke(id, data, backboard_manager.get_backboard(id).get_info_prompt(), reset=True): + # print('=*='*20) + # print(res.content) + # if ('speak' in res.content): + # reply = f"AI: {res.content['speak']}" + # with app.app_context(): emit('message',reply, room=id, namespace='/agent') + # if ('function' in res.content): + # functions = res.content['function'] + # if functions is None: break + # if len(functions)==0:break + if data['type'] == 'function': + agent_manager.function_call(id, data['data']) def on_disconnect(self): print("VSCode client disconnected") + ''' Markdown to HTML ''' @@ -207,6 +233,7 @@ Vscode @app.route('/desktop//') def hello_world(user_id, path): if 'user_id' not in session: + # return redirect(url_for('login')) session['user_id'] = 'user_' + str(uuid.uuid4()) print("user "+ user_id + "uuid is"+ session['user_id']) user_id2uuid[user_id] = session['user_id'] @@ -220,7 +247,7 @@ def hello_world(user_id, path): config_path = os.path.join(path_dir, '.config') with open(config_path, 'w') as f: f.write(f"user_id={user_id}\npath={path}") - return render_template('index.html', user_id=user_id, path=path) + return render_template('desktop.html', user_id=user_id, path=path) @app.route('/vscode_data', methods=['POST']) @@ -238,6 +265,65 @@ def get_session(): +''' +Login + +''' + +@app.route('/login') +def login(): + if 'user_id' not in session: + return render_template('login.html') + else: + return redirect(url_for('/')) + +from db.user_list import UserList +users_list = UserList() + +@app.route('/login_post', methods=['POST']) +def login_post(): + # 获取请求的 JSON 数据 + data = request.get_json() + + username = data.get('username') + password = data.get('password') + + user = users_list.get_user(username) + if user is None: + return jsonify({'success': False, 'message': '用户名或密码错误'}) + + if user['password'] != password: + return jsonify({'success': False, 'message': '用户名或密码错误'}) + + session['user_id'] = 'user_' + str(uuid.uuid4()) + print("user "+ username + "uuid is"+ session['user_id']) + user_id2uuid[username] = session['user_id'] + uuid2user_id[session['user_id']] = username + return jsonify({'success': True, 'message': '登录成功'}) + + +''' +DashBoard +''' +@app.route('/dashboard') +def dashboard(): + if 'user_id' not in session: + return redirect(url_for('login')) + return render_template('dashboard.html') + +''' +Book +''' + +@app.route('/book/') +def book(book_name): + return render_template('book.html', book_name=book_name) + + +@app.route('/') +def home_index(): + return render_template('index.html') + if __name__ == '__main__': socketio.on_namespace(VSCodeNamespace('/vscode')) socketio.on_namespace(AgentNamespace('/agent')) diff --git a/Html/backboardManager.py b/Html/backboardManager.py index 30c52ef..7ebfe2d 100644 --- a/Html/backboardManager.py +++ b/Html/backboardManager.py @@ -41,16 +41,19 @@ class Backboard: five_history = "" for h in self.history[-5:]: five_history += h['type'] + "\n" + h.get('filePath', '') + "\n\n" - return (f"###Global Info:###" - f"- User's total study time is {self.get_deltatime_mmss(self.create_time, time.time())}" - f"- User's current chapter study time is {self.get_deltatime_mmss(self.enter_chapter_time, time.time())}" - f"- Activated file path: {self.active_file_path}" - f"- Activated file content (current editing 10 lines): " - f"```" - f"{self.active_file_content[-10:]}" - f"```" - f"- Last five action:{five_history}" - f"- File tree: {self.file_tree}") + + endl = "\n" + return (f"###Global Info:###{endl}" + f"Here are some info about now user's IDE, refer to it when you need to handle some code.{endl}" + f"- User's total study time is {self.get_deltatime_mmss(self.create_time, time.time())}{endl}" + f"- User's current chapter study time is {self.get_deltatime_mmss(self.enter_chapter_time, time.time())}{endl}" + f"- Activated file path: {self.active_file_path}{endl}" + f"- Activated file content (current editing 10 lines): {endl}" + f"```{endl}" + f"{self.active_file_content[-10:]}{endl}" + f"```{endl}" + f"- Last five action:{five_history}{endl}" + f"- File tree: {self.file_tree}{endl}") def add_history(self, realtime_action): if(realtime_action['type']=='config'):return if len(self.history)!=0: diff --git a/Html/db/course_list.py b/Html/db/course_list.py new file mode 100644 index 0000000..6c444bf --- /dev/null +++ b/Html/db/course_list.py @@ -0,0 +1,105 @@ +import os +from tinydb import TinyDB, Query, where + +class CourseList: + def __init__(self, db_file='db/data/course/course_list.json'): + """初始化课程列表,自动打开并加载 JSON 数据文件。""" + self.db_file = db_file + self.db = TinyDB(self.db_file) + self.course_query = Query() + + def add_course(self, course_data): + """添加新的课程到课程列表。""" + if not self.db.search(self.course_query.course_id == course_data['course_id']): + self.db.insert(course_data) + else: + print(f"Course with ID {course_data['course_id']} already exists.") + + def get_course(self, course_id): + """获取指定 course_id 的课程信息。""" + result = self.db.search(self.course_query.course_id == course_id) + return result[0] if result else None + + def update_course(self, course_id, updated_data): + """更新指定 course_id 的课程信息。""" + self.db.update(updated_data, self.course_query.course_id == course_id) + + def delete_course(self, course_id): + """删除指定 course_id 的课程。""" + self.db.remove(self.course_query.course_id == course_id) + + def add_lesson_to_course(self, course_id, lesson_data): + """为指定课程添加课时。""" + course = self.get_course(course_id) + if course: + if 'lessons' not in course: + course['lessons'] = [] + course['lessons'].append(lesson_data) + self.update_course(course_id, {'lessons': course['lessons']}) + else: + print(f"Course with ID {course_id} not found.") + + def get_lessons_of_course(self, course_id): + """获取指定课程的所有课时信息。""" + course = self.get_course(course_id) + return course['lessons'] if course else None + + def get_all_courses(self): + """获取所有课程。""" + return self.db.all() + + +# # 课程数据结构示例 +# course_data = { +# 'course_id': 'CS101', +# 'course_name': 'Computer Science 101', +# 'course_img_path': 'images/cs101.png', +# 'lessons': [ +# { +# 'lesson_id': 'L001', +# 'lesson_name': 'Introduction to Computer Science', +# 'markdown': 'This is the introduction lesson.', +# 'markdown_prompt': 'What is Computer Science?', +# 'score_prompt': 'Please provide an overview of CS.' +# }, +# { +# 'lesson_id': 'L002', +# 'lesson_name': 'Data Structures', +# 'markdown': 'In this lesson, we cover Data Structures.', +# 'markdown_prompt': 'What is an array?', +# 'score_prompt': 'Explain how linked lists work.' +# } +# ] +# } + +# lesson_data = { +# 'lesson_id': 'L003', +# 'lesson_name': 'Algorithms', +# 'markdown': 'In this lesson, we will discuss algorithms.', +# 'markdown_prompt': 'What is a sorting algorithm?', +# 'score_prompt': 'Explain the difference between merge sort and quicksort.' +# } + +# # 创建 CourseList 实例 +# course_list = CourseList() + +# # 添加课程 +# course_list.add_course(course_data) + +# # 添加课时到现有课程 +# course_list.add_lesson_to_course('CS101', lesson_data) + +# # 获取并显示所有课程 +# courses = course_list.get_all_courses() +# print(courses) + +# # 获取某个课程的课时 +# lessons = course_list.get_lessons_of_course('CS101') +# print(lessons) + +# # 更新课程信息 +# updated_data = {'course_name': 'Computer Science 101 - Updated'} +# course_list.update_course('CS101', updated_data) + +# # 删除课程 +# course_list.delete_course('CS101') diff --git a/Html/db/data/course/course_list.json b/Html/db/data/course/course_list.json new file mode 100644 index 0000000..a1643a2 --- /dev/null +++ b/Html/db/data/course/course_list.json @@ -0,0 +1,21 @@ +{ + "course_id": "CS101", + "course_name": "Computer Science 101", + "course_img_path": "images/cs101.png", + "lessons": [ + { + "lesson_id": "L001", + "lesson_name": "Introduction to Computer Science", + "markdown": "This is the introduction lesson.", + "markdown_prompt": "What is Computer Science?", + "score_prompt": "Please provide an overview of CS." + }, + { + "lesson_id": "L002", + "lesson_name": "Data Structures", + "markdown": "In this lesson, we cover Data Structures.", + "markdown_prompt": "What is an array?", + "score_prompt": "Explain how linked lists work." + } + ] +} diff --git a/Html/db/data/user/cake.json b/Html/db/data/user/cake.json new file mode 100644 index 0000000..166b262 --- /dev/null +++ b/Html/db/data/user/cake.json @@ -0,0 +1,57 @@ +{ +"user_id": "user123", +"select_course": ["algorithm", "data-structure"], +"head_icon": "/static/image/book_cover.jpg", +"course_process_dict": { + "algorithm": { + "course_process": { + "lesson_processs": [ + { + "lesson_process": [ + { + "chapter_info": { + "dialog": ["This is chapter 1 dialog"], + "score": 85, + "is_rebuttal": true, + "rebuttal_score": 90 + } + }, + { + "chapter_info": { + "dialog": ["This is chapter 2 dialog"], + "score": 75, + "is_rebuttal": false, + "rebuttal_score": 0 + } + } + ] + } + ], + "course_put_in_date": "2024-01-01", + "course_last_study_date": "2024-12-25", + "course_total_study_time": 120 + } + }, + "data-structure": { + "course_process": { + "lesson_processs": [ + { + "lesson_process": [ + { + "chapter_info": { + "dialog": ["This is course2 chapter 1 dialog"], + "score": 88, + "is_rebuttal": false, + "rebuttal_score": 0 + } + } + ] + } + ], + "course_put_in_date": "2024-01-01", + "course_last_study_date": "2024-12-25", + "course_total_study_time": 90 + } + } +} +} \ No newline at end of file diff --git a/Html/db/data/user/user_id_list.json b/Html/db/data/user/user_id_list.json new file mode 100644 index 0000000..8e4d91c --- /dev/null +++ b/Html/db/data/user/user_id_list.json @@ -0,0 +1,7 @@ +{ + "user123": "password123", + "user456": "password456", + "user789": "password789", + "cake": "12138ckC" +} + \ No newline at end of file diff --git a/Html/db/user.py b/Html/db/user.py new file mode 100644 index 0000000..516c009 --- /dev/null +++ b/Html/db/user.py @@ -0,0 +1,116 @@ +from datetime import date +from tinydb import TinyDB, Query + +class User: + def __init__(self, db_file='user_data.json'): + """初始化用户管理,自动打开并加载 JSON 数据文件。""" + self.db_file = db_file + self.db = TinyDB(self.db_file) + self.user_query = Query() + + def add_user(self, user_data): + """添加新的用户""" + if not self.db.search(self.user_query.user_id == user_data['user_id']): + self.db.insert(user_data) + else: + print(f"User with ID {user_data['user_id']} already exists.") + + def get_user(self, user_id): + """根据 user_id 获取用户信息""" + result = self.db.search(self.user_query.user_id == user_id) + return result[0] if result else None + + def update_user(self, user_id, updated_data): + """更新用户信息""" + self.db.update(updated_data, self.user_query.user_id == user_id) + + def delete_user(self, user_id): + """删除用户""" + self.db.remove(self.user_query.user_id == user_id) + + def get_all_users(self): + """获取所有用户""" + return self.db.all() + + +# 用户数据结构示例 +# user_data = { +# 'user_id': 'user123', +# 'select_course': ['course1', 'course2'], +# 'course_process_dict': { +# 'course1': { +# 'course_process': { +# 'lesson_processs': [ +# { +# 'lesson_process': [ +# { +# 'chapter_info': { +# 'dialog': ['This is chapter 1 dialog'], +# 'score': 85, +# 'is_rebuttal': True, +# 'rebuttal_score': 90 +# } +# }, +# { +# 'chapter_info': { +# 'dialog': ['This is chapter 2 dialog'], +# 'score': 75, +# 'is_rebuttal': False, +# 'rebuttal_score': 0 +# } +# } +# ] +# } +# ], +# 'course_put_in_date': date(2024, 1, 1), +# 'course_last_study_date': date(2024, 12, 25), +# 'course_total_study_time': 120 # 总学习时间:分钟 +# } +# }, +# 'course2': { +# 'course_process': { +# 'lesson_processs': [ +# { +# 'lesson_process': [ +# { +# 'chapter_info': { +# 'dialog': ['This is course2 chapter 1 dialog'], +# 'score': 88, +# 'is_rebuttal': False, +# 'rebuttal_score': 0 +# } +# } +# ] +# } +# ], +# 'course_put_in_date': date(2024, 2, 1), +# 'course_last_study_date': date(2024, 12, 24), +# 'course_total_study_time': 90 +# } +# } +# } +# } + +# updated_user_data = { +# 'select_course': ['course1', 'course3'] +# } + +# # 创建 User 实例 +# user_list = User() + +# # 添加用户 +# user_list.add_user(user_data) + +# # 获取用户信息 +# user = user_list.get_user('user123') +# print(user) + +# # 更新用户选择的课程 +# user_list.update_user('user123', updated_user_data) + +# # 获取所有用户 +# all_users = user_list.get_all_users() +# print(all_users) + +# # 删除用户 +# user_list.delete_user('user123') diff --git a/Html/db/user_list.py b/Html/db/user_list.py new file mode 100644 index 0000000..2b0e915 --- /dev/null +++ b/Html/db/user_list.py @@ -0,0 +1,54 @@ +from tinydb import TinyDB, Query + +class UserList: + def __init__(self, db_file='db/data/user/user_id_list.json'): + """初始化用户管理,自动打开并加载 JSON 数据文件。""" + self.db_file = db_file + self.db = TinyDB(self.db_file) + self.user_query = Query() + + def add_user(self, user_id, password): + """添加新的用户登录信息""" + # 检查是否已经有该用户 + if not self.db.search(self.user_query.user_id == user_id): + self.db.insert({'user_id': user_id, 'password': password}) + else: + print(f"User with ID {user_id} already exists.") + + def get_user(self, user_id): + """获取指定 user_id 的用户信息""" + result = self.db.search(self.user_query.user_id == user_id) + return result[0] if result else None + + def update_password(self, user_id, new_password): + """更新用户的密码""" + self.db.update({'password': new_password}, self.user_query.user_id == user_id) + + def delete_user(self, user_id): + """删除用户""" + self.db.remove(self.user_query.user_id == user_id) + + def get_all_users(self): + """获取所有用户的信息""" + return self.db.all() + +# 创建 UserList 实例 +# user_list = UserList() + +# # 添加用户 +# user_list.add_user("user123", "password123") +# user_list.add_user("user456", "password456") + +# # 获取用户 +# user = user_list.get_user("user123") +# print(user) + +# # 更新密码 +# user_list.update_password("user123", "newpassword") + +# # 获取所有用户 +# all_users = user_list.get_all_users() +# print(all_users) + +# # 删除用户 +# user_list.delete_user("user123") diff --git a/Html/static/css/book.css b/Html/static/css/book.css new file mode 100644 index 0000000..cb77943 --- /dev/null +++ b/Html/static/css/book.css @@ -0,0 +1,158 @@ +/* Reset some default styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +/* Body & Page Background */ +body { + font-family: 'Arial', sans-serif; + background-color: #f5f5f5; + padding: 20px; +} + +/* 页眉导航栏 */ +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + background-color: #2575fc; + padding: 10px 30px; + color: white; + border-radius: 10px; + margin-bottom: 30px; +} + +.navbar .logo h1 { + font-size: 24px; + font-weight: bold; +} + +.navbar-links a { + color: white; + text-decoration: none; + margin: 0 15px; + font-size: 16px; + transition: color 0.3s; +} + +.navbar-links a:hover { + color: #f1c40f; +} + +.navbar .avatar img { + width: 40px; + height: 40px; + border-radius: 50%; + border: 2px solid white; +} + +/* 课程详情页面主体部分 */ +.course-details { + max-width: 1200px; + margin: 0 auto; + +} + +/* 上半部分:课程封面和课程详情 */ +.course-overview { + display: flex; + justify-content: space-between; + margin-bottom: 30px; + max-height: 200px; + height: 30%; +} + +.course-cover { + width: 40%; + text-align: center; +} + +.cover-image { + + height: 100%; + width: auto; + border-radius: 10px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); +} + +.course-info { + width: 55%; + padding-left: 20px; +} + +.course-title { + font-size: 32px; + font-weight: bold; + margin-bottom: 10px; + color: #333; +} + +.course-author { + font-size: 16px; + color: #777; + margin-bottom: 20px; +} + +.course-description { + font-size: 16px; + color: #555; +} + +/* 下半部分:课程教案列表 */ +.lesson-plans { + margin-top: 30px; +} + +.lesson-plans h3 { + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; + color: #333; +} + +/* 课程教案卡片容器 */ +.lesson-cards { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: space-around; +} + +/* 课程教案卡片 */ +.lesson-card { + width: 200px; + height: 300px; + background-color: white; + border-radius: 15px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.3s; +} + +.lesson-card:hover { + transform: translateY(-10px); /* 提升效果 */ +} + +.lesson-image { + width: 100%; + height: 150px; + object-fit: cover; +} + +.lesson-info { + padding: 15px; +} + +.lesson-title { + font-size: 18px; + font-weight: bold; + color: #333; + margin-bottom: 10px; +} + +.lesson-description { + font-size: 14px; + color: #777; +} diff --git a/Html/static/css/dashboard.css b/Html/static/css/dashboard.css new file mode 100644 index 0000000..979b688 --- /dev/null +++ b/Html/static/css/dashboard.css @@ -0,0 +1,185 @@ +/* Reset some default styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +/* Body & Page Background */ +body { + font-family: 'Arial', sans-serif; + background-color: #f5f5f5; + padding: 20px; +} + +/* 页眉导航栏 */ +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + background-color: #2575fc; + padding: 10px 30px; + color: white; + border-radius: 10px; + margin-bottom: 30px; +} + +.navbar .logo h1 { + font-size: 24px; + font-weight: bold; +} + +.navbar-links a { + color: white; + text-decoration: none; + margin: 0 15px; + font-size: 16px; + transition: color 0.3s; +} + +.navbar-links a:hover { + color: #f1c40f; +} + +.navbar .avatar img { + width: 40px; + height: 40px; + border-radius: 50%; + border: 2px solid white; +} + +/* Dashboard 主体部分 */ +.dashboard { + max-width: 1200px; + margin: 0 auto; +} + +.dashboard-title { + font-size: 28px; + font-weight: bold; + margin-bottom: 20px; + text-align: center; + +} + +/* 课程卡片容器 */ +.course-cards { + display: flex; + flex-wrap: wrap; + gap: 20px; + justify-content: center; +} + +/* 课程卡片 */ +.course-card { + width: 300px; + height: 500px; + background-color: white; + border-radius: 15px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + overflow: hidden; + position: relative; + transition: transform 0.3s; +} + +.course-card:hover { + transform: translateY(-10px); /* 提升效果 */ +} + +.course-image { + width: 100%; + height: 200px; + object-fit: cover; +} + +.course-info { + padding: 20px; +} + +.course-name { + font-size: 22px; + font-weight: bold; + color: #333; +} + +.course-description { + font-size: 14px; + color: #777; + margin-top: 10px; +} +/* 右侧滑出进度卡片样式 */ +.course-progress { + position: fixed; + top: 0; + right: -400px; /* 初始时在屏幕外 */ + width: 400px; + height: 100%; + background-color: #fff; + box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1); + padding: 30px; + transition: right 0.3s ease; + z-index: 9999; + border-radius: 15px; /* 添加圆角 */ +} + +.course-progress.open { + right: 0; /* 打开时显示 */ +} + +.progress-header { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 20px; + margin-bottom: 20px; +} + +.close-btn { + background: none; + border: none; + font-size: 24px; + cursor: pointer; +} + + +/* 章节和子章节样式 */ +.chapter-list { + list-style-type: none; + padding-left: 0; +} + +.chapter-item { + margin: 5px 0; + padding: 5px; + cursor: pointer; +} + +.chapter-item .chapter-title { + font-size: 16px; + font-weight: bold; +} + +.chapter-item .sub-chapter-list { + display: none; /* 初始状态下子章节隐藏 */ + padding-left: 20px; +} + +.chapter-item.open .sub-chapter-list { + display: block; /* 展开时显示子章节 */ +} + +.chapter-item:hover { + background-color: #f1f1f1; + border-radius: 5px; +} + +/* 子章节的样式 */ +.sub-chapter-item { + margin: 3px 0; + padding: 3px; + font-size: 14px; +} + +.sub-chapter-item:hover { + background-color: #e8e8e8; +} \ No newline at end of file diff --git a/Html/static/css/desktop.css b/Html/static/css/desktop.css new file mode 100644 index 0000000..1694649 --- /dev/null +++ b/Html/static/css/desktop.css @@ -0,0 +1,213 @@ + + /* 左右两侧的页面内容 */ + .sidebar { + background-color: #f0f0f0; + padding: 20px; + height: 100%; + } + + body { + font-family: Arial, sans-serif; + display: flex; + flex-direction: column; + height: 100vh; + margin: 0; + } + .chatbox { + flex-grow: 1; + padding: 20px; + height: 75%; + overflow-y: auto; + border: 1px solid #ccc; + background-color: #f9f9f9; + } + + .chat-message { + max-width: 70%; + padding: 10px 15px; + border-radius: 20px; + margin-bottom: 10px; + } + .user-message { + background-color: #d1e7dd; + text-align: right; + margin-left: auto; + } + .server-message { + background-color: #f1f1f1; + text-align: left; + margin-right: auto; + } + textarea { + flex-grow: 1; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + resize: none; + } + button { + padding: 10px 20px; + margin-left: 10px; + background-color: #28a745; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + } + pre { + background-color: #eee; + padding: 10px; + border-radius: 4px; + } + code { + font-family: monospace; + } + /* 让父容器为flex布局 */ + .maxcontainer { + display: flex; + flex-grow: 1; + height: 100%; + position: relative; + } + /* 中间的拖拽条 */ + .resizer { + width: 5px; + background-color: #ccc; + cursor: ew-resize; + position: relative; + } + + .chatbox-header { + padding: 10px; + background-color: #f1f1f1; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #ddd; + } + + + .input-area { + display: flex; + gap: 10px; + margin-top: 15px; + padding: 10px; + background-color: #f1f1f1; + border-top: 1px solid #ddd; + } + + .slider-container, .language-toggle { + display: flex; + align-items: center; + } + + .slider-container input[type="range"] { + width: 70%; + margin-left: 10px; + } + + .btn-group-toggle .btn { + padding: 5px 10px; + } + #leftSidebar, .vscode-web, #rightSidebar { + height: 100vh; + overflow: auto; + } + + #dragbar, #dragbar2 { + background-color: #ccc; + cursor: col-resize; + } + /* 主容器,使用 grid 布局 */ + #maxcontainer { + display: grid; + grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 默认宽度比例 */ + height: 100vh; + } + + /* 各区域基础样式 */ + .sidebar { + overflow: auto; /* 确保内容可以滚动 */ + } + + .vscode-web { + width: 100%; + height: 100%; + border: none; + } + + .resizer { + background-color: #ccc; + cursor: col-resize; + width: 5px; /* 设置拖动条宽度 */ + } + + .gutter { + background-color: #ccc; /* 分隔条颜色 */ + cursor: col-resize; + }#container { + display: grid; + grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 初始比例 */ + height: 100vh; + } + + #leftSidebar, #vscodeWeb, #rightSidebar { + overflow: auto; + } + + #dragbar, #dragbar2 { + background-color: #ccc; + cursor: col-resize; + } + + + + + + +/* 设置每个子框的基本样式 */ +.progress-box { + height: 100%; /* 高度为 100%,撑满容器 */ + display: flex; + flex-direction: row; /* 使子框横向排列 */ + justify-content: flex-start; +} + +.progress-title { + height: 60px; /* 每个进度节点的高度 */ + text-align: center; + line-height: 60px; /* 垂直居中 */ + margin: 2px; /* 水平方向上的间隔 */ + border: 1px solid #ddd; + transition: background-color 0.3s ease; + position: relative; +} + + +.green { + background-color: green; + color: white; +} + +.white { + background-color: white; + color: black; +} + +.progress-box > .progress-title:last-child { + margin-bottom: 0; /* 防止最后一个子框出现多余的间距 */ +} + +/* 进度条详细信息的样式 */ +#progress-detail { + position: absolute; + top: 0; + left: 0; + display: none; + padding: 10px; + background-color: rgba(0, 0, 0, 0.7); + color: white; + border-radius: 5px; + max-width: 200px; +} + diff --git a/Html/static/css/index.css b/Html/static/css/index.css index a3768e1..929f93a 100644 --- a/Html/static/css/index.css +++ b/Html/static/css/index.css @@ -1,162 +1,156 @@ +/* 页眉导航栏 */ +.navbar { + display: flex; + justify-content: space-between; + align-items: center; + background-color: #2575fc; + padding: 10px 30px; + color: white; + border-radius: 10px; + margin-bottom: 30px; +} - /* 左右两侧的页面内容 */ - .sidebar { - background-color: #f0f0f0; - padding: 20px; - height: 100%; - } +.navbar .logo h1 { + font-size: 24px; + font-weight: bold; +} - body { - font-family: Arial, sans-serif; - display: flex; - flex-direction: column; - height: 100vh; - margin: 0; - } - .chatbox { - flex-grow: 1; - padding: 20px; - height: 75%; - overflow-y: auto; - border: 1px solid #ccc; - background-color: #f9f9f9; - } +.navbar-links a { + color: white; + text-decoration: none; + margin: 0 15px; + font-size: 16px; + transition: color 0.3s; +} - .chat-message { - max-width: 70%; - padding: 10px 15px; - border-radius: 20px; - margin-bottom: 10px; - } - .user-message { - background-color: #d1e7dd; - text-align: right; - margin-left: auto; - } - .server-message { - background-color: #f1f1f1; - text-align: left; - margin-right: auto; - } - textarea { - flex-grow: 1; - padding: 10px; - border: 1px solid #ccc; - border-radius: 4px; - resize: none; - } - button { - padding: 10px 20px; - margin-left: 10px; - background-color: #28a745; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - } - pre { - background-color: #eee; - padding: 10px; - border-radius: 4px; - } - code { - font-family: monospace; - } - /* 让父容器为flex布局 */ - .maxcontainer { - display: flex; - flex-grow: 1; - height: 100%; - position: relative; - } - /* 中间的拖拽条 */ - .resizer { - width: 5px; - background-color: #ccc; - cursor: ew-resize; - position: relative; - } - - .chatbox-header { - padding: 10px; - background-color: #f1f1f1; - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid #ddd; - } +.navbar-links a:hover { + color: #f1c40f; +} + +.navbar .avatar img { + width: 40px; + height: 40px; + border-radius: 50%; + border: 2px solid white; +} +/* General reset */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} - .input-area { - display: flex; - gap: 10px; - margin-top: 15px; - padding: 10px; - background-color: #f1f1f1; - border-top: 1px solid #ddd; - } +/* Body & Page Background */ +body { + font-family: 'Arial', sans-serif; + background-color: #f5f5f5; + padding: 20px; +} - .slider-container, .language-toggle { - display: flex; - align-items: center; - } +/* Header styles */ +header { + background-color: #2575fc; + padding: 20px; + color: white; +} - .slider-container input[type="range"] { - width: 70%; - margin-left: 10px; - } +.header-container { + display: flex; + justify-content: space-between; + align-items: center; +} - .btn-group-toggle .btn { - padding: 5px 10px; - } - #leftSidebar, .vscode-web, #rightSidebar { - height: 100vh; - overflow: auto; - } - - #dragbar, #dragbar2 { - background-color: #ccc; - cursor: col-resize; - } - /* 主容器,使用 grid 布局 */ - #maxcontainer { - display: grid; - grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 默认宽度比例 */ - height: 100vh; - } - - /* 各区域基础样式 */ - .sidebar { - overflow: auto; /* 确保内容可以滚动 */ - } - - .vscode-web { - width: 100%; - height: 100%; - border: none; - } - - .resizer { - background-color: #ccc; - cursor: col-resize; - width: 5px; /* 设置拖动条宽度 */ - } - - .gutter { - background-color: #ccc; /* 分隔条颜色 */ - cursor: col-resize; - }#container { - display: grid; - grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 初始比例 */ - height: 100vh; - } - - #leftSidebar, #vscodeWeb, #rightSidebar { - overflow: auto; - } - - #dragbar, #dragbar2 { - background-color: #ccc; - cursor: col-resize; - } - \ No newline at end of file +.site-name h1 { + font-size: 24px; +} + +.nav ul { + list-style-type: none; + display: flex; + gap: 20px; +} + +.nav ul li { + display: inline; +} + +.nav ul li a { + color: white; + text-decoration: none; + font-size: 16px; +} + +.user-avatar img { + width: 40px; + height: 40px; + border-radius: 50%; +} + +/* Main content styles */ +main { + padding: 20px; +} + +.course-selection h2 { + font-size: 28px; + margin-bottom: 20px; +} + +.course-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; +} + +.course-card { + background-color: white; + border-radius: 8px; + width: 300px; + height: 400px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.3s ease; +} + +.course-card img { + width: 100%; + height: 261px; + object-fit: cover; +} + +.course-card h3 { + padding: 15px; + font-size: 20px; + color: #2575fc; +} + +.course-card p { + padding: 0 15px; + font-size: 14px; + color: #555; +} + +.select-button { + display: block; + width: 100%; + padding: 10px; + background-color: #2575fc; + color: white; + border: none; + cursor: pointer; + font-size: 16px; +} + +.select-button:hover { + background-color: #113c86; +} + +/* Footer styles */ +footer { + background-color: #333; + color: white; + text-align: center; + padding: 10px; + margin-top: 20px; +} diff --git a/Html/static/css/login.css b/Html/static/css/login.css new file mode 100644 index 0000000..f87219b --- /dev/null +++ b/Html/static/css/login.css @@ -0,0 +1,136 @@ +/* Reset some default styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +/* Body & Page background */ +body { + font-family: 'Arial', sans-serif; + background: linear-gradient(135deg, #70ff88, #e6d05f); /* 美丽的渐变背景 */ + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + padding: 20px; +} + +.login-container { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 40%; +} + +.login-card { + background-color: white; + padding: 40px; + border-radius: 15px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 500px; + text-align: center; +} + +.login-title { + font-size: 28px; + font-weight: bold; + color: #333; + margin-bottom: 20px; +} + +.login-form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.input-group { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.input-group label { + font-size: 14px; + color: #555; + margin-bottom: 5px; +} + +.input-group input { + width: 100%; + padding: 12px; + border-radius: 8px; + border: 1px solid #ddd; + font-size: 16px; + transition: border-color 0.3s; +} + +.input-group input:focus { + border-color: #2575fc; + outline: none; +} + +.login-btn { + background-color: #2575fc; + color: white; + padding: 14px; + font-size: 16px; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s; +} + +.login-btn:hover { + background-color: #6a11cb; +} + +.forgot-password { + margin-top: 10px; +} + +.forgot-password a { + color: #2575fc; + font-size: 14px; + text-decoration: none; +} + +.forgot-password a:hover { + text-decoration: underline; +} + +.social-login { + margin-top: 20px; +} + +.social-btn { + width: 100%; + padding: 12px; + margin: 5px 0; + font-size: 16px; + border: none; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.3s; +} + +.social-btn.google { + background-color: #db4437; + color: white; +} + +.social-btn.google:hover { + background-color: #c1351d; +} + +.social-btn.facebook { + background-color: #3b5998; + color: white; +} + +.social-btn.facebook:hover { + background-color: #2d4373; +} diff --git a/Html/static/image/algorithm/book_cover.png b/Html/static/image/algorithm/book_cover.png new file mode 100644 index 0000000..34e364b Binary files /dev/null and b/Html/static/image/algorithm/book_cover.png differ diff --git a/Html/static/js/chatbox.js b/Html/static/js/chatbox.js index 9efb140..f7a59bb 100644 --- a/Html/static/js/chatbox.js +++ b/Html/static/js/chatbox.js @@ -3,7 +3,7 @@ var socket; document.addEventListener('DOMContentLoaded', function() { data = window.appData; console.log(data); - socket = io('http://localhost:5000/agent?username='+data.username+'&folder='+data.folder); + socket = io('/agent?username='+data.username+'&folder='+data.folder); socket.on('connect', function() { console.log('Connected to server'); socket.emit('login',JSON.stringify(data)); @@ -18,6 +18,49 @@ document.addEventListener('DOMContentLoaded', function() { // 滚动到最新消息 document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; }); + socket.on('request_function', function(data){ + try { + console.log("request_function", data); + if (typeof data === 'string') { + data = JSON.parse(data); + } + for (let i = 0; i < data.length; i++) { + const element = data[i] + const requestMessage = document.createElement('div'); + requestMessage.className = 'chat-message server-message'; + const approveButton = document.createElement('button'); + approveButton.innerText = '批准'; + approveButton.data = element; + approveButton.onclick = function(event) { + console.log(element) + socket.emit('message', JSON.stringify({type:'function', data:element})); + event.currentTarget.disabled = true; + }; + // 将按钮添加到消息气泡中 + requestMessage.innerHTML = `Function: ${element.name}(${JSON.stringify(element.arguments)})
`; + requestMessage.appendChild(approveButton); + document.getElementById('chatbox').appendChild(requestMessage); + document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; + } + } catch (error) { + console.log(error) + } + }); + socket.on('next_chapter', function(data){ + next_chapter(data); + }); + socket.on('chapter_score',function(data){ + if (typeof data === 'string'){ + data = JSON.parse(data); + } + console.log(data) + const scoreMessage = document.createElement('div'); + scoreMessage.className = 'chat-message server-message'; + scoreMessage.innerHTML = `Chapter ${data.chapter_id} Score: ${data.data}`; + document.getElementById('chatbox').appendChild(scoreMessage); + document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; + }) + }); @@ -33,19 +76,32 @@ fontSizeSlider.addEventListener('input', function() { // 语言切换按钮 const englishRadio = document.getElementById('english'); const chineseRadio = document.getElementById('chinese'); -let language = 'en'; // 默认语言 + +const englishLabel = document.getElementById('label_for_en'); +const chineseLabel = document.getElementById('label_for_zh'); +let language = 'zh'; // 默认语言 // 切换语言事件 englishRadio.addEventListener('change', function() { - if (englishRadio.checked) language = 'en'; + if (englishRadio.checked) { + language = 'en'; + englishLabel.classList.add('active'); + chineseLabel.classList.remove('active'); + } + socket.emit('language',language); }); chineseRadio.addEventListener('change', function() { - if (chineseRadio.checked) language = 'zn'; + if (chineseRadio.checked) { + language = 'zh'; + chineseLabel.classList.add('active'); + englishLabel.classList.remove('active'); + } + socket.emit('language',language); }); - + // 发送消息 function sendMessage() { const input = document.getElementById('messageInput').value; @@ -60,7 +116,7 @@ function sendMessage() { document.getElementById('chatbox').appendChild(userMessage); // 发送消息到服务器 - socket.emit('message', input); + socket.emit('message', JSON.stringify({data: input, type:'text'})); // 清空输入框 document.getElementById('messageInput').value = ''; @@ -69,3 +125,95 @@ function sendMessage() { document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight; } +let currentChapterIndex = -1; +function next_chapter(data) { + // 获取所有的 h3 元素 + var iframe = document.getElementById('markdown-content-iframe'); + + // 访问 iframe 的文档对象 + var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; + var titles = [] + var h3Elements = iframeDocument.querySelectorAll('h3'); + console.log("h3Elements next") + if (currentChapterIndex >= h3Elements.length) { + return; // 如果已经到了最后一个章节,则不进行任何操作 + } + + // 隐藏当前章节及其后的内容 + for (let i = 0; i < h3Elements.length; i++) { + h3Elements[i].style.display = 'none'; + titles.push(h3Elements[i].innerText) + let nextSibling = h3Elements[i].nextElementSibling; + while (nextSibling && nextSibling.tagName !== 'H3') { + nextSibling.style.display = 'none'; + nextSibling = nextSibling.nextElementSibling; + } + } + + // 显示下一个章节及其后的内容,直到再下一个h3元素或结束 + if (currentChapterIndex + 1 < h3Elements.length) { + h3Elements[currentChapterIndex + 1].style.display = 'block'; + let nextSibling = h3Elements[currentChapterIndex + 1].nextElementSibling; + while (nextSibling && nextSibling.tagName !== 'H3') { + nextSibling.style.display = 'block'; + nextSibling = nextSibling.nextElementSibling; + } + } + currentChapterIndex++; + + generateProgressBar(h3Elements.length, currentChapterIndex, titles); +} + +function generateProgressBar(N, idx, titles) { + const container = document.getElementById('markdown-content-process'); + container.innerHTML = ''; // 清空内容 + + // 创建进度条的外框 + const progressBox = document.createElement('div'); + progressBox.className = 'progress-box'; + + // 获取详细信息容器 + const detailBox = document.getElementById('progress-detail'); + + // 根据N值动态调整每个进度节点的宽度 + const nodeWidth = 100 / N; // 每个节点的宽度为 100% / N + + // 根据N值生成子框并设置颜色 + for (let i = 0; i < N; i++) { + const titleBox = document.createElement('div'); + titleBox.className = 'progress-title'; + + // 设置进度条的颜色 + if (i < idx) { + titleBox.classList.add('green'); // 已完成部分 + } else { + titleBox.classList.add('white'); // 未完成部分 + } + + // 设置每个子框的标题 + titleBox.innerHTML = titles[i] || `Step ${i + 1}`; + + // 设置每个进度节点的宽度 + titleBox.style.width = `${nodeWidth}%`; + + // 监听鼠标移入事件来显示详情 + titleBox.addEventListener('mouseenter', function() { + detailBox.innerHTML = `Step ${i + 1}: ${titles[i] || "No title"}`;// - Additional details here...`; + detailBox.style.display = 'block'; + // 根据进度条位置显示详情 + const rect = titleBox.getBoundingClientRect(); + detailBox.style.top = `${rect.top + window.scrollY - 100}px`; // 微调位置 + detailBox.style.left = `${rect.left + window.scrollX + rect.width / 2 - detailBox.offsetWidth / 2}px`; + }); + + // 监听鼠标移出事件来隐藏详情 + titleBox.addEventListener('mouseleave', function() { + detailBox.style.display = 'none'; + }); + + progressBox.appendChild(titleBox); + } + + // 将生成的进度条添加到容器中 + container.appendChild(progressBox); +} \ No newline at end of file diff --git a/Html/static/js/dashboard.js b/Html/static/js/dashboard.js new file mode 100644 index 0000000..7830edf --- /dev/null +++ b/Html/static/js/dashboard.js @@ -0,0 +1,123 @@ +// dashboard.js + +function openCourseProgress(courseId) { + const courseData = { + 'algorithm': { + title: '算法分析与设计', + progress: '50%', + chapters: [ + { + title: '第一章: 算法基础', + subChapters: [ + { title: '子章节1: 算法介绍', path: 'introduction' }, + { title: '子章节2: 时间复杂度分析', path: 'time-complexity' } + ] + }, + { + title: '第二章: 排序与查找', + subChapters: [ + { title: '子章节1: 排序算法', path: 'sorting-algorithms' }, + { title: '子章节2: 查找算法', path: 'searching-algorithms' } + ] + }, + { + title: '第三章: 图算法', + subChapters: [ + { title: '子章节1: 图的表示', path: 'graph-representation' }, + { title: '子章节2: DFS 与 BFS', path: 'dfs-bfs' } + ] + } + ] + }, + 'data-structures': { + title: '数据结构与算法', + progress: '30%', + chapters: [ + { + title: '第一章: 数组与链表', + subChapters: [ + { title: '子章节1: 数组基础', path: 'array-basics' }, + { title: '子章节2: 链表实现', path: 'linked-list' } + ] + }, + { + title: '第二章: 栈与队列', + subChapters: [ + { title: '子章节1: 栈的实现', path: 'stack-implementation' }, + { title: '子章节2: 队列的应用', path: 'queue-application' } + ] + }, + { + title: '第三章: 二叉树', + subChapters: [ + { title: '子章节1: 二叉树的遍历', path: 'binary-tree-traversal' }, + { title: '子章节2: 二叉查找树', path: 'binary-search-tree' } + ] + } + ] + } + // 可以继续添加其他课程的数据 + }; + + // 获取对应课程的数据 + const course = courseData[courseId]; + if (!course) return; + + // 更新右侧滑出卡片的内容 + document.getElementById('course-title').textContent = course.title; + document.getElementById('progress').textContent = course.progress; + + // 更新章节列表 + const chapterList = document.getElementById('chapter-list'); + chapterList.innerHTML = ''; // 清空之前的章节 + + course.chapters.forEach(chapter => { + const chapterItem = document.createElement('li'); + chapterItem.classList.add('chapter-item'); + + const chapterTitle = document.createElement('div'); + chapterTitle.classList.add('chapter-title'); + chapterTitle.textContent = chapter.title; + chapterItem.appendChild(chapterTitle); + + // 创建子章节 + if (chapter.subChapters && chapter.subChapters.length > 0) { + const subChapterList = document.createElement('ul'); + subChapterList.classList.add('sub-chapter-list'); + + chapter.subChapters.forEach(subChapter => { + const subChapterItem = document.createElement('li'); + subChapterItem.classList.add('sub-chapter-item'); + subChapterItem.textContent = subChapter.title; + + // 添加点击事件跳转到学习页面 + subChapterItem.addEventListener('click', () => { + const userId = 'userid'; // 这里可以动态传入当前用户ID + const courseName = encodeURIComponent(course.title); // 对课程名称进行编码 + const subChapterPath = subChapter.path; // 子章节路径 + const url = `/desktop/${userId}/${courseName}/${subChapterPath}`; + window.location.href = url; // 跳转到该学习页面 + }); + + subChapterList.appendChild(subChapterItem); + }); + + chapterItem.appendChild(subChapterList); + + // 添加点击事件切换子章节显示 + chapterItem.addEventListener('click', () => { + chapterItem.classList.toggle('open'); + }); + } + + chapterList.appendChild(chapterItem); + }); + + // 打开滑出卡片 + document.getElementById('courseProgress').classList.add('open'); +} + +function closeCourseProgress() { + // 关闭滑出卡片 + document.getElementById('courseProgress').classList.remove('open'); +} diff --git a/Html/static/js/login.js b/Html/static/js/login.js new file mode 100644 index 0000000..a040a9b --- /dev/null +++ b/Html/static/js/login.js @@ -0,0 +1,60 @@ +document.addEventListener('DOMContentLoaded', function() { + // 获取登录表单及按钮 + const loginForm = document.querySelector('.login-form'); + const loginButton = document.querySelector('.login-btn'); + const usernameInput = document.getElementById('username'); + const passwordInput = document.getElementById('password'); + + // 在点击登录按钮时提交表单 + loginButton.addEventListener('click', function(event) { + event.preventDefault(); // 防止表单的默认提交行为 + + // 获取输入框的值 + const username = usernameInput.value.trim(); + const password = passwordInput.value.trim(); + + // 检查输入框是否为空 + if (!username || !password) { + alert('用户名和密码不能为空'); + return; + } + + // 显示加载提示 + loginButton.innerHTML = '登录中...'; + loginButton.disabled = true; + + // 创建一个请求对象 + const data = { + username: username, + password: password + }; + + // 使用 Fetch API 发送 POST 请求到 Flask 后端 + fetch('/login_post', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) // 将数据转换为 JSON 格式发送 + }) + .then(response => response.json()) // 解析响应数据 + .then(data => { + if (data.success) { + // 登录成功,跳转到主页 + window.location.href = '/dashboard'; // 根据需要修改跳转的路径 + } else { + // 登录失败,提示错误信息 + alert('登录失败: ' + data.message); + } + }) + .catch(error => { + console.error('Error:', error); + alert('登录请求失败,请稍后重试'); + }) + .finally(() => { + // 恢复按钮状态 + loginButton.innerHTML = '登录'; + loginButton.disabled = false; + }); + }); +}); diff --git a/Html/templates/book.html b/Html/templates/book.html new file mode 100644 index 0000000..7a65033 --- /dev/null +++ b/Html/templates/book.html @@ -0,0 +1,60 @@ + + + + + + 课程详情 + + + + + {% include 'navbar.html' %} + + +
+ +
+
+ 课程封面 +
+
+

计算机科学导论

+

作者:张教授

+

+ 本课程将介绍计算机科学的基本概念,包括计算机的硬件结构、操作系统、编程语言等基础知识,适合初学者。 +

+
+
+
+
+ +
+ +
+ +
+ 教案封面 +
+

第一章:计算机概述

+

介绍计算机的基础概念和发展历程。

+
+
+ +
+ 教案封面 +
+

第二章:操作系统基础

+

学习操作系统的基本原理与结构。

+
+
+ + +
+
+
+ + + diff --git a/Html/templates/dashboard.html b/Html/templates/dashboard.html new file mode 100644 index 0000000..8591251 --- /dev/null +++ b/Html/templates/dashboard.html @@ -0,0 +1,55 @@ + + + + + + 学生个人主页 + + + + + {% include 'navbar.html' %} + + +
+

—— 我的选课 ——

+
+ +
+ 课程封面 +
+

算法分析与设计

+

学习基本的算法知识概念,与算法实现。

+
+
+ +
+ 课程封面 +
+

数据结构与算法

+

深入学习数据结构和算法设计。

+
+
+ +
+
+ + + +
+
+ + +
+
+

当前学习进度:

+
+

章节列表:

+
    + +
+
+
+ + + diff --git a/Html/templates/desktop.html b/Html/templates/desktop.html new file mode 100644 index 0000000..fab5478 --- /dev/null +++ b/Html/templates/desktop.html @@ -0,0 +1,211 @@ + + + + + + Code Development Platform + + + + + + + + + +
+ + +
+ + +
+ +
+ + +
+ + +
+ + + + diff --git a/Html/templates/index.html b/Html/templates/index.html index b897503..6b92330 100644 --- a/Html/templates/index.html +++ b/Html/templates/index.html @@ -1,154 +1,42 @@ - + - Code Development Platform - + 课程选择主页 - - - - - -
- - -
- - -
- -
- - -
- -