前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >猿实战13——实现你没听说过的前台类目

猿实战13——实现你没听说过的前台类目

作者头像
山旮旯的胖子
发布2020-09-23 15:47:40
6320
发布2020-09-23 15:47:40
举报
文章被收录于专栏:猿人工厂

上几个章节,猿人君教会了你实现了属性/属性值和后台类目的绑定关系,今天,猿人君就带你来实现前台类目。

为什么会有前台类目

为了方便运营,在电商系统中,类目分为前台类目和后台类目。如果你经常网购,你会发现一个比较有意思的事情。鼠标移动到一级类目,会展示二级类目,点击一级二级类目,会跳转到对应的频道页面(不过做得大了都是分站,我们先考虑业务),点击三级类目会出发搜索的功能。

这些在之前的设计文章猿设计5——真电商之颠覆你的类目认知中已经讲述过了,这里就不再赘述了。

功能概览

前台类目,从某种程度上说,肩负了部分站点的导航以及内容组织的职责,要完成这一职责,我们可以先来看看需要实现的功能。

在前台类目模块中,系统体现了前台类目的层级关系,每一级类目都有对应的列表、新增/编辑功能。在一级类目中可以维护广告牌、在三级类目中,重点维护前后台类目的关系(以上本章节暂不讨论)。

数据库设计

根据之前的前台类目设计文章,我们很清楚的获知了前台类目的一些特征,我们根据之前的设计,将这些设计落地为数据库的物理设计,大家可以看一下(本章节仅讲述前台类目的实现)。

就类目而言,类目就像是一棵树,每一级类目都有可能有下一级类目,像这种特性,在数据库的设计上,是一种典型的自关联结构。但是类目用于检索时,往往伴随着层级关系的查找,比如提取某一级类目的信息。所以,我们在设计时,给予了一个level字段,用于方便提取固定层级的信息。

由于每一级维护的信息可能不同,所以在数据库设计上,我们做了一个整合,每一条行记录的信息是一二三级所有信息的集合,事实上,不同的层级只涉及部分字段内容,这种设计,也是一种常见的设计。

前台类目整体前端

类目的层级维护,从功能上讲,依然是一种整体和部分的关系,出于业务的考虑,在规模不是特别庞大的情况下,三级已经足够支撑你的运营。我们可以参考下之前的实现思路——将一级类目、二级类目、三级类目分别定义成小的组件。最后,由一个view来组织和整合它们就好了。

代码语言:javascript
复制
<template>
  <div id="fnCategoryDiv">
    <div v-if="oneCategoryShow">
      <frontDeskCategoryOneSearch ref="frontDeskCategoryOneSearch" @lookSubordinate="lookOneSubordinate" />
    </div>
    <div v-if="twoCategoryShow">
      <frontDeskCategoryTwoSearch ref="frontDeskCategoryTwoSearch" :pid="parentId" :pname="parentName" @lookSubordinate="lookTwoSubordinate" @returnBack="returnTwoBack" />
    </div>
    <div v-if="threeCategoryShow">
      <frontDeskCategoryThreeSearch ref="frontDeskCategoryThreeSearch" :pid="parentId" :pname="parentName" @returnBack="returnThreeBack" />
    </div>
  </div>
</template>
 
<script>
import frontDeskCategoryOneSearch from '@/components/productManage/frontDeskCategoryOneSearch'
import frontDeskCategoryTwoSearch from '@/components/productManage/frontDeskCategoryTwoSearch'
import frontDeskCategoryThreeSearch from '@/components/productManage/frontDeskCategoryThreeSearch'
export default {
  components: {
    frontDeskCategoryOneSearch,
    frontDeskCategoryTwoSearch,
    frontDeskCategoryThreeSearch
  },
  data() {
    return {
      // 一级类目
      oneCategoryShow: false,
      // 二级类目
      twoCategoryShow: false,
      // 三级类目
      threeCategoryShow: false,
      parentId: 0,
      parentName: '',
      catOneId: 0
    }
  },
  created() {
    // 显示一级类目
    this.oneCategoryShow = true
  },
  methods: {
    // 二级回退
    returnTwoBack() {
      // 一级二级三级类目显示设置
      this.oneCategoryShow = true
      this.twoCategoryShow = false
      this.threeCategoryShow = false
    },
    // 三级回退
    returnThreeBack(pid, pname) {
      // 一级二级三级类目显示设置
      this.oneCategoryShow = false
      this.twoCategoryShow = true
      this.threeCategoryShow = false
      this.parentId = this.catOneId
      this.parentName = pname
      console.log(this.parentId)
    },
    // 一级查看下级类目
    lookOneSubordinate(row) {
      // 一级二级三级类目显示设置
      this.oneCategoryShow = false
      this.twoCategoryShow = true
      this.threeCategoryShow = false
      this.parentId = row.fnCategoryId
      this.parentName = row.fnCategoryName
      this.catOneId = row.fnCategoryId
    },
    // 二级查看下级类目
    lookTwoSubordinate(row) {
      // 一级二级三级类目显示设置
      this.oneCategoryShow = false
      this.twoCategoryShow = false
      this.threeCategoryShow = true
      this.parentId = row.fnCategoryId
      this.parentName = row.fnCategoryName
    }
  }
}
</script>
 
