Vue2.0 + Element-UI + WebAPI實踐:簡易我的記帳系統

最近正在學習Vue2.0相關知識,正好近期餓了麼桌面端組件Element-UI發佈,便動手作了一款簡易我的記帳系統,以達到實踐及鞏固目的。前端

1.開發環境

Win10 + VS2015 + Sqlserver2008R2 + WebAPI + Dapper + Vue2.0 + Element-UIvue

2.項目解決方案概覽

 

簡單介紹下,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

 

3.前端實現

初始界面:app

 

 

1.左邊導航部分

使用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組件。

2.具體內容頁組件實現

這裏以日消費明細組件爲例來詳細介紹,裏邊囊括了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();
        }

  沒什麼特別, 根據最新頁索引或頁尺寸大小重新拉取一遍數據就好了。

 

 

4.源碼

https://github.com/KINGGUOKUN/Account/tree/master/AccountWeb

相關文章
相關標籤/搜索