亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Django搭建個(gè)人博客:用django-mptt實(shí)現(xiàn)多級(jí)評(píng)論功能

adie / 3763人閱讀

摘要:現(xiàn)在我們的博客已經(jīng)具有評(píng)論功能了。處理請(qǐng)求處理其他請(qǐng)求僅接受請(qǐng)求。前面寫視圖的時(shí)候,二級(jí)評(píng)論提交成功后會(huì)返回,回調(diào)函數(shù)接收到這個(gè)信號(hào)后,就會(huì)調(diào)用方法,刷新當(dāng)前的父頁(yè)面即文章所在的頁(yè)面,實(shí)現(xiàn)了數(shù)據(jù)的更新。

現(xiàn)在我們的博客已經(jīng)具有評(píng)論功能了。隨著文章的評(píng)論者越來(lái)越多,有的時(shí)候評(píng)論者之間也需要交流,甚至部分評(píng)論還能合并成一個(gè)小的整體。因此最好是有某種方法可以將相關(guān)的評(píng)論聚集到一起,這時(shí)候多級(jí)評(píng)論就非常的有用了。

多級(jí)評(píng)論意味著你需要將模型重新組織為樹形結(jié)構(gòu)。“樹根”是一級(jí)評(píng)論,而眾多“樹葉”則是次級(jí)評(píng)論。本教程會(huì)以第三方庫(kù)django-mptt為基礎(chǔ),開發(fā)多級(jí)評(píng)論功能。

django-mptt模塊包含了樹形數(shù)據(jù)結(jié)構(gòu)以及查詢、修改樹形數(shù)據(jù)的眾多方法。

任何需要樹形結(jié)構(gòu)的地方,都可以用 django-mptt 來(lái)搭建。比如目錄。

注意:本章新知識(shí)點(diǎn)較多,請(qǐng)讀者做好心理準(zhǔn)備,一定要耐心閱讀。

重構(gòu)模型

既然要建立樹形結(jié)構(gòu),老的評(píng)論模型肯定是要修改了。

首先安裝django-mptt

(env) > pip install django-mptt

安裝成功后,在配置中注冊(cè)

my_blog/settings.py

...
INSTALLED_APPS = [
    ...
    "mptt",

    ...
]
...

這些你已經(jīng)輕車熟路了。

接下來(lái),修改評(píng)論模型

comment/models.py

...
# django-mptt
from mptt.models import MPTTModel, TreeForeignKey

# 替換 models.Model 為 MPTTModel
class Comment(MPTTModel):
    ...
    
    # 新增,mptt樹形結(jié)構(gòu)
    parent = TreeForeignKey(
        "self",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="children"
    )

    # 新增,記錄二級(jí)評(píng)論回復(fù)給誰(shuí), str
    reply_to = models.ForeignKey(
        User,
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="replyers"
    )
    
    # 替換 Meta 為 MPTTMeta
    # class Meta:
    #     ordering = ("created",)
    class MPTTMeta:
        order_insertion_by = ["created"]

    ...

先引入MPTT相關(guān)模塊,然后改動(dòng)下列幾個(gè)位置:

模型不再繼承內(nèi)置的models.Model類,替換為MPTTModel,因此你的模型自動(dòng)擁有了幾個(gè)用于樹形算法的新字段。(有興趣的讀者,可以在遷移好數(shù)據(jù)之后在SQLiteStudio中查看)

parent字段是必須定義的,用于存儲(chǔ)數(shù)據(jù)之間的關(guān)系,不要去修改它。

reply_to外鍵用于存儲(chǔ)被評(píng)論人。

class Meta替換為class MPTTMeta,參數(shù)也有小的變化,這是模塊的默認(rèn)定義,實(shí)際功能是相同的。

這些改動(dòng)大部分都是django-mptt文檔的默認(rèn)設(shè)置。需要說(shuō)明的是這個(gè)reply_to。

