业务主要功能
-
获取所有的数据库列表
-
点击某一个数据库列表的时候,右侧分页展示数据
-
点击右侧某一条数据的时候,现实数据详情
以下是之前的页面,存在以下问题:
-
前段开发没有工程化(webpack)
-
主要功能耦合,列表,详情,(检索,重构的是为了加功能方便)
-
左侧的数据库链接是直接跳页的,分页的链接是跳页的,右侧点击的详情页却是vue-resource加载的
-
代码结构混乱,之前为了快速实现功能。所有代码写在一个文件上。难读。
功能效果图
数据列表页效果
数据详情效果
代码重构思路
-
利用compoment组件化,组件化原则:组件只需要告诉别自己什么状态,状态自治。组件不要去要求别的组件做什么
-
因为用vue就要体验spa,根据页面功能,利用vue-router配置页面路由
重构开始
-
因为功能少,所以就略过了 前段模块化开发,用webpack,参考我这篇
-
因为之前所有代码都是写在一个文件的,现在为了代码清晰易读易扩展,将前段js代码拆分为三个文件
compoments.js
对应抽离的组件
router.js
配置路由文件
show_database.js
vue实例
- 分别将页面抽成6个组件
itemsNumber
--显示数据条数
databaseLists
--左侧数据库列表
itemsLists
--右侧数据列表
pagination
--底部的分页
itemDetail
--详情页
listCompoment
--vue-router用到的组件包含itemsLists 和 pagination
- 页面路由暂时分成列表
/core/:core
和详情/detail/:id
两个路由,以后还有个检索页的路由
因为数据列表和详情的数据是独立接口获取的,这样分符合功能
(想了很久,没想到更好地想法,第一次接触vue-router,一边文档,一边coding)
重构中---踩坑--填坑
一、填坑工具
-
专注vue开发的老同学,一个电话什么疑难杂症全解决
二、遇到的那些坑
-
vue-router文档中的一些功能只有在新版本中才有效,一开始怕3.0太冒进了,就选了个2.0,一段时间后发现一些功能死活不成功如:
router.push
等。细看文档。原来是新版本特有的。所以以后看选版本要对着文档选,实在不行就选择最新的大版本,如3.0 -
vue-router中如何给组件传props?细看文档向路由组件传递 props搞定
-
vue-router中如何监听路由组件中的事件?因为路由组件listCompoment获取到数据库列表中需要向外发送找到的数据
//发送找到的数据 this.$emit('num-found',this.numFound);
我记得在官方文档通过事件向父级组件发送消息
v-on:num-found
在组件上监听是官方文档的做法。但是路由组件怎么绑定事件呢?$emit的事件是可以用$on来接受的?;然而试了不可以;
折腾了很久,直接电话老同学,得知可以直接在router-view中绑定事件监听
router-view(v-on:num-found='getnumFound')
-
如果是触发事件是包含大写的却不能成功
this.$emit(:'numFound',this.numFound);
v-on:numFound
-
。。。。。。未完待续
贴代码
-
templates/views/show_database/show_database.pug
extends ../../layouts/default include ../../mixins/footer-js include ../../mixins/search_components block css link(rel="stylesheet", href="/styles/document/document_search.css") style. .result_static { padding: 15px 0; margin-bottom: 20px; border: #BDE8F2 1px solid; background-color: #EFF6FA; text-align: center; font-size: 14px; } .result_static strong { color: #F30; font-size: 18px; font-weight: normal; padding: 0 5px; } .show_database_item{ border-bottom: 1px solid #e5e6e7; margin-top: 15px; padding-bottom: 5px; } .show_database_item h4{ color: #259; box-sizing: border-box; font-size: 14px; font-weight: bold; letter-spacing: 1px; line-height: 16px; text-align: left; } .show_database_item p{ font-weight: normal; text-align: left; word-break: break-all; color: #222; letter-spacing: 1px; font-size: 13px; } block content script(type='text/x-template',id='items_number') div.result_static 找到相关数据 strong(v-text='number || 0') | 条 script(type='text/x-template',id='database_lists') .list-group a.list-group-item(v-on:click='jumpNew(core.core._id,50,0)',href='javascript:void(0);',v-for="(core,index) in cores",v-bind:key="index", v-bind:class="{'active':core.core._id==currentId}",v-text="core.core.alias || core.core.name") script(type='text/x-template',id='items_lists') div .col-xs-12(v-for="(item,index) in data",v-bind:key="index") div.show_database_item h4 {{ parseInt(start)+(index+1)}}、 a(href='javascript:void(0);',v-on:click.prevent="showDetail(item.id)",v-text="item.name || item.Title || item.title || item.productname") p {{item | content}} script(type='text/x-template',id='item_detail') .col-xs-12.col-sm-8.col-md-9(v-if="isShowDetail") .row .col-xs-12 h5(v-on:click="backFn") a(href='javascript:void(0);') <<返回 .col-xs-12 table.table.table-bordered.table-striped(v-if="tableData.length") tbody tr(v-for="(item,index) in tableData",v-bind:key="index") td.text-center(v-text="item.name") td(v-text="item.value") script(type='text/x-template',id='pagination') div.text-center nav(aria-label="Page navigation") ul.pagination li(v-for="item in pagesList",v-bind:class="{'active':item.show}") a(v-bind:href="''+item.link") span(v-text="item.label") script(type='text/x-template',id='items_search') script(type='text/x-template',id='list_compoment') .col-xs-12.col-sm-8.col-md-9 .row items-lists(v-bind:data='data',v-bind:show-detail='showDetail',v-bind:start='start') pagination(v-bind:pages-list='pagesList') .row#app //- .patent-search-div.row if normalInfo && normalInfo.title div.patent-info.col-sm-3.text-center !{normalInfo.title} else div.patent-info.col-sm-3.text-center 检索 .patent-search-main.col-sm-9 //检索框 +searchBox(normalInfo,keyword) .col-xs-12.col-sm-4.col-md-3 items-number(v-bind:number='numFound') database-lists(v-bind:cores='cores',v-bind:current-id='currentId',v-bind:jump-new='jumpNew') //- .col-xs-12.col-sm-8.col-md-9(v-if="!isShowDetail") .row items-lists(v-bind:data='data',v-bind:show-detail='showDetail',v-bind:start='start') pagination(v-bind:pages-list='pagesList') //- item-detail(v-bind:is-show-detail='isShowDetail',v-bind:table-data='tableData',v-bind:back-fn='closeDetail') router-view(v-on:num-found='getnumFound') block page-js script(src="https://cdn.bootcss.com/vue/2.5.16/vue.js") script(src="https://cdn.bootcss.com/vue-resource/1.5.0/vue-resource.js") script(src="https://cdn.bootcss.com/vue-router/3.0.0/vue-router.js") //- script(src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js") //- script(src="https://cdn.bootcss.com/vue-resource/1.5.0/vue-resource.min.js") //- script(src="https://cdn.bootcss.com/vue-router/3.0.0/vue-router.min.js") script(src="/js/show_database/compoments.js") script(src="/js/show_database/router.js") script(src="/js/show_database/show_database.js")
-
public/js/show_database/show_database.js
var vm = new Vue({ el:'#app', data:{ name:'show_database', cores:[], currentId:'', currentCore:'', data:[], numFound:0, start:0, rows:50, pagesList:[], tableId:'', isShowDetail:false, detailData:{}, tableData:[] }, created:function () { this.$on('num-found',function (num) { console.log(num); }) }, mounted:function () { console.log(this.$router.params); // if (this.$router.params.core) { // this.currentId = this.$router.params.core; // } // if (this.$router.query.rows) { // this.rows = this.$router.query.rows; // } // if (this.$router.query.start) { // this.start = this.$router.query.rows // } if (location.search) { var search = location.search.slice(1,location.search.length); var l = search.split('&&'); for (var index = 0; index < l.length; index++) { var element = l[index]; var ll = element.split('='); if (ll.length&&ll[0]&&ll[1]&&ll[0]=='start') { this.start = ll[1]; console.log(ll[1]); } if (ll.length&&ll[0]&&ll[1]&&ll[0]=='rows') { this.rows = ll[1]; console.log(ll[1]); } if (ll.length&&ll[0]&&ll[1]&&ll[0]=='id') { this.currentId = ll[1]; console.log(ll[1]); } } } //获取所有的数据库,过滤后返回 this.$http.get('/api/core') .then(function (response) { if (response&&response.body.result) { this.cores = response.body.result.filter(function (core) { if (core.core.core&&core.core.core.indexOf('newscore')>-1) { return false; } return core.core.core; }) if (!this.currentId&&this.cores.length) { this.currentId = this.cores[0].core._id || ''; this.tableId = this.cores[0].core._id || ''; // this.getDatabaseData(this.currentId); this.$router.push({ name: 'core', params: { core: this.currentId, rows:this.rows, start:this.start }, query: { rows: this.rows, start:this.start } }) } var id = this.currentId; var tableId,currentCore; this.cores.filter(function (core) { if (core.core._id == id) { tableId = core.core._id currentCore = core; } return false; }); if (tableId) { this.tableId = tableId; } if (currentCore) { this.currentCore = currentCore; } } }).catch(function (err) { console.log(err); }); if (this.currentId) { // this.getDatabaseData(this.currentId); console.log(this.currentId,'dfdfdsfsdfsdf'); this.$router.push({ name: 'core', params: { core: this.currentId, rows:this.rows, start:this.start }, query: { rows: this.rows, start:this.start } }) } }, methods:{ //获取数据 // getDatabaseData(id){ // var id = id || this.currentId; // this.currentId = id; // var url = '/api/collection/search/'+id+'?start='+this.start+'&&rows='+this.rows; // this.$http.get(url) // .then(function (response) { // console.log(response); // this.data = response.body.result.docs; // this.numFound = response.body.result.numFound; // this.pagesList = getPageList(this.numFound,this.rows,this.start,'/show_database?id='+this.currentId) // }).catch(function (err) { // console.log(err); // }); // }, // 展示详情 //监听num-found事件 getnumFound(num){ this.numFound = num; }, showDetail(id,evt){ if (!id || !this.tableId) { return ; } var url = '/api/collection/search/'+this.tableId+'/'+id; this.$http.get(url).then(function (response) { if (response.body.success) { this.detailData = response.body.result; this.isShowDetail = true; }else{ alert(response.body.message) } }).catch(function (err) { console.log(err); }); }, closeDetail(){ this.isShowDetail = false; }, //TODO 重构 jumpNew(core,rows,start){ this.currentId = core; this.$router.push({ name: 'core', params: { core: core, rows:rows, start:start }, query: { rows: rows, start:start } }) } }, watch:{ detailData:function (nv,ov) { console.log(nv,ov); if (nv) { var arr = []; for (var key in nv) { if (nv.hasOwnProperty(key)) { var e = nv[key]; var obj = { name:'', value:'' }; obj.value= e; //获取中文名称 if (this.currentCore&&this.currentCore.schema&&this.currentCore.schema.length) { obj.name = getAlias(this.currentCore.schema,key) } arr.push(obj); } } this.tableData = arr; } } }, components:{ 'items-number':itemsNumber,//结果数目显示 'database-lists':databaseLists,//数据库列表展示 'items-lists':itemsLists,//数据列表展示 'pagination':pagination,//分页 'item-detail':itemDetail,//详情 'list-compoment':listCompoment }, router:router })
-
public/js/show_database/compoments.js
var itemsNumber = { template:'#items_number', props:['number'], }; var databaseLists = { template:'#database_lists', props:['cores','currentId','jumpNew'], }; var itemsLists = { template:'#items_lists', props:['data','showDetail','start'], filters:{ content:function (value) { var ss = ''; // console.log(value); if (!value) { return ss; } if (typeof value =='string') { return value; } if (typeof value =='object') { for(var key in value){ //获取中文名称 var name = key; if (vm.currentCore&&vm.currentCore.schema&&vm.currentCore.schema.length) { name = getAlias(vm.currentCore.schema,key) // console.log(name); } if (value.hasOwnProperty(key) === true && key!='id'){ var s = name+':'+value[key]+';' ss+=s; } } } if(ss&&ss.length>200){ ss = ss.slice(0,200)+'...' } return ss; } } }; var pagination = { template:'#pagination', props:['pagesList'] }; var itemDetail = { template:'#item_detail', props:['isShowDetail','tableData','backFn'] } var listCompoment = { template:'#list_compoment', // props:['data','showDetail','start','pagination'], props:['core','rows','start'], components:{ 'pagination':pagination,//分页 'item-detail':itemDetail,//详情 'items-lists':itemsLists }, data:function () { return { data:[], showDetail:false, pagination:[], numFound:0, currentId:'', pagesList:[] } }, methods:{ //获取数据 getDatabaseData(id){ var id = id || this.currentId; this.currentId = id; var url = '/api/collection/search/'+id+'?start='+this.start+'&&rows='+this.rows; this.$http.get(url) .then(function (response) { // console.log(response); this.data = response.body.result.docs; this.numFound = response.body.result.numFound; //发送找到的数据 this.$emit('num-found',this.numFound); this.pagesList = getPageList(this.numFound,this.rows,this.start,'/show_database?id='+this.currentId) }).catch(function (err) { console.log(err); }); } }, created:function () { console.log(this.$route.params) // console.log(this.core,this.rows,this.start) this.getDatabaseData(this.core); }, watch:{ '$route':function (to, from) { this.getDatabaseData(this.core); console.log(to,from); } } } function getAlias(schema,name) { if (!schema || !name || !schema.length) { return ''; } var alias=name; for (var index = 0; index < schema.length; index++) { var e = schema[index]; if (e.name && e.name == name && e.alias) { alias = e.alias; break; } } return alias; } function getPageList(total, rows, start, url) { var rtn = getPagesInfo(total, rows, start); var pages = rtn.pages; var _out = []; if (rtn.currentPage != 1) { _out.push({ label: '上一页', link: url+'&&start='+rows*(rtn.previous - 1)+'&&rows='+rows // link: toSearch(query, 'start', (rtn.previous - 1) * rtn.rows) }) } if (rtn.pages.indexOf(1) < 0) { _out.push({ label: '首页', link: url // link: toSearch(query, 'start', 0) }) } pages.map((p, i) => { _out.push({ show: p == rtn.currentPage, link: p == '...' ? "" : url+'&&start='+rows*(p-1)+'&&rows='+rows, // toSearch(query, 'start', (p - 1) * rtn.rows), // link: p == '...' ? "" : toSearch(query, 'start', (p - 1) * rtn.rows), label: p }) }) if (rtn.pages.indexOf(rtn.totalPages) < 0) { _out.push({ label: rtn.totalPages, link: url+'&&start='+rows*(rtn.totalPages - 1)+'&&rows='+rows, // toSearch(query, 'start', (rtn.totalPages - 1) * rtn.rows) // link: toSearch(query, 'start', (rtn.totalPages - 1) * rtn.rows) }) } if (rtn.currentPage != rtn.totalPages) { _out.push({ label: '下一页', link: url+'&&start='+rows*(rtn.next - 1)+'&&rows='+rows, // toSearch(query, 'start', (rtn.next - 1) * rtn.rows) // link: toSearch(query, 'start', (rtn.next - 1) * rtn.rows) }) } return _out; } function getPagesInfo(total, rows, start) { total = parseInt(total); rows = parseInt(rows); start = parseInt(start); var totalPages = Math.ceil(total / rows); var currentPage = parseInt(start / rows) + 1; var pagaSize = rows; var rtn = { total: total, currentPage: currentPage, totalPages: totalPages, pages: [], previous: (currentPage > 1) ? (currentPage - 1) : false, next: (currentPage < totalPages) ? (currentPage + 1) : false, first: 0, last: totalPages * pagaSize, rows: pagaSize, }; getPages(rtn, 10); console.log(rtn); return rtn; } function getPages (options, maxPages) { var surround = Math.floor(maxPages / 2); var firstPage = maxPages ? Math.max(1, options.currentPage - surround) : 1; var padRight = Math.max(((options.currentPage - surround) - 1) * -1, 0); var lastPage = maxPages ? Math.min(options.totalPages, options.currentPage + surround + padRight) : options.totalPages; var padLeft = Math.max(((options.currentPage + surround) - lastPage), 0); options.pages = []; firstPage = Math.max(Math.min(firstPage, firstPage - padLeft), 1); for (var i = firstPage; i <= lastPage; i++) { options.pages.push(i); } if (firstPage !== 1) { options.pages.shift(); options.pages.unshift('...'); } if (lastPage !== Number(options.totalPages)) { options.pages.pop(); options.pages.push('...'); } }
-
public/js/show_database/router.js
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter) // 1. 定义(路由)组件。 // 可以从其他文件 import 进来 // var Foo = { template: '<div>foo-><router-link to="/bar">Go to Bar</router-link></div>' } // var Bar = { template: '<div>bar-><router-link to="/foo">Go to Foo</router-link></div>' } // 2. 定义路由 // 每个路由应该映射一个组件。 其中"component" 可以是 // 通过 Vue.extend() 创建的组件构造器, // 或者,只是一个组件配置对象。 // 我们晚点再讨论嵌套路由。 var routes = [ { path: '/core/:core', component: listCompoment, name:'core', props:function (route) { // console.log(route,'-----') // routes.query.rows = route.params.rows; // routes.query.start = route.params.start; return { core:route.params.core, rows:route.params.rows, start:route.params.start } } } // , // { // path: '/detail/:tableId/:itemId', // component:itemDetail, // name:'detail', // props:{ // isShowDetail:this.isShowDetail, // tableData:this.tableData, // backFn:this.closeDetail // } // } ] // 3. 创建 router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 var router = new VueRouter({ routes: routes, mode: 'history', base:'/show_database/' // routes // (缩写)相当于 routes: routes }) // 4. 创建和挂载根实例。 // 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 // var app = new Vue({ // }).$mount('#app') // 现在,应用已经启动了!