后台管理项目

vue-admin-template-master

build

        -----index.js webpack配置文件【很少修改这个文件】

mock

        -------mock数据的文件夹【模拟一些假的数据mockjs实现的】,因为咱们实际开发的时候,利用的真是接口

node_modules

        ------项目依赖的模块

public

        -------ico图标,静态页面,public文件夹里面经常放置一些静态资源,而且在项目打包的时候webpack不会编译这个文件夹

src

        -------程序员代码的地方

        -------api文件夹:涉及请求相关的

        --------assets文件夹:里面放置一些静态资源(一般共享的),放在assets文件夹里面静态资源,在webpack打包的时候,会进行编译

        ---------components文件夹:一般放置非路由组件获取全局组件

        ---------icons:这个文件夹放置一些svg矢量图

        ---------layout文件夹,它里面放置一些组件与混入

        ---------router文件夹:与路由相关的

        ---------store文件夹:一定是与vuex相关的

        ---------style文件夹:与样式相关的

        ---------utils文件夹:request.js是axios二次封装文件

        ---------view文件夹:里面放置的是路由组件

App.vue 根组件

main.js 入口文件

permission.js  与导航守卫相关

settings:项目配置项文件

完成登录业务

        1.静态组件完成

        2.书写API(换成真实的接口)

        3.axios的二次封装

        4.换成真实接口之后需要解决代理跨域问题(这一步在vue.config.js里面操作)

                4.1.首先在vue.config.js删掉请求mock的代码

                4.2.在这里写入配置代理跨域

                        4.2.1在webpack中找到devserve中的proxy,找到以下代码

proxy: {
      '/api': {
        target: 'https://other-server.example.com',
        secure: false,
      },
    },

根据vue-admin-template-master的.env.development可知base_api为'/dev-api'

退出登录业务

登陆之后所有的页面由layout构成,layout有三个主体区域Siderbar,Navbar,AppMain,

退出登录在navbar中,修改里面的文字内容

修改侧边栏

侧边栏的内容写在view中,根据业务需求存放数据,文字内容由meta元数据中的title修改,其中AppMain中的css在Style中的index.css中修改,那里面要把app-container改为app-main才能让padding有效果

修改trademark

trademark 由三部分 按钮 表格 分页器 构成

分页器的layout可以通过修改里面的顺序来改位置

layout=" prev, pager, next, jumper,->, sizes, total"

这里的->是靠右

trademark请求

请求列表数据

url: ’/admin/product/baseTrademark/{page}/{limit}‘    method:'get'   请求携带page和limit

通过拼接字符串

// 获取品牌列表
export function reqTrademarkPage(page, limit) {
  return request({
    url: `/admin/product/baseTrademark/${page}/${limit}`,
    method: 'get',
  })
}
  async getPageList() {
        const {page, limit} = this
        let res = await this.$api.trademark.reqTrademarkPage(page, limit)
        if (res.code == 200) {
          this.total = res.data.total
          this.records = res.data.records
        }
      }

这里不能使用params拼接,因为后台接口里面并没有携带占位符,如果使用了params就会使路径变成xxxx/xxx?xxx

没有设置需要传递params,所以对params参数没有进行处理,当我们传递params的时候,路径里会含有?id=,所以请求不到数据

所以在使用axios发起get请求的时候,需要根据后台接口设置来判断是否需要传递params

之后通过构建出一个表格

表格中logoUrl和操作的按钮需要通过template获取当前行的数据,也就是需要通过作用域插槽     (v-slot或者slot-scope)

当使用v-slot=“scpoe”的时候,获取里面的数据需要通过scope.row.属性

当使用slot-scope="(row,$index)"的时候,获取里面的数据需要通过row.属性

修改和添加按钮

 修改按钮有两个功能          一、添加    二、修改

修改        url:/admin/acl/user/update          method:put          请求携带三个参数 id,名称,logo

添加        url: '/admin/product/baseTrademark/save'        method: 'post'        请求携带两个参数   名称,logo

当修改的时候需要获取id,当添加的时候不用获取id,因为添加之后id是从服务器中获取的

因此可以把修改和添加的请求合在一个,用if判断请求的是修改还是添加

