最近正在學習Vue2.0相關知識,正好近期餓了麼桌面端組件Element-UI發佈,便動手作了一款簡易我的記帳系統,以達到實踐及鞏固目的。前端
Win10 + VS2015 + Sqlserver2008R2 + WebAPI + Dapper + Vue2.0 + Element-UIvue
簡單介紹下,Account是WebAPI項目,承載前端請求;Account.BLL、Account.DAL、Account.Entity不廢話;Account.Common是對Dapper及Log4net等簡單封裝;KnockoutFE是較早時候弄的一個基於Bootstrap + Knockout的前端;VueFE是主角,待會兒重點介紹。git
Account工程截圖:github
Account.Common工程截圖:vue-router
VueFE工程截圖:api
初始界面:app
使用el-menu + vue-router實現:ide
<div id="sideBar"> <!--<ul> <router-link to="/manifests" tag="li">日消費明細</router-link> <router-link to="/daily" tag="li">日消費清單</router-link> <router-link to="/monthly" tag="li">月消費清單</router-link> <router-link to="/yearly" tag="li">年消費清單</router-link> </ul>--> <el-menu default-active="manifests" theme="dark" v-bind:router="true"> <el-menu-item index="manifests">日消費明細</el-menu-item> <el-menu-item index="daily">日消費清單</el-menu-item> <el-menu-item index="monthly">月消費清單</el-menu-item> <el-menu-item index="yearly">年消費清單</el-menu-item> </el-menu> </div>
註釋掉部分是最開始時候沒有采用el-menu組件導航,而是使用了vue-router本身的路由導航。vue-resource
路由部分對應JS代碼:post
const router = new VueRouter({ routes: [ { name: "manifests", path: "/manifests", component: Manifests }, { name: "daily", path: "/daily", component: Daily }, { name: "monthly", path: "/monthly", component: Monthly }, { name: "yearly", path: "/yearly", component: Yearly } ] });
其中,Manifests、Daily、Monthly、Yearly分別表明日消費明細、日消費清單、月消費清單、年消費清單自定義Vue組件。
這裏以日消費明細組件爲例來詳細介紹,裏邊囊括了CRUD。
/// <reference path="index.js" /> /// <reference path="vue.js" /> /// <reference path="vue-resource.js" /> /// <reference path="util.js" /> const Manifests = { template: "#manifests", created: function () { this.fetchData(); }, data: function () { let currentDate = new Date(); let costValidator = (rule, value, callback) => { if (!/^[0-9]+(.[0-9]{2})?$/.test(value)) { callback(new Error("請輸入合法金額")); } else { callback(); } }; return { start: new Date(currentDate.getFullYear(), currentDate.getMonth() - 3, 1), end: new Date(), manifests: [], title: "", manifest: {}, showOperateManifest: false, isAdd: false, rules: { Date: [ { type: "date", required: true, message: "請選擇消費日期", trigger: "change" } ], Cost: [ { required: true, message: "請填寫消費金額", trigger: "blur" }, { validator: costValidator, trigger: "change" } ], Remark: [ { required: true, message: "請填寫消費明細", trigger: "blur" } ] }, pageIndex: 0, pageSize: 10, total: 0, pageSizes: [10, 20, 50, 100] } }, methods: { fetchData: function () { this.manifests = []; this.$http.get("http://localhost:1500/api/Manifests/paged", { params: { start: this.start.format("yyyy-MM-dd"), end: this.end.format("yyyy-MM-dd"), pageIndex: this.pageIndex, pageSize: this.pageSize } }) .then(response => { this.total = response.body.count; this.manifests = response.body.data; }) .catch(response => this.$alert(response.body.Message, "日消費明細", { type: "error" })); }, add: function () { this.title = "添加消費明細"; this.manifest = { ID: Guid.NewGuid().ToString("D"), Date: new Date(), Cost: "", Remark: "" }; this.isAdd = true; this.showOperateManifest = true; }, save: function () { this.$refs.formManifest.validate(valid => { if (valid) { let operateManifest = JSON.parse(JSON.stringify(this.manifest)); operateManifest.Date = this.manifest.Date.format("yyyy-MM-dd"); if (this.isAdd) { this.$http.post("http://localhost:1500/api/Manifests", operateManifest) .then(() => { this.manifests.push(operateManifest); this.showOperateManifest = false; bus.$emit("manifestChanged"); this.$message({ message: "添加成功", type: "success" }); }) .catch(err => { //console.log(err); this.$alert(err.body.Message, "添加日消費明細", { type: "error" }); }); } else { this.$http.put("http://localhost:1500/api/Manifests", operateManifest) .then(response => { let updatedManifest = this.manifests.find(x => x.ID == this.manifest.ID); updatedManifest.Date = operateManifest.Date; updatedManifest.Cost = operateManifest.Cost; updatedManifest.Remark = operateManifest.Remark; this.showOperateManifest = false; bus.$emit("manifestChanged"); this.$message({ message: "修改爲功", type: "success" }); }) .catch(err => { //console.log(err); this.$alert(err.body.Message, "修改消費明細", { type: "error" }); }); } } else { return false; } }); }, cancel: function () { this.manifest = {}; this.showOperateManifest = false; }, edit: function (ID) { let currentManifest = this.manifests.find(x => x.ID == ID); this.manifest = JSON.parse(JSON.stringify(currentManifest)); this.manifest.Date = new Date(this.manifest.Date); this.title = "編輯消費明細"; this.isAdd = false; this.showOperateManifest = true; }, del: function (ID) { this.$confirm("是否刪除?", "警告", { type: "warning" }) .then(() => { this.$http.delete("http://localhost:1500/api/Manifests/" + ID) .then(response => { let index = this.manifests.findIndex(x => x.ID == ID); this.manifests.splice(index, 1); bus.$emit("manifestChanged"); this.$message({ message: "刪除成功", type: "success" }); }) .catch(err => { this.$alert(err.body.Message, "刪除消費明細", { type: "error" }); //console.log(err); }); }); }, dialogClosed: function () { this.$refs.formManifest.resetFields(); }, sizeChange: function (pageSize) { this.pageSize = pageSize; this.fetchData(); }, pageIndexChange: function (pageIndex) { this.pageIndex = pageIndex; this.fetchData(); } } }
<script type="text/x-template" id="manifests"> <div> <div> 開始日期: <el-date-picker v-model="start" type="date" placeholder="選擇日期"></el-date-picker> 結束日期: <el-date-picker v-model="end" type="date" placeholder="選擇日期"></el-date-picker> <el-button type="primary" size="small" v-on:click="fetchData" icon="search">查 詢</el-button> <el-button type="primary" size="small" v-on:click="add" icon="plus">添 加</el-button> </div> <div class="table"> <el-table v-bind:data="manifests" highlight-current-row border height="500"> <el-table-column prop="Date" label="日期"></el-table-column> <el-table-column prop="Cost" label="金額"></el-table-column> <el-table-column prop="Remark" label="備註"></el-table-column> <el-table-column inline-template label="操做"> <span> <el-button type="text" size="small" v-on:click="edit(row.ID)" icon="edit">編 輯</el-button> <el-button type="text" size="small" v-on:click="del(row.ID)" icon="delete">刪 除</el-button> </span> </el-table-column> </el-table> </div> <div class="pager"> <el-pagination v-bind:current-Page="pageIndex" v-bind:page-size="pageSize" :total="total" layout="total,sizes,prev,pager,next,jumper" v-bind:page-sizes="pageSizes" v-on:size-change="sizeChange" v-on:current-change="pageIndexChange"> </el-pagination> </div> <div> <el-dialog v-bind:title="title" v-bind:close-on-click-modal="false" v-model="showOperateManifest" v-on:close="dialogClosed"> <el-form v-bind:model="manifest" v-bind:rules="rules" ref="formManifest" label-position="left" label-width="80px"> <el-form-item label="日 期" prop="Date"> <el-date-picker v-model="manifest.Date"></el-date-picker> </el-form-item> <el-form-item label="金 額" prop="Cost"> <el-input v-model="manifest.Cost"></el-input> </el-form-item> <el-form-item label="備 注" prop="Remark"> <el-input v-model="manifest.Remark"></el-input> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="save">確 定</el-button> <el-button type="primary" v-on:click="cancel">取 消</el-button> </el-form-item> </el-form> </el-dialog> </div> </div> </script>
查詢部分 :
<div> 開始日期: <el-date-picker v-model="start" type="date" placeholder="選擇日期"></el-date-picker> 結束日期: <el-date-picker v-model="end" type="date" placeholder="選擇日期"></el-date-picker> <el-button type="primary" size="small" v-on:click="fetchData" icon="search">查 詢</el-button> <el-button type="primary" size="small" v-on:click="add" icon="plus">添 加</el-button> </div>
這裏關於事件處理綁定,官網推薦簡寫的@click,但這裏沒有采用,而是使用了完整綁定V-on:click,由於考慮到之後可能會和Razor整合,@符可能會衝突
查詢JS:
fetchData: function () { this.manifests = []; this.$http.get("http://localhost:1500/api/Manifests/paged", { params: { start: this.start.format("yyyy-MM-dd"), end: this.end.format("yyyy-MM-dd"), pageIndex: this.pageIndex, pageSize: this.pageSize } }) .then(response => { this.total = response.body.count; this.manifests = response.body.data; }) .catch(response => this.$alert(response.body.Message, "日消費明細", { type: "error" })); }
API請求採用了Vue開源社區的vue-resource,簡單輕便,再搭配ES6 Promise,寫起來很順手。
添加/編輯的實現:
這裏使用了el-dialog嵌套el-form
<el-dialog v-bind:title="title" v-bind:close-on-click-modal="false" v-model="showOperateManifest" v-on:close="dialogClosed"> <el-form v-bind:model="manifest" v-bind:rules="rules" ref="formManifest" label-position="left" label-width="80px"> <el-form-item label="日 期" prop="Date"> <el-date-picker v-model="manifest.Date"></el-date-picker> </el-form-item> <el-form-item label="金 額" prop="Cost"> <el-input v-model="manifest.Cost"></el-input> </el-form-item> <el-form-item label="備 注" prop="Remark"> <el-input v-model="manifest.Remark"></el-input> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="save">確 定</el-button> <el-button type="primary" v-on:click="cancel">取 消</el-button> </el-form-item> </el-form> </el-dialog>
save: function () { this.$refs.formManifest.validate(valid => { if (valid) { let operateManifest = JSON.parse(JSON.stringify(this.manifest)); operateManifest.Date = this.manifest.Date.format("yyyy-MM-dd"); if (this.isAdd) { this.$http.post("http://localhost:1500/api/Manifests", operateManifest) .then(() => { this.manifests.push(operateManifest); this.showOperateManifest = false; bus.$emit("manifestChanged"); this.$message({ message: "添加成功", type: "success" }); }) .catch(err => { //console.log(err); this.$alert(err.body.Message, "添加日消費明細", { type: "error" }); }); } else { this.$http.put("http://localhost:1500/api/Manifests", operateManifest) .then(response => { let updatedManifest = this.manifests.find(x => x.ID == this.manifest.ID); updatedManifest.Date = operateManifest.Date; updatedManifest.Cost = operateManifest.Cost; updatedManifest.Remark = operateManifest.Remark; this.showOperateManifest = false; bus.$emit("manifestChanged"); this.$message({ message: "修改爲功", type: "success" }); }) .catch(err => { //console.log(err); this.$alert(err.body.Message, "修改消費明細", { type: "error" }); }); } } else { return false; } }); }
其中包括了表單驗證部分,也是採用Element-UI文檔中介紹的驗證方式,目前有些驗證支持不是很好,好比number,多是哪裏用的不對吧,因此上邊對金額部分採起了正則自定義驗證。
底部分頁部分:
<el-pagination v-bind:current-Page="pageIndex" v-bind:page-size="pageSize" :total="total" layout="total,sizes,prev,pager,next,jumper" v-bind:page-sizes="pageSizes" v-on:size-change="sizeChange" v-on:current-change="pageIndexChange"> </el-pagination>
如上所示,直接使用了餓了麼分頁組件,設置幾個屬性, 再綁定幾個JS屬性、分頁事件處理程序便可,十分方便
Vue Date對象中分頁相關的屬性:
頁索引及分頁大小變更事件處理:
sizeChange: function (pageSize) { this.pageSize = pageSize; this.fetchData(); }, pageIndexChange: function (pageIndex) { this.pageIndex = pageIndex; this.fetchData(); }
沒什麼特別, 根據最新頁索引或頁尺寸大小重新拉取一遍數據就好了。
https://github.com/KINGGUOKUN/Account/tree/master/AccountWeb