尚筹网-3.Admin分页和批量删除

1. 修正

1.1 登录完成跳转到后台主页面改成重定向

@RequestMapping("/admin/do/login")
public String doLogin(@RequestParam("loginAcct") String loginAcct,
                      @RequestParam("userPswd")  String userPswd,
                      Model model,
                      HttpSession session){
    //调用adminService的login方法执行登陆业务逻辑,返回查询到的Admin对象
    Admin admin = adminService.login(loginAcct,userPswd);
    //判断admin是否为null
    if(admin == null){
        model.addAttribute(CrowdFundingConstant.ATTR_NAME_MESSAGE, CrowdFundingConstant.MESSAGE_LOGIN_FAILED);
        return "admin-login";
    }
    session.setAttribute(CrowdFundingConstant.ATTR_NAME_LOGIN_ADMIN,admin);
    return "redirect:/admin/to/main/page.html";
}

​ 说明:/admin/to/main/page.html地址是在view-controller中配置的。

2. 后台主页面完整显示

2.1 加入原型页面源代码

原型/main.html→admin-main.jsp

需要调整的内容:

  • 字符集改成UTF-8
  • 加入title和base标签
  • 把“张三”改成${sessionScope['LOGIN-ADMIN'].userName }
  • 把退出的超链接改成实际地址
<a href="admin/logout.html"><i class="glyphicon glyphicon-off"></i> 退出系统</a>

2.2 公共部分提取

  • head标签部分

    <%@ include file="/WEB-INF/include-head.jsp" %>

  • nav标签部分

    <%@ include file="/WEB-INF/include-nav.jsp" %>

  • sidebar部分

    <%@ include file="/WEB-INF/include-sidebar.jsp" %>

2.3 创建JSP模版

2.3.1 模版内容

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html lang="UTF-8">
        <%@ include file="/WEB-INF/include-head.jsp" %>
            <body>
                <%@ include file="/WEB-INF/include-nav.jsp" %>
                    <div class="container-fluid">
                        <div class="row">
                            <%@ include file="/WEB-INF/include-sidebar.jsp" %>
                                <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">

                                </div>
                        </div>
                    </div>
            </body>
    </html>

2.3.2 创建JSP模版操作

3. Admin维护功能清单

  • 分页显示全部Admin数据√
  • 分页显示Admin数据的关键词查询结果√
  • 批量删除
  • 单条删除
  • 新增Admin
  • 更新Admin

分页显示全部数据和查询结果在后端可以合并为同一个操作。

批量删除和单条删除在后端可以合并为同一个操作。

4. 分页功能

4.1 分析

4.2 执行查询的SQL语句

SELECT
    *
FROM
    t_admin
WHERE
    login_acct LIKE CONCAT("%", "", "%")
OR user_name LIKE CONCAT("%", "", "%")
OR email LIKE CONCAT("%", "", "%");

4.3 MyBatis的PageHelper插件

4.3.1 作用

以完全非侵入的方式在原有查询基础上附加分页效果。从SQL层面来说,在SQL语句后面附加LIMIT子句。从Java代码来说,把原来返回的List类型封装为Page类型。

4.3.2 依赖信息

<!-- 父工程 -->
<!-- MyBatis分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.0.0</version>
</dependency>
<!-- 子工程 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
</dependency>

4.3.3 配置方式

  • 所在工程:atcrowdfunding-admin-1-webui
  • 配置文件:spring-persist-mybatis.xml
  • 在SqlSessionFactoryBean中配置MyBatis插件
<!-- 配置PageHelper插件 -->
<property name="plugins">
    <array>
        <!--配置PageHelper全类名-->
        <bean class="com.github.pagehelper.PageHelper">
            <!--配置相关参数-->
            <property name="properties">
                <props>
                    <!--数据库方言指定数据库类型为MySQL-->
                    <prop key="dialect">mysql</prop>
                    <!--配置自动修正页码-->
                    <!--pageNo的有效范围:1~总页数-->
                    <prop key="reasonable">true</prop>
                </props>
            </property>
        </bean>
    </array>
</property>

4.4 AdminMapper

4.4.1 Mapper配置文件

  • 所在工程:atcrowdfunding-admin-1-webui
  • 文件:resources/mybatis/mapper/AdminMapper.xml
