一、商品列表的展示

1.1 创建商品修改页面goods-update.vue

  1. <template>
  2. <div class="mod-config">
  3. <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
  4. <el-form-item>
  5. <el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
  6. </el-form-item>
  7. <el-form-item>
  8. <el-button @click="getDataList()">查询</el-button>
  9. <el-button v-if="isAuth('manager:goods:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
  10. <el-button v-if="isAuth('manager:goods:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
  11. </el-form-item>
  12. </el-form>
  13. <el-table
  14. :data="dataList"
  15. border
  16. v-loading="dataListLoading"
  17. @selection-change="selectionChangeHandle"
  18. style="width: 100%;">
  19. <el-table-column
  20. type="selection"
  21. header-align="center"
  22. align="center"
  23. width="50">
  24. </el-table-column>
  25. <el-table-column
  26. prop="id"
  27. header-align="center"
  28. align="center"
  29. label="商品ID">
  30. </el-table-column>
  31. <el-table-column
  32. prop="goodsName"
  33. header-align="center"
  34. align="center"
  35. label="商品名称">
  36. </el-table-column>
  37. <el-table-column
  38. header-align="center"
  39. align="center"
  40. label="一级类目">
  41. <template slot-scope="scope">
  42. <span>{{itemCats[scope.row.category1Id]}}</span>
  43. </template>
  44. </el-table-column>
  45. <el-table-column
  46. header-align="center"
  47. align="center"
  48. label="二级类目">
  49. <template slot-scope="scope">
  50. <span>{{itemCats[scope.row.category2Id]}}</span>
  51. </template>
  52. </el-table-column>
  53. <el-table-column
  54. header-align="center"
  55. align="center"
  56. label="三级类目">
  57. <template slot-scope="scope">
  58. <span>{{itemCats[scope.row.category3Id]}}</span>
  59. </template>
  60. </el-table-column>
  61. <el-table-column
  62. header-align="center"
  63. align="center"
  64. label="状态">
  65. <template slot-scope="scope">
  66. <span>{{status[scope.row.auditStatus]}}</span>
  67. </template>
  68. </el-table-column>
  69. <el-table-column
  70. fixed="right"
  71. header-align="center"
  72. align="center"
  73. width="150"
  74. label="操作">
  75. <template slot-scope="scope">
  76. <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)">修改</el-button>
  77. <el-button type="text" size="small" @click="deleteHandle(scope.row.id)">删除</el-button>
  78. </template>
  79. </el-table-column>
  80. </el-table>
  81. <el-pagination
  82. @size-change="sizeChangeHandle"
  83. @current-change="currentChangeHandle"
  84. :current-page="pageIndex"
  85. :page-sizes="[10, 20, 50, 100]"
  86. :page-size="pageSize"
  87. :total="totalPage"
  88. layout="total, sizes, prev, pager, next, jumper">
  89. </el-pagination>
  90. </div>
  91. </template>

