讨论一些代码实现的逻辑四
上一篇博客介绍了项目的目录结构,已经有了一点对项目的认识,接下来我将具体简述一些主要功能的代码实现的逻辑(原本我是想具体讲述的,然后发现太多了,于是就挑选几个来将一下实现的逻辑吧)
其余更多代码实现还是直接看源码吧
# 前言
上一篇博客介绍了项目的目录结构,已经有了一点对项目的认识,接下来我将具体简述一些主要功能的代码实现的逻辑(原本我是想具体讲述的,然后发现太多了,于是就挑选几个来将一下实现的逻辑吧)
其余更多代码实现还是直接看源码吧
# 1. 登录功能
主要分为登录、注册、找回密码
有两个登录的地方,一个是介绍页面、一个是星云笔记页面
前者的前端是用bootstrap的选项卡功能实现的,后者则是加上了模态框。
至于其对应的js代码,我也是放在 /resource/js/login.js 文件中
实现了发送邮件、登录、注册、退出登录的事件
另外,我还添加了接受经度和维度,获取用户的地理位置的代码,及用户每次登录、以及用户注册时会获取(但是,这个功能要支持https服务才能开启,不然会被拦截,不能实现)
# 2. 悬浮框功能
这是一个小的功能,方便用户的快速跳转到主要功能页面
其主要是依靠css样式和js的鼠标监听事件来完成的
<link rel="stylesheet" href="/css/悬浮框.css"/>
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
<div class="Box_xuanfu">
<div class="box_xuanfu">
<ion-icon name="apps-sharp"></ion-icon>
</div>
<div>
<a href="/notebook" class="panel" >
<ion-icon name="brush"></ion-icon>
<span class="tooltiptext">笔记</span>
</a>
<a href="/person/{{session.get('userid')}}" class="panel" >
<ion-icon name="person"></ion-icon>
</a>
<a href="/chatroom/index" class="panel" >
<ion-icon name="chatbox-ellipses"></ion-icon>
</a>
<a href="/home/{{session.get('userid')}}" class="panel" >
<ion-icon name="home"></ion-icon>
</a>
<a href="/tools" class="panel" >
<ion-icon name="construct"></ion-icon>
</a>
<a href="#" class="panel" >
<ion-icon name="settings"></ion-icon>
</a>
</div>
</div>
<script src="/js/悬浮框.js"></script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 3. markdown编辑器和富文本编辑器的共同集成
星云笔记是支持使用markdown编辑器和富文本编辑器的共同使用。
而我是如何区分哪些文章是由markdown语法编写,哪些文章是html语法呢。
可以通过在article表中插入一列,来区别,比如我用1表示是markdown,用表示是文本, 然后我可以在提交文章的代码中添加这个判断参数,传给后端处理,
我定义了一个函数,用来区分它们
def is_markdown(articleid):
article = Article()
result = article.find_is_markdown_by_id(articleid)
if result and result.is_markdown == 1:
return "md_article"
else:
return "article"
2
3
4
5
6
7
8
9
10
将返回的值拼接到URL中,就能区别开来
markdown语法写的:
http://www.whtuu.cn/notebook/md_article/41
富文本写的:
http://www.whtuu.cn/notebook/article/42
2
3
4
5
6
# 4. 工具库的类别的分类
在工具库的工具分类时,我是通过给每一个类别映射一个数字
# 1代表AI工具、11代表AI工具下的”常用"、12代表AI工具下的”AI图像工具"、13代表AI工具下的”AI聊天工具"、14代表AI工具下的”AI 视频工具"
# 2代表生信软件,21、22、23、24
# 3代表生信网站,31、32、33、34
# 4代表消息获取
# 5代表设计美化
# 6代表学习网站
# 7代表其它
2
3
4
5
6
7
8
然后在提交工具的时候,将每个类别对应的数字提交到数据库就行
同样我也定义了一个代码用来返回对应的分类名称
# 根据tools_type命名对应的分类信息
def ming_tools_by_type(type):
primary_category = ""
secondary_category = ""
if type.startswith("1"):
primary_category = "AI工具"
if type == "11":
secondary_category = "常用"
elif type == "12":
secondary_category = "AI图像工具"
elif type == "13":
secondary_category = "AI聊天工具"
elif type == "14":
secondary_category = "AI视频工具"
elif type.startswith("2"):
primary_category = "生信软件"
if type == "21":
secondary_category = "常用"
elif type == "22":
secondary_category = "基因相关"
elif type == "23":
secondary_category = "环境相关"
elif type == "24":
secondary_category = "其它"
elif type.startswith("3"):
primary_category = "生信网站"
if type == "31":
secondary_category = "常用"
elif type == "32":
secondary_category = "基因相关"
elif type == "33":
secondary_category = "环境相关"
elif type == "34":
secondary_category = "其它"
elif type.startswith("4"):
primary_category = "消息获取"
if type == "41":
secondary_category = "日常"
elif type == "42":
secondary_category = "生信相关"
elif type == "43":
secondary_category = "AI相关"
elif type == "44":
secondary_category = "古诗词文化"
elif type == "5":
primary_category = "设计美化"
elif type == "6":
primary_category = "学习网站"
elif type == "7":
primary_category = "其他"
return primary_category, secondary_category
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 5. 在flask中引入echarts图表
主要是将从后端获取的数据填充到前端的图表中
大致分为两类:
一类是 返回 xxx.dump_options_with_quotes()
# 用户的文章条形图
@py_echarts.route("/py_echarts/bar-article-user", methods=["POST", "GET"])
def get_bar_chart_user():
userid = request.form.get("userid")
article = Article()
favorite = Favorite()
comment = Comment()
favorite_count = favorite.query_my_favorite_count(userid)
favorited_count = favorite.query_my_article_favorited_count(userid)
article_count = article.get_count_user_except_drafted(userid)
drafted_count = article.get_count_user_drafted(user_id=userid)
comment_count = comment.query_comments_count_by_userid(userid)
commented_count = comment.query_comments_count_by_articleid(userid)
read_count = article.get_readcount_by_user(userid)
recommend_count = article.query_recommend_count_by_user(userid)
hide_count = article.query_hide_count_by_user(userid)
# 设置条形图的标签和值
bar = (
Bar()
.add_xaxis(
["收藏数", "被收藏数", "文章数", "草稿数", "评论数", "被评论数", "阅读数", "推荐数", "隐藏数"]) # x轴标签
.add_yaxis("用户文章信息",
[favorite_count, favorited_count, article_count, drafted_count, comment_count, commented_count,
read_count, recommend_count, hide_count]
, itemstyle_opts=opts.ItemStyleOpts(color="#749f83")) # y轴值,对应上面的标签
)
return bar.dump_options_with_quotes()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
然后在前端页面的js代码中发送ajax请求给上面定义的接口
//文章总结的条形图
var chart_bar = echarts.init(document.getElementById('bar-article'), 'white', {renderer: 'canvas'});
$(
function () {
fetchData_bar(chart_bar);
}
);
function fetchData_bar() {
$.ajax({
type: "POST",
data: {
userid: userid
},
url: "/py_echarts/bar-article-user",
dataType: "json",
success: function (result) {
chart_bar.setOption(result);
}
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
另一类则是,使用flask 的render_template() 函数将 生成的嵌入式代码 rendered_chart = c.render_embed(),传给前端;
# 管理后台的用户数据
@py_echarts.route("/person/admin/user")
def admin_user():
user = Users()
user_at = user.find_by_admin()
user_count = user.get_count_all_user()
user_locations = user.find_all_users_location()
# 在这里执行生成地图的代码
from pyecharts import options as opts
# 创建 Geo 地图实例
c = (
Geo(InitOpts(width="1000px", height="1000px"))
.add_schema(maptype="china") # 设置地图类型为中国地图
)
data = []
# 遍历 user_locations,添加每个用户的地理位置到地图中
for userid, time, lon, lat in user_locations:
# 添加坐标点,这里假设每个用户的位置都有一个唯一的名称
# 例如,可以使用时间戳或者用户ID作为名称
name = userid
c.add_coordinate(name, lon, lat)
value = str(time) # 或者其他您想展示的值
# 将当前用户的位置和值添加到 data 列表中
data.append((name, value))
series_name = "用户来源"
c.add(series_name, data, type_=ChartType.EFFECT_SCATTER)
# 设置系列和全局选项
c.set_series_opts(label_opts=opts.LabelOpts(is_show=False)) # 设置标签不显示
c.set_global_opts(
visualmap_opts=opts.VisualMapOpts(is_show=False), # 设置视觉映射配置项,比如颜色条
title_opts=opts.TitleOpts(title="用户地理位置分布") # 设置图表标题
)
# 生成嵌入代码
rendered_chart = c.render_embed()
return render_template('/shujumianbang/user_admin.html',
user_at=user_at, user_count=user_count, chart=rendered_chart)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
然后,在前端使用jinji2的语法 ,将图表插入
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="row" style="text-align: center;">
<div class="col-6">
<div class="d-md-flex align-items-center " style="width:900px">
<h2>用户的来源记录</h2>
</div>
<div style="width:1600px; height:900px;">
{{chart |safe}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6. 聊天室的实现
要安装flask_socketio ,创建socketio实例
控制台代码:
@chatroom.route("/chatroom/chatroom", methods=['POST', 'GET'])
def chatroom_chatroom():
if session.get('islogin') is None:
return redirect(url_for('index'))
else:
user_id = session.get('userid')
message = Message()
message_result = message.get_messages()
# users = Users().find_all_users_all()
follow = Follow()
user_guanzhu = follow.get_follow_users_with_details(userid=session.get('userid'))
avatar = message.get_at_avatar(user_id=user_id)
# nickname = session.get('nickname')
return render_template("/chatroom/chatroom.html", message_result=message_result, user_guanzhu=user_guanzhu,
avatar=avatar)
# 连接聊天室
@socketio.on('connect', namespace='/chatroom/chatroom')
def connect():
print('连接成功!!')
# 加入房间
@socketio.on('joined', namespace='/chatroom/chatroom')
def joined(information):
# 'joined'路由是传入一个room_name,给该websocket连接分配房间,返回一个'status'路由
room_name = 'chat room'
user_name = session.get('nickname')
print(user_name)
join_room(room_name)
emit('status', {'server_to_client': user_name + ' enter the room'}, 'room=room_name')
# 接收聊天信息
@socketio.on('text', namespace='/chatroom/chatroom')
def text(information):
text = information.get('text')
nick_name = session.get('nickname') # 获取用户名称
user_id = session.get('userid') # 获取用户ID
communicate_user = request.form.get('communicate_user')
Message().insert_message(content=text, communicate_userid=communicate_user) # 将聊天信息插入数据库,更新数据库
create_time = time.strftime('%Y-%m-%d %H:%M:%S')
avatar = Message().get_at_avatar(user_id=user_id) # 获取用户头像
# 返回聊天信息给前端
emit('message', {
'nickname': nick_name,
'text': text,
'create_time': create_time,
'avatar': avatar,
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
前后台对接的js代码:
$(document).ready(function () {
//创建实例
socket = io.connect('https://' + document.domain + ':' + location.port + '/chatroom/chatroom');
log('xxqqq', socket)
// alert()
socket.on('connect', function () {
log('连接成功');
});
socket.on('message', function (data) {
Username = data.nickname
text = data.text
create_time = data.create_time
var avatar_url = data.avatar
log(Username + ':' + text)
var deleteButton = "";
// if (userid === session.get("userid")) {
deleteButton = "<button class='delete-chedi btn btn-outline-success' data-messageid='" + data.message_id + "'>❌</button>";
// }
if (filepath) {
$(".chat-discussion").append("<div class='chat-message left'> <img class='message-avatar' src='/chat_room/images/" + avatar_url + "' alt='' > <div class='message'><a class='message-author' > " + Username + "</a> <span class='message-date'>" + create_time + " </span><span class='message-content'> <img src='" + filepath + "' class='imgchat' style='height: 100px;weight:100px'/>" + text + "</span> " + deleteButton +"</div> </div>");
$(".img").attr("src", '');
} else {
$(".chat-discussion").append("<div class='chat-message left'> <img class='message-avatar' src='/chat_room/images/" + avatar_url + "' alt='' > <div class='message'><a class='message-author' > " + Username + "</a> <span class='message-date'>" + create_time + " </span><span class='message-content'>" + text + "</span>" + deleteButton + " </div> </div>");
}
});
$('.enter').keypress(function (e) {
var code = e.keyCode || e.which;
if (code == 13) {
//获取聊天框聊天内容
var Texttext = $('.enter').val();
//清空聊天框
$('.enter').val('');
//向后端发送聊天内容
socket.emit('text', {'text': Texttext});
}
});
$('#Emoji').change(function () {
//一:
console.log($(this).val());
//二:
$("#Emoji").val();//获取当前选择项的值.
var options = $("#Emoji option:selected");//获取当前选择项.
options.val();//获取当前选择项的值.
$('.enter').val($('.enter').val() + options.val());
console.log(options.val());
})
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 总结
差不多就到这吧,其实,这个项目感觉并没有达到预期,只能说与我最开始想的样子不太一样,但是不管怎么说,也就先做到这一步了,虽然有些功能还有问题,需要完善。
寂寞空庭春欲晚,梨花满地不开门
--2024-3-22