<style scoped>
 
</style>

值得注意的是,在查看下级和返回上级种,涉及组件之间的参数传递。您需要将,本级类目的ID作为父ID传入下级,而在下级返回上级的操作种,您需要将上上级的id作为父ID传入上一级列表页面(二级除外)。

关于父子组件之间的通信问题,在之前的文章中已经讲述过很多了,这里就不再赘述了。

前台类目后端实现

其实就前台类目的普通功能而言,目前来说相对简单,最主要的是建立父子关联这样一个自关联的概念。

由于之前已经给出了我们自己定义的代码生成器,属性组的实现也相对简单,考虑到篇幅问题,这一部分我们给出Controller层面的功能实现,service、和dao,还是希望你自行实现,在初学时期,多谢代码,对你熟悉掌握代码编写的技巧,是一个必不可少的环节。

代码语言:javascript
复制
/**
 * Copyright(c) 2004-2020 pangzi
 * com.pz.basic.mall.controller.product.fncategory.MallFnCategoryController.java
 */
package com.pz.basic.mall.controller.product.fncategory;
 
import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.product.category.query.QueryMallCategory;
import com.pz.basic.mall.domain.product.category.vo.MallCategoryVo;
import com.pz.basic.mall.domain.product.fncategory.MallFnBgCategoryRel;
import com.pz.basic.mall.domain.product.fncategory.MallFnCategory;
import com.pz.basic.mall.domain.product.fncategory.query.QueryMallFnBgCategoryRel;
import com.pz.basic.mall.domain.product.fncategory.query.QueryMallFnCategory;
import com.pz.basic.mall.service.product.category.MallCategoryService;
import com.pz.basic.mall.service.product.fncategory.MallFnBgCategoryRelService;
import com.pz.basic.mall.service.product.fncategory.MallFnCategoryService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
 
 
/**
 *
 * @author pangzi
 * @date 2020-06-22 20:47:27
 *
 *
 */
@RestController
@RequestMapping("/fncategory")
public class MallFnCategoryController {
 
 
    private MallFnCategoryService mallFnCategoryService;
 
 
    public void setMallFnCategoryService(MallFnCategoryService mallFnCategoryService) {
        this.mallFnCategoryService = mallFnCategoryService;
    }
 
    /**
     * 新增类目
     * @param mallFnCategory
     * @return
     */
    @RequestMapping("/addMallFnCategory")
    public Result<MallFnCategory>  addMallFnCategory(@RequestBody MallFnCategory mallFnCategory){
        try{
            return mallFnCategoryService.addMallFnCategory(mallFnCategory);
        }catch(Exception e){
            e.printStackTrace();
            return new Result(false);
        }
    }
 
    /**
     * 根据ID查找类目
     * @param id
     * @return
     */
    @RequestMapping("/findMallFnCategoryById")
    public  Result<MallFnCategory> findMallFnCategoryById(Long id){
        return mallFnCategoryService.getMallFnCategoryById(id.intValue());
    }
 
    /**
     * 修改类目
     * @param mallFnCategory
     * @return
     */
    @RequestMapping("/updateMallFnCategory")
    public Result updateMallFnCategory(@RequestBody MallFnCategory mallFnCategory){
        try{
            return  mallFnCategoryService.updateMallFnCategoryById(mallFnCategory);
        }catch(Exception e){
            e.printStackTrace();
            return new Result(false);
        }
    }
 
 
    /**
     * 分页返回类目列表
     * @param queryMallFnCategory
     * @return
     */
    @RequestMapping("/findByPage")
    public  Result<List<MallFnCategory>> findByPage(@RequestBody QueryMallFnCategory queryMallFnCategory){
        return mallFnCategoryService.getMallFnCategorysByPage(queryMallFnCategory);
    }
}

后台类目前端实现

聊完了后端数据接口的事情,我们一起来看看前端实现的问题,考虑到大部分朋友前端并不是很熟悉,我们再讲讲后台类目前端API组件的封装。

我们先封装访问后端的数据接口:

代码语言:javascript
复制
// 前台类目
 
export function fetchFnCategoryList(query) {
  return request({
    url: '/fncategory/findByPage',
    method: 'post',
    data: query
  })
}
 
export function createMallFnCategory(data) {
  return request({
    url: '/fncategory/addMallFnCategory',
    method: 'post',
    data
  })
}
 
export function updateMallFnCategory(data) {
  return request({
    url: '/fncategory/updateMallFnCategory',
    method: 'post',
    data
  })
}

考虑到之前有朋友说页面编写起来实在困难,在这里, 为了让你更好的掌握前台类目的内容,这次将一二三级的前台页面都给到你。不过猿人君还是希望你尽量自己编码。不然,你真的很难掌握开发技巧。

一级前台类目