先思考一下,多級(jí)評(píng)論是否允許無(wú)限級(jí)數(shù)?無(wú)限級(jí)數(shù)聽起來(lái)很美好,但是嵌套的層級(jí)如果過(guò)多,反而會(huì)導(dǎo)致結(jié)構(gòu)混亂,并且難以排版。所以這里就限制評(píng)論最多只能兩級(jí),超過(guò)兩級(jí)的評(píng)論一律重置為兩級(jí),然后再將實(shí)際的被評(píng)論人存儲(chǔ)在reply_to字段中。

舉例說(shuō)明:一級(jí)評(píng)論人為 a,二級(jí)評(píng)論人為 b(parent 為 a),三級(jí)評(píng)論人為 c(parent 為 b)。因?yàn)槲覀儾辉试S評(píng)論超過(guò)兩級(jí),因此將 c 的 parent 重置為 a,reply_to 記錄為 b,這樣就能正確追溯真正的被評(píng)論者了。

模型修改完了,添加了很多非空的字段進(jìn)去,因此最好先清空所有的評(píng)論數(shù)據(jù),再進(jìn)行數(shù)據(jù)遷移。

遷移時(shí)出現(xiàn)下面的提示也不要慌,一律選第 1 項(xiàng)、填入數(shù)據(jù) 0 就可以了:

(env) > python manage.py makemigrations

You are trying to add a non-nullable field "level" to comment without a default; we can"t do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py

Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type "exit" to exit this prompt
>>> 0
要還不行,就把數(shù)據(jù)庫(kù)文件刪了重新遷移吧。開發(fā)階段用點(diǎn)笨辦法也沒關(guān)系。

數(shù)據(jù)遷移還是老規(guī)矩:

(env) > python manage.py makemigrations
(env) > python manage.py migrate

這就完成了。

視圖

前面章節(jié)已經(jīng)寫過(guò)一個(gè)視圖post_comment用于處理評(píng)論了,我們將復(fù)用它,以求精簡(jiǎn)代碼。

改動(dòng)較大,代碼全貼出來(lái),請(qǐng)對(duì)照改動(dòng):

comment/views.py

...
# 記得引入 Comment !
from .models import Comment

...
@login_required(login_url="/userprofile/login/")
# 新增參數(shù) parent_comment_id
def post_comment(request, article_id, parent_comment_id=None):
    article = get_object_or_404(ArticlePost, id=article_id)

    # 處理 POST 請(qǐng)求
    if request.method == "POST":
        comment_form = CommentForm(request.POST)
        if comment_form.is_valid():
            new_comment = comment_form.save(commit=False)
            new_comment.article = article
            new_comment.user = request.user

            # 二級(jí)回復(fù)
            if parent_comment_id:
                parent_comment = Comment.objects.get(id=parent_comment_id)
                # 若回復(fù)層級(jí)超過(guò)二級(jí),則轉(zhuǎn)換為二級(jí)
                new_comment.parent_id = parent_comment.get_root().id
                # 被回復(fù)人
                new_comment.reply_to = parent_comment.user
                new_comment.save()
                return HttpResponse("200 OK")

            new_comment.save()
            return redirect(article)
        else:
            return HttpResponse("表單內(nèi)容有誤,請(qǐng)重新填寫。")
    # 處理 GET 請(qǐng)求
    elif request.method == "GET":
        comment_form = CommentForm()
        context = {
            "comment_form": comment_form,
            "article_id": article_id,
            "parent_comment_id": parent_comment_id
        }
        return render(request, "comment/reply.html", context)
    # 處理其他請(qǐng)求
    else:
        return HttpResponse("僅接受GET/POST請(qǐng)求。")

主要變化有3個(gè)地方:

視圖的參數(shù)新增parent_comment_id=None。此參數(shù)代表父評(píng)論id值,若為None則表示評(píng)論為一級(jí)評(píng)論,若有具體值則為多級(jí)評(píng)論。

如果視圖處理的是多級(jí)評(píng)論,則用MPTTget_root()方法將其父級(jí)重置為樹形結(jié)構(gòu)最底部的一級(jí)評(píng)論,然后在reply_to中保存實(shí)際的被回復(fù)人并保存。視圖最終返回的是HttpResponse字符串,后面會(huì)用到。

新增處理GET請(qǐng)求的邏輯,用于給二級(jí)回復(fù)提供空白的表單。后面會(huì)用到。

