Odoo pagination

Using kkpager plugin for Odoo Ajax pagination method

controllers/controllers.py
from odoo import http
from odoo.http import request
from odoo.addons.portal.controllers.portal import pager


class MyWeb(http.Controller):
    _news_per_page = 15
    
    @http.route(['/news/', '/news/?page/<int:page>'], type='http', auth="public", methods=['GET', 'POST'], website=True)
    def news_list(self, page=1, **kw):
        news = request.env['my.news'].sudo()
        domain = []
        page_count = news.search_count(domain)
        # make pagination
        pagination = pager(
            url="/news",
            total=page_count,
            page=page,
            step=self._news_per_page,
        )
        records = news.sudo().search(
            domain, order="create_date desc", limit=self._news_per_page, offset=pagination['offset']
        )
    
        return request.render("my_website_module.news_list", {'records': records})

    @http.route('/news/<int:news_id>', type='http', auth="public", methods=['GET', 'POST'], website=True)
    def news_detail(self, news_id=None, **kw):
        record = request.env['my.news'].sudo().browse(int(news_id)).exists()

        return request.render("my_website_module.news_detail", {'record': record})
        
    @http.route('/session_info.json', type='json', auth="public", methods=['POST'], website=True)
    def session_info(self, **kw):
        news_records = request.env['my.news'].sudo().search_count([])

        news_pages, a = divmod(news_records, self._news_per_page)
        if a:
            news_pages += 1

        return dict(
            news_pages=news_pages,
            news_records=news_records,
        )
my_website_module/templates/news_list_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>

        <template id="news_list" name="News list">
            <t t-call="website.layout">
                <div class="container">
                    <div class="divider clearfix">
                        <div class="divFirst">
                            <div class="innerInfo" style="padding-top:0;">
                                <ul class="clearfix" id="">
                                    <li t-if="not records">No news records&amp;nbsp;...</li>
                                    <li t-foreach="records" t-as="r">
                                        <div class="clearfix">
                                            <span class="bfR" t-field="r.create_date"
                                                  t-options='{"widget": "date"}'/>
                                            <a t-attf-href="/news/#{r.id}" class="mainA">
                                                <t t-esc="r.name"/>
                                            </a>
                                        </div>
                                    </li>
                                </ul>
                                
                                <div id="pager"/>

                            </div>
                        </div>
                    </div>
                </div>
                <script type="text/javascript" src="/my_website_module/static/src/js/news_pager.js"/>
            </t>
        </template>

    </data>
</odoo>
my_website_module/templates/news_detail_templates.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>

        <template id="news_detail" name="News detail">
            <t t-call="website.layout">
                <div class="container">
                    <div class="divider clearfix">
                        <div class="divFirst">
                            <div class="innerNewsInfo">
                                <div class="newTitle">
                                    <h2 t-esc="record.name"/>
                                    <div id="divCome" t-field="record.create_date"/>
                                </div>
                                <div id="divContent" class="newsContent" t-raw="record.page"></div>          
                            </div>
                        </div>
                    </div>
                </div>
            </t>
        </template>

    </data>
