商品管理菜单下的商品分类子菜单
新建分支并推送到码云
在component文件夹中新建一个goods文件夹存放商品管理相关组件,新建Cate.vue子组件;在router/index.js中导入子组件并定义子路由规则,注意,路径是浏览器地址栏中的路径名,这里是/categories
data() {
return {
// 5.1 商品分类的数据列表
cateList: [],
// 5.2
queryInfo: {
type: 3,
pagenum: 1,
pagesize: 5
},
total: 0
}
},
created() {
// 5.3
this.getCateList()
},
methods: {
// 5.4 获取商品分类数据
async getCateList() {
// 发送请求
const { data: res } = await this.$http.get('categories', {
params: this.queryInfo
})
if (res.meta.status !== 200) {
return this.$message.error('获取商品分类数据列表失败')
}
console.log(res.data)
// 把成功获取到的数据保存到数组中
this.cateList = res.data.result
// 为总数据条数赋值
this.total = res.data.total
}
}
官方文档链接:
https://github.com/MisterTaki/vue-table-with-tree-grid/blob/master/example/Example.vue
https://github.com/MisterTaki/vue-table-with-tree-grid
a. 下载依赖 ,在入口文件导入这个插件并全局注册,组件名统一为 tree-table
npm i vue-table-with-tree-grid
API—Table Attributes:
属性 说明 类型 参数 默认值 data 表格各行的数据 Array - [] empty-text 表格数据为空时显示的文字 String - ‘暂无数据’ columns 表格各列的配置(具体见下文:Columns Configs) Array - [] selection-type 是否为多选类型表格 Boolean - false expand-type 是否为展开行类型表格(为 True 时,需要添加名称为 ‘$expand’ 的作用域插槽, 它可以获取到 row, rowIndex) Boolean - false show-index 是否显示数据索引 Boolean - false index-text 数据索引名称 String - ‘序号’
<tree-table
:data="cateList"
:columns="columns"
:selection-type="false"
:expand-type="false"
show-index="true"
index-text="#"
border
></tree-table>
columns: [
{
label: '分类名称',
prop: 'cat_name',
}]
b. 通过自定义模板列的形式将后三列数据渲染出来
是否有效cat_deleted:false
如果值为false,渲染成绿色的✔,如果值为true,则渲染成红色的✘,需要用到作用域插槽
思路:首先在data中定义一个列“是否有效”,然后在表格的内容节点中使用作用域插槽定义一个模板,添加slot,v-slot 属性,使用指令 v-if 如果scope值为false就显示success图标,否则,error图标
排序 cat_level
同样是要使用 v-if 进行按需渲染,cat_level值为0:一级;值为1:二级;值为2:三级
操作
在自定义模板中添加两个按钮,分别是编辑和删除,后续再完成这两者的功能
// 5. 底部 Pagination 分页区域 -->
// 5.5 监听 pagesize 的改变,获取到最新的 newSize
handleSizeChange(newSize) {
// console.log(newSize)
this.queryInfo.pagesize = newSize
this.getCateList()
},
// 5.6 监听 pagenum 的改变,获取到最新的 页码值 newPage
handleCurrentChange(newPage) {
// console.log(newPage)
this.queryInfo.pagenum = newPage
this.getCateList()
}
addCateDialogVisible
控制添加分类对话框的显示与隐藏,首先在data 中将其设置为false// 3.1 添加分类的功能对话框 -->
<el-dialog
title="添加分类"
:visible.sync="addCateDialogVisible"
width="50%"
>
// 3.4.添加分类的表单区域 -->
<el-form
:model="addCateForm"
:rules="addCateFormRules"
ref="addCateFormRef"
label-width="80px"
>
<el-form-item label="分类名称" prop="cat_name">
<el-input v-model="addCateForm.cat_name"></el-input>
</el-form-item>
<el-form-item label="父级分类"></el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addCateDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="addCateDialogVisible = false"
>确 定</el-button>
</span>
</el-dialog>
<script>
export default {
data() {
return{
...
// 3.5 添加分类的表单数据对象
addCateForm: {
// 将要添加的分类的名称
cat_name: '',
// 父级分类的 id
cat_pid: 0,
// 分类的等级,默认要添加的是一级分类
cat_level: 0
},
// 3.6 添加分类的表单验证规则
addCateFormRules: {
cat_name: [{ required: true, message: '请输入分类名称', trigger: blur }]
},
// 3.8 保存父级分类数据的列表
parentCateList: []
}
},
methods:{
// 3.3 点击按钮展示添加分类的对话框
showAddCateDialog() {
// 3.7.2 先获取父级分类的数据列表
this.getParentCateList()
// 再展示对话框
this.addCateDialogVisible = true
},
// 3.7 定义一个函数 getParentCateList 来获取父级分类的数据列表
async getParentCateList() {
// 3.7.1 发送请求
const { data: res } = await this.$http.get('categories', {
params: { type: 2 }
})
if (res.meta.status !== 200) {
return this.$message.error('获取父级分类数据列表失败')
}
console.log(res.data)
this.parentCateList = res.data
// this.getParentCateList()
}
}
}
</script>
Cascader
组件,并注册,然后添加使用级联菜单组件:这里遇到一个问题:
原版本是将 expandTrigger="hover"
和 :props="cascaderProps"
分开写的,但是官方文档更新之后,写法变为了 :props="{ expandTrigger: 'hover' }"
,导致我不知道 cascaderProps 应该放在什么位置进行配置
// 原版本
<el-cascader>
v-model="selectedKeys"
:options="parentCateList"
expandTrigger="hover"
:props="cascaderProps"
@change="parentCateChange"
</el-cascader>
// data函数中
// 3.10 指定级联选择器的配置对象 cascaderProps
cascaderProps: {
value: 'cat_id',
label: 'cat_name',
children: 'children'
},
// 3.11 选中的父级分类的 id 数组
selectedKeys: []
// 官方文档更新后的写法
:props="{ expendTrigger:'hover' }"
// 导致我不知道 cascaderProps 应该放在什么位置进行配置,如果修改为以下写法,页面只显示级联选择器的框架,内部文字无法显示
:props="{ expendTrigger:'hover',cascaderProps}"
// 感谢弹幕,找到了新用法,应该使用扩展运算符更改为...cascaderProps,页面数据才能渲染出来
:props="{ expendTrigger:'hover',...cascaderProps}"
// 查看了文档,props属性可以有很多参数,包括 value,label,children,最后修改为
<el-cascader
v-model="selectedKeys"
:options="parentCateList"
:props="{
expandTrigger: 'hover',
value: 'cat_id',
label: 'cat_name',
children: 'children',
checkStrictly: true
}"
@change="parentCateChange"
clearable
></el-cascader>
Cascader Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
value / v-model | 选中项绑定值 | - | — | — |
options | 可选项数据源,键名可通过 Props 属性配置 |
array | — | — |
props | 配置选项,具体见下表 | object | — | — |
clearable | 是否支持清空选项 | boolean | — | false |
Props
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
expandTrigger | 次级菜单的展开方式 | string | click / hover | ‘click’ |
checkStrictly | 是否严格的遵守父子节点不互相关联 | boolean | - | false |
value | 指定选项的值为选项对象的某个属性值 | string | — | ‘value’ |
label | 指定选项标签为选项对象的某个属性值 | string | — | ‘label’ |
children | 指定选项的子选项为选项对象的某个属性值 | string | — | ‘children’ |
disabled | 指定选项的禁用为选项对象的某个属性值 | string | — | ‘disabled’ |
有一个小问题:为当前父级分类id赋值时,下面两种写法的区别
// 错误写法:
this.addCateForm.cat_pid = this.selectedKeys.length - 1
// 正确写法:
this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1]
// 3.1 添加分类的功能对话框 -->
<el-dialog
title="添加分类"
:visible.sync="addCateDialogVisible"
width="50%"
@close="addCateDialogClosed"
></el-dialog>
methods:{
...
// 3.15 监听对话框的关闭事件,重置表单数据
addCateDialogClosed() {
this.$refs.addCateFormRef.resetFields()
// 重置级联选择器的数组,父级分类id以及level
this.selectedKeys = []
this.addCateForm.cat_pid = 0
this.addCateForm.cat_level = 0
}
}
完成添加分类功能
先进行预验证,然后发送post请求完成添加分类的功能
查看API接口文档1.6.2 添加分类
注意:请求发送成功的status = 201,不是200哦!!!!
methods:{
...
// 3.14 addCate 点击按钮,添加新的分类
addCate() {
// console.log(this.addCateForm)
this.$refs.addCateFormRef.validate(async (valid) => {
// 先进行预验证
// console.log(valid)
if (!valid) return
// 再发送请求
const { data: res } = await this.$http.post(
'categories',
this.addCateForm
)
if (res.meta.status !== 201) return this.$message.error('添加分类失败')
this.$message.success('添加分类成功了')
this.addCateDialogVisible = false
this.getCateList()
})
}
问题点:为什么发送请求判断状态码返回结果时,有时候 return 需要添加{ },有时候又不允许添加{ }呢?
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
cat_name | 分类名称 | 不能为空【此参数,放到请求体中】 |
// 6.2 展示修改分类的对话框
async showEditCateDialog(id) {
// console.log(id)
const { data: res } = await this.$http.get(`categories/${id}`)
if (res.meta.status !== 200) {
return this.$message.error('查询分类信息失败')
}
this.editCateForm = res.data
this.editCateDialogVisible = true
},
// 6.5 监听修改分类名称对话框的关闭事件,重置表单数据
editCateDialogClosed() {
this.$refs.editCateFormRef.resetFields()
},
// 6.6 点击确定按钮,修改分类名称
editCate() {
this.$refs.editCateFormRef.validate(async (valid) => {
// 进行预验证
// console.log(valid)
if (!valid) return
// 发送请求
const { data: res } = await this.$http.put(
'categories/' + this.editCateForm.cat_id,
{ cat_name: this.editCateForm.cat_name }
)
if (res.meta.status !== 200) {
return this.$message.success('修改分类名称失败')
}
this.editCateDialogVisible = false
this.getCateList()
// console.log(res)
})
},
根据ID删除用户信息,跳出提示框,利用MessageBox弹框,如果用户确认删除,则返回值为字符串 ‘confirm’;如果用户取消删除,则返回值为字符串 ‘cancel’,如果是 confirm 的话,发送请求确认删除
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
// 7.1 根据ID删除用户信息,跳出提示框
async removeCateById(id) {
// console.log(id)
// MessageBox 弹出框
const confirmResult = await this.$confirm(
'此操作将永久删除该文件, 是否继续?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).catch((err) => err)
// console.log(res)
if (confirmResult !== 'confirm') return this.$message.info('已取消删除')
// 如果是 confirm 的话,发送请求确认删除
const { data: res } = await this.$http.delete(`categories/${id}`)
if (res.meta.status !== 200) {
return this.$message.error('删除分类失败')
}
this.$message.success('删除分类成功')
this.getCateList()
}
参数管理概述:商品参数用于显示商品的固定特征信息,可以通过电商平台商品详情页面直观的看到
注意:只允许为第三级分类设置相关参数!
面包屑导航区域
卡片视图区域
3.Alert警告
4.选择商品分类的级联选择器
查看API接口文档 1.6.1. 商品分类数据列表
async getCateList() {
const { data: res } = await this.$http.get('categories')
if (res.meta.status !== 200) {
return this.$message.error('获取商品分类列表失败')
}
this.cateList = res.data
// console.log(this.cateList)
},
通过监听选择器的change事件,控制只能选择三级分类,如果length = 3 ,表示选中的是三级分类,如果length !== 3,将数组重置为空
// 选择项发生变化触发这个函数
handelChange() {
// console.log(this.selectKeys)
if (this.selectedKeys.length !== 3) {
this.selectedKeys = []
}
// console.log(this.selectedKeys)
}
// 5. 动态分类,静态属性Tabs标签页 -->
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="动态分类" name="first">用户管理</el-tab-pane>
<el-tab-pane label="静态属性" name="second">配置管理</el-tab-pane>
</el-tabs>
activeName:'first'
// 5.1 tab 页签点击事件的处理函数
handleClick() {
console.log(this.activeName)
}
渲染添加参数、属性按钮,同时控制禁用和启用的状态,当我们没有选择三级分类商品时,按钮被禁用,
查看API接口文档 1.7.1.参数列表
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
sel | [only,many] | 不能为空,通过 only 或 many 来获取分类静态参数还是动态参数 |
将pane中的name修改为only和many
问题1:在动态参数和静态属性之间跳转时,不会再次发送请求去获取数据,只有级联选择器事件改变时才触发
解决思路:将获取数据的这些业务逻辑全部抽离出来定义一个新的函数,然后分别进行调用,那么不论是级联选择器中选中项发生变化,还是面板的选中项发生变化,都可以调用这个函数
// 4.2 选择项发生变化触发这个函数
handelChange() {
// console.log(this.selectKeys)
this.getParamsData()
},
// 5.2 tab 页签点击事件的处理函数
handleClick() {
console.log(this.activeName)
this.getParamsData()
},
// 5.8 解决面板无法获取数据的问题
async getParamsData() {
if (this.selectedKeys.length !== 3) {
this.selectedKeys = []
}
// 证明选中了三级分类
// console.log(this.selectedKeys)
// 5.7 在级联选择器事件改变时发起请求
// 根据所选分类的id和当前所处的面板获取对应的参数
const { data: res } = await this.$http.get(
`categories/${this.cateId}/attributes`,
{
params: { sel: this.activeName }
}
)
if (res.meta.status !== 200) {
// this.cateId = this.cateList[this.cateList.length - 1]
return this.$message.error('获取列表参数失败')
}
console.log(res.data)
}
问题2:获取到的数据
res.data
到底是给动态参数表格使用,还是静态属性表格使用呢?
解决思路:判断 activeName 为 many / only ,分别定义一个专门的数据对象,用来保存 res.data
data() {
return {
...
// 5.10 动态参数/静态数据数据列表
manyTableData: [],
onlyTableData: []
}
},
// 5.8 解决面板无法获取数据的问题
async getParamsData() {
...
// 5.9 将res.data 分别挂载到对应的数据对象中
if (this.activeName === 'many') {
this.manyTableData = res.data
} else {
this.onlyTableData = res.data
}
}
绘制表格展示动态参数数据以及静态属性数据
// 5. 动态分类,静态属性Tabs标签页 -->
<el-tabs v-model="activeName" @tab-click="handleClick">
...
// 6. 动态参数表格 -->
<el-table :data="manyTableData" style="width: 100%" border stripe>
// 展开行 -->
<el-table-column type="expand"></el-table-column>
// 索引列 -->
<el-table-column type="index"></el-table-column>
<el-table-column prop="attr_name" label="参数名称">
</el-table-column>
<el-table-column label="操作">
<template v-slot="scope">
// 修改按钮 -->
<el-button
type="primary"
icon="el-icon-edit"
size="medium"
@click="showEditDialog(scope.row.id)"
>修改</el-button
>
// 删除按钮 -->
<el-button
type="danger"
icon="el-icon-delete"
size="medium"
@click="removeUserById(scope.row.id)"
>删除</el-button
>
</template>
</el-table-column>
</el-tab-pane>
</el-table>
...
// 7. 静态属性表格
<el-table :data="onlyTableData" style="width: 100%" border stripe>
// 展开行
<el-table-column type="expand"></el-table-column>
// 索引列
<el-table-column type="index"></el-table-column>
<el-table-column prop="attr_name" label="参数名称">
</el-table-column>
<el-table-column label="操作">
<template v-slot="scope">
// 修改按钮 -->
<el-button
type="primary"
icon="el-icon-edit"
size="medium"
@click="showEditDialog(scope.row.id)"
>修改</el-button
>
// 删除按钮 -->
<el-button
type="danger"
icon="el-icon-delete"
size="medium"
@click="removeUserById(scope.row.id)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
两者除了命名不同以外,其他都一样,所以设置为共用的
title必须设置为动态的,在计算属性中定义一个titleText函数
注意:为了方便定义内容主体区域form表单的label,函数返回值需是“动态参数”,“静态属性”,而不是“添加动态参数”,”添加静态属性“
添加功能对话框,在date中定义addDialogVisible为false,直接给面板的添加属性/参数按钮绑定点击事件@click="addDialogVisible = true"
,那么点击按钮时对话框显示
完善内容主体区域的form表单 ,设置添加参数的表单数据对象和验证规则以及监听对话框的关闭事件,重置表单
完成添加参数或属性,给确认按钮绑定点击事件addParams,当点击确认时,进行表单预验证,验证通过后发送请求
API接口文档1.7.2.添加动态参数和静态属性,注意,成功的状态码是201
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
attr_name | 参数名称 | 不能为空 |
attr_sel | [only,many] | 不能为空 |
attr_vals | 如果是 many 就需要填写值的选项,以逗号分隔 | 【可选参数】 |
遇到的小问题:在发送请求时,请求参数我不知道怎么设置
错误写法:
this.$http.post(`categories/${this.cateId}/attributes`,
{params: { attr_name, sel: this.activeName })
// 报错,请求体写错了
正确写法:
this.$http.post(`categories/${this.cateId}/attributes`, {
attr_name: this.addForm.attr_name,
attr_sel: this.activeName
})
// 8. 添加参数的功能对话框 -->
<el-dialog
:title="'添加' + titleText"
:visible.sync="addDialogVisible"
width="50%"
@close="addDialogClosed"
>
// 内容主体区域 -->
<el-form
:model="addForm"
:rules="addFormRules"
ref="addFormRef"
label-width="80px"
>
<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>
data() {
return {
...
// 8.2 控制添加对话框的显示与隐藏
addDialogVisible: false,
// 8.3 添加参数的表单数据对象
addForm: {
attr_name: ''
},
// 8.4 添加表单的验证规则对象
addFormRules: {
attr_name: [
{ required: true, message: '请输入参数名称', trigger: 'blur' }
]
}
}
}
methods:{
...
// 8.5 监听对话框的关闭事件
addDialogClosed() {
this.$refs.addFormRef.resetFields()
},
// 8.6 完成添加参数操作
addParams() {
// 8.6.1 先进行表单预验证
this.$refs.addFormRef.validate(async (valid) => {
// console.log(valid) // true or false
if (!valid) return
// 8.6.2 验证通过后发送请求
const { data: res } = await this.$http.post(
`categories/${this.cateId}/attributes`,
{
attr_name: this.addForm.attr_name,
attr_sel: this.activeName
}
)
if (res.meta.status !== 201) {
return this.$message.error('添加参数失败')
}
this.$message.success('添加参数成功')
// 关闭添加对话框
this.addDialogVisible = false
this.getParamsData()
})
}
},
computed:{
...
// 8.1 动态计算标题的文本
titleText() {
if (this.activeName === 'many') {
return '动态参数'
} else {
return '静态属性'
}
}
}
添加功能对话框,在date中定义editDialogVisible为false
完善内容主体区域的form表单 ,设置修改参数的表单数据对象和验证规则以及监听对话框的关闭事件,重置表单
给编辑按钮绑定点击事件showEditDialog,点击按钮,展示修改对话框
完成修改参数或属性,给确认按钮绑定点击事件editParams,当点击确认时,进行表单预验证,验证通过后发送请求
点击编辑按钮的同时,将对应参数的 id 传到处理函数中,然后根据 id 去查询参数的原始数据,并将数据绑定到表单中展示出来
查看API接口文档 1.7.4.根据ID查询参数
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
:attrId | 属性 ID | 不能为空携带在url中 |
attr_sel | [only,many] | 不能为空 |
attr_vals | 如果是 many 就需要填写值的选项,以逗号分隔 |
问题:为什么发送请求时,只有一个参数和多个参数的写法是不一样的?
只一个参数时:
const { data: res } = await this.$http.get(
`categories/${this.cateId}/attributes/${attrid}`,
{ params: { attr_sel: this.activeName } }
)
多个参数时:
const { data: res } = await this.$http.post(
`categories/${this.cateId}/attributes`,
{
attr_name: this.addForm.attr_name,
attr_sel: this.activeName
}
)
点击确认按钮完成修改操作,先进行表单预校验,通过之后再发起请求修改对应的数据
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
:attrId | 属性 ID | 不能为空携带在url中 |
attr_name | 新属性的名字 | 不能为空,携带在请求体 中 |
attr_sel | 属性的类型[many或only] | 不能为空,携带在请求体 中 |
attr_vals | 参数的属性值 | 可选参数,携带在请求体 中 |
根据获取的id删除参数或属性,给删除按钮绑定事件,当点击按钮时,将参数id传入函数中
查看API文档1.7.3.删除参数
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
:attrid | 参数 ID | 不能为空携带在url中 |
forEach()
方法对数组的每个元素执行一次给定的函数。有个bug:attr_vals为空时,会出现下面的情况
解决办法:不应该直接分隔后赋值,而是先通过三元表达式进行判断是否为空
// 展开行 -->
<el-table-column type="expand">
// 10.2 将数组内容渲染到标签上 -->
<template slot-scope="scope">
<el-tag
v-for="(item, index) in scope.row.attr_vals"
:key="index"
closable
>{{ item }}</el-tag
></template>
</el-table-column>
async getParamsData() {
...
// 10.1 将attr_vals转换为数组
res.data.forEach((item) => {
item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
// console.log(item)
})
}
// 展开行 -->
<el-table-column type="expand">
//10.2 将数组内容循环渲染到标签上 -->
<template slot-scope="scope">
<el-tag
v-for="(item, index) in scope.row.attr_vals"
:key="index"
closable
>{{ item }}
</el-tag>
// 11.添加标签的文本框
// @keyup.enter.native 和 @blur事件 在按下回车/文本框失去焦点时触发
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="small"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
// 12. +New Tag 标签按钮 -->
<el-button
v-else
class="button-new-tag"
size="small"
@click="showInput"
>+ New Tag
</el-button>
</template>
</el-table-column>
data(){
return{
...
// 11.1 标签文本框的显示与隐藏
inputVisible: false,
// 11.2 文本框中输入的内容
inputValue: ''
}
}
methods:{
// 11.3 文本框失去焦点或回车都会触发此函数
handleInputConfirm() {},
// 12.1 点击按钮展示input文本输入框
showInput() {
this.inputVisible = true
}
}
bug:参数列表中的按钮和输入框会联动,变化都一模一样
因为它们共用了inputVisible
的布尔值和inputValue
的值
解决办法:为每一行数据单独提供这两个值inputVisible
改为scope.row.inputVisible
表示每一行的布尔值
// 11.添加标签的文本框 -->
<el-input
class="input-new-tag"
v-if="scope.row.inputVisible"
v-model="scope.row.inputValue"
ref="saveTagInput"
size="small"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
methods:{
async getParamsData() {
...
// 10.1 将attr_vals转换为数组
res.data.forEach((item) => {
item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []
// 12.2 控制文本框的显示与隐藏
item.inputVisible = false
// 文本框中输入的值
item.inputValue = ''
// console.log(item)
})
}
// 12.1 点击按钮展示input文本输入框
showInput(row) {
row.inputVisible = true
// 12.3 让文本框自动获得焦点
this.$nextTick((_) => {
this.$refs.saveTagInput.$refs.input.focus()
// console.log(this.$refs) //{saveTagInput: VueComponent}
// console.log(this.$refs.saveTagInput.$refs)
})
}
}
业务逻辑:监听文本框失去焦点的事件,在调用了handelInputConfirm()
函数时,立即将该行的inputVisible
改为false,因为是每行的布尔值和inputValue值,所以需要传参scope.row
methods:{
...
// 11.3 文本框失去焦点或回车都会触发此函数
handleInputConfirm(row) {
// 12.4 文本框切换回按钮
row.inputVisible = false
},
...
}
优化:如果所输入的内容不合法,那么切换回按钮(文本框隐藏)前需要将文本框内容重置
业务逻辑:如果该行文本框内容去除空格之后的长度全等于0,那么将该行文本框内容清空,然后再切换回按钮,如果输入的内容合法,需要做后续处理(渲染成新的tag标签)
methods:{
...
// 11.3 文本框失去焦点或回车都会触发此函数
handleInputConfirm(row) {
// 12.5 判断文本框输入的内容是否合法
if (row.inputValue.trim().length === 0) {
row.inputValue = ''
// 12.4 文本框切换回按钮
row.inputVisible = false
}
// 如果输入内容合法,稍后继续进行后续处理
},
...
}
业务逻辑:紧接着上一步,如果输入的内容是合法的,那么我们需要拿到这一行的 inputValue
值,通过 push()
方法将其添加到attr_vals上
methods:{
// 11.3 文本框失去焦点或回车都会触发此函数
handleInputConfirm(row) {
...
// 如果输入内容合法,稍后继续进行后续处理
row.attr_vals.push(row.inputValue.trim())
row.inputValue = ''
row.inputVisible = false
},
}
但是仍然有bug:如果什么都没输入,只是回车,会直接出现两个新的空白标签,如果输入了合法内容,按下回车,会出现新标签以及一个空白新标签,但是使文本框失去焦点的话是不会有这种情况的
目前,只是在前端的页面中,将用户输入的值保存到了数组中,但是并没有把对应的数据上传到后台数据库中,所以刷新页面后,之前的所有的操作全部没有了,那么就需要发起请求保存数据
业务逻辑:查看API文档1.7.5.编辑提交参数,下面三个参数都可以直接使用该行的参数,但是注意,参数的属性值是数组形式,而服务器只接收字符串,需要使用join()
方法,将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串
参数名 | 参数说明 | 备注 |
---|---|---|
:id | 分类 ID | 不能为空携带在url中 |
:attrId | 属性 ID | 不能为空携带在url中 |
attr_name | 新属性的名字 | 不能为空,携带在请求体 中 |
attr_sel | 属性的类型[many或only] | 不能为空,携带在请求体 中 |
attr_vals | 参数的属性值 | 可选参数,携带在请求体 中 |
最后完整代码:
// 11.3 文本框失去焦点或回车都会触发此函数
async handleInputConfirm(row) {
// 12.5 判断文本框输入的内容是否合法
if (row.inputValue.trim().length === 0) {
row.inputValue = ''
// 12.4 文本框切换回按钮
row.inputVisible = false
}
// 12.6 如果输入内容合法,稍后继续进行后续处理
row.attr_vals.push(row.inputValue.trim())
row.inputValue = ''
row.inputVisible = false
// 12.7 发起请求保存上述操作的数据
const { data: res } = await this.$http.put(
`categories/${this.cateId}/attributes/${row.attr_id}`,
{
attr_name: row.attr_name,
attr_sel: row.attr_sel,
attr_vals: row.attr_vals.join(' ')
// 服务器只接收字符串,需要将数组转化为字符串
}
)
if (res.meta.status !== 200) {
return this.$message.error('修改参数项失败')
}
this.$message.success('修改参数项成功')
},
业务逻辑:给el-tag标签绑定一个close事件handelClosed()
,并将参数项的索引值传入函数中,根据索引值将其从数组中删除,我们还会用到这一行的数据,scope.row
也需要传入进去,在methods中定义这个函数,通过splice()
方法删除数组中的某一项,然后发起请求保存数据,这一步和上面添加参数项保存数据的请求操作一样,所以把发起请求保存数据的操作单拎出来为一个函数saveAttrvals()
,分别调用即可,记得传参,定义saveAttrVals
函数以及调用时,都需要传入参数 row
// 13.1 删除参数项
handelClosed(index, row) {
row.attr_vals.splice(index, 1)
this.saveAttrVals(row)
},
优化:选中三级分类后,删除/选中一级或二级分类时,动态属性和静态参数面板都没有清空
// 5.8 获取参数的列表数据,解决面板无法获取数据的问题
async getParamsData() {
if (this.selectedKeys.length !== 3) {
this.selectedKeys = []
// 13.3 选中三级分类又删除后,动态参数和静态属性面板也需要被清空
this.manyTableData = []
this.onlyTableData = []
}
...
}
静态参数的展开项和动态属性的展开项一样,可以直接复制展开项代码
通过终端手动输入git命令提交代码
查看当前所在分支
git branch
查看当前所在分支的文件状态
git status
将所有的修改添加到暂存区
git add .
将代码提交到本地仓库中
git commit -m '实现了分类管理功能'
将本地存储的goods_params分支推送到码云中
git push
将码云中goods_params分支的代码合并到主分支master中
先切换回主分支,再合并
git checkout master
git branch
git merge goods_params
合并完成后,将master主分支的代码也推送到码云中进行保存
git push
直接通过左侧操作栏提交代码
上面有几个小问题和疑惑,后续补充…