很好,現(xiàn)在視圖中有一個(gè)parent_comment_id參數(shù)用于區(qū)分多級(jí)評(píng)論,因此就要求有的url傳入此參數(shù),有的不傳入,像下面這樣:

comment/urls.py

...
urlpatterns = [
    # 已有代碼,處理一級(jí)回復(fù)
    path("post-comment/", views.post_comment, name="post_comment"),
    # 新增代碼,處理二級(jí)回復(fù)
    path("post-comment//", views.post_comment, name="comment_reply")
]

兩個(gè)path都使用了同一個(gè)視圖函數(shù),但是傳入的參數(shù)卻不一樣多,仔細(xì)看。第一個(gè)path沒有parent_comment_id參數(shù),因此視圖就使用了缺省值None,達(dá)到了區(qū)分評(píng)論層級(jí)的目的。

前端渲染

在前端的邏輯上,我們的理想很豐滿:

二級(jí)回復(fù)同樣要使用富文本編輯器

回復(fù)時(shí)不能離開當(dāng)前頁(yè)面

多個(gè)ckeditor加載時(shí),不能有性能問(wèn)題

然而理想越豐滿,代碼寫得就越痛苦。

首先就是detail.html的代碼要大改,主要集中在顯示評(píng)論部分以及相關(guān)的JavaScript。

需要改動(dòng)的地方先全部貼出來(lái):

templates/article/detail.html

...



{% load mptt_tags %}

共有{{ comments.count }}條評(píng)論

{% recursetree comments %} {% with comment=node %}

{{ comment.user }} {% if comment.reply_to %} {{ comment.reply_to }} {% endif %}

{{ comment.body|safe }}
{{ comment.created|date:"Y-m-d H:i" }}
{% if not comment.is_leaf_node %}
{{ children }}
{% endif %}
{% endwith %} {% endrecursetree %}
... {% block script %} ... {% endblock script %}

這么大段肯定把你看暈了,不要急,讓我們拆開來(lái)講解。

遍歷樹

第一個(gè)問(wèn)題,如何遍歷樹形結(jié)構(gòu)?

django-mptt提供了一個(gè)快捷方式:

{% load mptt_tags %}
    {% recursetree objs %}
  • {{ node.your_field }} {% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}

內(nèi)部的實(shí)現(xiàn)你不用去管,當(dāng)成一個(gè)黑盒子去用就好了。objs是需要遍歷的數(shù)據(jù)集node是其中的單個(gè)數(shù)據(jù)。有兩個(gè)地方要注意:

{% load mptt_tags %}不要忘記寫

node這個(gè)變量名太寬泛,用{% with comment=node %}給它起了個(gè)別名

Modal

ModalBootstrap內(nèi)置的彈窗。本文相關(guān)代碼如下:





它幾乎就是從Bootstrap官方文檔抄下來(lái)的(所以讀者要多瀏覽官網(wǎng)?。?。有點(diǎn)不同的是本文沒有用原生的按鈕,而是用JavaScript加載的Modal;還有就是增加了幾個(gè)容器的id屬性,方便后面的JavaScript查詢。

和之前章節(jié)用的Layer.js相比,Bootstrap的彈窗更笨重些,也更精致些,很適合在這里使用。

加載Modal

最難理解的可能就是這段加載Modal的JavaScript代碼了:

// 加載 modal
function load_modal(article_id, comment_id) {
    let modal_body = "#modal_body_" + comment_id;
    let modal_id = "#comment_" + comment_id;

    // 加載編輯器
    if ($(modal_body).children().length === 0) {
        let content = "";
        $(modal_body).append(content);
    };

    $(modal_id).modal("show");
}

實(shí)際上核心邏輯只有3步:

點(diǎn)擊回復(fù)按鈕時(shí)喚醒了load_modal()函數(shù),并將文章id、父級(jí)評(píng)論id傳遞進(jìn)去

$(modal_body).append(content)找到對(duì)應(yīng)Modal的容器,并將一個(gè)iframe容器動(dòng)態(tài)添加進(jìn)去

$(modal_id).modal("show")找到對(duì)應(yīng)的Modal,并將其喚醒

為什么iframe需要動(dòng)態(tài)加載?這是為了避免潛在的性能問(wèn)題。你確實(shí)可以在頁(yè)面初始加載時(shí)把所有iframe都渲染好,但是這需要花費(fèi)額外的時(shí)間,并且絕大部分的Modal用戶根本不會(huì)用到,很不劃算。

if語(yǔ)句的作用是判斷Modal中如果已經(jīng)加載過(guò),就不再重復(fù)加載了。

最后,什么是iframe?這是HTML5中的新特性,可以理解成當(dāng)前網(wǎng)頁(yè)中嵌套的另一個(gè)獨(dú)立的網(wǎng)頁(yè)。既然是獨(dú)立的網(wǎng)頁(yè),那自然也會(huì)獨(dú)立的向后臺(tái)請(qǐng)求數(shù)據(jù)。仔細(xì)看src中請(qǐng)求的位置,正是前面我們?cè)?b>urls.py中寫好的第二個(gè)path。即對(duì)應(yīng)了post_comment視圖中的GET邏輯:

comment/views.py

def post_comment(request, article_id, parent_comment_id=None):
    ...
    # 處理 GET 請(qǐng)求
    elif request.method == "GET":
        ...
        return render(request, "comment/reply.html", context)
    ...

視圖返回的comment/reply.html模板還沒有寫,接下來(lái)就把它寫好。

老實(shí)說(shuō)用iframe來(lái)加載ckeditor彈窗并不是很“優(yōu)雅”。單頁(yè)面上多個(gè)ckeditor的動(dòng)態(tài)加載、取值、傳參,博主沒能嘗試成功。有興趣的讀者可以和我交流。
Ajax提交表單

templates中新建comment目錄,并新建reply.html,寫入代碼:

templates/comment/reply.html


{% load staticfiles %}




    
    



    
{% csrf_token %}
{{ comment_form.media }} {{ comment_form.body }}

這個(gè)模板的作用是提供一個(gè)ckeditor的編輯器,所以沒有繼承base.html。讓我們拆開來(lái)講。

Ajax是什么

Ajax技術(shù)來(lái)提交表單,與傳統(tǒng)方法非常不同。

傳統(tǒng)方法提交表單時(shí)向后端提交一個(gè)請(qǐng)求。后端處理請(qǐng)求后會(huì)返回一個(gè)全新的網(wǎng)頁(yè)。這種做法浪費(fèi)了很多帶寬,因?yàn)榍昂髢蓚€(gè)頁(yè)面中大部分內(nèi)容往往都是相同的。與此不同,AJAX技術(shù)可以僅向服務(wù)器發(fā)送并取回必須的數(shù)據(jù),并在客戶端采用JavaScript處理來(lái)自服務(wù)器的回應(yīng)。因?yàn)樵诜?wù)器和瀏覽器之間交換的數(shù)據(jù)大量減少,服務(wù)器回應(yīng)更快了。

雖然本教程只用到Ajax的一點(diǎn)皮毛,但是Ajax的應(yīng)用非常廣泛,建議讀者多了解相關(guān)知識(shí)。

這里會(huì)用到Ajax,倒不是因?yàn)槠湫矢?,而是因?yàn)锳jax可以在表單提交成功后得到反饋,以便刷新頁(yè)面。

核心代碼如下:

function confirm_submit(article_id, comment_id){
    // 從 ckeditor 中取值
    let content = CKEDITOR.instances["id_body"].getData();
    // 調(diào)用 ajax 與后端交換數(shù)據(jù)
    $.ajax({
        url: "/comment/post-comment/" + article_id + "/" + comment_id,
        type: "POST",
        data: {body: content},
        // 成功回調(diào)
        success: function(e){
            if(e === "200 OK"){
                parent.location.reload();
            }
        }
    })
}

CKEDITOR是編輯器提供的全局變量,這里用CKEDITOR.instances["id_body"].getData()取得當(dāng)前編輯器中用戶輸入的內(nèi)容。

