فهرست منبع

feature(数据-授权):新增产品线授权、销售区域授权

wanglj 2 سال پیش
والد
کامیت
115aebe9f2

+ 14 - 0
src/api/base/productAuth.js

@@ -0,0 +1,14 @@
+import micro_request from '@/utils/micro_request'
+const basePath = process.env.VUE_APP_AdminPath
+export default {
+  // 用户列表
+  getList(query) {
+    return micro_request.postRequest(basePath, 'BaseProductAuth', 'GetProductLineUsers', query)
+  },
+  getDetail(query) {
+    return micro_request.postRequest(basePath, 'BaseProductAuth', 'GetList', query)
+  },
+  authorize(query) {
+    return micro_request.postRequest(basePath, 'BaseProductAuth', 'Save', query)
+  },
+}

+ 24 - 0
src/api/base/regionAuth.js

@@ -0,0 +1,24 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-02-14 11:17:19
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-14 14:44:34
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\api\base\regionAuth.js
+ */
+import micro_request from '@/utils/micro_request'
+const basePath = process.env.VUE_APP_AdminPath
+export default {
+  // 用户列表
+  getList(query) {
+    return micro_request.postRequest(basePath, 'BaseRegionAuth', 'GetRegionUsers', query)
+  },
+  // 授权
+  saveRegion(query) {
+    return micro_request.postRequest(basePath, 'BaseRegionAuth', 'Save', query)
+  },
+  // 区域/个人授权详情
+  getDetail(query) {
+    return micro_request.postRequest(basePath, 'BaseRegionAuth', 'GetList', query)
+  },
+}

+ 3 - 0
src/vab/components/VabQueryForm/index.vue

@@ -21,6 +21,9 @@
     margin: 0 0 0 0;
     > .el-button {
       margin: 0 10px $base-margin/2 0 !important;
+      &:last-child {
+        margin-right: 0 !important;
+      }
     }
   }
 

+ 21 - 4
src/vab/styles/default.scss

@@ -4049,7 +4049,7 @@ $--background-color-base: $base-color-background;
     align-items: center;
     font-size: 14px;
     padding-right: 8px;
