demo
by elick
HTML
<script src="https://cdn.jsdelivr.net/vue/1.0.24/vue.js"></script>
<div id="app">
<div class="container">
<div class="form-group">
<label>Search</label>
<input type="text" class="search-input" v-model="searchQuery" />
</div>
</div>
<div class="container">
<simple-grid :data-list="people" :columns="columns" :search-key="searchQuery"></simple-grid>
</div>
</div>
<template id="grid-template">
<table>
<thead>
<tr>
<th v-for="col in columns" @click="sortBy(col)">
{{ col.name | capitalize}}
</th>
<th>
Delete
</th>
</tr>
</thead>
<tbody>
<tr v-for="(index,entry) in dataList | filterBy searchKey | orderBy sortKey sortOrder">
<td v-for="col in columns">
<span v-if="col.isKey"><a href="javascript:void(0)" @click="openEditItemDialog(index,'Edit item '+entry[col.name])">{{ entry[col.name] }}</a></span>
<span v-else>{{entry[col.name]}}</span>
</td>
<td class="text-center">
<button class="btn-danger" @click="deleteItem(entry)">delete</button>
</td>
</tr>
</tbody>
</table>
<!-- 测试不用广播打开对话, test 必须是 :test.sync 同步模式 这样在子组件关闭的时候,父组件也知道状态 可能广播更科学吧 利于解耦 -->
<!-- 前面是子后面是父 :fields="columns" :fields 子 columns 父-->
<!-- @create-item="createItem" 这个绑定事件 就和其他不一样了 两个都是父东西 把父事件 create-item 绑定到 createItem 方法 -->
<!-- 又看遍文档 -@-> 在子组件上监听自定义事件(当子组件触发 “my-event” 时将调用事件处理器) -->
<!-- 其实是约定,自定义子组件有这么一个事件, 如果子组件触发,发送了这个事件, 父组件就用 **同名** 的事件处理器,处理这个事件的响应 -->
<!-- 或者这么理解吧 有助于记忆 绑定子发送的事件 @create-item 到父方法 createItem 这样就又是 子:父 -->
<modal-dialog :mode="mode" :title="title" :fields="columns" :item="item" :test.sync='test' @create-item="createItem" @update-item="updateItem">
</modal-dialog>
<div class="container">
<button class="btn" @click="openNewItemDialog('Create new item')">Create</button>
</div>
</template>
<template id="dialog-template">
<div id="dialogs">
<!-- <div class="dialog" :class="{ 'dialog-active' : test }"> -->
<div...
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Helvetica, simhei, Arial, sans-serif;
}
html {
font-size: 1rem;
}
body {
margin-top: 100px;
}
table,
td,
th {
border-collapse: collapse;
border-spacing: 0
}
table {
width: 100%;
}
td,
th {
border: 1px solid #bcbcbc;
padding: 5px 10px;
}
th {
padding: 10px;
font-weight: 400;
color: #fff;
background: #0090d3;
cursor: pointer;
}
tr:nth-of-type(odd) {
background: #fff
}
tr:nth-of-type(even) {
background: #eee
}
h1 {
font-size: 1.5rem;
margin-bottom: 2rem;
}
input {
outline: none
}
input[type=text] {
padding: 3px 6px;
font-size: 1.2rem;
border: 1px solid #ccc;
}
input[type=text]:focus {
border-color: #0090d3;
transition: .3s ease-in;
}
button {
display: inline-block;
box-sizing: border-box;
padding: 10px 30px;
background: #0090d3;
color: #fff;
border: 1px solid #0090d3;
border-radius: 3px;
outline: 0;
transition: .4s ease-out;
}
button:hover,
button:focus {
opacity: 0.8;
cursor: pointer;
transition: .15s ease-in;
}
#app {
margin: 0 auto;
max-width: 640px;
}
.btn-danger {
padding: 5px 15px;
border: 1px solid salmon;
background: salmon;
}
.btn-save {
border: 1px solid #0090d3;
background: #0090d3;
}
.btn-close {
border: 1px solid #ccc;
background: #ccc;
}
.container {
padding-left: 15px;
padding-right: 15px;
margin: 10px;
}
.search-input {
width: 80%;
}
.form-group {
margin: 10px;
}
.form-group > label {
display: inline-block;
padding-right: 1rem;
width: 5rem;
text-align: right;
}
.form-group > input,
.form-group > select {
display: inline-block;
height: 1.8rem;
line-height: 1.8rem;
}
.text-center {
text-align: center;
}
.dialog {
width: 480px;
position: fixed;
left: 50%;
top: 2em;
transform: translateX(-50%);
z-index: 2000;
visibility: hidden;
backface-visibility: hidden;
perspective: 1300px;
}
.dialog-active {
visibility:...
JavaScript
Vue.component('simple-grid', {
template: '#grid-template',
props: ['dataList', 'columns', 'searchKey'],
data: function() {
return {
sortKey: '',
sortOrder: -1,
mode: 0,
item: {},
title: '',
//currentKey: '',
test: false //测试是可行的 不过必须是同步状态
}
},
methods: {
deleteItem: function(entry) {
var data = this.dataList
data.forEach(function(item, i) {
if (item === entry) {
data.splice(i, 1)
return
}
})
},
sortBy: function(key) {
//alert(key.name)
this.sortKey = key.name;
this.sortOrder = this.sortOrder > 0 ? -1 : 1;
},
openNewItemDialog: function(title) {
//测试不用广播打开对话框 //测试是可行的 不过必须是同步状态 //广播利于解耦
this.test = true;
//对话框标题
this.title = title;
// mode = 1 表示新建模式
this.mode = 1;
// 初始化this.item //原版为空
this.item = {
sex: 'Male'
};
// 广播事件,showDialog是modal-dialog组件的一个方法,传入参数true表示显示对话框
//广播比派发简单多了 不用绕一下不错.
this.$broadcast('showDialog', true);
},
openEditItemDialog: function(index, title) {
// 对话框标题
this.title = title;
// 模式2 修改
this.mode = 2;
// 修改数据
this.item = this.dataList[index];
// 打开对话框
this.$broadcast('showDialog',true);
//修改当期key 以便保存的时候用.
this.currentKey = index;
//下面是曾经测试用的
// //终于明白API说的 不能检测是啥意思了. 这个可以这么设置,但是页面不能自动刷新
// //虽然数据变了,但是页面上不显示
// this.dataList[index] = {
// name: 'elick',
// age: 34,
// sex: 'Male'
// }
// //所以用这种方法可以更新数据数组,那么原本的那些复杂的写法真就不懂干啥用了
// this.dataList.$set(index, {
// name: 'xwiwi',
// age: '24',
// sex: 'Female'
// })
// var aa = this.dataList[index];
// console.log(aa);
//
},
createItem: function() {
if(!this.itemExists()){
alert('这是方法');
//测试不用广播关闭对话框
this.test =...