vue-element-ui前后端交互实现分页查询

Posted YDaryl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue-element-ui前后端交互实现分页查询相关的知识,希望对你有一定的参考价值。

大体思路:

①添加element-ui分页组件

②在data里定义几个参数用来存放当前页,每页条数,条目总数以及存放后端分页查询的结果

③后端使用分页查询,controller层接收当前页以及每页条数的参数

④前端编写方法发送请求到controller层调用分页查询的方法,并传参当前页及每页条数

⑤接收查询结果并存放进之前定义好的参数中

⑥给前端点击上下页等功能绑定事件,事件调用分页查询的方法并传参

前端:

①添加element-ui分页组件

<!--
            @size-change,pageSize 改变时会触发
            @current-change , currentPage 改变时会触发
            :current-page 当前页码
            :page-sizes  选择每页显示个数
            :page-size  默认每页显示条目个数,支持 .sync 修饰符
            layout 组件布局,
            :total 总条目数
            -->
        <el-pagination
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
                :current-page="pageNum"
                :page-sizes="[5, 10, 15, 20]"
                :page-size="pageSize"
                layout="total, sizes, prev, pager, next, jumper"
                :total="total">
        </el-pagination>

②在data里定义几个参数用来存放当前页,每页条数,条目总数以及存放后端分页查询的结果

newsData:[], //存放接收后端的数据
pageNum: 1, // 当前页 默认设值1
pageSize: 5, // 每页显示条目 默认设置5
total: '' ,// 条目总数

 

 ③后端使用分页查询,controller层接收当前页以及每页条数的参数

 controller层

 

service层

 

 ④前端编写方法发送请求到controller层调用分页查询的方法,并传参当前页及每页条数

⑤接收查询结果并存放进之前定义好的参数中

                loadPage(pageNum,pageSize) 
                    let url = '/news/page';
                    axios.get(url,
                        params:
                            // pageNum:this.pageNum,
                            // pageSize:this.pageSize
                            pageNum,
                            pageSize
                        
                    ).then(resp => 
                        resp.data.list.get
                        this.newsData = resp.data.list;
                        this.total = resp.data.total;
                        // this.total = resp.data.list.total;
                        console.log(resp.data);
                    );
                

⑥给前端点击上下页等功能绑定事件,事件调用分页查询的方法并传参

                handleSizeChange(val) 
                    this.pageSize = val;
                    this.loadPage(this.pageNum,this.pageSize);
                    console.log(`每页 $val 条`);
                ,
                handleCurrentChange(val) 
                    this.pageNum = val;
                    this.loadPage(this.pageNum,this.pageSize);
                    console.log(`当前页: $val`);
                ,

功能展示:

 