</odoo>
static/src/js/news_pager.js
odoo.define('my_website_module.news_pager', function (require) {
    "use strict";
    var ajax = require('web.ajax');

    function getParameter(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]);
        return null;
    }

    $(function () {
        ajax.jsonRpc('/session_info.json', 'call', {}).then(function (data) {
            var totalPage = data.news_pages;
            var totalRecords = data.news_records;
            var pageNo = getParameter('page');
            if (!pageNo) {
                pageNo = 1;
            }
            kkpager.generPageHtml({
                pno: pageNo,
                total: totalPage,
                totalRecords: totalRecords,
                hrefFormer: '/news/',
                hrefLatter: '',
                getLink: function (n) {
                    return this.hrefFormer + this.hrefLatter + "?page=" + n;
                }
            })
        });

    });

});
static/src/js/kkpager.js
var kkpager = {
    pagerid: 'pager', //divID
    mode: 'link', //模式(link 或者 click)
    pno: 1, //当前页码
    total: 1, //总页码
    totalRecords: 0, //总数据条数
    isShowFirstPageBtn: true, //是否显示首页按钮
    isShowLastPageBtn: true, //是否显示尾页按钮
    isShowPrePageBtn: true, //是否显示上一页按钮
    isShowNextPageBtn: true, //是否显示下一页按钮
    isShowTotalPage: true, //是否显示总页数
    isShowCurrPage: true,//是否显示当前页
    isShowTotalRecords: false, //是否显示总记录数
    isGoPage: true, //是否显示页码跳转输入框
    isWrapedPageBtns: true, //是否用span包裹住页码按钮
    isWrapedInfoTextAndGoPageBtn: true, //是否用span包裹住分页信息和跳转按钮
    hrefFormer: '', //链接前部
    hrefLatter: '', //链接尾部
    gopageWrapId: 'pager_gopage_wrap',
    gopageButtonId: 'pager_btn_go',
    gopageTextboxId: 'pager_btn_go_input',
    lang: {
        firstPageText: '首页',
        firstPageTipText: '首页',
        lastPageText: '尾页',
        lastPageTipText: '尾页',
        prePageText: '上一页',
        prePageTipText: '上一页',
        nextPageText: '下一页',
        nextPageTipText: '下一页',
        totalPageBeforeText: '共',
        totalPageAfterText: '页',
        currPageBeforeText: '当前第',
        currPageAfterText: '页',
        totalInfoSplitStr: '/',
        totalRecordsBeforeText: '共',
        totalRecordsAfterText: '条数据',
        gopageBeforeText: '&nbsp;转到',
        gopageButtonOkText: '确定',
        gopageAfterText: '页',
        buttonTipBeforeText: '第',
        buttonTipAfterText: '页'
    },
    //链接算法(当处于link模式),参数n为页码
    getLink: function (n) {
        //这里的算法适用于比如:
        //hrefFormer=http://www.xx.com/news/20131212
        //hrefLatter=.html
        //那么首页(第1页)就是http://www.xx.com/news/20131212.html
        //第2页就是http://www.xx.com/news/20131212_2.html
        //第n页就是http://www.xx.com/news/20131212_n.html
        if (n == 1) {
            return this.hrefFormer + this.hrefLatter;
        }
        return this.hrefFormer + '_' + n + this.hrefLatter;
    },
    //页码单击事件处理函数(当处于mode模式),参数n为页码
    click: function (n) {
        //这里自己实现
        //这里可以用this或者kkpager访问kkpager对象
        return false;
    },
    //获取href的值(当处于mode模式),参数n为页码
    getHref: function (n) {
        //默认返回'#'
        return '#';
    },
    //跳转框得到输入焦点时
    focus_gopage: function () {
        var btnGo = $('#' + this.gopageButtonId);
        $('#' + this.gopageTextboxId).attr('hideFocus', true);
        btnGo.show();
        btnGo.css('left', '25px');
        $('#' + this.gopageTextboxId).addClass('focus');
        btnGo.animate({left: '+=30'}, 50);
    },
    //跳转框失去输入焦点时
    blur_gopage: function () {
        var _this = this;
        setTimeout(function () {
            var btnGo = $('#' + _this.gopageButtonId);
            btnGo.animate({
                left: '-=25'
            }, 100, function () {
                btnGo.hide();
                $('#' + _this.gopageTextboxId).removeClass('focus');
            });
        }, 400);
    },
    //跳转输入框按键操作
    keypress_gopage: function () {
        var event = arguments[0] || window.event;
        var code = event.keyCode || event.charCode;
        //delete key
        if (code == 8) return true;
        //enter key
        if (code == 13) {
            kkpager.gopage();
            return false;
        }
        //copy and paste
        if (event.ctrlKey && (code == 99 || code == 118)) return true;
        //only number key
        if (code < 48 || code > 57) return false;
        return true;
    },
    //跳转框页面跳转
    gopage: function () {
        var str_page = $('#' + this.gopageTextboxId).val();
        if (isNaN(str_page)) {
            $('#' + this.gopageTextboxId).val(this.next);
            return;
        }
        var n = parseInt(str_page);
        if (n < 1) n = 1;
        if (n > this.total) n = this.total;
        if (this.mode == 'click') {
            this._clickHandler(n);
        } else {
            window.location = this.getLink(n);
        }
    },
    //不刷新页面直接手动调用选中某一页码
    selectPage: function (n) {
        this._config['pno'] = n;
        this.generPageHtml(this._config, true);
    },
    //生成控件代码
    generPageHtml: function (config, enforceInit) {
        if (enforceInit || !this.inited) {
            this.init(config);
        }

        var str_first = '', str_prv = '', str_next = '', str_last = '';
        if (this.isShowFirstPageBtn) {
            if (this.hasPrv) {
                str_first = '<a ' + this._getHandlerStr(1) + ' title="'
                    + (this.lang.firstPageTipText || this.lang.firstPageText) + '">' + this.lang.firstPageText + '</a>';
            } else {
                str_first = '<span class="disabled">' + this.lang.firstPageText + '</span>';
            }
        }
        if (this.isShowPrePageBtn) {
            if (this.hasPrv) {
                str_prv = '<a ' + this._getHandlerStr(this.prv) + ' title="'
                    + (this.lang.prePageTipText || this.lang.prePageText) + '">' + this.lang.prePageText + '</a>';
            } else {
                str_prv = '<span class="disabled">' + this.lang.prePageText + '</span>';
            }
        }
        if (this.isShowNextPageBtn) {
            if (this.hasNext) {
                str_next = '<a ' + this._getHandlerStr(this.next) + ' title="'
                    + (this.lang.nextPageTipText || this.lang.nextPageText) + '">' + this.lang.nextPageText + '</a>';
            } else {
                str_next = '<span class="disabled">' + this.lang.nextPageText + '</span>';
            }
        }
        if (this.isShowLastPageBtn) {
            if (this.hasNext) {
                str_last = '<a ' + this._getHandlerStr(this.total) + ' title="'
                    + (this.lang.lastPageTipText || this.lang.lastPageText) + '">' + this.lang.lastPageText + '</a>';
            } else {
                str_last = '<span class="disabled">' + this.lang.lastPageText + '</span>';
            }
        }
        var str = '';
        var dot = '<span class="spanDot">...</span>';
        var total_info = '<span class="totalText">';
        var total_info_splitstr = '<span class="totalInfoSplitStr">' + this.lang.totalInfoSplitStr + '</span>';
        if (this.isShowCurrPage) {
            total_info += this.lang.currPageBeforeText + '<span class="currPageNum">' + this.pno + '</span>' + this.lang.currPageAfterText;
            if (this.isShowTotalPage) {
                total_info += total_info_splitstr;
                total_info += this.lang.totalPageBeforeText + '<span class="totalPageNum">' + this.total + '</span>' + this.lang.totalPageAfterText;
            } else if (this.isShowTotalRecords) {
                total_info += total_info_splitstr;
                total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
            }
        } else if (this.isShowTotalPage) {
            total_info += this.lang.totalPageBeforeText + '<span class="totalPageNum">' + this.total + '</span>' + this.lang.totalPageAfterText;
            if (this.isShowTotalRecords) {
                total_info += total_info_splitstr;
                total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
            }
        } else if (this.isShowTotalRecords) {
            total_info += this.lang.totalRecordsBeforeText + '<span class="totalRecordNum">' + this.totalRecords + '</span>' + this.lang.totalRecordsAfterText;
        }
        total_info += '</span>';

        var gopage_info = '';
        if (this.isGoPage) {
            gopage_info = '<span class="goPageBox">' + this.lang.gopageBeforeText + '<span id="' + this.gopageWrapId + '">' +
                '<input type="button" id="' + this.gopageButtonId + '" onclick="kkpager.gopage()" value="'
                + this.lang.gopageButtonOkText + '" />' +
                '<input type="text" id="' + this.gopageTextboxId + '" onfocus="kkpager.focus_gopage()"  onkeypress="return kkpager.keypress_gopage(event);"   onblur="kkpager.blur_gopage()" value="' + this.next + '" /></span>' + this.lang.gopageAfterText + '</span>';
        }

        //分页处理
        if (this.total <= 8) {
            for (var i = 1; i <= this.total; i++) {
                if (this.pno == i) {
                    str += '<span class="curr">' + i + '</span>';
                } else {
                    str += '<a ' + this._getHandlerStr(i) + ' title="'
                        + this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
                }
            }
        } else {
            if (this.pno <= 5) {
                for (var i = 1; i <= 5; i++) {
                    if (this.pno == i) {
                        str += '<span class="curr">' + i + '</span>';
                    } else {
                        str += '<a ' + this._getHandlerStr(i) + ' title="' +
                            this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
                    }
                }
                str += dot;
            } else {
                // str += '<a ' + this._getHandlerStr(1) + ' title="'
                //     + this.lang.buttonTipBeforeText + '1' + this.lang.buttonTipAfterText + '">1</a>';
                // str += '<a ' + this._getHandlerStr(2) + ' title="'
                //     + this.lang.buttonTipBeforeText + '2' + this.lang.buttonTipAfterText + '">2</a>';
                str += dot;

                var begin = this.pno - 2;
                var end = this.pno + 2;
                if (end > this.total) {
                    end = this.total;
                    begin = end - 4;
                    if (this.pno - begin < 2) {
                        begin = begin - 1;
                    }
                } else if (end + 1 == this.total) {
                    end = this.total;
                }
                for (var i = begin; i <= end; i++) {
                    if (this.pno == i) {
                        str += '<span class="curr">' + i + '</span>';
                    } else {
                        str += '<a ' + this._getHandlerStr(i) + ' title="'
                            + this.lang.buttonTipBeforeText + i + this.lang.buttonTipAfterText + '">' + i + '</a>';
                    }
                }
                if (end != this.total) {
                    str += dot;
                }
            }
        }
        var pagerHtml = '<div>';
        if (this.isWrapedPageBtns) {
            pagerHtml += '<span class="pageBtnWrap">' + str_first + str_prv + str + str_next + str_last + '</span>'
        } else {
            pagerHtml += str_first + str_prv + str + str_next + str_last;
        }
        if (this.isWrapedInfoTextAndGoPageBtn) {
            pagerHtml += '<span class="infoTextAndGoPageBtnWrap hidden-xs">' + total_info + gopage_info + '</span>';
        } else {
            pagerHtml += total_info + gopage_info;
        }
        pagerHtml += '</div><div style="clear:both;"></div>';
        $("#" + this.pagerid).html(pagerHtml);
    },
    //分页按钮控件初始化
    init: function (config) {
        this.pno = isNaN(config.pno) ? 1 : parseInt(config.pno);
        this.total = isNaN(config.total) ? 1 : parseInt(config.total);
        this.totalRecords = isNaN(config.totalRecords) ? 0 : parseInt(config.totalRecords);
        if (config.pagerid) {
            this.pagerid = config.pagerid;
        }
        if (config.mode) {
            this.mode = config.mode;
        }
        if (config.gopageWrapId) {
            this.gopageWrapId = config.gopageWrapId;
        }
        if (config.gopageButtonId) {
            this.gopageButtonId = config.gopageButtonId;
        }
        if (config.gopageTextboxId) {
            this.gopageTextboxId = config.gopageTextboxId;
        }
        if (config.isShowFirstPageBtn != undefined) {
            this.isShowFirstPageBtn = config.isShowFirstPageBtn;
        }
        if (config.isShowLastPageBtn != undefined) {
            this.isShowLastPageBtn = config.isShowLastPageBtn;
        }
        if (config.isShowPrePageBtn != undefined) {
            this.isShowPrePageBtn = config.isShowPrePageBtn;
        }
        if (config.isShowNextPageBtn != undefined) {
            this.isShowNextPageBtn = config.isShowNextPageBtn;
        }
        if (config.isShowTotalPage != undefined) {
            this.isShowTotalPage = config.isShowTotalPage;
        }
        if (config.isShowCurrPage != undefined) {
            this.isShowCurrPage = config.isShowCurrPage;
        }
        if (config.isShowTotalRecords != undefined) {
            this.isShowTotalRecords = config.isShowTotalRecords;
        }
        if (config.isWrapedPageBtns) {
            this.isWrapedPageBtns = config.isWrapedPageBtns;
        }
        if (config.isWrapedInfoTextAndGoPageBtn) {
            this.isWrapedInfoTextAndGoPageBtn = config.isWrapedInfoTextAndGoPageBtn;
        }
        if (config.isGoPage != undefined) {
            this.isGoPage = config.isGoPage;
        }
        if (config.lang) {
            for (var key in config.lang) {
                this.lang[key] = config.lang[key];
            }
        }
        this.hrefFormer = config.hrefFormer || '';
        this.hrefLatter = config.hrefLatter || '';
        if (config.getLink && typeof(config.getLink) == 'function') {
            this.getLink = config.getLink;
        }
        if (config.click && typeof(config.click) == 'function') {
            this.click = config.click;
        }
        if (config.getHref && typeof(config.getHref) == 'function') {
            this.getHref = config.getHref;
        }
        if (!this._config) {
            this._config = config;
        }
        //validate
        if (this.pno < 1) this.pno = 1;
        this.total = (this.total <= 1) ? 1 : this.total;
        if (this.pno > this.total) this.pno = this.total;
        this.prv = (this.pno <= 2) ? 1 : (this.pno - 1);
        this.next = (this.pno >= this.total - 1) ? this.total : (this.pno + 1);
        this.hasPrv = (this.pno > 1);
        this.hasNext = (this.pno < this.total);

        this.inited = true;
    },
    _getHandlerStr: function (n) {
        if (this.mode == 'click') {
            return 'href="' + this.getHref(n) + '" onclick="return kkpager._clickHandler(' + n + ')"';
        }
        //link模式,也是默认的
        return 'href="' + this.getLink(n) + '"';
    },
    _clickHandler: function (n) {
        var res = false;
        if (this.click && typeof this.click == 'function') {
            res = this.click.call(this, n) || false;
        }
        return res;
    }
};
static/src/css/kkpager.css
#pager {
    clear: both;
    color: #999;
    font-size: 13px;
    padding-bottom: 16px;
}