<select id="selectAdminListByKeyword" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from t_admin
    WHERE
    login_acct LIKE CONCAT("%", #{keyword}, "%")
    OR user_name LIKE CONCAT("%", #{keyword}, "%")
    OR email LIKE CONCAT("%", #{keyword}, "%")
</select>

4.4.2 Mapper接口

  • 所在工程:atcrowdfunding-admin-2-component
  • 全类名:com.rgh.crowd.funding.mapper.AdminMapper
List<Admin> selectAdminListByKeyword(String keyword);

4.5 AdminService

所在工程:atcrowdfunding-admin-2-component

接口方法:

PageInfo<Admin> queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword);

实现类方法:

@Override
public PageInfo<Admin> queryForKeywordSearch(Integer pageNum, Integer pageSize, String keyword) {
    // 1.调用PageHelper的工具方法,开启分页功能
    PageHelper.startPage(pageNum, pageSize);
    // 2.执行分页查询
    List<Admin> list = adminMapper.selectAdminListByKeyword(keyword);
    // 3.将list封装到PageInfo对象中
    return new PageInfo<>(list);
}

4.6 AdminHandler

  • 所在工程:atcrowdfunding-admin-2-component
  • 全类名:com.rgh.crowd.funding.handler.AdminHandler
@RequestMapping("/admin/query/for/search")
public String queryForSearch(       
    // 如果页面上没有提供对应的请求参数,那么可以使用defaultValue指定默认值
    @RequestParam(value="pageNum", defaultValue="1") Integer pageNum, 
    @RequestParam(value="pageSize", defaultValue="5") Integer pageSize, 
    @RequestParam(value="keyword", defaultValue="") String keyword,
    Model model) {  
    PageInfo<Admin> pageInfo = adminService.queryForKeywordSearch(pageNum, pageSize, keyword);
    model.addAttribute(CrowdFundingConstant.ATTR_NAME_PAGE_INFO, pageInfo);
    return "admin-page";
}

4.7 插入测试用的假数据

  • 所在工程:atcrowdfunding-admin-1-webui
  • 全类名:com.rgh.crowd.funding.test.CrowdFundingTest
@Test
public void batchSaveAdmin() {
    for(int i = 0; i < 500; i++) {
        adminMapper.insert(new Admin(null, "loginAcct"+i, "1111111", "userName"+i, "email"+i+"@qq.com", null));
    }
}

4.8 页面显示:主体部分

<tbody>
    <c:if test="${empty requestScope['PAGE-INFO'].list }">
        <tr>
            <td style="text-align: center;" colspan="6">抱歉!没有符合您要求的查询结果!</td>
        </tr>
    </c:if>
    <c:if test="${!empty requestScope['PAGE-INFO'].list }">
        <c:forEach items="${requestScope['PAGE-INFO'].list }" var="admin" varStatus="myStatus">
            <tr>
                <td>${myStatus.count }</td>
                <td><input type="checkbox"></td>
                <td>${admin.loginAcct }</td>
                <td>${admin.userName }</td>
                <td>${admin.email }</td>
                <td>
                    <button type="button" class="btn btn-success btn-xs">
                        <i class=" glyphicon glyphicon-check"></i>
                    </button>
                    <button type="button" class="btn btn-primary btn-xs">
                        <i class=" glyphicon glyphicon-pencil"></i>
                    </button>
                    <button type="button" class="btn btn-danger btn-xs">
                        <i class=" glyphicon glyphicon-remove"></i>
                    </button>
                </td>
            </tr>
        </c:forEach>
    </c:if>
</tbody>

4.9 页面显示:导航条部分

使用一个基于jQuery的分页插件:Pagination


4.9.1 环境搭建

  1. 加入样式文件

    将pagination.css复制到/atcrowdfunding-admin-1-webui/src/main/webapp/css目录下
    
  2. 在需要的页面引用pagination.css

    在admin-page.jsp中引入

    <link rel="stylesheet" href="css/pagination.css"/>
    
  3. 加入Pagination的js文件

    将jquery.pagination.js复制到/atcrowdfunding-admin-1-webui/src/main/webapp/script目录下
    
  4. 在需要的页面引用jquery.pagination.js

    <script type="text/javascript" src="script/jquery.pagination.js"></script>
    

4.9.2 分页导航条需要在HTML标签中加入的部分

<tfoot>
    <tr>
        <td colspan="6" align="center">
            <div id="Pagination" class="pagination"><!-- 这里显示分页 --></div>
        </td>
    </tr>
</tfoot>

4.9.3 jQuery代码

$(function() {
    
    // 对分页导航条显示进行初始化
    initPagination();
    
});
// 声明函数封装导航条初始化操作
function initPagination() { 
    // 声明变量存储总记录数
    var totalRecord = ${requestScope['PAGE-INFO'].total};
    // 声明变量存储分页导航条显示时的属性设置
    var paginationProperties = {
        num_edge_entries : 3,           //边缘页数
        num_display_entries : 5,        //主体页数
        callback : pageselectCallback,  //回调函数
        items_per_page : ${requestScope['PAGE-INFO'].pageSize}, //每页显示数据数量,就是pageSize
        current_page : ${requestScope['PAGE-INFO'].pageNum - 1},//当前页页码
        prev_text : "上一页",          //上一页文本
        next_text : "下一页"           //下一页文本
    };
    // 显示分页导航条
    $("#Pagination").pagination(totalRecord, paginationProperties);
}
// 在每一次点击“上一页”、“下一页”、“页码”时执行这个函数跳转页面
function pageselectCallback(pageIndex, jq) {
    // pageIndex从0开始,pageNum从1开始
    var pageNum = pageIndex + 1;
    // 跳转页面
    window.location.href = "admin/query/for/search.html?pageNum="+pageNum;
    return false;
}

4.9.4 Pagination修改源码

修改/atcrowdfunding-admin-1-webui/src/main/webapp/script/jquery.pagination.js文件,将158行注释掉
// opts.callback(current_page, this);

对应的问题:Pagination因为在这个地方重新加载页面,会造成死循环

4.10 关键词查询

4.10.1 将表单修改为可以提交的状态

  • 所在工程:atcrowdfunding-admin-1-webui
  • 所在页面:admin-page.jsp
<form action="admin/query/for/search.html" class="form-inline" role="form" style="float:left;" method="post">
    <div class="form-group has-feedback">
        <div class="input-group">
            <div class="input-group-addon">查询条件</div>
            <input class="form-control has-success"
                   type="text"
                   placeholder="请输入查询条件"
                   name="keyword"
                   >
        </div>
    </div>
    <button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i> 查询
    </button>
</form>

4.10.2 跳转页面时携带keyword请求参数

// 在每一次点击“上一页”、“下一页”、“页码”时执行这个函数跳转页面
function pageselectCallback(pageIndex, jq) {
    // pageIndex从0开始,pageNum从1开始
    var pageNum = pageIndex + 1;
    // 跳转页面
    window.location.href = "admin/query/for/search.html?pageNum="+pageNum+"&keyword=${param.keyword}";  
    return false;
}

AdminMapper.xml

  <select id="selectAdminListByKeyword" resultMap="BaseResultMap">
    SELECT 
    <include refid="Base_Column_List"/>
    from t_admin
    where
    login_acct like concat("%",#{keyword},"%")
    OR user_name LIKE concat("%",#{keyword},"%")
    OR email LIKE concat("%",#{keyword},"%")
    order by id desc
  </select>

5. 批量删除

5.1 全选/全不选功能

5.1.1 标记要操作的多选框

<thead>
    <tr>
        <th width="30">#</th>
        <th width="30"><input id="summaryBox" type="checkbox"></th>
        <th>账号</th>
        <th>名称</th>
        <th>邮箱地址</th>
        <th width="100">操作</th>
    </tr>
</thead>
<tbody>
    <c:if test="${empty requestScope['PAGE-INFO'].list }">
        <tr>
            <td style="text-align: center;" colspan="6">抱歉!没有符合您要求的查询结果!</td>
        </tr>
    </c:if>
    <c:if test="${!empty requestScope['PAGE-INFO'].list }">
        <c:forEach items="${requestScope['PAGE-INFO'].list }"
                   var="admin" varStatus="myStatus">
            <tr>
                <td>${myStatus.count }</td>
                <td><input class="itemBox" type="checkbox"></td>
                <td>${admin.loginAcct }</td>
                <td>${admin.userName }</td>
                <td>${admin.email }</td>
                <td>
                    <button type="button" class="btn btn-success btn-xs">
                        <i class=" glyphicon glyphicon-check"></i>
                    </button>
                    <button type="button" class="btn btn-primary btn-xs">
                        <i class=" glyphicon glyphicon-pencil"></i>
                    </button>
                    <button type="button" class="btn btn-danger btn-xs">
                        <i class=" glyphicon glyphicon-remove"></i>
                    </button>
                </td>
            </tr>
        </c:forEach>
    </c:if>
</tbody>

5.1.2 jQuery代码

// 全选/全不选功能
$("#summaryBox").click(function() {
    // 获取当前#summaryBox的勾选状态
    // this代表当前多选框对象(DOM对象)
    // checked属性为true时表示被勾选,为false时表示没有被勾选
    // 使用checkStatus设置.itemBox的状态
    $(".itemBox").prop("checked",this.checked);
});

5.2 批量删除按钮绑定单击响应函数

5.2.1 给批量删除按钮标记id

<button 
        id="batchRemoveBtn"
        type="button" 
        class="btn btn-danger"
        style="float: right; margin-left: 10px;">
    <i class=" glyphicon glyphicon-remove"></i> 删除
</button>

5.2.2 给itemBox设置adminId属性

<input adminId="${admin.id }" class="itemBox" type="checkbox">

说明:adminId属性是HTML标签本身并没有的属性,是我们强行设置的。

5.3 封装统一的Ajax响应结果

所在工程:atcrowdfunding-admin-4-entity

全类名:com.rgh.crowd.funding.entity.ResultEntity

/**
 * 统一整个项目中所有Ajax请求的响应格式,作为项目的一个开发规范
 * @author Lenovo
 *
 */
public class ResultEntity<T> {
    public static final String SUCCESS = "SUCCESS";
    public static final String FAILED = "FAILED";
    public static final String NO_MESSAGE = "NO_MESSAGE";
    public static final String NO_DATA = "NO_DATA";
    // 方便返回成功结果(不携带查询结果情况)
    public static ResultEntity<String> successWithoutData() {
        return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
    }
    // 方便返回成功结果(携带查询结果情况)
    public static <E> ResultEntity<E> successWithoutData(E data) {
        return new ResultEntity<E>(SUCCESS, NO_MESSAGE, data);
    }
    // 方便返回失败结果
    public static <E> ResultEntity<E> failed(E data, String message) {
        return new ResultEntity<E>(FAILED, message, data);
    }
    private String result;
    private String message;
    private T data;
    public ResultEntity() {
    }
    public ResultEntity(String result, String message, T data) {
        super();
        this.result = result;
        this.message = message;
        this.data = data;
    }
    @Override
    public String toString() {
        return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
    }
    public String getResult() {
        return result;
    }
    public void setResult(String result) {
        this.result = result;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

5.4 jackson和Spring版本兼容性问题

5.4.1 方案一【×】

  • 和jackson-mapper-asl的1.9.2兼容的Spring版本是4.0.0。
  • 但是Spring的4.0.0和我们要使用的SpringSecurity版本不兼容。

5.4.2 方案二【√】

  • Spring使用5.0.2
  • jackson-core使用2.9.8
  • jackson-databind使用2.9.8

父工程pom.xml文件更改依赖

<!-- Spring进行JSON数据转换依赖 -->
<!--<dependency>
      <groupId>org.codehaus.jackson</groupId>
           <artifactId>jackson-mapper-asl</artifactId>
           <version>1.9.2</version>
       </dependency>-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

子工程atcrowdfunding-admin-2-component的pom.xml文件

<!--<dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
        </dependency>-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

5.5 勾选检查

当用户在页面上没有勾选任何多选框,然后点击批量删除按钮时应该给出提示并不执行删除。

5.6 删除前确认

弹出确认框,让用户确认是否删除。

5.7 删除完成善后

  • 如果删除操作执行成功则跳转页面。
  • 如果失败则弹出提示消息。

5.8 最终代码

5.8.1 前端JS部分

// 全选/全不选功能
$("#summaryBox").click(function() {
    // 获取当前#summaryBox的勾选状态
    // this代表当前多选框对象(DOM对象)
    // checked属性为true时表示被勾选,为false时表示没有被勾选
    // 使用checkStatus设置.itemBox的状态
    $(".itemBox").prop("checked",this.checked);
});
// 给批量删除按钮绑定单击响应函数
$("#batchRemoveBtn").click(function(){
    // 创建数组对象:存储adminId
    var adminIdArray = new Array();
    // 创建数组对象:存储loginAcct
    var loginAcctArray = new Array();
    // 通过jQuery选择器定位到所有被选中itemBox,然后遍历
    $(".itemBox:checked").each(function(){
        <%-- <input adminId="${admin.id }" class="itemBox" type="checkbox"> --%>
            // this.adminId拿不到值,原因是:this作为DOM对象无法读取HTML标签本身没有的属性
            // 将this转换为jQuery对象调用attr()函数就能够拿到值
            var adminId = $(this).attr("adminId");
        // 调用数组对象的push()方法将数据存入数组
        adminIdArray.push(adminId);
        // <td><input type="checkbox" /></td><td>loginAcct</td>
        var loginAcct = $(this)             // 当前checkbox对象
        .parent("td")   // 包含checkbox的td
        .next()         // 当前td的下一个兄弟元素,也就是下一个td
        .text();        // 下一个td的标签内部的文本
        loginAcctArray.push(loginAcct);
    });
    // 检查adminIdArray是否包含有效数据
    if(adminIdArray.length == 0) {
        // 给出提示
        alert("请勾选您要删除的记录!");
        // 函数停止执行
        return ;
    }
    // 给出确认提示,让用户确认是否真的删除这两条记录
    var confirmResult = confirm("您真的要删除"+loginAcctArray+"信息吗?操作不可逆,请谨慎决定!");
    // 如果用户点击了取消,那么让函数停止执行
    if(!confirmResult) {
        return ;
    }
    // 将JSON数组转换为JSON字符串
    // var a = [1,2,3,4,5];                 数组类型
    // var b = "[1,2,3,4,5]";               字符串类型
    // var c = {"userName":"tom"};          对象类型
    // var d = "{\"userName\":\"tom\"}";    字符串类型
    var requestBody = JSON.stringify(adminIdArray);
    // 发送Ajax请求将adminIdArray发送给handler方法
    $.ajax({
        "url":"admin/batch/remove.json",    // 服务器端接收请求的URL地址
        "type":"post",  // 设置请求方式为POST
        "contentType":"application/json;charset=UTF-8", // 设置请求体内容类型,告诉服务器当前请求体发送的是JSON数据
        "data":requestBody, // 请求体真正要发送给服务器的数据
        "dataType":"json",      // 把服务器端返回的数据当作JSON格式解析
        "success":function(response) {      // 服务器处理请求成功后执行的函数,响应体以参数形式传入当前函数
            console.log(response);
            var result = response.result;
            if(result == "SUCCESS") {
                // 跳转页面
                window.location.href = "admin/query/for/search.html?pageNum=${requestScope['PAGE-INFO'].pageNum}&keyword=${param.keyword}";
            }
            if(result == "FAILED") {
                alert(response.message);
                return ;
            }
        },
        "error":function(response) {    // 服务器处理请求失败后执行的函数,响应体以参数形式传入当前函数
            alert(response.message);
            return ;
        }
    });
});

5.8.2 后端部分

5.8.2.1 @RequestBody和@ResponseBody注解
  • 所在工程:atcrowdfunding-admin-2-component
  • 全类名:com.rgh.crowd.funding.handler.AdminHandler
//将当前handler方法的返回值作为响应体返回,不经过试图解析器
@ResponseBody
@RequestMapping("/admin/batch/remove")
public ResultEntity<String> batchRemove(@RequestBody List<Integer> adminIdList){
    try {
        adminService.batchRemove(adminIdList);
        return ResultEntity.successWithoutData();
    }catch (Exception e){
        return ResultEntity.failed(null,e.getMessage());
    }
}
  • 所在工程:atcrowdfunding-admin-2-component
  • 全类名:com.rgh.crowd.funding.service.impl.AdminServiceImpl
public void batchRemove(List<Integer> adminIdList) {
    //QBC:Query By Criteria
    //创建AdminExample对象
    AdminExample adminExample = new AdminExample();
    //创建Criteria对象
    //Criteria对象可以帮助我们封装查询条件
    //通过使用Criteria对象,可以把Java代码转换成SQL语句中WHERE字句里面的具体查询条件
    AdminExample.Criteria criteria = adminExample.createCriteria();
    //针对要查询的字段封装具体的查询条件
    criteria.andIdIn(adminIdList);
    //执行具体操作时把封装了查询条件的Example对象传入
    adminMapper.deleteByExample(adminExample);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容