小編這次要給大家分享的是如何使用Vue+Django+Ant Design做一個(gè)留言評(píng)論模塊,文章內(nèi)容豐富,感興趣的小伙伴可以來(lái)了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。
創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站建設(shè)、成都網(wǎng)站制作、千山網(wǎng)絡(luò)推廣、小程序開(kāi)發(fā)、千山網(wǎng)絡(luò)營(yíng)銷、千山企業(yè)策劃、千山品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供千山建站搭建服務(wù),24小時(shí)服務(wù)熱線:13518219792,官方網(wǎng)址:www.cdcxhl.com
1.總覽
留言的展示參考網(wǎng)絡(luò)上參見(jiàn)的格式,如掘金社區(qū):
一共分為兩層,子孫留言都在第二層中
最終效果如下:
接下是數(shù)據(jù)庫(kù)的表結(jié)構(gòu),如下所示:
有一張user表和留言表,關(guān)系為一對(duì)多,留言表有父留言字段的id,和自身有一個(gè)一對(duì)多的關(guān)系,建表語(yǔ)句如下:
CREATE TABLE `message` ( `id` int NOT NULL AUTO_INCREMENT, `date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `content` text NOT NULL, `parent_msg_id` int DEFAULT NULL, `user_id` int NOT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `message_ibfk_1` (`parent_msg_id`), CONSTRAINT `message_ibfk_1` FOREIGN KEY (`parent_msg_id`) REFERENCES `message` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `message_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, `identity` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
2.后臺(tái)接口
2.1獲取留言接口
在Django的views.py中定義兩個(gè)接口,一個(gè)負(fù)責(zé)提供留言內(nèi)容,一個(gè)負(fù)責(zé)插入留言,如下:
# 獲取留言信息 @require_http_methods(['GET']) def findAllMsg(request): response = {} try: sql = ''' SELECT msg1.*, user.username, msg2.username AS parent_msg_username FROM message msg1 LEFT JOIN (SELECT m.id, user.username FROM message m LEFT JOIN USER ON m.user_id = user.id )AS msg2 ON msg1.parent_msg_id = msg2.id LEFT JOIN USER ON msg1.user_id = user.id ORDER BY msg1.date DESC; ''' with connection.cursor() as cursor: cursor.execute(sql) response['messages'] = sortMsg(cursor) response['status_code'] = 200 except Exception as e: response['status_code'] = 500 response['error'] = e return JsonResponse(response)
先來(lái)看看這個(gè)sql能查出些什么東西:
上面接口中的sorMsg()函數(shù)用于整理留言信息,使子留言和父留言能對(duì)應(yīng)起來(lái),算法實(shí)現(xiàn)如下:
# 整理留言信息返回格式 def sortMsg(cursor): list = [] allMsg = dictfetchall(cursor) for i in range(len(allMsg)): tmpParent = allMsg[i] tmpChild = [] # 如果沒(méi)有屬于根評(píng)論,則搜索該評(píng)論下的所有子評(píng)論 if tmpParent.get('parent_msg_id') == None: tmpChild = bfs(tmpParent, allMsg) # 如果是子評(píng)論則跳過(guò),子評(píng)論最終會(huì)出現(xiàn)在根評(píng)論的子節(jié)點(diǎn)中 else: continue tmpParent['children'] = tmpChild # 格式化時(shí)間 tmpParent['date'] = datetime.datetime.strftime(tmpParent['date'], '%Y-%m-%d %H:%M:%S') list.append(tmpParent) return list # 搜索一條留言的所有子留言,廣度優(yōu)先 import queue def bfs(parent, allMsg): childrenList = [] q = queue.Queue() q.put(parent) while(not q.empty()): tmpChild = q.get() for i in range(len(allMsg)): if allMsg[i]['parent_msg_id'] is not None and allMsg[i]['parent_msg_id'] == tmpChild['id']: childrenList.append(allMsg[i]) q.put(allMsg[i]) # 子留言列表按時(shí)間降序排序 childrenList = sorted(childrenList, key = lambda d: d['date'], reverse = True) # 格式化日期格式 for item in childrenList: item['date'] = datetime.datetime.strftime(item['date'], '%Y-%m-%d %H:%M:%S') return childrenList
用postman測(cè)試接口,得到的json格式如下:
{ "messages": [ { "id": 12, "date": "2020-05-31 12:19:43", "content": "你好啊,太棒了", "parent_msg_id": null, "user_id": 5, "username": "wangwu", "parent_msg_username": null, "children": [] }, { "id": 11, "date": "2020-05-31 12:18:55", "content": "的時(shí)刻層6666666632\n2面的思考名稱看到什么材料是isdafjoisdjiojildsc", "parent_msg_id": null, "user_id": 3, "username": "zhangsan", "parent_msg_username": null, "children": [] }, { "id": 5, "date": "2020-05-29 19:09:33", "content": "發(fā)的發(fā)射點(diǎn)發(fā)吖方吖是發(fā)是呵等方5愛(ài)的非4阿瑟東方 發(fā)", "parent_msg_id": null, "user_id": 4, "username": "lisi", "parent_msg_username": null, "children": [ { "id": 13, "date": "2020-05-31 12:20:12", "content": "號(hào)好好好矮好矮好矮好好", "parent_msg_id": 5, "user_id": 6, "username": "zhaoliu", "parent_msg_username": "lisi" } ] }, { "id": 1, "date": "2020-05-29 19:06:21", "content": "fasfdsafas法阿薩德方吖65阿瑟東方5是的發(fā)", "parent_msg_id": null, "user_id": 1, "username": "student", "parent_msg_username": null, "children": [ { "id": 7, "date": "2020-05-29 19:29:29", "content": "hfhf2h32h322223232", "parent_msg_id": 6, "user_id": 1, "username": "student", "parent_msg_username": "zhaoliu" }, { "id": 6, "date": "2020-05-29 19:09:56", "content": "而離開(kāi)離開(kāi)鄰居哦i據(jù)哦i報(bào)價(jià)哦v保健品45465", "parent_msg_id": 4, "user_id": 6, "username": "zhaoliu", "parent_msg_username": "mike" }, { "id": 4, "date": "2020-05-29 19:09:14", "content": "發(fā)送端非場(chǎng)地薩擦手d5asd32 1dads\r\ndsac十多次ds出錯(cuò)", "parent_msg_id": 2, "user_id": 8, "username": "mike", "parent_msg_username": "lisi" }, { "id": 3, "date": "2020-05-29 19:08:56", "content": "奮發(fā)惡法撒打發(fā)士大夫士大夫是大 大師傅撒", "parent_msg_id": 2, "user_id": 2, "username": "teacher", "parent_msg_username": "lisi" }, { "id": 2, "date": "2020-05-29 19:08:41", "content": "fasdfasdf發(fā)生的法撒旦飛灑多發(fā)點(diǎn)房地產(chǎn)", "parent_msg_id": 1, "user_id": 4, "username": "lisi", "parent_msg_username": "student" } ] } ], "status_code": 200 }
這個(gè)就是前臺(tái)所要的內(nèi)容了。
其實(shí)一開(kāi)始我是很直觀地認(rèn)為是用深度優(yōu)先來(lái)取出層層嵌套的留言的,如下:
# 遞歸搜索一條留言的所有子留言,深度優(yōu)先 def dfs(parent, allMsg): childrenList = [] for i in range(len(allMsg)): if allMsg[i]['parent_msg_id'] is not None and allMsg[i]['parent_msg_id'] == parent['id']: allMsg[i]['children'] = dfs(allMsg[i], allMsg) childrenList.append(allMsg[i]) return childrenList
這樣取出的json格式是這樣的:
{ "messages": [ { "id": 5, "date": "2020-05-29 19:09:33", "content": "發(fā)的發(fā)射點(diǎn)發(fā)吖方吖是發(fā)是呵等方5愛(ài)的非4阿瑟東方 發(fā)", "parent_msg_id": null, "user_id": 4, "username": "lisi", "children": [ { "id": 8, "date": "2020-05-29T17:23:37", "content": "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈呵呵呵呵呵呵", "parent_msg_id": 5, "user_id": 3, "username": "zhangsan", "children": [] } ] }, { "id": 1, "date": "2020-05-29 19:06:21", "content": "fasfdsafas法阿薩德方吖65阿瑟東方5是的發(fā)", "parent_msg_id": null, "user_id": 1, "username": "student", "children": [ { "id": 2, "date": "2020-05-29T19:08:41", "content": "fasdfasdf發(fā)生的法撒旦飛灑多發(fā)點(diǎn)房地產(chǎn)", "parent_msg_id": 1, "user_id": 4, "username": "lisi", "children": [ { "id": 4, "date": "2020-05-29T19:09:14", "content": "發(fā)送端非場(chǎng)地薩擦手d5asd32 1dads\r\ndsac十多次ds出錯(cuò)", "parent_msg_id": 2, "user_id": 8, "username": "mike", "children": [ { "id": 6, "date": "2020-05-29T19:09:56", "content": "而離開(kāi)離開(kāi)鄰居哦i據(jù)哦i報(bào)價(jià)哦v保健品45465", "parent_msg_id": 4, "user_id": 6, "username": "zhaoliu", "children": [ { "id": 7, "date": "2020-05-29T19:29:29", "content": "hfhf2h32h322223232", "parent_msg_id": 6, "user_id": 1, "username": "student", "children": [] } ] } ] }, { "id": 3, "date": "2020-05-29T19:08:56", "content": "奮發(fā)惡法撒打發(fā)士大夫士大夫是大 大師傅撒", "parent_msg_id": 2, "user_id": 2, "username": "teacher", "children": [] }, { "id": 9, "date": "2020-05-29T17:27:13", "content": "alalla啦啦啦啦啦啦來(lái)的隊(duì)列李大水泛濫的薩拉發(fā) 的 第三方哈l", "parent_msg_id": 2, "user_id": 7, "username": "joke", "children": [] } ] } ] } ], "status_code": 200 }
但仔細(xì)一想,實(shí)際頁(yè)面展示的時(shí)候肯定不能這樣一層層無(wú)限地嵌套下去,否則留言多了頁(yè)面就裝不下了,于是還是改成了兩層留言的格式,第二層使用廣度優(yōu)先搜索將樹(shù)轉(zhuǎn)為列表存儲(chǔ)。
2.2 新增留言接口
前臺(tái)提供留言內(nèi)容、留言者id以及父留言的id(如果不是回復(fù)信息的話就是空)
import datetime @require_http_methods(['POST']) def insertMsg(request): response = {} try: request.POST = request.POST.copy() request.POST['date'] = datetime.datetime.now() msg = Message() msg.date = request.POST.get('date') msg.content = request.POST.get('content') msg.parent_msg_id = request.POST.get('parent_msg_id') msg.user_id = request.POST.get('user_id') msg.save() response['msg'] = 'success' response['status_code'] = 200 except Exception as e: response['error'] = str(e) response['status_code'] = 500 return JsonResponse(response)
3.前臺(tái)設(shè)計(jì)
有了后臺(tái)提供的數(shù)據(jù),前臺(tái)展示就比較簡(jiǎn)單了。
留言板塊的設(shè)計(jì)我使用了Ant Design的留言組件。
留言界面主要由兩個(gè)組件所構(gòu)成——留言區(qū)組件以及評(píng)論表單的組件
3.1主視圖Messeage.vue
3.2 留言區(qū)域組件CommentMessage.vue:
3.3 留言區(qū)域由多個(gè)Comment留言組件所構(gòu)成,留言組件定義如下
回復(fù) {{comment.username}} {{comment.content}}
{{comment.date}}
3.4 添加留言或回復(fù)的表單組件CommentArea.vue
添加留言
組裝完成后實(shí)現(xiàn)的功能有:
留言界面的展示
點(diǎn)擊回復(fù)按鈕跳到留言表單(這里我直接用了a標(biāo)簽來(lái)錨定位,試過(guò)用scrollToView來(lái)平滑滾動(dòng)過(guò)去,但不知道為什么只有第一次點(diǎn)擊回復(fù)按鈕時(shí)才能平滑滾動(dòng)到,之后再點(diǎn)擊他就不滾動(dòng)了。。。),并把被回復(fù)者的用戶名顯示在placeholder中
點(diǎn)擊添加留言按鈕,清空placeholder,并自動(dòng)實(shí)現(xiàn)router-view的局部刷新(不是整頁(yè)刷新)顯示出新增的留言
局部刷新的實(shí)現(xiàn)就是通過(guò)代碼中的自定義事件 reload
,具體就是從表單組件開(kāi)始發(fā)送 reload
事件,其父組件 Message.vue
收到后,再繼續(xù)發(fā)送 reload
事件給外層的視圖Home.vue,Home的再外層就是App.vue了,Home.vue的定義如下:
里面有一個(gè)reload方法,通過(guò)改變isRouterAlive來(lái)讓router-view先隱藏,再顯示,實(shí)現(xiàn)重新掛載。
看完這篇關(guān)于如何使用Vue+Django+Ant Design做一個(gè)留言評(píng)論模塊的文章,如果覺(jué)得文章內(nèi)容寫得不錯(cuò)的話,可以把它分享出去給更多人看到。