接下來(lái)調(diào)用了Jquery的ajax方法與視圖進(jìn)行數(shù)據(jù)交換。ajax中定義了視圖的url、請(qǐng)求的方法、提交的數(shù)據(jù)。

success是ajax的回調(diào)函數(shù)。當(dāng)?shù)玫揭晥D的相應(yīng)后執(zhí)行內(nèi)部的函數(shù)。

前面寫視圖的時(shí)候,二級(jí)評(píng)論提交成功后會(huì)返回200 OK,回調(diào)函數(shù)接收到這個(gè)信號(hào)后,就會(huì)調(diào)用reload()方法,刷新當(dāng)前的父頁(yè)面(即文章所在的頁(yè)面),實(shí)現(xiàn)了數(shù)據(jù)的更新。

csrf問(wèn)題

代碼中有這么一行:

沒有這一行,后端會(huì)返回403 Forbidden錯(cuò)誤,并且表單提交失敗。

還記得之前提交傳統(tǒng)表單時(shí)的{% csrf_token %}嗎?Django為了防止跨域攻擊,要求表單必須提供這個(gè)token,驗(yàn)證提交者的身份。

問(wèn)題是在Ajax中怎么解決這個(gè)問(wèn)題呢?一種方法就是在頁(yè)面中插入這個(gè)csrf.js模塊。

在static目錄中將csrf.js文件粘貼進(jìn)去,并在頁(yè)面中引用,就可以解決此問(wèn)題了。

csrf.js文件可以在我的GitHub倉(cāng)庫(kù)下載。
測(cè)試!

進(jìn)入文章頁(yè)面,評(píng)論的邊上多出一個(gè)按鈕,可以對(duì)評(píng)論者進(jìn)行評(píng)論了:

點(diǎn)擊回復(fù)按鈕,彈出帶有富文本編輯器的彈窗:

點(diǎn)擊發(fā)送按鈕,頁(yè)面會(huì)自動(dòng)刷新,并且二級(jí)評(píng)論也出現(xiàn)了:

還可以繼續(xù)對(duì)二級(jí)評(píng)論者評(píng)論,不過(guò)更高級(jí)的評(píng)論會(huì)被強(qiáng)制轉(zhuǎn)換為二級(jí)評(píng)論:

功能正常運(yùn)行了。

有興趣的讀者可以打開SQLiteStudio,研究一下comment數(shù)據(jù)表的結(jié)構(gòu)。

總結(jié)

認(rèn)真看完本章并實(shí)現(xiàn)了多級(jí)評(píng)論的同學(xué),可以給自己點(diǎn)掌聲了。本章應(yīng)該是教程到目前為止知識(shí)點(diǎn)最多、最雜的章節(jié),涵蓋了MTV、Jquery、Ajax、iframe、modal等多種前后端技術(shù)。

沒成功實(shí)現(xiàn)也不要急躁,web開發(fā)嘛,走點(diǎn)彎路很正常的。多觀察Django和控制臺(tái)的報(bào)錯(cuò)信息,找到問(wèn)題并解決它。


有疑問(wèn)請(qǐng)?jiān)诙刨惖膫€(gè)人網(wǎng)站留言,我會(huì)盡快回復(fù)。

或Email私信我:dusaiphoto@foxmail.com

項(xiàng)目完整代碼:Django_blog_tutorial

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/43717.html