-    span:first-child {
+    >span:first-child {
       flex: 1;
       overflow: hidden;
       text-overflow: ellipsis;
@@ -4084,11 +4084,28 @@ $--background-color-base: $base-color-background;
   display: flex;
   width: 100%;
   .tree-side {
-    flex: 0 0 210px;
-    margin-right: 10px;
+    // flex: 1 1 210px;
+    width: 210px;
+    border-right: 6px solid #f6f8f9;
+    display: flex;
+    flex-direction: column;
+    background: #fff; 
+    .el-tree {
+      flex:1;
+      overflow: auto;
+    }
+    footer {
+      flex:0 0 32px;
+      display: flex;
+      .el-button {
+        flex:1;
+      }
+    }
   }
   .tree-table {
-    width: calc(100% - 220px);
+    width: calc(100% - 216px);
+    padding:0 6px;
+    background: #fff;
   }
   .info-side {
     flex: 0 0 400px;

+ 224 - 0
src/views/base/productAuth/index.vue

@@ -0,0 +1,224 @@
+<!--
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-02-13 11:50:01
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-14 09:32:28
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\views\base\productAuth\index.vue
+-->
+<template>
+  <div class="product-auth-container">
+    <vab-query-form>
+      <vab-query-form-left-panel>
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input v-model="queryForm.keyWords" placeholder="姓名" />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="handleSearch">查询</el-button>
+            <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <table-tool :check-list.sync="checkList" :columns="columns" />
+      </vab-query-form-right-panel>
+    </vab-query-form>
+    <el-table v-loading="listLoading" border :data="list" :height="$baseTableHeight(1)">
+      <el-table-column
+        v-for="(item, index) in finallyColumns"
+        :key="index"
+        align="center"
+        :label="item.label"
+        :prop="item.prop"
+        show-overflow-tooltip
+        :sortable="item.sortable"
+        :width="item.width">
+        <template #default="{ row }">
+          <span>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="操作" width="120px">
+        <template #default="{ row }">
+          <el-button type="text" @click="handleAuth(row)">授权</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      background
+      :current-page="queryForm.pageNum"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :page-sizes="[5, 10]"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange" />
+    <!-- 授权弹窗 -->
+    <el-dialog title="产品线授权" :visible.sync="visible" width="500px" @close="handleClose">
+      <el-form ref="form" :model="form">
+        <el-form-item prop="productLine">
+          <el-checkbox-group v-model="form.productLine" @change="$forceUpdate()">
+            <el-checkbox v-for="item in productList" :key="item.productCode" :label="item.productCode">
+              {{ item.productName }}
+            </el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="visible = false">取 消</el-button>
+        <el-button type="primary" @click="save">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import proAuthApi from '@/api/base/productAuth'
+  import TableTool from '@/components/table/TableTool'
+  import to from 'await-to-js'
+  export default {
+    name: 'ProductAuthorize',
+    components: {
+      TableTool,
+    },
+    data() {
+      return {
+        total: 0,
+        queryForm: {
+          pageNum: 1,
+          pageSize: 5,
+          keyWords: '',
+          deptId: 2,
+        },
+        layout: 'total, sizes, prev, pager, next, jumper',
+        listLoading: false,
+        list: [],
+        checkList: [],
+        columns: [
+          {
+            label: '姓名',
+            width: '120px',
+            prop: 'userName',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '岗位',
+            width: '120px',
+            prop: 'userPost',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '电话',
+            width: '120px',
+            prop: 'userPhone',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '授权产品线',
+            width: 'auto',
+            prop: 'productName',
+            sortable: false,
+            disableCheck: true,
+          },
+        ],
+        visible: false,
+        productList: [],
+        form: {
+          userId: 0,
+          userName: '',
+          userPost: '',
+          userPhone: '',
+          productLine: [],
+        },
+      }
+    },
+    computed: {
+      finallyColumns() {
+        return this.columns.filter((item) => this.checkList.includes(item.label))
+      },
+    },
+    mounted() {
+      this.getProducts()
+      this.fetchData()
+    },
+    methods: {
+      async fetchData() {
+        const [err, res] = await to(proAuthApi.getList(this.queryForm))
+        if (err) return
+        this.list = res.data.list || []
+        this.total = res.data.total
+      },
+      handleSearch() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
+      getProducts() {
+        this.getDicts('sys_product_line').then((response) => {
+          let arr = response.data.values || []
+          for (const item of arr) {
+            this.productList.push({
+              productCode: item.key,
+              productName: item.value,
+              remark: item.remark,
+            })
+          }
+        })
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          keyWords: '',
+          deptId: 2,
+        }
+        this.fetchData()
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      // 授权
+      async handleAuth(row) {
+        this.form = { ...row }
+        this.form.productLine = row.productCode ? row.productCode.split(',') : []
+        this.visible = true
+      },
+      handleClose() {
+        this.form = {
+          userId: 0,
+          userName: '',
+          userPost: '',
+          userPhone: '',
+          productLine: [],
+        }
+        this.$refs.form.clearValidate()
+      },
+      // 确认授权
+      async save() {
+        let arr = []
+        for (const key of this.form.productLine) {
+          const obj = this.productList.find((item) => item.productCode == key)
+          if (obj) arr.push(obj)
+        }
+        const params = { ...this.form }
+        params.lines = arr
+        const [err, res] = await to(proAuthApi.authorize(params))
+        if (err) return
+        this.$message.success(res.msg)
+        this.visible = false
+        this.fetchData()
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  .el-checkbox {
+    width: 100%;
+  }
+</style>

+ 429 - 0
src/views/base/regionAuth/index.vue

@@ -0,0 +1,429 @@
+<!--
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-02-13 13:51:45
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-14 18:05:38
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\views\base\regionAuth\index.vue
+-->
+<template>
+  <div class="region-auth-container">
+    <vab-query-form>
+      <el-form>
+        <vab-query-form-left-panel>
+          <el-form-item>
+            <el-input v-model="queryForm.keyWords" placeholder="姓名" />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="handleSearch">查询</el-button>
+            <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+          </el-form-item>
+        </vab-query-form-left-panel>
+        <vab-query-form-right-panel>
+          <el-button icon="el-icon-circle-check" type="primary" @click="handleAuth">授权</el-button>
+          <table-tool :check-list.sync="checkList" :columns="columns" />
+        </vab-query-form-right-panel>
+      </el-form>
+    </vab-query-form>
+    <el-row :gutter="10">
+      <el-col :span="12">
+        <el-table
+          v-loading="listLoading"
+          border
+          :data="list"
+          :height="$baseTableHeight(1)"
+          style="width: 100%"
+          @row-click="handleRowClick">
+          <el-table-column
+            v-for="(item, index) in finallyColumns"
+            :key="index"
+            align="center"
+            :label="item.label"
+            :prop="item.prop"
+            show-overflow-tooltip
+            :sortable="item.sortable"
+            :width="item.width">
+            <template #default="{ row }">
+              <span>{{ row[item.prop] }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" label="选择" width="80">
+            <template #default="{ row }">
+              <i v-if="form.userId == row.userId" class="el-icon-check"></i>
+            </template>
+          </el-table-column>
+          <template #empty>
+            <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
+          </template>
+        </el-table>
+        <el-pagination
+          background
+          :current-page="queryForm.pageNum"
+          :layout="layout"
+          :page-size="queryForm.pageSize"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange" />
+      </el-col>
+      <el-col :span="12">
+        <el-table v-loading="listLoading" border :data="regionList" :height="$baseTableHeight(0)" style="width: 100%">
+          <el-table-column align="center" label="销售区域" prop="saleRegionName" />
+          <el-table-column align="center" label="省" prop="provinceName" />
+          <el-table-column align="center" label="市" prop="cityName" />
+        </el-table>
+      </el-col>
+    </el-row>
+
+    <el-dialog title="授权区域" top="5vh" :visible.sync="visible" width="80%" @close="handleClose">
+      <div v-loading="dialogLoading" class="side-layout">
+        <div class="tree-side">
+          <el-tree
+            ref="tree"
+            class="filter-tree"
+            :data="deptOptions"
+            default-expand-all
+            :default-expanded-keys="[1]"
+            :filter-node-method="filterNode"
+            highlight-current
+            node-key="id"
+            :props="defaultProps"
+            @node-click="handleNodeClick">
+            <span slot-scope="{ node }" class="custom-tree-node">
+              <span>{{ node.label }}</span>
+              <span><i class="el-icon-more"></i></span>
+            </span>
+          </el-tree>
+        </div>
+        <div class="tree-table">
+          <el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
+            全选
+          </el-checkbox>
+          <div v-for="item in options" :key="item.custProvinceId" class="province" :label="item.custProvince">
+            <h3>{{ item.custProvince }}</h3>
+            <div class="check-container">
+              <el-checkbox
+                v-model="item.checkAll"
+                :indeterminate="item.isIndeterminate"
+                @change="(val) => handleSndCheckAllChange(val, item)">
+                全选
+              </el-checkbox>
+              <el-checkbox-group v-model="form.checkList" @change="(val) => handleChange(val, item)">
+                <el-checkbox v-for="city in item.children" :key="city.id" :label="city.custCityId">
+                  {{ city.custCity }}
+                  <span v-if="city.allocator.length">({{ city.allocator.join() }})</span>
+                </el-checkbox>
+              </el-checkbox-group>
+            </div>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <el-button @click="visible = false">取 消</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import regionApi from '@/api/base/region'
+  import regAuthApi from '@/api/base/regionAuth'
+  import TableTool from '@/components/table/TableTool'
+  export default {
+    name: 'RegionAuth',
+    components: {
+      TableTool,
+    },
+    data() {
+      return {
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          keyWords: '',
+          deptId: 2,
+        },
+        layout: 'total, sizes, prev, pager, next, jumper',
+        listLoading: false,
+        total: 0,
+        list: [],
+        regionList: [],
+        checkList: [],
+        columns: [
+          {
+            label: '姓名',
+            width: '120px',
+            prop: 'userName',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '岗位',
+            width: 'auto',
+            prop: 'userPost',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '电话',
+            width: 'auto',
+            prop: 'userPhone',
+            sortable: false,
+            disableCheck: false,
+          },
+        ],
+        checkAll: false,
+        isIndeterminate: false,
+        form: {
+          userId: 0,
+          checkList: [],
+        },
+        regionId: 0,
+        saleRegionName: '',
+        visible: false,
+        deptOptions: [],
+        defaultProps: {
+          id: 'id',
+          regionCode: 'regionCode',
+          label: 'regionDesc',
+        },
+        options: [], //区域选项
+        flatOptions: [], //扁平化区域
+        dialogLoading: false,
+        allocated: [], //已被分配的城市
+      }
+    },
+    computed: {
+      finallyColumns() {
+        return this.columns.filter((item) => this.checkList.includes(item.label))
+      },
+    },
+    mounted() {
+      this.fetchData()
+    },
+    methods: {
+      async fetchData() {
+        const [err, res] = await to(regAuthApi.getList(this.queryForm))
+        if (err) return
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.form = {
+          userId: 0,
+          checkList: [],
+        }
+        this.regionList = []
+      },
+      handleRowClick(row) {
+        this.form.userId = row.userId
+        this.form.userName = row.userName
+        this.form.userPost = row.userPost
+        this.form.userPhone = row.userPhone
+        this.getSndList()
+      },
+      async getSndList() {
+        const [err, res] = await to(
+          regAuthApi.getDetail({
+            userId: this.form.userId,
+          })
+        )
+        if (err) return
+        this.regionList = res.data.list || []
+      },
+      handleSearch() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          keyWords: '',
+          deptId: 2,
+        }
+        this.fetchData()
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      async handleAuth() {
+        if (!this.form.userId) return this.$message.warning('请选择用户')
+        this.visible = true
+        this.getRegionList()
+      },
+      async getRegionList() {
+        const { data: data } = await regionApi.getRegionList()
+        this.deptOptions = data.list
+        this.regionId = data.list[0].id
+        this.saleRegionName = data.list[0].regionDesc
+        //默认第一选中
+        this.$nextTick(() => {
+          this.$refs.tree.setCurrentKey(this.regionId)
+          this.handleNodeClick({ id: this.regionId, regionDesc: this.saleRegionName })
+        })
+      },
+      async handleDetail() {
+        this.form.checkList = []
+        this.allocated = []
+        const [err, res] = await to(
+          regAuthApi.getDetail({
+            // userId: this.form.userId,
+            saleRegionId: this.regionId,
+          })
+        )
+        if (err) return
+        const list = res.data.list || []
+        for (const item of list) {
+          if (item.userId == this.form.userId) this.form.checkList.push(item.cityId)
+          else this.allocated.push(item)
+        }
+      },
+      async handleNodeClick(data) {
+        this.checkAll = false
+        this.isIndeterminate = false
+        this.regionId = data.id
+        this.saleRegionName = data.regionDesc
+        await this.handleDetail()
+        const [err, res] = await to(regionApi.getRegionDetailList({ regionId: data.id }))
+        if (err) return
+        this.flatOptions = res.data.list || []
+        this.flatOptions.forEach((item) => (item.saleRegionName = data.regionDesc))
+        let obj = []
+        for (const item of this.flatOptions) {
+          const target = obj.findIndex((each) => each.custProvinceId == item.custProvinceId)
+          const newItem = { ...item, allocator: [] }
+          const allocator = this.allocated.find((each) => each.cityId == item.custCityId)
+          if (allocator) {
+            newItem.allocator.push(allocator.userName)
+          }
+          if (target > -1) {
+            obj[target].children.push(newItem)
+          } else {
+            obj.push({
+              checkAll: false,
+              isIndeterminate: false,
+              custProvinceId: item.custProvinceId,
+              custProvince: item.custProvince,
+              children: [newItem],
+            })
+          }
+        }
+        this.options = obj
+        this.handleChange(this.form.checkList)
+      },
+      // 筛选节点
+      filterNode(value, data) {
+        if (!value) return true
+        return data[this.defaultProps.label].indexOf(value) !== -1
+      },
+      handleClose() {
+        // this.form = {
+        //   userId: 0,
+        //   checkList: [],
+        // }
+        this.getSndList()
+      },
+      handleCheckAllChange(val) {
+        this.form.checkList = val ? this.flatOptions.map((item) => item.custCityId) : []
+        this.options.forEach((item) => {
+          item.checkAll = val
+        })
+        this.isIndeterminate = false
+        this.$forceUpdate()
+        this.save()
+      },
+      handleSndCheckAllChange(val, item) {
+        item.isIndeterminate = false
+        for (let i = 0; i < item.children.length; i++) {
+          const index = this.form.checkList.findIndex((each) => each == item.children[i].custCityId)
+          if (index > -1) {
+            this.form.checkList.splice(index, 1)
+            i--
+          }
+        }
+        const arr = item.children.map((each) => each.custCityId)
+        if (val) this.form.checkList = [...this.form.checkList, ...arr]
+        this.$forceUpdate()
+        this.save()
+      },
+      handleChange(val, item) {
+        if (item) {
+          let count = 0
+          for (const child of item.children) {
+            console.log(child, child.custCityId, this.form.checkList, '---')
+            if (this.form.checkList.includes(child.custCityId)) count++
+          }
+          item.checkAll = count == item.children.length
+          item.isIndeterminate = count > 0 && count < item.children.length
+          this.save()
+        } else {
+          for (const pro of this.options) {
+            let count = 0
+            pro.children.forEach((city) => {
+              if (this.form.checkList.includes(city.custCityId)) count++
+            })
+            pro.checkAll = count == pro.children.length
+            pro.isIndeterminate = count > 0 && count < pro.children.length
+          }
+        }
+        this.$forceUpdate()
+        this.isIndeterminate = val.length > 0 && val.length < this.flatOptions.length
+        this.checkAll = val.length == this.flatOptions.length && val.length > 0
+      },
+      async save() {
+        this.dialogLoading = true
+        let params = { ...this.form }
+        params.saleRegionId = this.regionId
+        params.saleRegionName = this.saleRegionName
+        let arr = []
+        for (const item of params.checkList) {
+          const obj = this.flatOptions.find((each) => each.id == item)
+          if (obj)
+            arr.push({
+              saleRegionId: obj.regionId,
+              saleRegionName: obj.saleRegionName,
+              provinceId: obj.custProvinceId,
+              provinceName: obj.custProvince,
+              cityId: obj.custCityId,
+              cityName: obj.custCity,
+            })
+        }
+        params.regions = arr
+        const [err, res] = await to(regAuthApi.saveRegion(params))
+        if (err) return (this.dialogLoading = false)
+        this.$message.success(res.msg)
+        this.dialogLoading = false
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .side-layout {
+    height: 600px;
+  }
+  .el-icon-check {
+    font-size: 18px;
+    color: #67c23a;
+    font-weight: bold;
+  }
+  .vab-theme-blue-black .side-layout .tree-table {
+    padding: 10px 20px;
+    .province {
+      margin: 20px 0;
+      line-height: 24px;
+      h3 {
+        padding: 10px;
+        border-radius: 5px;
+        background: #eee;
+      }
+      .check-container {
+        padding-left: 8px;
+        line-height: 30px;
+      }
+    }
+  }
+</style>