本项目主要实现依靠人工智能的自动化思维导图生成,是一个使用Flask框架实现GPT与Xmind结合的项目。
项目说明书
1. 简介
我们的网站提供了一个便捷的平台,旨在帮助用户根据他们提出的话题或要求生成递归或非递归的思维导图,并提供可下载的Xmind类型的 思维导图文件。通过这个网站,用户可以快速、自动地生成符合自己需求的思维导图,帮助他们组织思维、解决问题和提高工作效率。
2. 功能介绍
2.1 登录注册功能
为了提供个性化的服务和记录用户生成的思维导图历史,我们的网站提供了登录注册功能。用户可以通过注册账号并登录系统,这样他们的历史记录和个人设置将得到保存。在登录状态下,用户可以方便地查看和管理自己的思维导图文件。
图1、2——登录注册界面
2.2 网站主页
进入网站主页后,用户会看到一行滚动图片,这些图片可以用于后续的广告招商以支持网站的运营和改进。
在中央区域下方有一个方便的输入框,用户可以输入他们想要生成思维导图的话题或内容。点击右侧的搜索按钮后,网站将根据用户输入自动生成一个思维导图。这个功能是通过连接GPT接口连接ChatGPT实现的,这些强大的AI模型能够根据用户的输入理解意图并生成高质量的思维导图。
网站顶部有一排导航栏,方便用户浏览和操作。导航栏中包括了产品介绍,使用指南,开发团队介绍以及跳转界面。另外,用户还可以在跳转页面找到历史记录和可下载的思维导图文件,选择是否递归生成思维导图。
图3、4——产品介绍及使用指南
图5、6——网站主页
2.3 跳转页面
在网站的右上方,我们设计了一个跳转页面的下拉导航栏。用户可以通过点击下拉导航栏选择生成递归式思维导图或非递归式思维导图,查看历史记录文件以及下载xmind。
通过选择递归式思维导图和非递归式思维导图,网站可以生成递归或者非递归的思维导图(递归例如生成做蛋糕的步骤,会显示步骤的先后,而非递归例如生成数据结构的思维导图,各项数据结构为并列显示)。
在用户可以通过网站下载生成的xmind格式思维导图,下面的连接可以直接下载xmind。同时在下载界面,用户可以看到自己历史生成的思维导图进行下载。我们的数据库会记录每个用户生成思维导图的历史记录。当用户登录网站后,他们可以方便地查看自己的历史文件并进行下载。这样,用户可以轻松地管理和回顾之前生成的思维导图,为日后的工作和学习提供参考和便利。
图7——文件提取及历史记录
图8、9——生成的思维导图实例
3. 技术支持
为了实现上述功能,我们的网站主要基于Python的Flask框架进行搭建。通过连接后端数据库、Xmind软件接口和ChatGPT接口,我们成功实现了登录注册、思维导图生成以及思维导图文件和历史记录文件的下载等核心功能。这些技术的融合使得我们的网站成为一个有效、高效且易于使用的资源工具。
使用的工具和版本:Python 3.11.1、Flask 2.3.3、Promptulate 1.8.2、WTForms 0.1、Xmind 1.2.0、Flask-WTF 1.1.1、Jinja2 3.1.2、Markdown 3.5.2、SQLite3
项目的实现
1.项目框架
此项目使用flask,其框架即为flask框架
templates文件的目录如下
2.代码实现
main.py文件(项目的核心逻辑以及flask框架的搭建)
import codecs
import re
import markdown2
from flask import Flask, render_template, request, send_file, session
from wtforms import StringField, SubmitField
from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
import os
from promptulate import Conversation
from promptulate.utils import export_openai_key_pool
import xmind
from promptulate.preset_roles import CustomPresetRole
from flask import Flask, render_template, request, redirect, url_for
import sqlite3
os.environ["OPENAI_API_KEY"] = "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"
# keys = [
# {"model": "gpt-3.5-turbo", "keys": "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, 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, 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"}
# ]
# export_openai_key_pool(keys)
app = Flask(__name__, template_folder='templates')
app.config['SECRET_KEY'] = 'JIIDIN'
count = 0
# class SpiritualTeacher(CustomPresetRole):
# name: str = "思维导图生成器"
# description: str = """
# 生成思维导图。输入要求,将生成的内容为 Markdown 格式。"""
# role = SpiritualTeacher()
conversation = Conversation(role='mind-map-generator')
# 创建数据库连接和游标
conn = sqlite3.connect('database2.db', check_same_thread=False)
c = conn.cursor()
# 创建用户表
c.execute('''CREATE TABLE IF NOT EXISTS users
(username TEXT PRIMARY KEY, password TEXT)''')
conn.commit()
def dealStr(line):
return line.strip('#').strip('-').strip()
class Test(FlaskForm):
requirement = StringField(label='请输入您的需求', validators=[DataRequired("需求不能为空")])
submit = SubmitField(label='提交', render_kw={'onclick': 'showLoading()'})
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
return render_template('login.html')
@app.route('/register')
def register():
return render_template('register.html')
@app.route('/register', methods=['POST'])
def register_user():
username = request.form['username']
password = request.form['password']
# 检查用户名是否已存在
c.execute('SELECT * FROM users WHERE username = ?', (username,))
existing_user = c.fetchone()
if existing_user:
return '用户名已存在,请选择其他用户名'
else:
# 插入新用户到数据库
c.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, password))
conn.commit()
return redirect(url_for('login'))
@app.route('/login', methods=['POST'])
def login_user():
username = request.form['username']
password = request.form['password']
# 查询用户
c.execute('SELECT * FROM users WHERE username = ? AND password = ?', (username, password))
user = c.fetchone()
if user:
session['username'] = username
return redirect(url_for("test"))
else:
return '用户名或密码错误,请重试'
@app.route("/test", methods=['GET', 'POST'])
def test():
try:
os.remove("output.xmind")
print("释放资源,程序初始化")
except:
print("程序初始化")
form = Test()
if request.method == 'GET':
data = {
'requirement': '此处返回思维导图生成情况',
}
html_content = markdown2.markdown("##提示:\n"
"##此处为动态内容,将以文本显示生成的内容(非思维导图格式),以便在您下载思维导图文件前能够确认对本次生成结果是否满意\n"
"##文本生成过程中耐心等待,大约需要30-60秒,请勿重复进行操作,在结果生成完毕后再点击下载xmind文件")
return render_template('test.html', form=form, data=data, iframe_content=html_content)
elif request.method == 'POST':
requirement = form.requirement.data
print(requirement)
ret = conversation.predict("生成一段{}的思维导图".format(requirement))
print(ret)
# 转化成生成html格式
html_content = markdown2.markdown(ret)
print(html_content)
# with codecs.open(r"D:\pythonProject\practice\templates\example.html", mode="w", encoding="utf8") as html_file:
# html_file.write(html_content)
data = {
'requirement': '{}思维导图已生成并保存'.format(requirement)
}
# 创建思维导图
workbook = xmind.load("output.xmind")
s1 = workbook.getPrimarySheet()
r1 = s1.getRootTopic()
# 解析文本并创建相应的思维导图结构
r2 = r3 = r4 = r5 = None
lines = ret.strip().split('\n')
for line in lines:
if line.count('#') == 1:
# 创建新的主题
r1.setTitle(dealStr(line))
elif line.count('#') == 2:
# 创建新的子主题
r2 = r1.addSubTopic()
r2.setTitle(dealStr(line))
elif line.count('#') == 3:
# 创建新的子主题
if r2:
r3 = r2.addSubTopic()
r3.setTitle(dealStr(line))
elif line.startswith("-"):
# 创建新的子主题
if r3:
r4 = r3.addSubTopic()
r4.setTitle(dealStr(line))
else:
if r2:
r4 = r2.addSubTopic()
r4.setTitle(dealStr(line))
else:
r4 = r1.addSubTopic()
r4.setTitle(dealStr(line))
elif line.startswith(" -"):
# 创建新的子主题
if r4:
r5 = r4.addSubTopic()
r5.setTitle(dealStr(line))
elif line.startswith(" -"):
# 创建新的子主题
if r5:
r6 = r5.addSubTopic()
r6.setTitle(dealStr(line))
# 获取当前工作目录
current_directory = os.getcwd()
files_and_folders = os.listdir(current_directory)
files = [file for file in files_and_folders if os.path.isfile(os.path.join(current_directory, file))]
# 保存思维导图
n = 1
file_name = "{}.xmind".format(session['username'] + "-" + str(n))
while 1:
if file_name in files:
n += 1
file_name = "{}.xmind".format(session['username'] + "-" + str(n))
else:
xmind.save(workbook, "{}.xmind".format(session['username'] + "-" + str(n)))
break
print("思维导图已生成并保存为 {}".format(file_name))
# 指定XMind文件的路径
return render_template('test.html', form=form, data=data, iframe_content=html_content)
@app.route("/test1", methods=['GET', 'POST'])
def test1():
try:
os.remove("output.xmind")
print("释放资源,程序初始化")
except:
print("程序初始化")
form = Test()
if request.method == 'GET':
data = {
'requirement': '此处返回思维导图生成情况',
}
html_content = markdown2.markdown("#提示:\n"
"##此处为动态内容,将以文本显示生成的内容(非思维导图格式),以便在您下载思维导图文件前能够确认对本次生成结果是否满意\n"
"##文本生成过程中耐心等待,大约需要30-60秒,请勿重复进行操作,在结果生成完毕后再点击下载xmind文件")
return render_template('test1.html', form=form, data=data, iframe_content=html_content)
elif request.method == 'POST':
requirement = form.requirement.data
print(requirement)
ret = conversation.predict("生成一段{}的思维导图".format(requirement))
print(ret)
# 转化成生成html格式
html_content = markdown2.markdown(ret)
print(html_content)
# with codecs.open(r"D:\pythonProject\practice\templates\example.html", mode="w", encoding="utf8") as html_file:
# html_file.write(html_content)
data = {
'requirement': '{}思维导图已生成并保存'.format(requirement)
}
# 创建思维导图
workbook = xmind.load("output.xmind")
s1 = workbook.getPrimarySheet()
r1 = s1.getRootTopic()
# 解析文本并创建相应的思维导图结构
r2 = r3 = r4 = r5 = None
lines = ret.strip().split('\n')
title_number = 1
for line in lines:
if line.count('#') == 1:
# 创建新的主题
r1.setTitle(dealStr(line))
if line.count("#") == 2:
r2 = r1.addSubTopic()
#r2.setTitle(dealStr(line))
# 创建新的主题
r2.setTitle(f"{title_number}. {dealStr(line)}")
title_number += 1
elif line.count('#') == 3:
# 创建新的子主题
if r2:
r3 = r2.addSubTopic()
r3.setTitle(dealStr(line))
elif line.count('#') == 4:
if r3:
r4 = r3.addSubTopic()
r4.setTitle(dealStr(line))
elif line.startswith("-"):
# 创建新的子主题
if r4:
r5 = r4.addSubTopic()
r5.setTitle(dealStr(line))
elif r3:
r5 = r3.addSubTopic()
r5.setTitle(dealStr(line))
elif r2:
r5 = r2.addSubTopic()
r5.setTitle(dealStr(line))
else:
r5 = r1.addSubTopic()
r5.setTitle(dealStr(line))
elif line.startswith(" -"):
# 创建新的子主题
if r5:
r6 = r5.addSubTopic()
r6.setTitle(dealStr(line))
elif line.startswith(" -"):
# 创建新的子主题
if r6:
r7 = r6.addSubTopic()
r7.setTitle(dealStr(line))
elif line.startswith(" -"):
# 创建新的子主题
if r7:
r8 = r7.addSubTopic()
r8.setTitle(dealStr(line))
# 获取当前工作目录
current_directory = os.getcwd()
files_and_folders = os.listdir(current_directory)
files = [file for file in files_and_folders if os.path.isfile(os.path.join(current_directory, file))]
# 保存思维导图
n = 1
file_name = "{}.xmind".format(session['username'] + "-" + str(n))
while 1:
if file_name in files:
n += 1
file_name = "{}.xmind".format(session['username'] + "-" + str(n))
else:
xmind.save(workbook, "{}.xmind".format(session['username'] + "-" + str(n)))
break
print("思维导图已生成并保存为 {}".format(file_name))
# 指定XMind文件的路径
return render_template('test1.html', form=form, data=data, iframe_content=html_content)
# # 文件下载路由
# @app.route('/download_mindmap')
# def download_mindmap():
# # 这里使用 send_file 来提供文件下载,你需要提供文件的路径
# # 例如,假设要提供名为 "example.txt" 的文件
# # xmind_file_path = "C:\\Users\Administrator\Desktop\dist\output.xmind"
# xmind_file_path = "output.xmind"
# # html_content = markdown2.markdown("#这里简略展示您生成的内容")
# # with codecs.open("C:\\Users\LX\Desktop\example.html", mode="w", encoding="utf8") as html_file:
# # html_file.write(html_content)
# return send_file(xmind_file_path, as_attachment=True)
@app.route('/history')
def history():
# 获取当前工作目录
current_directory = os.getcwd()
# 获取当前目录下所有文件的名称
files = os.listdir(current_directory)
files = sorted(files, key=lambda x: os.path.getmtime(os.path.join(current_directory, x)))
files.reverse()
files1 = []
for i in files:
if re.match("{}\d+.xmind".format(session['username']+"-"), i):
files1.append(i)
return render_template('index.html', files=files1)
@app.route('/extract', methods=['POST'])
def extract():
selected_files = request.form.getlist('file')
print(selected_files)
# path = "C:\\Users\Administrator\Desktop\dist\{}".format(selected_files[0])
# 在这里添加提取选定文件的逻辑
# 例如可以将选定的文件复制到特定目录中
return send_file(selected_files[0], as_attachment=True)
# @app.route('/html')
# def html_show():
# return render_template('example.html')
if __name__ == '__main__':
app.run()
login.html(登录界面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Login</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(to left, #04f87d, #c7e8bf);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
text-align: center;
}
h2 {
color: #333;
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: calc(100% - 22px);
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
input[type="submit"] {
width: 100%;
background-color: #4caf50;
color: #fff;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #45a049;
}
p {
margin-top: 15px;
font-size: 14px;
}
p a {
text-decoration: none;
color: #007bff;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>GPT-MindMap</h1>
<h2>用户登录</h2>
<form action="{{ url_for('login') }}" method="post">
<label for="login-username">用户名:</label>
<input type="text" id="login-username" name="username" required><br>
<label for="login-password">密码:</label>
<input type="password" id="login-password" name="password" required><br>
<input type="submit" value="登录">
</form>
<p>没有账户?<a href="{{ url_for('register') }}">点击注册</a></p>
</div>
</body>
</html>
register.html(注册界面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Registration</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(to right, #04f87d, #c7e8bf);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
text-align: center;
}
h2 {
color: #333;
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: calc(100% - 22px);
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
input[type="submit"] {
width: 100%;
background-color: #4caf50;
color: #fff;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
input[type="submit"]:hover {
background-color: #45a049;
}
p {
margin-top: 15px;
font-size: 14px;
}
p a {
text-decoration: none;
color: #007bff;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>GPT-MindMap</h1>
<h2>用户注册</h2>
<form action="{{ url_for('register') }}" method="post">
<label for="reg-username">用户名:</label>
<input type="text" id="reg-username" name="username" required><br>
<label for="reg-password">密码:</label>
<input type="password" id="reg-password" name="password" required><br>
<input type="submit" value="注册">
</form>
<p>已有账户?<a href="{{ url_for('login') }}">点击登录</a></p>
</div>
</body>
</html>
index.html(历史记录提取界面)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Extractor</title>
<style>
body {
font-family: Arial, sans-serif;
background: linear-gradient(to right, #ffffff, #abfd98);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
text-align: center;
}
h1 {
color: #333;
margin-bottom: 20px;
}
form {
margin-top: 20px;
}
input[type="checkbox"] {
margin-right: 10px;
}
button[type="submit"] {
background-color: #069d19;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button[type="submit"]:hover {
background-color: #03f806;
}
</style>
</head>
<body>
<div class="container">
<h1>文件提取</h1>
<form action="/extract" method="post">
{% for file in files %}
<input type="checkbox" name="file" value="{{ file }}">{{ file }}<br>
{% endfor %}
<button type="submit">提取文件</button>
</form>
</div>
</body>
</html>
test.html(主界面--生成非递进思维导图)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mind Map Generator</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css">
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background-size: cover;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: white;
}
.swiper-container {
z-index: -1;
/* 将容器置于最低图层 */
width: 65%;
max-width: 975px;
overflow: hidden;
max-height: 390px;
margin-top: 0;
margin-left: auto;
margin-right: auto;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
}
.swiper-slide img {
width: 100%;
height: auto;
/* 让图片按比例缩放 */
display: block;
/* 垂直居中需要将图片设置为块级元素 */
margin: 0 auto;
/* 水平居中图片 */
}
.title {
z-index: -1;
position: absolute;
top: 110px;
/* 距离顶部的距离 */
left: 50%;
/* 让左边缘置于页面的中央 */
transform: translateX(-50%);
/* 通过平移来实现水平居中 */
padding: 1px;
/* 内边距 */
font-size: 70px;
/* 字体大小 */
font-weight: bold;
/* 字体加粗 */
}
.bottom-left {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-end;
padding: 20px;
flex: 1;
margin-top: 275px;
float: left;
}
.top-right {
position: absolute;
/* 使用绝对定位 */
bottom: -500px;
/* 元素距离页面底部的距离 */
left: 50%;
/* 将元素左边缘置于页面的中央 */
transform: translateX(-50%);
/* 将元素的中心点对齐到页面的中央 */
padding: 20px;
}
.top-right h3 {
font-size: 24px;
/* 调整字体大小 */
font-weight: bold;
/* 加粗字体 */
text-align: center;
/* 文字水平居中 */
margin: 0;
/* 去除默认的外边距 */
padding: 10px;
/* 添加内边距 */
}
.container1 {
height: 100%;
width: 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
header {
background-color: #333;
color: white;
padding: 10px 0;
text-align: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 70px;
}
nav ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-around;
height: 100%;
}
nav ul li {
display: inline;
position: relative;
}
nav ul li a {
text-decoration: none;
color: white;
padding: 10px;
line-height: 70px;
}
nav ul li .tooltip {
display: none;
/* 初始隐藏 */
position: absolute;
top: 100%;
/* 相对于父元素底部定位 */
left: 50%;
/* 相对于父元素居中定位 */
transform: translateX(-50%);
/* 水平居中 */
background: rgba(153, 153, 153, 0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 17px;
width: 400px;
/* 增加宽度 */
}
nav ul li:hover .tooltip {
display: block;
/* 鼠标悬停时显示 */
}
.container {
margin-top: 70px;
/* 为了避免内容被导航栏遮挡 */
padding: 100px;
}
h1 {
color: #333;
}
h2 {
color: #444;
margin-top: 20px;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
p {
color: #555;
}
form {
display: flex;
flex-direction: column;
}
iframe {
width: 250%;
height: 50vh;
border: 2px solid #0abab5;
/* 添加边框 */
border-radius: 5px;
/* 添加边框圆角 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
/* 添加阴影效果 */
position: relative;
/* 使用相对定位 */
left: 50%;
/* 将左边缘置于页面的中央 */
transform: translateX(-50%);
/* 通过平移来实现水平居中 */
}
.input-group {
display: flex;
flex-direction: column;
/* 修改为垂直方向排列 */
align-items: center;
/* 让内容水平居中 */
}
input[type="submit"] {
padding: 15px 30px;
/* 调整按钮的内边距 */
font-size: 20px;
/* 调整按钮的字体大小 */
border: none;
/* 移除按钮的边框 */
border-radius: 5px;
/* 添加按钮的边框半径 */
background-color: #0abab5;
/* 设置按钮的背景颜色为蒂芙尼绿 */
color: white;
/* 设置按钮文字颜色 */
cursor: pointer;
/* 添加鼠标悬停样式 */
margin-top: 10px;
/* 向下移动按钮 */
}
input[type="submit"]:hover {
background-color: #0cafb5;
/* 鼠标悬停时更换按钮的背景颜色 */
}
label {
flex: 1;
text-align: right;
margin-right: 10px;
font-weight: bold;
color: #333;
}
input[type="text"] {
border: 2px solid #0abab5;
flex: 2;
padding: 5px;
border: 3px solid #ccc;
border-radius: 5px;
font-size: 16px;
width: 900px;
}
.submit-button {
width: 150px;
margin-top: 10px;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
font-size: 18px;
cursor: pointer;
}
.submit-button:hover {
background-color: #0056b3;
}
.download-link {
color: #007bff;
text-decoration: none;
}
.download-link:hover {
text-decoration: underline;
}
.tips {
margin-top: 20px;
width: 80%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.tips-text {
flex: 1;
color: #555;
}
.icon {
font-size: 18px;
margin-right: 5px;
color: #007bff;
}
.form-container {
margin-top: 550px;
/* 根据需要调整上边距 */
text-align: center;
/* 让内容水平居中 */
}
.form-container h3 {
font-size: 24px;
/* 调整字体大小 */
font-weight: bold;
/* 加粗字体 */
}
.tooltip p {
color: white;
/* 设置段落文字颜色为白色 */
}
#loading {
position: fixed;
transform: translate(-0%, 0%);
display: none;
width: 100%;
height: 100%;
background: rgba(128, 128, 128, 0.8);
align-items: center;
justify-content: center;
z-index: 1000;
}
#content {
font-size: 160px;
color: white;
}
#timer {
display: flex;
position: top;
font-size: 108px;
color: white;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<header>
<nav>
<ul>
<li>
<a href="#usage">产品介绍</a>
<div class="tooltip" style="height: 320px">
本产品思维作为一个思维导图生成网站,通过调用chatgpt接口生成文本并进行处理得到xmind思维导图格式文本并生成xmind文件。
用于生成递进(如制作步骤)和非递进(如旅游指南)两类逻辑关系的思维导图。并具有用户登录以及历史记录功能
以便每个用户能够查看生成记录以及提取到本账号上生成过的思维导图<br><br><br>
使用工具及其版本:<br>
python3.11.1,flask(2.3.3),promptulate(1.8.2)、wtforms(0.1)、Xmind(1.2.0)、Flask-wtf(1.1.1)、
jinja2(3.1.2)、markdown(3.5.2)、sqlite3,腾讯云服务器
</div>
</li>
<li>
<a href="#tips">使用指南</a>
<div class="tooltip" style="height: 500px">
首先选择您需要生成的思维导图类别【递进(如制作步骤)和非递进(如旅游指南)】,并点击上方“跳转页面”中的“点击此处跳转递进式页面”,切换到相应的生成页面。<br><br>
在输入框中输入您想要生成的思维导图(如:线性代数,兰州旅游指南等),而后点击“提交”按钮等待即可生成<br><br>
生成结果会在页面下方的生成框中以文本的形式展现,如满足需求,您可以点击“跳转页面”中的“点击此处查看历史记录并提取文件”来获得生成的思维导图文件<br><br>
您生成的所有文件都会进行保存,您可以在“跳转页面”中的“点击此处查看历史记录并提取文件”来查看生成记录并提取生成过的文件<br><br>
思维导图文件为xmind格式,需要相应的打开方式,若您没有此文件的打开方式,可以在“跳转页面”中的“点击此处”点击此处下载Xmind8-Update9“来获取打开方式
</div>
</li>
<li>
<a href="#tips">开发团队</a>
<div class="tooltip" style="height: 100px">开发负责人:张锐垚<br><br><br> 成员:张屹哲,屠思成,严凯丰,张俊豪</div>
</p>
</div>
</li>
<li>
<a href="#tips">跳转页面</a>
<div class="tooltip" style="height: 200px">
<a href="{{ url_for('test1') }}">点击此处跳转递进式页面</a>
<br>
<a href="{{ url_for('history') }}">点击此处查看历史记录并提取文件</a>
<br>
<a href="https://xmind.cn/download/xmind8/" target="_blank" class="download-link"><i class="fas fa-download icon"></i>点击此处下载Xmind8-Update9</a>
</div>
</li>
</ul>
</nav>
</header>
<div class="title">GPT-MindMap</div>
<div id="loading">
<div id="content">生成中...<br>大约 10-20 秒</div>
<div id="timer">00:00:00</div>
</div>
<br>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide"> <img src="https://ts1.cn.mm.bing.net/th/id/R-C.9de53f9726576696b318a8d95c0946cb?rik=sWB3V9KSxHbThw&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f95%2f9995_1.jpg&ehk=GcPUjJED69TBvg9XxQr2klzDzfRsQWhAfLKlIAUWHJQ%3d&risl=&pid=ImgRaw&r=0"></div>
<div class="swiper-slide"> <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.YKoZzgmubNBxQ8j-mmoTKAHaEK?rs=1&pid=ImgDetMain"></div>
<div class="swiper-slide"> <img src="https://ts1.cn.mm.bing.net/th/id/R-C.d07816d313cad5d2b53b30192443c4c5?rik=SUozltQgAs8%2bNQ&riu=http%3a%2f%2fn.sinaimg.cn%2fsinacn10119%2f600%2fw1920h1080%2f20190325%2f6449-hutwezf3366892.jpg&ehk=DH2hT8Ey9e3gfn%2fUBKrQFjRb3LPXUs9sEIOq4LRDZfQ%3d&risl=&pid=ImgRaw&r=0"></div>
</div>
<div class="swiper-pagination"></div>
</div>
<div class="form-container">
<form method="post" action="{{ test }}" onsubmit="showLoading()">
{{ form.csrf_token }}
<h3>请输入您的需求</h3>
<div class="input-group">
{{ form.requirement }}
</div>
<div class="input-group">
{{ form.submit }}
</div>
</form>
</div>
<br><br>
<div class="top-right">
<h3>思维导图生成情况</h3>
<br>
<iframe srcdoc="{{ iframe_content }}"></iframe>
</div>
</body>
</html>
<script>
function showLoading() {
// 在开始时清除之前的定时器,避免重复创建
clearInterval(intervalId);
// 设置定时器,每隔固定时间间隔(比如1000毫秒,即1秒)调用一次函数
intervalId = setInterval(updateTimer, 1000);
// Show loading message when form is submitted
document.getElementById('loading').style.display = 'grid';
// Disable the submit button to prevent multiple submissions
document.getElementById('submitBtn').disabled = true;
}
let intervalId;
let seconds = 0;
let minutes = 0;
let hours = 0;
function updateTimer() {
seconds++;
if (seconds === 60) {
seconds = 0;
minutes++;
if (minutes === 60) {
minutes = 0;
hours++;
}
}
const formattedTime = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
document.getElementById('timer').innerText = formattedTime;
}
function pad(value) {
return value < 10 ? '0' + value : value;
}
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var mySwiper = new Swiper('.swiper-container', {
loop: true, // 无限循环
autoplay: {
delay: 3000 // 自动播放间隔
},
pagination: {
el: '.swiper-pagination', // 分页器元素
clickable: true // 可点击切换
}
});
});
</script>
test1.py(主界面--生成递进思维导图)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mind Map Generator</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css">
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background-size: cover;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: white;
}
.swiper-container {
z-index: -1;
/* 将容器置于最低图层 */
width: 65%;
max-width: 975px;
overflow: hidden;
max-height: 390px;
margin-top: 0;
margin-left: auto;
margin-right: auto;
position: absolute;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
}
.swiper-slide img {
width: 100%;
height: auto;
/* 让图片按比例缩放 */
display: block;
/* 垂直居中需要将图片设置为块级元素 */
margin: 0 auto;
/* 水平居中图片 */
}
.title {
z-index: -1;
position: absolute;
top: 110px;
/* 距离顶部的距离 */
left: 50%;
/* 让左边缘置于页面的中央 */
transform: translateX(-50%);
/* 通过平移来实现水平居中 */
padding: 1px;
/* 内边距 */
font-size: 70px;
/* 字体大小 */
font-weight: bold;
/* 字体加粗 */
}
.bottom-left {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-end;
padding: 20px;
flex: 1;
margin-top: 275px;
float: left;
}
.top-right {
position: absolute;
/* 使用绝对定位 */
bottom: -500px;
/* 元素距离页面底部的距离 */
left: 50%;
/* 将元素左边缘置于页面的中央 */
transform: translateX(-50%);
/* 将元素的中心点对齐到页面的中央 */
padding: 20px;
}
.top-right h3 {
font-size: 24px;
/* 调整字体大小 */
font-weight: bold;
/* 加粗字体 */
text-align: center;
/* 文字水平居中 */
margin: 0;
/* 去除默认的外边距 */
padding: 10px;
/* 添加内边距 */
}
.container1 {
height: 100%;
width: 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
border-radius: 5px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
header {
background-color: #333;
color: white;
padding: 10px 0;
text-align: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 70px;
}
nav ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-around;
height: 100%;
}
nav ul li {
display: inline;
position: relative;
}
nav ul li a {
text-decoration: none;
color: white;
padding: 10px;
line-height: 70px;
}
nav ul li .tooltip {
display: none;
/* 初始隐藏 */
position: absolute;
top: 100%;
/* 相对于父元素底部定位 */
left: 50%;
/* 相对于父元素居中定位 */
transform: translateX(-50%);
/* 水平居中 */
background: rgba(153, 153, 153, 0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-size: 17px;
width: 400px;
/* 增加宽度 */
}
nav ul li:hover .tooltip {
display: block;
/* 鼠标悬停时显示 */
}
.container {
margin-top: 70px;
/* 为了避免内容被导航栏遮挡 */
padding: 100px;
}
h1 {
color: #333;
}
h2 {
color: #444;
margin-top: 20px;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
p {
color: #555;
}
form {
display: flex;
flex-direction: column;
}
iframe {
width: 250%;
height: 50vh;
border: 2px solid #0abab5;
/* 添加边框 */
border-radius: 5px;
/* 添加边框圆角 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
/* 添加阴影效果 */
position: relative;
/* 使用相对定位 */
left: 50%;
/* 将左边缘置于页面的中央 */
transform: translateX(-50%);
/* 通过平移来实现水平居中 */
}
.input-group {
display: flex;
flex-direction: column;
/* 修改为垂直方向排列 */
align-items: center;
/* 让内容水平居中 */
}
input[type="submit"] {
padding: 15px 30px;
/* 调整按钮的内边距 */
font-size: 20px;
/* 调整按钮的字体大小 */
border: none;
/* 移除按钮的边框 */
border-radius: 5px;
/* 添加按钮的边框半径 */
background-color: #0abab5;
/* 设置按钮的背景颜色为蒂芙尼绿 */
color: white;
/* 设置按钮文字颜色 */
cursor: pointer;
/* 添加鼠标悬停样式 */
margin-top: 10px;
/* 向下移动按钮 */
}
input[type="submit"]:hover {
background-color: #0cafb5;
/* 鼠标悬停时更换按钮的背景颜色 */
}
label {
flex: 1;
text-align: right;
margin-right: 10px;
font-weight: bold;
color: #333;
}
input[type="text"] {
border: 2px solid #0abab5;
flex: 2;
padding: 5px;
border: 3px solid #ccc;
border-radius: 5px;
font-size: 16px;
width: 900px;
}
.submit-button {
width: 150px;
margin-top: 10px;
padding: 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
font-size: 18px;
cursor: pointer;
}
.submit-button:hover {
background-color: #0056b3;
}
.download-link {
color: #007bff;
text-decoration: none;
}
.download-link:hover {
text-decoration: underline;
}
.tips {
margin-top: 20px;
width: 80%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.tips-text {
flex: 1;
color: #555;
}
.icon {
font-size: 18px;
margin-right: 5px;
color: #007bff;
}
.form-container {
margin-top: 550px;
/* 根据需要调整上边距 */
text-align: center;
/* 让内容水平居中 */
}
.form-container h3 {
font-size: 24px;
/* 调整字体大小 */
font-weight: bold;
/* 加粗字体 */
}
.tooltip p {
color: white;
/* 设置段落文字颜色为白色 */
}
#loading {
position: fixed;
transform: translate(-0%, 0%);
display: none;
width: 100%;
height: 100%;
background: rgba(128, 128, 128, 0.8);
align-items: center;
justify-content: center;
z-index: 1000;
}
#content {
font-size: 160px;
color: white;
}
#timer {
display: flex;
position: top;
font-size: 108px;
color: white;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<header>
<nav>
<ul>
<li>
<a href="#usage">产品介绍</a>
<div class="tooltip" style="height: 320px">
本产品思维作为一个思维导图生成网站,通过调用chatgpt接口生成文本并进行处理得到xmind思维导图格式文本并生成xmind文件。
用于生成递进(如制作步骤)和非递进(如旅游指南)两类逻辑关系的思维导图。并具有用户登录以及历史记录功能
以便每个用户能够查看生成记录以及提取到本账号上生成过的思维导图<br><br><br>
使用工具及其版本:<br>
python3.11.1,flask(2.3.3),promptulate(1.8.2)、wtforms(0.1)、Xmind(1.2.0)、Flask-wtf(1.1.1)、
jinja2(3.1.2)、markdown(3.5.2)、sqlite3,腾讯云服务器
</div>
</li>
<li>
<a href="#tips">使用指南</a>
<div class="tooltip" style="height: 500px">
首先选择您需要生成的思维导图类别【递进(如制作步骤)和非递进(如旅游指南)】,并点击上方“跳转页面”中的“点击此处跳转非递进式页面”,切换到相应的生成页面。<br><br>
在输入框中输入您想要生成的思维导图(如:线性代数,兰州旅游指南等),而后点击“提交”按钮等待即可生成<br><br>
生成结果会在页面下方的生成框中以文本的形式展现,如满足需求,您可以点击“跳转页面”中的“点击此处查看历史记录并提取文件”来获得生成的思维导图文件<br><br>
您生成的所有文件都会进行保存,您可以在“跳转页面”中的“点击此处查看历史记录并提取文件”来查看生成记录并提取生成过的文件<br><br>
思维导图文件为xmind格式,需要相应的打开方式,若您没有此文件的打开方式,可以在“跳转页面”中的“点击此处”点击此处下载Xmind8-Update9“来获取打开方式
</div>
</li>
<li>
<a href="#tips">开发团队</a>
<div class="tooltip" style="height: 100px">开发负责人:张锐垚<br><br><br> 成员:张屹哲,屠思成,严凯丰,张俊豪</div>
</p>
</div>
</li>
<li>
<a href="#tips">跳转页面</a>
<div class="tooltip" style="height: 200px">
<a href="{{ url_for('test') }}">点击此处跳转非递进式页面</a>
<br>
<a href="{{ url_for('history') }}">点击此处查看历史记录并提取文件</a>
<br>
<a href="https://xmind.cn/download/xmind8/" target="_blank" class="download-link"><i class="fas fa-download icon"></i>点击此处下载Xmind8-Update9</a>
</div>
</li>
</ul>
</nav>
</header>
<div class="title">GPT-MindMap</div>
<div id="loading">
<div id="content">生成中...<br>大约 30-60 秒</div>
<div id="timer">00:00:00</div>
</div>
<br>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide"> <img src="https://ts1.cn.mm.bing.net/th/id/R-C.9de53f9726576696b318a8d95c0946cb?rik=sWB3V9KSxHbThw&riu=http%3a%2f%2fpic.bizhi360.com%2fbbpic%2f95%2f9995_1.jpg&ehk=GcPUjJED69TBvg9XxQr2klzDzfRsQWhAfLKlIAUWHJQ%3d&risl=&pid=ImgRaw&r=0"></div>
<div class="swiper-slide"> <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.YKoZzgmubNBxQ8j-mmoTKAHaEK?rs=1&pid=ImgDetMain"></div>
<div class="swiper-slide"> <img src="https://ts1.cn.mm.bing.net/th/id/R-C.d07816d313cad5d2b53b30192443c4c5?rik=SUozltQgAs8%2bNQ&riu=http%3a%2f%2fn.sinaimg.cn%2fsinacn10119%2f600%2fw1920h1080%2f20190325%2f6449-hutwezf3366892.jpg&ehk=DH2hT8Ey9e3gfn%2fUBKrQFjRb3LPXUs9sEIOq4LRDZfQ%3d&risl=&pid=ImgRaw&r=0"></div>
</div>
<div class="swiper-pagination"></div>
</div>
<div class="form-container">
<form method="post" action="{{ test }}" onsubmit="showLoading()">
{{ form.csrf_token }}
<h3>请输入您的需求</h3>
<div class="input-group">
{{ form.requirement }}
</div>
<div class="input-group">
{{ form.submit }}
</div>
</form>
</div>
<br><br>
<div class="top-right">
<h3>思维导图生成情况</h3>
<br>
<iframe srcdoc="{{ iframe_content }}"></iframe>
</div>
</body>
</html>
<script>
function showLoading() {
// 在开始时清除之前的定时器,避免重复创建
clearInterval(intervalId);
// 设置定时器,每隔固定时间间隔(比如1000毫秒,即1秒)调用一次函数
intervalId = setInterval(updateTimer, 1000);
// Show loading message when form is submitted
document.getElementById('loading').style.display = 'grid';
// Disable the submit button to prevent multiple submissions
document.getElementById('submitBtn').disabled = true;
}
let intervalId;
let seconds = 0;
let minutes = 0;
let hours = 0;
function updateTimer() {
seconds++;
if (seconds === 60) {
seconds = 0;
minutes++;
if (minutes === 60) {
minutes = 0;
hours++;
}
}
const formattedTime = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
document.getElementById('timer').innerText = formattedTime;
}
function pad(value) {
return value < 10 ? '0' + value : value;
}
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var mySwiper = new Swiper('.swiper-container', {
loop: true, // 无限循环
autoplay: {
delay: 3000 // 自动播放间隔
},
pagination: {
el: '.swiper-pagination', // 分页器元素
clickable: true // 可点击切换
}
});
});
</script>
视频演示
演示视频
结语
此项目仅为学生创新项目,只是提供了框架和思路,并不完善,如有可改进之处希望各位不吝赐教,若有问题也欢迎留言讨论。