相關(guān)文章

  • Django搭建個(gè)人博客:結(jié)束和開始

    摘要:教程看到這里,你已經(jīng)學(xué)會(huì)如下內(nèi)容搭建開發(fā)環(huán)境博文管理用戶管理發(fā)表評(píng)論若干小功能搭建簡(jiǎn)單的小博客,以上的功能夠用了。教程為了起步平緩,沒有展開這方面的內(nèi)容。陌生人,祝你學(xué)業(yè)進(jìn)步事業(yè)有成歡迎常到杜賽的個(gè)人網(wǎng)站做客 教程看到這里,你已經(jīng)學(xué)會(huì)如下內(nèi)容: 搭建開發(fā)環(huán)境 博文管理 用戶管理 發(fā)表評(píng)論 若干小功能 搭建簡(jiǎn)單的小博客,以上的功能夠用了。 相信你的志向不止于此。畢竟程序員面試個(gè)個(gè)造火...

    zqhxuyuan 評(píng)論0 收藏0
  • Django搭建個(gè)人博客:在博文中發(fā)表評(píng)論

    摘要:確認(rèn)創(chuàng)建成功后,記得在中注冊(cè)因?yàn)槲覀兿腼@示發(fā)表評(píng)論的時(shí)間,修改時(shí)區(qū)設(shè)置為上海的時(shí)區(qū)。處理錯(cuò)誤請(qǐng)求發(fā)表評(píng)論僅接受請(qǐng)求。返回到一個(gè)適當(dāng)?shù)闹屑从脩舭l(fā)送評(píng)論后,重新定向到文章詳情頁(yè)面。總結(jié)本章實(shí)現(xiàn)了發(fā)表評(píng)論展示評(píng)論的功能。 在沒有互聯(lián)網(wǎng)的年代,我們用日記來(lái)記錄每天的心得體會(huì)。小的時(shí)候我有一個(gè)帶鎖的日記本,生怕被別人看見里面寫了啥,鑰匙藏得那叫一個(gè)絕。 現(xiàn)在時(shí)代變了,網(wǎng)絡(luò)版的日記本:博客,卻巴不...

    Jinkey 評(píng)論0 收藏0
  • Django搭建個(gè)人博客django-notifications實(shí)現(xiàn)消息通知

    摘要:接下來(lái)你就可以在項(xiàng)目的任何地方發(fā)送通知了像這樣其中的參數(shù)釋義發(fā)送通知的對(duì)象接收通知的對(duì)象動(dòng)詞短語(yǔ)鏈接到動(dòng)作的對(duì)象可選執(zhí)行通知的對(duì)象可選有點(diǎn)繞,舉個(gè)栗子杜賽在搭建個(gè)人博客中對(duì)你發(fā)表了評(píng)論。有疑問(wèn)請(qǐng)?jiān)诙刨惖膫€(gè)人網(wǎng)站留言,我會(huì)盡快回復(fù)。 憑借你勤奮的寫作,拜讀你文章的用戶越來(lái)越多,他們的評(píng)論也分散在眾多的文章之中。作為博主,讀者的留言肯定是要都看的;而讀者給你留言,自然也希望得到回復(fù)。 怎么...

    Zoom 評(píng)論0 收藏0
  • Django搭建個(gè)人博客:錨點(diǎn)定位

    摘要:在父頁(yè)面中文章詳情模板添加需要執(zhí)行錨點(diǎn)拼接的函數(shù)新增函數(shù),處理二級(jí)回復(fù)去除尾部符號(hào)刷新并定位到錨點(diǎn)函數(shù)中運(yùn)用了的三元運(yùn)算符,翻譯成人話就是如果成立則返回,如果不成立就返回。 老讀者注意:上一章消息通知有個(gè)bug,即發(fā)給管理員的notify必須移動(dòng)到new_comment.save()的后面,否則會(huì)導(dǎo)致action_object存儲(chǔ)為NULL,并且導(dǎo)致本章的html拼接錨點(diǎn)失效。原文已...

    xi4oh4o 評(píng)論0 收藏0
  • Django搭建個(gè)人博客:使django-ckeditor富文本編輯器

    摘要:后面兩個(gè)編輯器自帶,不用單獨(dú)下載,添上就可以了添加相關(guān)插件這樣就完成了代碼高亮效果不錯(cuò)在前臺(tái)使用為了讓用戶在前臺(tái)也能使用富文本編輯器,還得對(duì)代碼稍加改動(dòng)。對(duì)于有些不喜歡的人來(lái)說(shuō),甚至可以連博文都使用提供的富文本編輯器。 前面我們已經(jīng)實(shí)現(xiàn)了用Markdown語(yǔ)法寫文章了。但是文章的評(píng)論用Markdown就不太合適了,你不能強(qiáng)求用戶也花時(shí)間去熟悉語(yǔ)法啊。另外評(píng)論中通常還有表情、帶顏色的字體...

    beanlam 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<