代码语言:javascript
复制
<template>
  <div id="frontDeskCategoryOneSearchDiv">
    <div>
      <el-form ref="listQuery" :model="listQuery" :inline="true">
        <el-form-item label="类目名称:" prop="fnCategoryNameLike">
          <el-input v-model="listQuery.fnCategoryNameLike" placeholder="请输入类目名称" clearable />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" icon="el-icon-search" @click="fetchData()">查询</el-button>
          <el-button type="primary" icon="el-icon-edit" style="float:right;margin-bottom:20px;" @click="addDate()">新增一级</el-button>
        </el-form-item>
      </el-form>
    </div>
    
    <div>
      <el-table
        ref="table"
        v-loading="listLoading"
        :data="list"
        style="width: 100%"
        border
      >
        <el-table-column label="类目ID">
          <template slot-scope="scope">{{ scope.row.fnCategoryId }}</template>
        </el-table-column>
        <el-table-column label="类目名称">
          <template slot-scope="scope">{{ scope.row.fnCategoryName }}</template>
        </el-table-column>
        <el-table-column label="父类目ID">
          无
        </el-table-column>
        <el-table-column label="类目级别">
          一级类目
        </el-table-column>
        <el-table-column label="排序">
          <template slot-scope="scope">{{ scope.row.sortOrder }}</template>
        </el-table-column>status
        <el-table-column label="是否上架">
          <template slot-scope="scope">{{ scope.row.status ==1?"上架":"下架" }}</template>
        </el-table-column>
        <el-table-column label="操作" width="260">
          <template slot-scope="scope">
            <el-button
              type="primary"
              size="mini"
              @click="handleSubordinate(scope.row)"
            >查看下级
            </el-button>
            <el-button
              type="primary"
              size="mini"
              @click="handleUpdate(scope.row)"
            >修改
            </el-button>
            <el-button
              type="primary"
              size="mini"
              @click="handleAdvertisingBoard(scope.row)"
            >广告牌
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.pageSize" @pagination="getList" />
    <!-- 新增/编辑弹框 -->
    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
      <el-form ref="dataForm" :rules="rules" :model="temp" label-position="right" label-width="120px" style="width: 320px; margin-left:50px;">
        <el-form-item label="类目名称:" prop="fnCategoryName">
          <el-input v-model="temp.fnCategoryName" placeholder="请输入类目中文名" />
        </el-form-item>
        <el-form-item label="类目排序:" prop="sortOrder">
          <el-input-number
            v-model="temp.sortOrder"
            :min="0"
            :max="100"
            placeholder="请输入类目排序"
          />
        </el-form-item>
        <el-form-item label="是否上柜:" prop="status">
          <el-select v-model="temp.status" placeholder="请选择">
            <el-option
              v-for="(item,index) in valueList"
              :key="index"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="类目ICON:">
          <div style="width:300%">
            <el-upload
              action="http://127.0.0.1:9201//upload/uploadFile?moudle=fncategory"
              list-type="picture-card"
              :on-preview="handlePictureCardPreview"
              :on-remove="handleRemove"
              :on-success="handleSuccess"
              :file-list="fnCategoryFileList"
            >
              <i />
            </el-upload>
            <el-dialog :visible.sync="dialogVisible">
              <img width="100%" :src="dialogImageUrl" alt="">
            </el-dialog>
          </div>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="dialogFormVisible = false">
          取消
        </el-button>
        <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
          确定
        </el-button>
      </div>
    </el-dialog>
    <!-- 广告牌 -->
    <el-dialog title="广告牌" :visible.sync="dialogAdvertisingBoardVisible">
      <el-form ref="dataFormAdvertisingBoard" :rules="billboardRules" :model="tempBillboard" label-position="right" label-width="120px" style="width: 320px; margin-left:50px;">
        <el-form-item label="广告图片:" prop="picture">
          <div style="width:300%">
            <el-upload
              action="http://127.0.0.1:9201//upload/uploadFile?moudle=fncategory/billboard"
              list-type="picture-card"
              :on-preview="handleBillboardPreview"
              :on-remove="handleRemove"
              :on-success="handleBillboardSuccess"
              :file-list="fnCategoryBillboardFileList"
            >
              <i />
            </el-upload>
            <el-dialog :visible.sync="dialogVisible">
              <img width="100%" :src="billboardImageUrl" alt="">
            </el-dialog>
          </div>
        </el-form-item>
        <el-form-item label="链接类型:" prop="type">
          <el-radio v-model="tempBillboard.type" :label="1">单链接</el-radio>
          <el-radio v-model="tempBillboard.type" :label="2">多链接</el-radio>
        </el-form-item>
        <el-form-item label="跳转内容" prop="redirectArea">
          <el-input
            v-model="tempBillboard.redirectArea"
            style="width: 200%;"
            type="textarea"
            :rows="3"
            placeholder="请输入内容"
          />
        </el-form-item>
        <el-form-item label="广告牌状态:" prop="status">
          <el-radio v-model="tempBillboard.status" :label="1">停用</el-radio>
          <el-radio v-model="tempBillboard.status" :label="2">启用</el-radio>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="dialogAdvertisingBoardVisible = false">
          取消
        </el-button>
        <el-button type="primary" @click="advertisingBoardVisibleClick">
          确定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
 
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import { fetchFnCategoryList, createMallFnCategory, updateMallFnCategory, fetchFnCategoryBillboardList, createMallFnCategoryBillboard, updateMallFnCategoryBillboard } from '@/api/product-manage'
export default {
  components: { Pagination },
  data() {
    return {
      tempBillboard: {
        id: undefined,
        fnCategoryId: undefined,
        // 链接类型
        type: 1,
        // map_area
        redirectArea: '',
        // 广告牌状态
        status: 1,
        imageUrl: ''
      },
      addBillboard: true,
      billboardImageUrl: '',
      //
      dialogImageUrl: '',
      dialogVisible: false,
      //
      dialogStatus: '',
      // 弹框是否显示
      dialogFormVisible: false,
      // 广告牌弹框是否显示
      dialogAdvertisingBoardVisible: false,
      // 广告牌弹框校验规则
      billboardRules: {
        picture: [{ required: true, message: '请上传图片', trigger: 'blur' }],
        redirectArea: [{ required: true, message: '请输入跳转内容', trigger: 'blur' }],
        status: [{ required: true, message: '请选择是否上架', trigger: 'blur' }],
        type: [{ required: true, message: '请选择连接类型', trigger: 'blur' }]
      },
      // 弹框校验规则
      rules: {
        fnCategoryName: [{ required: true, message: '请输入类目名称', trigger: 'change' }],
        status: [{ required: true, message: '请选择是否上架', trigger: 'change' }],
        sortOrder: [{ required: true, message: '请输入排序', trigger: 'blur' }]
      },
      temp: {
        fnCategoryId: undefined,
        // 前台类目名:
        fnCategoryName: '',
        // 是否上架
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 1,
        special: 0,
        fnCategoryImage: ''
      },
      fnCategoryFileList: [],
      fnCategoryBillboardFileList: [],
      // 状态
      valueList: [{
        value: 1,
        label: '是'
      }, {
        value: 0,
        label: '否'
      }],
      textMap: {
        update: '一级类目修改',
        create: '一级类目新增'
      },
      // table集合
      list: null,
      multipleSelection: [],
      // 分页
      total: 0,
      // loading
      listLoading: true,
      // 属性集合
      categorypointToList: [
        {
          value: '无',
          label: '无'
        }
      ],
      listQuery: {
        level: 1,
        page: 1,
        pageSize: 10
      },
      listBillboardQuery: {
        page: 1,
        pageSize: 1
      }
    }
  },
  created() {
    // 列表查询
    this.getList()
  },
  methods: {
    // 广告牌弹框保存
    advertisingBoardVisibleClick(row) {
      if (this.addBillboard) {
        createMallFnCategoryBillboard(this.tempBillboard).then(res => {
          this.tempBillboard.id = res.model.id
          this.dialogAdvertisingBoardVisible = false
          this.$notify({
            title: 'Success',
            message: 'Save Successfully',
            type: 'success',
            duration: 2000
          })
        })
      } else {
        updateMallFnCategoryBillboard(this.tempBillboard).then(res => {
          this.dialogAdvertisingBoardVisible = false
          this.$notify({
            title: 'Success',
            message: 'Save Successfully',
            type: 'success',
            duration: 2000
          })
        })
      }
    },
    // 广告牌
    handleAdvertisingBoard(row) {
      this.dialogAdvertisingBoardVisible = true
      this.tempBillboard.fnCategoryId = row.fnCategoryId
      this.listBillboardQuery.fnCategoryId = row.fnCategoryId
      fetchFnCategoryBillboardList(this.listBillboardQuery).then(response => {
        this.fnCategoryBillboardFileList = []
        if (response.model !== null && response.model.length > 0) {
          this.tempBillboard = response.model[0]
          this.addBillboard = false
          if (this.tempBillboard.imageUrl !== null && this.tempBillboard.imageUrl !== '') {
            const obj = new Object()
            obj.url = this.tempBillboard.imageUrl
            this.fnCategoryBillboardFileList.push(obj)
          }
        }
 
        // Just to simulate the time of the request
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
      })
    },
    // 查看下级
    handleSubordinate(row) {
      this.$emit('lookSubordinate', row)
    },
    // 编辑
    handleUpdate(row) {
      this.fnCategoryFileList = []
      console.log(row)
      this.temp = Object.assign({}, row) // copy obj
      if (undefined !== row.fnCategoryImage && row.fnCategoryImage !== null && row.fnCategoryImage !== '') {
        const obj = new Object()
        obj.url = row.fnCategoryImage
        console.log(obj)
        this.fnCategoryFileList.push(obj)
      }
      this.dialogStatus = 'update'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 重置
    resetTemp() {
      this.temp = {
        id: undefined,
        // 类目中文名:
        fnCategoryName: '',
        // 是否上柜
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 1,
        special: 0,
        fnCategoryImage: ''
      }
      this.fnCategoryFileList = []
    },
    resetTempBillboard() {
      this.tempBillboard = {
        id: undefined,
        fnCategoryId: undefined,
        // 链接类型
        type: 1,
        // map_area
        redirectArea: '',
        // 广告牌状态
        status: 1,
        imageUrl: ''
      }
      this.fnCategoryBillboardFileList = []
    },
    // 新增一级类目
    addDate() {
      this.resetTemp()
      this.dialogStatus = 'create'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 更新保存方法
    updateData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          const tempData = Object.assign({}, this.temp)
          updateMallFnCategory(tempData).then(() => {
            const index = this.list.findIndex(v => v.id === this.temp.id)
            this.list.splice(index, 1, this.temp)
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Update Successfully',
              type: 'success',
              duration: 2000
            })
            this.getList()
          })
        }
      })
    },
    // 创建保存方法
    createData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          createMallFnCategory(this.temp).then(() => {
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Created Successfully',
              type: 'success',
              duration: 2000
            })
            this.getList()
          })
        }
      })
    },
    fetchData() {
      this.getList()
    },
    // 列表查询
    getList() {
      this.listLoading = true
      fetchFnCategoryList(this.listQuery).then(response => {
        this.list = response.model
        this.total = response.totalItem
 
        // Just to simulate the time of the request
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
      })
    },
    handleRemove(file, fileList) {
      console.log(file, fileList)
    },
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url
    },
    handleSuccess(res, file, fileList) {
      this.temp.fnCategoryImage = res.model
      console.log(this.temp.fnCategoryImage)
    },
    handleBillboardPreview(file) {
      this.billboardImageUrl = file.url
    },
    handleBillboardSuccess(res, file, fileList) {
      this.tempBillboard.imageUrl = res.model
      console.log(this.tempBillboard.imageUrl)
    }
  }
}
</script>
 
