Vue入门之Web端CURD前端项目示例
Vue入門(mén)之Web端CURD前端項(xiàng)目示例
隨著vue.js越來(lái)越火,很多不太懂前端的小伙伴想要入坑。而入坑最好的辦法就是上手實(shí)際操作,因此本文將重點(diǎn)放在實(shí)際操作上,對(duì)理論不做過(guò)多解釋,不明白的地方小伙伴們可以去看官方文檔或者相關(guān)書(shū)籍。
目錄
- 一、安裝npm
- 二、安裝vue-cli
- 三、使用vue-cli創(chuàng)建項(xiàng)目
- 四、修改項(xiàng)目配置
- 五、啟動(dòng)項(xiàng)目
- 六、安裝依賴
- 七、建立基本視圖框架
- 八、完善CURD功能
- 九、總結(jié)
本文的示例項(xiàng)目操作環(huán)境如下:
| 操作系統(tǒng) | Mac OS 10.13.6 (17G65) |
| 瀏覽器 | Chrome 77.0.3865.120 |
| nvm下nodejs版本 | 10.13.0 |
| 編輯器 | Visual Studio Code 1.39.2 |
一、安裝npm
npm有多種安裝方式,這里推薦使用nvm進(jìn)行安裝。
1.1 安裝nvm
- windows
地址:https://github.com/coreybutler/nvm-windows/releases
根據(jù)文檔說(shuō)明安裝,記得設(shè)置淘寶的鏡像。
nvm node_mirror https://npm.taobao.org/mirrors/node/ nvm npm_mirror https://npm.taobao.org/mirrors/npm/或者修改settings.txt文件,在后面加上淘寶鏡像設(shè)置。
node_mirror: https://npm.taobao.org/mirrors/node/ npm_mirror: https://npm.taobao.org/mirrors/npm/- linux/mac
地址:https://github.com/creationix/nvm
根據(jù)文檔說(shuō)明安裝,記得設(shè)置淘寶的鏡像。
# bash_profile export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node1.2 配置npm
npm config set registry https://registry.npm.taobao.org npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass1.3 安裝yarn
npm install -g yarn1.4 配置yarn
已配置過(guò)npm則無(wú)需再次配置。
yarn config set registry https://registry.npm.taobao.org -g yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g二、安裝vue-cli
細(xì)心的小伙伴會(huì)發(fā)現(xiàn)vue官網(wǎng)是不推薦入門(mén)直接上vue-cli的,這里考慮到很多小伙伴不是前端工程師,而僅僅是需要了解vue項(xiàng)目的基本結(jié)構(gòu),所以直接使用vue-cli創(chuàng)建項(xiàng)目吧。
使用npm或者yarn全局安裝vue-cli:
注意:這里的@vue/cli不要寫(xiě)成了vue-cli,vue-cli是舊版本。
npm install -g @vue/cli # OR yarn global add @vue/cli三、使用vue-cli創(chuàng)建項(xiàng)目
現(xiàn)在可以開(kāi)始使用vue-cli創(chuàng)建項(xiàng)目了,這里項(xiàng)目名稱(chēng)為example-curd。
這里也可以通過(guò)vue ui來(lái)創(chuàng)建,使用方法是控制臺(tái)輸入vue ui,具體內(nèi)容本文不做展示
vue create example-curd這時(shí)會(huì)有一個(gè)選項(xiàng),第一項(xiàng)為默認(rèn)配置,這里我們選擇第二項(xiàng)手動(dòng)配置,回車(chē)確定。
之后會(huì)出現(xiàn)一個(gè)多選,這時(shí)的操作是空格鍵選擇,空格選擇完所有需要的配置之后,最后按回車(chē)確定。
本文用于演示基本的項(xiàng)目流程,只選擇3項(xiàng),其它項(xiàng)小伙伴們自行查閱資料按需選擇
選好依賴項(xiàng)后,進(jìn)入vue-router設(shè)置提示,是否使用history mode,輸入n然后回車(chē)確定。
這里的區(qū)別小伙伴們自己查閱資料。
然后會(huì)問(wèn)如何處理Babel、ESLint之類(lèi)的配置文件,這里為了便于修改,不與package.json混在一起,選擇第一項(xiàng)。
最后問(wèn)你是否記錄下這套配置,由于本文只是演示,選擇的配置項(xiàng)并不適合正式項(xiàng)目,所以選擇不記錄。
選擇包管理器,這里推薦使用yarn。
項(xiàng)目配置完成。
四、修改項(xiàng)目配置
生成的項(xiàng)目目錄結(jié)構(gòu)如圖:
為了避免默認(rèn)端口被占用的情況,我們新建一個(gè)名為vue.config.js的配置文件,指定host與port。配置參考
五、啟動(dòng)項(xiàng)目
可以在package.json中修改腳本命令,例如增加一個(gè)與serve功能相同的dev命令:
{"name": "example-curd","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build","dev": "vue-cli-service serve"},"dependencies": {"core-js": "^3.1.2","vue": "^2.6.10","vue-router": "^3.0.6","vuex": "^3.0.1"},"devDependencies": {"@vue/cli-plugin-babel": "^4.0.0","@vue/cli-plugin-router": "^4.0.0","@vue/cli-plugin-vuex": "^4.0.0","@vue/cli-service": "^4.0.0","vue-template-compiler": "^2.6.10"} }啟動(dòng)項(xiàng)目
yarn dev至此,基本的項(xiàng)目創(chuàng)建與啟動(dòng)演示完畢,接下來(lái)進(jìn)入實(shí)例演示。
六、安裝依賴
本文主要目的是演示創(chuàng)建基本CURD的前端WEB頁(yè)面,因此不做后端API的創(chuàng)建,這里使用mock.js攔截請(qǐng)求并返回模擬數(shù)據(jù)。
本文需要用到的依賴有:
- element-ui
- mockjs
- axios
使用yarn安裝以上依賴
如果已經(jīng)啟動(dòng)了項(xiàng)目,可以先ctrl + c退出,安裝完依賴后再啟動(dòng)。
yarn add element-ui mockjs axios七、建立基本視圖框架
修改App.vue、main.js、router.js,同時(shí)刪除項(xiàng)目創(chuàng)建時(shí)的默認(rèn)文件About.vue、Home.vue、HelloWorld.vue,在src/views目錄下創(chuàng)建一個(gè)layout目錄和page目錄,分別用于存放布局和頁(yè)面。
7.1 修改main.js文件
引入element組件庫(kù)及其樣式
這里的@在vue-cli默認(rèn)語(yǔ)法中代表項(xiàng)目中的src目錄
src/main.js:
import Vue from 'vue'; import App from '@/App.vue'; import router from '@/router'; import store from '@/store'; import ElementUI from 'element-ui'; // ElementUI組件庫(kù) import 'element-ui/packages/theme-chalk/lib/index.css'; // ElementUI組件庫(kù)樣式// 注冊(cè)餓了么UI Vue.use(ElementUI);new Vue({router,store,render: h => h(App) }).$mount('#app')7.2 修改App.vue
同時(shí)調(diào)整一些必要的全局html樣式。
src/App.vue:
<template><div id="app"><router-view></router-view></div> </template><style> html, body {padding: 0;margin: 0; } #app {font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,Microsoft YaHei, SimSun, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale; } a:focus, a:active {outline: none; }a, a:focus, a:hover {cursor: pointer;color: inherit;text-decoration: none; } </style>7.3 創(chuàng)建布局文件
在layout目錄下創(chuàng)建一個(gè)default.vue作為默認(rèn)布局文件,初學(xué)可以使用element的Container 布局容器組件。
src/views/layout/default.vue:
<template><el-container><el-aside width="200px">Aside</el-aside><el-container><el-header>Header</el-header><el-main><router-view></router-view></el-main></el-container></el-container> </template>7.4 修改router
指定src/views/page/index.vue為本項(xiàng)目默認(rèn)首頁(yè),且作用于默認(rèn)布局下。
() => import(‘xxx’) 為動(dòng)態(tài)加載組件,這樣做的好處是用到哪個(gè)頁(yè)面加載哪個(gè)頁(yè)面,避免導(dǎo)致首頁(yè)加載時(shí)間過(guò)長(zhǎng)。
src/router/index.js:
import Vue from 'vue' import VueRouter from 'vue-router' import DefaultLayout from '@/views/layout/default'Vue.use(VueRouter)const routes = [{path: '',component: DefaultLayout,redirect: 'index',children: [{path: 'index',name: 'index',component: () => import('@/views/page/index'),}],}, ]const router = new VueRouter({routes })export default router;7.5 修改index.vue
修改首頁(yè)內(nèi)容,測(cè)試布局是否生效。
src/views/page/index.vue:
<template><div><el-button type="primary">首頁(yè)的按鈕</el-button></div> </template>7.6 查看基本布局是否生效
可以看到首頁(yè)的ElementUI的按鈕組件正常顯示,且首頁(yè)位于默認(rèn)布局之下。
此時(shí)項(xiàng)目結(jié)構(gòu)如下:
八、完善CURD功能
最后只剩下界面優(yōu)化及基本的業(yè)務(wù)功能頁(yè)面了。
8.1 優(yōu)化界面
增加左側(cè)菜單以及右側(cè)上方的導(dǎo)航欄。可以使用ElementUI的NavMenu 導(dǎo)航菜單組件來(lái)實(shí)現(xiàn)。
此時(shí)可以將菜單欄和標(biāo)題欄拆分成獨(dú)立組件引入默認(rèn)布局,創(chuàng)建src/views/layout/components目錄并新建header.vue及menu.vue。
此時(shí)我們希望菜單根據(jù)router配置自動(dòng)生成,所以需要修改router,可以將router配置單獨(dú)提取成routes.js便于其它頁(yè)面調(diào)用。同時(shí)新建一個(gè)CURD頁(yè)面curd.vue。
[標(biāo)題欄] src/views/layout/components/header.vue:
<template><div class="layout-header"><i class="icon-collapse" :class="`el-icon-s-${collapse ? 'unfold' : 'fold'}`" @click="collapseMenu"></i><div v-if="$route.name === 'index'" class="el-page-header"><div class="el-page-header__content">首頁(yè)</div></div><el-page-header v-else @back="goBack" :content="content"></el-page-header></div> </template><script> export default {props: {collapse: {type: Boolean,}},data: () => ({content: ''}),watch: {$route: {handler(to) {this.content = to.meta.title;},immediate: true}},methods: {goBack() {this.$router.back();},collapseMenu() {this.$emit('collapse-menu');}}, } </script><style> .layout-header {position: relative;height: 100%;padding: 0 20px;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);display: flex;align-items: center; } .layout-header .icon-collapse {cursor: pointer;font-size: 20px;padding-right: 20px; } </style>[導(dǎo)航菜單] src/views/layout/components/menu.vue:
<template><el-menuref="menu"class="layout-menu"background-color="#001529"text-color="#fff"active-text-color="#1890ff":collapse="collapse":default-active="$route.name"><router-link v-for="(menu, index) in menus" :key="index" :to="menu.path"><el-menu-item :index="menu.name"><i :class="`el-icon-${menu.meta.icon || 'menu'}`"></i><span slot="title">{{ menu.meta.title }}</span></el-menu-item></router-link></el-menu> </template><script> import routes from '@/router/routes';export default {props: {collapse: Boolean},computed: {menus() {return routes[0].children || [];}}, } </script><style> .layout-menu {height: 100%;width: 100%; } </style>[路由設(shè)置] src/router/index.js:
import Vue from 'vue' import VueRouter from 'vue-router' import routes from './routes'Vue.use(VueRouter)const router = new VueRouter({routes })export default router;[路由表] src/router/routes.js:
import DefaultLayout from '@/views/layout/default';export default [{path: '',component: DefaultLayout,redirect: 'index',children: [{path: 'index',name: 'index',component: () => import('@/views/page/index'),meta: { title: '首頁(yè)', icon: 's-home' }},{path: 'curd',name: 'curd',component: () => import('@/views/page/curd'),meta: { title: '增刪改查', icon: 's-opportunity' }}],}, ]src/views/page/curd.vue:
<template><div>CURD頁(yè)面</div> </template>src/views/layout/default.vue
<template><el-container class="layout-default"><el-aside class="layout-default__menu" :style="{ width: `${collapse ? 64 : 256}px` }"><layout-menu :collapse="collapse"></layout-menu></el-aside><el-container class="layout-default__main" :style="{ 'margin-left': `${collapse ? 64 : 256}px` }"><el-header><layout-header @collapse-menu="collapse = !collapse" :collapse="collapse"></layout-header></el-header><el-main><router-view></router-view></el-main></el-container></el-container> </template><script> import LayoutMenu from './components/menu'; import LayoutHeader from './components/header';export default {components: { LayoutMenu, LayoutHeader },data: () => ({collapse: false,}), } </script><style> .layout-default .el-header {padding: 0; } .layout-default__menu {position: fixed;overflow-x: hidden;background-color: #001529;transition: width 300ms;overflow-x: hidden !important;height: 100vh; } .layout-default__main {transition: margin 300ms; } .layout-default__menu, .layout-default__menu .el-menu {border: 0 !important; } </style>此時(shí)的目錄結(jié)構(gòu)是這樣的:
這時(shí)可以看到頁(yè)面已經(jīng)有了較為美觀的樣式:
且菜單支持收起和展開(kāi):
8.2 修改curd頁(yè)面結(jié)構(gòu)
一個(gè)基本的CURD頁(yè)面應(yīng)該包括:查詢條件、操作按鈕、數(shù)據(jù)列表、列表分頁(yè)、編輯表單、詳情展示等幾個(gè)基本模塊。
因此我們可以修改原有的curd.vue為一個(gè)curd目錄,目錄下包括index.vue及其相關(guān)組件。由于表單頁(yè)面相對(duì)而言重復(fù)性較高,代碼較為冗長(zhǎng),所以我們將其拆分為index.vue、search.vue、form.vue三個(gè)組件,其中index.vue為列表分頁(yè)展示及數(shù)據(jù)控制,search.vue和form.vue為表單組件。
search.vue搜索表單我們可以參照element的行內(nèi)表單示例進(jìn)行修改:
src/views/curd/search.vue:
<template><el-card shadow="never"><el-form inline :model="model" size="small"><el-form-item label="姓名"><el-input v-model="model.name" placeholder="請(qǐng)輸入姓名"></el-input></el-form-item><el-form-item label="性別"><el-select v-model="model.gender" placeholder="請(qǐng)選擇性別"><el-option label="小姐姐" value="famale"></el-option><el-option label="小哥哥" value="male"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="handleSubmit">查詢</el-button><el-button plain @click="handleReset">重置</el-button></el-form-item></el-form></el-card> </template><script>export default {props: {value: {type: Object,default: () => {return {};}},},data: () => ({model: {}}),watch: {value: {handler(val) {if (val) {Object.keys(this.model).forEach(key => {this.model[key] = val[key];});} else {Object.keys(this.model).forEach(key => {this.model[key] = null;});}},immediate: true,},model: {handler(val) {this.$emit("input", val);},deep: true}},methods: {handleSubmit() {this.$emit('search', this.model);},handleReset() {Object.keys(this.model).forEach(key => {this.model[key] = null;});}}} </script>form.vue搜索表單我們可以參照element的表單驗(yàn)證示例進(jìn)行修改:
src/views/curd/form.vue:
<template><el-form ref="form" :model="model" :rules="rules" size="small" label-width="100px"><el-form-item label="姓名" prop="name"><el-input v-model="model.name"></el-input></el-form-item><el-form-item label="性別" prop="gender"><el-select v-model="model.gender" placeholder="請(qǐng)選擇性別"><el-option label="小姐姐" value="famale"></el-option><el-option label="小哥哥" value="male"></el-option></el-select></el-form-item><el-form-item label="年齡" prop="age"><el-input-number v-model="model.age"></el-input-number></el-form-item><el-form-item><el-button type="primary" size="small" @click="handleSubmit">確定</el-button><el-button plain size="small" @click="handleCancel" style="margin-left: 8px">取消</el-button></el-form-item></el-form> </template><script> export default {props: {value: {type: Object,default: () => {return {};}},},data: () => ({model: {name: '',gender: null,age: 18,},rules: {name: [{ required: true, message: '請(qǐng)輸入姓名', trigger: 'blur' }],gender: [{ required: true, message: '請(qǐng)選擇性別', trigger: 'change' }],age: [{ required: true, message: '請(qǐng)輸入年齡', trigger: 'change' }],}}),watch: {value: {handler(val) {if (val) {Object.keys(this.model).forEach(key => {this.model[key] = val[key];});} else {Object.keys(this.model).forEach(key => {this.model[key] = null;});}},immediate: true,},model: {handler(val) {this.$emit("input", val);},deep: true}},methods: {// 點(diǎn)擊確定提交表單的操作handleSubmit(name) {this.$refs.form.validate(valid => {if (valid) {const result = this.submitPure ? this.getPureModel() : JSON.parse(JSON.stringify(this.model));this.$emit("submit", result);}});},// 校驗(yàn)表單validate() {this.$refs.form.validate(valid => {this.$emit("validate", valid);});},// 重置表單reset() {Object.keys(this.model).forEach(key => {this.model[key] = undefined;});this.$nextTick(() => {this.$refs.form.clearValidate();});},// 點(diǎn)擊取消的操作handleCancel() {this.$emit("cancel");}} }; </script>index.vue則是對(duì)以上組件的整合以及列表數(shù)據(jù)的展示操作。
src/views/curd/index.vue:
<template><div class="page-curd"><curd-search @search="handleSearch"></curd-search><div class="page-curd__action-bar"><el-button type="primary" icon="el-icon-plus" size="small" @click="handleNew">新增</el-button><el-button icon="el-icon-delete" size="small" :disabled="tableSelection && tableSelection.length <= 0" @click="handleDeleteMul">批量刪除</el-button></div><el-table size="mini" :data="tableData" class="page-curd__table" border @selection-change="handleTableSelectionChange"><el-table-column type="selection" min-width="35"></el-table-column><el-table-column label="姓名" prop="name" min-width="100"><template slot-scope="{ row }"><span><i class="el-icon-user-solid"></i>{{ row.name }}</span></template></el-table-column><el-table-column label="性別" prop="gender"><template slot-scope="{ row }"><el-tag v-if="row.gender === 'male'" size="small">小哥哥 ♂</el-tag><el-tag v-else type="danger" size="small">小姐姐 ♀</el-tag></template></el-table-column><el-table-column label="年齡" prop="age"></el-table-column><el-table-column label="操作" min-width="140" fixed="right"><template slot-scope="slotScope"><el-button class="eagle-scheme__table-btn" type="text" icon="el-icon-view" title="詳情" @click="handleView(slotScope)"></el-button><el-button class="eagle-scheme__table-btn" type="text" icon="el-icon-edit" title="編輯" @click="handleEdit(slotScope)"></el-button><el-button type="text" icon="el-icon-delete" title="刪除" @click="handleDelete(slotScope)"></el-button></template></el-table-column></el-table><el-paginationclass="page-curd__pagination"@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[10, 20, 50, 100]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"background></el-pagination><el-dialog :visible.sync="dialogVisible" :title="title" width="450px" :close-on-click-modal="dialogMode === 'view'" append-to-body><template v-if="dialogMode === 'view'"><el-row :gutter="20"><el-col :span="12">姓名:{{ formModel.name }}</el-col><el-col :span="12">性別:<template v-if="formModel.gender"><el-tag v-if="formModel.gender === 'male'" size="small">小哥哥 ♂</el-tag><el-tag v-else type="danger" size="small">小姐姐 ♀</el-tag></template></el-col><el-col :span="12" style="margin-top: 20px;">年齡:{{ formModel.age }}</el-col></el-row></template><template v-else><curd-form ref="curdForm" v-model="formModel" @submit="handleSubmit" @cancel="handleCancel"></curd-form></template></el-dialog></div> </template><script> import CurdSearch from './search'; import CurdForm from './form';export default {components: { CurdSearch, CurdForm },data: () => ({dialogMode: 'new',dialogVisible: false,tableData: [{ name: '古力娜扎', age: 27, gender: 'famale' },{ name: '迪麗熱巴', age: 27, gender: 'famale' },{ name: '尼格買(mǎi)提', age: 36, gender: 'male' },],currentPage: 1,pageSize: 10,total: 3,tableSelection: [],formModel: {}}),computed: {title() {if (this.dialogMode === 'view') {return '查看';}return this.dialogMode === 'edit' ? '編輯' : '新增';}},methods: {async handleSearch(value, needReset) {if (needReset) {this.currentPage = 1;}const param = {...value,currentPage: this.currentPage,pageSize: this.pageSize,};console.log(param);},handleNew() {if (this.$refs['curdForm']) { this.$refs['curdForm'].reset(); }this.dialogMode = 'new';this.dialogVisible = true;this.formModel = {};},handleView({ row }) {this.dialogMode = 'view';this.dialogVisible = true;this.formModel = { ...row };},handleEdit({ row }) {if (this.$refs['curdForm']) { this.$refs['curdForm'].reset(); }this.dialogMode = 'edit';this.dialogVisible = true;this.formModel = { ...row };},handleDelete({ row }) {console.log('delete:', row);},handleDeleteMul() {console.log('mul delete:', this.tableSelection);},handleSubmit() {if (this.dialogMode === 'new') {console.log('new', this.formModel);} else if (this.dialogMode === 'edit') {console.log('edit', this.formModel);}},handleCancel() {this.dialogVisible = false;},handleTableSelectionChange(selection) {this.tableSelection = selection;},handleSizeChange(val) {this.currentPage = 1;this.pageSize = val;this.handleSearch();},handleCurrentChange(val) {this.currentPage = val;this.handleSearch();},} } </script><style> .page-curd__action-bar {margin: 20px 0px; } .page-curd__table {margin-bottom: 20px; } .page-curd .el-icon-user-solid {padding-right: 10px; } .page-curd__pagination {text-align: right; } </style>假如你有強(qiáng)迫癥的話,那就順便把首頁(yè)略微修改一下。
src/views/page/index.vue:
<template><div class="page-index">Vue入門(mén)項(xiàng)目CURD示例</div> </template><style> .page-index {text-align: center;padding-top: calc(50vh - 60px - 24px);font-size: 24px; } </style>此時(shí)的項(xiàng)目效果如下:
首頁(yè)
CURD頁(yè)面
新增
編輯
查看詳情
8.3 模擬接口數(shù)據(jù)
本示例項(xiàng)目為純前端項(xiàng)目,所以使用mockjs來(lái)模擬增刪改查的基本API。
在項(xiàng)目中新建一個(gè)mock目錄用于存放模擬mockjs配置文件。包括index.js、util.js、module/user.js。
src/mock/index.js:
import Mock from 'mockjs'; import { parseUrl } from './util'; import userAPI from './module/user';// 臨時(shí)緩存mockjs數(shù)據(jù) let MockCache = {};const MockBot = {// 通用模板APIfastAPI: {// 獲取數(shù)據(jù)列表page: (base) => config => {const list = MockCache[base] || [];const param = parseUrl(config.url) || {};const { page = 1, size = 10, ...query } = param;// 計(jì)算有幾個(gè)搜索條件let queryCount = false;for (let key in query) {if (query[key]) {queryCount += 1;break;}}// 根據(jù)搜索條件過(guò)濾結(jié)果const filteredList = queryCount > 0 ? list.filter(data => {let result = false;for (let key in query) {if (data[key] === query[key]) {result = true;break;}}return result;}) : list;// 根據(jù)結(jié)果處理分頁(yè)數(shù)據(jù)const _page = Number(page);const _limit = Number(size);const pageList = filteredList.filter((item, index) => index < _limit * _page && index >= _limit * (_page - 1));const response = {page: _page,size: _limit,result: {list: pageList,total: filteredList.length,},success: true,};return response;},// 查詢數(shù)據(jù)詳情get: (base) => config => {const list = MockCache[base] || [];const param = parseUrl(config.url) || {};const id = param.id;const result = list.find((item) => item.id == id);return {result: result,success: true,};},// 新增數(shù)據(jù)add: (base) => config => {const param = JSON.parse(config.body);MockCache[base].unshift(param);return {success: true,};},// 編輯數(shù)據(jù)update: (base) => config => {const param = JSON.parse(config.body);const index = MockCache[base].findIndex(item => item.id === param.id);MockCache[base][index] = param;return {success: true,};},// 刪除數(shù)據(jù)delete: (base) => config => {const ids = JSON.parse(config.body);ids.forEach(id => {MockCache[base] = MockCache[base].filter(item => item.id !== id);});return {success: true,};}},// 根據(jù)通用模板API快速創(chuàng)建模擬接口fastMock: (url, list) => {MockCache[url] = list;Mock.mock(new RegExp(`\/${url}\/page`), 'get', MockBot.fastAPI.page(url));Mock.mock(new RegExp(`\/${url}\/get`), 'get', MockBot.fastAPI.get(url));Mock.mock(new RegExp(`\/${url}\/add`), 'post', MockBot.fastAPI.add(url));Mock.mock(new RegExp(`\/${url}\/update`), 'post', MockBot.fastAPI.update(url));Mock.mock(new RegExp(`\/${url}\/delete`), 'post', MockBot.fastAPI.delete(url));}, }// 產(chǎn)品管理 MockBot.fastMock('user', userAPI.userList);src/mock/util.js:
import Mock from 'mockjs';export const parseUrl = (url) => {let obj = {};// 創(chuàng)建一個(gè)Objectlet reg = /[?&][^?&]+=[^?&]+/g;// 正則匹配 ?&開(kāi)始 =拼接 非?&結(jié)束 的參數(shù)let arr = url.match(reg);// match() 方法可在字符串內(nèi)檢索指定的值,或找到一個(gè)或多個(gè)正則表達(dá)式的匹配。// arr數(shù)組形式 ['?id=12345','&a=b']if (arr) {arr.forEach((item) => {/*** tempArr數(shù)組 ['id','12345']和['a','b']* 第一個(gè)是key,第二個(gè)是value* */let tempArr = item.substring(1).split('=');let key = decodeURIComponent(tempArr[0]);let val = decodeURIComponent(tempArr[1]);obj[key] = val;});}return obj; }export default {// 根據(jù)配置文件和數(shù)量快速生成數(shù)據(jù)列表fastList: (config, count) => {const list = []for (let i = 0; i < count; i++) {list.push(Mock.mock(config));}return list;}, }src/mock/module/user.js:
import MockUtil from '../util';export default {// 用戶列表userList: MockUtil.fastList({id: '@guid()',name: '@cname()','gender|1': ['male', 'famale'],'age|16-40': 1,}, 43), }8.4 對(duì)接接口數(shù)據(jù)
模擬好接口,接下來(lái)就需要進(jìn)行接口對(duì)接了,首先我們修改main.js,將axios創(chuàng)建一個(gè)實(shí)例并設(shè)置為Vue的prototype,這樣可以在每個(gè)Vue頁(yè)面或組件中直接通過(guò)this進(jìn)行訪問(wèn)。
創(chuàng)建axios實(shí)例后可以設(shè)置request和response攔截器,具體用法請(qǐng)查看axios官方文檔
src/main.js:
import Vue from 'vue'; import axios from 'axios'; import App from '@/App.vue'; import router from '@/router'; import store from '@/store'; import ElementUI from 'element-ui'; // ElementUI組件庫(kù) import 'element-ui/packages/theme-chalk/lib/index.css'; // ElementUI組件庫(kù)樣式 import '@/mock';const request = axios.create({baseURL: 'http://localhost',timeout: 1000 * 60,withCredentials: true, });// respone 攔截器 request.interceptors.response.use(response => {const { data = {} } = response;const { success } = data;if (success) {return data;} else {return { success };}},error => {return { success: false };});Vue.prototype.$request = request;// 注冊(cè)餓了么UI Vue.use(ElementUI);new Vue({router,store,render: h => h(App) }).$mount('#app')配置好axios之后,我們就可以在curd頁(yè)面中進(jìn)行對(duì)接了。
src/views/page/curd/index.vue:
<template><div class="page-curd"><curd-search @search="handleSearch"></curd-search><div class="page-curd__action-bar"><el-button type="primary" icon="el-icon-plus" size="small" @click="handleNew">新增</el-button><el-button icon="el-icon-delete" size="small" :disabled="tableSelection && tableSelection.length <= 0" @click="handleDeleteMul">批量刪除</el-button></div><el-table size="mini" :data="tableData" class="page-curd__table" border @selection-change="handleTableSelectionChange"><el-table-column type="selection" min-width="35"></el-table-column><el-table-column label="姓名" prop="name" min-width="100"><template slot-scope="{ row }"><span><i class="el-icon-user-solid"></i>{{ row.name }}</span></template></el-table-column><el-table-column label="性別" prop="gender"><template slot-scope="{ row }"><el-tag v-if="row.gender === 'male'" size="small">小哥哥 ♂</el-tag><el-tag v-else type="danger" size="small">小姐姐 ♀</el-tag></template></el-table-column><el-table-column label="年齡" prop="age"></el-table-column><el-table-column label="操作" min-width="140" fixed="right"><template slot-scope="slotScope"><el-button class="eagle-scheme__table-btn" type="text" icon="el-icon-view" title="詳情" @click="handleView(slotScope)"></el-button><el-button class="eagle-scheme__table-btn" type="text" icon="el-icon-edit" title="編輯" @click="handleEdit(slotScope)"></el-button><el-button type="text" icon="el-icon-delete" title="刪除" @click="handleDelete(slotScope)"></el-button></template></el-table-column></el-table><el-paginationclass="page-curd__pagination"@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[10, 20, 50, 100]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"background></el-pagination><el-dialog :visible.sync="dialogVisible" :title="title" width="450px" :close-on-click-modal="dialogMode === 'view'" append-to-body><template v-if="dialogMode === 'view'"><el-row :gutter="20"><el-col :span="12">姓名:{{ formModel.name }}</el-col><el-col :span="12">性別:<template v-if="formModel.gender"><el-tag v-if="formModel.gender === 'male'" size="small">小哥哥 ♂</el-tag><el-tag v-else type="danger" size="small">小姐姐 ♀</el-tag></template></el-col><el-col :span="12" style="margin-top: 20px;">年齡:{{ formModel.age }}</el-col></el-row></template><template v-else><curd-form ref="curdForm" v-model="formModel" @submit="handleSubmit" @cancel="handleCancel"></curd-form></template></el-dialog></div> </template><script> import CurdSearch from './search'; import CurdForm from './form';export default {components: { CurdSearch, CurdForm },data: () => ({dialogMode: 'new',dialogVisible: false,tableData: [],currentPage: 1,pageSize: 10,total: 3,tableSelection: [],formModel: {},currentRow: {},}),computed: {title() {if (this.dialogMode === 'view') {return '查看';}return this.dialogMode === 'edit' ? '編輯' : '新增';}},created() {this.handleSearch();},methods: {async handleSearch(value, needReset) {if (needReset) {this.currentPage = 1;}const params = {...value,page: this.currentPage,size: this.pageSize,};this.$request.get('/user/page', { params }).then(response => {const { result: { list, total } = {} } = response;this.tableData = list;this.total = total;});},handleNew() {if (this.$refs['curdForm']) { this.$refs['curdForm'].reset(); }this.dialogMode = 'new';this.dialogVisible = true;this.formModel = {};},handleView({ row }) {this.dialogMode = 'view';this.dialogVisible = true;this.formModel = { ...row };},handleEdit({ row }) {if (this.$refs['curdForm']) { this.$refs['curdForm'].reset(); }this.dialogMode = 'edit';this.$request.get('/user/get', { params: { id: row.id } }).then(response => {const { result } = response;this.formModel = result;this.currentRow = result;this.dialogVisible = true;});},handleDelete({ row }) {this.$request.post('/user/delete', [row.id]).then(response => {const { success } = response;if (success) {this.$message.success('刪除成功');this.handleSearch();}});},handleDeleteMul() {const ids = this.tableSelection.map(selection => selection.id);this.$request.post('/user/delete', ids).then(response => {const { success } = response;if (success) {this.$message.success('刪除成功');this.handleSearch();}});},handleSubmit() {if (this.dialogMode === 'new') {this.$request.post('/user/add', { ...this.formModel, id: undefined }).then(response => {const { success } = response;if (success) {this.$message.success('新增成功');this.handleSearch();this.dialogVisible = false;}});} else if (this.dialogMode === 'edit') {this.$request.post('/user/update', { ...this.formModel, id: this.currentRow.id }).then(response => {const { success } = response;if (success) {this.$message.success('編輯成功');this.handleSearch();this.dialogVisible = false;}});}},handleCancel() {this.dialogVisible = false;},handleTableSelectionChange(selection) {this.tableSelection = selection;},handleSizeChange(val) {this.currentPage = 1;this.pageSize = val;this.handleSearch();},handleCurrentChange(val) {this.currentPage = val;this.handleSearch();},} } </script><style> .page-curd__action-bar {margin: 20px 0px; } .page-curd__table {margin-bottom: 20px; } .page-curd .el-icon-user-solid {padding-right: 10px; } .page-curd__pagination {text-align: right; } </style>至此,一個(gè)基本的CURD示例就完成了。
九、總結(jié)
在本實(shí)例項(xiàng)目中,并沒(méi)有使用多少依賴項(xiàng),只有基本的UI組件庫(kù)element、基于promise的HTTP庫(kù)axios、前端模擬數(shù)據(jù)的mockjs,至于本文開(kāi)始時(shí),創(chuàng)建項(xiàng)目選擇的vuex并沒(méi)有提到,這個(gè)建議小伙伴們自己查閱相關(guān)資料去學(xué)習(xí)。
在我的學(xué)習(xí)理念中,每當(dāng)學(xué)習(xí)到一個(gè)新的技術(shù)時(shí),可以先查閱學(xué)習(xí)技術(shù)的簡(jiǎn)單教程,然后上手實(shí)踐,從模仿到舉一反三再到創(chuàng)新,千萬(wàn)不要只學(xué)習(xí)理論而不主動(dòng)去實(shí)踐,這樣效果并不深刻。
本文中有一些ES6以上的語(yǔ)法并沒(méi)有過(guò)多提及,有許多技巧也并沒(méi)有過(guò)多的給大家解釋,更多的技術(shù)細(xì)節(jié)我會(huì)在以后的文章中提到,如果有BUG或者不對(duì)的地方,歡迎各位小伙伴留言指正!
謝謝。
總結(jié)
以上是生活随笔為你收集整理的Vue入门之Web端CURD前端项目示例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 视频剪辑软件哪个好用?快把这些软件收好
- 下一篇: 【小实验】C51单片机 DS18B20温