可以跑了 准备做系统
This commit is contained in:
Binary file not shown.
114
Html/app.py
114
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_cors import CORS
|
||||||
from flask_socketio import SocketIO, join_room, emit, Namespace
|
from flask_socketio import SocketIO, join_room, emit, Namespace
|
||||||
import markdown
|
import markdown
|
||||||
@@ -14,13 +14,13 @@ sys.path.insert(0, current_dir)
|
|||||||
from AlgoriAgent.projects.algoriAgent.agent_manager import AgentManager
|
from AlgoriAgent.projects.algoriAgent.agent_manager import AgentManager
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
# socketio = SocketIO(app, cors_allowed_origins="*",ping_timeout=60, ping_interval=5)
|
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="http://localhost:9888") # 设置跨域支持
|
||||||
import logging
|
import logging
|
||||||
app.secret_key = 'cakebaker'
|
app.secret_key = 'cakebaker'
|
||||||
app.logger.setLevel(logging.DEBUG)
|
app.logger.setLevel(logging.DEBUG)
|
||||||
# 配置 CORS
|
# 配置 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']
|
userid_recorder = {} # user_id&path -> session['user_id']
|
||||||
@@ -46,6 +46,7 @@ class VSCodeNamespace(Namespace):
|
|||||||
path = dataconfig.get('path')
|
path = dataconfig.get('path')
|
||||||
print(f"User {user_id} connected with path: {path}")
|
print(f"User {user_id} connected with path: {path}")
|
||||||
useruuid = user_id2uuid[user_id]
|
useruuid = user_id2uuid[user_id]
|
||||||
|
join_room(useruuid, namespace='/vscode')
|
||||||
if backboard_manager.get_backboard(useruuid) == None:
|
if backboard_manager.get_backboard(useruuid) == None:
|
||||||
backboard_manager.add_backboard(useruuid, path)
|
backboard_manager.add_backboard(useruuid, path)
|
||||||
|
|
||||||
@@ -94,9 +95,13 @@ def realtime_response(config, realtime_action):
|
|||||||
'''
|
'''
|
||||||
Agent and Chat
|
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):
|
class AgentNamespace(Namespace):
|
||||||
def on_login(self, data):
|
def on_login(self, data):
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
@@ -112,27 +117,48 @@ class AgentNamespace(Namespace):
|
|||||||
markdown = fmd.read()
|
markdown = fmd.read()
|
||||||
markdown_prompts = fmdp.read()
|
markdown_prompts = fmdp.read()
|
||||||
score_prompts = fsp.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)
|
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)
|
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):
|
def on_message(self, data):
|
||||||
print(f"Message from client: {data}")
|
print(f"Message from client: {data}")
|
||||||
id = session.get('user_id')
|
id = session.get('user_id')
|
||||||
|
if (type(data)==str):
|
||||||
|
data = json.loads(data)
|
||||||
print(id)
|
print(id)
|
||||||
# 构造回复消息
|
if data['type'] == 'text':
|
||||||
res = agent_manager.invoke(id, data, backboard_manager.get_backboard(id).get_info_prompt())
|
res = agent_manager.invoke(id, data['data'], backboard_manager.get_backboard(id).get_info_prompt())
|
||||||
reply = f"AI: {res.content['speak']}"
|
print('=*='*20)
|
||||||
# 将回复发送回前端
|
print(res.content)
|
||||||
emit('message', reply)
|
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):
|
def on_disconnect(self):
|
||||||
print("VSCode client disconnected")
|
print("VSCode client disconnected")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Markdown to HTML
|
Markdown to HTML
|
||||||
'''
|
'''
|
||||||
@@ -207,6 +233,7 @@ Vscode
|
|||||||
@app.route('/desktop/<user_id>/<path>')
|
@app.route('/desktop/<user_id>/<path>')
|
||||||
def hello_world(user_id, path):
|
def hello_world(user_id, path):
|
||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
|
# return redirect(url_for('login'))
|
||||||
session['user_id'] = 'user_' + str(uuid.uuid4())
|
session['user_id'] = 'user_' + str(uuid.uuid4())
|
||||||
print("user "+ user_id + "uuid is"+ session['user_id'])
|
print("user "+ user_id + "uuid is"+ session['user_id'])
|
||||||
user_id2uuid[user_id] = 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')
|
config_path = os.path.join(path_dir, '.config')
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w') as f:
|
||||||
f.write(f"user_id={user_id}\npath={path}")
|
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'])
|
@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/<book_name>')
|
||||||
|
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__':
|
if __name__ == '__main__':
|
||||||
socketio.on_namespace(VSCodeNamespace('/vscode'))
|
socketio.on_namespace(VSCodeNamespace('/vscode'))
|
||||||
socketio.on_namespace(AgentNamespace('/agent'))
|
socketio.on_namespace(AgentNamespace('/agent'))
|
||||||
|
|||||||
@@ -41,16 +41,19 @@ class Backboard:
|
|||||||
five_history = ""
|
five_history = ""
|
||||||
for h in self.history[-5:]:
|
for h in self.history[-5:]:
|
||||||
five_history += h['type'] + "\n" + h.get('filePath', '') + "\n\n"
|
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())}"
|
endl = "\n"
|
||||||
f"- User's current chapter study time is {self.get_deltatime_mmss(self.enter_chapter_time, time.time())}"
|
return (f"###Global Info:###{endl}"
|
||||||
f"- Activated file path: {self.active_file_path}"
|
f"Here are some info about now user's IDE, refer to it when you need to handle some code.{endl}"
|
||||||
f"- Activated file content (current editing 10 lines): "
|
f"- User's total study time is {self.get_deltatime_mmss(self.create_time, time.time())}{endl}"
|
||||||
f"```"
|
f"- User's current chapter study time is {self.get_deltatime_mmss(self.enter_chapter_time, time.time())}{endl}"
|
||||||
f"{self.active_file_content[-10:]}"
|
f"- Activated file path: {self.active_file_path}{endl}"
|
||||||
f"```"
|
f"- Activated file content (current editing 10 lines): {endl}"
|
||||||
f"- Last five action:{five_history}"
|
f"```{endl}"
|
||||||
f"- File tree: {self.file_tree}")
|
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):
|
def add_history(self, realtime_action):
|
||||||
if(realtime_action['type']=='config'):return
|
if(realtime_action['type']=='config'):return
|
||||||
if len(self.history)!=0:
|
if len(self.history)!=0:
|
||||||
|
|||||||
105
Html/db/course_list.py
Normal file
105
Html/db/course_list.py
Normal file
@@ -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')
|
||||||
21
Html/db/data/course/course_list.json
Normal file
21
Html/db/data/course/course_list.json
Normal file
@@ -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."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
57
Html/db/data/user/cake.json
Normal file
57
Html/db/data/user/cake.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Html/db/data/user/user_id_list.json
Normal file
7
Html/db/data/user/user_id_list.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"user123": "password123",
|
||||||
|
"user456": "password456",
|
||||||
|
"user789": "password789",
|
||||||
|
"cake": "12138ckC"
|
||||||
|
}
|
||||||
|
|
||||||
116
Html/db/user.py
Normal file
116
Html/db/user.py
Normal file
@@ -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')
|
||||||
54
Html/db/user_list.py
Normal file
54
Html/db/user_list.py
Normal file
@@ -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")
|
||||||
158
Html/static/css/book.css
Normal file
158
Html/static/css/book.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
185
Html/static/css/dashboard.css
Normal file
185
Html/static/css/dashboard.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
213
Html/static/css/desktop.css
Normal file
213
Html/static/css/desktop.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
/* 左右两侧的页面内容 */
|
.navbar .logo h1 {
|
||||||
.sidebar {
|
font-size: 24px;
|
||||||
background-color: #f0f0f0;
|
font-weight: bold;
|
||||||
padding: 20px;
|
}
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
.navbar-links a {
|
||||||
font-family: Arial, sans-serif;
|
color: white;
|
||||||
display: flex;
|
text-decoration: none;
|
||||||
flex-direction: column;
|
margin: 0 15px;
|
||||||
height: 100vh;
|
font-size: 16px;
|
||||||
margin: 0;
|
transition: color 0.3s;
|
||||||
}
|
}
|
||||||
.chatbox {
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: 20px;
|
|
||||||
height: 75%;
|
|
||||||
overflow-y: auto;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-message {
|
.navbar-links a:hover {
|
||||||
max-width: 70%;
|
color: #f1c40f;
|
||||||
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 {
|
.navbar .avatar img {
|
||||||
padding: 10px;
|
width: 40px;
|
||||||
background-color: #f1f1f1;
|
height: 40px;
|
||||||
display: flex;
|
border-radius: 50%;
|
||||||
justify-content: space-between;
|
border: 2px solid white;
|
||||||
align-items: center;
|
}
|
||||||
border-bottom: 1px solid #ddd;
|
/* General reset */
|
||||||
}
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.input-area {
|
/* Body & Page Background */
|
||||||
display: flex;
|
body {
|
||||||
gap: 10px;
|
font-family: 'Arial', sans-serif;
|
||||||
margin-top: 15px;
|
background-color: #f5f5f5;
|
||||||
padding: 10px;
|
padding: 20px;
|
||||||
background-color: #f1f1f1;
|
}
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-container, .language-toggle {
|
/* Header styles */
|
||||||
display: flex;
|
header {
|
||||||
align-items: center;
|
background-color: #2575fc;
|
||||||
}
|
padding: 20px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.slider-container input[type="range"] {
|
.header-container {
|
||||||
width: 70%;
|
display: flex;
|
||||||
margin-left: 10px;
|
justify-content: space-between;
|
||||||
}
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-group-toggle .btn {
|
.site-name h1 {
|
||||||
padding: 5px 10px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
#leftSidebar, .vscode-web, #rightSidebar {
|
|
||||||
height: 100vh;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dragbar, #dragbar2 {
|
.nav ul {
|
||||||
background-color: #ccc;
|
list-style-type: none;
|
||||||
cursor: col-resize;
|
display: flex;
|
||||||
}
|
gap: 20px;
|
||||||
/* 主容器,使用 grid 布局 */
|
}
|
||||||
#maxcontainer {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 默认宽度比例 */
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 各区域基础样式 */
|
.nav ul li {
|
||||||
.sidebar {
|
display: inline;
|
||||||
overflow: auto; /* 确保内容可以滚动 */
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.vscode-web {
|
.nav ul li a {
|
||||||
width: 100%;
|
color: white;
|
||||||
height: 100%;
|
text-decoration: none;
|
||||||
border: none;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resizer {
|
.user-avatar img {
|
||||||
background-color: #ccc;
|
width: 40px;
|
||||||
cursor: col-resize;
|
height: 40px;
|
||||||
width: 5px; /* 设置拖动条宽度 */
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gutter {
|
/* Main content styles */
|
||||||
background-color: #ccc; /* 分隔条颜色 */
|
main {
|
||||||
cursor: col-resize;
|
padding: 20px;
|
||||||
}#container {
|
}
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 5px 3fr 5px 1fr; /* 初始比例 */
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftSidebar, #vscodeWeb, #rightSidebar {
|
.course-selection h2 {
|
||||||
overflow: auto;
|
font-size: 28px;
|
||||||
}
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
#dragbar, #dragbar2 {
|
.course-list {
|
||||||
background-color: #ccc;
|
display: grid;
|
||||||
cursor: col-resize;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
136
Html/static/css/login.css
Normal file
136
Html/static/css/login.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
BIN
Html/static/image/algorithm/book_cover.png
Normal file
BIN
Html/static/image/algorithm/book_cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 912 KiB |
@@ -3,7 +3,7 @@ var socket;
|
|||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
data = window.appData;
|
data = window.appData;
|
||||||
console.log(data);
|
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() {
|
socket.on('connect', function() {
|
||||||
console.log('Connected to server');
|
console.log('Connected to server');
|
||||||
socket.emit('login',JSON.stringify(data));
|
socket.emit('login',JSON.stringify(data));
|
||||||
@@ -18,6 +18,49 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// 滚动到最新消息
|
// 滚动到最新消息
|
||||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
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 = `<b>Function:</b> ${element.name}(${JSON.stringify(element.arguments)})<br>`;
|
||||||
|
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 = `<b>Chapter ${data.chapter_id} Score:</b> ${data.data}`;
|
||||||
|
document.getElementById('chatbox').appendChild(scoreMessage);
|
||||||
|
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -33,15 +76,28 @@ fontSizeSlider.addEventListener('input', function() {
|
|||||||
// 语言切换按钮
|
// 语言切换按钮
|
||||||
const englishRadio = document.getElementById('english');
|
const englishRadio = document.getElementById('english');
|
||||||
const chineseRadio = document.getElementById('chinese');
|
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() {
|
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() {
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +116,7 @@ function sendMessage() {
|
|||||||
document.getElementById('chatbox').appendChild(userMessage);
|
document.getElementById('chatbox').appendChild(userMessage);
|
||||||
|
|
||||||
// 发送消息到服务器
|
// 发送消息到服务器
|
||||||
socket.emit('message', input);
|
socket.emit('message', JSON.stringify({data: input, type:'text'}));
|
||||||
|
|
||||||
// 清空输入框
|
// 清空输入框
|
||||||
document.getElementById('messageInput').value = '';
|
document.getElementById('messageInput').value = '';
|
||||||
@@ -69,3 +125,95 @@ function sendMessage() {
|
|||||||
document.getElementById('chatbox').scrollTop = document.getElementById('chatbox').scrollHeight;
|
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);
|
||||||
|
}
|
||||||
123
Html/static/js/dashboard.js
Normal file
123
Html/static/js/dashboard.js
Normal file
@@ -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');
|
||||||
|
}
|
||||||
60
Html/static/js/login.js
Normal file
60
Html/static/js/login.js
Normal file
@@ -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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
60
Html/templates/book.html
Normal file
60
Html/templates/book.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>课程详情</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/book.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %} <!-- 引入navbar.html -->
|
||||||
|
|
||||||
|
<!-- 课程详情主体部分 -->
|
||||||
|
<div class="course-details">
|
||||||
|
<!-- 上半部分:课程封面和详情 -->
|
||||||
|
<div class="course-overview">
|
||||||
|
<div class="course-cover">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="课程封面" class="cover-image">
|
||||||
|
</div>
|
||||||
|
<div class="course-info">
|
||||||
|
<h2 class="course-title">计算机科学导论</h2>
|
||||||
|
<p class="course-author">作者:张教授</p>
|
||||||
|
<p class="course-description">
|
||||||
|
本课程将介绍计算机科学的基本概念,包括计算机的硬件结构、操作系统、编程语言等基础知识,适合初学者。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<br/>
|
||||||
|
<!-- 下半部分:课程教案列表 -->
|
||||||
|
<div class="lesson-plans">
|
||||||
|
<!-- <h3>课程教案</h3> -->
|
||||||
|
<div class="lesson-cards">
|
||||||
|
<!-- 每个教案的卡片 -->
|
||||||
|
<div class="lesson-card">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="教案封面" class="lesson-image">
|
||||||
|
<div class="lesson-info">
|
||||||
|
<h4 class="lesson-title">第一章:计算机概述</h4>
|
||||||
|
<p class="lesson-description">介绍计算机的基础概念和发展历程。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 继续添加其他教案 -->
|
||||||
|
<div class="lesson-card">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="教案封面" class="lesson-image">
|
||||||
|
<div class="lesson-info">
|
||||||
|
<h4 class="lesson-title">第二章:操作系统基础</h4>
|
||||||
|
<p class="lesson-description">学习操作系统的基本原理与结构。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 可以继续添加更多教案卡片 -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
var book_name = "{{book_name}}";
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
55
Html/templates/dashboard.html
Normal file
55
Html/templates/dashboard.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>学生个人主页</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/dashboard.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
{% include 'navbar.html' %} <!-- 引入navbar.html -->
|
||||||
|
|
||||||
|
<!-- 主页主体部分 -->
|
||||||
|
<div class="dashboard">
|
||||||
|
<h2 class="dashboard-title">—— 我的选课 ——</h2>
|
||||||
|
<div class="course-cards" >
|
||||||
|
<!-- 每个课程的卡片 -->
|
||||||
|
<div class="course-card"onclick="openCourseProgress('algorithm')">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="课程封面" class="course-image">
|
||||||
|
<div class="course-info">
|
||||||
|
<h3 class="course-name">算法分析与设计</h3>
|
||||||
|
<p class="course-description">学习基本的算法知识概念,与算法实现。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 继续添加其他课程卡片 -->
|
||||||
|
<div class="course-card"onclick="openCourseProgress('data-structures')">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="课程封面" class="course-image">
|
||||||
|
<div class="course-info">
|
||||||
|
<h3 class="course-name">数据结构与算法</h3>
|
||||||
|
<p class="course-description">深入学习数据结构和算法设计。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 可以继续添加课程卡片 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 右侧滑出的学习进度卡片 -->
|
||||||
|
<div id="courseProgress" class="course-progress">
|
||||||
|
<div class="progress-header">
|
||||||
|
<span id="course-title"></span>
|
||||||
|
<button class="close-btn" onclick="closeCourseProgress()">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="progress-content">
|
||||||
|
<p>当前学习进度: <span id="progress"></span></p>
|
||||||
|
<hr/>
|
||||||
|
<h3>章节列表:</h3>
|
||||||
|
<ul id="chapter-list">
|
||||||
|
<!-- 章节列表会在点击课程时动态生成 -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/static/js/dashboard.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
211
Html/templates/desktop.html
Normal file
211
Html/templates/desktop.html
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Code Development Platform</title>
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="/static/css/desktop.css">
|
||||||
|
<script>
|
||||||
|
document.cookie = "vscode-tkn=44edc269-6f88-46ab-9790-dc253f66ac36; path=/; SameSite=None; Secure";
|
||||||
|
</script>
|
||||||
|
<script src="https://cdn.bootcdn.net/ajax/libs/marked/13.0.2/marked.min.js"></script>
|
||||||
|
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script src="https://unpkg.com/split.js/dist/split.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="maxcontainer" id="maxcontainer">
|
||||||
|
<!-- 左侧内容 -->
|
||||||
|
<div class="sidebar" id="leftSidebar" style="width: 100%;height: 100%;">
|
||||||
|
<div id="markdown-content"style="width: 100%;height: 95%;">
|
||||||
|
<!-- 动态加载的 HTML 会显示在这里 -->
|
||||||
|
</div>
|
||||||
|
<div id="markdown-content-process"style="width: 100%;height: 5%;">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="progress-detail" style="position: absolute; top: 0; left: 0; display: none; padding: 10px; background-color: rgba(0, 0, 0, 0.7); color: white; border-radius: 5px;">
|
||||||
|
<!-- 这里将显示鼠标悬停的详情 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="dragbar"></div>
|
||||||
|
|
||||||
|
<!-- 中间嵌入的 Vscode-web 窗口,带有 tkn 参数 -->
|
||||||
|
<div class="vscode-web"id="vscodeWeb">
|
||||||
|
<!-- <iframe src="http://127.0.0.1:9888/?workspace=/mnt/c/CAKE/vscode/example_folder&folder=/mnt/c/CAKE/vscode/example_folder" style="width: 100%;height: 100%;"></iframe> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="dragbar2"></div>
|
||||||
|
<!-- 右侧内容 -->
|
||||||
|
<div class="sidebar" id="rightSidebar"style="width: 100%;height: 100%;">
|
||||||
|
<div class="chatbox-header">
|
||||||
|
<div class="slider-container">
|
||||||
|
<label for="fontSizeSlider">Aa</label>
|
||||||
|
<input type="range" id="fontSizeSlider" min="12" max="24" value="16" />
|
||||||
|
</div>
|
||||||
|
<div class="language-toggle btn-group btn-group-toggle" data-toggle="buttons">
|
||||||
|
<label class="btn btn-outline-secondary " id="label_for_en">
|
||||||
|
<input type="radio" name="language" id="english" autocomplete="off" > En
|
||||||
|
</label>
|
||||||
|
<label class="btn btn-outline-secondary active" id="label_for_zh">
|
||||||
|
<input type="radio" name="language" id="chinese" autocomplete="off" checked> Zn
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="chatbox" id="chatbox" style="width: 100%;height: 80%;">
|
||||||
|
<!-- 消息会在这里显示 -->
|
||||||
|
</div>
|
||||||
|
<div class="input-area">
|
||||||
|
<textarea id="messageInput" rows="2" class="form-control" placeholder="输入你的消息 (支持 Markdown 语法)"></textarea>
|
||||||
|
<button class="btn btn-primary" onclick="sendMessage()">发送</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.appData = {
|
||||||
|
username:"{{user_id}}",
|
||||||
|
folder:"{{path}}",
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/chatbox.js"></script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 实现左右边栏可拖动
|
||||||
|
|
||||||
|
const dragbar = document.getElementById('dragbar');
|
||||||
|
const dragbar2 = document.getElementById('dragbar2');
|
||||||
|
const container = document.getElementById('maxcontainer');
|
||||||
|
let leftWidth = 300; // 设置左侧栏的初始宽度
|
||||||
|
let rightWidth = 300; // 设置右侧栏的初始宽度
|
||||||
|
|
||||||
|
let isDraggingLeft = false;
|
||||||
|
let isDraggingRight = false;
|
||||||
|
|
||||||
|
dragbar.addEventListener('mousedown', function() {
|
||||||
|
isDraggingLeft = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
dragbar2.addEventListener('mousedown', function() {
|
||||||
|
isDraggingRight = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', function(e) {
|
||||||
|
if (isDraggingLeft) {
|
||||||
|
leftWidth = e.clientX; // 更新左侧栏宽度
|
||||||
|
container.style.gridTemplateColumns = `${leftWidth}px 5px 1fr 5px ${rightWidth}px`;
|
||||||
|
} else if (isDraggingRight) {
|
||||||
|
rightWidth = window.innerWidth - e.clientX; // 更新右侧栏宽度
|
||||||
|
container.style.gridTemplateColumns = `${leftWidth}px 5px 1fr 5px ${rightWidth}px`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', function() {
|
||||||
|
isDraggingLeft = false;
|
||||||
|
isDraggingRight = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 使用 fetch 发送请求到 Flask 服务器,获取 session 信息
|
||||||
|
fetch('/get_session', {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include' // 包含 Cookie,确保 Flask 可以识别 session
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) return response.json();
|
||||||
|
throw new Error('Failed to fetch session');
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
console.log('Session data:', data);
|
||||||
|
|
||||||
|
// 在成功获取 session 后创建并插入 iframe
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
const sessionInfo = data
|
||||||
|
iframe.src = 'http://127.0.0.1:9090/?workspace=/mnt/c/CAKE/vscode/{{user_id}}/{{path}}&folder=/mnt/c/CAKE/vscode/{{user_id}}/{{path}}';
|
||||||
|
iframe.style.width = '100%';
|
||||||
|
iframe.style.height = '100%';
|
||||||
|
|
||||||
|
// 将 iframe 插入到指定的 div 中
|
||||||
|
document.getElementById('vscodeWeb').appendChild(iframe);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching session:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 动态加载 Markdown 文件
|
||||||
|
function loadMarkdown(filename) {
|
||||||
|
fetch(`/${filename}-markdown`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.html_url) {
|
||||||
|
document.getElementById('markdown-content').innerHTML = `<iframe id="markdown-content-iframe" src="${data.html_url}"style="width: 100%;height: 100%;"></iframe>`;
|
||||||
|
document.getElementById('markdown-content-iframe').addEventListener('load', function() {
|
||||||
|
// 在这里执行你想要的操作
|
||||||
|
console.log('Iframe has finished loading');
|
||||||
|
next_chapter();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Markdown file not found');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error loading markdown:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
loadMarkdown('{{path}}');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// function next_chapter(data) {
|
||||||
|
// // 获取所有的 h3 元素
|
||||||
|
// var iframe = document.getElementById('markdown-content-iframe');
|
||||||
|
|
||||||
|
// // 访问 iframe 的文档对象
|
||||||
|
// var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
|
|
||||||
|
// var h3Elements = iframeDocument.querySelectorAll('h3');
|
||||||
|
// console.log("h3Elements next")
|
||||||
|
// console.log(iframeDocument)
|
||||||
|
// if (currentChapterIndex >= h3Elements.length) {
|
||||||
|
// return; // 如果已经到了最后一个章节,则不进行任何操作
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 隐藏当前章节及其后的内容
|
||||||
|
// for (let i = 0; i < h3Elements.length; i++) {
|
||||||
|
// h3Elements[i].style.display = 'none';
|
||||||
|
// 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++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,154 +1,42 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="zh">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Code Development Platform</title>
|
<title>课程选择主页</title>
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="/static/css/index.css">
|
<link rel="stylesheet" href="/static/css/index.css">
|
||||||
<script>
|
|
||||||
document.cookie = "vscode-tkn=44edc269-6f88-46ab-9790-dc253f66ac36; path=/; SameSite=None; Secure";
|
|
||||||
</script>
|
|
||||||
<script src="https://cdn.bootcdn.net/ajax/libs/marked/13.0.2/marked.min.js"></script>
|
|
||||||
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<script src="https://unpkg.com/split.js/dist/split.min.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="maxcontainer" id="maxcontainer">
|
{% include 'navbar.html' %} <!-- 引入navbar.html -->
|
||||||
<!-- 左侧内容 -->
|
<main>
|
||||||
<div class="sidebar" id="leftSidebar" style="width: 100%;height: 100%;">
|
<div class="course-selection">
|
||||||
<div id="markdown-content"style="width: 100%;height: 100%;">
|
<h2>课程列表</h2>
|
||||||
<!-- 动态加载的 HTML 会显示在这里 -->
|
<div class="course-list">
|
||||||
</div>
|
<div class="course-card">
|
||||||
</div>
|
<img src="/static/image/algorithm/book_cover.png" alt="课程1">
|
||||||
<div id="dragbar"></div>
|
<h3>课程名称 1</h3>
|
||||||
|
<p>课程描述信息,简要介绍课程内容和学习目标。</p>
|
||||||
<!-- 中间嵌入的 Vscode-web 窗口,带有 tkn 参数 -->
|
<button class="select-button">选择课程</button>
|
||||||
<div class="vscode-web"id="vscodeWeb">
|
|
||||||
<!-- <iframe src="http://127.0.0.1:9888/?workspace=/mnt/c/CAKE/vscode/example_folder&folder=/mnt/c/CAKE/vscode/example_folder" style="width: 100%;height: 100%;"></iframe> -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="dragbar2"></div>
|
|
||||||
<!-- 右侧内容 -->
|
|
||||||
<div class="sidebar" id="rightSidebar"style="width: 100%;height: 100%;">
|
|
||||||
<div class="chatbox-header">
|
|
||||||
<div class="slider-container">
|
|
||||||
<label for="fontSizeSlider">Aa</label>
|
|
||||||
<input type="range" id="fontSizeSlider" min="12" max="24" value="16" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="language-toggle btn-group btn-group-toggle" data-toggle="buttons">
|
<div class="course-card">
|
||||||
<label class="btn btn-outline-secondary active">
|
<img src="/static/image/algorithm/book_cover.png" alt="课程2">
|
||||||
<input type="radio" name="language" id="english" autocomplete="off" checked> En
|
<h3>课程名称 2</h3>
|
||||||
</label>
|
<p>课程描述信息,简要介绍课程内容和学习目标。</p>
|
||||||
<label class="btn btn-outline-secondary">
|
<button class="select-button">选择课程</button>
|
||||||
<input type="radio" name="language" id="chinese" autocomplete="off"> Zn
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="course-card">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="课程3">
|
||||||
|
<h3>课程名称 3</h3>
|
||||||
|
<p>课程描述信息,简要介绍课程内容和学习目标。</p>
|
||||||
|
<button class="select-button">选择课程</button>
|
||||||
|
</div>
|
||||||
|
<!-- 添加更多课程卡片 -->
|
||||||
</div>
|
</div>
|
||||||
<div class="chatbox" id="chatbox" style="width: 100%;height: 80%;">
|
|
||||||
<!-- 消息会在这里显示 -->
|
|
||||||
</div>
|
|
||||||
<div class="input-area">
|
|
||||||
<textarea id="messageInput" rows="2" class="form-control" placeholder="输入你的消息 (支持 Markdown 语法)"></textarea>
|
|
||||||
<button class="btn btn-primary" onclick="sendMessage()">发送</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.appData = {
|
|
||||||
username:"{{user_id}}",
|
|
||||||
folder:"{{path}}",
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script src="/static/js/chatbox.js"></script>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
<script>
|
<footer>
|
||||||
// 实现左右边栏可拖动
|
<p>© 2024 学生管理系统 | 所有权利保留</p>
|
||||||
|
</footer>
|
||||||
const dragbar = document.getElementById('dragbar');
|
|
||||||
const dragbar2 = document.getElementById('dragbar2');
|
|
||||||
const container = document.getElementById('maxcontainer');
|
|
||||||
let leftWidth = 300; // 设置左侧栏的初始宽度
|
|
||||||
let rightWidth = 300; // 设置右侧栏的初始宽度
|
|
||||||
|
|
||||||
let isDraggingLeft = false;
|
|
||||||
let isDraggingRight = false;
|
|
||||||
|
|
||||||
dragbar.addEventListener('mousedown', function() {
|
|
||||||
isDraggingLeft = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
dragbar2.addEventListener('mousedown', function() {
|
|
||||||
isDraggingRight = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', function(e) {
|
|
||||||
if (isDraggingLeft) {
|
|
||||||
leftWidth = e.clientX; // 更新左侧栏宽度
|
|
||||||
container.style.gridTemplateColumns = `${leftWidth}px 5px 1fr 5px ${rightWidth}px`;
|
|
||||||
} else if (isDraggingRight) {
|
|
||||||
rightWidth = window.innerWidth - e.clientX; // 更新右侧栏宽度
|
|
||||||
container.style.gridTemplateColumns = `${leftWidth}px 5px 1fr 5px ${rightWidth}px`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('mouseup', function() {
|
|
||||||
isDraggingLeft = false;
|
|
||||||
isDraggingRight = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 动态加载 Markdown 文件
|
|
||||||
function loadMarkdown(filename) {
|
|
||||||
fetch(`/${filename}-markdown`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.html_url) {
|
|
||||||
document.getElementById('markdown-content').innerHTML = `<iframe src="${data.html_url}"style="width: 100%;height: 100%;"></iframe>`;
|
|
||||||
} else {
|
|
||||||
console.error('Markdown file not found');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error loading markdown:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
loadMarkdown('{{path}}');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用 fetch 发送请求到 Flask 服务器,获取 session 信息
|
|
||||||
fetch('/get_session', {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'include' // 包含 Cookie,确保 Flask 可以识别 session
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (response.ok) return response.json();
|
|
||||||
throw new Error('Failed to fetch session');
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
console.log('Session data:', data);
|
|
||||||
|
|
||||||
// 在成功获取 session 后创建并插入 iframe
|
|
||||||
const iframe = document.createElement('iframe');
|
|
||||||
const sessionInfo = data
|
|
||||||
iframe.src = 'http://127.0.0.1:9888/?workspace=/mnt/c/CAKE/vscode/{{user_id}}/{{path}}&folder=/mnt/c/CAKE/vscode/{{user_id}}/{{path}}';
|
|
||||||
iframe.style.width = '100%';
|
|
||||||
iframe.style.height = '100%';
|
|
||||||
|
|
||||||
// 将 iframe 插入到指定的 div 中
|
|
||||||
document.getElementById('vscodeWeb').appendChild(iframe);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching session:', error);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
35
Html/templates/login.html
Normal file
35
Html/templates/login.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>用户登录</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/login.css">
|
||||||
|
<script src="/static/js/login.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="login-card">
|
||||||
|
<h2 class="login-title">欢迎回来!</h2>
|
||||||
|
<form class="login-form">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="username">用户名</label>
|
||||||
|
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="password">密码</label>
|
||||||
|
<input type="password" id="password" name="password" placeholder="请输入密码" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="login-btn">登录</button>
|
||||||
|
<div class="forgot-password">
|
||||||
|
<a href="#">忘记密码?</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="social-login">
|
||||||
|
<button class="social-btn google">Google 登录</button>
|
||||||
|
<button class="social-btn facebook">Facebook 登录</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
14
Html/templates/navbar.html
Normal file
14
Html/templates/navbar.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!-- 页眉导航栏 -->
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="logo">
|
||||||
|
<h1>学生管理系统</h1>
|
||||||
|
</div>
|
||||||
|
<nav class="navbar-links">
|
||||||
|
<a href="/">首页</a>
|
||||||
|
<a href="/dashboard">课程</a>
|
||||||
|
<a href="/">成绩</a>
|
||||||
|
</nav>
|
||||||
|
<div class="avatar">
|
||||||
|
<img src="/static/image/algorithm/book_cover.png" alt="个人头像" class="avatar-img">
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
Reference in New Issue
Block a user