1.2 goods-update.vue JS代码部分:

  1. <script>
  2. export default {
  3. data () {
  4. return {
  5. dataList: [],
  6. pageIndex: 1,
  7. pageSize: 10,
  8. totalPage: 0,
  9. dataListLoading: false,
  10. dataListSelections: [],
  11. status:["未审核","己审核","审核未通过","己关闭"], //代表商品状态值,数据库为数值,为了显示成中文状态这里定义成数组
  12. itemCats:[],
  13. }
  14. },
  15. activated () {
  16. //1. 查询所有商品数据
  17. this.getDataList()
  18. //2. 查询所有分类
  19. this.findItemCats();
  20. },
  21. methods: {
  22. // 查询所有分类
  23. findItemCats(){
  24. this.$http({
  25. url: this.$http.adornUrl('/shop/itemcat/findAll'),
  26. method: 'get',
  27. }).then(({data}) => {
  28. if (data && data.code === 0) {
  29. for (let i = 0; i < data.list.length; i++) {
  30. let itemCat = data.list[i];
  31. console.log("itemCat:",itemCat);
  32. //以分类id为key,以分类名称为值放到分类数组中(为了显示中文字符)
  33. this.itemCats[itemCat.id] = itemCat.name;
  34. }
  35. }
  36. })
  37. },
  38. // 获取数据列表
  39. getDataList () {
  40. this.dataListLoading = true
  41. this.$http({
  42. url: this.$http.adornUrl('/shop/goods/list'),
  43. method: 'get',
  44. params: this.$http.adornParams({
  45. 'page': this.pageIndex,
  46. 'limit': this.pageSize,
  47. 'key': this.dataForm.key
  48. })
  49. }).then(({data}) => {
  50. if (data && data.code === 0) {
  51. this.dataList = data.page.list
  52. this.totalPage = data.page.totalCount
  53. } else {
  54. this.dataList = []
  55. this.totalPage = 0
  56. }
  57. this.dataListLoading = false
  58. })
  59. },
  60. // 每页数
  61. sizeChangeHandle (val) {
  62. this.pageSize = val
  63. this.pageIndex = 1
  64. this.getDataList()
  65. },
  66. // 当前页
  67. currentChangeHandle (val) {
  68. this.pageIndex = val
  69. this.getDataList()
  70. },
  71. // 多选
  72. selectionChangeHandle (val) {
  73. this.dataListSelections = val
  74. },
  75. // 新增 / 修改
  76. addOrUpdateHandle (id) {
  77. this.$router.push(`/shop-goods?id=${id}`)
  78. },
  79. // 删除
  80. deleteHandle (id) {
  81. var ids = id ? [id] : this.dataListSelections.map(item => {
  82. return item.id
  83. })
  84. this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
  85. confirmButtonText: '确定',
  86. cancelButtonText: '取消',
  87. type: 'warning'
  88. }).then(() => {
  89. this.$http({
  90. url: this.$http.adornUrl('/shop/goods/delete'),
  91. method: 'post',
  92. data: this.$http.adornData(ids, false)
  93. }).then(({data}) => {
  94. if (data && data.code === 0) {
  95. this.$message({
  96. message: '操作成功',
  97. type: 'success',
  98. duration: 1500,
  99. onClose: () => {
  100. this.getDataList()
  101. }
  102. })
  103. } else {
  104. this.$message.error(data.msg)
  105. }
  106. })
  107. })
  108. }
  109. }
  110. }
  111. </script>

1.3 运行效果如下:

image.png

二、商品的修改

第一部分:修改商品的展示:

2.1 点击“修改”按钮事件处理:

  1. // 修改
  2. addOrUpdateHandle (id) {
  3. this.$router.push(`/shop-goods?id=${id}`)
  4. },

说明: 代表跳转到/shop-goods页面,即:shop目录下的goods.vue页面。