#pager a {
    float: left;
    border: 1px solid #ccc;
    display: inline;
    padding: 3px 10px 3px 10px;
    margin-right: 5px;
    border-radius: 3px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    cursor: pointer;
    background: #fff;
    text-decoration: none;
    color: #999;
}

#pager span.disabled {
    float: left;
    display: inline;
    padding: 3px 10px 3px 10px;
    margin-right: 5px;
    border-radius: 3px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    border: 1px solid #DFDFDF;
    background-color: #FFF;
    color: #DFDFDF;
}

#pager span.curr {
    float: left;
    border: 1px solid #ef2629;
    display: inline;
    padding: 3px 10px 3px 10px;
    margin-right: 5px;
    border-radius: 3px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    background: #FFEEE5;
    color: #ef2629;
}

#pager a:hover {
    border: 1px solid #ef2629;
    background-color: #ef2629;
    color: #fff;
}

#pager span.normalsize {
}

#pager_gopage_wrap {
    position: relative;
    left: 0px;
    top: 0px;
}

#pager_btn_go {
    width: 44px;
    height: 18px;
    border: 0px;
    overflow: hidden;
    line-height: 140%;
    padding: 0px;
    margin: 0px;
    text-align: center;
    cursor: pointer;
    background-color: #ef2629;
    color: #FFF;
    position: absolute;
    left: 0px;
    top: -1px;
    font-size: 13px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    display: none;
}

#pager_btn_go_input {
    width: 36px;
    height: 15px;
    color: #999;
    text-align: center;
    margin-left: 1px;
    margin-right: 1px;
    border: 1px solid #DFDFDF;
    position: relative;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    left: 0px;
    top: -1.5px;
    outline: none;
}

#pager_btn_go_input.focus {
    border-color: #ef2629;
}

#pager .pageBtnWrap {
    float: left;
}

#pager .infoTextAndGoPageBtnWrap {
    float: right;
}

#pager .spanDot {
    float: left;
    margin-right: 5px;
}

#pager .currPageNum {
    color: #ef2629;
}

#pager .infoTextAndGoPageBtnWrap {
    padding-top: 5px;
    margin-right: 37px;
    /*float: left;*/
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容