登录:
在登陆页面输入账号密码,讲数据发送给服务器
服务器返回登录的结果,成功返回的数据中带有token
在得到token后,将他进行保存,存储在本地存储中。
登录成功之后,需要讲后台返回的token保存到本地存储中,
操作完毕后,需要跳转到home

login() {this.$refs.ruleForm.validate(async (v) => {if (v) {let res = await loginAPI.login(this.loginForm);console.log(res);if (res.data.meta.status == 200) {this.$message.success(res.data.meta.msg);localStorage.setItem("token", res.data.data.token);this.$router.push("/home");} else {this.$message.error(res.data.meta.msg);}}});},

进行路由鉴权,如果没有登录,不能访问/home,强制跳转到登录页面

router.beforeEach((to, from, next) => {if (to.path === "/") {return next()}let token = localStorage.getItem("token")if (!token) {return next("/")}next()
})

实现退出
在home组件添加一个退出按钮,点击直接清空本地存储中的token即可。

首页布局:
使用element-ui进行快速布局,
请求侧边栏数据

created() {HomeAPI.leftMensList(this.menuList).then((res) => {// console.log(res);if (res.data.meta.status == 200) {this.menuList = res.data.data;} else {this.$message({message: "菜单数据获取失败",});}});},

通过v-for 双重循环渲染左侧菜单

<el-menu:default-active="$route.path"class="el-menu-vertical-demo"background-color="#333744"text-color="#fff"active-text-color="#ffd04b":unique-opened="true"router:collapse="isCollapse":collapse-transition="false"@open="handleOpen"@close="handleClose"><el-submenu:index="item.id + ''"v-for="item in menuList":key="item.id"><template slot="title"><i :class="iconsObj[item.id]"></i><span>{{ item.authName }}</span></template><el-menu-itemv-for="item1 in item.children":key="item1.id":index="'/' + item1.path"><i :class="iconsObj1[item1.id]"></i>{{ item1.authName }}</el-menu-item></el-submenu></el-menu>

侧边菜单栏的伸缩功能

<el-aside :width="isCollapse ? '64px' : '300px'"><div class="toggles" @click="toggleCollapse">|||</div>

用户列表基本结构
使用element-ui面包屑组件,完成顶部导航
使用element-ui卡片组件完成主体表格
然后讲用户数据渲染

 <div><!-- 面包屑导航 --><Bread></Bread><!-- 卡片区域 --><el-card class="box-card"><!-- 搜索框 --><el-inputplaceholder="请输入内容"v-model="input"clearableclass="input"><template slot="append"><span class="el-icon-search" @click="userSeach"></span></template></el-input><el-button type="primary" class="user" @click="addUser">添加用户</el-button><!-- 用户列表 --><el-table :data="tableData" border style="width: 100%" class="table"><el-table-column type="index" width="100px" label="#"></el-table-column><el-table-column prop="username" label="姓名"> </el-table-column><el-table-column prop="email" label="邮箱"> </el-table-column><el-table-column prop="mobile" label="电话"> </el-table-column><el-table-column prop="role_name" label="角色"> </el-table-column><el-table-column label="状态"><template slot-scope="scope"><el-switchv-model="scope.row.mg_state"@change="userEdit(scope.row)"></el-switch></template></el-table-column><el-table-column label="操作"><template slot-scope="scope"><!-- 修改按钮 --><el-buttontype="primary"icon="el-icon-edit"size="mini"@click="editState(scope.row.id)"></el-button><!-- 删除按钮 --><el-buttontype="danger"icon="el-icon-delete"size="mini"@click="delUser(scope.row.id)"></el-button><!-- 分配权限按钮 --><el-tooltipeffect="dark"content="分配权限"placement="top":enterable="false"><el-buttontype="warning"icon="el-icon-setting"size="mini"@click="distribution(scope.row)"></el-button></el-tooltip></template></el-table-column></el-table><!-- 分页 --><div class="block"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[1, 2, 5, 10]":page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div>

请求用户列表数据:

//用户列表getTableData() {userAPI.getUserList(this.queryInfo).then((res) => {// console.log(res);if (res.data.meta.status !== 200) {this.$message.error(res.data.meta.msg);} else {this.$message.success(res.data.meta.msg);this.tableData = res.data.data.users;this.total = res.data.data.total;}});},

实现分页功能

handleSizeChange(val) {this.queryInfo.pagesize = val;// console.log(`每页 ${val} 条`);this.getTableData();},handleCurrentChange(val) {this.queryInfo.pagenum = val;// console.log(`当前页: ${val}`);this.getTableData();},

实现更新用户状态

 <el-switchv-model="scope.row.mg_state"@change="userEdit(scope.row)"></el-switch>

发送请求完成状态的更改

userEdit(stateChange) {// console.log(stateChange);userAPI.editUserState(stateChange.id, stateChange.mg_state).then((res) => {// console.log(res);if (res.data.meta.status == 200) {this.$message.success("更新用户状态成功");} else {this.$message.error("更新用户状态失败");}});},

实现搜索功能

 <el-inputplaceholder="请输入内容"v-model="input"clearableclass="input"><template slot="append"><span class="el-icon-search" @click="userSeach"></span></template></el-input>//搜索userSeach() {this.queryInfo.query = this.input;this.getTableData();},

实现添加用户

<el-dialog :visible.sync="addDialogVisible" width="30%"><el-form:model="addForm":rules="addFormRules"ref="addFormRef"label-width="100px"class="demo-ruleForm"><el-form-item label="用户名" prop="username"><el-input v-model="addForm.username"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input v-model="addForm.password"></el-input></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="addForm.email"></el-input></el-form-item><el-form-item label="电话" prop="mobile"><el-input v-model="addForm.mobile"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" @click="submitUser">提交</el-button><el-button @click="addDialogVisible = false">取消</el-button></span></el-dialog>

添加数据绑定和校验规则:

  data() {//验证邮箱的正则var checkEmail = (rule, value, callback) => {//alert(value)var reg = /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;if (reg.test(value)) {// 格式正确返回truecallback();} else {// 格式不正确return callback(new Error("邮箱格式不对"));}};//验证手机号的正则var checkMobile = (rule, value, callback) => {var mobileReg = /^1[3456789]\d{9}$/;if (mobileReg.test(value)) {// 格式正确返回truecallback();} else {// 格式不正确return callback(new Error("手机号格式不对"));}};return {input: "",tableData: [],//获取用户列表的参数queryInfo: {query: "",pagenum: 1, //当前页数pagesize: 10, //当前每页显示多少数据},total: 0,addDialogVisible: false, //添加弹出框//添加 验证addFormRules: {username: [{ required: true, message: "请输入活动名称", trigger: "blur" },{ min: 3, max: 5, message: "长度在 3 到 5 个字符", trigger: "blur" },],password: [{ required: true, message: "请输入活动名称", trigger: "blur" },{ min: 6, max: 15, message: "长度在6 到 15 个字符", trigger: "blur" },],email: [{ required: true, message: "请输入邮箱地址", trigger: "blur" },{ validator: checkEmail, trigger: "blur" },],mobile: [{ required: true, message: "请输入电话号码", trigger: "blur" },{ validator: checkMobile, trigger: "blur" },],},addForm: {username: "",password: "",email: "",mobile: "",},dialogVisible: false, //编辑弹出框//验证 修改editFormRules: {email: [{ required: true, message: "请输入邮箱地址", trigger: "blur" },{ validator: checkEmail, trigger: "blur" },],mobile: [{ required: true, message: "请输入电话号码", trigger: "blur" },{ validator: checkMobile, trigger: "blur" },],},editForm: {},//需要被分配的用户信息userInfo: {},selectedRole: "",//角色列表roleList: [],roleDialogVisible: false,};},

当关闭对话框时,重置表单。

修改用户信息
根据id查询需要修改的用户数据

//显示编辑对话框editState(id) {userAPI.editState(id).then((res) => {console.log(res);this.editForm = res.data.data;});this.dialogVisible = true;},

弹出框数据绑定及验证:

//控制修改用户对话框的显示与否
editDialogVisible: false,
//修改用户的表单数据
editForm: {username: '',email: '',mobile: ''
},
//修改表单的验证规则对象
editFormRules: {email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },{validator: checkEmail,message: '邮箱格式不正确,请重新输入',trigger: 'blur'}],mobile: [{ required: true, message: '请输入手机号码', trigger: 'blur' },{validator: checkMobile,message: '手机号码不正确,请重新输入',trigger: 'blur'}]
}

用户点击确定按钮,验证数据成功发送请求完成修改

editUser() {this.$refs.editRuleForm.validate((valid) => {if (valid) {userAPI.editUsers(id, {email: this.editForm.email,mobile: this.editForm.mobile,}).then((res) => {console.log(res);if (res.data.meta.status == 200) {this.$message.success("编辑用户成功");} else {this.$message.error("编辑用户失败");}this.dialogVisible = false;this.getTableData();});}});},

删除用户:
根据id删除相对应的数据

delUser(id) {userAPI.deletUser(id).then((res) => {console.log(res);if (res.data.meta.status == 200) {this.$message.success("用户删除成功");} else {this.$message.error("用户删除失败");}this.getTableData();});},

权限管理
权限列表

使用element-ui组件添加面包屑导航
显示数据

<el-table :data="rightsList" border style="width: 100%"><el-table-column type="index" label="#" width="60"></el-table-column><el-table-columnprop="authName"label="权限名称"width="360"></el-table-column><el-table-column prop="path" label="路径" width="360"></el-table-column><el-table-column prop="level" label="权限等级" width="360"><template slot-scope="scope"><el-tag v-if="scope.row.level === '0'">一级</el-tag><el-tag type="success" v-else-if="scope.row.level === '1'">二级</el-tag><el-tag type="warning" v-else>三级</el-tag></template></el-table-column></el-table>created() {roleAPI.getRightList(this.rightsList).then((res) => {console.log(res);this.rightsList = res.data.data;});},

角色列表
使用element-ui面包屑导航,进行布局
显示数据

<el-table row-key="id" :data="roleList" border><!-- 添加展开列 --><el-table-column type="expand"></el-table-column><el-table-column type="index"></el-table-column><el-table-column label="角色名称" prop="roleName"></el-table-column><el-table-column label="角色描述" prop="roleDesc"></el-table-column><el-table-column label="操作" width="300px"><template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button><el-button size="mini" type="warning" icon="el-icon-setting">分配权限</el-button></template></el-table-column>
</el-table>export default {data(){return {roleList:[]}},created(){this.getRoleList();},methods:{async getRoleList(){const {data:res} = await this.$http.get('roles')console.log(res.data)this.roleList = res.data;}}
}

生成权限列表

<el-table-column type="expand"><el-row slot-scope="scope"><el-rowv-for="item in scope.row.children":key="item.id"style="border-bottom: 1px solid #eee"><!-- 一级权限 --><el-col :span="5"><el-tag closable @close="removeById(scope.row, item.id)">{{item.authName}}</el-tag><i class="el-icon-caret-right"></i></el-col><!-- 二级权限和三级权限 --><el-col :span="19"><el-rowv-for="item1 in item.children":key="item1.id"style="border-bottom: 1px solid #eee"><el-col :span="6"><el-tagclosabletype="success"@close="removeById(scope.row, item1.id)">{{ item1.authName }}</el-tag><i class="el-icon-caret-right"></i></el-col><el-col :span="14"><el-tagtype="warning"v-for="item2 in item1.children":key="item2.id"closable@close="removeById(scope.row, item2.id)">{{ item2.authName }}</el-tag></el-col></el-row></el-col></el-row></el-row></el-table-column>

添加权限删除功能

 //删除权限removeById(role, rightId) {// console.log(role,rightId);this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {roleAPT.removeRightsById(this.roleId,this.rightId).then((res) => {role.children = res.data.data;});}).catch(() => {this.$message({type: "info",message: "已取消删除",});});},

.完成树形结构弹窗
在element.js中引入Tree,注册Tree

<!-- 分配权限对话框 -->
<el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClose"><!-- 树形组件show-checkbox:显示复选框node-key:设置选中节点对应的值default-expand-all:是否默认展开所有节点:default-checked-keys 设置默认选中项的数组ref:设置引用 --><el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree><span slot="footer" class="dialog-footer"><el-button @click="setRightDialogVisible = false">取 消</el-button><el-button type="primary" @click="allotRights">确 定</el-button></span>
</el-dialog><script>
export default {data() {return {//角色列表数据roleList: [],//控制分配权限对话框的显示setRightDialogVisible: false,//权限树数据rightsList: [],//树形控件的属性绑定对象treeProps: {//通过label设置树形节点文本展示authNamelabel: 'authName',//设置通过children属性展示子节点信息children: 'children'},//设置树形控件中默认选中的内容defKeys: [],//保存正在操作的角色idroleId:''}},created() {this.getRoleList()},methods: {async getRoleList() {const { data: res } = await this.$http.get('roles')//如果返回状态为异常状态则报错并返回if (res.meta.status !== 200)return this.$message.error('获取角色列表失败')//如果返回状态正常,将请求的数据保存在data中// this.roleList = res.dataconsole.log(res.data)this.roleList = res.data},async removeRightById(role, rightId) {//弹窗提示用户是否要删除const confirmResult = await this.$confirm('请问是否要删除该权限','删除提示',{confirmButtonText: '确认删除',cancelButtonText: '取消',type: 'warning'}).catch(err => err)//如果用户点击确认,则confirmResult 为'confirm'//如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'if (confirmResult != 'confirm') {return this.$message.info('已经取消删除')}//用户点击了确定表示真的要删除//当发送delete请求之后,返回的数据就是最新的角色权限信息const { data: res } = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)if (res.meta.status !== 200)return this.$message.error('删除角色权限失败')//无需再重新加载所有权限//只需要对现有的角色权限进行更新即可role.children = res.data// this.getRoleList();},async showSetRightDialog(role) {//将role.id保存起来以供保存权限时使用this.roleId = role.id;  //获取所有权限的数据const { data: res } = await this.$http.get('rights/tree')//如果返回状态为异常状态则报错并返回if (res.meta.status !== 200) return this.$message.error('获取权限树失败')//如果返回状态正常,将请求的数据保存在data中this.rightsList = res.data//调用getLeafKeys进行递归,将三级权限添加到数组中this.getLeafKeys(role, this.defKeys)//当点击分配权限按钮时,展示对应的对话框this.setRightDialogVisible = trueconsole.log(this.defKeys)},getLeafKeys(node, arr) {//该函数会获取到当前角色的所有三级权限id并添加到defKeys中//如果当前节点不包含children属性,则表示node为三级权限if (!node.children) {return arr.push(node.id)}//递归调用node.children.forEach(item => this.getLeafKeys(item, arr))},setRightDialogClose() {//当用户关闭树形权限对话框的时候,清除掉所有选中状态this.defKeys = []},async allotRights() {//当用户在树形权限对话框中点击确定,将用户选择的//权限发送请求进行更新//获取所有选中及半选的内容const keys = [...this.$refs.treeRef.getCheckedKeys(),...this.$refs.treeRef.getHalfCheckedKeys()]//将数组转换为 , 拼接的字符串const idStr = keys.join(',')//发送请求完成更新const { data: res } = await this.$http.post(`roles/${this.roleId}/rights`,{ rids:idStr })if (res.meta.status !== 200)return this.$message.error('分配权限失败')this.$message.success("分配权限成功")this.getRoleList();//关闭对话框this.setRightDialogVisible = false;}}
}
</script>

商品分类
请求分类数据

data() {return {queryInfo: {query: "",// 当前的页数pagenum: 1,// 当前每页显示多少条数据pagesize: 10,},goodsList: [],total: 0,};},getGoodsList() {goodAPI.getGoodsList(this.queryInfo).then((res) => {console.log(res);this.goodsList = res.data.data.goods;this.total = res.data.data.total;});},

完成分页功能:

// 监听 pagesize 改变的事件handleSizeChange(newSize) {this.queryInfo.pagesize = newSize;this.getGoodsList();},// 监听 页码值 改变的事件handleCurrentChange(newPage) {this.queryInfo.pagenum = newPage;this.getGoodsList();},

完成添加分类

<template><div><!-- 面包屑导航区域 --><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><!-- 卡片视图区域 --><el-card><el-row><el-col><el-button type="primary" @click="showAddCateDialog">添加分类</el-button></el-col></el-row><!-- 表格 --><tree-table class="treeTable" :data="catelist" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border :show-row-hover="false"><!-- 是否有效 --><template slot="isok" slot-scope="scope"><i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color: lightgreen;"></i><i class="el-icon-error" v-else style="color: red;"></i></template><!-- 排序 --><template slot="order" slot-scope="scope"><el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag><el-tag type="success" size="mini" v-else-if="scope.row.cat_level===1">二级</el-tag><el-tag type="warning" size="mini" v-else>三级</el-tag></template><!-- 操作 --><template slot="opt"><el-button type="primary" icon="el-icon-edit" size="mini">编辑</el-button><el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button></template></tree-table><!-- 分页区域 --><el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="querInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="querInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"></el-pagination></el-card><!-- 添加分类的对话框 --><el-dialog title="添加分类" :visible.sync="addCateDialogVisible" width="50%" @close="addCateDialogClosed"><!-- 添加分类的表单 --><el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRef" label-width="100px"><el-form-item label="分类名称:" prop="cat_name"><el-input v-model="addCateForm.cat_name"></el-input></el-form-item><el-form-item label="父级分类:"><!-- options 用来指定数据源 --><!-- props 用来指定配置对象 --><el-cascader expand-trigger="hover" :options="parentCateList" :props="cascaderProps" v-model="selectedKeys" @change="parentCateChanged" clearable change-on-select></el-cascader></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addCateDialogVisible = false">取 消</el-button><el-button type="primary" @click="addCate">确 定</el-button></span></el-dialog></div>
</template><script>
import goodAPI from "../../http/API/Good"
export default {data() {return {// 查询条件querInfo: {type: 3,pagenum: 1,pagesize: 5},// 商品分类的数据列表,默认为空catelist: [],// 总数据条数total: 0,// 为table指定列的定义columns: [{label: '分类名称',prop: 'cat_name'},{label: '是否有效',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'isok'},{label: '排序',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'order'},{label: '操作',// 表示,将当前列定义为模板列type: 'template',// 表示当前这一列使用模板名称template: 'opt'}],// 控制添加分类对话框的显示与隐藏addCateDialogVisible: false,// 添加分类的表单数据对象addCateForm: {// 将要添加的分类的名称cat_name: '',// 父级分类的Idcat_pid: 0,// 分类的等级,默认要添加的是1级分类cat_level: 0},// 添加分类表单的验证规则对象addCateFormRules: {cat_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]},// 父级分类的列表parentCateList: [],// 指定级联选择器的配置对象cascaderProps: {value: 'cat_id',label: 'cat_name',children: 'children'},// 选中的父级分类的Id数组selectedKeys: []}},created() {this.getCateList()},methods: {// 获取商品分类数据async getCateList() {const { data: res } = await goodAPI.getCateList(this.querInfo)if (res.meta.status !== 200) {return this.$message.error('获取商品分类失败!')}console.log(res.data)// 把数据列表,赋值给 catelistthis.catelist = res.data.result// 为总数据条数赋值this.total = res.data.total},// 监听 pagesize 改变handleSizeChange(newSize) {this.querInfo.pagesize = newSizethis.getCateList()},// 监听 pagenum 改变handleCurrentChange(newPage) {this.querInfo.pagenum = newPagethis.getCateList()},// 点击按钮,展示添加分类的对话框showAddCateDialog() {// 先获取父级分类的数据列表this.getParentCateList()// 再展示出对话框this.addCateDialogVisible = true},// 获取父级分类的数据列表async getParentCateList() {const { data: res } = await goodAPI.getCateList( this.querInfo,{params: { type: 2 }})if (res.meta.status !== 200) {return this.$message.error('获取父级分类数据失败!')}console.log(res.data)this.parentCateList = res.data},// 选择项发生变化触发这个函数parentCateChanged() {console.log(this.selectedKeys)// 如果 selectedKeys 数组中的 length 大于0,证明选中的父级分类// 反之,就说明没有选中任何父级分类if (this.selectedKeys.length > 0) {// 父级分类的Idthis.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]// 为当前分类的等级赋值this.addCateForm.cat_level = this.selectedKeys.length} else {// 父级分类的Idthis.addCateForm.cat_pid = 0// 为当前分类的等级赋值this.addCateForm.cat_level = 0}},// 点击按钮,添加新的分类addCate() {this.$refs.addCateFormRef.validate(async valid => {if (!valid) returnconst { data: res } = await this.$http.post('categories', this.addCateForm)if (res.meta.status !== 201) {return this.$message.error('添加分类失败!')}this.$message.success('添加分类成功!')this.getCateList()this.addCateDialogVisible = false})},// 监听对话框的关闭事件,重置表单数据addCateDialogClosed() {this.$refs.addCateFormRef.resetFields()this.selectedKeys = []this.addCateForm.cat_level = 0this.addCateForm.cat_pid = 0}}
}
</script>

参数管理
首先完成参数管理的布局

<template><div><!-- 面包屑导航区域 --><Break></Break><!-- 卡片区域 --><el-card class="box-card"><!-- 警告区域 --><el-alerttitle="注意:只允许为第三级分类设置相关参数!"type="warning"effect="dark":closable="false"show-icon></el-alert><div class="selectType"><span>选择商品分类:</span><!-- 级联选择器 --><el-cascaderv-model="selectValue":options="selectOptions":props="cateProps"@change="selectChange"></el-cascader></div><el-tabstype="card"class="tabsCard"v-model="activeName"@tab-click="tabClick"><!-- 动态参数 --><el-tab-pane label="动态参数" name="many"><!-- 添加参数 --><el-buttontype="primary"size="mini"class="btn":disabled="btnDisabled"@click="addDialogVisible = true">添加参数</el-button><!-- 添加参数 表格 --><el-table :data="manyParams" style="width: 100%" border><el-table-column type="expand"><template slot-scope="scope"><el-tag:key="index"v-for="(item, index) in scope.row.attr_vals"closable:disable-transitions="false"@close="attrHandleClose(tag)">{{ item }}</el-tag><el-inputclass="input-new-tag"v-if="scope.row.inputVisible"v-model="scope.row.inputValue"ref="saveTagInput"size="small"@keyup.enter.native="handleInputConfirm(scope.row)"@blur="handleInputConfirm(scope.row)"></el-input><el-buttonv-elseclass="button-new-tag"size="small"@click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><el-table-column type="index" label="#"> </el-table-column><el-table-column label="参数名称" prop="attr_name"></el-table-column><el-table-column label="操作" prop="id"><template v-slot="scope"><el-buttontype="primary"size="mini"icon="el-icon-edit"@click="editParamsDialog(scope.row)">修改</el-button><el-buttontype="danger"size="mini"icon="el-icon-delete"@click="delParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane><!-- 静态属性 --><el-tab-pane label="静态属性" name="only"><!-- 添加属性 --><el-buttontype="primary"size="mini"class="btn":disabled="btnDisabled"@click="addDialogVisible = true">添加属性</el-button><!-- 添加属性表格 --><el-table :data="onlyParams" style="width: 100%" border><el-table-column type="expand"><template slot-scope="scope"><el-tag:key="index"v-for="(item, index) in scope.row.attr_vals"closable:disable-transitions="false"@close="attrHandleClose(tag)">{{ item }}</el-tag><el-inputclass="input-new-tag"v-if="scope.row.inputVisible"v-model="scope.row.inputValue"ref="saveTagInput"size="small"@keyup.enter.native="handleInputConfirm(scope.row)"@blur="handleInputConfirm(scope.row)"></el-input><el-buttonv-elseclass="button-new-tag"size="small"@click="showInput(scope.row)">+ New Tag</el-button></template></el-table-column><el-table-column type="index" label="#"> </el-table-column><el-table-column label="参数名称" prop="attr_name"></el-table-column><el-table-column label="操作" prop="id"><template v-slot="scope"><el-buttontype="primary"size="mini"icon="el-icon-edit"@click="editParamsDialog(scope.row)">修改</el-button><el-buttontype="danger"size="mini"icon="el-icon-delete"@click="delParams(scope.row.attr_id)">删除</el-button></template></el-table-column></el-table></el-tab-pane></el-tabs></el-card><!-- 修改动态参数弹出框 --><el-dialog:title="'修改' + titleText":visible.sync="editManyDialogVisible"width="30%"><el-form:model="editManyRuleForm":rules="editManyRules"ref="edifManyRuleFormRef"label-width="100px"class="demo-ruleForm"><el-form-item label="动态参数" prop="attr_name"><el-input v-model="editManyRuleForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editManyDialogVisible = false">取 消</el-button><el-button type="primary" @click="editMany">确 定</el-button></span></el-dialog><!-- 添加参数弹出框 --><el-dialog:title="'添加' + titleText":visible.sync="addDialogVisible"width="30%"><el-form:model="addParamsForm":rules="editManyRules"ref="addRuleFormRef"label-width="100px"class="demo-ruleForm"><el-form-item label="动态参数" prop="attr_name"><el-input v-model="addParamsForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">取 消</el-button><el-button type="primary" @click="addParams">确 定</el-button></span></el-dialog></div>
</template>

完成级联选择框

<!-- 选择商品分类区域 -->
<el-row class="cat_opt"><el-col><span>选择商品分类:</span><!-- 选择商品分类的级联选择框 --><el-cascader expandTrigger='hover' v-model="selectedCateKeys" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader></el-col><el-col></el-col>
</el-row>
......
<script>
export default {data() {return {//分类列表cateList:[],//用户在级联下拉菜单中选中的分类idselectedCateKeys:[],//配置级联菜单中数据如何展示cateProps: {value: 'cat_id',label: 'cat_name',children: 'children'}}},created() {this.getCateList()},methods: {async getCateList(){//获取所有的商品分类列表const { data: res } = await this.$http.get('categories')if (res.meta.status !== 200) {return this.$message.error('获取分类数据失败')}//将数据列表赋值给cateListthis.cateList = res.data// //保存总数据条数// this.total = res.data.total//   console.log(res.data);},handleChange(){//当用户在级联菜单中选择内容改变时触发console.log(this.selectedCateKeys);}}
}
</script>

展示参数
展示动态参数数据以及静态属性数据

<!-- tab页签区域 -->
<el-tabs v-model="activeName" @tab-click="handleTabClick"><!-- 添加动态参数的面板 将标签页改为many --><el-tab-pane label="动态参数" name="many"><el-button size="mini" type="primary" :disabled="isButtonDisabled">添加参数</el-button><!-- 动态参数表格 --><el-table :data="manyTableData" border stripe><!-- 展开行 --><el-table-column type="expand"></el-table-column><!-- 索引列 --><el-table-column type="index"></el-table-column><el-table-column label="参数名称" prop="attr_name"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button></template></el-table-column></el-table></el-tab-pane><!-- 添加静态属性的面板 将标签页改为only --><el-tab-pane label="静态属性" name="only"><el-button size="mini" type="primary" :disabled="isButtonDisabled">添加属性</el-button><!-- 静态属性表格 --><el-table :data="onlyTableData" border stripe><!-- 展开行 --><el-table-column type="expand"></el-table-column><!-- 索引列 --><el-table-column type="index"></el-table-column><el-table-column label="属性名称" prop="attr_name"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button><el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button></template></el-table-column></el-table></el-tab-pane></el-tabs><script>
export default {data() {return {......//tab页签激活显示的页签项activeName: 'many',//用来保存动态参数数据manyTableData: [],//用来保存静态属性数据onlyTableData: []  }methods: {.......async handleChange() {//当用户在级联菜单中选择内容改变时触发console.log(this.selectedCateKeys)//发送请求,根据用户选择的三级分类和面板获取参数数据const { data: res } = await this.$http.get(`categories/${this.cateId}/attributes`,{ params: { sel: this.activeName } })if (res.meta.status !== 200) {return this.$message.error('获取参数列表数据失败')}console.log(res.data)if (this.activeName === 'many') {//获取的是动态参数this.manyTableData = res.data} else if (this.activeName === 'only') {//获取的是静态属性this.onlyTableData = res.data}},handleTabClick() {console.log(this.activeName)this.handleChange()}},computed: {//添加计算属性用来获取按钮禁用与否isButtonDisabled() {return this.selectedCateKeys.length !== 3},//获取选中的三级分类idcateId() {if (this.selectedCateKeys.length === 3) {return this.selectedCateKeys[this.selectedCateKeys.length - 1]}return null}}

添加参数

<!-- 添加参数或属性对话框 -->
<el-dialog :title="'添加'+titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"><!-- 添加表单 --><el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="addForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">取 消</el-button><el-button type="primary" @click="addParams">确 定</el-button></span>
</el-dialog>export default {data() {return {.......//控制添加参数.属性对话框的显示或隐藏addDialogVisible: false,//添加参数的表单数据对象addForm: {attr_name: ''},//添加表单验证规则addFormRules: {attr_name: [{ required: true, message: '请输入名称', trigger: 'blur' }]}}},methods: {.......addParams() {//当用户点击对话框中的确定时,校验表单this.$refs.addFormRef.validate(async valid => {//校验不通过,returnif (!valid) return//校验通过,发送请求完成添加参数或者属性const { data: res } = this.$http.post(`categories/${this.cateId}/attributes`,{ attr_name: this.addForm.attr_name, attr_sel: this.activeName,attr_vals: "a,b,c" })console.log(res)if (res.meta.status !== 201) {return this.$message.error('添加' + this.titleText + '数据失败')}this.$message.success('添加' + this.titleText + '数据成功')this.addDialogVisible = falsethis.getCateList()})}}

编辑参数

<!-- 修改参数或属性对话框 -->
<el-dialog :title="'修改'+titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"><!-- 添加表单 --><el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px"><el-form-item :label="titleText" prop="attr_name"><el-input v-model="editForm.attr_name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">取 消</el-button><el-button type="primary" @click="editParams">确 定</el-button></span>
</el-dialog>export default {data() {return {.......//控制修改参数.属性对话框的显示或隐藏editDialogVisible:false,//修改参数.属性对话框中的表单editForm:{attr_name:''},//修改表单的验证规则editFormRules:{attr_name:[{ required: true, message: '请输入名称', trigger: 'blur' }]}}},methods: {.......async showEditDialog(attr_id){//发起请求获取需要修改的那个参数数据const {data:res} = await this.$http.get(`categories/${this.cateId}/attributes/${attr_id}`,{params:{ attr_sel:this.activeName }})if (res.meta.status !== 200) {return this.$message.error('获取参数数据失败')}this.editForm = res.data;//显示修改参数.属性对话框this.editDialogVisible = true;},editDialogClosed(){//当关闭修改参数.属性对话框时this.$refs.editFormRef.resetFields()},editParams(){//验证表单this.$refs.editFormRef.validate(async valid => {if(!valid) return;//发送请求完成修改const {data:res} = await this.$http.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`,{attr_name:this.editForm.attr_name,attr_sel:this.activeName})if (res.meta.status !== 200) {return this.$message.error('获取参数数据失败')}this.$message.success('修改' + this.titleText + '数据成功')this.editDialogVisible = falsethis.handleChange();})}}

删除参数

给两个删除按钮添加事件
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button>添加对应的事件处理函数
async removeParams(attr_id){//根据id删除对应的参数或属性//弹窗提示用户是否要删除const confirmResult = await this.$confirm('请问是否要删除该'+this.titleText,'删除提示',{confirmButtonText: '确认删除',cancelButtonText: '取消',type: 'warning'}).catch(err => err)//如果用户点击确认,则confirmResult 为'confirm'//如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel'if (confirmResult != 'confirm') {return this.$message.info('已经取消删除')}//没有取消就是要删除,发送请求完成删除const {data:res} = await this.$http.delete(`categories/${this.cateId}/attributes/${attr_id}`)if (res.meta.status !== 200) {return this.$message.error('删除参数数据失败')}this.$message.success('删除' + this.titleText + '数据成功')this.handleChange()
}

商品列表

<template><div><!-- 面包屑导航区域 --><Break></Break><!-- 卡片视图区域 --><el-card><!-- 搜索与添加区域 --><el-row :gutter="20"><el-col :span="8"><el-input placeholder="请输入内容" clearable><el-button slot="append" icon="el-icon-search"></el-button></el-input></el-col><el-col :span="4"><el-button type="primary" @click="addGoods">添加商品</el-button></el-col></el-row><!-- 用户列表区域 --><el-table :data="goodsList" border><el-table-column label="#" type="index"></el-table-column><el-table-columnlabel="商品名称"prop="goods_name"width="672px"></el-table-column><el-table-columnlabel="商品价格(元)"prop="goods_price"width="100px"></el-table-column><el-table-columnlabel="商品重量"prop="goods_weight"width="100px"></el-table-column><el-table-columnlabel="创建时间"prop="add_time"width="150px"></el-table-column><el-table-column label="操作" width="150px"><template slot-scope="scope"><!-- 修改按钮 --><el-buttontype="primary"icon="el-icon-edit"size="mini"></el-button><!-- 删除按钮 --><el-buttontype="danger"icon="el-icon-delete"size="mini"@click="removeGoods(scope.row.id)"></el-button></template></el-table-column></el-table><!-- 分页器 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[5, 10, 20, 50]":page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></el-card></div>
</template><script>
import goodAPI from "../../http/API/Good"
import Break from "../bread";
export default {components: {Break,},data() {return {queryInfo: {query: "",// 当前的页数pagenum: 1,// 当前每页显示多少条数据pagesize: 10,},goodsList: [],total: 0,};},methods: {// 监听 pagesize 改变的事件handleSizeChange(newSize) {this.queryInfo.pagesize = newSize;this.getGoodsList();},// 监听 页码值 改变的事件handleCurrentChange(newPage) {this.queryInfo.pagenum = newPage;this.getGoodsList();},//获取商品列表getGoodsList() {goodAPI.getGoodsList(this.queryInfo).then((res) => {console.log(res);this.goodsList = res.data.data.goods;this.total = res.data.data.total;});},//添加商品addGoods() {this.$router.push("/goods/addgoods");},},created() {this.getGoodsList();},mounted() {},
};
</script> <style scoped></style>

在mian.js添加过滤器:将时间戳转换为年月日

Vue.filter('dateFormat',function(originVal){const dt = new Date(originVal)const y = dt.getFullYear()const m = (dt.getMonth()+1+'').padStart(2,'0')const d = (dt.getDate()+'').padStart(2,'0')const hh = (dt.getHours()+'').padStart(2,'0')const mm = (dt.getMinutes()+'').padStart(2,'0')const ss = (dt.getSeconds()+'').padStart(2,'0')return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
})

实现删除商品

 //删除商品removeGoods(id) {this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {goodAPI.deleteGoods(id).then((res) => {console.log(res);});}).then(() => {this.$message({type: "success",message: "删除成功!",});this.getGoodsList();}).catch(() => {this.$message({type: "info",message: "已取消删除",});});},

添加商品:
点击添加跳转到另一个页面

//添加商品addGoods() {this.$router.push("/goods/addgoods");},

在添加组件中进行布局:

<template><div><!-- 面包屑导航区域 --><Break></Break><!-- 卡片区域 --><el-card><!-- 提示区 --><el-alerttitle="添加商品信息"type="info"centershow-icon:closable="false"></el-alert><!-- 步骤条 --><el-steps:space="200":active="activeIndex - 0"finish-status="success"align-center><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><!-- tab栏区域 --><el-form:model="addForm":rules="addrules"ref="addGoodsForm"label-width="100px"label-position="top"><el-tabsv-model="activeIndex":tab-position="'left'"@tab-click="tabClick":before-leave="beforeTabLeave"><el-tab-pane label="基本信息" name="0"><el-form-item label="商品名称 " prop="goods_name"><el-input v-model="addForm.goods_name"></el-input></el-form-item><el-form-item label="商品价格" prop="goods_price"><el-input v-model="addForm.goods_price" type="number"></el-input></el-form-item><el-form-item label="商品重量" prop="goods_weight"><el-input v-model="addForm.goods_weight" type="number"></el-input></el-form-item><el-form-item label="商品数量" prop="goods_number"><el-input v-model="addForm.goods_number" type="number"></el-input></el-form-item><el-form-item label="商品分类" prop><el-cascaderexpand-trigger="hover"v-model="addForm.goods_cat":options="cateList":props="cateProps"@change="handleChange"></el-cascader></el-form-item></el-tab-pane><el-tab-pane label="商品参数" name="1"><el-form-item:label="item.attr_name"v-for="item in manyTableData":key="item.attr_id"><!-- 复选框组 --><el-checkbox-group v-model="item.attr_vals"><el-checkbox:label="cb"v-for="(cb, i) in item.attr_vals":key="i"></el-checkbox></el-checkbox-group></el-form-item></el-tab-pane><el-tab-pane label="商品属性" name="2"><el-form-item:label="item.attr_name"v-for="item in onlyTableData":key="item.attr_id"><el-input v-model="item.attr_vals"></el-input></el-form-item></el-tab-pane><el-tab-pane label="商品图片" name="3"><!-- 商品图片上传action:指定图片上传api接口:on-preview : 当点击图片时会触发该事件进行预览操作,处理图片预览:on-remove : 当用户点击图片右上角的X号时触发执行:on-success:当用户点击上传图片并成功上传时触发list-type :设置预览图片的方式:headers :设置上传图片的请求头 --><el-uploadaction="http://127.0.0.1:8888/api/private/v1/upload":on-preview="handlePreview":on-remove="handleRemove":on-success="handleSuccess"list-type="picture":headers="headerObj"><el-button size="small" type="primary">点击上传</el-button></el-upload></el-tab-pane><el-tab-pane label="商品内容" name="4"><!-- 富文本编辑器组件 --><quill-editor v-model="addForm.goods_introduce"></quill-editor><!-- 添加商品按钮 --><el-button type="primary" class="btnAdd" @click="handAdd">添加商品</el-button></el-tab-pane></el-tabs></el-form></el-card></div>
</template><script>
import goodAPI from "../../http/API/Good"
import Break from "../bread";
export default {components: {Break,},data() {return {activeIndex: "0",addForm: {goods_name: "",goods_price: 0,goods_weight: 0,goods_number: 0,//商品所属分类数组goods_cat: [],pics: [],goods_introduce: "",},addrules: {goods_name: [{ required: true, message: "请输入商品名称", trigger: "blur" },],goods_price: [{ required: true, message: "请输入商品价格", trigger: "blur" },],goods_weight: [{ required: true, message: "请输入商品重量", trigger: "blur" },],goods_number: [{ required: true, message: "请输入商品数量", trigger: "blur" },],goods_cat: [{ required: true, message: "请选择商品分类", trigger: "blur" },],},cateList: [],cateProps: {label: "cat_name",value: "cat_id",children: "children",},//动态参数列表数据manyTableData: [],//静态属性列表数据onlyTableData: [],//图片上传组件的headers请求头对象headerObj: { Authorization: window.sessionStorage.getItem("token") },//保存预览图片的url地址previewPath: "",//控制预览图片对话框的显示和隐藏previewVisible: false,};},methods: {getGoodsList() {this.$http.get("categories").then((res) => {this.cateList = res.data.data;});},//级联选择器选中项变化handleChange() {if (this.addForm.goods_cat.length !== 3) {this.addForm.goods_cat = [];}},beforeTabLeave(activeName, oldActiveName) {if (oldActiveName === "0" && this.addForm.goods_cat.length !== 3) {this.$message.error("请选择商品分类");return false;}},//商品参数tabClick() {if (this.activeIndex === "1") {goodAPI.getParamsList({params: { sel: "many" },}).then((res) => {res.data.data.forEach((item) => {item.attr_vals =item.attr_vals.length === 0 ? [] : item.attr_vals.split(" ");});this.manyTableData = res.data.data;});} else if (this.activeIndex === "2") {goodAPI.getParamsList({params: { sel: "only" },}).then((res) => {console.log(res);this.onlyTableData = res.data.data;});}},handlePreview(file) {this.previewPath = file.response.data.url;this.previewVisible = true;},handleRemove(file) {const filePath = file.response.data.tmp_path;const index = this.addForm.pics.findIndex((item) => item.pic === filePath);this.addForm.pics.splice(index, 1);},handleSuccess(response) {this.addForm.pics.push({ pic: response.data.tmp_path });},handAdd() {this.$refs.addGoodsForm.validate(async (valid) => {if (!valid) {return this.$message.error("请填写必要的选项!");}// console.log(this.addForm);let form = this.deepClone(this.addForm);form.goods_cat = form.goods_cat.join(",");form.attrs = [];this.manyTableData.forEach((item) => {form.attrs.push({attr_id: item.attr_id,attr_value: item.attr_vals.join(","),});});this.onlyTableData.forEach((item) => {form.attrs.push({attr_id: item.attr_id,attr_value: item.attr_vals,});});console.log(form);const { data: res } = await goodAPI.addGoods(form);// console.log(res);if (res.meta.status !== 201) return this.$message.error(res.meta.msg);this.$message.success("添加商品成功!");this.$router.push("/goods");});},deepClone(obj) {if (typeof obj !== "object" || obj == null) {return obj;}let result;if (obj instanceof Array) {result = [];} else {result = {};}for (let key in obj) {if (obj.hasOwnProperty(key)) {result[key] = this.deepClone(obj[key]);}}return result;},},created() {this.getGoodsList();},computed: {cateId() {if (this.addForm.goods_cat.length === 3) {return this.addForm.goods_cat[2];}return null;},},mounted() {},
};
</script> <style scoped>
.el-card {margin-top: 15px;
}
.el-steps {margin: 15px 0;
}
.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;
}
.avatar-uploader .el-upload:hover {border-color: #409eff;
}
.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;
}
.avatar {width: 178px;height: 178px;display: block;
}
.ql-editor {min-height: 300px;
}
.btnAdd {margin-top: 15px;
}
</style>

完成图片上传
使用upload组件完成图片上传
在element.js中引入upload组件,并注册
因为upload组件进行图片上传的时候并不是使用axios发送请求
所以,我们需要手动为上传图片的请求添加token,即为upload组件添加headers属性

//在页面中添加upload组件,并设置对应的事件和属性
<el-tab-pane label="商品图片" name="3"><!-- 商品图片上传action:指定图片上传api接口:on-preview : 当点击图片时会触发该事件进行预览操作,处理图片预览:on-remove : 当用户点击图片右上角的X号时触发执行:on-success:当用户点击上传图片并成功上传时触发list-type :设置预览图片的方式:headers :设置上传图片的请求头 --><el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" list-type="picture" :headers="headerObj"><el-button size="small" type="primary">点击上传</el-button></el-upload>
</el-tab-pane>
//在el-card卡片视图下面添加对话框用来预览图片
<!-- 预览图片对话框 -->
<el-dialog title="图片预览" :visible.sync="previewVisible" width="50%"><img :src="previewPath" class="previewImg" />
</el-dialog>//在data中添加数据
data(){return {......//添加商品的表单数据对象addForm: {goods_name: '',goods_price: 0,goods_weight: 0,goods_number: 0,goods_cat: [],//上传图片数组pics: []},//上传图片的url地址uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',//图片上传组件的headers请求头对象headerObj: { Authorization: window.sessionStorage.getItem('token') },//保存预览图片的url地址previewPath: '',//控制预览图片对话框的显示和隐藏previewVisible:false}
},
//在methods中添加事件处理函数
methods:{.......handlePreview(file) {//当用户点击图片进行预览时执行,处理图片预览//形参file就是用户预览的那个文件this.previewPath = file.response.data.url//显示预览图片对话框this.previewVisible = true},handleRemove(file) {//当用户点击X号删除时执行//形参file就是用户点击删除的文件//获取用户点击删除的那个图片的临时路径const filePath = file.response.data.tmp_path//使用findIndex来查找符合条件的索引const index = this.addForm.pics.findIndex(item => item.pic === filePath)//移除索引对应的图片this.addForm.pics.splice(index, 1)},handleSuccess(response) {this.addForm.pics.push({ pic: response.data.tmp_path })}
}

使用富文本插件
引入并注册vue-quill-editor

//导入vue-quill-editor(富文本编辑器)
import VueQuillEditor from 'vue-quill-editor'
//导入vue-quill-editor的样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
......
//全局注册组件
Vue.component('tree-table', TreeTable)
//全局注册富文本组件
Vue.use(VueQuillEditor)
<!-- 富文本编辑器组件 -->
<el-tab-pane label="商品内容" name="4"><!-- 富文本编辑器组件 --><quill-editor v-model="addForm.goods_introduce"></quill-editor><!-- 添加商品按钮 --><el-button type="primary" class="btnAdd">添加商品</el-button>
</el-tab-pane>//在数据中添加goods_introduce
//添加商品的表单数据对象
addForm: {goods_name: '',goods_price: 0,goods_weight: 0,goods_number: 0,goods_cat: [],//上传图片数组pics: [],//商品的详情介绍goods_introduce:''
}
//在global.css样式中添加富文本编辑器的最小高度
.ql-editor{min-height: 300px;
}
//给添加商品按钮添加间距
.btnAdd{margin-top:15px;
}

添加商品:

//打开Add.vue,导入lodash
<script>
//官方推荐将lodash导入为_
import _ from 'lodash'//给添加商品按钮绑定点击事件
<!-- 添加商品按钮 -->
<el-button type="primary" class="btnAdd" @click="add">添加商品</el-button>
//编写点击事件完成商品添加
add(){this.$refs.addFormRef.validate(async valid=>{if(!valid) return this.$message.error("请填写必要的表单项!")//将addForm进行深拷贝,避免goods_cat数组转换字符串之后导致级联选择器报错const form = _.cloneDeep(this.addForm)//将goods_cat从数组转换为"1,2,3"字符串形式form.goods_cat = form.goods_cat.join(",")//处理attrs数组,数组中需要包含商品的动态参数和静态属性//将manyTableData(动态参数)处理添加到attrsthis.manyTableData.forEach(item=>{form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals.join(" ") }) })//将onlyTableData(静态属性)处理添加到attrsthis.onlyTableData.forEach(item=>{form.attrs.push({ attr_id:item.attr_id, attr_value:item.attr_vals }) })//发送请求完成商品的添加,商品名称必须是唯一的const {data:res} = await this.$http.post('goods',form)if(res.meta.status !== 201){return this.$message.error('添加商品失败')}this.$message.success('添加商品成功')//编程式导航跳转到商品列表this.$router.push('/goods')})
}
</script>

订单列表
实现数据展示及分页

<template><div><!-- 面包屑导航区域 --><Break></Break><!-- 卡片视图区域 --><el-card class="box-card"><el-row><el-col :span="8"><el-input placeholder="请输入内容"><el-button slot="append" icon="el-icon-search"></el-button></el-input></el-col></el-row><!-- 订单列表数据 --><el-table :data="orderList" border stripe class="tableList"><el-table-column type="index"></el-table-column><el-table-column label="订单编号" prop="order_number"></el-table-column><el-table-column label="订单价格" prop="order_price"></el-table-column><el-table-column label="是否付款" prop="pay_status"><template slot-scope="scope"><el-tag type="success" v-if="scope.row.pay_status === '1'">已付款</el-tag><el-tag type="danger" v-else>未付款</el-tag></template></el-table-column><el-table-column label="是否发货" prop="is_send"><template slot-scope="scope"><template>{{ scope.row.is_send }}</template></template></el-table-column><el-table-column label="下单时间" prop="create_time"><template slot-scope="scope">{{ scope.row.create_time | dataForm }}</template></el-table-column><el-table-column label="操作"><template><el-buttonsize="mini"type="primary"icon="el-icon-edit"@click="showEdit"></el-button><el-buttonsize="mini"type="success"icon="el-icon-location"@click="showProgress"></el-button></template></el-table-column></el-table><!-- 分页区域 --><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="queryInfo.pagenum":page-sizes="[5, 10, 15]":page-size="queryInfo.pagesize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></el-card><!-- 修改地址的对话框 --><el-dialogtitle="修改地址":visible.sync="editDialogVisible"width="50%"@close="editDialogClosed"><el-form:model="EditRuleForm":rules="editFormRules"ref="addressFormRef"label-width="100px"><el-form-item label="省市区/县" prop="address1"><el-cascader:options="cityData"v-model="EditRuleForm.address1"></el-cascader></el-form-item><el-form-item label="详细地址" prop="address2"><el-input v-model="EditRuleForm.address2"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">取 消</el-button><el-button type="primary" @click="editDialogVisible = false">确 定</el-button></span></el-dialog><!-- 展示物流进度的对话框 --><el-dialog title="物流进度" :visible.sync="progressVisible" width="50%"><!-- 时间线 --><el-timeline><el-timeline-itemv-for="(activity, index) in progressInfo":key="index":timestamp="activity.time">{{ activity.context }}</el-timeline-item></el-timeline></el-dialog></div>
</template><script>
import cityData from "./citydata.js";
import Break from "../bread";
import ordersAPI from "../../http/API/Order"
export default {components: {Break,},data() {return {queryInfo: {query: "",pagenum: 1,pagesize: 10,},total: 0,orderList: [],editDialogVisible: false,EditRuleForm: {address1: [],address2: "",},editFormRules: {address1: [{ required: true, message: "请选择省市区县", trigger: "blur" },],address2: [{ required: true, message: "请填写详细地址", trigger: "blur" },],},cityData,progressVisible: false,progressInfo: [],};},created() {this.getOrderList();},methods: {async getOrderList() {let { data: res } = await ordersAPI.getOrder(this.queryInfo);// console.log(res);if (res.meta.status !== 200) {return this.$message.error("获取订单列表失败!");}console.log(res);this.total = res.data.total;this.orderList = res.data.goods;},handleSizeChange(newSize) {this.queryInfo.pagesize = newSize;this.getOrderList();},handleCurrentChange(newPage) {this.queryInfo.pagenum = newPage;this.getOrderList();},showEdit() {this.editDialogVisible = true;},editDialogClosed() {this.$refs.addressFormRef.resetFields();},async showProgress() {let { data: res } = await ordersAPI.getOrder();if (res.meta.status !== 200) {return this.$message.error("获取物流进度失败!");}this.progressInfo = res.data;this.progressVisible = true;console.log(this.progressInfo);},},
};
</script><style  scoped>
.el-cascader {width: 100%;
}
.box-card{margin-top: 20px;
}
.tableList{margin-top: 20px;
}
</style>

数据统计
导入ECharts并使用

<template><div><!-- 面包屑导航区域 --><Break></Break><el-card><div id="myChart" :style="{ width: '700px', height: '400px' }"></div></el-card></div>
</template><script>
import reoprtsAPI from "../../http/API/reports"
import Break from "../bread";
import _ from "lodash";
export default {components: {Break,},data() {return {options: {title: {text: "用户来源",},tooltip: {trigger: "axis",axisPointer: {type: "cross",label: {backgroundColor: "#E9EEF3",},},},grid: {left: "3%",right: "4%",bottom: "3%",containLabel: true,},xAxis: [{boundaryGap: false,},],yAxis: [{type: "value",},],},};},mounted() {// 基于准备好的dom,初始化echarts实例let myChart = this.$echarts.init(document.getElementById("myChart"));reoprtsAPI.getreports().then((res) => {this.reportsList = res.data.data;//准备数据和配置项const result = _.merge(res.data.data, this.options);// 展示数据myChart.setOption(result);});},methods: {},
};
</script> <style scoped>
.el-card {margin-top: 15px;
}
</style>

完成以后进行项目优化
生产打包报告,根据报告优化项目
第三方库启用cdn
element-ui组件按需引入
路由懒加载
首页内容定制
添加进度条

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import "./assets/style.css";
import "./assets/fonts/iconfont.css";
import axios from 'axios'
import './plugins/element.js'
// import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import * as echarts from 'echarts';
import TreeTable from 'vue-table-with-tree-grid';
import VueQuillEditor from 'vue-quill-editor';
axios.defaults.baseURL = 'https://www.liulongbin.top:8888/api/private/v1/'
axios.interceptors.request.use((config) => {//当进入request拦截器,表示发送了请求,我们就开启进度条NProgress.start()//为请求头对象,添加token验证的Authorization字段config.headers.Authorization = localStorage.getItem('token');//必须返回configreturn config
});//在response拦截器中,隐藏进度条
axios.interceptors.response.use(config => {//当进入response拦截器,表示请求已经结束,我们就结束进度条NProgress.done()return config
})

执行build
打开Babel.config.js

生产打包报告
修改webpack的默认配置

```javascript
// vue.config.js
module.exports = {publicPath: "./", //部署应用包时的基本相对路径地址chainWebpack: confing => {//修改入口文件//when判断环境条件,配置//发布模式confing.when(process.env.NODE_ENV === "production", confing => {//entry找到当前默认的打包入口, 调用clear清空默认打包入口, 使用add添加新的打包入口//add方法后面参数为新的入口文件 地址confing.entry('app').clear().add('./src/main-prod.js');//在打包的时候首先检查window上是否有下列组件(CDN提前挂载),如果有的话就不在重新importconfing.set('externals', {vue: 'Vue','vue-router': 'VueRouter',axios: 'axios',lodash: '_',echarts: 'echarts',nprogress: 'NProgress','vue-quill-editor': 'VueQuillEditor'});//插件confing.plugin('html').tap(args => {args[0].isProd = true;return args})});// 开发模式confing.when(process.env.NODE_ENV === "development", confing => {confing.entry('app').clear().add('./src/main-dev.js');confing.plugin('html').tap(args => {//判断是否为发布模式手动添加属性值为是否是发布模式args[0].isProd = false;return args})});}
}

加载外部cdn

module.exports = {chainWebpack:config=>{//发布模式config.when(process.env.NODE_ENV === 'production',config=>{//entry找到默认的打包入口,调用clear则是删除默认的打包入口//add添加新的打包入口config.entry('app').clear().add('./src/main-prod.js')//使用externals设置排除项config.set('externals',{vue:'Vue','vue-router':'VueRouter',axios:'axios',lodash:'_',echarts:'echarts',nprogress:'NProgress','vue-quill-editor':'VueQuillEditor'})})//开发模式config.when(process.env.NODE_ENV === 'development',config=>{config.entry('app').clear().add('./src/main-dev.js')})}
}

然后打开public/index.html添加外部cdn引入代码

<!DOCTYPE html>
<html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>电商管理系统</title><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"><link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.15.1/theme-chalk/index.css"><script src="https://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script><script src="https://cdn.staticfile.org/vue-router/3.2.0/vue-router.min.js"></script><script src="https://cdn.staticfile.org/axios/0.21.1/axios.min.js"></script><script src="https://cdn.staticfile.org/lodash.js/4.17.11/lodash.min.js"></script><script src="https://cdn.staticfile.org/echarts/5.0.2/echarts.min.js"></script><script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script><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.4/dist/vue-quill-editor.js"></script><script src="https://cdn.staticfile.org/element-ui/2.15.1/index.js"></script><link href="css/goods.20d11ae6.css" rel="prefetch"><link href="css/login_home_welcome.f172da36.css" rel="prefetch"><link href="css/order.7d547f74.css" rel="prefetch"><link href="css/power.9d8f660a.css" rel="prefetch"><link href="css/report.d5a63329.css" rel="prefetch"><link href="css/user.feb292b3.css" rel="prefetch"><link href="js/goods.e452d00b.js" rel="prefetch"><link href="js/login_home_welcome.bb7e7edd.js" rel="prefetch"><link href="js/order.6115a44b.js" rel="prefetch"><link href="js/power.d3b71a1e.js" rel="prefetch"><link href="js/report.0e8de479.js" rel="prefetch"><link href="js/user.0bb9f204.js" rel="prefetch"><link href="css/app.599ad7ef.css" rel="preload" as="style"><link href="css/chunk-vendors.c470e980.css" rel="preload" as="style"><link href="js/app.4942fcdb.js" rel="preload" as="script"><link href="js/chunk-vendors.24e8034f.js" rel="preload" as="script"><link href="css/chunk-vendors.c470e980.css" rel="stylesheet"><link href="css/app.599ad7ef.css" rel="stylesheet">
</head><body><noscript><strong>We're sorry but vue_shop doesn't work properly without JavaScript enabled. Please enable it tocontinue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.24e8034f.js"></script><script src="js/app.4942fcdb.js"></script>
</body></html>

路由懒加载

const Login = () => import( /* webpackChunkName:"login_home_welcome" */ '../views/Login.vue')
const Home = () => import( /* webpackChunkName:"login_home_welcome" */ '../views/Home.vue')
const Welcome = () => import( /* webpackChunkName:"login_home_welcome" */ '../components/Welcome.vue')
const Users = () => import( /* webpackChunkName:"user" */ '../components/User/User.vue')
const Rights = () => import( /* webpackChunkName:"power" */ '../components/Role/rights.vue')
const Roles = () => import( /* webpackChunkName:"power" */ '../components/Role/roles.vue')
const Cate = () => import( /* webpackChunkName:"goods" */ '../components/Good/cate.vue')
const Params = () => import( /* webpackChunkName:"goods" */ '../components/Good/params.vue')
const GoodList = () => import( /* webpackChunkName:"goods" */ '../components/Good/goods.vue')
const GoodAdd = () => import( /* webpackChunkName:"goods" */ '../components/Good/addgoods.vue')
const Order = () => import( /* webpackChunkName:"order" */ '../components/Orders/orders.vue')
const Report = () => import( /* webpackChunkName:"report" */ '../components/Reports/reports.vue')

电商后台管理系统(各模块技术点)相关推荐

  1. Vue项目实战之电商后台管理系统(二) 主页模块

    前言 目录 前言 一.主页布局 1.1 整体布局 1.2 头部区域布局 1.3 左侧菜单布局 1.3.1 静态布局 1.3.2 通过axios请求拦截器来进行权限验证 1.3.3 通过axios获取左 ...

  2. Vue项目实战之电商后台管理系统(一) 用户登录模块

    目录 一.项目概述 二.项目初始化 2.1 前端项目初始化步骤 2.2 后台项目的环境安装配置 三.用户登录/登出功能实现 3.1 登录功能概述 3.1.1 登录状态保持 3.1.2 登录逻辑: 3. ...

  3. Vue全家桶 - 电商后台管理系统项目开发实录(详)

    目录 1. 项目概述 1.1 电商项目基本业务概述 1.2 电商后台管理系统的功能 1.3 电商后台管理系统的开发模式(前.后端分离) 2. 项目初始化 2.1 前端项目初始化步骤 码云相关操作 2. ...

  4. 计算机毕业设计SSM电商后台管理系统【附源码数据库】

    项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...

  5. 计算机毕业设计node.js+vue+Element电商后台管理系统

    项目介绍 网络的广泛应用给生活带来了十分的便利.所以把电商后台管理与现在网络相结合,利用node技术建设电商后台管理系统,实现电商后台管理的信息化.则对于进一步提高电商后台管理发展,丰富电商后台管理经 ...

  6. Vue电商后台管理系统项目开发实战(一)

    前言 当下根据不同的应用场景,电商系统一般都提供了PC端,移动APP,移动Web,微信等多种访问方式.如下图. 不同的客户端共用同一个服务器,数据库,API.本次项目着重设计PC后台管理,供电商后台管 ...

  7. Vue2项目总结-电商后台管理系统

    Vue2项目总结-电商后台管理系统 去年做的项目,拖了很久,总算是打起精力去做这个项目的总结,并对Vue2的相关知识进行回顾与复习 各个功能模块如果有过多重复冗杂的部分,将会抽取部分值得记录复习的地方 ...

  8. 电商后台管理系统简介

    项目介绍 黑马后台管理系统是一个电商后台管理系统的前端项目,基于Vue+Element实现. 主要包括商品管理.订单管理.会员管理.促销管理.运营管理.内容管理.统计报表.财务管理.权限管理.设置等功 ...

  9. Vue+Element-UI 电商后台管理系统详细总结

    一.概述 基于 Vue 和 Element-UI 的电商后台管理系统 1.1 实现功能 用户登录/退出 用户管理 用户列表 实现用户的增删改查.分页查询以及分配角色功能 权限管理 角色列表 实现角色的 ...

最新文章

  1. C#通过WMI的wind32 的API函数实现msinfo32的本地和远程计算机的系统摘要信息查看功能...
  2. 联机日志损坏时的恢复(非正常关闭数据库)
  3. 键盘按下某键 停止运行java_实现按下一个键执行操作/松开一个键停止操作
  4. 如何知道远程电脑某一端口是否打开?
  5. MyBatis创建SqlSession-怎么拿到一个SqlSessionTemplate?
  6. yml eureka defaultzone 只生效第一个_SpringCloud基础教程(三)-Eureka进阶
  7. pythonwhile循环怎么修改数据类型_python基础--数据类型循环
  8. vc6项目-vc8项目 转换日志
  9. 设计中最困难的部分是决定设计什么
  10. linux端口映射命令
  11. 《普林斯顿微积分读本》笔记-第2章三角学回顾
  12. 【数字图像处理5.3】SLIC算法 超像素分割(无监督聚类方式)python
  13. 计算机多媒体基础应用,《计算机应用基础》典型多媒体课件简介
  14. Python爬取——国家统计局省份加城市 并写入数据库
  15. Java实现PC端支付宝网页支付
  16. iPhone 14分辨率,屏幕尺寸,PPI 详细数据对比 iPhone 14 Plus、iPhone 14 Pro、iPhone 14 Pro Max
  17. MySQL5.7官方下载链接导航
  18. Linux下套接字详解(六)----基于pthread的多线程的TCP套接字(阻塞/同步/并发)
  19. 记一次PyQT5 core dump调试过程
  20. windoes 平台 Qt 的下载与安装-(Qt 5.15.2 LTS,这是一个长期支持版本)

热门文章

  1. 暑期实践——6.27
  2. Mac应用程序无法打开或文件损坏的处理方法,Mac任何来源开启教程
  3. java 替换第二个字符_字符串替换第二个相同的字符
  4. github copilot X - chat 使用体验分享
  5. 典型的学生思维有哪些
  6. 计算机网络实验-IP协议及TraceRT路由跟踪
  7. VBA 函数参数和结果输出的二三事
  8. Vue3 实现路由跳转
  9. 【工具篇】Unity翻书效果的三种方式
  10. 2022 OpenSDV年度技术大会成功举办