// 修改品牌
export function reqTrademarkEdit(tradeMark) {
  // 带给服务器数据携带ID      添加和修改集于一体
  if (tradeMark.id) {
    // 如果有id的话,修改
    return request({
      // 修改品牌列表     携带三个参数 id,名称,logo
      url: '/admin/product/baseTrademark/update',
      method: 'put',
      data: tradeMark
    })
  } else {
    return request({
      // 添加品牌     携带两个参数   名称,logo
      url: '/admin/product/baseTrademark/save',
      method: 'post',
      data: tradeMark
    })
  }
}

弄一个对话框,当用户点击添加或者修改按钮之后可以调用这个请求接口

<!-- 按钮 -->
    <el-button type="primary" icon="el-icon-plus" style="margin-bottom: 20px;" @click="tradeAdd">添加</el-button>
<el-table-column prop="date" label="操作">
        <template slot-scope="{row,$index}">
          <el-button type="primary" icon="el-icon-edit" @click="tradeEdit(row)">修改</el-button>
          <el-button type="danger" icon="el-icon-delete" @click="tradeDelete(row)">删除</el-button>
        </template>
      </el-table-column>
<!-- 对话框 -->
    <el-dialog title="添加品牌" :visible.sync="editVisible" width="30%">
      <el-form :model="editForm" :rules="editRules" ref="editForm">
        <el-form-item label="品牌名称" label-width="120px" prop="tmName">
          <el-input v-model="editForm.tmName" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="品牌LOGO" label-width="120px">
          <el-upload class="avatar-uploader" action="/dev-api/admin/product/fileUpload"
            :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload">
            <img v-if="editForm.imageUrl" :src="editForm.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>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editVisible = false">取 消</el-button>
        <el-button type="primary" @click="AddOrUppdateClick">确 定</el-button>
      </span>
    </el-dialog>

el-upload上传

action需要修改自己提交的地址

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;
      }
}

这上面的method是饿了么原生的方法

我们需要把上传成功之后的res的data传给data中存放的地方,再把el-load中的v-if和:src绑定给editForm-image

在表单验证的基础上调用接口

当点击添加的按钮时,我们需要传入商品名称和logo,这需要两个参数,而这两个参数属于editForm,当我们写入这两个数据之后,v-model已经双向绑定在了数据editForm中,调用接口时,只需要传入这个editForm

// 点击添加按钮
      tradeAdd() {
        this.editVisible = true
        this.editForm.tmName = ''
        this.editForm.logoUrl = ''
      },

当点击修改的按钮时,我们需要知道当前的id,通过slot-scope得到当前行的数据,在点击之后传到editForm,但是这个需要深拷贝,如果是双向绑定的话,当我们在修改表单数据时,表格的数据也会一起发生变化,我们需要当用户点击完确定再响应页面

 // 点击修改按钮
      tradeEdit(row) {
        this.editVisible = true
        this.editForm = {...row}
      },

然后如果成功的话通过是否有id来判断是修改还是品牌

 // 点击添加按钮中确定
      AddOrUppdateClick() {
        this.$refs.editForm.validate(async valid => {
          if (!valid) {
            return false
          }
          this.editVisible = false
          let res = await this.$api.trademark.reqTrademarkEdit(this.editForm)
          console.log(res)
          if (res.code == 200) {
            this.$message.success(this.editForm.id ? '修改品牌成功' : '添加品牌成功')
            this.getPageList()
          } else {
            this.$message.error('失败')
          }
        })
      },

删除按钮

这个就直接贴代码了

// 删除品牌
export function reqTradeDelete(id){
  return request({
    url:`/admin/product/baseTrademark/remove/${id}`,
    method:'delete'
  })
}
<el-table-column prop="date" label="操作">
        <template slot-scope="{row,$index}">
          <el-button type="primary" icon="el-icon-edit" @click="tradeEdit(row)">修改</el-button>
          <el-button type="danger" icon="el-icon-delete" @click="tradeDelete(row)">删除</el-button>
        </template>
      </el-table-column>
