大家好,欢迎来到IT知识分享网。
Vue入门之Web端CURD前端项目示例
随着vue.js
越来越火,很多不太懂前端的小伙伴想要入坑。而入坑最好的办法就是上手实际操作
,因此本文将重点放在实际操作上,对理论不做过多解释,不明白的地方小伙伴们可以去看官方文档或者相关书籍。
本文的示例项目操作环境如下:
平台/工具 | 说明 |
---|---|
操作系统 | 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进行安装。
1.1 安装nvm
- windows
地址:https://github.com/coreybutler/nvm-windows/releases
根据文档说明安装,记得设置淘宝的镜像。
nvm node_mirror https://npm.taobao.org/mirrors/node/
nvm npm_mirror https://npm.taobao.org/mirrors/npm/
或者修改settings.txt文件,在后面加上淘宝镜像设置。
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
- linux/mac
地址:https://github.com/creationix/nvm
根据文档说明安装,记得设置淘宝的镜像。
# 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/node
1.2 配置npm
npm config set registry https://registry.npm.taobao.org
npm config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass
1.3 安装yarn
npm install -g yarn
1.4 配置yarn
已配置过npm则无需再次配置。
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
细心的小伙伴会发现vue官网是不推荐入门直接上vue-cli的,这里考虑到很多小伙伴不是前端工程师,而仅仅是需要了解vue项目的基本结构,所以直接使用vue-cli创建项目吧。
使用npm或者yarn全局安装vue-cli:
注意:这里的@vue/cli不要写成了vue-cli,vue-cli是旧版本。
npm install -g @vue/cli
# OR
yarn global add @vue/cli
三、使用vue-cli创建项目
现在可以开始使用vue-cli创建项目了,这里项目名称为example-curd
。
这里也可以通过vue ui来创建,使用方法是控制台输入vue ui,具体内容本文不做展示
vue create example-curd
这时会有一个选项,第一项为默认配置,这里我们选择第二项手动配置,回车确定。
之后会出现一个多选,这时的操作是空格键选择,空格选择完所有需要的配置之后,最后按回车确定。
本文用于演示基本的项目流程,只选择3项,其它项小伙伴们自行查阅资料按需选择
选好依赖项后,进入vue-router设置提示,是否使用history mode,输入n
然后回车确定。
这里的区别小伙伴们自己查阅资料。
然后会问如何处理Babel、ESLint之类的配置文件,这里为了便于修改,不与package.json混在一起,选择第一项。
最后问你是否记录下这套配置,由于本文只是演示,选择的配置项并不适合正式项目,所以选择不记录。
选择包管理器,这里推荐使用yarn。
项目配置完成。
四、修改项目配置
生成的项目目录结构如图:
为了避免默认端口被占用的情况,我们新建一个名为vue.config.js
的配置文件,指定host
与port
。配置参考
module.exports = {
devServer: {
open: true,
host: '0.0.0.0',
port: 9000,
},
};
五、启动项目
可以在package.json
中修改脚本命令,例如增加一个与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"
}
}
启动项目
yarn dev
至此,基本的项目创建与启动演示完毕,接下来进入实例演示。
六、安装依赖
本文主要目的是演示创建基本CURD的前端WEB页面,因此不做后端API的创建,这里使用mock.js
拦截请求并返回模拟数据
。
本文需要用到的依赖有:
- element-ui
- mockjs
- axios
使用yarn安装以上依赖
如果已经启动了项目,可以先ctrl + c退出,安装完依赖后再启动。
yarn add element-ui mockjs axios
七、建立基本视图框架
修改App.vue、main.js、router.js,同时删除项目创建时的默认文件About.vue、Home.vue、HelloWorld.vue,在src/views
目录下创建一个layout
目录和page
目录,分别用于存放布局和页面。
7.1 修改main.js文件
引入element组件库及其样式
这里的@在vue-cli默认语法中代表项目中的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组件库
import 'element-ui/packages/theme-chalk/lib/index.css'; // ElementUI组件库样式
// 注册饿了么UI
Vue.use(ElementUI);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
7.2 修改App.vue
同时调整一些必要的全局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 创建布局文件
在layout
目录下创建一个default.vue作为默认布局文件,初学可以使用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为本项目默认首页,且作用于默认布局下。
() => import(‘xxx’) 为动态加载组件,这样做的好处是用到哪个页面加载哪个页面,避免导致首页加载时间过长。
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
修改首页内容,测试布局是否生效。
src/views/page/index.vue:
<template>
<div>
<el-button type="primary">首页的按钮</el-button>
</div>
</template>
7.6 查看基本布局是否生效
可以看到首页的ElementUI的按钮组件正常显示,且首页位于默认布局之下。
此时项目结构如下:
八、完善CURD功能
最后只剩下界面优化及基本的业务功能页面了。
8.1 优化界面
增加左侧菜单以及右侧上方的导航栏。可以使用ElementUI的NavMenu 导航菜单组件来实现。
此时可以将菜单栏和标题栏拆分成独立组件引入默认布局,创建src/views/layout/components
目录并新建header.vue及menu.vue。
此时我们希望菜单根据router配置自动生成,所以需要修改router,可以将router配置单独提取成routes.js便于其它页面调用。同时新建一个CURD页面curd.vue。
[标题栏] 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">首页</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>
[导航菜单] src/views/layout/components/menu.vue:
<template>
<el-menu ref="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>
[路由设置] 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: '首页', 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页面</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>
此时的目录结构是这样的:
这时可以看到页面已经有了较为美观的样式:
且菜单支持收起和展开:
8.2 修改curd页面结构
一个基本的CURD
页面应该包括:查询条件、操作按钮、数据列表、列表分页、编辑表单、详情展示等几个基本模块。
因此我们可以修改原有的curd.vue为一个curd目录
,目录下包括index.vue及其相关组件。由于表单页面相对而言重复性较高,代码较为冗长,所以我们将其拆分为index.vue、search.vue、form.vue三个组件,其中index.vue为列表分页展示及数据控制,search.vue和form.vue为表单组件。
search.vue搜索表单我们可以参照element的行内表单示例进行修改:
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="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="model.gender" placeholder="请选择性别">
<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的表单验证示例进行修改:
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="请选择性别">
<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: '请输入姓名', trigger: 'blur' }], gender: [{
required: true, message: '请选择性别', trigger: 'change' }], age: [{
required: true, message: '请输入年龄', 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: {
// 点击确定提交表单的操作 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); } }); }, // 校验表单 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(); }); }, // 点击取消的操作 handleCancel() {
this.$emit("cancel"); } } }; </script>
index.vue则是对以上组件的整合以及列表数据的展示操作。
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-pagination class="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: '尼格买提', 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>
假如你有强迫症的话,那就顺便把首页略微修改一下。
src/views/page/index.vue:
<template>
<div class="page-index">
Vue入门项目CURD示例
</div>
</template>
<style> .page-index {
text-align: center; padding-top: calc(50vh - 60px - 24px); font-size: 24px; } </style>
此时的项目效果如下:
首页
CURD页面
新增
编辑
查看详情
8.3 模拟接口数据
本示例项目为纯前端项目,所以使用mockjs
来模拟增删改查的基本API。
在项目中新建一个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';
// 临时缓存mockjs数据
let MockCache = {
};
const MockBot = {
// 通用模板API
fastAPI: {
// 获取数据列表
page: (base) => config => {
const list = MockCache[base] || [];
const param = parseUrl(config.url) || {
};
const {
page = 1, size = 10, ...query } = param;
// 计算有几个搜索条件
let queryCount = false;
for (let key in query) {
if (query[key]) {
queryCount += 1;
break;
}
}
// 根据搜索条件过滤结果
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;
// 根据结果处理分页数据
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;
},
// 查询数据详情
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,
};
},
// 新增数据
add: (base) => config => {
const param = JSON.parse(config.body);
MockCache[base].unshift(param);
return {
success: true,
};
},
// 编辑数据
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,
};
},
// 删除数据
delete: (base) => config => {
const ids = JSON.parse(config.body);
ids.forEach(id => {
MockCache[base] = MockCache[base].filter(item => item.id !== id);
});
return {
success: true,
};
}
},
// 根据通用模板API快速创建模拟接口
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));
},
}
// 产品管理
MockBot.fastMock('user', userAPI.userList);
src/mock/util.js:
import Mock from 'mockjs';
export const parseUrl = (url) => {
let obj = {
};// 创建一个Object
let reg = /[?&][^?&]+=[^?&]+/g;// 正则匹配 ?&开始 =拼接 非?&结束 的参数
let arr = url.match(reg);// match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
// arr数组形式 ['?id=12345','&a=b']
if (arr) {
arr.forEach((item) => {
/** * tempArr数组 ['id','12345']和['a','b'] * 第一个是key,第二个是value * */
let tempArr = item.substring(1).split('=');
let key = decodeURIComponent(tempArr[0]);
let val = decodeURIComponent(tempArr[1]);
obj[key] = val;
});
}
return obj;
}
export default {
// 根据配置文件和数量快速生成数据列表
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 对接接口数据
模拟好接口,接下来就需要进行接口对接了,首先我们修改main.js,将axios
创建一个实例并设置为Vue的prototype,这样可以在每个Vue页面或组件中直接通过this进行访问。
创建axios实例后可以设置request和response拦截器,具体用法请查看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组件库
import 'element-ui/packages/theme-chalk/lib/index.css'; // ElementUI组件库样式
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;
// 注册饿了么UI
Vue.use(ElementUI);
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
配置好axios之后,我们就可以在curd页面中进行对接了。
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-pagination class="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>
至此,一个基本的CURD示例就完成了。
九、总结
在本实例项目中,并没有使用多少依赖项,只有基本的UI组件库element、基于promise
的HTTP库axios、前端模拟数据的mockjs,至于本文开始时,创建项目选择的vuex并没有提到,这个建议小伙伴们自己查阅相关资料去学习。
在我的学习理念中,每当学习到一个新的技术时,可以先查阅学习技术的简单教程,然后上手实践,从模仿到举一反三再到创新,千万不要只学习理论而不主动去实践,这样效果并不深刻。
本文中有一些ES6
以上的语法并没有过多提及,有许多技巧也并没有过多的给大家解释,更多的技术细节我会在以后的文章中提到,如果有BUG或者不对的地方,欢迎各位小伙伴留言指正!
谢谢。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/14155.html