业务主要功能
获取所有的数据库列表
点击某一个数据库列表的时候,右侧分页展示数据
点击右侧某一条数据的时候,现实数据详情
以下是之前的页面,存在以下问题:
前段开发没有工程化(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.0vue-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') // 现在,应用已经启动了!