2.2 在modules/shop/goods.vue文件中,得到传入id并发请求获取复合商品对象

  1. export default {
  2. data () {
  3. return {
  4. goodsForm:{
  5. goods:{typeTemplateId:'',brandId:''},
  6. goodsDesc:{itemImages:[],customAttributeItems:[],specificationItems:[]},
  7. items:[],
  8. },
  9. categorys1:[], //一级分类
  10. categorys2:[], //二级分类
  11. categorys3:[], //三级分类
  12. brandList:[], //代表某个模板下的品牌列表
  13. dialogFormVisible:false, //代表对话框的显示与隐藏
  14. imageEntity:{}, //代表每次上传的文件对象
  15. fileList:[], //代表上传后的文件列表
  16. specGroup:[], //代表规格及规格列表
  17. optionList:[], //代表是否选择
  18. }
  19. },
  20. created() {
  21. //1. 查询一级分类
  22. this.findCategorys1();
  23. //2. 接受修改页面的商品id,并根据id查询商品
  24. this.findById();
  25. },
  26. watch:{ //用于监控某个变量值的变化
  27. category1newId(itemCatId,oldV){ //itemCatId:代表当前用户选择的一级分类的id
  28. //1. 根据一级分类的父id向后台发出查询请求,查询出此一级分类id对应的二级分类列表
  29. this.$http({
  30. url: this.$http.adornUrl(`/shop/itemcat/findByParentId/${itemCatId}`),
  31. method: 'get',
  32. }).then(({data}) => {
  33. if(data.code == 0){
  34. // delete this.goodsForm.goods.category2Id
  35. this.categorys2 = data.list;
  36. }
  37. })
  38. },
  39. category2newId(itemCatId,oldV){ //itemCatId:代表当前用户选择的二级分类的id
  40. //1. 根据二级分类的父id向后台发出查询请求,查询出此一级分类id对应的三级分类列表
  41. this.$http({
  42. url: this.$http.adornUrl(`/shop/itemcat/findByParentId/${itemCatId}`),
  43. method: 'get',
  44. }).then(({data}) => {
  45. if(data.code == 0){
  46. // delete this.goodsForm.goods.category3Id
  47. this.categorys3 = data.list;
  48. }
  49. })
  50. },
  51. category3newId(itemCatId,oldV){ //itemCatId:代表当前用户选择的三级分类的id
  52. //1. 根据三级分类的父id向后台发出查询请求,查询出此三级分类对应的模板id
  53. this.$http({
  54. url: this.$http.adornUrl(`/shop/itemcat/info/${itemCatId}`),
  55. method: 'get',
  56. }).then(({data}) => {
  57. if(data.code == 0){
  58. this.goodsForm.goods.typeTemplateId = data.itemCat.typeId;
  59. }
  60. })
  61. },
  62. typetemplatenewId(typeId,oldV){ //typeId:代表当前模板id
  63. //0.1 得到修改页面传入的参数id
  64. let id = this.$route.query.id;
  65. //1. 模板id发生变化就到后台查询出这个模板对应的品牌列表
  66. //2. 模板id发生变化,到模板对象中的扩展属性
  67. this.$http({
  68. url: this.$http.adornUrl(`/shop/typetemplate/info/${typeId}`),
  69. method: 'get',
  70. }).then(({data}) => {
  71. if(data.code == 0){
  72. // console.log("data:",data)
  73. //① 得到模板的品牌列表
  74. this.brandList = JSON.parse(data.typeTemplate.brandIds);
  75. //② 得到模板的扩展属性列表
  76. if(!id){ //如果是添加商品操作,才执行下面的语句
  77. this.goodsForm.goodsDesc.customAttributeItems = JSON.parse(data.typeTemplate.customAttributeItems)
  78. }
  79. //③ 得到模板的规格及规格选项列表
  80. this.$http({
  81. url: this.$http.adornUrl(`shop/specification/findSpecByTypeId/${typeId}`),
  82. method: 'get',
  83. }).then(({data}) => {
  84. this.specGroup = data.specifications;
  85. console.log("data:",this.specGroup);
  86. if(!this.$route.query.id){ //此处一定要添加这个条件,否则,此值就会在显示修改页面时,将得到的optionList的值覆盖掉
  87. this.optionList = [];
  88. data.specifications.forEach(sp=>{
  89. this.optionList.push([]);
  90. })
  91. }
  92. })
  93. }
  94. })
  95. }
  96. },
  97. computed:{ //自动计算属性值的变化
  98. category1newId(){ //1. 计算一级分类id的值是否变化
  99. return this.goodsForm.goods.category1Id;
  100. },
  101. category2newId(){ //2. 计算二级分类id的值是否变化
  102. return this.goodsForm.goods.category2Id;
  103. },
  104. category3newId(){ //3. 计算三级分类id的值是否变化
  105. return this.goodsForm.goods.category3Id;
  106. },
  107. typetemplatenewId(){ //4. 监控模板id值的变化
  108. return this.goodsForm.goods.typeTemplateId;
  109. }
  110. },
  111. methods: {
  112. //0.根据商品id,查询商品
  113. findById(){
  114. //0.1 得到修改页面传入的参数id
  115. let id = this.$route.query.id;
  116. let _this = this;
  117. //0.2 根据商品id在后台查询商品
  118. this.$http({
  119. url: this.$http.adornUrl(`/shop/goods/findById/${id}`),
  120. method: 'get',
  121. }).then(({data}) => {
  122. if(data.code == 0){
  123. _this.goodsForm = data.goods;
  124. this.goodsForm.goods.brandId = parseInt(data.goods.goods.brandId);
  125. //0.3 将后端查询到的字符串转换为json对象
  126. this.goodsForm.goodsDesc.specificationItems = JSON.parse(this.goodsForm.goodsDesc.specificationItems)
  127. this.goodsForm.goodsDesc.itemImages = JSON.parse(this.goodsForm.goodsDesc.itemImages)
  128. this.goodsForm.goodsDesc.customAttributeItems = JSON.parse(this.goodsForm.goodsDesc.customAttributeItems)
  129. this.goodsForm.items.forEach(item=>{
  130. item.spec = JSON.parse(item.spec);
  131. })
  132. //0.4 让规格选项自动勾选
  133. this.optionList = [];
  134. this.goodsForm.goodsDesc.specificationItems.forEach(spec=>{
  135. this.optionList.push(spec.attributeValue);
  136. })
  137. console.log("optionList:",this.optionList);
  138. }
  139. })
  140. },
  141. //1. 查询一级分类
  142. findCategorys1(){
  143. this.$http({
  144. url: this.$http.adornUrl('/shop/itemcat/findAll'),
  145. method: 'get',
  146. }).then(({data}) => {
  147. this.categorys1 = data.categorys1;
  148. })
  149. },
  150. //保存商品
  151. save(){
  152. //1. 处理前端的json对象为字符串,因为后台都是字符串
  153. this.goodsForm.goodsDesc.specificationItems = JSON.stringify(this.goodsForm.goodsDesc.specificationItems)
  154. this.goodsForm.goodsDesc.itemImages = JSON.stringify(this.goodsForm.goodsDesc.itemImages)
  155. this.goodsForm.goodsDesc.customAttributeItems = JSON.stringify(this.goodsForm.goodsDesc.customAttributeItems)
  156. this.goodsForm.items.forEach(item=>{
  157. item.spec = JSON.stringify(item.spec);
  158. })
  159. //2. 向后台发送请求
  160. this.$http({
  161. url: this.$http.adornUrl(`/shop/goods/${this.$route.query.id ? 'update':'save'} `),
  162. method: `${this.$route.query.id ? 'put' : 'post'}`,
  163. data:this.goodsForm
  164. }).then(({data}) => {
  165. if(data.code == 0){
  166. this.goodsForm={
  167. goods:{typeTemplateId:''},
  168. goodsDesc:{itemImages:[],customAttributeItems:[],specificationItems:[]},
  169. items:[],
  170. }
  171. this.fileList = []
  172. this.optionList=[]; //选择的规格选项列表
  173. this.specGroup=[]
  174. this.imageEntity = {}
  175. }
  176. })
  177. },
  178. //进行文件上传
  179. uploadFile(val){
  180. //1. 构造上传的表单对象
  181. let data = new FormData();
  182. //2. 添加要上传的文件数据
  183. data.append("file",val.file); //上传的文件对象
  184. //3. 清空上传的列表
  185. this.fileList = [];
  186. //4. 开始上传
  187. this.$http({
  188. url: this.$http.adornUrl('/shop/upload'),
  189. method: 'post',
  190. data:data,
  191. headers:{"Context-Type":"multipart/form-data"}
  192. }).then(({data}) => {
  193. if(data.code == 0){
  194. //如果上传成功,就向文件列表添加文件
  195. this.fileList.push({name:val.raw,url:data.url});
  196. this.imageEntity.url = data.url;
  197. }
  198. })
  199. },
  200. //保存图片
  201. saveImage(){
  202. this.goodsForm.goodsDesc.itemImages.push(this.imageEntity);
  203. this.dialogFormVisible=false
  204. },
  205. //点击规格选项时的事件处理
  206. getSpecItems(){
  207. console.log("optionList:",this.optionList); //this.optionList:放的是选中的规格选项的值
  208. if(!this.$route.query.id){
  209. }
  210. //0. 每次要设置为默认值
  211. this.goodsForm.goodsDesc.specificationItems = [];
  212. //1. 遍历optionList,在其中遍历this.specGroup,为this.goodsForm.goodsDesc.specificationItems赋值
  213. for (let i = 0; i < this.optionList.length; i++) {
  214. //1.1 得到对应规格的名称
  215. let specName = this.specGroup[i].text;
  216. //1.2 为this.goodsForm.goodsDesc.specificationItems赋值
  217. this.goodsForm.goodsDesc.specificationItems.push({"attributeName":specName,"attributeValue":this.optionList[i]})
  218. }
  219. //2. 【方法一】进行兼容性处理,如果attributeValue这个数组没有值,就将这个对象从this.goodsForm.goodsDesc.specificationItems数组中删除
  220. // for (let i = 0; i < this.optionList.length; i++) {
  221. // let len = this.goodsForm.goodsDesc.specificationItems[i].attributeValue.length;
  222. // if(len == 0){
  223. // this.goodsForm.goodsDesc.specificationItems.splice(i,1);
  224. // break;
  225. // }
  226. // }
  227. //3. 【方法二】采用过滤器处理,只显示attributeValue.length > 0
  228. this.goodsForm.goodsDesc.specificationItems = this.goodsForm.goodsDesc.specificationItems.filter(f=>f.attributeValue.length > 0);
  229. //4. 为goodsForm.items(sku商品列表)赋值
  230. this.createItems();
  231. },
  232. createItems(){
  233. //1. 定义sku商品列表的初始值
  234. this.goodsForm.items = [{spec:{},price:0,num:9999,status:'0',isDefault:'0'}];
  235. //2. 得到用户点击的规格及选项的值
  236. let items = this.goodsForm.goodsDesc.specificationItems;
  237. //3. 遍历上面的items将用户勾选的规格及选项的值赋值给sku列表
  238. items.forEach(item=>{
  239. this.goodsForm.items = this.createColumn(this.goodsForm.items,item.attributeName,item.attributeValue);
  240. })
  241. },
  242. createColumn(items,specName,specValue){
  243. //1. 定义存放返回结果的数组
  244. let skuList = [];
  245. //2. 遍历items数组
  246. items.forEach(item=>{
  247. specValue.forEach(val=>{
  248. //2.1 根据原来的数据深克隆出新行数据
  249. let row = JSON.parse(JSON.stringify(item));
  250. //2.2 为新行的spec对象赋值
  251. row.spec[specName] = val
  252. //2.3 添加此新行到返回的新数组中
  253. skuList.push(row);
  254. })
  255. })
  256. return skuList;
  257. },
  258. }
  259. }
  260. </script>

注意: 1、在修改页面到达时,对于自定义扩展属性一定要判断是否有id,如果没有id才会其设置默认值:

  1. //② 得到模板的扩展属性列表
  2. if(!id){ //如果是添加商品操作,才执行下面的语句
  3. this.goodsForm.goodsDesc.customAttributeItems = JSON.parse(data.typeTemplate.customAttributeItems)
  4. }

注意: 2、在修改页面到达时,对于添加规格默认选中项列表值数组opitonList的处理:

  1. if(!this.$route.query.id){ //此处一定要添加这个条件,否则,此值就会在显示修改页面时,将得到的optionList的值覆盖掉
  2. this.optionList = [];
  3. data.specifications.forEach(sp=>{
  4. this.optionList.push([]);
  5. })
  6. }

2.3 goods.vue中的