目录
路由的搭建
title设置左侧 侧边栏的信息 icon图标
所有组件路由都在Layout 下
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: 'Dashboard', icon: 'dashboard' }
}]
},
{
path: '/product',
component: Layout,
name: 'Product',
meta: { title: '商品管理', icon: 'el-icon-goods' },
children: [{
path: 'tradeMark',
name: 'tradeMark',
component: () => import('@/views/product/tradeMark'),
meta: { title: '品牌管理' }
},
{
path: 'Attr',
name: 'Attr',
component: () => import('@/views/product/Attr'),
meta: { title: '平台属性管理' }
},
{
path: 'Spu',
name: 'Spu',
component: () => import('@/views/product/Spu'),
meta: { title: 'Spu管理' }
},
{
path: 'Sku',
name: 'Sku',
component: () => import('@/views/product/Sku'),
meta: { title: 'Sku管理' }
}]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
品牌管理静态组件
el-table 表单组件 data表单需要展示的数据(数组) border:表单的边框
el-table-column 代表每一列 label显示的标题 width列的宽度 align对齐方式-
el-pagination 分页器
<template>
<div>
<!-- 按钮 -->
<el-button type="primary" icon="el-icon-plus">添加</el-button>
<!-- 表单组件 data表单需要展示的数据(数组) border:表单的边框 -->
<el-table>
<!-- 代表每一列 label显示的标题 width列的宽度 align对齐方式-->
<el-table-column
label="序号"
width="width"
align="center"
></el-table-column>
<el-table-column label="品牌名称" width="width"></el-table-column>
<el-table-column label="品牌LOGO" width="width"></el-table-column>
<el-table-column label="操作" width="width"></el-table-column>
</el-table>
<!-- 分页器
current-page 当前第几页
total 数据总条数
page-size 每一页展示条数
page-sizes 设置每一页展示条数
layout 可以实现分页布局
pager-count:按钮的数量(如果是9 连续是7)
-->
<el-pagination
:current-page="6"
:total="99"
:page-size="3"
:page-sizes="[3, 5, 10]"
layout="prev,pager,next,jumper,sizes,total"
>
</el-pagination>
</div>
</template>
品牌管理列表展示
element ui--- table表单组件
el-table 表单组件 data表单需要展示的数据(数组) border:表单的边框
el-table-column
- element-ui 当中的table组件展示数据以一列进行展示数据 代表每一列
- label显示的标题
- width列的宽度
- align对齐方式
- prop:对应内容字段名
- 序号 type="index"
element ui el-pagination分页器组件
- current-page 当前第几页
- total 数据总条数
- page-size 每一页展示条数
- page-sizes 设置每一页展示条数
- layout 可以实现分页布局
- page-count:连续页码数(如果是9 连续是7)
- @current-change:页数发生变化的时候触发
- @size-change:每一页展示数据条数时触发
插槽
可以自定义一些内容
默认插槽 slot定义插槽 用户提供自定以内容
具名插槽 定义插槽 slot name=“aa” 使用写在template上 v-slot:aa 默认default(可以区分多个)
作用域插槽:可以获取组件数据 定义插槽 slot:info=info 使用v-slot:default=aa(aa接收数据)
网络请求
/admin/product/baseTrademark/{page}/{limit} 获取品牌列表接口
- 路径参数直接使用模板字符串 问号传参使用 params
- post· 通过主体发送 直接写对象
- import * as sku from './product/sku' 导出全部变量
// 将四个模块的接口统一暴露
import * as tradeMark from './product/tradeMark'
import * as attr from './product/attr'
import * as spu from './product/spu'
import * as sku from './product/sku'
//对外暴露
export default {
tradeMark,
attr,
sku,
spu
}
/admin/product/baseTrademark/{page}/{limit} //获取品牌列表接口
import request from '@/utils/request'
//这个模块主要是获取品牌管理的模块
export const baseTrademark = (page, limit) => {
return request.get(`/admin/product/baseTrademark/${page}/${limit}`)
}
main.js中挂载到vue原型上供每一个.vue页面实例使用
//引入相关api请求接口
import api from './api/index'
//组件实例的原型的原型指向vue.prototype
//任何组件可以使用api的接口
Vue.prototype.$api = api
增删改牌静态页面
el-dialog 对话框 visible.sync:控制对话框显示隐藏
Form 组件提供了表单验证的功能,只需要通过 rules
属性传入约定的验证规则,并将 Form-Item 的 prop
属性设置为需校验的字段名即可。
form表单 label 标题 label-width 宽度 model:收集表单的数据(表单验证)
el-upload上传图片
- 上传图片 收集数据不能使用 v-model(不是表单元素)
- on-success图片上传成功回调
- before-upload:图片上传之前的回调
- :show-file-list 是否展示文件列表
- action上传的地址
<!-- 对话框 visible.sync:控制对话框显示隐藏-->
<el-dialog title="添加品牌" :visible.sync="dialogFormVisible">
<!-- form表单 label 标题 label-width 宽度-->
<el-form>
<el-form-item label="品牌名称" label-width="100px">
<el-input autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="品牌logo" label-width="100px">
<!--
上传图片
on-success图片上传成功回调
before-upload:图片上传之前的回调
:show-file-list 是否展示文件列表
action上传的地址
-->
<el-upload
class="avatar-uploader"
action="https://jsonplaceholder.typicode.com/posts/"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="el-upload__tip" slot="tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogFormVisible = false"
>确 定</el-button
>
</div>
</el-dialog>
增删改品牌功能
书写api
新增品牌 /admin/product/baseTrademark/save 携带两个参数:品牌名称、品牌logo
修改品牌 /admin/product/baseTrademark/update put 携带三个参数:id、品牌名称、品牌logo
删除品牌 /admin/product/baseTrademark/remove/{id} delete
// 新增品牌
// /admin/product/baseTrademark/save 携带两个参数:品牌名称、品牌logo
// 切记:对于新增的品牌,给服务器传递数据,不需要传递ID,ID是由服务器生成
//修改品牌的
//修改品牌 /admin/product/baseTrademark/update put 携带三个参数:id、品牌名称、品牌logo
//切记:对于修改某一个品牌的操作,前端携带的参数需要带上id,你需要告诉服务器修改的是哪一个品牌
export const AddUpdateTrademark = (Trademark) => {
//带给服务器数据携带ID---修改
if (Trademark.id) {
return request.put('/admin/product/baseTrademark/update', Trademark)
} else {
return request.post('/admin/product/baseTrademark/save', Trademark)
}
}
//删除品牌
///admin/product/baseTrademark/remove/{id} delete
export const removeTrademark = (id) => {
return request.delete(`admin/product/baseTrademark/remove/${id} `)
}
增删改逻辑
编辑
- row 当前用户选中的行
- 将已有的品牌信息,赋值给tmForm展示
- 将服务器返回的品牌信息直接赋值给tmForm展示
- 也就是说tmForm存储为服务器返回的信息 { ...row }; //浅拷贝避免直接操作服务器数据
添加
- //重新获取商品列表
- //如果添加商品停留在第一页,修改商品停留在当前页
删除 如果当前页数据大于一留在当前页否则page-1
图片上传 handleAvatarSuccess(res, file)
- // res 上传成功之后返回的前端数据
- // file上传成功之后服务器返回的数据(file文件对象)
// 添加按钮
async AddUpdateTrademark() {
this.dialogFormVisible = false;
let { code, } = await this.$api.tradeMark.AddUpdateTrademark(
this.tmForm
);
if (code == 200) {
this.$message({
type: "success",
message: this.tmForm.id ? "修改商品成功" : "添加商品成功",
});
//重新获取商品列表
//如果添加商品停留在第一页,修改商品停留在当前页
this.getPageList(this.tmForm.id ? this.page : 1);
}
},
// 编辑按钮
editChange(row) {
// row 当前用户选中的行
this.dialogFormVisible = true;
// 将已有的品牌信息,赋值给tmForm展示
// 将服务器返回的品牌信息直接赋值给tmForm展示
// 也就是说tmForm存储为服务器返回的信息
this.tmForm = { ...row }; //浅拷贝避免直接操作服务器数据
},
//删除
deletechange(row) {
this.$confirm(`您确定删除${row.tmName}?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
//当用户点击确定按钮时
let { code } = await this.$api.tradeMark.removeTrademark(row.id);
//弹框
if (code == 200) {
this.$message({
type: "success",
message: "删除成功!",
});
}
this.getPageList(this.list.length > 1 ? this.page : this.page - 1);
})
.catch(() => {
// 当用户点击取消按钮时
this.$message({
type: "info",
message: "已取消删除",
});
});
},
表单校验
Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。
定义rules校验规则对象
- required 必须校验的字段
- message 提示的信息
- trigger 用户行为设置 blur失去焦点 change 内容发生变化
- min max 长度限制
点击确定时 this.$refs.rulesForm.validate((success) => {})在表单全部校验
rules: {
/* required 必须校验的字段
message 提示的信息
trigger 用户行为设置 blur失去焦点 change 内容发生变化
min max 长度限制
*/
//品牌名称
tmName: [
{ required: true, message: "请输入品牌名称", trigger: "blur" },
{
min: 2,
max: 10,
message: "长度在 2 到 10 个字符",
trigger: "change",
},
],
//品牌loggo
tmName: [{ required: true, message: "请选择品牌图片" }],
},
自定投校验规则
data中定义校验规则变量
- // value输入的值
- // rule校验规则
- // callbackcallback 执行
data是一个函数return数据是为了保证变量私有化(闭包)
data() {
//自定义校验规则
var validatetmName = (rule, value, callback) => {
// value输入的值
// rule校验规则
// callbackcallback 执行
if (value.length < 2 || value.length > 10) {
callback(new Error("品牌名称2-10位"));
} else {
callback();
}
};
return {
//品牌名称
tmName: [
{ required: true, message: "请输入品牌名称", trigger: "blur" },
//自定义校验规则
{ validator: validatetmName, trigger: "blur" },
],
}
}
完整代码
<template>
<div>
<!-- 按钮 -->
<el-button type="primary" icon="el-icon-plus" @click="showChange"
>添加</el-button
>
<!-- 表单组件 data表单需要展示的数据(数组) border:表单的边框 -->
<el-table :data="list">
<!--
element-ui 当中的table组件展示数据以一列进行展示数据 代表每一列
label显示的标题
width列的宽度
align对齐方式
prop:对应内容字段名-->
<!-- 第一列 序号 type="index"-->
<el-table-column
type="index"
label="序号"
width="width"
align="center"
></el-table-column>
<!-- 第二列 名称 -->
<el-table-column
label="品牌名称"
width="width"
prop="tmName"
></el-table-column>
<!-- 第三列 默认作用域插槽 slot-scope||v-slot(接收当前行的数据,索引) -->
<el-table-column label="品牌LOGO" width="width" prop="logoUrl">
<template slot-scope="{ row }">
<img class="images" :src="row.logoUrl" alt="" />
</template>
</el-table-column>
<el-table-column label="操作" width="width">
<template slot-scope="{ row }">
<el-button
type="warning"
size="mini"
icon="el-icon-edit"
@click="editChange(row)"
>修改
</el-button>
<el-button
type="danger"
size="mini"
icon="el-icon-delete"
@click="deletechange(row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页器
current-page 当前第几页
total 数据总条数
page-size 每一页展示条数
page-sizes 设置每一页展示条数
layout 可以实现分页布局
page-count:连续页码数(如果是9 连续是7)
@current-change:页数发生变化的时候触发
@size-change:每一页展示数据条数时触发
-->
<el-pagination
:current-page="page"
:total="total"
:page-size="limt"
:page-count="7"
:page-sizes="[3, 5, 10]"
layout="prev,pager,next,jumper,sizes,total"
@current-change="getPageList"
@size-change="sizeChange"
>
</el-pagination>
<!-- 对话框 visible.sync:控制对话框显示隐藏-->
<!-- Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。 -->
<el-dialog
:title="tmForm.id ? '修改品牌' : '添加品牌'"
:visible.sync="dialogFormVisible"
>
<!--
form表单
label 标题
label-width 宽度
model:收集表单的数据(表单验证)
-->
<el-form :model="tmForm" :rules="rules" ref="rulesForm">
<el-form-item label="品牌名称" label-width="100px " prop="tmName">
<el-input autocomplete="off" v-model="tmForm.tmName"></el-input>
</el-form-item>
<el-form-item label="品牌logo" label-width="100px" prop="logoUrl">
<!--
上传图片 收集数据不能使用 v-model(不是表单元素)
on-success图片上传成功回调
before-upload:图片上传之前的回调
:show-file-list 是否展示文件列表
action上传的地址
-->
<el-upload
class="avatar-uploader"
action="/dev-api/admin/product/fileUpload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="tmForm.logoUrl" :src="tmForm.logoUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="el-upload__tip" slot="tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="AddUpdateTrademark">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "tradeMark",
data() {
//自定义校验规则
var validatetmName = (rule, value, callback) => {
// value输入的值
// rule校验规则
// callbackcallback 执行
if (value.length < 2 || value.length > 10) {
callback(new Error("品牌名称2-10位"));
} else {
callback();
}
};
return {
//代表分页第几页
page: 1,
//当前展示数据条数
limt: 3,
//总共数据条数
total: 0,
//列表展示的数据
list: [],
//对话框显与隐藏的控制属性
dialogFormVisible: false,
//收集品牌信息
tmForm: {
logoUrl: "",
tmName: "",
},
//表单验证规则
rules: {
/* required 必须校验的字段
message 提示的信息
trigger 用户行为设置 blur失去焦点 change 内容发生变化
min max 长度限制
*/
//品牌名称
tmName: [
{ required: true, message: "请输入品牌名称", trigger: "blur" },
//自定义校验规则
{ validator: validatetmName, trigger: "blur" },
],
//品牌loggo
logoUrl: [{ required: true, message: "请选择品牌图片" }],
},
};
},
//组件初始化完毕发送请求
created() {
//获取数据的方法
this.getPageList();
},
methods: {
//获取品牌列表
async getPageList(pager = 1) {
console.log(pager);
this.page = pager;
// 解构出参数
let { page, limt } = this;
// 获取品牌列表接口
//当你向服务器发送请求时,携带 分页第几页 当前展示数据条数
let { code, data } = await this.$api.tradeMark.baseTrademark(page, limt);
if (code == 200) {
//数据总条数,列表数据
this.total = data.total;
this.list = data.records;
}
},
sizeChange(limt) {
this.limt = limt;
this.getPageList();
},
// 点击添加的按钮
showChange() {
this.dialogFormVisible = true;
// 清空表单元素
this.tmForm = {
logoUrl: "",
tmName: "",
};
},
// 编辑按钮
editChange(row) {
// row 当前用户选中的行
this.dialogFormVisible = true;
// 将已有的品牌信息,赋值给tmForm展示
// 将服务器返回的品牌信息直接赋值给tmForm展示
// 也就是说tmForm存储为服务器返回的信息
this.tmForm = { ...row }; //浅拷贝避免直接操作服务器数据
},
//图片上传成功
handleAvatarSuccess(res, file) {
// res 上传成功之后返回的前端数据
// file上传成功之后服务器返回的数据(file文件对象)
this.tmForm.logoUrl = res.data;
},
//图片上传之前
beforeAvatarUpload(file) {
const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error("上传头像图片只能是 JPG 格式!");
}
if (!isLt2M) {
this.$message.error("上传头像图片大小不能超过 2MB!");
}
return isJPG && isLt2M;
},
// 添加按钮
AddUpdateTrademark() {
// 当全部表单验证通过在去写逻辑
this.$refs.rulesForm.validate(async (success) => {
// 如果全部字段符合条件
if (success) {
this.dialogFormVisible = false;
let { code } = await this.$api.tradeMark.AddUpdateTrademark(
this.tmForm
);
if (code == 200) {
this.$message({
type: "success",
message: this.tmForm.id ? "修改商品成功" : "添加商品成功",
});
//重新获取商品列表
//如果添加商品停留在第一页,修改商品停留在当前页
this.getPageList(this.tmForm.id ? this.page : 1);
} else {
return false;
}
}
});
},
//删除
deletechange(row) {
this.$confirm(`您确定删除${row.tmName}?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
//当用户点击确定按钮时
let { code } = await this.$api.tradeMark.removeTrademark(row.id);
//弹框
if (code == 200) {
this.$message({
type: "success",
message: "删除成功!",
});
}
this.getPageList(this.list.length > 1 ? this.page : this.page - 1);
})
.catch(() => {
// 当用户点击取消按钮时
this.$message({
type: "info",
message: "已取消删除",
});
});
},
},
};
</script>
<style>
.el-pagination {
text-align: center;
margin-top: 30px;
}
.images {
height: 100px;
width: 100px;
}
.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;
}
</style>