多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] # 建立目录 在项目根目录下执行以下命令创建目录 ~~~ mkdir -p ./src/views/portal/news ~~~ # 创建新闻列表文件 /src/views/portal/news/index.vue ~~~ <template> <div> <ChannelHeader> <NavigatorPath :path="navigatorPathData"></NavigatorPath> <template v-slot:extra> <Input search enter-button v-model="keywords" placeholder="输入关键字..." @on-search="doSearch" /> </template> </ChannelHeader> <Card class="card" v-for="(item, index) of tableData.data" :key="index" :to="{ path: '/portal/news/detail', query: { id: item.id, menu: 'news' }, }" > <Row :gutter="20"> <Col span="6"> <div class="banner"> <img :src="item.cover" /></div ></Col> <Col span="18"> <div class="info"> <div class="title" v-line-clamp="1"> {{ item.title }} </div> <div class="tag"> <Tag color="orange"> {{ item.create_at | formatDate("yyyy-MM-dd") }}</Tag > </div> <div class="abstract" v-line-clamp="3">{{ item.abstract }}</div> </div> </Col> </Row> </Card> <Row> <Page :page-size="tableData.pageSize" :total="tableData.count" :current="tableData.currentPage" @on-change="changePage" show-total ></Page> </Row> </div> </template> <script> import NavigatorPath from "@/components/navigator-path"; import ChannelHeader from "@/components/channel-header"; import { news } from "@/common/api/portal"; export default { components: { NavigatorPath, ChannelHeader, }, data() { return { navigatorPathData: [{ title: "首页", path: "/", query: {} }, "新闻动态"], keywords: "", //检索关键字 map: { pageSize: 3, page: 1 }, //搜索条件 tableData: {}, //搜索结果 }; }, methods: { doSearch() { this.map.keywords = this.keywords; this.map.page = 1; //开启新的搜索,显示第一页内容 this.getList(); }, changePage(index) { this.map.page = index; //改变页码 this.getList(); }, getList() { news.getList(this.map).then((res) => { if (res.errno == 0) { this.tableData = res.data; //对数据排序 this.tableData.data = this.tableData.data.sort((x, y) => { return x.create_at < y.create_at ? 1 : -1; }); } else { this.$Message.error(res.errmsg); } }); }, async getList2() { let res = await news.getList(this.map); if (res.errno == 0) { this.tableData = res.data; } else { this.$Message.error(res.errmsg); } }, }, mounted() { this.doSearch(); }, filters: { // 移动到main.js实现全局注册,方便其他页面使用 formatDate(date, fmt = "yyyy-MM-dd") { date = new Date(date); if (/(y+)/.test(fmt)) { fmt = fmt.replace( RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length) ); } let o = { "M+": date.getMonth() + 1, "d+": date.getDate(), "h+": date.getHours(), "m+": date.getMinutes(), "s+": date.getSeconds(), }; for (let k in o) { if (new RegExp(`(${k})`).test(fmt)) { let str = o[k] + ""; fmt = fmt.replace( RegExp.$1, RegExp.$1.length === 1 ? str : ("00" + str).substr(str.length) ); } } return fmt; }, }, }; </script> <style scoped lang="scss"> .card { margin-bottom: 10px; } .banner { img { height: 150px; width: 100%; } } .info { line-height: 1.8; .title { font-size: large; } .tag { font-size: small; } .abstract { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; font-size: 16px; } } </style> ~~~ # 代码解释 ## 1、数组排序 为了让新闻按照时间倒序排列,在拿到数据时候实行本地排序,数组排序需要对排序中的比较函数进行重载,定义自己的实现比价大小的逻辑 ~~~ //对数据排序 this.tableData.data = this.tableData.data.sort((x, y) => { return x.create_at < y.create_at ? 1 : -1; }); ~~~ ## 2、v-for指令注意事项 vuejs模板中使用v-for执行循环渲染一组数据,需要指定key属性,而且这个key需要唯一,否则会严重影响性能,一般简单的办法是使用数组下标index,使用Card组件显示内容时候,可以使用to属性绑定类似于vue-router的参数对象实现跳转链接 ~~~ <Card class="card" v-for="(item, index) of tableData.data" :key="index" :to="{ path: '/portal/news/detail', query: { id: item.id, menu: 'news' }, }" > </Card> ~~~ ## 3、async/await语法 API调用可以使用链式语法也可以使用async/await语法 使用链式调用 ~~~ getList() { news.getList(this.map).then((res) => { if (res.errno == 0) { this.tableData = res.data; //对数据排序 this.tableData.data = this.tableData.data.sort((x, y) => { return x.create_at < y.create_at ? 1 : -1; }); } else { this.$Message.error(res.errmsg); } }); }, ~~~ 使用async/await语法 ~~~ async getList() { let res = await news.getList(this.map); if (res.errno == 0) { this.tableData = res.data; } else { this.$Message.error(res.errmsg); } }, ~~~ ## 4、搜索功能 执行搜索的时候一定要重置显示的页面为第一页 ~~~ doSearch() { this.map.keywords = this.keywords; this.map.page = 1; //开启新的搜索,显示第一页内容 this.getList(); }, ~~~ ## 5、添加路由 添加新的页面一定要修改路由文件,将新页面的路由配置加进去,如果项目路由很多,还需要分目录组织路由,不同模块的路由分开组织,便于编辑和理解。 测试访问路径:http://127.0.0.1:8080/portal/news ~~~ { path: "/poral/news", name: "PoraltNews", component: () => import("../views/portal/news/index.vue"), }, ~~~ 完整的路由文件 /router/index.js ~~~ import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../views/Home.vue"; Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/about", name: "About", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ "../views/About.vue"), }, { path: "/poral/news", name: "PoraltNews", component: () => import("../views/portal/news/index.vue"), }, ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); export default router; ~~~ ## 6、过滤器的定义 过滤器的第一个参数是通过管道传递过来的 ~~~ <Tag color="orange"> {{ item.create_at | formatDate("yyyy-MM-dd") }} </Tag> ~~~ 如果需要全局注册一个filter,则需要在main.js中完成注册过程 ~~~ //注册一个全局的过滤器,用于格式化显示日期和时间 Vue.filter("formatDate", function(date, fmt = "yyyy-MM-dd") { date = new Date(date); if (/(y+)/.test(fmt)) { fmt = fmt.replace( RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length) ); } let o = { "M+": date.getMonth() + 1, "d+": date.getDate(), "h+": date.getHours(), "m+": date.getMinutes(), "s+": date.getSeconds(), }; for (let k in o) { if (new RegExp(`(${k})`).test(fmt)) { let str = o[k] + ""; fmt = fmt.replace( RegExp.$1, RegExp.$1.length === 1 ? str : ("00" + str).substr(str.length) ); } } return fmt; }); new Vue({ router, store, render: (h) => h(App), }).$mount("#app"); ~~~ 运行效果示意图: ![](https://img.kancloud.cn/43/54/43543fd159063c15f8ec36943a6c10ef_727x785.png) # 能力提升 ## 1、 新闻列表文件用到了几个自定义的组件 ~~~ NavigatorPath、ChannelHeader ~~~ 這些组件都是基础组件,实现相对比较简单,后续我们分解组件的源码 自定义组件需要在使用的时候import并在components属性中注册 ~~~ import NavigatorPath from "@/components/navigator-path"; import ChannelHeader from "@/components/channel-header"; import { news } from "@/common/api/portal"; export default { components: { NavigatorPath, ChannelHeader, }, ... ~~~ 自定义组件一般一个组件放在一个目录下,入口文件使用index.vue,即便整个组件只有一个文件也要遵循改规则,导入的时候只需要写目录的名称,不需要写index.vue,但是要注意,不能存在与目录同级的后缀名为.vue的与目录同名文件,否则会出错。 ~~~ import NavigatorPath from "@/components/navigator-path"; ~~~ 该组件使用的其他相关文件放在该组件的目录下。 ## 2、用到了ViewDesign UI库中Card、Row、Col、Input和Page组件 > ViewDesign UI库的组件通过iview-loader实现了自动加载,可以全局直接使用。