我的前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>listdemo</title>
    <script src="/js/vue-2.6.10.js"></script>
    <script src="/js/axios.min.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>
    <div id="root">
        <el-button
                size="mini"
                type="primary"
                @click="clean">发布新闻</el-button>
        <el-table
                ref="news"
                :data="newsData"
                tooltip-effect="dark"
                style="width: 100%"
                @selection-change="handleSelectionChange">
            <el-table-column
                    type="selection"
                    width="55">
            </el-table-column>
            <el-table-column
                    prop="nid"
                    label="新闻编号"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="ntid"
                    label="主题编号"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="ntitle"
                    label="新闻标题"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="nauthor"
                    label="新闻作者"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="ncreateDate"
                    label="创建日期"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="npicPath"
                    label="图片路径"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="ncontent"
                    label="新闻内容"
                    width="120"
                    show-overflow-tooltip="true">
            </el-table-column>
            <el-table-column
                    prop="nmodifyDate"
                    label="修改时间"
                    width="120">
            </el-table-column>
            <el-table-column
                    prop="nsummary"
                    label="备注信息"
                    width="120"
                    show-overflow-tooltip="true">
            </el-table-column>
            <el-table-column
                    prop="comment"
                    label="评论信息"
                    width="120">
            </el-table-column>
            <el-table-column label="操作" width="120">
                <template slot-scope="scope">
                    <el-button
                            size="mini" 
                            @click="dialogFormVisible = true;getDate(scope.$index)">编辑
                    </el-button>
                    <el-dialog title="修改信息"
                               :visible.sync="dialogFormVisible"
                               :close-on-click-modal="false"
                               @close="onClose">
                        <el-form ref="form"
                                 :model="form"
                                 :rules="rules"
                                 label-width="100px"
                                 class="demo-form">
                            <el-form-item label="新闻编号" prop="nid">
                                <el-input v-model="form.nid"></el-input>
                            </el-form-item>
                            <el-form-item label="主题编号" prop="ntid">
                                <el-input v-model="form.ntid"></el-input>
                            </el-form-item>
                            <el-form-item label="新闻标题" prop="ntitle">
                                <el-input type="textarea" v-model="form.ntitle"></el-input>
                            </el-form-item>
                            <el-form-item label="新闻作者" prop="nauthor">
                                <el-input v-model="form.nauthor"></el-input>
                            </el-form-item>
                            <el-form-item label="创建日期" prop="ncreateDate">
                                <el-date-picker type="date"
                                                placeholder="选择日期"
                                                v-model="form.ncreateDate"
                                                style="width: 100%;"></el-date-picker>
                            </el-form-item>
                            <el-form-item label="图片路径" prop="npicPath">
                                <el-input v-model="form.npicPath"></el-input>
                            </el-form-item>
                            <el-form-item label="新闻内容" prop="ncontent">
                                <el-input type="textarea" v-model="form.ncontent"></el-input>
                            </el-form-item>
                            <el-form-item label="修改时间" prop="nmodifyDate">
                                <el-date-picker type="date" 
                                                placeholder="修改时间" 
                                                v-model="form.nmodifyDate" 
                                                style="width: 100%;"></el-date-picker>
                            </el-form-item>
                            <el-form-item label="备注信息" prop="nsummary">
                                <el-input type="textarea" v-model="form.nsummary"></el-input>
                            </el-form-item>
                            <el-form-item>
                                <el-button type="primary" @click="submitForm('form')">立即创建</el-button>
                                <el-button @click="resetForm('form')">重置</el-button>
                            </el-form-item>
                        </el-form>
                        <div slot="footer" class="dialog-footer">
                            <el-button @click="dialogFormVisible = false">取 消</el-button>
                            <el-button type="primary" 
                                       @click="dialogFormVisible = false;loadUpdate">确 定
                            </el-button>
                        </div>
                    </el-dialog>
                    <el-button
                            size="mini"
                            type="danger"
                            @click="handleDelete(scope.$index, scope.row)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!--
            @size-change,pageSize 改变时会触发
            @current-change , currentPage 改变时会触发
            :current-page 当前页码
            :page-sizes  选择每页显示个数
            :page-size  默认每页显示条目个数,支持 .sync 修饰符
            layout 组件布局,
            :total 总条目数
            -->
        <el-pagination
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
                :current-page="pageNum"
                :page-sizes="[5, 10, 15, 20]"
                :page-size="pageSize"
                layout="total, sizes, prev, pager, next, jumper"
                :total="total">
        </el-pagination>
    </div>
    <script>
        new Vue(
            el:"#root",
            data()
                return
                    newsData:[],
                    pageNum: 1, // 当前页
                    pageSize: 5, // 每页显示条目
                    total: '' ,// 条目总数
                    dialogFormVisible: false,
                    form: 
                        delivery: false,
                        type: [],
                        nid:'',
                        ntid:'',
                        ntitle:'',
                        nauthor:'',
                        ncreateDate:'',
                        npicPath:'',
                        ncontent:'',
                        nmodifyDate:'',
                        nsummary:''
                    ,
                    rules: 
                        nid: [
                             required: true, message: '请输入新闻编号', trigger: 'blur' ,
                             min: 1, max: 5, message: '长度在 1 到 5 个字符', trigger: 'blur' 
                        ],
                        ntid: [
                             required: true, message: '请输入主题编号', trigger: 'blur' 
                        ],
                        ntitle: [
                             required: true, message: '请输入新闻标题', trigger: 'blur' 
                        ],
                        nauthor: [
                             required: true, message: '请输入新闻作者', trigger: 'blur' 
                        ],
                        ncreateDate: [
                             type: 'date', required: true, message: '请选择创建日期', trigger: 'change' 
                        ],
                        npicPath: [
                             required: true, message: '请输入图片路径', trigger: 'blur' 
                        ],
                        ncontent: [
                             required: true, message: '请输入新闻内容', trigger: 'blur' 
                        ],
                        nmodifyDate: [
                             type: 'date', required: true, message: '请选择修改时间', trigger: 'change' 
                        ],
                        nsummary: [
                             required: true, message: '请输入备注信息', trigger: 'blur' 
                        ],
                    ,
                    formLabelWidth: '120px'
                
            ,

            methods: 
                onClose() 
                    this.form = '';
                ,
                clean() 
                    // this.form = '';
                    this.dialogFormVisible = true;
                    // this.show();
                ,
                show() 
                    this.dialogFormVisible = true;
                ,
                toggleSelection(rows) 
                    if (rows) 
                        rows.forEach(row => 
                            this.$refs.multipleTable.toggleRowSelection(row);
                        );
                     else 
                        this.$refs.multipleTable.clearSelection();
                    
                ,
                handleSelectionChange(val) 
                    this.multipleSelection = val;
                ,
                handleEdit(index, row) 
                    console.log(index, row);
                ,
                getDate(index)
                  this.form = this.newsData[index];
                ,
                handleDelete(index, row) 
                    this.$confirm('是否确定删除此条数据','提示', 
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                        // center: true
                    ).then(() => 
                        let url = `/news/delete/$row.nid`;
                        axios.delete(url).then(resp => 
                            this.loadAll();
                            console.log(resp.data);
                        );
                        this.$message(
                            type: 'success',
                            message: '删除成功!'
                        );
                    ).catch(() => 
                        this.$message(
                            type: 'info',
                            message: '已取消删除'
                        );
                    );
                    // console.log(index, row);
                ,
                loadAll() 
                    //then()回调函数,获取后台响应的数据
                    let url = '/news/findAll';
                    axios.get(url).then(resp => 
                        this.newsData = resp.data;
                        console.log(resp.data);
                    );
                ,
                loadUpdate() 
                    let url = 'news/update';
                    // let news = JSON.parse(this.form);
                    axios.put(url, this.form).then(resp => 
                        this.loadAll();
                        console.log(resp.data);
                    );
                ,
                submitForm(formName) 
                    this.$refs[formName].validate((valid) => 
                        if (valid) 
                            alert('submit!');
                         else 
                            console.log('error submit!!');
                            return false;
                        
                    );
                ,
                resetForm(formName) 
                    this.$refs[formName].resetFields();
                ,
                loadAdd() 
                    let url = 'news/insert';
                    axios.post(url,params).then(resp => 
                        console.log(resp.data);
                    );
                ,
                handleSizeChange(val) 
                    this.pageSize = val;
                    this.loadPage(this.pageNum,this.pageSize);
                    console.log(`每页 $val 条`);
                ,
                handleCurrentChange(val) 
                    this.pageNum = val;
                    this.loadPage(this.pageNum,this.pageSize);
                    console.log(`当前页: $val`);
                ,
                loadPage(pageNum,pageSize) 
                    let url = '/news/page';
                    axios.get(url,
                        params:
                            // pageNum:this.pageNum,
                            // pageSize:this.pageSize
                            pageNum,
                            pageSize
                        
                    ).then(resp => 
                        resp.data.list.get
                        this.newsData = resp.data.list;
                        this.total = resp.data.total;
                        // this.total = resp.data.list.total;
                        console.log(resp.data);
                    );
                
            ,
            created()
                this.loadPage(this.pageNum,this.pageSize);
                // this.loadAll();
            
        )
    </script>