<style scoped>
</style>

二级前台类目

代码语言:javascript
复制
<template>
  <div id="backgroundCategoryTwoSearchDiv">
    <div style="float:right">
      <span @click="returnBack">< <span style="font-size:15px;margin-top:50px;line-height: 30px;">返回上一级</span></span>
      <el-button type="primary" icon="el-icon-edit" style="margin-bottom:20px;float: left;margin-right: 40px;" @click="addDate()">新增二级</el-button>
    </div>
    <div>
      <el-table
        ref="table"
        v-loading="listLoading"
        :data="list"
        style="width: 100%"
        border
      >
        <el-table-column label="类目ID">
          <template slot-scope="scope">{{ scope.row.fnCategoryId }}</template>
        </el-table-column>
        <el-table-column label="类目名称">
          <template slot-scope="scope">{{ scope.row.fnCategoryName }}</template>
        </el-table-column>
        <el-table-column label="父类目ID">
          <template slot-scope="scope">{{ scope.row.parentId }}</template>
        </el-table-column>
        <el-table-column label="类目级别">
          二级类目
        </el-table-column>
        <el-table-column label="排序">
          <template slot-scope="scope">{{ scope.row.sortOrder }}</template>
        </el-table-column>status
        <el-table-column label="是否上架">
          <template slot-scope="scope">{{ scope.row.status ==1?"上架":"下架" }}</template>
        </el-table-column>
        <el-table-column label="类目指向">
          <template slot-scope="scope">{{ scope.row.conditions }}</template>
        </el-table-column>
        <el-table-column label="操作" width="200">
          <template slot-scope="scope">
            <el-button
              type="primary"
              size="mini"
              @click="handleSubordinate(scope.row)"
            >查看下级
            </el-button>
            <el-button
              type="primary"
              size="mini"
              @click="handleUpdate(scope.row)"
            >修改
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
    <!-- 新增/编辑弹框 -->
    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
      <el-form ref="dataForm" :rules="rules" :model="temp" label-position="right" label-width="120px" style="width: 320px; margin-left:50px;">
        <el-form-item label="类目名称:" prop="fnCategoryName">
          <el-input v-model="temp.fnCategoryName" placeholder="请输入类目中文名" />
        </el-form-item>
        <el-form-item label="类目排序:" prop="sortOrder">
          <el-input-number
            v-model="temp.sortOrder"
            :min="0"
            :max="100"
            placeholder="请输入类目排序"
          />
        </el-form-item>
        <el-form-item label="是否上柜:" prop="status">
          <el-select v-model="temp.status" placeholder="请选择">
            <el-option
              v-for="(item,index) in valueList"
              :key="index"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="类目指向:" prop="conditions">
          <el-input v-model="temp.conditions" placeholder="请输入类目指向" />
        </el-form-item>
        <el-form-item label="类目ICON:">
          <div style="width:300%">
            <el-upload
              action="http://127.0.0.1:9201//upload/uploadFile?moudle=fncategory"
              list-type="picture-card"
              :on-preview="handlePictureCardPreview"
              :on-remove="handleRemove"
              :file-list="fnCategoryFileList"
            >
              <i />
            </el-upload>
            <el-dialog :visible.sync="dialogVisible">
              <img width="100%" :src="dialogImageUrl" alt="">
            </el-dialog>
          </div>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="dialogFormVisible = false">
          取消
        </el-button>
        <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
          确定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
 
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import { fetchFnCategoryList, createMallFnCategory, updateMallFnCategory } from '@/api/product-manage'
export default {
  components: { Pagination },
  props: {
    pid: Number,
    pname: String
  },
  data() {
    return {
      // 二级类目指向弹框是否显示
      dialogFormPointToVisible: false,
      dialogStatus: '',
      // 弹框是否显示
      dialogFormVisible: false,
      tempPointTo: {
        channelPage: ''
      },
      rulesPointTo: {
        channelPage: [{ required: true, message: 'channelPage is required', trigger: 'blur' }]
      },
      // 弹框校验规则
      rules: {
        fnCategoryName: [{ required: true, message: '请输入类目名称', trigger: 'change' }],
        status: [{ required: true, message: '请选择是否上架', trigger: 'change' }],
        sortOrder: [{ required: true, message: '请输入排序', trigger: 'blur' }]
      },
      temp: {
        fnCategoryId: undefined,
        // 前台类目名:
        fnCategoryName: '',
        // 是否上架
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 1,
        special: 0,
        fnCategoryImage: '',
        parentId: this.pid
      }, // 状态
      valueList: [{
        value: 1,
        label: '是'
      }, {
        value: 0,
        label: '否'
      }],
      textMap: {
        update: '二级类目修改',
        create: '二级类目新增'
      },
      // table集合
      list: null,
      multipleSelection: [],
      // 分页
      total: 0,
      // loading
      listLoading: true,
      //
      dialogImageUrl: '',
      dialogVisible: false,
      //
      listQuery: {
        level: 2,
        page: 1,
        pageSize: 10,
        parentId: this.pid
      },
      fnCategoryFileList: []
    }
  },
  created() {
    // 列表查询
    this.getList()
  },
  methods: {
    /**
     * 回退
     */
    returnBack() {
      this.$emit('returnBack')
    },
    // 管理
    handleManagement(row) {
      this.tempPointTo.channelPage = ''
      this.dialogFormPointToVisible = true
    },
    // 查看下级
    handleSubordinate(row) {
      this.$emit('lookSubordinate', row)
    },
    // 编辑
    handleUpdate(row) {
      this.temp = Object.assign({}, row) // copy obj
      this.fnCategoryFileList = []
      if (row.imageUrl !== null && row.imageUrl !== '') {
        const obj = {}
        obj.url = row.imageUrl
        this.fnCategoryFileList.push(obj)
      }
      this.dialogStatus = 'update'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 重置
    resetTemp() {
      this.temp = {
        id: undefined,
        // 类目中文名:
        fnCategoryName: '',
        // 是否上柜
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 1,
        special: 0,
        fnCategoryImage: '',
        parentId: this.pid
 
      }
      this.fnCategoryFileList = []
    },
    // 新增一级类目
    addDate() {
      this.resetTemp()
      this.dialogStatus = 'create'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 更新保存方法
    updateData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          const tempData = Object.assign({}, this.temp)
          updateMallFnCategory(tempData).then(() => {
            const index = this.list.findIndex(v => v.id === this.temp.id)
            this.list.splice(index, 1, this.temp)
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Update Successfully',
              type: 'success',
              duration: 2000
            })
            this.getList()
          })
        }
      })
    },
    // 创建保存方法
    createData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          createMallFnCategory(this.temp).then(() => {
            this.list.unshift(this.temp)
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Created Successfully',
              type: 'success',
              duration: 2000
            })
            this.getList()
          })
        }
      })
    },
    // 列表查询
    getList() {
      this.listLoading = true
      fetchFnCategoryList(this.listQuery).then(response => {
        this.list = response.model
        this.total = response.totalItem
 
        // Just to simulate the time of the request
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
      })
    },
    handleRemove(file, fileList) {
      console.log(file, fileList)
    },
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url
      this.dialogVisible = true
    }
  }
}
</script>
 