// 删除按钮
      async tradeDelete(row) {
        const confirmResult = await this.$confirm(`此操作将永久删除${row.tmName}, 是否继续?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).catch(err => {
          return err
        })
        if (confirmResult !== 'confirm') {
          return this.$message.info('已取消删除')

        }
        let res = await this.$api.trademark.reqTradeDelete(row.id)
        console.log(res)
        if(res.code !== 200){
          return false
        }
        this.$message.success('已删除成功')
        this.getPageList(this.records.length>1?this.page:this.page-1)
      }

attr请求

attr页面有可以共用的地方,于是我们创建一个全局组件

在components下新建一个CategorySelect文件,里面存放行内表单

这个组件全局使用,所以在main.js导入

import categoryselect from 'xxxx'

Vue.component(categoryselect.name,categoryselect)

attr里面有两个模块,一个是三级联动,一个是卡片

三级联动封装成CategorySelect

CategorySelect由一个行内表单构成,里面有一级分类、二级分类、三级分类,

1.先单向绑定表单数据cForm

2.每个el-select里面分别双向绑定一级分类id、二级分类id、三级分类id

3.挂载方法,直接调用此方法,获取一级分类数据,把数据存放在一个data数据中,然后在el-option中遍历数据得到选项

4.当选择一级分类数据之后,把选择的一级分类id传给二级分类,以此类推

5.当用户选择过选项,又想修改时,需要清空后面的选项,把data数据清空

6.CategorySelect这个组件会在attr里面用到,所以还需要子传父

<div>
    <el-form :inline="true" class="demo-form-inline" :model="cForm">
      <el-form-item label="一级分类" >
        <el-select placeholder="请选择" v-model="cForm.category1Id" @change="changeCategory1">
          <el-option v-for="(c1,index) in list1" :key="c1.id" :label="c1.name" :value="c1.id" ></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="二级分类">
        <el-select placeholder="请选择" v-model="cForm.category2Id" @change="changeCategory2">
          <el-option v-for="(c2,index) in list2" :key="c2.id" :label="c2.name" :value="c2.id"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="三级分类">
        <el-select placeholder="请选择" v-model="cForm.category3Id" @change="changeCategory3">
          <el-option v-for="(c3,index) in list3" :key="c3.id" :label="c3.name" :value="c3.id"></el-option>
        </el-select>
      </el-form-item>
    </el-form>
  </div>
 data () {
    return {
      // 一级分类的数据
      list1:[],
      // 二级分类的数据
      list2:[],
      // 三级分类的数据
      list3:[],
      // 收集相应的一级二级三级分类的id
      cForm:{
        category1Id:'',
        category2Id:'',
        category3Id:'',
      }
    }
  },
  mounted() {
    // 获取一级分类的数据方法
    this.getCategory1()
  },
  methods:{
    // 一级分类
    async getCategory1(){
      let res = await this.$api.attr.reqGetCategory1()
      if(res.code == 200){
        this.list1 = res.data
      }
    },
    // 改变了一级分类
    async changeCategory1(){
      // 清除数据
      this.list2 = []
      this.list3 = []
      this.cForm.category2Id = ''
      this.cForm.category3Id = ''
      let res = await this.$api.attr.reqGetCategory2(this.cForm.category1Id)
      if(res.code == 200){
        this.list2 = res.data
      }
    },
    // 改变了二级分类
    async changeCategory2(){
      // 清除数据
      this.list3 = []
      this.cForm.category3Id = ''
      let res = await this.$api.attr.reqGetCategory3(this.cForm.category2Id)
      if(res.code == 200){
        this.list3 = res.data
      }
    },
    // 三级分类有内容时
    changeCategory3(){
      if(this.cForm.category3Id){
        this.$emit('getCategoryAll',this.cForm)
      }
    }
  }
}

 attr里面的卡片

先引用categoryselect,用事件接收三级联动的数据,并且存储在attr的data里面,用这个三级id的数据获取平台属性的数据

先写出静态,一个el-button,一个el-table。

el-button当点击之后,会切换到另外一个当前界面

el-table中的属性值列表和操作需要使用template,当选择修改的按钮时,也会切换到另外一个当前界面

另外一个界面:

先写出静态,用el-form写属性名

再用两个el-button写按钮

最后是一个表格

当没有输入属性名时,添加属性值的按钮要:disabled

el-table中数据需要在prop中写属性名,并且在el-table中单向绑:data="baseAttrInfo.attrValueList"需要在template中写el-input,而且这个数据要与row.valueName双向绑定,因此在改变input的时候,另外一个界面的数据也会发生改变,这是因为发生了浅拷贝,我们需要深拷贝来修改当前的数据,因为此时的数据格式为json,则不能使用{...row},{...row}只能用在对象中,json的格式并不适用,因为对象里面还包裹着数组,需要使用

// 深拷贝
        this.baseAttrInfo = JSON.parse(JSON.stringify(row))

在点击修改的时候,在baseAttrInfo.attrValueList用$set新增一个属性

this.baseAttrInfo.attrValueList.forEach(item =>{
          this.$set(item,'flag',false)
        })

 接下来我们需要在完成input和p的交换显示

点击p的时候,会显示input,input失焦的时候会变成p

因此在data中用flag控制显示与隐藏

input失焦变成p

当input输入的值不为空或者不能都是空格,而且不能与之前的属性值相同

我们需要判断

// 失去焦点的事件-----切换为查看模式,展示span
      toLook(row) {
        // 如果属性值为空不能作为新的属性值,需要用户提示,让他输入一个其他的属性值
        if (row.valueName.trim() == '') {
          this.$message.error('请输入属性值')
          return false
        }
        // 新增的属性值不能与之前的属性值相同
        let isReap = this.baseAttrInfo.attrValueList.some(item => {
          // console.log(item)
          // 需要将row从数组里面判断的时候去除
          // row最新新增的属性值【数组的最后一项元素】
          // 判断的时候,需要把已有的数组当中新增的这个属性值去掉
          if (row !== item){
            return row.valueName == item.valueName
          }
        })
        if(isReap == true){
          this.$message.error('属性值不能相同')
          return false
        }
        row.flag = false
      },

点击p的时候,会显示input,并且会自动聚焦到当前的input

我们需要获取input节点,实现自动聚焦,因为当切换dom的时候,对于浏览器来说,页面的结构发生了改变,页面需要重构

使用$nextTick,所指定的回调函数,会在DOM节点更新完毕之后再执行

<el-table style="width: 100%;margin-bottom: 20px;" border :data="baseAttrInfo.attrValueList">
          <el-table-column type="index" label="序号" width="90px">
          </el-table-column>
          <el-table-column prop="valueName" label="属性值名称">
            <template slot-scope="{row,$index}">
              <el-input v-model="row.valueName" placeholder="请输入属性值名称" v-if="row.flag" @blur="toLook(row)"
                @keyup.native.enter="toLook(row)" :ref="$index"></el-input>
              <p class="clickspan" v-else @click="toForce(row,$index)">{{row.valueName}}</p>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="540px">
            <template slot-scope="{row,$index}">
              <el-button type="danger" icon="el-icon-delete" style="margin-bottom: 0;"
                @click="deleteAttr(scope.row.attrId)"></el-button>
            </template>
          </el-table-column>
        </el-table>
// 点击span,显示input
      toForce(row,index){
        row.flag = true
        this.$nextTick(()=>{
         // 获取input节点,实现自动聚焦
         // 点击span的时候,切换为input,这对于浏览器来说,页面的结构变了,页面重绘与重排是需要耗时间
          this.$refs[index].focus()
        })
      }

删除属性值

这里选用饿了么的Popconfirm气泡确认框,当选择删除的时候弹出气泡确认框选择是否删除

注意选用气泡确认框,el-button里面有个slot="reference"一定要写在里面

然后像之前一样发请求,然后重新调用数据就行了

<el-popconfirm :title="`你确定要删除${row.attrName}?`" @onConfirm="deleteAttr(row.id)">
                <el-button slot="reference" type="danger" icon="el-icon-delete" style="margin-bottom: 0;"></el-button>
              </el-popconfirm>
// 删除
      async deleteAttr(id) {
        let res = await this.$api.attr.reqDeleteAttr(id)
        console.log(res)
        // console.log(index)
        this.getAttrList()
      },

保存

点击了保存,用户填写的数据就会保存到数据库中

这里要把之前加入的属性flag删除

当属性名不为空,且attrValueList过滤出flag的属性并且删除,之后调用接口

async getSaveAttrInfo() {
        this.baseAttrInfo.attrValueList = this.baseAttrInfo.attrValueList.filter(item => {
          if (item.valueName.trim() !== '') {
            delete item.flag
            return item
          }
        })
        try {
          this.$message.success('添加成功')
          let res = await this.$api.attr.reqSaveAttrInfo(this.baseAttrInfo)
          this.getAttrList()
          this.isShowTable = true
        } catch (e) {
          //TODO handle the exception
          alert(e.message)
        }
      },

切换到界面二的时候,三级联动不能被选择

当切换到界面二,让上面的卡片:disabled="true",因为之前我们有控制切换界面一还是界面二的属性,我们用这个属性来控制卡片是否隐藏,界面一isShowTable=true,界面二isShowTable=false,并且这需要父传子

<el-card>
      <categoryselect @getCategoryAll="getCategoryAll" :show="!isShowTable"></categoryselect>
    </el-card>

 categoryselect:

props:['show'],

Spu管理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值