</body>
</html>

 

 

前后端分离实践分页请求历史聊天消息

分页加载原因

在一个系统数据很大,在接口数据交互,海量数据查询,服务器接口返回的数据不可能一次性返回。

数据量大,从数据库一次性查询,再到网络传输是要花费更多的时间客户端才能响应拿到数据进行 UI 界面渲染。

从接口拿到大量数据渲染,Web 端会造成界面卡顿,移动端处理大量数据,会出现 OOM。

所以获取数据可以通过分页加载的方式处理数据和UI交互。这样解决性能问题,让 UED 效果更好一点。

通常前端可以通过上拉刷新、下拉加载更多等方式。

解决当前聊天问题

  • 在单聊,会获取聊天历史记录,分页拉取

    • 获取聊天历史记录,其实就是一个下拉加载更多数据的操作
  • 更新聊天记录列表

    • 方法一、从新调用获取

    • 方法二、在接收到消息到时候更新

    • 方法三、查找现有列表,获取在列表位置,根据更新对应字段

解决方案监听

  • 监听滚动鼠标滚轮或滚动事件
  • 使用第三方模块 pullrefreshjs
  • ...

添加正在加载

修改 pages/Im/chat/index.jsx,在请求的时候才显示 Spin 组件,良好提示。

...
<div className={styles['chat-container']}>
  <div id="chatItems" className={styles['chat-items']}>
    {loading && (
      <div className={styles['chat-loading']}>
        <Spin />
      </div>
    )}
    
    ...

loading 属性可以从 chat  model 中获取,dva effects 封装了在发起请求都会维护一个 models 属性