<style scoped>
/* .mouseMark:hover {
  cursor: pointer;
} */
</style>

三级前台类目

代码语言:javascript
复制
<template>
  <div id="backgroundCategoryTwoSearchDiv">
    <div style="float:right">
      <span @click="returnBack(pid,pname)">< <span style="font-size:15px;margin-top:50px;line-height: 30px;">返回上一级</span></span>
      <el-button type="primary" icon="el-icon-edit" style="margin-bottom:20px;float: left;margin-right: 40px;" @click="addDate()">新增三级</el-button>
    </div>
    <div>
      <el-table
        ref="table"
        v-loading="listLoading"
        :data="list"
        style="width: 100%"
        border
      >
        <el-table-column label="类目ID">
          <template slot-scope="scope">{{ scope.row.fnCategoryId }}</template>
        </el-table-column>
        <el-table-column label="类目名称">
          <template slot-scope="scope">{{ scope.row.fnCategoryName }}</template>
        </el-table-column>
        <el-table-column label="父类目ID">
          <template slot-scope="scope">{{ scope.row.parentId }}</template>
        </el-table-column>
        <el-table-column label="类目级别">
          三级类目
        </el-table-column>
        <el-table-column label="排序">
          <template slot-scope="scope">{{ scope.row.sortOrder }}</template>
        </el-table-column>status
        <el-table-column label="是否上架">
          <template slot-scope="scope">{{ scope.row.status ==1?"上架":"下架" }}</template>
        </el-table-column>
        <el-table-column label="类目指向">
          <template slot-scope="scope">
            <a style="color:#4395ff" @click="handleManagement(scope.row)">管理</a>
            <!-- </div> -->
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button
              type="primary"
              size="mini"
              @click="handleUpdate(scope.row)"
            >修改
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.pageSize" @pagination="getList" />
    <!-- 新增/编辑弹框 -->
    <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
      <el-form ref="dataForm" :rules="rules" :model="temp" label-position="right" label-width="120px" style="width: 320px; margin-left:50px;">
        <el-form-item label="类目名称:" prop="fnCategoryName">
          <el-input v-model="temp.fnCategoryName" placeholder="请输入类目中文名" />
        </el-form-item>
        <el-form-item label="类目排序:" prop="sortOrder">
          <el-input-number
            v-model="temp.sortOrder"
            :min="0"
            :max="100"
            placeholder="请输入类目排序"
          />
        </el-form-item>
        <el-form-item label="是否上柜:" prop="status">
          <el-select v-model="temp.status" placeholder="请选择">
            <el-option
              v-for="(item,index) in valueList"
              :key="index"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="类目指向:" prop="conditions">
          <el-input v-model="temp.conditions" placeholder="请输入类目指向" />
        </el-form-item>
        <el-form-item label="类目ICON:">
          <div style="width:300%">
            <el-upload
              action="http://127.0.0.1:9201//upload/uploadFile?moudle=fncategory"
              list-type="picture-card"
              :on-preview="handlePictureCardPreview"
              :on-remove="handleRemove"
              :file-list="fnCategoryFileList"
            >
              <i />
            </el-upload>
            <el-dialog :visible.sync="dialogVisible">
              <img width="100%" :src="dialogImageUrl" alt="">
            </el-dialog>
          </div>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="dialogFormVisible = false">
          取消
        </el-button>
        <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
          确定
        </el-button>
      </div>
    </el-dialog>
 
    <!-- 三级类目指向弹框 -->
    <el-dialog v-if="dialogFormSpecialVisible" title="三级类目指向" :visible.sync="dialogFormSpecialVisible">
      <el-row>
        <el-col :span="6">
          <div style="margin-bottom:10px;font-weight: 600;">第一步:选择后台类目</div>
          <el-card shadow="never">
            <div>
              <el-tree
                ref="tree"
                :data="data"
                show-checkbox
                node-key="categoryId"
                :default-expanded-keys="[1,2]"
                :props="treeProps"
                clearable
                filterable
                allow-create
                @check-change="updateKeyChildren"
              />
            </div>
          </el-card>
        </el-col>
        <el-col :span="1"> </el-col>
        <el-col :span="17">
          <el-tooltip placement="top-start" effect="light">
            <div slot="content">当某一后台三级类目与前台类目<br>进行初次绑定时,系统会强制将<br>对应前台类目设置成主类目</div>
            <div style="margin-bottom:10px;font-weight: 600;">第二步:设置当前类目为前台主类目    <i /></div>
          </el-tooltip>
          <el-card shadow="never">
            <div>
              <div>
                <el-table
                  ref="table"
                  :data="tableList"
                  style="width: 100%;"
                  border
                >
                  <el-table-column label="已选后台类目">
                    <template slot-scope="scope">
                      {{ scope.row.categoryName }}
                    </template>
                  </el-table-column>
                  <el-table-column label="已选前台主类目">
                    <template slot-scope="scope">
                      {{ scope.row.fnCategoryName }}
                    </template>
                  </el-table-column>
                  <el-table-column label="是否设置当前类目为前台主类目" width="140">
                    <template slot-scope="scope">
                      <el-checkbox v-model="scope.row.checked">是</el-checkbox>
                    </template>
                  </el-table-column>
                  <el-table-column label="操作" width="300">
                    <template slot-scope="scope">
                      <el-button
                        width="100"
                        type="danger"
                        @click="handleDelete(scope.$index, scope.row)"
                      >删除
                      </el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </el-card>
        </el-col>
        <div style="text-align: center;margin-top:20px;">
          <el-button @click="dialogFormSpecialVisible = false">
            取消
          </el-button>
          <el-button type="primary" @click="specialClick">
            确定
          </el-button>
        </div>
      </el-row>
 
    </el-dialog>
  </div>
