发布时间:2023-11-01 15:00
什么是后台管理系统项目?
在前端领域当中,开发后台管理系统项目,并非是Java,PHP等后台语言项目
在前面课程当中,我们已经开发了一个项目【尚品汇电商平台项目】,这个项目主要针对的是用户(游客),可以让用户在平台当中购买产品。
但是你需要想明白一件事情,用户购买产品信息从何而来呀?
比如:前台项目当中的数据来源于卖家(公司),但是需要注意的时候,卖家它不会数据库操作。对于卖家而言,需要把产品的信息上传于服务器,写入数据库。
卖家并非程序员,不会数据库操作(增删改查)。导致卖家,找了一个程序员,开发一个产品,可以让我进行可视化操作数据库(增删改查)
卖家(公司)组成:老板、员工。
老板:开发这个项目,对于老板而言,什么都可以操作。【产品的上架、产品的下架、查看员工的个人业绩、其他等等】
员工:可能就是查看个人业绩
后台管理系统:可以让用户通过一个可视化工具,可以实现对于数据库进行增删改查的操作。
而且需要注意,根据不同的角色(老板、员工),看到的、操作内容是不同的。
对于后台管理系统项目,一般而言,是不需要注册的。
简洁版: https://github.com/PanJiaChen/vue-admin-template 我们用这个
加强版: https://github.com/PanJiaChen/vue-element-admin
解压后发现文件夹里没有node_modules文件夹,我们需要安装依赖npm install
然后我们运行一下---->去看他的package.json文件下"dev": "vue-cli-service serve",
说明我们用dev来运行npm run dev
运行界面如下:
build
----index.js 是webpack配置文件【很少修改这个文件】
mock
----mock数据的文件夹【模拟一些假的数据mockjs实现的】,因为咱们实际开发的时候,利用的是真是接口
node_modules
------项目依赖的模块
public
------ico图标,静态页面,public文件夹里面经常放置一些静态资源,而且在项目打包的时候webpack不会编译这个文件夹,原封不动的打包到dist文件夹里面
src
-----程序员源代码的地方
------api文件夹:涉及请求相关的
------assets文件夹:里面放置一些静态资源(一般共享的),放在aseets文件夹里面静态资源,在webpack打包的时候,会进行编译
------components文件夹:一般放置非路由组件获取全局组件
------icons 这个文件夹的里面放置了一些svg矢量图
------layout文件夹:他里面放置一些组件与混入
------router文件夹:与路由相关的
-----store文件夹:一定是与vuex相关的
-----style文件夹:与样式先关的
------utils文件夹:request.js 是axios二次封装文件⭐️
------views文件夹:里面放置的是路由组件
App.vue:根组件
main.js:入口文件
permission.js:与导航守卫先关、
settings:项目配置项文件
.env.development:开发环境的配置文件
.env.producation:生产环境的配置文件
http://39.98.123.211:8170/swagger-ui.html
http://39.98.123.211:8216/swagger-ui.html
最新后台文档swagger地址:
http://39.98.123.211:8510/swagger-ui.html#/
修改里面的样式以及内容
这里修改了背景图片,以及把英文的都换成了中文
⚠️这里引入背景图片可以用
@
符号,但是前面要加~
另外把背景图片的大小调整为 100% 100%
.login-container {
min-height: 100%;
width: 100%;
background: url(~@/assets/城堡.jpg);
background-size: 100% 100%;
overflow: hidden;
这里之前登录的使用模拟的数据(mock),我们换成真实的接口
Swagger UI这里找接口
url改为接口里的url
// src/api/user.js
// 对外暴露登录的接口函数
export function login(data) {
return request({
url: 'dev-api/admin/acl/index/login',
method: 'post',
data
})
}
// 对外暴露获取用户信息的接口函数
export function getInfo(token) {
return request({
url: 'dev-api/admin/acl/index/info',
method: 'get',
params: { token }
})
}
// 对外暴露退出登录的接口函数
export function logout() {
return request({
url: 'dev-api/admin/acl/index/logout',
method: 'post'
})
}
这里我们的请求头携带的token字段
响应拦截器
我们换成了真实的接口,真实服务器返回的code ,有可能是20000,也有可能是200
我们有两个端口号,一个是8170,一个是8510,所以我们可以设置多个代理跨域
我们这里的传递的是
/dev-api
和dev-list
,target地址也要修改
// vue.config.js
// 配置代理跨域
proxy: {
// 关于用户的接口
"/dev-api": {
target: "http://39.98.123.211:8170",
pathRewrite: { "^/dev-api": "" }
},
// 关于品牌的接口
"/dev-list": {
target: "http://39.98.123.211:8510",
pathRewrite: { "^/dev-list": "" }
},
}
⚠️修改完配置项,要重新启动项目
修改退出登录的样式和结构,将英文修改成中文的
// src/api/user.js
// 对外暴露退出登录的接口函数
export function logout() {
return request({
url: '/admin/acl/index/logout',
method: 'post'
})
}
我们只需要首页,删掉其余不需要的路由文件夹,然后重新配置路由
我们的一级路由都是在Layout这个骨架下搭建的,所以一级路由的componen都写Layout
首页(一级路由)
权限管理(一级路由)
商品管理(一级路由)
-------品牌管理(二级路由)
---- --平台属性管理(二级路由)
-------Spu管理(二级路由)
-------Sku管理(二级路由)
// src/router/index.js
{ // 一级路由
path:'/product',
component:Layout, // 在Layout的骨架下!!
name:'Product',
meta:{title:'商品管理',icon:'el-icon-goods'}, // // title是设置侧边栏的文字
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管理'},
},
],
},
搭建后的:
另外,我们的内容 AppMain 需要与左侧有个内边距,我们去加个
padding:20px
添加button按钮
// src/views/product/tradeMark/index.vue
<!-- 按钮 -->
<el-button type="primary" icon="el-icon-plus" style="margin:10px 0">添加</el-button>
添加table表格—显示的标题要居中—且序号的占比要小
el-table
属性: data:表格组件将来需要展示的数据【数组类型】
border:是给表格带上边框
el-table-column
属性: label:显示的标题
width:对应列的宽度
align:对齐方式
<!-- 表格组件 -->
<el-table border>
<el-table-column label="序号" width="80" align="center"></el-table-column>
<el-table-column label="品牌类型" align="center"></el-table-column>
<el-table-column label="品牌LOGO" align="center"></el-table-column>
<el-table-column label="操作" align="center"></el-table-column>
</el-table>
分页器----要居中
el-pagination
的属性: current-page:当前第几页
page-size:每一页展示多少数据
layout:实现分页器顺序布局
->
后面的数据在最右面 total:数据总条数
<!-- 分页器 -->
<!--@size-change="handleSizeChange"
@current-change="handleCurrentChange" -->
<el-pagination
:current-page="6"
:page-sizes="[3, 5, 10]"
:page-size="3"
layout=" prev, pager, next, jumper,->,sizes, total"
:total="90"
style="margin-top:20px;text-align:center"
>
</el-pagination>
获取分页列表数据 /admin/product/baseTrademark/{page}/{limit} GET 带参数
// src/api/product/tradeMark.js
// 品牌管理的相关接口
// 我们发请求需要用的是封装好的axios
import request from '@/utils/request'
// 获取品牌分页列表数据 dev-api/admin/product/baseTrademark/{page}/{limit} GET
export const reqTradeMarkList = (page, limit) => {
return request({ url: `dev-list/admin/product/baseTrademark/${page}/${limit}`, method: 'GET' })
}
为方便使用,将四个模块的接口函数统一对外暴露
为了方便,可以任意使用接口函数,可以在入口函数中,将接口挂载在原型上
// src/main.js
// 引入相关API请求接口---挂载在原型上
import API from '@/api/index'
// 组件实例的原型的原型指向的是Vue.prototype
// 任意组件可以使用API相关的接口
Vue.prototype.$API = API;
我们挂载的时候需要获取数据,点击xx页的时候也要获取数据,所以我们将获取数据写在方法里面
<script>
export default {
name: "tradeMark",
data() {
return {
page: 2, // 代表分页器当前第几页
limit: 3, // 当前页数展示的多少条数据
total:0, // 总共数据的条数
list:[], // 列表里面要展示的数据
};
},
mounted() {
// 获取列表数据的方法
this.getPageList();
},
methods: {
// 获取品牌列表的数据
async getPageList() {
// 解构出参数
const { page, limit } = this;
// 获取品牌列表的接口
let result = await this.$API.trademark.reqTradeMarkList(page, limit);
if(result.code == 200){
// 展示数据的总条数与 列表要展示的数据
this.total = result.data.total;
this.list = result.data.records;
}
},
},
};
</script>
👇已经得到的数据,接下来开始展示
elementUI当中的table组件,展示的数据是以一列一列进行展示数据的
如果需要显示索引,可以增加一列
el-table-column
,设置type属性为index即可显示从 1 开始的索引号。
prop
:对应列内容的字段名,也可以使用 property 属性【字符串类型】
el-tabel
:data="list"
表示这个表格展示的是哪里面的数据第一列 我们要展示的是序列号,可以用
type="index"
表示从1 开始展示索引号第二列 我们展示的是品牌类型,在list 里面的tmName,我们可以用
prop
:对应列内容的字段名
<el-table border :data="list">
<el-table-column
label="序号"
width="80"
align="center"
type="index"
></el-table-column>
<el-table-column label="品牌类型" prop="tmName"></el-table-column>
<el-table-column label="品牌LOGO">
<!-- { row, $index }是固定的名字,不能瞎写 -->
<template slot-scope="{ row, $index }">
<!-- row代表的是数组回传的数据 -->
<img :src="row.logoUrl" alt="" style="width: 100px; height: 100px" />
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="{ row, $index }">
<el-button type="warning" icon="el-icon-edit>" size="mini"
>修改</el-button
>
<el-button type="danger" icon="el-icon-delete>" size="mini"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
第三列 展示的是品牌logo,是个图片,我们有地址,我们可以用【作用域插槽】来展示图片
slot-scope="{ row, $index }"
代表的是子组件回传过来的数据,也就是list
然后我们进行动态展示图片
:src
❓第四列 有两个按钮,为什么要用作用域插槽??????
👇分页器渲染数据
<el-pagination
:current-page="page"
:page-sizes="[3, 5, 10]"
:page-size="limit"
layout=" prev, pager, next, jumper,->,sizes, total"
:total="total"
style="margin-top: 20px; text-align: center"
:page-count="7"
>
</el-pagination>
当点击页数的时候,修改data里面的page,然后再次发请求
@current-change="handleCurrentChange"
当你点击其他页数时触发
<el-pagination
:current-page="page"
:page-sizes="[3, 5, 10]"
:page-size="limit"
layout=" prev, pager, next, jumper,->,sizes, total"
:total="total"
style="margin-top: 20px; text-align: center"
:page-count="7"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
</el-pagination>
// 点击页码进行切换
handleCurrentChange(pager) {
// pager你点击的页数
// 修改参数,然后再发请求
this.page = pager ;
this.getPageList();
},
@size-change="handleSizeChange"
当pageSize【展示数据条数】改变时触发
// 当分页器某一页需要展示数据的条数发生变化时触发
handleSizeChange(limit){
// 修改数据,再次发请求
this.limit = limit ;
this.getPageList();
},
当你点击品牌管理页面 左上角【添加】的时候,会弹出一个类似遮罩层的页面
当你点击【修改】,也会弹出一个类似遮罩层的页面-----------显示对话框
我们这里用elementui的 dialog 对话框
<!-- 对话框 -->
<!-- :visible.sync:控制对话框的显示与隐藏 -->
<el-dialog :title="tmForm.id ? '修改品牌' : '添加品牌'" :visible.sync="dialogFormVisible">
<!-- 展示表单元素form -->
<el-form style="width: 80%">
<!-- 第一行:品牌名称 -->
<el-form-item label="品牌名称" label-width="100px">
<el-input autocomplete="off"></el-input>
</el-form-item>
<!-- 第二行:品牌LOGO -->
<el-form-item label="品牌LOGO" label-width="100px">
<!--上传图片的位置-->
</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>
上传图片用elementui的Upload上传
<el-upload
class="avatar-uploader"
action="https://xxxxxxxxxx"
: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 slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
粘贴elementui里面相关的数据和方法与样式
data(){
return{
dialogFormVisible: false, // 对话框显示与隐藏的控制属性
imageUrl: "", // 上传图片使用的属性
}
},
methods:{
// 上传图片相关的回调
handleAvatarSuccess(res, file) {
this.imageUrl = URL.createObjectURL(file.raw);
},
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;
},
}
当点击【添加品牌】按钮
- 收集相应【品牌名称和LOGO】
- 将收集到的发请求提交给后台
- 然后再发请求获取品牌列表数据
新增品牌 /admin/product/baseTrademark/save POST 带参数
// 处理 添加品牌的 操作 /admin/product/baseTrademark/save POST 带参数 品牌名称和品牌logo
// 切记:对于新增的品牌,给服务器传递参数,不需要传递ID,ID是由服务器生成
export const reqAddTradeMark = (tradeMark) =>{
return request({url:'dev-list/admin/product/baseTrademark/save',method:'POST',data:tradeMark})
}
收集到表单元素里的数据
要收集到elementui里面的表单数据,就要在该表单上面添加
model
属性,将来表单验证也需要这个属性
<el-form style="width: 80%" :model="tmForm">
<!-- 第一行:品牌名称 -->
<el-form-item label="品牌名称" label-width="100px">
<el-input autocomplete="off" v-model="tmForm.tmName"></el-input>
</el-form-item>
收集LOGO图片
这里收集数据不能使用v-model,因为不是表单元素,我们这里用了另一个
el-upload
action:设置图片上传的地址
on-success 可以检测到图片上传成功,图片上传成功会执行一次
before-upload :可以在上传图片之前,会执行一次
<el-upload
class="avatar-uploader"
action="/dev-list/admin/product/upload"
: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 slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
// 点击【添加品牌】的按钮
showDialog() {
// 显示对话框
this.dialogFormVisible = true;
// 先清除之前的数据
this.tmForm = {tmName:'',logoUrl:''}
},
// 图片上传成功
handleAvatarSuccess(res, file) {
// res是你上传成功后服务器返回你的数据,里面是图片在服务器的真实地址
// file :上传成功后服务器返回的前端数据
// 收集上传图片数据
this.tmForm.logoUrl = res.data;
},
发请求
当点击【添加品牌】里的【确定】按钮时
- 将弹出的对话框隐藏
- 发请求(添加品牌
- 如果成功,弹出一个message框:可能是添加成功,也有可能是修改成功,然后重新获取新的品牌列表
<el-button type="primary" @click="addOrUpdateTradeMark">确 定</el-button>
// 点击【添加按钮】里面的【确定按钮】(可以添加品牌|修改品牌)
async addOrUpdateTradeMark() {
// 将弹出的对话框隐藏
this.dialogFormVisible = false;
// 发请求(添加品牌 | 修改品牌)
let result = await this.$API.trademark.reqAddTradeMark(this.tmForm);
if (result.code == 200) {
// 弹出一个message框:可能是添加成功,也有可能是修改成功
this.$message(this.tmForm.id ? "修改品牌成功" : "添加品牌成功");
// 获取新的品牌列表
this.getPageList();
}
},
修改 品牌的操作 /admin/product/baseTrademark/update PUT 带参数 ID和品牌名称和品牌logo
// 修改 品牌的操作 /admin/product/baseTrademark/update PUT 带参数 ID和品牌名称和品牌logo
// 切记:对于修改某一个品牌的参数,前端携带的参数需要带上id,你需要告诉服务器修改的是哪一个品牌
export const reqUpdateTradeMark = (tradeMark) =>{
return request({url:'dev-list/admin/product/baseTrademark/update',method:'PUT',data:tradeMark})
}
当你点击【修改】按钮时,会弹出一个对话框,里面默认显示你点这个品牌的信息
所以我们点击【修改】按钮时,会把
row
这一品牌的信息传过去,这里用到了作用域插槽
<el-table-column label="操作">
<!-- row代表的是这一品牌的信息 -->
<template slot-scope="{ row, $index }">
<el-button
type="warning"
icon="el-icon-edit>"
size="mini"
@click="updateTrademark(row)"
>修改</el-button>
<el-button type="danger" icon="el-icon-delete>" size="mini">删除</el-button>
</template>
</el-table-column>
tmForm 是我们收集的弹出框的表单数据,并非我们要展示的列表数据,我们要用tmFrom当我们的参数去发请求
list 是我们要展示的列表数据
❓为什么要用浅拷贝 tmForm????
(这里不用浅拷贝的话,当你在弹出框更改品牌名称的时候,品牌列表展示页面会自动的更新你修改的数据—无论你点了【取消】还是【确定修改】)
// 点击【修改某一个品牌】按钮
updateTrademark(row) {
// row 是当前用户选中的这个品牌的信息,服务器返回给我们的
// 显示对话框
this.dialogFormVisible = true;
// 将已有的品牌信息赋值给tmForm进行展示
// 将服务器返回的品牌信息,直接赋值给了tmForm进行展示
// tmForm存储的就是服务器返回的品牌信息
this.tmForm = {...row} ;
},
- 当你修改完成后,点击【确定】按钮,发请求去修改品牌,请求成功后再发请求获取品牌列表进行展示
- 将弹出的对话框隐藏
- 判断是【添加】还是【修改】 看是否tmForm.id
- 是修改的就发修改的请求,是添加的就发添加的请求
- 请求成功后,弹出一个message框:可能是添加成功,也有可能是修改成功
- 再次获取新的品牌列表
// 点击【添加按钮】里面的【确定按钮】(可以添加品牌|修改品牌)
async addOrUpdateTradeMark() {
// 1. 将弹出的对话框隐藏
this.dialogFormVisible = false;
// 2. 判断是【添加】还是【修改】 看是否tmForm.id
let result = null;
if (this.tmForm.id) {
// 是修改
result = await this.$API.trademark.reqUpdateTradeMark(this.tmForm);
} else {
// 发请求(添加品牌 | 修改品牌)
result = await this.$API.trademark.reqAddTradeMark(this.tmForm);
}
if (result.code == 200) {
// 3. 弹出一个message框:可能是添加成功,也有可能是修改成功
this.$message({
type: 'success',
message: this.tmForm.id ? "修改品牌成功" : "添加品牌成功",
});
// 4. 获取新的品牌列表
this.getPageList();
}
},
Form 组件提供了表单验证的功能,只需要通过rules
属性传入约定的验证规则,并将 Form-Item 的 prop
属性设置为需校验的字段名即可。校验规则参见 async-validator
最重要的是自定义校验规则!!!form表单校验–Vue UI 框架 (eleme.cn)
<template>
<div>
<!-- 按钮 -->
<el-button
type="primary"
icon="el-icon-plus"
style="margin: 10px 0"
@click="showDialog"
>添加</el-button
>
<!-- 表格组件 -->
<!-- data:表格组件将来需要展示的数据【数组类型】
border:是给表格带上边框
el-table-column:label【显示的标题】 width【对应列的宽度】align【对齐方式】
注意1:elementUI当中的table组件,展示的数据是以一列一列进行展示数据的
如果需要显示索引,可以增加一列el-table-column,设置type属性为index即可显示从 1 开始的索引号。
prop:对应列内容的字段名,也可以使用 property 属性【字符串类型】
-->
<el-table border :data="list">
<el-table-column
label="序号"
width="80"
align="center"
type="index"
></el-table-column>
<el-table-column label="品牌类型" prop="tmName"></el-table-column>
<el-table-column label="品牌LOGO">
<!-- { row, $index }是固定的名字,不能瞎写 -->
<template slot-scope="{ row, $index }">
<!-- row代表的是数组回传的数据,logoUrl是服务器返回的数据 -->
<img :src="row.logoUrl" style="width: 100px; height: 100px" />
</template>
</el-table-column>
<el-table-column label="操作">
<!-- row代表的是这一品牌的信息 -->
<template slot-scope="{ row, $index }">
<el-button
type="warning"
icon="el-icon-edit>"
size="mini"
@click="updateTrademark(row)"
>修改</el-button
>
<el-button type="danger" icon="el-icon-delete>" size="mini"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 分页器 -->
<!-- 当前第几页[current-page],
数据总条数[total],
每一页数据展示多少[page-size],
连续页码数
layout:实现分页器布局
pager-count:按钮的数量,如果是9,那么连续页码是7
-->
<el-pagination
:current-page="page"
:page-sizes="[3, 5, 10]"
:page-size="limit"
layout=" prev, pager, next, jumper,->,sizes, total"
:total="total"
style="margin-top: 20px; text-align: center"
:page-count="7"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
</el-pagination>
<!-- 弹出的对话框 -->
<!-- :visible.sync:控制对话框的显示与隐藏 -->
<el-dialog
:title="tmForm.id ? '修改品牌' : '添加品牌'"
:visible.sync="dialogFormVisible"
>
<!-- 展示表单元素form -->
<!-- :model 收集到的表单数据 ,将来表单验证也需要这个属性,tmForm将数据保存到这里-->
<!-- Form 组件提供了表单验证的功能,只需要通过`rules` 属性传入约定的验证规则,并将 Form-Item 的 `prop`属性设置为需校验的字段名即可。 -->
<el-form style="width: 80%" :model="tmForm" :rules="rules" ref="ruleForm">
<!-- 第一行:品牌名称 -->
<el-form-item label="品牌名称" label-width="100px" prop="tmName">
<el-input autocomplete="off" v-model="tmForm.tmName"></el-input>
</el-form-item>
<!-- 第二行:品牌LOGO -->
<el-form-item label="品牌LOGO" label-width="100px" prop="logoUrl">
<!-- 这里收集数据不能使用v-model,因为不是表单元素 -->
<!-- action:设置图片上传的地址 -->
<!-- on-success 可以检测到图片上传成功,图片上传成功会执行一次 -->
<!-- before-upload :可以在上传图片之前,会执行一次 -->
<el-upload
class="avatar-uploader"
action="/dev-list/admin/product/upload"
: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 slot="tip" class="el-upload__tip">
只能上传jpg/png文件,且不超过2MB
</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="addOrUpdateTradeMark"
>确 定</el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "tradeMark",
data() {
return {
page: 2, // 代表分页器当前第几页
limit: 3, // 当前页数展示的多少条数据
total: 0, // 总共数据的条数
list: [], // 列表里面要展示的数据
dialogFormVisible: false, // 对话框显示与隐藏的控制属性
tmForm: {
// 收集的品牌信息,对象身上的属性不能瞎写,需要看文档
tmName: "",
logoUrl: "",
},
// 表单验证的规则
rules: {
// 品牌名称的验证规则
tmName: [
// required:必须要检验的字段,是否带*号
// message:提示的信息
// trigger:用户行为设置(事件的设置blur失焦,change变化)
{ required: true, message: "请输入品牌名称", trigger: "blur" },
{
min: 2, // 品牌名称的长度
max: 10,
message: "长度在 2 到 10 个字符",
trigger: "change", // 文本发生变化就触发
},
],
// 品牌logo的验证规则
logoUrl: [{ required: true, message: "请选择品牌的图片" }],
},
};
},
mounted() {
// 获取列表数据的方法
this.getPageList();
},
methods: {
// 获取品牌列表的数据
async getPageList() {
// 解构出参数
const { page, limit } = this;
// 获取品牌列表的接口
let result = await this.$API.trademark.reqTradeMarkList(page, limit);
if (result.code == 200) {
// 展示数据的总条数与 列表要展示的数据
this.total = result.data.total;
this.list = result.data.records;
}
},
// 点击页码进行切换
handleCurrentChange(pager) {
// pager你点击的页数
// 修改参数,然后再发请求
this.page = pager;
this.getPageList();
},
// 当分页器某一页需要展示数据的条数发生变化时触发
handleSizeChange(limit) {
// 修改数据,再次发请求
this.limit = limit;
this.getPageList();
},
// 点击【添加品牌】的按钮
showDialog() {
// 显示对话框
this.dialogFormVisible = true;
// 清除数据
this.tmForm = { tmName: "", logoUrl: "" };
},
// 点击【修改某一个品牌】按钮
updateTrademark(row) {
// row 是当前用户选中的这个品牌的信息,服务器返回给我们的
// 显示对话框
this.dialogFormVisible = true;
// 将已有的品牌信息赋值给tmForm进行展示
// 将服务器返回的品牌信息,直接赋值给了tmForm进行展示
// tmForm存储的就是服务器返回的品牌信息
this.tmForm = { ...row };
},
// 图片上传成功
handleAvatarSuccess(res, file) {
// res是你上传成功后服务器返回你的数据,里面是图片在服务器的真实地址
// 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;
},
// 点击【添加按钮】里面的【确定按钮】(可以添加品牌|修改品牌)
addOrUpdateTradeMark() {
// 当全部验证的字段通过,再去书写业务逻辑
this.$refs.ruleForm.validate(async (success) => {
// 如果全部字段符合条件
if (success) {
// 1. 将弹出的对话框隐藏
this.dialogFormVisible = false;
// 2. 判断是【添加】还是【修改】 看是否tmForm.id
let result = null;
if (this.tmForm.id) {
// 是修改
result = await this.$API.trademark.reqUpdateTradeMark(this.tmForm);
} else {
// 3. 发请求(添加品牌 | 修改品牌)
result = await this.$API.trademark.reqAddTradeMark(this.tmForm);
}
if (result.code == 200) {
// 4. 弹出一个message框:可能是添加成功,也有可能是修改成功
this.$message({
type: "success",
message: this.tmForm.id ? "修改品牌成功" : "添加品牌成功",
});
// 获取新的品牌列表
this.getPageList();
}
} else {
console.log("提交错误");
return false;
}
});
},
},
};
</script>
当点击【删除】按钮时,会弹出一个弹框【确定要删除吗】,然后有【确定】和【取消】两个按钮
点击【删除】按钮,给【删除】绑定点击事件deleteTradeMark
,要传参数(你点击该品牌的信息—row)
弹出一个弹框,里面是【确定删除】还是【取消】
确定后,发请求删除该品牌,然后再发请求获取品牌列表数据;
删除该品牌 /admin/product/baseTrademark/remove/{id} DELETE 带参数
// 删除该品牌 /admin/product/baseTrademark/remove/{id} DELETE 带参数
export const reqDeleteTradeMark = (id) => {
return request({ url: `dev-list/admin/product/baseTrademark/remove/${id}`, method: 'DELETE' })
}
弹窗
// 删除品牌的操作
deleteTradeMark(row) {
// 弹窗
this.$confirm(`你确定删除${row.tmName}吗?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
// 当用户点击【确定】按钮的时候触发
this.$message({
type: "success",
message: "删除成功!",
});
})
.catch(() => {
// 当用户点击【取消】按钮的时候会触发
this.$message({
type: "info",
message: "已取消删除",
});
});
},
发请求----完成删除功能
// 删除品牌的操作
deleteTradeMark(row) {
// 弹窗
this.$confirm(`你确定删除${row.tmName}吗?`, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(async () => {
// 1. 当用户点击【确定】按钮的时候触发
// 2. 向服务器发请求
let result = await this.$API.trademark.reqDeleteTradeMark(row.id);
if (result.code == 200) {
// 3. 弹出message框
this.$message({
type: "success",
message: "删除成功!",
});
// 4. 再次获取品牌列表数据
this.getPageList();
}
})
.catch(() => {
// 当用户点击【取消】按钮的时候会触发
this.$message({
type: "info",
message: "已取消删除",
});
});
},
应该会跳转到下一文章👉
SAP Fiori 注解 @ObjectModel.readOnly工作原理解析
OSI网络模型,IO模型,BIO模型,NIO模型,AIO模型,TCP/IP协议
R语言Bootstrap、百分位Bootstrap法抽样参数估计置信区间分析通勤时间和学生锻炼数据
每日新闻丨华为被拘留前员工再回应;亚马逊云发布量子计算服务Braket预览;硅谷“六巨头”10年避税超千亿美元...
基于springboot实现多消费者监听注解MultiJmsListener
向量化执行引擎框架 Gluten 宣布正式开源,并亮相 Spark 技术峰会
无法将“node.exe”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。