export default connect(({ user, chat, loading }) => ({
  user,
  chat,
  loading: loading.models.chat, // if user ==> loading.models.user
}))(Chat);

监听滚动鼠标滚轮

监听聊天容器鼠标滚动

const onChatItemScroll = (e) => {
  
  // hasMore 标示加载最后一次,已经没有更多数据了
  // loading 表示正在加载中,不再加载,等上一次请求完成才能发起请求,避免请求过快
    if (!hasMore || loading) return;

  // 鼠标滚轮是否滚动
  
  // (e.wheelDelta > 0) 向上滚动
  // 否则向下滚动
    if (e.wheelDelta > 0) {
      
      let chatItems = document.getElementById('chatItems');
      // 聊天容器滚动至顶部的时候才发起请求
      if (chatItems && chatItems.scrollTop === 0) {
        // 获取当前聊天对象聊天记录
        let chatId = getDiffChatId(chatUserInfo, userId);
        // 发起请求 -->. dispatch model
        dispatch({ type'chat/getMessageChatList'payload: { chatId, pageNum: pageNum + 1 } });
      } else {
        console.log('follow @全栈技术部 thank');
      }
    }
  };

const addChatItemScrollListener = () => {
    let chatItems = document.getElementById('chatItems');
    chatItems && chatItems.addEventListener('mousewheel', onChatItemScroll, false);
  };

  const removeChatItemScrollListener = () => {
    let chatItems = document.getElementById('chatItems');
    chatItems && chatItems.removeEventListener('mousewheel', onChatItemScroll, false);
  };

  useEffect(() => {
    if (!receiveId) {
      history.replace({ pathname'/im' });
      return;
    }
    
    // 添加事件
    addChatItemScrollListener();
    return () => {
    // 移除事件
      removeChatItemScrollListener();
    };
    
    // 当以下属性发生变化,事件监听都需要获取当前最新的属性数据
  }, [pageNum, hasMore, loading]);

mousewheel 事件存在 浏览器兼容性问题

Chat  Model 逻辑处理

添加状态

state {
...
  // 请求页码
    pageNum1,

    // 标示 是否还有更多数据
    hasMoretrue
...
},
  
  effects{
    ...
  /**
     * 获取聊天请求
     */

    *getMessageChatList({ payload }, { call, put, select }) {
      const { chatId, pageNum } = payload;

      const chatState = yield select((state) => state['chat']);
      const { messageChatList = [] } = chatState;

      const response = yield call(getMessageChatList, { chatId, pageNum });
      if (response.code === 200) {
        
        // 响应数据
        const lists = (response.data && response.data.lists) || [];
        
        // 这个数据根据接口返回
        // 数据反转,无需遍历添加
        lists.reverse();

        // 加载下一页数据,继续往聊天记录中添加 concat
        let messageList = pageNum !== 1 ? lists.concat(messageChatList) : lists;
        yield put({
          type'updateMessageChatList',
          
          // dispatch 传递参数
          // 请求最后一次返回 lists 字段为空,表示没有很多数据了
          payload: { messageChatList: messageList, pageNum: pageNum, hasMore: lists.length > 0 },
        });
      }
    ...
}

发送消息更新聊天记录

*sendMessage({ payload }, { call, put, select }) {
      const { messageTemplate } = payload;
      const chatState = yield select((state) => state['chat']);

      const { messageChatList = [], socket, messageRecordList = [] } = chatState;

      const temp = messageChatList.concat();
      temp.push(messageTemplate);
      yield put({ type'refreshChatList'payload: { messageChatList: temp } });
      yield put({ type'chatInputMessageChange'payload: { contentnull } });

      if (!socket) console.warn('socket 不存在,需要重新登录,请检查 Socket 连接。');
      if (socket) socket.emit('SINGLE_CHAT', messageTemplate);

      // 更新聊天记录列表

      // 方法一、从新调用获取
      yield put({ type'getMessageRecordList' });

      // 方法二、在接收到消息到时候更新

      // 方法三、查找现有列表 更新对应字段
      // const { sendId, receiveId } = messageTemplate;
      // let recordIndex = -1;
      // messageRecordList.map((item, index) => {
      //   if (
      //     (item.sendId === sendId && item.receiveId === receiveId) ||
      //     (item.receiveId === sendId && item.sendId === receiveId)
      //   ) {
      //     recordIndex = index;
      //   }
      // });
      // if (recordIndex !== -1) {
      //   messageRecordList[recordIndex]
      // }
      // console.log(recordIndex);

      console.log(chatState);
    },

API 修改查看


效果图


end