</template>
 
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import { fetchFnCategoryList, createMallFnCategory, updateMallFnCategory, fetchFnCategoryForSelect, createMallFnBgCategoryRel, findForSelectedTable } from '@/api/product-manage'
export default {
  components: { Pagination },
  props: {
    pid: Number,
    pname: String
  },
  data() {
    return {
      atteibute: {
        id: undefined,
        mainCategory: 1,
        fnCategoryId: 0,
        categoryId: 0,
        categoryIdName: '',
        fnCategoryName: '',
        checked: true
      },
      fnCategoryId: undefined,
      // 前台类目名:
      fnCategoryName: '',
      fnCategoryFileList: [],
      treeProps: {
        label: 'categoryName',
        children: 'children'
      },
      data: [],
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      //
      dialogImageUrl: '',
      dialogVisible: false,
      //
      dialogStatus: '',
      // 特殊属性弹框是否显示
      dialogFormSpecialVisible: false,
      // 弹框是否显示
      dialogFormVisible: false,
      // 弹框校验规则
      rules: {
        fnCategoryName: [{ required: true, message: '请输入类目名称', trigger: 'change' }],
        status: [{ required: true, message: '请选择是否上架', trigger: 'change' }],
        sortOrder: [{ required: true, message: '请输入排序', trigger: 'blur' }]
      },
      // 类目指向
      categorypointToList: [
        {
          value: '无',
          label: '无'
        }, {
          value: '个性URL',
          label: '个性URL'
        }, {
          value: '频道页',
          label: '频道页'
        }, {
          value: '后台类目',
          label: '后台类目'
        }
      ],
      temp: {
        fnCategoryId: undefined,
        // 前台类目名:
        fnCategoryName: '',
        // 是否上架
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 3,
        special: 0,
        fnCategoryImage: '',
        parentId: this.pid
      },
      // 状态
      valueList: [
        {
          value: '是',
          label: '是'
        }, {
          value: '否',
          label: '否'
        }
      ],
      textMap: {
        update: '三级类目修改',
        create: '三级类目新增'
      },
      // table集合
      list: null,
      tableList: [
      ],
      multipleSelection: [],
      // 分页
      total: 0,
      // loading
      listLoading: true,
      listQuery: {
        level: 3,
        page: 1,
        pageSize: 10,
        parentId: this.pid
      },
      listDataQuery: {
        fnCategoryId: 0
      }
    }
  },
  created() {
    // 列表查询
    this.getList()
  },
  methods: {
    updateKeyChildren(data, key1, key2) {
      const checkedNodes = this.$refs.tree.getCheckedNodes()
      if (checkedNodes != null && checkedNodes.length > 0) {
        for (let i = 0; i < checkedNodes.length; i++) {
          this.atteibute = {
            id: undefined,
            fnCategoryId: this.fnCategoryId,
            categoryId: checkedNodes[i].categoryId,
            categoryName: checkedNodes[i].categoryName,
            fnCategoryName: this.fnCategoryName,
            status: 1,
            checked: true
          }
          const checkedNode = checkedNodes[i]
          if (checkedNode.categoryId !== null && checkedNode.level === 3) {
            let flag = true
            for (let j = 0; j < this.tableList.length; j++) {
              if (this.tableList[j].categoryId === checkedNode.categoryId) {
                console.log(checkedNode)
                flag = false
              }
            }
            if (flag) {
              if (checkedNode.checked) {
                this.atteibute.mainCategory = 1
              } else {
                this.atteibute.mainCategory = 0
              }
              this.tableList.push(this.atteibute)
            }
          }
        }
      }
    },
    // 管理保存
    specialClick() {
      if (this.tableList.length < 1) {
        this.dialogFormSpecialVisible = false
        return
      }
 
      createMallFnBgCategoryRel(this.tableList).then(() => {
        this.dialogFormVisible = false
        this.dialogFormSpecialVisible = false
        this.$notify({
          title: 'Success',
          message: 'Save Successfully',
          type: 'success',
          duration: 2000
        })
      })
    },
    handleDelete(index, row) {
      this.tableList.splice(index, 1)
    },
    /**
     * 回退
     */
    returnBack() {
      this.$emit('returnBack')
    },
    // 管理
    handleManagement(row) {
      this.tableList = []
      this.listDataQuery.fnCategoryId = this.fnCategoryId = row.fnCategoryId
      this.fnCategoryName = row.fnCategoryName
      this.fnCategoryId = row.fnCategoryId
      this.fnCategoryName = row.fnCategoryName
      fetchFnCategoryForSelect(this.listDataQuery).then(response => {
        this.data = response.model
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
        findForSelectedTable(this.listDataQuery).then(response => {
          this.tableList = response.model
          for (let i = 0; i < this.tableList.length; i++) {
            this.tableList[i].fnCategoryName = row.fnCategoryName
            // console.log(this.data)
          }
        })
        // Just to simulate the time of the request
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
      })
 
      this.dialogFormSpecialVisible = true
    },
    // 编辑
    handleUpdate(row) {
      this.temp = Object.assign({}, row) // copy obj
      this.dialogStatus = 'update'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 重置
    resetTemp() {
      this.temp = {
        id: undefined,
        // 类目中文名:
        fnCategoryName: '',
        // 是否上柜
        status: 1,
        // 类目排序
        sortOrder: 0,
        conditions: 'N',
        level: 3,
        special: 0,
        fnCategoryImage: '',
        parentId: this.pid
      }
      this.fnCategoryFileList = []
    },
    // 新增三级类目
    addDate() {
      this.resetTemp()
      this.dialogStatus = 'create'
      this.dialogFormVisible = true
      this.$nextTick(() => {
        this.$refs['dataForm'].clearValidate()
      })
    },
    // 更新保存方法
    updateData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          const tempData = Object.assign({}, this.temp)
          updateMallFnCategory(tempData).then(() => {
            const index = this.list.findIndex(v => v.id === this.temp.id)
            this.list.splice(index, 1, this.temp)
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Update Successfully',
              type: 'success',
              duration: 2000
            })
          })
        }
      })
    },
    // 创建保存方法
    createData() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          createMallFnCategory(this.temp).then(() => {
            this.list.unshift(this.temp)
            this.dialogFormVisible = false
            this.$notify({
              title: 'Success',
              message: 'Created Successfully',
              type: 'success',
              duration: 2000
            })
            this.getList()
          })
        }
      })
    },
    handleRemove(file, fileList) {
      console.log(file, fileList)
    },
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url
      this.dialogVisible = true
    },
    // 列表查询
    getList() {
      this.listLoading = true
      fetchFnCategoryList(this.listQuery).then(response => {
        this.list = response.model
        this.total = response.totalItem
 
        // Just to simulate the time of the request
        setTimeout(() => {
          this.listLoading = false
        }, 1.5 * 1000)
      })
    }
  }
}
</script>
 
<style scoped>
/* .mouseMark:hover {
  cursor: pointer;
} */
</style>
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿人工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档