电商后台管理系统–笔记
线上预览地址==>>http://52xhl.top/admin/
线上后台接口==>>http://www.52xhl.top:8889/api/private/v1/
小破站教程===>>https://www.bilibili.com/video/BV1x64y1S7S7
gitee仓库 ===>>https://gitee.com/elfin-chen/shop
后台api接口==>>http://52xhl.top/markdown/api.html
配置工作
router 配置使用
- router>index.js中 引入
import Vue from "vue";
import VueRouter from "vue-router";
- 声明变量 方便路由懒加载
//懒加载
const Login = () => import( /* webpackChunkName: "login_home_welome" */ '../views/login.vue')
const Home = () => import( /* webpackChunkName: "login_home_welome" */ '../views/home.vue')
const Welcome = () => import( /* webpackChunkName: "login_home_welome" */ '../views/welcome.vue')
- 具体设置方法
const routes = [{
path: "/", //访问更目录
redirect: "/login", //地址重定向到/home
},
{
path: "/login",
component: Login,
},
{
path: "/home",
component: Home,
redirect: '/welcome',
children: [{
path: "/welcome",
component: Welcome,
},
{
path: "/users",
component: Users,
},],
},
];
- 前置路由守卫
router.beforeEach((to, from, next) => {
if (to.path !== '/login') {
//判断是否登录
if (sessionStorage.getItem('token')) {
next();
} else {
Vue.prototype.$message.error('请先登录');
next('/login?redirect=' + to.path);
}
} else {
next();
}
})
- router使用方法
//query 是地址里 域名/后面的内容 解析成对象
this.$route.query.redirect
//跳转
this.$router.push({ path: '/home' })
this.$router.push('/goods/add');
axios配置使用
- vue ui中安装 main.js导入
//axios
import axios from "axios";
- 配置请求根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/
- 请求前守卫
//挂载token
axios.interceptors.request.use(config => {
NProgress.start();
// 在发送请求之前做些什么
config.headers.Authorization = window.sessionStorage.getItem('token');
return config;
})
- 请求后守卫
axios.interceptors.response.use(config => {
NProgress.done();
return config
})
- 把axios挂载到vue的原型对象上
Vue.prototype.$http = axios
- 请求方式
//post
let { data: res } = await this.$http.post("login", this.login);
element ui 配置使用
- vue ui中安装(按需加载) main.js导入
import "./plugins/element.js";
plugins/element.js
配置按需加载项
import Vue from "vue";
import { Button,Form,Message,MessageBox, } from "element-ui";
Vue.use(Button);
Vue.use(Form);
//消息提示挂载到原型对象
Vue.prototype.$message = Message;
Vue.prototype.$confirm = MessageBox.confirm;
iconfont 配置使用
- https://www.iconfont.cn/ 下载素材 把压缩包解压到项目目录
assets/font/....
- main.js引入css
//iconfont
import "./assets/font/iconfont.css";
- 使用时直接添加需要的图片类名 比如
iconfont icon-denglu
global全局样式使用
- 创建文件
assets/css/global.css
- main.js引入全局样式
//全局css
import "./assets/css/global.css";
moment配置使用
- 导入
import moment from 'moment';
- 使用方法
moment(parseInt(v)).format('YYYY-MM-DD')
lodash配置使用
- 导入
import _ from 'lodash';
使用
- 深拷贝
let cat = _.cloneDeep(this.basicinfo_data.classification)
- 合并
option = _.merge(res.data, option)
vue-quill-editor配置使用
- 导入
//富文本
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css' // import styles
import 'quill/dist/quill.snow.css' // for snow theme
import 'quill/dist/quill.bubble.css' // for bubble theme
Vue.use(VueQuillEditor)
- 使用
<!-- 富文本 -->
<quill-editor ref="myQuillEditor" v-model="supertxt" />
vue-table-with-tree-grid配置使用
- 导入
//vue-table
import ZkTable from 'vue-table-with-tree-grid'
Vue.component('tree-table', ZkTable)
- 使用
<tree-table style="margin: 15px 0;" :border="true" show-index index-text="#" :expand-type="false" :selection-type="false" :data="tableData" :columns="columns">
---
</tree-table>
echarts 配置使用
- 导入
import * as echarts from 'echarts'
- 准备一个盒子
<div ref="main" style="width: 600px;height:400px;"></div>
- 数据操作
var myChart = echarts.init(this.$refs.main);
option = _.merge(res.data, option) //option合并了
myChart.setOption(option);
总结知识点
Container 布局容器
width
设置侧边栏宽度
<el-container class="box">
<el-header>
</el-header>
<el-container class="content">
<el-aside :width="!flg ? '200px' : '64px'">
</el-aside>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
Form 表单
ref
方便通过this.$refs的方式使用form表单的方法 比如validate
表单校验this.$refs.loginref.validate(v => {})
返回校验结果在回调函数内操作resetFields
重置表单this.$refs.loginref.resetFields()
rules
表单校验规则 放在data的return里rules
和prop
绑定变量名要相同model
表单绑定的对象label-width
表单左侧的文字区域 宽度<el-form-item prop="username">
继承绑定 rules校验规则 : model对象中的具体属性<el-button type="primary" @click="dl">登录</el-button>
type指定了按钮颜色
<el-form ref="loginref" :rules="rules" :model="login" class="form" label-width="0">
<el-form-item prop="username">
<el-input prefix-icon="iconfont icon-denglu" v-model="login.username">
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" prefix-icon="iconfont icon-mima" v-model="login.password"></el-input>
</el-form-item>
<el-form-item class="btns">
<el-button type="primary" @click="dl">登录</el-button>
<el-button type="info" @click="cz">重置</el-button>
</el-form-item>
</el-form>
数据
login: {
username: "admin",
password: "123456",
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' }
],
}
- 自定义表单校验规则
//自定义校验表单
let checkEmail = (rule, value, callback) => {
if (!value) {
return callback(new Error('不能为空'));
}
if (value.indexOf('@') == -1) {
callback(new Error('请输入正确的邮箱'));
} else {
callback();
}
};
email: [
{ validator: checkEmail, trigger: 'blur' }
]
Message 消息提示
error
是错误类型,还有很多类型 和button的type一样
this.$message.error(res.meta.msg);
sessionStorage 本地存储
- 写入token
sessionStorage.setItem('token', res.data.token);
- 读取token
sessionStorage.getItem('token');
- 移出token
sessionStorage.removeItem('token')
NavMenu 导航菜单
el-menu
是整个menuel-submenu
是子菜单router
启用router模式 以index
作为 path 进行路由跳转collapse
是否折叠collapse-transition
折叠动画unique-opened
始终只保持一个菜单展开background-color
text-color
active-text-color
背景颜色 文字颜色 选中文字颜色default-active
当前激活高亮的的index
- 子菜单
index
是当前的索引 唯一标识 用来跳转<template slot="title">
菜单的标题 也就是菜单的内容
<el-menu :router="true" :collapse="flg" :collapse-transition="false" :unique-opened=true
background-color="#333744" text-color="#fff" active-text-color="#409eff"
:default-active="$route.path == '/goods/add' ? '/goods' : $route.path">
<!-- 一级菜单 -->
<el-submenu :index="item.path" v-for="(item, index) in asideList" :key="item.id">
<template slot="title">
<i :class="'iconfont ' + iconImg[index]"></i>
<span>{{ item.authName }}</span>
</template>
<!-- 二级菜单 -->
<el-menu-item :index="'/' + item2.path" v-for="(item2) in item.children" :key="item2.id">
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{ item2.authName }}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
Breadcrumb 面包屑
:to="{ path: '/home' }"
指定跳转路径
<!-- 面包屑 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item>
<el-breadcrumb-item>用户列表</el-breadcrumb-item>
</el-breadcrumb>
Card 卡片
<el-card class="box-card"></el-card>
Layout 布局
- 栅格布局 宽度分为24份 高度不限制
:span="8"
占用8份:gutter="20"
每一个格子有多少间隔
<!-- 栅格布局 每一行col总共24份 当前占8份 gutter每一格中间的空隙 -->
<el-row :gutter="20">
<el-col :span="8">
<div class="grid-content bg-purple">
<!-- 一个输入框 clear清空触发事件 -->
<el-input @clear="init" clearable v-model="gettableinfo.query" placeholder="请输入内容" class="input-with">
<!-- slot="append" 尾部的插槽 -->
<el-button @click="init" slot="append" icon="el-icon-search"></el-button>
</el-input>
</div>
</el-col>
<!-- 当前栅格占4 -->
<el-col :span="4">
<div class="grid-content bg-purple">
<el-button @click="adduser_dialog = true" type="primary">添加用户</el-button>
</div>
</el-col>
</el-row>
Button 按钮
- 第一行
type
类型 - 第二行
plain
朴素 - 第三行
round
圆角 - 第四行
circle
圆形 size
尺寸icon="el-icon-search"
图标
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="info">信息按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
Input 输入框
prefix-icon
头部图标suffix-icon
尾部图标clearable
是否显示清空的x@clear
点击x的清空事件slot="append"
尾部插槽 按钮disabled
禁用
<el-input @clear="init" clearable v-model="gettableinfo.query" placeholder="请输入内容" class="input-with">
<!-- slot="append" 尾部的插槽 -->
<el-button @click="init" slot="append" icon="el-icon-search"></el-button>
</el-input>
Switch 开关
@change
状态发生变化时的回调函数
<el-switch @change="switch_value(scope.row)" v-model="scope.row.mg_state"></el-switch>
Tooltip 文字提示
enterable
鼠标是否可进入到 tooltip 中effect
提示的主题样式content
提示的内容
<el-tooltip :enterable="false" class="item" effect="dark" content="分配权限" placement="top">
<el-button @click="allot(scope.row)" size="mini" type="warning" icon="el-icon-setting"></el-button>
</el-tooltip>
Tag 标签
closable
是否显示xx@close
点击xx的事件type="primary"
样式 和 button一样disable-transitions
禁用渐变动画{{ item1.authName }}
标签内容
<el-tag @close="removtag3(props.row, item1.id)" closable type="primary" disable-transitions>
{{ item1.authName }}
</el-tag>
动态编辑标签
<el-tag v-for="(tag, index) in props.row.attr_vals" :key="'tag' + index" closable
:disable-transitions="true" @close="handleClose(props.row.attr_vals, tag, props.row)"
:style="[{ marginLeft: 25 + 'px' }]">
{{ tag }}
</el-tag>
<el-input style="margin-left: 25px;width: 100px;" class="input-new-tag" v-if="props.row.inputVisible"
v-model="props.row.taginput" ref="taginput" size="small"
@keyup.enter.native="handleInputConfirm(props.row)" @blur="handleInputConfirm(props.row)">
</el-input>
<el-button style="margin-left: 25px;" v-else class="button-new-tag" size="small"
@click="tagbtn(props.row)">+ New Tag</el-button>
Table 表格
:data
表格绑定的数据对象border
边框stripe
隔行变色label
表中每一个属性的表头名字type="index"
表示当前列是索引prop="username"
表示数据绑定的表格数据data
中的username属性<template slot-scope="scope">
插槽 自定义内容scope.row
是当前行数据<el-table-column type="expand">
该插槽时展开后 看到的内容
<el-table :data="tableData" :border="true" stripe style="width: 100%">
<!-- 每一行 index表示当前行是索引-->
<el-table-column label="#" type="index"></el-table-column>
<!-- prop="username" 这个属性表示表格内容采用table的 :data对象内的对应属性-->
<el-table-column label="姓名" prop="username"></el-table-column>
<el-table-column label="邮箱" prop="email"></el-table-column>
<el-table-column label="电话" prop="mobile"></el-table-column>
<el-table-column label="角色" prop="role_name"></el-table-column>
<el-table-column label="状态" prop="mg_state">
<!-- 自定义表格内容 插槽 插槽传入的数据在scope.row内-->
<template slot-scope="scope">
<!-- 开关触发事件 change -->
<el-switch @change="switch_value(scope.row)" v-model="scope.row.mg_state">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作">
<!-- eslint-disable-next-line 去除报错-->
<template slot-scope="scope">
<el-button @click="setitem(scope.row)" size="mini" type="primary" icon="el-icon-edit"></el-button>
<el-button @click="deleteitem(scope.row.id)" size="mini" type="danger" icon="el-icon-delete"></el-button>
<!--
tooltip 文字提示 enterable鼠标不能进入到提示区域
effect 主题 content文字内容 placement悬浮提示到哪
-->
<el-tooltip :enterable="false" class="item" effect="dark" content="分配权限" placement="top">
<el-button @click="allot(scope.row)" size="mini" type="warning" icon="el-icon-setting"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
Pagination 分页器
@size-change
每页条数变化时触发@current-change
当前页数变化时触发current-page
当前页数page-sizes
每页显示个数选择器的选项设置page-size
每一页显示条目个数layout
关于分页器的各种工具total
数据总条数 数量
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="gettableinfo.pagenum" :page-sizes="[2, 5, 10]" :page-size="gettableinfo.pagesize"
layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
Dialog 对话框
title
对话框标题@close
对话框关闭事件:visible.sync
控制对话框是否显示的变量slot="footer"
底部插槽
<el-dialog title="收货地址" @close="set_close" :visible.sync="setuser_dialog">
<!-- 插槽 footer 弹窗尾部-->
<div slot="footer" class="dialog-footer">
<el-button @click="setuser_dialog = false">取 消</el-button>
<el-button type="primary" @click="set_ok()">确 定</el-button>
</div>
</el-dialog>
MessageBox 弹框
- 直接函数调用就能弹出
deleteitem(id) {
this.$confirm('此操作将永久删除, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
//删除操作
this.$message.success('删除成功');
}).catch(() => {
//取消操作
// this.$message({
// type: 'info',
// message: '已取消删除'
// });
});
},
Tree 树形控件
ref 绑定后方便使用他的事件
this.$refs.tree.getCheckedKeys()
返回目前被选中的节点的 key 所组成的数组this.$refs.tree.getHalfCheckedKeys()
返回目前半选中的节点的 key 所组成的数组
data
是绑定的数据 []show-checkbox
显示复选框node-key
节点的keydefault-expand-all
是否默认展开所有节点default-checked-keys
默认勾选的节点的 key 的数组props
配置选项defaultProps: { children: 'children', label: 'authName' },
<el-tree ref="tree" :data="allot_role_data" show-checkbox node-key="id" default-expand-all
:default-checked-keys="efaultCheckedKeys" :props="defaultProps">
</el-tree>
- 遍历三级菜单(打勾的) 当前数据是三级子菜单树的数据 遍历成数组
//遍历三级菜单 打钩的
foreachCheckedKeys(node) {
if (!node.children) {
return this.efaultCheckedKeys.push(node.id)
}
node.children.forEach(item => {
this.foreachCheckedKeys(item)
});
},
Alert 警告
center
居中title
标题closable
是否可关闭type
类型show-icon
显示图标
<el-alert center style="margin-bottom: 15px;;" title="添加商品信息" :closable="false" type="info" show-icon>
</el-alert>
Steps 步骤条
align-center
居中active
当前索引 数字类型finish-status
完成步骤的样式
<el-steps align-center style="width: 100%;" :active="active - 0" finish-status="success">
<el-step title="基本信息"></el-step>
<el-step title="商品参数"></el-step>
<el-step title="商品属性"></el-step>
<el-step title="商品图片"></el-step>
<el-step title="商品内容"></el-step>
<el-step title="完成"></el-step>
</el-steps>
Tabs 标签页
:before-leave
切换标签之前的钩子@tab-click
tab 被选中时触发tab-position
位置v-model
点击会吧name
的值给绑定
<el-tabs :before-leave="beforeLeave" @tab-click="tabClick" v-model="active" style="margin-top: 25px;"
tab-position="left">
<el-tab-pane name="0" label="基本信息">
---
</el-tab-pane>
<el-tab-pane name="1" label="商品参数">
---
</el-tab-pane>
<el-tab-pane name="2" label="商品属性">
---
</el-tab-pane>
<el-tab-pane name="3" label="商品图片">
---
</el-tab-pane>
<el-tab-pane name="4" label="商品内容">
---
</el-tab-pane>
</el-tabs>
Cascader 级联选择器
clearable
清除按钮options
数据源props
数据源的配置
selectprop: {
label: 'cat_name',
value: 'cat_id',
children: 'children',
expandTrigger: 'hover',
},
@change
值变化触发
<el-cascader style="width: 100%;" clearable v-model="basicinfo_data.classification"
:options="selectoptions" :props="selectprop" @change="handleChange">
</el-cascader>
Checkbox 多选框
el-checkbox-group
中v-model
绑定一个数组label
复选框的值
<el-checkbox-group v-model="item.attr_vals">
<el-checkbox style="margin: 0 5px 0 0 ;" v-for="(v, i) in item.attr_vals"
:key="'checkbox_in' + i" :border="true" :label="v">
</el-checkbox>
</el-checkbox-group>
Upload 上传
:on-remove
文件列表移除文件时的钩子on-success
文件上传成功时的钩子headers
设置上传的请求头部action
上传地址on-preview
点击文件列表中已上传的文件时的钩子list-type
文件列表的类型
<el-upload :on-remove="onRemove" :on-success="onSuccess" :headers="headers"
action="http://127.0.0.1:8888/api/private/v1/upload" :on-preview="handlePreview"
list-type="picture">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
tree-table树形展开表
vue-table-with-tree-grid
show-index
是否显示索引index-text
索引的标题文字expand-type
是否为展开行类型表格selection-type
是否为多选类型表格columns
配置项columns: [ { label: '分类名称', prop: 'cat_name', }, { label: '是否有效', align: 'center', type: 'template', template: 'isok', }, { label: '排序', align: 'center', type: 'template', template: 'sort', }, { label: '操作', align: 'center', type: 'template', template: 'operation', }, ],
<tree-table style="margin: 15px 0;" :border="true" show-index index-text="#" :expand-type="false"
:selection-type="false" :data="tableData" :columns="columns">
<template slot="isok" slot-scope="scope">
<i style="color:lightgreen" class="el-icon-success" v-if="scope.row.cat_deleted === false"></i>
<i style="color:red" class="el-icon-error" v-else></i>
</template>
<template slot="sort" slot-scope="scope">
<el-tag v-if="scope.row.cat_level == 0">一级</el-tag>
<el-tag v-else-if="scope.row.cat_level == 1" type="success">二级</el-tag>
<el-tag v-else type="warning">三级</el-tag>
</template>
<template slot="operation" slot-scope="scope">
<el-button @click="set_btn(scope.row)" size="mini" type="primary" icon="el-icon-edit">编辑
</el-button>
<el-button @click="delete_btn(scope.row.cat_id)" size="mini" type="danger" icon="el-icon-delete">删除
</el-button>
</template>
</tree-table>
项目优化
nprogress添加进度条效果
- 导入
//导入nprogress
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
- 使用 在axios发起请求前和后设置进度条开始结束
NProgress.start();
NProgress.done();
axios.interceptors.request.use(config => {
NProgress.start();
// 在发送请求之前做些什么
config.headers.Authorization = window.sessionStorage.getItem('token');
return config;
})
axios.interceptors.response.use(config => {
NProgress.done();
return config
})
移除所有的console
babel-plugin-transform-remove-console
chainWebpack自定义打包入口
- 把main.js拆成生产和开发环境 main-prod.js main-dev.js
通过extemals加载外部CDN资源
- vue.config.js中生产环境配置
config.set('externals', {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios',
lodash: '_',
echarts: 'echarts',
nprogress: 'NProgress',
'vue-quill-editor': 'VueQuillEditor',
"moment": "moment",
})
- 通过externals 加载外部cdn资源
<!-- nprogress 的样式表文件 -->
<link rel="stylesheet" href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" />
<!-- 富文本编辑器 的样式表文件 -->
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.core.min.css" />
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.snow.min.css" />
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.bubble.min.css" />
<!-- element-ui 的样式表文件 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.staticfile.org/vue/2.6.3/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script>
<script src="https://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
<script src="https://cdn.staticfile.org/lodash.js/4.17.15/lodash.min.js"></script>
<script src="https://cdn.staticfile.org/echarts/4.7.0/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
<!-- 富文本编辑器的 js 文件 -->
<script src="https://cdn.staticfile.org/quill/1.3.4/quill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.6/dist/vue-quill-editor.js"></script>
<!-- element-ui 的 js 文件 -->
<!-- <script src="https://cdn.staticfile.org/element-ui/2.13.0/index.js"></script> -->
<!-- moment -->
<script src="https://cdn.bootcss.com/moment.js/2.20.1/moment.min.js"></script>
<script src="https://cdn.bootcss.com/moment.js/2.20.1/locale/zh-cn.js"></script>
- 配置好后 main-prod.js中重复引入的配置文件就可以注释了
自定制首页内容
生产环境 true
config.plugin('html').tap(args => {
args[0].isProd = true
return args
})
开发环境 false
config.plugin('html').tap(args => {
args[0].isProd = false
return args
})
index.html中配置
<title><%= htmlWebpackPlugin.options.isProd ? '' : 'dev - ' %>电商后台管理系统</title>
<% if (htmlWebpackPlugin.options.isProd) {%>
//引入的cdn
<% } %>
index.html中
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.isProd ? '' : 'dev - ' %>电商后台管理系统</title>
<% if (htmlWebpackPlugin.options.isProd) {%>
<!-- nprogress 的样式表文件 -->
<link rel="stylesheet" href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" />
<!-- 富文本编辑器 的样式表文件 -->
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.core.min.css" />
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.snow.min.css" />
<link rel="stylesheet" href="https://cdn.staticfile.org/quill/1.3.4/quill.bubble.min.css" />
<!-- element-ui 的样式表文件 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.staticfile.org/vue/2.6.3/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script>
<script src="https://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
<script src="https://cdn.staticfile.org/lodash.js/4.17.15/lodash.min.js"></script>
<script src="https://cdn.staticfile.org/echarts/4.7.0/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
<!-- 富文本编辑器的 js 文件 -->
<script src="https://cdn.staticfile.org/quill/1.3.4/quill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-quill-editor@3.0.6/dist/vue-quill-editor.js"></script>
<!-- element-ui 的 js 文件 -->
<!-- <script src="https://cdn.staticfile.org/element-ui/2.13.0/index.js"></script> -->
<!-- moment -->
<script src="https://cdn.bootcss.com/moment.js/2.20.1/moment.min.js"></script>
<script src="https://cdn.bootcss.com/moment.js/2.20.1/locale/zh-cn.js"></script>
<% } %>
</head>
vue.config.js
module.exports = {
publicPath: "./", //打包时加上
productionSourceMap: false,
// 发布模式
chainWebpack: config => {
config.when(process.env.NODE_ENV === 'production', config => {
config.entry('app').clear().add('./src/main-prod.js')
config.set('externals', {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios',
lodash: '_',
echarts: 'echarts',
nprogress: 'NProgress',
'vue-quill-editor': 'VueQuillEditor',
"moment": "moment",
})
config.plugin('html').tap(args => {
args[0].isProd = true
return args
})
})
// 开发模式
config.when(process.env.NODE_ENV === 'development', config => {
config.entry('app').clear().add('./src/main-dev.js')
config.plugin('html').tap(args => {
args[0].isProd = false
return args
})
})
}
}
实现路由懒加载
‘@babel/plugin-syntax-dynamic-import’
- /**/ 中间的内容 可以把路由分组 打包到一起加载
其他
小知识
使用对象赋值的使用 其实赋值了一个引用地址 修改了值会连带着改变
接口请求来之后 数据有问题可以自己处理或者添加,到对象上 和上游下游一个道理
比如 row传下来可以绑定东西上去 比如res.data可以绑定东西上去
这段代码可以去除报错
<!-- eslint-disable-next-line -->
字符串 ‘0’ - 0 = 0
用到的方法
map
.this.options = res.data.map((v, i, arr) => {
return {
value: v.id,
label: v.roleName,
};
});
递归循环
//遍历三级菜单 打钩的
foreachCheckedKeys(node) {
if (!node.children) {
return this.efaultCheckedKeys.push(node.id)
}
node.children.forEach(item => {
this.foreachCheckedKeys(item)
});
},
forEach
res.data.forEach((item) => {
item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
item.checkbox = true;
})
findIndex
//找到
let path = file.response.data.tmp_path;
let weizhi = this.pics.findIndex((item) => {
return item.pic === path
});
splice
//删除
this.pics.splice(weizhi, 1)
join
attr_value: item.attr_vals.join(' ')
split
item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
indexOf
//当前点击的索引
let listindex = list.indexOf(tag)
$nextTick
this.$nextTick(() => {
this.$refs.taginput.$refs.input.focus()
})
深拷贝
//深拷贝
deepcopy(copy, obj) {
//获取属性 判断是数组?是对象?还是简单数据类型
for (let k in obj) {
if (obj[k] instanceof Array) {
copy[k] = [];
this.deepcopy(copy[k], obj[k]);
} else if (obj[k] instanceof Object) {
copy[k] = {};
this.deepcopy(copy[k], obj[k]);
} else {
copy[k] = obj[k];
}
}
},