Browse Source

Merge branch 'test' into 'master'

Test

See merge request quanshu/mp-ui-pc!71
zhong chunping 3 years ago
parent
commit
4580030c39
39 changed files with 6221 additions and 17084 deletions
  1. 1 17064
      package-lock.json
  2. 1 0
      package.json
  3. 1 0
      public/index.html
  4. 202 0
      public/jump-mp.html
  5. 180 0
      public/jump.html
  6. 8 0
      public/tinymce.min.js
  7. 13 0
      src/api/admin/area.js
  8. 71 0
      src/api/admin/channel.js
  9. 52 0
      src/api/admin/salesite.js
  10. 44 0
      src/api/business/coupon.js
  11. 44 0
      src/api/business/goods.js
  12. 93 0
      src/api/business/ticket.js
  13. 8 8
      src/api/common.js
  14. 1 1
      src/api/system/role.js
  15. 5 1
      src/assets/styles/index.scss
  16. 32 2
      src/components/ImageUpload/index.vue
  17. 72 0
      src/components/TinyEditor.vue
  18. 2 1
      src/main.js
  19. 1 1
      src/permission.js
  20. 20 1
      src/router/index.js
  21. 1 1
      src/utils/util.js
  22. 469 0
      src/views/business/channel/components/Create.vue
  23. 252 0
      src/views/business/channel/components/Detail.vue
  24. 426 0
      src/views/business/channel/index.vue
  25. 213 0
      src/views/business/coupon/add.vue
  26. 125 0
      src/views/business/coupon/components/selectTicket.vue
  27. 150 0
      src/views/business/coupon/index.vue
  28. 232 0
      src/views/business/goods/add.vue
  29. 335 0
      src/views/business/goods/components/spec.vue
  30. 180 0
      src/views/business/goods/index.vue
  31. 554 0
      src/views/business/salesite/components/Create.vue
  32. 279 0
      src/views/business/salesite/components/Detail.vue
  33. 429 0
      src/views/business/salesite/index.vue
  34. 923 0
      src/views/business/ticket/create.vue
  35. 0 0
      src/views/business/ticket/detail.vue
  36. 262 0
      src/views/business/ticket/import.vue
  37. 458 0
      src/views/business/ticket/index.vue
  38. 78 0
      src/views/skip.vue
  39. 4 4
      src/views/system/role/index.vue

File diff suppressed because it is too large
+ 1 - 17064
package-lock.json


+ 1 - 0
package.json

@@ -44,6 +44,7 @@
     "@fullcalendar/timeline": "^4.3.0",
     "@fullcalendar/timeline": "^4.3.0",
     "@fullcalendar/vue": "^4.3.1",
     "@fullcalendar/vue": "^4.3.1",
     "@riophae/vue-treeselect": "0.4.0",
     "@riophae/vue-treeselect": "0.4.0",
+    "@tinymce/tinymce-vue": "^3.2.8",
     "axios": "^0.21.1",
     "axios": "^0.21.1",
     "clipboard": "2.0.6",
     "clipboard": "2.0.6",
     "core-js": "3.8.1",
     "core-js": "3.8.1",

+ 1 - 0
public/index.html

@@ -6,6 +6,7 @@
     <meta name="renderer" content="webkit">
     <meta name="renderer" content="webkit">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <script src="<%= BASE_URL %>tinymce.min.js"></script>
     <title><%= webpackConfig.name %></title>
     <title><%= webpackConfig.name %></title>
     <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
     <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
 	  <style>
 	  <style>

+ 202 - 0
public/jump-mp.html

@@ -0,0 +1,202 @@
+<html>
+  <head>
+    <title>打开小程序</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
+    <script>
+      window.onerror = e => {
+        console.error(e)
+        alert('发生错误' + e)
+      }
+    </script>
+    <!-- weui 样式 -->
+    <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.4.1/weui.min.css"></link>
+    <!-- 调试用的移动端 console -->
+    <!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script> -->
+    <!-- <script>eruda.init();</script> -->
+    <!-- 公众号 JSSDK -->
+    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+    <!-- 云开发 Web SDK -->
+    <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script>
+    <script>
+      function docReady(fn) {
+        if (document.readyState === 'complete' || document.readyState === 'interactive') {
+          fn()
+        } else {
+          document.addEventListener('DOMContentLoaded', fn);
+        }
+      }
+
+      docReady(async function() {
+        var ua = navigator.userAgent.toLowerCase()
+        var isWXWork = ua.match(/wxwork/i) == 'wxwork'
+        var isWeixin = !isWXWork && ua.match(/micromessenger/i) == 'micromessenger'
+        var isMobile = false
+        var isDesktop = false
+        if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) {
+          isMobile = true
+        } else {
+          isDesktop = true
+        }
+
+        if (isWeixin) {
+          var containerEl = document.getElementById('wechat-web-container')
+          containerEl.classList.remove('hidden')
+          containerEl.classList.add('full', 'wechat-web-container')
+
+          var launchBtn = document.getElementById('launch-btn')
+          launchBtn.addEventListener('ready', function (e) {
+            console.log('开放标签 ready')
+          })
+          launchBtn.addEventListener('launch', function (e) {
+            console.log('开放标签 success')
+          })
+          launchBtn.addEventListener('error', function (e) {
+            console.log('开放标签 fail', e.detail)
+          })
+
+          wx.config({
+            // debug: true, // 调试时可开启
+            appId: '小程序 AppID', // <!-- replace -->
+            timestamp: 0, // 必填,填任意数字即可
+            nonceStr: 'nonceStr', // 必填,填任意非空字符串即可
+            signature: 'signature', // 必填,填任意非空字符串即可
+            jsApiList: ['chooseImage'], // 必填,随意一个接口即可 
+            openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名
+          })
+        } else if (isDesktop) {
+          // 在 pc 上则给提示引导到手机端打开
+          var containerEl = document.getElementById('desktop-web-container')
+          containerEl.classList.remove('hidden')
+          containerEl.classList.add('full', 'desktop-web-container')
+        }  else {
+          var containerEl = document.getElementById('public-web-container')
+          containerEl.classList.remove('hidden')
+          containerEl.classList.add('full', 'public-web-container')
+          var c = new cloud.Cloud({
+            // 必填,表示是未登录模式
+            identityless: true,
+            // 资源方 AppID
+            resourceAppid: '小程序 AppID', // <!-- replace -->
+            // 资源方环境 ID
+            resourceEnv: '云开发环境 ID', // <!-- replace -->
+          })
+          await c.init()
+          window.c = c
+
+          var buttonEl = document.getElementById('public-web-jump-button')
+          var buttonLoadingEl = document.getElementById('public-web-jump-button-loading')
+          try {
+            await openWeapp(() => {
+              buttonEl.classList.remove('weui-btn_loading')
+              buttonLoadingEl.classList.add('hidden')
+            })
+          } catch (e) {
+            buttonEl.classList.remove('weui-btn_loading')
+            buttonLoadingEl.classList.add('hidden')
+            throw e
+          }
+        }
+      })
+
+      async function openWeapp(onBeforeJump) {
+        var c = window.c
+        const res = await c.callFunction({
+          name: 'public',
+          data: {
+            action: 'getUrlScheme',
+          },
+        })
+        console.warn(res)
+        if (onBeforeJump) {
+          onBeforeJump()
+        }
+        location.href = res.result.openlink
+      }
+    </script>
+    <style>
+      .hidden {
+        display: none;
+      }
+
+      .full {
+        position: absolute;
+        top: 0;
+        bottom: 0;
+        left: 0;
+        right: 0;
+      }
+
+      .public-web-container {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      .public-web-container p {
+        position: absolute;
+        top: 40%;
+      }
+
+      .public-web-container a {
+        position: absolute;
+        bottom: 40%;
+      }
+
+      .wechat-web-container {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      .wechat-web-container p {
+        position: absolute;
+        top: 40%;
+      }
+
+      .wechat-web-container wx-open-launch-weapp {
+        position: absolute;
+        bottom: 40%;
+        left: 0;
+        right: 0;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      .desktop-web-container {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+      }
+
+      .desktop-web-container p {
+        position: absolute;
+        top: 40%;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="page full">
+      <div id="public-web-container" class="hidden">
+        <p class="">正在打开 “填入你的小程序名称”...</p> <!-- replace -->
+        <a id="public-web-jump-button" href="javascript:" class="weui-btn weui-btn_primary weui-btn_loading" onclick="openWeapp()">
+          <span id="public-web-jump-button-loading" class="weui-primary-loading weui-primary-loading_transparent"><i class="weui-primary-loading__dot"></i></span>
+          打开小程序
+        </a>
+      </div>
+      <div id="wechat-web-container" class="hidden">
+        <p class="">点击以下按钮打开 “填入你的小程序名称”</p> <!-- replace -->
+        <!-- 跳转小程序的开放标签。文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html -->
+        <wx-open-launch-weapp id="launch-btn" username="小程序原始账号 ID(gh_ 开头的)" path="要跳转到的页面路径"> <!-- replace -->
+          <template>
+            <button style="width: 200px; height: 45px; text-align: center; font-size: 17px; display: block; margin: 0 auto; padding: 8px 24px; border: none; border-radius: 4px; background-color: #07c160; color:#fff;">打开小程序</button>
+          </template>
+        </wx-open-launch-weapp>
+      </div>
+      <div id="desktop-web-container" class="hidden">
+        <p class="">请在手机打开网页链接</p>
+      </div>
+    </div>
+  </body>
+</html>

+ 180 - 0
public/jump.html

@@ -0,0 +1,180 @@
+<html>
+
+<head>
+  <title>打开小程序</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
+
+  <script>
+    function docReady(fn) {
+      if (document.readyState === 'complete' || document.readyState === 'interactive') {
+        fn()
+      } else {
+        document.addEventListener('DOMContentLoaded', fn);
+      }
+    }
+
+    docReady(async function () {
+      var ua = navigator.userAgent.toLowerCase()
+      var isWXWork = ua.match(/wxwork/i) == 'wxwork'
+      var isWeixin = !isWXWork && ua.match(/micromessenger/i) == 'micromessenger'
+      var isMobile = false
+      var isDesktop = false
+      if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|IEMobile)/i)) {
+        isMobile = true
+      } else {
+        isDesktop = true
+      }
+
+      var container = document.getElementById('container')
+      var wx = document.getElementById('container-wx')
+      var tip = document.getElementById('container-tip')
+
+      if (isWeixin) {
+        container.innerHTML = '打开微信小程序'
+        tip.innerHTML = '点击以下按钮打开 “盲票小程序”'
+        wx.style.display = "none";
+      } else {
+        container.style.display = "none";
+        wx.style.display = "inline";
+        wx.innerHTML = '打开微信'
+        tip.innerHTML = '请使用微信扫描当前二维码'
+      }
+    })
+
+
+    //获取当前URL
+    var url = document.location.href;
+    //声明一个对象
+    var getRequest = new Object();
+    //获取?的位置
+    var index = url.indexOf("?")
+
+    if (index != -1) {
+      //截取出?后面的字符串
+      var str = url.substr(index + 1);
+      //将截取出来的字符串按照&变成数组
+      strs = str.split("&");
+      //将get传参存入对象中
+      for (var i = 0; i < strs.length; i++) {
+        getRequest[strs[i].split("=")[0]] = (strs[i].split("=")[1]);
+      }
+    }
+
+    console.log('getRequest', getRequest)
+
+
+    function ajax(options) {
+      return new Promise((resolve, reject) => {
+        if (!options.url) {
+          return;
+        }
+        let method = options.method || 'GET';
+        let async = options.async || true;
+        let xhr = new XMLHttpRequest();
+        if (method === 'GET') {
+          // url后面添加随机数防止请求缓存
+          xhr.open(method, options.url + "?" + Math.random(), async);
+          xhr.send(null);
+        } else if (method === 'POST') {
+          xhr.open(method, options.url, async);
+          xhr.setRequestHeader('Content-type', 'application/json;charset=utf-8');
+          xhr.send(JSON.stringify(options.data));
+        }
+
+        xhr.onreadystatechange = () => {
+          if (xhr.responseText) {
+            resolve(xhr.responseText);
+          }
+        };
+        xhr.onerror = (e) => {
+          reject(e);
+        }
+      }).catch(e => {
+      });
+    }
+
+    function openWeapp() {
+      let req = ajax({
+        method: 'POST',
+        url: 'https://test-mp-adm.quanshu123.com/test-api/api/v1/mp/wx/urlschema/generate',
+        data: {
+          path: '/pages/bills/detail',
+          query: `id=${ getRequest.id }`
+        }
+      })
+
+      req.then(data => {
+        let resData = JSON.parse(data)
+        if(resData.code == 0){
+          window.location.href = resData.data;
+        }
+      })
+    }
+
+    function openWx() {
+      window.location.href = "weixin://";
+    }
+  </script>
+  <style>
+    .skip-wrap {
+      width: 100%;
+      height: 100%;
+      position: relative;
+    }
+
+    .skip-theme {
+      display: flex;
+      align-items: center;
+      flex-direction: column;
+      padding: 200px 0 0;
+    }
+
+    .skip-theme-iamge {
+      width: 60px;
+      height: 60px;
+      line-height: 60px;
+      text-align: center;
+      color: #fff;
+      background-color: #e96737;
+      border-radius: 50%;
+      margin-bottom: 10px;
+    }
+
+    .skip-theme-txt {
+      color: rgb(194, 184, 184);
+      text-align: center;
+    }
+
+    .skip-btn {
+      position: absolute;
+      bottom: 50px;
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .skip-btn p {
+      height: 30px;
+      width: 200px;
+      background-color: rgb(7, 192, 88);
+      border-radius: 8px;
+      line-height: 30px;
+      color: #fff;
+      text-align: center;
+    }
+  </style>
+</head>
+
+<body>
+  <div class="skip-wrap">
+    <div class="skip-theme" id="container-tip"></div>
+    <div class="skip-btn">
+      <p onclick="openWeapp()" id="container"></p>
+      <p onclick="openWeapp()" id="container-wx"></p>
+    </div>
+  </div>
+</body>
+
+</html>

File diff suppressed because it is too large
+ 8 - 0
public/tinymce.min.js


+ 13 - 0
src/api/admin/area.js

@@ -0,0 +1,13 @@
+import request from '@/utils/request'
+
+// 查询Pid查询区域列表列表
+export function listAreaByPid(pid) {
+  var query = {
+    pid:pid
+  }
+  return request({
+    url: '/api/v1/mp/listByPid',
+    method: 'get',
+    params: query
+  })
+}

+ 71 - 0
src/api/admin/channel.js

@@ -0,0 +1,71 @@
+import request from '@/utils/request'
+
+
+// 查询渠道树列表
+export function treeChannel(data) {
+  return request({
+    url: '/api/v1/mp/admin/channel/tree',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询子渠道列表
+export function listChannel(urlParams, data) {
+  return request({
+    url: '/api/v1/mp/admin/channel/list',
+    method: 'post',
+    data: data,
+    urlParams: urlParams
+  })
+}
+
+
+// 查询所有渠道列表
+export function listAllChannel() {
+  return request({
+    url: '/api/v1/mp/admin/channel/listAll',
+    method: 'post'
+  })
+}
+
+export function getChannelDetail(channelId) {
+  return request({
+    url: '/api/v1/mp/admin/channel/detail',
+    method: 'post',
+    data: {
+      channelId
+    }
+  })
+}
+
+
+// 新增渠道
+export function addChannel(data) {
+  return request({
+    url: '/api/v1/mp/admin/channel/create',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改渠道
+export function updateChannel(data) {
+  return request({
+    url: '/api/v1/mp/admin/channel/update',
+    method: 'post',
+    data: data
+  })
+}
+
+
+//编辑渠道用户状态信息
+export function updateChannelStatus(data) {
+  return request({
+    url: '/api/v1/mp/admin/channel/status',
+    method: 'post',
+    data: data
+  })
+}
+
+

+ 52 - 0
src/api/admin/salesite.js

@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+
+// 查询经销商列表
+export function listSaleSite(urlParams, data) {
+  return request({
+    url: '/api/v1/mp/admin/salesite/list',
+    method: 'post',
+    data: data,
+    urlParams: urlParams
+  })
+}
+
+export function getSaleSiteDetail(channelId) {
+  return request({
+    url: '/api/v1/mp/admin/salesite/detail',
+    method: 'post',
+    data: {
+      channelId
+    }
+  })
+}
+
+// 新增经销商
+export function addSaleSite(data) {
+  return request({
+    url: '/api/v1/mp/admin/salesite/create',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改经销商
+export function updateSaleSite(data) {
+  return request({
+    url: '/api/v1/mp/admin/salesite/update',
+    method: 'post',
+    data: data
+  })
+}
+
+
+//编辑经销商用户状态信息
+export function updateSaleSiteStatus(data) {
+  return request({
+    url: '/api/v1/mp/admin/salesite/status',
+    method: 'post',
+    data: data
+  })
+}
+
+

+ 44 - 0
src/api/business/coupon.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+export function getCouponList(urlParams, data) {
+  return request({
+    url: '/api/v1/mp/admin/coupon/list',
+    method: 'post',
+    data,
+    urlParams
+  })
+}
+
+export function getCouponDetail(couponId) {
+  return request({
+    url: '/api/v1/mp/admin/coupon/detail',
+    method: 'post',
+    data: {
+      couponId
+    }
+  })
+}
+
+export function addCoupon(data) {
+  return request({
+    url: `/api/v1/mp/admin/coupon/${data.couponId ? 'update' : 'create'}`,
+    method: 'post',
+    data
+  })
+}
+
+export function delCoupon(couponId) {
+  return request({
+    url: '/api/v1/mp/admin/coupon/remove',
+    method: 'post',
+    data: { couponId }
+  })
+}
+
+export function setCouponStatus(data) {
+  return request({
+    url: '/api/v1/mp/admin/coupon/status',
+    method: 'post',
+    data
+  })
+}

+ 44 - 0
src/api/business/goods.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+export function getGoodsList(urlParams, data) {
+  return request({
+    url: '/api/v1/mp/admin/goods/list',
+    method: 'post',
+    data,
+    urlParams
+  })
+}
+
+export function getGoodsDetail(goodsId) {
+  return request({
+    url: '/api/v1/mp/admin/goods/detail',
+    method: 'post',
+    data: {
+      goodsId
+    }
+  })
+}
+
+export function addGoods(data) {
+  return request({
+    url: `/api/v1/mp/admin/goods/${data.goodsId ? 'update' : 'create'}`,
+    method: 'post',
+    data
+  })
+}
+
+export function delGoods(goodsId) {
+  return request({
+    url: '/api/v1/mp/admin/goods/remove',
+    method: 'post',
+    data: { goodsId }
+  })
+}
+
+export function setGoodsStatus(data) {
+  return request({
+    url: '/api/v1/mp/admin/goods/status',
+    method: 'post',
+    data
+  })
+}

+ 93 - 0
src/api/business/ticket.js

@@ -0,0 +1,93 @@
+import request from '@/utils/request'
+
+// 盲票列表
+export function getTicketList(urlParams, data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/list',
+    method: 'post',
+    data,
+    urlParams
+  })
+}
+
+// 创建盲票组
+export function ticketBoxCreate(data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/create',
+    method: 'post',
+    data
+  })
+}
+
+// /api/v1/mp/wx/urlschema/generate
+export function urlschemaGeneratee(data) {
+  return request({
+    url: '/api/v1/mp/wx/urlschema/generate',
+    method: 'post',
+    data
+  })
+}
+
+// 盲票组导出
+export function exportTicket(data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/export',
+    method: 'post',
+    timeout: 15 * 60 * 1000,
+    data
+  })
+}
+
+// 导出印刷
+export function exportDraw(data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/exportDraw',
+    method: 'post',
+    timeout: 15 * 60 * 1000,
+    data
+  })
+}
+
+// 下载批量导入模板
+export function importTemplate(){
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/importTemplate',
+    method: 'get'
+  })
+}
+
+
+// 批量导入
+export function importTicket(data, config = {}){
+  var formData  = new FormData()
+  Object.keys(data).forEach(key => {
+    formData.append(key, data[key])
+  })
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/importTicket',
+    method: 'post',
+    data: formData ,
+    ...config,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  })
+}
+
+// 上架、下架
+export function ticketBoxPut(data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/put',
+    method: 'post',
+    data
+  })
+}
+
+// 删除
+export function ticketBoxRemove(data) {
+  return request({
+    url: '/api/v1/mp/admin/ticket/box/remove',
+    method: 'post',
+    data
+  })
+}

+ 8 - 8
src/api/common.js

@@ -4,15 +4,15 @@ import request from '@/utils/request'
  * 附件
  * 附件
  * @param {*} data
  * @param {*} data
  */
  */
-export const privateFileSaveUrl = '/api/v1/ygp/image/remote/upload/1'
+export const privateFileSaveUrl = '/api/v1/mp/image/remote/upload/1'
 
 
-export const publicFileSaveUrl = '/api/v1/ygp/image/remote/upload/0'
+export const publicFileSaveUrl = '/api/v1/mp/image/remote/upload/0'
 
 
 // 云端非公开可访问的BaseUrl
 // 云端非公开可访问的BaseUrl
-export const privateFileGetUrl = '/api/v1/ygp/file/remote/download?name='
+export const privateFileGetUrl = '/api/v1/mp/file/remote/download?name='
 
 
 // 云端公开可访问的BaseUrl
 // 云端公开可访问的BaseUrl
-export const publicFileGetUrl = process.env.NODE_ENV === "production" ? 'https://ygp-public-1307117429.cos.ap-shanghai.myqcloud.com/':'https://ygp-public-test-1307117429.cos.ap-shanghai.myqcloud.com/'
+export const publicFileGetUrl = process.env.NODE_ENV === "production" ? 'https://mp-public-1307117429.cos.ap-shanghai.myqcloud.com/':'https://mp-public-test-1307117429.cos.ap-shanghai.myqcloud.com/'
 
 
 /**
 /**
  * Put 方式上传图片
  * Put 方式上传图片
@@ -23,7 +23,7 @@ export function publicFileSaveAPI(data, config = {}) {
     formData.append(key, data[key])
     formData.append(key, data[key])
   })
   })
   return request({
   return request({
-    url: '/api/v1/ygp/image/remote/upload/0',
+    url: '/api/v1/mp/image/remote/upload/0',
     method: 'put',
     method: 'put',
     data: formData ,
     data: formData ,
     ...config,
     ...config,
@@ -42,7 +42,7 @@ export function privateFileSaveAPI(data, config = {}) {
     formData.append(key, data[key])
     formData.append(key, data[key])
   })
   })
   return request({
   return request({
-    url: '/api/v1/ygp/image/remote/upload/1',
+    url: '/api/v1/mp/image/remote/upload/1',
     method: 'put',
     method: 'put',
     data: formData ,
     data: formData ,
     ...config,
     ...config,
@@ -61,7 +61,7 @@ export function uploadFileRequest(data, bizType, config = {}) {
     formData.append(key, data[key])
     formData.append(key, data[key])
   })
   })
   return request({
   return request({
-    url: '/api/v1/ygp/file/remote/upload/' + bizType,
+    url: '/api/v1/mp/file/remote/upload/' + bizType,
     method: 'put',
     method: 'put',
     data: formData ,
     data: formData ,
     ...config,
     ...config,
@@ -90,7 +90,7 @@ export function importFileSaveAPI(data, config = {}) {
     formData.append(key, data[key])
     formData.append(key, data[key])
   })
   })
   return request({
   return request({
-    url: '/api/v1/ygp/meter/importData',
+    url: '/api/v1/mp/meter/importData',
     method: 'post',
     method: 'post',
     data: formData ,
     data: formData ,
     ...config,
     ...config,

+ 1 - 1
src/api/system/role.js

@@ -9,7 +9,7 @@ export function listRole(query) {
   })
   })
 }
 }
 
 
-// 查询可分配的角色(门店角色和商户角色)列表(下拉框使用)
+// 查询可分配的角色(渠道商角色和经销商角色)列表(下拉框使用)
 export function itemRole(query) {
 export function itemRole(query) {
   // console.log("itemRole query == "+JSON.stringify(query))
   // console.log("itemRole query == "+JSON.stringify(query))
   return request({
   return request({

+ 5 - 1
src/assets/styles/index.scss

@@ -213,4 +213,8 @@ aside {
 ::-webkit-scrollbar-track-piece { background-color: #f5f5f5; border-radius: 6px; }
 ::-webkit-scrollbar-track-piece { background-color: #f5f5f5; border-radius: 6px; }
 ::-webkit-scrollbar-thumb { background-color: #cccccc; border-radius: 6px; }
 ::-webkit-scrollbar-thumb { background-color: #cccccc; border-radius: 6px; }
 ::-webkit-scrollbar-corner { background-color: #f5f5f5; }
 ::-webkit-scrollbar-corner { background-color: #f5f5f5; }
-::-webkit-resizer { background-repeat: no-repeat; background-position: bottom right; }
+::-webkit-resizer { background-repeat: no-repeat; background-position: bottom right; }
+
+.del {
+  color: #F56C6C;
+}

+ 32 - 2
src/components/ImageUpload/index.vue

@@ -1,11 +1,11 @@
 <template>
 <template>
   <div class="component-upload-image img-accessory-1">
   <div class="component-upload-image img-accessory-1">
-    <div class="img-box-1">
+    <div class="img-box-1" :class="low && 'low'">
 
 
       <ul class="el-upload-list el-upload-list--picture-card">
       <ul class="el-upload-list el-upload-list--picture-card">
         <li v-for="(item, index) in previewList" class="el-upload-list__item is-ready" style="float: left;">
         <li v-for="(item, index) in previewList" class="el-upload-list__item is-ready" style="float: left;">
           <auth-img v-if="!isPublic" :auth-src="item.thumbUrl" image-width="80px" image-height="78px"></auth-img>
           <auth-img v-if="!isPublic" :auth-src="item.thumbUrl" image-width="80px" image-height="78px"></auth-img>
-          <el-image v-if="isPublic" :src="item.thumbUrl" style="width: 80px;height: 78px;border-radius: 4px;"></el-image>
+          <el-image v-if="isPublic" :src="item.thumbUrl" fit="contain" style="width: 80px;height: 78px;border-radius: 4px;"></el-image>
           <label class="el-upload-list__item-status-label"><i class="el-icon-upload-success el-icon-check"></i></label>
           <label class="el-upload-list__item-status-label"><i class="el-icon-upload-success el-icon-check"></i></label>
           <span class="el-upload-list__item-actions">
           <span class="el-upload-list__item-actions">
             <span class="el-upload-list__item-preview"><i class="el-icon-zoom-in" @click="handlePreview(item)"></i></span>
             <span class="el-upload-list__item-preview"><i class="el-icon-zoom-in" @click="handlePreview(item)"></i></span>
@@ -107,6 +107,11 @@
       isPublic: {
       isPublic: {
         type: Boolean,
         type: Boolean,
         default: true
         default: true
+      },
+      // 半高显示
+      low: {
+        type: Boolean,
+        default: false
       }
       }
     },
     },
     data() {
     data() {
@@ -176,6 +181,7 @@
         this.fileList.splice(findex, 1);
         this.fileList.splice(findex, 1);
         this.previewList.splice(findex, 1);
         this.previewList.splice(findex, 1);
         this.$emit("input", this.fileList);
         this.$emit("input", this.fileList);
+        this.$emit('change')
       },
       },
 
 
       // 删除图片
       // 删除图片
@@ -184,6 +190,7 @@
         this.fileList.splice(findex, 1);
         this.fileList.splice(findex, 1);
         this.previewList.splice(findex, 1);
         this.previewList.splice(findex, 1);
         this.$emit("input", this.fileList);
         this.$emit("input", this.fileList);
+        this.$emit('change')
       },
       },
 
 
       /**
       /**
@@ -200,6 +207,7 @@
           fileType: response.fileType
           fileType: response.fileType
         });
         });
         this.$emit("input", this.fileList);
         this.$emit("input", this.fileList);
+        this.$emit('change')
         this.loading.close();
         this.loading.close();
 
 
       },
       },
@@ -265,6 +273,7 @@
             fileType: response.fileType
             fileType: response.fileType
           });
           });
           this.$emit("input", this.fileList);
           this.$emit("input", this.fileList);
+          this.$emit('change')
           this.loading.close();
           this.loading.close();
 
 
 
 
@@ -467,3 +476,24 @@
       display: block;
       display: block;
   }
   }
 </style>
 </style>
+<style lang="scss">
+.component-upload-image {
+  .low {
+    height: 40px;
+    .el-upload--picture-card {
+      height: 40px !important;
+      line-height: 50px !important;
+    }
+    .el-upload-list__item {
+      margin: 0 !important;
+      height: 40px !important;
+    }
+    .el-image {
+      height: 38px !important;
+    }
+    .el-upload-list__item-actions {
+      height: 38px !important;
+    }
+  }
+}
+</style>

+ 72 - 0
src/components/TinyEditor.vue

@@ -0,0 +1,72 @@
+<template>
+  <TinyEditor v-model="content" :placeholder="placeholder" api-key="ushn75pw8xd7ec18wwdatp7bi75o98pm0ob10zncyyrbkj4k" :init="options" />
+</template>
+<script>
+import TinyEditor from '@tinymce/tinymce-vue'
+import { publicFileSaveAPI } from '@/api/common'
+import { publicFileGetUrl } from "@/api/common"
+import { getToken } from '@/utils/auth'
+export default {
+  components: {
+    TinyEditor
+  },
+  props: {
+    value: {
+      default: '',
+      type: String
+    },
+    height: {
+      default: 800,
+      type: Number
+    },
+    placeholder: {
+      default: '',
+      type: String
+    }
+  },
+  data() {
+    return {
+      options: {
+        base_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/',
+        language: 'zh_CN',
+        language_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/zh_CN.js',
+        height: 500,
+        resize: true,
+        statusbar: false,
+        // skin_url: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/oxide-dark', // 暗色皮肤
+        // content_css: '//itie-static.oss-cn-hangzhou.aliyuncs.com/assets/tinymce/content.css',
+        plugins: ['advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount placeholder indent2em'], // 插件
+        image_advtab: true,
+        images_upload_handler: this.updateImage,
+        imagetools_toolbar: '',
+        fontsize_formats: '8px 10px 12px 14px 16px 18px 20px 22px 24px 36px',
+        toolbar: ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen indent2em']
+      }
+    }
+  },
+  computed: {
+    content: {
+      get() {
+        return this.value
+      },
+      set(val) {
+        this.$emit('input', val)
+      }
+    }
+  },
+  methods: {
+    updateImage(blobInfo, success, failure) {
+      publicFileSaveAPI(
+        { file: blobInfo.blob() },
+        {
+          "Authorization": "Bearer " + getToken(),
+          "x-zz-timestamp": new Date().valueOf()
+        }).then(res => {
+        success(publicFileGetUrl + res.data.fileName)
+      }).catch(err => {
+        failure(err)
+      })
+    }
+  }
+}
+</script>

+ 2 - 1
src/main.js

@@ -16,7 +16,7 @@ import './assets/icons' // icon
 import './permission' // permission control
 import './permission' // permission control
 import { getDicts } from "@/api/system/dict/data";
 import { getDicts } from "@/api/system/dict/data";
 import { getConfigKey } from "@/api/system/config";
 import { getConfigKey } from "@/api/system/config";
-import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/util";
+import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree, numberFormat } from "@/utils/util";
 import Pagination from "@/components/Pagination";
 import Pagination from "@/components/Pagination";
 // 自定义表格工具组件
 // 自定义表格工具组件
 import RightToolbar from "@/components/RightToolbar"
 import RightToolbar from "@/components/RightToolbar"
@@ -43,6 +43,7 @@ Vue.prototype.selectDictLabel = selectDictLabel
 Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.download = download
 Vue.prototype.download = download
 Vue.prototype.handleTree = handleTree
 Vue.prototype.handleTree = handleTree
+Vue.prototype.$numberFormat = numberFormat
 
 
 // 处理时间的过滤器
 // 处理时间的过滤器
 Vue.use(require('vue-moment'))
 Vue.use(require('vue-moment'))

+ 1 - 1
src/permission.js

@@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth'
 
 
 NProgress.configure({ showSpinner: false })
 NProgress.configure({ showSpinner: false })
 
 
-const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
+const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/skip', '/jump']
 
 
 router.beforeEach((to, from, next) => {
 router.beforeEach((to, from, next) => {
   NProgress.start()
   NProgress.start()

+ 20 - 1
src/router/index.js

@@ -58,6 +58,11 @@ export const constantRoutes = [
     component: (resolve) => require(['@/views/error/401'], resolve),
     component: (resolve) => require(['@/views/error/401'], resolve),
     hidden: true
     hidden: true
   },
   },
+  {
+    path: '/skip',
+    component: (resolve) => require(['@/views/skip'], resolve),
+    hidden: true
+  },
   {
   {
     path: '',
     path: '',
     component: Layout,
     component: Layout,
@@ -149,7 +154,21 @@ export const constantRoutes = [
         meta: { title: '修改生成配置', activeMenu: '/tool/gen'}
         meta: { title: '修改生成配置', activeMenu: '/tool/gen'}
       }
       }
     ]
     ]
-  }
+  },
+  {
+    path: '/ticket',
+    component: Layout,
+    hidden: true,
+    children: [
+      {
+        path: 'create',
+        component: (resolve) => require(['@/views/business/ticket/create'], resolve),
+        name: 'TicketCreate',
+        meta: { title: '添加盲票组' ,activeMenu: '/business/ticket'}
+      },
+    ]
+  },
+
 ]
 ]
 
 
 export default new Router({
 export default new Router({

+ 1 - 1
src/utils/util.js

@@ -175,7 +175,7 @@ export function accMul(arg1, arg2) {
 	return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
 	return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
 }
 }
 
 
-export function numberFormat(number, decimals, dec_point, thousands_sep) {
+export function numberFormat(number, decimals = 2, dec_point = '.', thousands_sep = ',') {
   /*
   /*
     * 参数说明:
     * 参数说明:
     * number:要格式化的数字
     * number:要格式化的数字

+ 469 - 0
src/views/business/channel/components/Create.vue

@@ -0,0 +1,469 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    width="750px"
+    :append-to-body="true"
+    :before-close="close"
+    :destroy-on-close="true"
+    :close-on-click-modal="false"
+  >
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="120px"
+      label-position="top"
+      style="max-height: 375px;overflow: auto;"
+    >
+    <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+      <el-form-item label="渠道名称" prop="name" style="width: 50%;">
+        <el-input v-model="form.name" placeholder="请输入渠道名称" />
+      </el-form-item>
+       <el-form-item label="手机号码" prop="mobile" style="width: 50%;">
+         <el-input v-model="form.mobile" placeholder="请输入手机号码" />
+       </el-form-item>
+    </flexbox>
+      <!-- <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="上级渠道" prop="parentId" style="width: 50%;">
+          <el-select
+            v-model="form.parentId"
+            placeholder="请选择上级渠道"
+            style="width: 100%;"
+            filterable
+            clearable
+            :filter-method="dataFilter"
+          >
+            <el-option
+              v-for="(item, index) in channelList"
+              :key="item.channelId"
+              :label="item.name"
+              :value="item.channelId">
+              <div>
+                <span style="float: left;">{{item.name}} </span>
+                <span style="float: right;">{{item.mobile}}</span>
+              </div>
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </flexbox> -->
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="联系人" prop="contact" style="width: 50%;">
+          <el-input v-model="form.contact" placeholder="联系人" />
+        </el-form-item>
+        <el-form-item label="佣金比例" prop="commRate" style="width: 50%;">
+          <el-input v-model="form.commRate" placeholder="请输入佣金比例" />
+        </el-form-item>
+      </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="省" prop="provinceId" style="width: 30%;">
+          <el-select
+            v-model="form.provinceId"
+            placeholder="选择省份"
+            clearable
+            size="small"
+            @change="getCityList()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in provinceList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+
+        </el-form-item>
+        <el-form-item label="市" prop="cityId" style="width: 30%;">
+          <el-select
+            v-model="form.cityId"
+            placeholder="选择市"
+            clearable
+            size="small"
+             @change="getAreaList()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in cityList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区" prop="areaId" style="width: 30%;" >
+          <el-select v-model="form.areaId"
+            placeholder="选择区"
+            clearable
+            size="small"
+            @change="getAreaName()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in areaList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+        </el-form-item>
+      </flexbox>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" :disabled="loading" @click="saveClick">确 定</el-button>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import { addChannel, updateChannel, listAllChannel, getChannelDetail} from "@/api/admin/channel";
+import { listAreaByPid} from "@/api/admin/area";
+import { mapGetters } from "vuex";
+export default {
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    },
+    editId: [Number, String] //  编辑用
+  },
+
+  data() {
+    return {
+      loading: false,
+      // 上级渠道列表
+      channelList:[],
+      channelCopyList:[],
+      provinceList:[],
+      cityList:[],
+      areaList:[],
+      statusOptions: [],
+      // 表单参数
+      form: {
+        name:"",
+        mobile:"",
+        contact:"",
+        commRate:"",
+        provinceId:"",
+        cityId:"",
+        areaId:"",
+        province:"",
+        city:"",
+        area:""
+      },
+      detail: {},
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: "请输入渠道名称", trigger: "blur" }
+        ],
+        mobile: [
+          { required: true, message: "请输入手机号码", trigger: "blur" },
+          {pattern: /^((\+?86)|(\(\+86\)))?1\d{10}$/, message: "请输入正确的手机号码", trigger: "blur" }
+        ],
+        // parentId: [{ required: true, message: "请选择上级渠道", trigger: "change" }],
+        contact: [
+          { required: true, message: "请输入联系人", trigger: "blur" }
+        ],
+        commRate: [
+          { required: true, message: "请输入佣金比例", trigger: "blur" },
+          { pattern: /^(([1-9]{1}\d{0,99}))(\.\d{1,2})?$/, message: "佣金比例只能为大于0的数字", trigger:['blur', 'change'] },
+        ],
+        provinceId: [{ required: true, message: "请选择省", trigger: "change" }],
+        cityId: [{ required: true, message: "请选择市", trigger: "change" }],
+        areaId: [{ required: true, message: "请选择区", trigger: "change" }],
+
+      },
+      //招商推广宣传图
+      picture: []
+    };
+  },
+
+  computed: {
+    ...mapGetters(["userInfo"]),
+    title() {
+      return this.editId ? "编辑一级渠道" : "添加一级渠道";
+    }
+  },
+
+  created() {
+    //this.getCustItems();
+   // this.getTypeItems();
+    //console.log("watch dialogVisible created == ");
+    // 是编辑
+    if (this.editId) {
+       this.getDetail();
+    }else{
+       this.getProvinceList()
+    }
+    // this.getChannelList()
+
+  },
+
+  mounted() {
+    document.body.appendChild(this.$el);
+  },
+
+  destroyed() {
+    // if appendToBody is true, remove DOM node after destroy
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el);
+    }
+  },
+
+  methods: {
+
+    // 获取上级渠道下拉列表
+    getChannelList(){
+      listAllChannel().then(response => {
+        var noneObj = {
+          channelId:0,
+          name:'无(新建一级渠道)'
+          }
+          this.channelList.push(noneObj)
+        // console.log("getChannelList == "+ JSON.stringify(response.data))
+         this.channelList = this.channelList.concat(response.data || []);
+         this.channelCopyList = this.channelList;
+      });
+    },
+    // 省
+    getProvinceList(){
+      this.cityList = []
+      this.areaList = []
+      this.form.cityId = ""
+      this.form.city = ""
+      this.form.areaId = ""
+      this.form.area = ""
+      listAreaByPid(0).then(response => {
+        // console.log("getProvinceList"+JSON.stringify(response))
+         console.log("getProvinceList this.form=="+JSON.stringify(this.form))
+         this.provinceList = response || [];
+          console.log("this.editId && this.form.provinceId == "+(this.editId && this.form.provinceId))
+         if(this.editId && this.detail.provinceId){
+            this.form.provinceId = this.detail.provinceId
+            this.getCityList()
+         }
+      });
+    },
+
+    getProvinceName(){
+      let val = this.form.provinceId
+      if(val){
+        let nameObj = this.provinceList.find(item => {
+              return item.areaId == val;
+        })
+        return nameObj && nameObj.areaName
+      }
+      return ""
+    },
+
+
+    getCityList(){
+      this.cityList = []
+      this.areaList = []
+      this.form.cityId = ""
+      this.form.city = ""
+      this.form.areaId = ""
+      this.form.area = ""
+      var provinceId = this.form.provinceId
+      this.form.province = this.getProvinceName()
+      listAreaByPid(provinceId).then(response => {
+         this.cityList = response || [];
+         if(this.editId && this.detail.cityId){
+            this.form.cityId = this.detail.cityId
+            this.getAreaList()
+         }
+      });
+
+    },
+
+    getCityName(){
+      let val = this.form.cityId
+      if(val){
+        let nameObj = this.cityList.find(item => {
+              return item.areaId == val;
+        })
+        return nameObj && nameObj.areaName
+      }
+      return ""
+    },
+
+    getAreaList(){
+      var cityId = this.form.cityId
+      this.form.city = this.getCityName();
+      listAreaByPid(cityId).then(response => {
+        // console.log("getAreaList"+JSON.stringify(response))
+         this.areaList = response || [];
+         if(this.editId && this.detail.areaId){
+            this.form.areaId = this.detail.areaId
+         }
+      });
+    },
+
+    getAreaName(){
+      let val = this.form.areaId
+      if(val){
+        let nameObj = this.areaList.find(item => {
+              return item.areaId == val;
+        })
+        this.form.area = nameObj && nameObj.areaName
+      }else{
+        this.form.area = "";
+      }
+    },
+
+    dataFilter(val) {
+      this.value = val;
+      if (val) { //val存在
+        this.channelList = this.channelCopyList.filter((item) => {
+          // console.log("dataFilter item"+JSON.stringify(item))
+          if (!!~item.mobile.indexOf(val) || !!~item.mobile.toUpperCase().indexOf(val.toUpperCase())) {
+             return true
+          }
+        })
+      } else { //val为空时,还原数组
+        this.channelList = this.channelCopyList;
+      }
+    },
+
+
+    /**
+     * 获取项目详情
+     */
+    getDetail() {
+      this.loading = true;
+      getChannelDetail(this.editId).then(res => {
+          const data = res.data || {}
+          // console.log("getDetail :" + JSON.stringify(data))
+          this.detail = data
+          this.form.name = data.name
+          this.form.mobile = data.mobile
+          this.form.contact = data.contact
+          this.form.commRate = data.commRate
+          this.loading = false
+          this.getProvinceList()
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+
+
+    saveClick(){
+      // console.log("saveClick tthis.form = "+JSON.stringify(this.form))
+      this.loading = true
+      const subForm = this.$refs["form"]
+      subForm.validate(valid => {
+        if (valid) {
+          this.submitForm(this.form);
+        } else {
+          this.loading = false;
+          // 提示第一个error
+          this.getFormErrorMessage(subForm);
+          return false;
+        }
+      });
+    },
+
+    /**
+     * 保存
+     */
+    submitForm(params) {
+      //  console.log("submitForm =============")
+      if (this.editId) {
+        params.channelId = this.editId;
+      }
+      const request = this.editId ? updateChannel : addChannel
+      // console.log("submitForm params = "+ JSON.stringify(params))
+      request(params).then(response => {
+        this.loading = false
+        if(this.editId){
+           this.$message.success('更新成功')
+        }else{
+           this.$message.success('新增成功')
+        }
+        this.$emit('saveSuccess')
+        this.close()
+      }).catch(() => {
+        this.loading = false
+      });
+    },
+
+    /**
+     * 关闭窗口
+     */
+    close() {
+      this.$emit("close");
+      this.form = {
+        month: 3,
+        packageType: "1"
+      };
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.tag {
+  margin-right: 15px;
+  width: 90px;
+  text-align: center;
+  cursor: pointer;
+}
+
+.tag-select {
+  background-color: #409eff !important;
+  border-color: #409eff !important;
+  color: #fff !important;
+}
+
+.cover-content-item {
+  width: 220px;
+  float: left;
+  position: relative;
+  .cover-img {
+    width: 200px;
+    height: 100px;
+  }
+  .cover-mark {
+    position: absolute;
+    top: 0px;
+    right: 28px;
+    z-index: 1;
+    color: red;
+    cursor: pointer;
+    visibility: hidden;
+  }
+  .select {
+    visibility: visible !important;
+  }
+}
+
+.dialog-footer {
+  text-align: center;
+}
+</style>
+
+<style lang="scss">
+.ygp-form-items {
+  .el-form-item {
+    padding: 0 5px;
+  }
+  .el-form-item__label {
+    line-height: 1.2;
+    padding-bottom: 8px;
+    word-break: break-all;
+    word-wrap: break-word;
+    color: #333;
+  }
+
+  .el-form-item__error {
+    position: relative;
+    top: auto;
+    left: auto;
+  }
+
+  .el-form-item.is-desc_text {
+    .el-form-item__label {
+      display: none;
+    }
+  }
+}
+</style>

+ 252 - 0
src/views/business/channel/components/Detail.vue

@@ -0,0 +1,252 @@
+<template>
+ <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    width="750px"
+    :append-to-body="true"
+    :before-close="close"
+    :destroy-on-close="true"
+    :close-on-click-modal="false">
+   <el-form ref="form" label-width="120px" label-position="top" >
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+        <el-form-item label="渠道名称" style="width: 50%; margin-bottom:5px;" >
+          <span :class="loading?'el-icon-loading':''"></span>
+          {{detail.name}}
+        </el-form-item>
+         <el-form-item label="手机号码" style="width: 50%; margin-bottom:5px;" >
+            <span :class="loading?'el-icon-loading':''"></span>
+            {{detail.mobile}}
+         </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="联系人" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.contact}}
+         </el-form-item>
+          <el-form-item label="渠道级别" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             {{detail.level}}级渠道
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="上级渠道" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           <span v-if="detail.parentName"> {{detail.parentName}}</span>
+           <span v-else>无</span>
+         </el-form-item>
+          <el-form-item label="佣金比例" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             {{detail.commRate}}%
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="所在地区" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.province}} {{detail.city}} {{detail.area}}
+         </el-form-item>
+          <el-form-item label="创建时间" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"> </span>
+            {{ detail.createdTime | moment("YYYY-MM-DD HH:mm:ss") }}
+          </el-form-item>
+     </flexbox>
+      <el-divider></el-divider>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="经销商" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.siteCnt}}
+         </el-form-item>
+          <el-form-item label="用户数" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             {{detail.userCnt}}
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="订单数" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"> </span>
+           {{detail.operData && detail.operData.orderCnt}}
+         </el-form-item>
+          <el-form-item label="营业额" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>{{detail.operData &&detail.operData.saleAmt/100}}
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="佣金" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''">{{detail.operData && detail.operData.commAmt/100}} </span>
+         </el-form-item>
+     </flexbox>
+   </el-form>
+   <div slot="footer" class="dialog-footer">
+     <el-button @click="close">关 闭</el-button>
+   </div>
+ </el-dialog>
+</template>
+<script>
+
+import { mapGetters } from 'vuex'
+import { getChannelDetail} from "@/api/admin/channel";
+export default {
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    },
+    editId: [Number, String] //  编辑用
+  },
+
+  data() {
+    return {
+      loading: false,
+      // 详情数据
+      detail: {}
+    }
+  },
+
+  computed: {
+    ...mapGetters(['userInfo']),
+    title() {
+      return '渠道详情'
+    }
+  },
+
+  created() {
+    // 是编辑
+    if (this.editId) {
+      this.getDetail()
+    }
+  },
+
+  mounted() {
+    document.body.appendChild(this.$el)
+  },
+
+  destroyed() {
+    // if appendToBody is true, remove DOM node after destroy
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el)
+    }
+  },
+
+  methods: {
+
+    /**
+     * 获取项目详情
+     */
+    getDetail() {
+      this.loading = true
+      getChannelDetail(this.editId).then(res => {
+          this.loading = false
+          const data = res.data || {}
+          // console.log("getDetail :" + JSON.stringify(data))
+          this.detail = data
+        })
+        .catch(() => {
+          this.loading = false
+        })
+    },
+
+    /**
+     * 关闭窗口
+     */
+    close() {
+      this.$emit('close')
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.tag{
+  margin-right: 15px;
+  width: 90px;
+  text-align: center;
+  cursor: pointer;
+
+}
+
+.tag-select{
+  background-color: #409eff!important;
+  border-color: #409eff!important;
+  color: #fff!important;
+}
+
+.cover-content-item{
+  width: 220px;
+  float: left;
+  position: relative;
+  .cover-img {
+    width: 200px;
+    height: 100px;
+  }
+  .cover-mark {
+    position: absolute;
+    top: 0px;
+    right: 28px;
+    z-index: 1;
+    color: red;
+    cursor: pointer;
+    visibility: hidden;
+  }
+  .select {
+      visibility: visible!important;
+  }
+}
+
+.dialog-footer{
+  text-align: center;
+}
+
+</style>
+
+<style lang="scss">
+
+.el-dialog__body {
+    padding: 15px 20px;
+}
+
+.ygp-form-items {
+   .el-form-item{
+     padding: 0 5px;
+   }
+
+  .el-form-item__label {
+    line-height: 1.2;
+    padding-bottom: 0px;
+    word-break: break-all;
+    word-wrap: break-word;
+    color: #333;
+  }
+
+  .el-form-item__error {
+    position: relative;
+    top: auto;
+    left: auto;
+  }
+
+  .el-form-item.is-desc_text {
+    .el-form-item__label {
+      display: none;
+    }
+  }
+}
+</style>

+ 426 - 0
src/views/business/channel/index.vue

@@ -0,0 +1,426 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <!--部门数据-->
+      <el-col :span="4" :xs="24" >
+        <div class="head-container">
+        <!--  <el-input
+            v-model="channelName"
+            placeholder="请输入渠道名称"
+            clearable
+            size="small"
+            prefix-icon="el-icon-search"
+            style="margin-bottom: 20px"
+          /> -->
+        </div>
+        <div class="head-container" style="overflow-x:scroll">
+          <el-tree
+            :props="defaultProps"
+            ref="tree"
+            :load="loadNode"
+            :data="channelOptions"
+            lazy
+            @node-click="handleNodeClick"
+          />
+        </div>
+      </el-col>
+      <!--用户数据-->
+      <el-col :span="20" :xs="24">
+        <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+          <el-form-item label="渠道名称" prop="name">
+            <el-input
+              v-model="queryParams.name"
+              placeholder="请输入渠道名称"
+              clearable
+              size="small"
+              style="width: 240px"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="手机号码" prop="mobile">
+            <el-input
+              v-model="queryParams.mobile"
+              placeholder="请输入手机号码"
+              clearable
+              size="small"
+              style="width: 240px"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+         <el-form-item label="地区">
+           <el-select
+             v-model="queryParams.provinceId"
+             placeholder="选择省份"
+             clearable
+             size="small"
+             @change="getCityList()"
+             style="width: 120px;margin-right: 1px;">
+             <el-option
+               v-for="item in provinceList"
+               :key="item.areaId"
+               :label="item.areaName"
+               :value="item.areaId"
+             />
+           </el-select>
+           <el-select
+             v-model="queryParams.cityId"
+             placeholder="选择市"
+             clearable
+             size="small"
+              @change="getAreaList()"
+             style="width: 120px;margin-right: 1px;">
+             <el-option
+               v-for="item in cityList"
+               :key="item.areaId"
+               :label="item.areaName"
+               :value="item.areaId"
+             />
+           </el-select>
+           <el-select v-model="queryParams.areaId" placeholder="选择区" clearable size="small" style="width: 120px;margin-right: 1px;">
+             <el-option
+               v-for="item in areaList"
+               :key="item.areaId"
+               :label="item.areaName"
+               :value="item.areaId"
+             />
+           </el-select>
+         </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+            <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button
+              type="primary"
+              icon="el-icon-plus"
+              size="mini"
+              @click="handleAdd"
+              v-hasPermi="['system:user:add']"
+            >添加一级渠道</el-button>
+          </el-col>
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+
+        <el-table v-loading="loading" :data="channelList">
+          <el-table-column label="渠道编号" prop="channelId" width="100px" />
+          <el-table-column label="渠道名称" prop="name" show-overflow-tooltip  width="180px"/>
+          <el-table-column label="手机号码" prop="mobile" show-overflow-tooltip  width="120px"/>
+          <el-table-column label="佣金比例" prop="commRate">
+            <template slot-scope="scope">
+              {{scope.row.commRate}}%
+            </template>
+          </el-table-column>
+          <el-table-column label="渠道级别" prop="level">
+            <template slot-scope="scope">
+              {{scope.row.level}}级渠道
+            </template>
+          </el-table-column>
+          <el-table-column label="经销商数" prop="siteCnt"  />
+          <el-table-column label="用户数" prop="userCnt"  />
+          <el-table-column label="状态" key="statusV">
+            <template slot-scope="scope">
+              <el-switch
+                v-model="scope.row.statusV"
+                active-value="1"
+                inactive-value="2"
+                @change="handleStatusChange(scope.row)"
+              ></el-switch>
+            </template>
+           <!-- <template slot-scope="{ row, column }">
+               <span v-if="getValue(row.status) == 1" style="color: blue;"> {{getDesc(row.status)}}</span>
+               <span v-if="getValue(row.status) == 2" style="color: red;"> {{getDesc(row.status)}}</span>
+           </template> -->
+          </el-table-column>
+          <el-table-column
+            label="操作"
+            align="center"
+            width="200"
+            class-name="small-padding fixed-width"
+          >
+            <template slot-scope="scope" v-if="scope.row.userId !== 1">
+              <el-button
+                size="mini"
+                type="text"
+                @click="handleDetail(scope.row)"
+                v-hasPermi="['admin:channel:read']"
+              >查看</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                @click="handleUpdate(scope.row)"
+                v-hasPermi="['admin:channel:edit']"
+              >编辑</el-button>
+             <!-- <el-button
+                size="mini"
+                type="text"
+                disabled
+                icon="el-icon-plus"
+                @click="handleAdd(scope.row)"
+                v-hasPermi="['admin:channel:add']"
+              >添加子渠道</el-button> -->
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="total>0"
+          :total="total"
+          :page.sync="pageParams.pageNum"
+          :limit.sync="pageParams.pageSize"
+          @pagination="getList"
+        />
+      </el-col>
+    </el-row>
+    <!-- 新建 -->
+    <channel-create
+      v-if="createShow"
+      :dialog-visible="createShow"
+      :edit-id="editId"
+      @saveSuccess="submitSuccess"
+      @close="hideDialog"
+    />
+
+    <!-- 详情 -->
+    <channel-detail
+      v-if="detailShow"
+      :dialog-visible="detailShow"
+      :edit-id="editId"
+      @close="hideDialog"
+    />
+
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+import { treeChannel, listChannel, updateChannelStatus} from "@/api/admin/channel";
+import { listAreaByPid} from "@/api/admin/area";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import ChannelCreate from './components/Create'
+import ChannelDetail from './components/Detail'
+export default {
+  name: "ChannelIndex",
+  components: {
+    Treeselect,
+    ChannelCreate,
+    ChannelDetail
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 创建编辑
+      createShow: false,
+      // 详情
+      detailShow: false,
+      provinceList:[],
+      cityList:[],
+      areaList:[],
+      // 编辑项ID
+      editId: null,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户表格数据
+      channelList: [],
+      channelOptions: [],
+      channelName:"",
+      // 状态数据字典
+      statusOptions: [],
+      // 表单参数
+      form: {},
+      defaultProps: {
+        children: "children",
+        label: "name"
+      },
+      // 查询参数
+      queryParams: {
+        name: "",
+        mobile: "",
+        provinceId:"",
+        cityId:"",
+        areaId:""
+      },
+      pageParams: {
+        pageNum: 1,
+        pageSize: 10
+      }
+    };
+  },
+  watch: {
+    // 根据渠道名称筛选渠道树
+    channelName(val) {
+      this.$refs.tree.filter(val);
+    }
+  },
+  mounted() {
+     this.getProvinceList()
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询用户列表 */
+    getList() {
+      this.loading = true;
+      listChannel('pageNum='+this.pageParams.pageNum + '&pageSize='+this.pageParams.pageSize+'&', this.queryParams).then(response => {
+          this.channelList = response.rows;
+          this.channelList.forEach(item => {
+              item.statusV = JSON.parse(item.status).value+""
+          })
+          this.total = response.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.queryParams.parentId = data.channelId;
+      this.getList();
+    },
+    // 懒加载节点
+    loadNode(node, resolve) {
+      if(node.level === 0){
+       treeChannel({}).then(response => {
+          return resolve(response.data);
+       });
+      }else{
+        var params = {
+          parentId:node.data.channelId
+        }
+        treeChannel(params).then(response => {
+           return resolve(response.data);
+        });
+      }
+    },
+
+    refreshChannelTree(){
+      treeChannel({}).then(response => {
+         this.channelOptions = response.data
+      });
+    },
+
+    // 省
+    getProvinceList(){
+      this.cityList = []
+      this.areaList = []
+      this.queryParams.cityId = ""
+      this.queryParams.areaId = ""
+      listAreaByPid(0).then(response => {
+        // console.log("getProvinceList"+JSON.stringify(response))
+         this.provinceList = response || [];
+      });
+    },
+
+    getCityList(){
+      this.cityList = []
+      this.areaList = []
+      this.queryParams.cityId = ""
+      this.queryParams.areaId = ""
+      var provinceId = this.queryParams.provinceId
+      listAreaByPid(provinceId).then(response => {
+        // console.log("getCityList"+JSON.stringify(response))
+         this.cityList = response || [];
+      });
+    },
+
+    getAreaList(){
+      var cityId = this.queryParams.cityId
+      listAreaByPid(cityId).then(response => {
+        // console.log("getAreaList"+JSON.stringify(response))
+         this.areaList = response || [];
+      });
+    },
+
+    // 状态修改
+    handleStatusChange(row) {
+      var newStatus = this.getValue(row.status) == 2?1:2
+      let text = this.getValue(row.status) == 2 ? "启用" : "停用";
+      this.$confirm('确认要"' + text + '""' + row.name + '"渠道吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var params={
+            channelId:row.channelId,
+            status: newStatus
+          }
+          return updateChannelStatus(params);
+        }).then(() => {
+          this.msgSuccess(text + "成功");
+          this.getList();
+        }).catch(function() {
+          // row.status = row.status === "0" ? "1" : "0";
+        });
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.queryParams= {
+        pageNum: 1,
+        pageSize: 10,
+         name: "",
+         mobile: "",
+         provinceId:"",
+         cityId:"",
+         areaId:""
+      },
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    handleDetail(row){
+      this.detailShow = true
+      this.editId = row.channelId
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.createShow = true
+      this.editId = null
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.createShow = true
+      this.editId = row.channelId
+    },
+    // 保存后的操作
+    submitSuccess(){
+      this.getList();
+      this.refreshChannelTree();
+    },
+    /**关闭弹窗*/
+    hideDialog(){
+      this.createShow = false
+      this.detailShow = false
+    },
+
+    getDesc(val) {
+      // console.log("val == "+val);
+      const dataObj = JSON.parse(val);
+      return (dataObj && dataObj.desc) || "";
+    },
+
+    getValue(val) {
+      // console.log("val == "+val);
+      const dataObj = JSON.parse(val);
+      return (dataObj && dataObj.value) || "";
+    },
+  }
+};
+</script>
+<style lang="sass" scoped>
+
+</style>

+ 213 - 0
src/views/business/coupon/add.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="app-container coupon-add">
+    <br>
+    <el-row>
+      <el-col :span="23">
+        <el-form ref="addItem" :model="addData" :rules="rules" label-width="160px">
+          <el-form-item label="券名称:" prop="title">
+            <el-input v-model="addData.title" :readonly="readonly" placeholder="请输入券名称" :maxlength="32" show-word-limit />
+          </el-form-item>
+          <el-form-item label="使用场景:" prop="type">
+            <el-radio-group v-model="addData.type" :disabled="readonly">
+              <el-radio :label="2">门店消费</el-radio>
+              <el-radio :label="1">盲票购买</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="券金额:" prop="discount">
+            <el-input v-model.number="addData.discount" :readonly="readonly" type="number" :min="0" placeholder="请输入券券金额">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+          <!-- <el-form-item label="数量:" prop="quantity">
+            <el-input v-model.number="addData.quantity" type="number" :min="0" placeholder="请输入券数量">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item> -->
+          <!-- <el-form-item label="图片:" prop="picUrl">
+            <Upload :value="addData.picUrl ? [{ fileName: addData.picUrl }] : []" @input="addData.picUrl = $event[0] ? $event[0].fileName : ''" :limit="1" />
+          </el-form-item> -->
+          <el-form-item v-if="addData.type === 2" label="门店默认承担比例:" prop="channelSharedRate">
+            <el-input v-model="addData.channelSharedRate" :readonly="readonly" type="number" placeholder="请输入门店默认承担比例">
+              <template slot="append">%</template>
+            </el-input>
+            <div class="tip">若门店承担100%,则核销后公司不需要打款给门店,依此类推。</div>
+          </el-form-item>
+          <el-form-item label="最低消费金额:" prop="minOrderAmt">
+            <el-input v-model.number="addData.minOrderAmt" :readonly="readonly" type="number" placeholder="请输入可使用的最低消费金额">
+              <template slot="append">元</template>
+            </el-input>
+            <div class="tip">最低消费金额小于等于面值则为无门槛券</div>
+          </el-form-item>
+          <el-form-item label="有效期限:" prop="dueDays">
+            <el-select v-model="addData.dueDays" clearable placeholder="请选择有效期限">
+              <el-option label="30天" :value="30"></el-option>
+              <el-option label="60天" :value="60"></el-option>
+              <el-option label="90天" :value="90"></el-option>
+              <el-option label="180天" :value="180"></el-option>
+            </el-select>
+            <!-- <el-input v-model.number="addData.dueDays" :readonly="readonly" placeholder="请输入有效期限">
+              <template slot="prepend">领取之日起</template>
+              <template slot="append">天有效</template>
+            </el-input> -->
+          </el-form-item>
+          <el-form-item label="使用说明:" prop="description">
+            <el-input type="textarea" rows="4" :readonly="readonly" v-model="addData.description" placeholder="请输入使用说明 对用户可见"/>
+          </el-form-item>
+          <el-form-item v-if="addData.type === 2" label="使用范围:" prop="useArea">进票门店</el-form-item>
+          <el-form-item v-else label="使用范围:" prop="useArea">
+            <el-radio-group v-model="addData.useArea" :disabled="readonly">
+              <el-radio :label="0">所有盲票</el-radio>
+              <!-- <el-radio :label="1">指定盲票</el-radio> -->
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+        <div v-if="addData.useArea === 1" style="padding: 0 20px">
+          <el-table :data="addData.ticketBoxList" height="300px">
+            <el-table-column label="盲票图片" prop="picUrl" align="center" width="80">
+              <template slot-scope="{row}">
+                <a target="_black" v-if="row.picUrl && row.picUrl.split(',').length > 0" :href="`${IMG_URL + row.picUrl}`"><img :src="`${IMG_URL + row.picUrl.split(',')[0]}`" style="max-height: 37px;max-width: 54px"></a>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="盲票名称" prop="title" />
+            <el-table-column label="盲票面值" prop="facePrice" />
+            <el-table-column label="盲票类型" prop="type">
+              <template slot-scope="{row}">
+                {{ JSON.parse(row.type).desc }}
+              </template>
+            </el-table-column>
+            <el-table-column label="盲票状态" prop="status">
+              <template slot-scope="{row}">
+                <el-tag :type="JSON.parse(row.status).value === 'on' ? 'success' : 'info'">{{ JSON.parse(row.status).desc }}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column v-if="!readonly" prop="date" label="操作">
+              <template slot-scope="{ $index }">
+                <el-button type="text" class="del" @click="addData.ticketBoxList.splice($index, 1)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <br>
+          <el-button v-if="!readonly" type="primary" size="small" plain @click="$refs.select.show()">选择盲票</el-button> <span style="margin-left: 10px">已选盲票({{ addData.ticketBoxList.length }})</span>
+        </div>
+      </el-col>
+    </el-row>
+    <el-row v-if="!readonly">
+      <el-col :span="23" style="text-align: right">
+        <el-button type="info" @click="$router.go(-1)">取消</el-button>
+        <el-button :loading="addIng" type="primary" @click="update()">保存</el-button>
+      </el-col>
+    </el-row>
+
+    <!-- 弹出层 -->
+    <SelectTicket ref="select" v-model="addData.ticketBoxList" />
+  </div>
+</template>
+<script>
+import Upload from '@/components/ImageUpload'
+import SelectTicket from './components/selectTicket'
+import { publicFileGetUrl } from '@/api/common'
+import { getCouponDetail, addCoupon } from '@/api/business/coupon'
+import { accDiv, accMul } from '@/utils/util'
+export default {
+  name: 'CouponAdd',
+  components: {
+    Upload,
+    SelectTicket
+  },
+  data() {
+    return {
+      IMG_URL: publicFileGetUrl,
+      id: this.$route.query.id,
+      readonly: this.$route.name === 'View',
+      addData: {
+        ticketBoxList:[],
+        useArea: 0,
+        dueDays: 90,
+        type: 2
+      },
+      addIng: false,
+      rules: {
+        title: [{ required: true, message: '请输入券名称', trigger: 'blur' }],
+        type: [{ required: true, message: '请输入券类型', trigger: 'blur' }],
+        quantity: [
+          { required: true, message: '请输入券发放数量', trigger: 'blur' },
+          { pattern: /^([1-9]\d*)$/, message: "请输入正确的数量", trigger: ["blur", "change"] }
+        ],
+        discount: [
+          { required: true, message: '请输入券面值', trigger: 'blur' },
+          { pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的面值,最多两位小数", trigger: ["blur", "change"] }
+        ],
+        channelSharedRate: [
+          { required: true, message: '请输入门店默认承担比例', trigger: 'blur' },
+          { pattern: /^100$|^(\d|[1-9]\d)(\.\d+)*$/, message: "请输入正确的百分比数字", trigger: ["blur", "change"] }
+        ],
+        minOrderAmt: [
+          { required: true, message: '请输入最低消费金额', trigger: 'blur' },
+          { pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }
+        ],
+        dueDays: [
+          { required: true, message: '请输入有效期', trigger: 'blur' },
+          { pattern: /^([1-9]\d*)$/, message: "请输入正确的数字", trigger: ["blur", "change"] }
+        ],
+        description: [{ required: false, message: '请输入使用说明', trigger: 'blur' }],
+        useArea: [{ required: true, message: '请选择使用范围', trigger: 'blur' }]
+      }
+    }
+  },
+  created() {
+    if (this.id) {
+      getCouponDetail(this.id).then(res => {
+        const { couponId, title, quantity, discount, channelSharedRate, minOrderAmt, dueDays, description, ticketBoxList } = res.data
+        this.addData = {
+          type: JSON.parse(res.data.type).value,
+          useArea: JSON.parse(res.data.useArea).value,
+          discount: accDiv(discount, 100),
+          minOrderAmt: accDiv(minOrderAmt, 100),
+          couponId, title, quantity, channelSharedRate, dueDays, description, ticketBoxList
+        }
+      })
+    }
+  },
+  methods: {
+    update() {
+      this.$refs.addItem.validate((valid, items) => {
+        if (valid) {
+          this.addIng = true
+          if(this.addData.useArea === 1) {
+            this.addData.boxIds = this.addData.ticketBoxList.map(item => { return item.boxId }).toString()
+          }
+          const { discount, minOrderAmt, ...rest } = this.addData
+          addCoupon({...rest, ...{ discount: accMul(discount, 100), minOrderAmt: accMul(minOrderAmt, 100),}}).then(res => {
+            this.addIng = false
+            if (res.code === 0) {
+              this.$message({
+                message: this.addData.couponId ? '修改成功!' : '添加成功!',
+                type: 'success'
+              })
+              this.$store.dispatch('tagsView/delView', this.$route)
+              this.$router.go(-1)
+            }
+          }).catch(() => {
+            this.addIng = false
+          })
+        } else {
+          if (items && Object.keys(items).length > 0) {
+            this.$message({
+              message: items[Object.keys(items)[0]][0].message,
+              type: 'warning'
+            })
+          }
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tip {
+  font-size: 12px;
+  color: #888;
+}
+</style>

+ 125 - 0
src/views/business/coupon/components/selectTicket.vue

@@ -0,0 +1,125 @@
+<template>
+  <el-dialog title="选择盲票" :visible.sync="selectShow" width="800px" :close-on-click-modal="false" :destroy-on-close="true">
+    <el-form :model="queryParams" ref="queryForm" label-width="80px" size="small">
+      <el-row :gutter="10">
+        <el-col :span="7">
+          <el-form-item label="盲票名称">
+            <el-input
+              v-model="queryParams.title"
+              placeholder="请输入盲票名称"
+              clearable
+              @keyup.enter.native="queryParams.pageNum = 1;getList()"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="7">
+          <el-form-item label="盲票类型">
+            <el-select v-model="queryParams.type" placeholder="请选择盲票类型" clearable>
+              <el-option label="全部" value="" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="7">
+          <el-form-item label="上架状态">
+            <el-select v-model="queryParams.status" placeholder="请选择上架状态" clearable>
+              <el-option label="全部" value="" />
+              <!-- <el-option label="待上架" value="init" /> -->
+              <el-option label="已上架" value="on" />
+              <el-option label="已下架" value="off" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="3">
+          <el-form-item label-width="0">
+            <el-button type="primary" icon="el-icon-search" @click="queryParams.pageNum = 1;getList()">搜索</el-button>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <el-table v-loading="loading" :data="tableData" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" />
+      <el-table-column label="盲票ID" prop="boxId" width="80" />
+      <el-table-column label="盲票图片" prop="picUrl" align="center" width="80">
+        <template slot-scope="{row}">
+          <a target="_black" v-if="row.picUrl && row.picUrl.split(',').length > 0" :href="`${IMG_URL + row.picUrl}`"><img :src="`${IMG_URL + row.picUrl.split(',')[0]}`" style="max-height: 37px;max-width: 54px"></a>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="盲票名称" prop="title" />
+      <el-table-column label="盲票面值" prop="facePrice" />
+      <el-table-column label="盲票类型" prop="type">
+        <template slot-scope="{row}">
+          {{ JSON.parse(row.type).desc }}
+        </template>
+      </el-table-column>
+      <el-table-column label="盲票状态" prop="status">
+        <template slot-scope="{row}">
+          <el-tag :type="JSON.parse(row.status).value === 'on' ? 'success' : 'info'">{{ JSON.parse(row.status).desc }}</el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList()" />
+    <span slot="footer">
+      <el-button type="info" plain @click="selectShow = false">取消</el-button>
+      <el-button type="primary" @click="update">确定</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+import { getTicketList } from "@/api/business/ticket"
+import { publicFileGetUrl } from "@/api/common"
+export default {
+  name: "SelectTicket",
+  props: {
+    value: {
+      type: Array,
+      default: []
+    }
+  },
+  data() {
+    return {
+      IMG_URL: publicFileGetUrl,
+      loading: false,
+      selectShow: false,
+      tableData: [],
+      queryParams: {},
+      total: 0,
+      selection: []
+    }
+  },
+  methods: {
+    show() {
+      this.getList(true)
+      this.selectShow = true
+      this.selection = this.value
+      // this.$refs.table.toggleRowSelection(this.selection)
+    },
+    getList(reset) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+      if (reset) {
+        this.queryParams = { pageNum: 1, pageSize: 20 }
+      }
+      getTicketList('pageNum='+this.queryParams.pageNum + '&pageSize='+this.queryParams.pageSize+'&', this.queryParams).then(res => {
+        this.loading = false
+        if (res.code === 0) {
+          this.tableData = res.rows
+          
+          this.total = res.total
+        }
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    handleSelectionChange(val) {
+      this.selection = val
+    },
+    update() {
+      this.$emit('input', this.selection)
+      this.selectShow = false
+    }
+  }
+}
+</script>

+ 150 - 0
src/views/business/coupon/index.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="app-container coupon-list">
+    <el-form v-show="showSearch" :model="queryParams" ref="queryForm" :inline="true" size="small">
+      <el-form-item label="券名称">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入券名称"
+          clearable
+          @keyup.enter.native="queryParams.pageNum = 1;getList()"
+        />
+      </el-form-item>
+      <el-form-item label="使用场景">
+        <el-select v-model="queryParams.type" placeholder="请选择券状态" clearable @change="queryParams.pageNum = 1;getList()">
+          <el-option label="全部" value="" />
+          <el-option label="盲票购买" :value="1" />
+          <el-option label="门店消费" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态">
+        <el-select v-model="queryParams.status" placeholder="请选择券状态" clearable @change="queryParams.pageNum = 1;getList()">
+          <el-option label="全部" value="" />
+          <el-option label="待上架" value="init" />
+          <el-option label="已上架" value="on" />
+          <el-option label="已下架" value="off" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="queryParams.pageNum = 1;getList()">搜索</el-button>
+        <el-button icon="el-icon-refresh" @click="getList(true)">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="10">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="$router.push('/coupon/add')"
+          v-hasPermi="['business:coupon:add']"
+        >生成优惠券</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+    <el-table v-loading="loading" :data="tableData">
+      <el-table-column label="券ID" prop="couponId" width="80" />
+      <el-table-column label="券名称" prop="title" />
+      <el-table-column label="券金额" prop="discount">
+        <template slot-scope="{row}">
+          ¥{{$numberFormat(row.discount)}}
+        </template>
+      </el-table-column>
+      <el-table-column label="使用场景" prop="type">
+        <template slot-scope="{row}">
+          {{ JSON.parse(row.type).desc }}
+        </template>
+      </el-table-column>
+      <el-table-column label="已领取数量" prop="quantity" />
+      <el-table-column label="有效期限(天)" prop="dueDays">
+        <template slot-scope="{row}">
+          领取后{{ row.dueDays }}天内有效
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" prop="status">
+        <template slot-scope="{row}">
+          <el-tag :type="JSON.parse(row.status).value === 'on' ? 'success' : 'info'">{{ JSON.parse(row.status).desc }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column fixed="right" align="right" label="操作" width="150">
+        <template slot-scope="{row}">
+          <el-button v-hasPermi="['business:coupon:query']" type="text" @click="$router.push('/coupon/view?id=' + row.couponId)">查看</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'init'" v-hasPermi="['business:coupon:edit']" type="text" @click="$router.push('/coupon/edit?id=' + row.couponId)">编辑</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'off' || JSON.parse(row.status).value === 'init'" v-hasPermi="['business:coupon:on']" type="text" @click="setStatus(row, 'on')">上架</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'on'" v-hasPermi="['business:coupon:off']" type="text" @click="setStatus(row, 'off')">下架</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'init'" v-hasPermi="['business:coupon:remove']" class="del" type="text" @click="del(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList()" />
+  </div>
+</template>
+<script>
+import { getCouponList, delCoupon, setCouponStatus } from '@/api/business/coupon'
+import { accDiv } from '@/utils/util'
+export default {
+  name: 'CouponList',
+  data() {
+    return {
+      loading: false,
+      showSearch: true,
+      tableData: [],
+      queryParams: {},
+      total: 0
+    }
+  },
+  created() {
+    this.getList(true)
+  },
+  methods: {
+    getList(reset) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+      if (reset) {
+        this.queryParams = { pageNum: 1, pageSize: 20 }
+      }
+      getCouponList('pageNum='+this.queryParams.pageNum + '&pageSize='+this.queryParams.pageSize+'&', this.queryParams).then(res => {
+        this.loading = false
+        if (res.code === 0) {
+          this.tableData = res.rows
+          this.total = res.total
+        }
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    setStatus(item, status) {
+      this.$confirm(`确认${status === 'on' ? '上架' : '下架'}券 “${item.title}” 吗?`, `${status === 'on' ? '上架' : '下架'}券`, {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        setCouponStatus({
+          couponId: item.couponId,
+          status
+        }).then(res => {
+          if (res.code === 0) {
+            this.$message.success('操作已完成!')
+            this.getList()
+          }
+        })
+      })
+    },
+    del(item) {
+      this.$confirm(`确认删除券 “${item.title}” 吗?`, '删除券', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        delCoupon(item.couponId).then(res => {
+          if (res.code === 0) {
+            this.$message.success('操作已完成!')
+            this.getList()
+          }
+        })
+      })
+    }
+  }
+}
+</script>

+ 232 - 0
src/views/business/goods/add.vue

@@ -0,0 +1,232 @@
+<template>
+  <div class="app-container goods-add">
+    <el-divider content-position="left">商品信息</el-divider>
+    <el-form ref="addItem" :rules="rules" :model="addData" label-width="120px">
+      <el-row :gutter="40" style="width: 600px">
+        <el-col :span="23">
+          <el-form-item label="商品名称:" prop="title">
+            <el-input v-model="addData.title" placeholder="请输入商品名称"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="23">
+          <el-form-item label="商品主图:" prop="picUrl">
+            <Upload v-model="mainPicUrl" :limit="10" />
+            <br>
+            <div class="tip">第一张图片将作为商品列表图片,最多上传10张,多张图片之间可随意调整位置,支持jpg、png格式上传,建议使用尺寸800x800像素以上、大小不超过1M的正方形图片;</div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="40" style="width: 500px">
+        <el-col :span="23">
+          <el-form-item label="支持盲豆兑换:" prop="exchangeShow">
+            <el-switch
+              v-model="addData.exchangeShow"
+              :active-value="1"
+              :inactive-value="0"
+            />
+            <div class="tip">关闭则不再兑换大厅显示,不支持盲豆兑换。</div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-divider content-position="left">价格库存</el-divider>
+      <el-row>
+        <el-col :span="23">
+          <el-form-item label="启用多SKU:" prop="multiSku">
+            <el-switch
+              v-model="addData.multiSku"
+              :active-value="1"
+              :inactive-value="0"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="22">
+          <el-form-item prop="skuList">
+            <Spec ref="spec" @valid="update" :multiSku="addData.multiSku" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      
+      <el-row v-if="addData.multiSku === 0" :gutter="40" style="width: 600px">
+        <el-col :span="23">
+          <el-form-item label="价格:" prop="value">
+            <el-input v-model="addData.value" type="number" placeholder="请输入商品价格">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="23">
+          <el-form-item label="兑换盲豆数量:" prop="value">
+            <el-input :value="(addData.value * 10).toFixed(0)" type="number" readonly disabled>
+              <template slot="append">盲豆</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="23">
+          <el-form-item label="采购价格:" prop="cost">
+            <el-input v-model="addData.cost" type="number" placeholder="请输入商品采购价格">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="23">
+          <el-form-item label="库存:" prop="quantity">
+            <el-input v-model="addData.quantity" type="number" placeholder="请输入商品库存">
+              <template slot="append">件</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- <el-divider content-position="left">商品详情</el-divider> -->
+      <el-row>
+        <el-col :span="22">
+          <el-form-item label="商品详情" prop="description">
+            <TinyEditor v-model="addData.description" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <br>
+    <el-row>
+      <el-col :span="24" style="text-align: center">
+        <el-button type="info" @click="$router.replace('/goods/list')">取消</el-button>
+        <el-button type="primary" @click="updateItem()">保存</el-button>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script>
+import Upload from '@/components/ImageUpload'
+import Spec from './components/spec'
+import TinyEditor from '@/components/TinyEditor'
+import { getGoodsDetail, addGoods } from '@/api/business/goods'
+import { publicFileGetUrl } from "@/api/common"
+import { accDiv, accMul } from '@/utils/util'
+export default {
+  components: {
+    TinyEditor,
+    Upload,
+    Spec
+  },
+  data() {
+    return {
+      IMG_URL: publicFileGetUrl,
+      id: this.$route.query.id,
+      addData: {
+        multiSku: 0,
+        description: ''
+      },
+      rules: {
+        title: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
+        picUrl: [{ required: true, message: '请上传商品图片', trigger: 'change' }],
+        exchangeShow: [{ required: true, message: '请选择是否支持盲豆兑换', trigger: 'change' }],
+        multiSku: [{ required: true, message: '请选择SKU类型', trigger: 'change' }],
+        value: [
+          { required: true, message: '请输入商品价格', trigger: 'blur' },
+          { pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }
+        ],
+        exchangePrice: [
+          { required: true },
+          // { pattern: /^([1-9]\d*)$/, message: "请输入正确的数字", trigger: ["blur", "change"] }
+        ],
+        cost: [
+          { required: false, message: '请输入采购价格', trigger: 'blur' },
+          { pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }
+        ],
+        quantity: [
+          { required: true, message: '请输入库存', trigger: 'blur' },
+          { pattern: /^([1-9]\d*)$/, message: "请输入正确的数字", trigger: ["blur", "change"] }
+        ],
+        description: [{ required: true, message: '请输入商品详情', trigger: 'blur' }]
+      }
+    }
+  },
+  computed: {
+    mainPicUrl: {
+      get() {
+        return this.addData.picUrl ? this.addData.picUrl.split(',').map(item => {
+          return {
+            fileName: item
+          }
+        }) : []
+      },
+      set(val) {
+        this.$set(this.addData, 'picUrl', val.map(item => { return item.fileName }).toString())
+      }
+    }
+  },
+  created() {
+    if (this.id) {
+      getGoodsDetail(this.id).then(res => {
+        const { goodsId, title, picUrl, exchangeShow, multiSku, value, exchangePrice, cost, quantity, description, skuList } = res.data
+        this.addData = {
+          value: accDiv(value, 100),
+          cost: cost == '' || cost == null ? '' : accDiv(cost, 100),
+          goodsId, title, picUrl, exchangeShow, multiSku, exchangePrice, quantity, description, skuList
+        }
+        if (multiSku && skuList instanceof Array && skuList.length > 0) {
+          this.$nextTick(() => {
+            this.$refs.spec.setSkuList(skuList)
+          })
+        }
+      })
+    }
+  },
+  methods: {
+    updateItem() {
+      if (this.addData.multiSku == 1) {
+        this.$refs.spec.getSkuList()
+      } else {
+        this.update()
+      }
+    },
+    update(skuList) {
+      this.$refs.addItem.validate((valid, items) => {
+        if (valid) {
+          this.addData.skuList = skuList
+          const { value, cost, description, ...rest } = this.addData
+          addGoods({...rest, ...{
+            value: accMul(value, 100),
+            cost: cost == '' || cost == null ? '' : accMul(cost, 100),
+            exchangePrice: accMul(value, 10),
+            description: encodeURI(description)
+          }}).then(res => {
+            if (res.code === 0) {
+              this.$message({
+                message: this.addData.goodsId ? '修改成功!' : '添加成功!',
+                type: 'success'
+              })
+              this.$store.dispatch('tagsView/delView', this.$route)
+              this.$router.go(-1)
+            }
+          })
+        } else {
+          if (items && Object.keys(items).length>0) {
+            this.$message({
+              message: items[Object.keys(items)[0]][0].message,
+              type: 'warning'
+            })
+          }
+        }
+      })
+    }
+  }
+}
+</script>
+<style lang="scss">
+.goods-add {
+  .tip {
+    font-size: 12px;
+    color: #999;
+  }
+  .el-divider {
+    .is-left {
+      color: #409EFF;
+      font-size: 20px;
+      font-weight: bolder;
+      left: 10px;
+    }
+  }
+}
+</style>

+ 335 - 0
src/views/business/goods/components/spec.vue

@@ -0,0 +1,335 @@
+<template>
+  <div v-if="multiSku === 1" style="padding: 16px 0px">
+    <el-form ref="spec" :model="{ specList }" :inline="false" label-width="80px" @submit.native.prevent>
+      <div v-for="(spec, index) in specList" :key="index" style="margin-bottom: 20px">
+        <el-row style="margin-bottom: 10px">
+          <el-col :span="8">
+            <el-form-item :label="`规格项${index + 1}`" :prop="`specList.${index}.name`" :rules="{ required: true, message: '请输入规格名称', trigger: 'blur' }">
+              <el-input v-model="spec.name" :disabled="!spec.edit" @keyup.enter.native="checkSpecName(spec)" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="16">
+            <el-button v-if="spec.edit" type="text" style="margin-left: 10px" @click="checkSpecName(spec)">确定</el-button>
+            <template v-else>
+              <el-button type="text" style="margin-left: 10px" @click="spec.edit = true">编辑</el-button>
+              <el-button type="text" class="del" style="margin-left: 10px" @click="specList.splice(index, 1)">删除</el-button>
+            </template>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-form-item :prop="`specList.${index}.vals`">
+            <el-col :span="24">
+              <el-tag v-for="(tag, index) in spec.vals" :key="index" closable style="margin-right: 10px" @close="spec.vals.splice(index, 1)">{{ tag }}</el-tag>
+              <el-input v-if="spec.editVals" v-model="specValTmp" v-focus style="display: inline-block;width: 120px; margin-right: 10px" size="mini" @keyup.enter.native="checkSpecVal(spec, `specList.${index}.vals`)" />
+              <el-button v-if="!spec.edit&&!spec.editVals" type="text" style="margin-left: 10px" icon="el-icon-plus" size="small" @click="spec.editVals = true">添加规格值</el-button>
+              <el-button v-if="spec.editVals" type="text" @click="checkSpecVal(spec, `specList.${index}.vals`)">确认</el-button>
+              <el-button v-if="spec.editVals" type="text" @click="spec.editVals = false;specValTmp = ''">取消</el-button>
+            </el-col>
+          </el-form-item>
+        </el-row>
+      </div>
+    </el-form>
+    <el-button type="primary" size="small" plain @click="specList.push({ name: '', vals: [], edit: true, editVals: false })" :disabled="!!specList.find(item => item.edit)" style="margin-bottom: 10px">添加规格</el-button>
+    <el-button type="primary" size="small" style="margin-bottom: 10px;margin-left: 10px" :disabled="specList.length === 0 || !!specList.find(item => item.edit)" @click="genSku">{{ skuList.length > 0 ? '重新生成SKU表格' : '生成SKU表格' }}</el-button>
+    <br>
+    <el-form ref="sku" :model="{ skuList }" :inline="false">
+      <table v-if="skuList instanceof Array && skuList.length > 0" class="spec-table" border="1" bordercolor="#CCC">
+        <tr>
+          <th :colspan="specListTmp.length">商品规格</th>
+          <th rowspan="2" class="required"><span>*</span>SKU主图</th>
+          <th rowspan="2" class="required"><span>*</span>名称</th>
+          <th rowspan="2" class="required"><span>*</span>价格(元)</th>
+          <th rowspan="2" class="required"><span>*</span>兑换盲豆数量</th>
+          <th rowspan="2">采购价格(元)</th>
+          <th rowspan="2" class="required"><span>*</span>库存</th>
+          <th rowspan="2">启用</th>
+        </tr>
+        <tr>
+          <th v-for="(spec, index) in specListTmp" :key="index" style="width: 80px">{{ spec.name }}</th>
+        </tr>
+        <!-- k1:v1;k2:v2 -->
+        <tr v-for="(sku, index) in skuList" :key="index">
+          <td v-for="(spec, i) in specListTmp" :key="i" style="min-width: 50px">
+            {{ formatObj(sku.properties)[spec.name] }}
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.picUrlArr`" :rules="{ required: true, message: '请上传SKU图片', trigger: ['blur', 'change'] }">
+              <Upload v-model="sku.picUrlArr" :limit="1" :low="true" style="width: 100%" @change="$refs.sku.validateField([`skuList.${index}.picUrlArr`])" />
+            </el-form-item>
+          </td>
+          <td class="pd">
+            <el-form-item :prop="`skuList.${index}.name`" :rules="{ required: true, message: '名称不能为空', trigger: 'blur' }">
+              <el-input v-model="sku.name" />
+            </el-form-item>
+          </td>
+          <td class="pd">
+            <el-form-item :prop="`skuList.${index}.valueY`" :rules="valueYRules">
+              <el-input v-model="sku.valueY" />
+            </el-form-item>
+          </td>
+          <td>
+            <el-form-item :prop="`skuList.${index}.valueY`" :rules="exchangeRules">
+              <p>{{ (sku.valueY * 10).toFixed(0) }}</p>
+            </el-form-item>
+          </td>
+          <td class="pd">
+            <el-form-item :prop="`skuList.${index}.costY`" :rules="costYRules">
+              <el-input v-model="sku.costY" />
+            </el-form-item>
+          </td>
+          <td class="pd">
+            <el-form-item :prop="`skuList.${index}.quantity`" :rules="{ required: true, message: '请输入库存', trigger: 'blur' }">
+              <el-input-number v-model="sku.quantity" :min="0" @change="$event === 0 ? sku.status = false : sku.status = true" />
+            </el-form-item>
+          </td>
+          <td style="padding: 0px 10px">
+            <el-form-item :prop="`skuList.${index}.status`">
+              <el-switch v-model="sku.status" active-color="#13ce66" inactive-color="#ff4949" @change="!$event ? sku.quantity = 0 : sku.quantity = 1" />
+            </el-form-item>
+          </td>
+        </tr>
+      </table>
+    </el-form>
+  </div>
+</template>
+<script>
+import Upload from '@/components/ImageUpload'
+import { accMul, accDiv } from '@/utils/util'
+export default {
+  name: 'Spec',
+  components: {
+    Upload
+  },
+  props: {
+    multiSku: {
+      type: Number,
+      default: 0
+    },
+    sku: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      specList: [],
+      specListTmp: [],
+      skuList: [],
+      specValTmp: '',
+      specifications: [],
+      valueYRules: [{ required: true, message: '请输入价格', trigger: 'blur' },{ pattern: /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"]}],
+      costYRules: [{ required: false, message: '请输入采购价格', trigger: 'blur' },{ pattern: /^([1-9]\d*(\.\d{1,2}\d[0])?|0|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/, message: "请输入正确的金额,最多两位小数", trigger: ["blur", "change"] }],
+      exchangeRules: [{ required: true, trigger: 'blur' }]
+    }
+  },
+  directives: {
+    focus: {
+      inserted: function (el) {
+        el.querySelector('input').focus()
+      }
+    }
+  },
+  methods: {
+    setSkuList(sku) {
+      let specObj = {}
+      sku.forEach(item => {
+        item.properties.split(';').forEach(p => {
+          const key = p.split(':')[0]
+          // console.log(specObj[key] instanceof Array)
+          if (!specObj[key]) {
+            specObj[key] = []
+            console.log(specObj[key])
+          }
+          specObj[key].push(p.split(':')[1])
+        })
+      })
+      this.specList = Object.keys(specObj).map(key => {
+        return {
+          name: key,
+          vals: [...new Set(specObj[key])],
+          edit: false,
+          editVals: false
+        }
+      })
+      this.specListTmp = JSON.parse(JSON.stringify(this.specList))
+      this.skuList = sku.map(item => {
+        this.$set(item, 'valueY', accDiv(item.value, 100))
+        this.$set(item, 'costY', item.cost == '' ? '' : accMul(item.cost, 100) )
+        this.$set(item, 'picUrlArr', item.picUrl.split(',').map(item => { return { fileName: item }}))
+        this.$set(item, 'status', !!item.quantity)
+        return item
+      })
+    },
+    formatObj(properties) {
+      if (properties) {
+        let obj = {}
+        properties.split(';').forEach(item => {
+          obj[item.split(':')[0]] = item.split(':')[1]
+        })
+        return obj
+      }
+      return {}
+    },
+    checkSpecVal(spec, prop) {
+      if (!this.specValTmp) {
+        this.$message.warning('请输入规格值')
+        return
+      }
+      if (spec.vals.find(item => item === this.specValTmp)) {
+        this.$message.warning('规格值已存在')
+        return 
+      }
+      spec.vals.push(this.specValTmp)
+      this.specValTmp = ''
+      spec.editVals = false
+      this.$refs.spec.validateField([prop])
+    },
+    checkSpecName(spec) {
+      if (this.specList.filter(item => item.name === spec.name).length > 1) {
+        this.$message.warning('规格名已存在')
+      } else {
+        spec.edit = false
+      }
+    },
+    genSku() {
+      this.$refs.spec.validate((valid, items) => {
+        if (valid) {
+          this.genSkuList()
+        }
+      })
+    },
+    genSkuList() {
+      let specificationsTmp = []
+      this.specList.forEach(spec => {
+        spec.vals.forEach(val => {
+          specificationsTmp.push({
+            specification: spec.name,
+            value: val
+          })
+        })
+      })
+      var specValues = []
+      var spec = specificationsTmp[0].specification
+      var values = []
+      values.push(0)
+
+      for (var i = 1; i < specificationsTmp.length; i++) {
+        const aspec = specificationsTmp[i].specification
+
+        if (aspec === spec) {
+          values.push(i)
+        } else {
+          specValues.push(values)
+          spec = aspec
+          values = []
+          values.push(i)
+        }
+      }
+      specValues.push(values)
+      var productsIndex = 0
+      var skuList = []
+      var combination = []
+      var n = specValues.length
+      for (var s = 0; s < n; s++) {
+        combination[s] = 0
+      }
+      var index = 0
+      var isContinue = false
+      do {
+        var specifications = []
+        var properties = ''
+        for (var x = 0; x < n; x++) {
+          var z = specValues[x][combination[x]]
+          specifications.push(specificationsTmp[z].value)
+          properties = `${properties ? (properties + ';') : ''}${specificationsTmp[z].specification}:${specificationsTmp[z].value}`
+        }
+        skuList[productsIndex] = {
+          idx: productsIndex,
+          name: specifications.toString(),
+          picUrlArr: [],
+          valueY: 0.00,
+          costY: '',
+          quantity: 0,
+          status: true,
+          properties
+        }
+        productsIndex++
+
+        index++
+        combination[n - 1] = index
+        for (var j = n - 1; j >= 0; j--) {
+          if (combination[j] >= specValues[j].length) {
+            combination[j] = 0
+            index = 0
+            if (j - 1 >= 0) {
+              combination[j - 1] = combination[j - 1] + 1
+            }
+          }
+        }
+        isContinue = false
+        for (var p = 0; p < n; p++) {
+          if (combination[p] !== 0) {
+            isContinue = true
+          }
+        }
+      } while (isContinue)
+      this.skuList = skuList
+      this.specListTmp = JSON.parse(JSON.stringify(this.specList))
+    },
+    getSkuList() {
+      if (this.skuList.length === 0) {
+        this.$message.warning('请完善规格并生成sku表格')
+        return false
+      }
+      this.$refs.sku.validate((valid, items) => {
+        if (valid) {
+          this.skuList.forEach(item => {
+            const { valueY, costY } = item
+            item.value = accMul(valueY, 100)
+            item.exchangePrice = accDiv(item.value, 10)
+            item.cost = costY == '' ? '' : accMul(costY, 100)
+            item.picUrl = item.picUrlArr.map(item => { return item.fileName }).toString()
+          })
+          this.$emit('valid', this.skuList)
+        } else {
+          if (items && Object.keys(items).length>0) {
+            this.$message({
+              message: items[Object.keys(items)[0]][0].message,
+              type: 'warning'
+            })
+          }
+        }
+      })
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.spec-table {
+  border-collapse: collapse;
+  width: 100%;
+  line-height: 32px;
+  color: #606266;
+  font-size: 14px;
+  th {
+    background-color: #ECECEC;
+  }
+  td {
+    text-align: center;
+  }
+  .pd {
+    padding: 15px;
+  }
+  .required {
+    position: relative;
+    span {
+      position: relative;
+      top: -6px;
+      left: 2px;
+      font-size: 10px;
+      color: red;
+    }
+  }
+  
+}
+</style>

+ 180 - 0
src/views/business/goods/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div class="app-container coupon-list">
+    <el-form v-show="showSearch" :model="queryParams" ref="queryForm" :inline="true" size="small">
+      <el-form-item label="商品名称">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入商品名称"
+          clearable
+          @keyup.enter.native="queryParams.pageNum = 1;getList()"
+        />
+      </el-form-item>
+      <el-form-item label="商品ID">
+        <el-input
+          v-model="queryParams.goodsId"
+          placeholder="请输入商品名称"
+          clearable
+          @keyup.enter.native="queryParams.pageNum = 1;getList()"
+        />
+      </el-form-item>
+      <el-form-item label="上架状态">
+        <el-select v-model="queryParams.status" placeholder="请选择商品状态" clearable @change="queryParams.pageNum = 1;getList()">
+          <el-option label="全部" value="" />
+          <el-option label="待上架" value="init" />
+          <el-option label="已上架" value="on" />
+          <el-option label="已下架" value="off" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="采购价格">
+        <el-input v-model="queryParams.minCost" placeholder="最低采购价格" clearable @keyup.enter.native="queryParams.pageNum = 1;getList()" />
+      </el-form-item>
+      <el-form-item label="-">
+        <el-input v-model="queryParams.maxCost" placeholder="最高采购价格" clearable @keyup.enter.native="queryParams.pageNum = 1;getList()" />
+      </el-form-item>
+      <el-form-item label="价格">
+        <el-input v-model="queryParams.minValue" placeholder="最低价格" clearable @keyup.enter.native="queryParams.pageNum = 1;getList()" />
+      </el-form-item>
+      <el-form-item label="-">
+        <el-input v-model="queryParams.maxValue" placeholder="最高价格" clearable @keyup.enter.native="queryParams.pageNum = 1;getList()" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="queryParams.pageNum = 1;getList()">搜索</el-button>
+        <el-button icon="el-icon-refresh" @click="getList(true)">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="10">
+        <el-button v-hasPermi="['business:goods:add']" type="primary" icon="el-icon-plus" size="mini" @click="$router.push('/goods/add')">添加商品</el-button>
+        <!-- <el-button v-hasPermi="['business:goods:on']" type="primary" plain size="mini">上架</el-button> -->
+        <!-- <el-button v-hasPermi="['business:goods:off']" type="primary" plain size="mini">下架</el-button> -->
+        <!-- <el-button v-hasPermi="['business:goods:remove']" type="danger" plain size="mini">删除</el-button> -->
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+    <el-table v-loading="loading" :data="tableData">
+      <el-table-column label="商品ID" prop="goodsId" width="60" />
+      <el-table-column label="商品图片" prop="picUrl" align="center">
+        <template slot-scope="{row}">
+          <div v-if="row.picUrl">
+            <el-image
+              style="width: 100px; height: 100px"
+              :src="IMG_URL + row.picUrl.split(',')[0]"
+              :preview-src-list="row.picUrl.split(',').map(item => { return IMG_URL + item })"
+            />
+          </div>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="商品名称" prop="title" />
+      <el-table-column label="价格" prop="value">
+        <template slot-scope="{row}">
+          ¥{{$numberFormat(row.value)}}
+        </template>
+      </el-table-column>
+      <el-table-column label="采购价格" prop="cost">
+        <template slot-scope="{row}">
+          ¥{{$numberFormat(row.cost)}}
+        </template>
+      </el-table-column>
+      <el-table-column label="兑换盲豆数量" prop="exchangePrice" />
+      <el-table-column label="销量" prop="exchangedQty" />
+      <el-table-column label="库存" prop="quantity" />
+      <el-table-column label="状态" prop="status">
+        <template slot-scope="{row}">
+          <el-tag :type="JSON.parse(row.status).value === 'on' ? 'success' : 'info'">{{ JSON.parse(row.status).desc }}</el-tag>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="排序" prop="sort" /> -->
+      <el-table-column fixed="right" align="right" label="操作" width="140">
+        <template slot-scope="{row}">
+          <!-- <el-button v-hasPermi="['business:goods:query']" type="text">查看</el-button> -->
+          <el-button v-hasPermi="['business:goods:edit']" type="text" @click="$router.push('/goods/edit?id=' + row.goodsId)">编辑</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'off' || JSON.parse(row.status).value === 'init'" v-hasPermi="['business:goods:on']" type="text" @click="setStatus(row, 'on')">上架</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'on'" v-hasPermi="['business:goods:off']" type="text" @click="setStatus(row, 'off')">下架</el-button>
+          <el-button v-if="JSON.parse(row.status).value === 'init'" v-hasPermi="['business:coupon:remove']" class="del" type="text" @click="del(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList()" />
+  </div>
+</template>
+<script>
+import { publicFileGetUrl } from "@/api/common"
+import { getGoodsList, delGoods, setGoodsStatus } from '@/api/business/goods'
+import { accMul } from '@/utils/util'
+export default {
+  name: 'GoodsList',
+  data() {
+    return {
+      IMG_URL: publicFileGetUrl,
+      loading: false,
+      showSearch: true,
+      queryParams: {},
+      tableData: [],
+      total: 0
+    }
+  },
+  created() {
+    this.getList(true)
+  },
+  methods: {
+    getList(reset) {
+      if (this.loading) {
+        return
+      }
+      this.loading = true
+      if (reset) {
+        this.queryParams = { pageNum: 1, pageSize: 20 }
+      }
+      getGoodsList('pageNum='+this.queryParams.pageNum + '&pageSize='+this.queryParams.pageSize+'&', {
+        title: this.queryParams.title,
+        goodsId: this.queryParams.goodsId,
+        status: this.queryParams.status,
+        minCost: accMul(this.queryParams.minCost, 100),
+        maxCost: accMul(this.queryParams.maxCost, 100),
+        minValue: accMul(this.queryParams.minValue, 100),
+        maxValue: accMul(this.queryParams.maxValue, 100)
+      }).then(res => {
+        this.loading = false
+        if (res.code === 0) {
+          this.tableData = res.rows
+          this.total = res.total
+        }
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    setStatus(item, status) {
+      this.$confirm(`确认${status === 'on' ? '上架' : '下架'}商品 “${item.title}” 吗?`, `${status === 'on' ? '上架' : '下架'}商品`, {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        setGoodsStatus({
+          goodsId: item.goodsId,
+          status
+        }).then(res => {
+          if (res.code === 0) {
+            this.$message.success('操作已完成!')
+            this.getList()
+          }
+        })
+      })
+    },
+    del(item) {
+      this.$confirm(`确认删除商品 “${item.title}” 吗?`, '删除商品', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        delGoods(item.goodsId).then(res => {
+          if (res.code === 0) {
+            this.$message.success('操作已完成!')
+            this.getList()
+          }
+        })
+      })
+    }
+  }
+}
+</script>

+ 554 - 0
src/views/business/salesite/components/Create.vue

@@ -0,0 +1,554 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    width="750px"
+    :append-to-body="true"
+    :before-close="close"
+    :destroy-on-close="true"
+    :close-on-click-modal="false"
+  >
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="120px"
+      label-position="top"
+      style="max-height: 375px;overflow: auto;"
+    >
+    <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+      <el-form-item label="经销商名称" prop="colAddress" style="width: 50%;">
+        <el-input v-model="form.name" placeholder="请输入经销商名称" />
+      </el-form-item>
+       <el-form-item label="手机号码" prop="mobile" style="width: 50%;">
+         <el-input v-model="form.mobile" placeholder="请输入采集器编号"/>
+       </el-form-item>
+    </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="上级渠道" prop="parentId" style="width: 50%;">
+          <el-select
+            v-model="form.parentId"
+            placeholder="请选择上级渠道"
+            style="width: 100%;"
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="(item, index) in channelList"
+              :key="index"
+              :label="item.name"
+              :value="item.channelId"
+            />
+          </el-select>
+        </el-form-item>
+       <el-form-item label="联系人" prop="contact" style="width: 50%;">
+         <el-input v-model="form.contact" placeholder="请输入联系人" />
+       </el-form-item>
+      </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="佣金比例" prop="commRate" style="width: 50%;">
+          <el-input v-model="form.commRate" placeholder="请输入佣金比例" />
+        </el-form-item>
+        <el-form-item label="门店类型" prop="siteType" style="width: 50%;">
+          <el-select
+            v-model="form.siteType"
+            placeholder="请选择门店类型"
+            style="width: 100%;"
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="(item, index) in typeOptions"
+              :key="index"
+              :label="item.desc"
+              :value="item.typeId"
+            />
+          </el-select>
+        </el-form-item>
+      </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+       <el-form-item label="门店名称" prop="siteName" style="width: 100%;">
+         <el-input v-model="form.siteName" placeholder="例如xxxx店, 对用户可见" />
+       </el-form-item>
+      </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+        <el-form-item label="省" prop="provinceId" style="width: 30%;">
+          <el-select
+            v-model="form.provinceId"
+            placeholder="选择省份"
+            clearable
+            size="small"
+            @change="getCityList()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in provinceList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+
+        </el-form-item>
+        <el-form-item label="市" prop="cityId" style="width: 30%;">
+          <el-select
+            v-model="form.cityId"
+            placeholder="选择市"
+            clearable
+            size="small"
+             @change="getAreaList()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in cityList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="区" prop="areaId" style="width: 30%;" >
+          <el-select v-model="form.areaId"
+            placeholder="选择区"
+            clearable
+            size="small"
+            @change="getAreaName()"
+            style="width: 100%;margin-right: 1px;">
+            <el-option
+              v-for="item in areaList"
+              :key="item.areaId"
+              :label="item.areaName"
+              :value="item.areaId"
+            />
+          </el-select>
+        </el-form-item>
+      </flexbox>
+      <flexbox
+          class="ygp-form-items"
+          align="flex-start"
+          justify="flex-start">
+          <el-form-item label="详细地址" prop="adress" style="width: 100%;">
+            <el-input v-model="form.adress" placeholder="请输入详细地址" />
+          </el-form-item>
+      </flexbox>
+      <flexbox
+          class="ygp-form-items"
+          align="flex-start"
+          justify="flex-start">
+         <el-form-item label="营业执照" prop="bizLicensePic" style="width: 50%;">
+          <image-upload
+            :limit="1"
+            :value="form.bizLicensePic"
+            :file-size="50"
+            :is-public="false"
+            :show-file-list="true"
+             @input="bizLicensePicSelect"
+          />
+
+         </el-form-item>
+
+         <el-form-item label="门头照片" prop="doorPic" style="width: 50%;">
+          <image-upload
+            :limit="1"
+            :value="form.doorPic"
+            :file-size="50"
+            :is-public="true"
+            :show-file-list="true"
+             @input="doorPicSelect"
+          />
+
+         </el-form-item>
+
+      </flexbox>
+      <flexbox class="ygp-form-items" align="flex-start" justify="flex-start">
+         <el-form-item label="认证状态" prop="certifyStatus" style="width: 50%;">
+          <el-checkbox v-model="form.certifyStatus">已认证</el-checkbox>
+         </el-form-item>
+      </flexbox>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" :disabled="loading" @click="saveClick">确 定</el-button>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import { addSaleSite, updateSaleSite, getSaleSiteDetail} from "@/api/admin/salesite";
+import { listAllChannel} from "@/api/admin/channel";
+import { listAreaByPid} from "@/api/admin/area";
+import { mapGetters } from "vuex";
+export default {
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    },
+    editId: [Number, String] //  编辑用
+  },
+
+  data() {
+    return {
+      IMG_URL: process.env.VUE_APP_IMG_URL,
+      loading: false,
+      // 上级渠道列表
+      channelList:[],
+      channelCopyList:[],
+      provinceList:[],
+      cityList:[],
+      areaList:[],
+      statusOptions: [],
+      typeOptions:[],
+      // 表单参数
+      form: {
+        name:"",
+        mobile:"",
+        contact:"",
+        commRate:"",
+        provinceId:"",
+        cityId:"",
+        areaId:"",
+        province:"",
+        city:"",
+        area:""
+      },
+      detail: {},
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: "请输入渠道名称", trigger: "blur" }
+        ],
+        mobile: [
+          { required: true, message: "请输入手机号码", trigger: "blur" },
+          {pattern: /^((\+?86)|(\(\+86\)))?1\d{10}$/, message: "请输入正确的手机号码", trigger: "blur" }
+        ],
+        parentId: [{ required: true, message: "请选择上级渠道", trigger: "change" }],
+        contact: [
+          { required: true, message: "请输入联系人", trigger: "blur" }
+        ],
+        commRate: [
+          { required: true, message: "请输入佣金比例", trigger: "blur" },
+          { pattern: /^(([1-9]{1}\d{0,99}))(\.\d{1,2})?$/, message: "佣金比例只能为大于0的数字", trigger:['blur', 'change'] },
+        ],
+        siteType: [
+          { required: true, message: "请选择门店类型", trigger: "change" }
+        ],
+        siteName: [
+          { required: true, message: "请输入门店名称", trigger: "blur" }
+        ],
+        provinceId: [{ required: true, message: "请选择省", trigger: "change" }],
+        cityId: [{ required: true, message: "请选择市", trigger: "change" }],
+        areaId: [{ required: true, message: "请选择区", trigger: "change" }],
+        address: [
+          { required: true, message: "请输入详细地址", trigger: "blur" }
+        ],
+        bizLicensePic: [
+          { required: true, message: "请选择营业执照", trigger: "blur" }
+        ],
+        doorPic: [
+          { required: true, message: "请选择门头照片", trigger: "blur" }
+        ],
+
+      },
+      //招商推广宣传图
+      picture: []
+    };
+  },
+
+  computed: {
+    ...mapGetters(["userInfo"]),
+    title() {
+      return this.editId ? "编辑经销商" : "添加经销商";
+    }
+  },
+
+  created() {
+    //this.getCustItems();
+   // this.getTypeItems();
+    //console.log("watch dialogVisible created == ");
+    // 是编辑
+    if (this.editId) {
+       this.getDetail();
+    }else{
+       this.getProvinceList()
+    }
+    this.getChannelList()
+
+  },
+
+  mounted() {
+    document.body.appendChild(this.$el);
+  },
+
+  destroyed() {
+    // if appendToBody is true, remove DOM node after destroy
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el);
+    }
+  },
+
+  methods: {
+
+    // 获取上级渠道下拉列表
+    getChannelList(){
+      listAllChannel().then(response => {
+        // console.log("getChannelList == "+ JSON.stringify(response.data))
+         this.channelList = this.channelList.concat(response.data || []);
+         this.channelCopyList = this.channelList;
+      });
+    },
+
+
+    // 添加营业执照
+    bizLicensePicSelect(data) {
+      this.form.bizLicensePic = data
+      console.log("bizLicensePicSelect 添加营业执照 data = "+JSON.stringify(data))
+    },
+
+    // 添加门头照片
+    doorPicSelect(data) {
+      this.form.doorPic = data
+      console.log("doorPicSelect 添加门头照片 data = "+JSON.stringify(data))
+    },
+
+    // 省
+    getProvinceList(){
+      this.cityList = []
+      this.areaList = []
+      this.form.cityId = ""
+      this.form.city = ""
+      this.form.areaId = ""
+      this.form.area = ""
+      listAreaByPid(0).then(response => {
+        // console.log("getProvinceList"+JSON.stringify(response))
+         console.log("getProvinceList this.form=="+JSON.stringify(this.form))
+         this.provinceList = response || [];
+          console.log("this.editId && this.form.provinceId == "+(this.editId && this.form.provinceId))
+         if(this.editId && this.detail.provinceId){
+            this.form.provinceId = this.detail.provinceId
+            this.getCityList()
+         }
+      });
+    },
+
+    getProvinceName(){
+      let val = this.form.provinceId
+      if(val){
+        let nameObj = this.provinceList.find(item => {
+              return item.areaId == val;
+        })
+        return nameObj && nameObj.areaName
+      }
+      return ""
+    },
+
+
+    getCityList(){
+      this.cityList = []
+      this.areaList = []
+      this.form.cityId = ""
+      this.form.city = ""
+      this.form.areaId = ""
+      this.form.area = ""
+      var provinceId = this.form.provinceId
+      this.form.province = this.getProvinceName()
+      listAreaByPid(provinceId).then(response => {
+         this.cityList = response || [];
+         if(this.editId && this.detail.cityId){
+            this.form.cityId = this.detail.cityId
+            this.getAreaList()
+         }
+      });
+
+    },
+
+    getCityName(){
+      let val = this.form.cityId
+      if(val){
+        let nameObj = this.cityList.find(item => {
+              return item.areaId == val;
+        })
+        return nameObj && nameObj.areaName
+      }
+      return ""
+    },
+
+    getAreaList(){
+      var cityId = this.form.cityId
+      this.form.city = this.getCityName();
+      listAreaByPid(cityId).then(response => {
+        // console.log("getAreaList"+JSON.stringify(response))
+         this.areaList = response || [];
+         if(this.editId && this.detail.areaId){
+            this.form.areaId = this.detail.areaId
+         }
+      });
+    },
+
+    getAreaName(){
+      let val = this.form.areaId
+      if(val){
+        let nameObj = this.areaList.find(item => {
+              return item.areaId == val;
+        })
+        this.form.area = nameObj && nameObj.areaName
+      }else{
+        this.form.area = "";
+      }
+    },
+
+    dataFilter(val) {
+      this.value = val;
+      if (val) { //val存在
+        this.channelList = this.channelCopyList.filter((item) => {
+          // console.log("dataFilter item"+JSON.stringify(item))
+          if (!!~item.mobile.indexOf(val) || !!~item.mobile.toUpperCase().indexOf(val.toUpperCase())) {
+             return true
+          }
+        })
+      } else { //val为空时,还原数组
+        this.channelList = this.channelCopyList;
+      }
+    },
+
+
+    /**
+     * 获取项目详情
+     */
+    getDetail() {
+      this.loading = true;
+      getSaleSiteDetail(this.editId).then(res => {
+          const data = res.data || {}
+          // console.log("getDetail :" + JSON.stringify(data))
+          this.detail = data
+          this.form.name = data.name
+          this.form.mobile = data.mobile
+          this.form.contact = data.contact
+          this.form.commRate = data.commRate
+          this.loading = false
+          this.getProvinceList()
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+
+
+    saveClick(){
+      // console.log("saveClick tthis.form = "+JSON.stringify(this.form))
+      this.loading = true
+      const subForm = this.$refs["form"]
+      subForm.validate(valid => {
+        if (valid) {
+          this.submitForm(this.form);
+        } else {
+          this.loading = false;
+          // 提示第一个error
+          this.getFormErrorMessage(subForm);
+          return false;
+        }
+      });
+    },
+
+    /**
+     * 保存
+     */
+    submitForm(params) {
+      //  console.log("submitForm =============")
+      if (this.editId) {
+        params.channelId = this.editId;
+      }
+      const request = this.editId ? updateSaleSite : addSaleSite
+      // console.log("submitForm params = "+ JSON.stringify(params))
+      request(params).then(response => {
+        this.loading = false
+        if(this.editId){
+           this.$message.success('更新成功')
+        }else{
+           this.$message.success('新增成功')
+        }
+        this.$emit('saveSuccess')
+        this.close()
+      }).catch(() => {
+        this.loading = false
+      });
+    },
+
+    /**
+     * 关闭窗口
+     */
+    close() {
+      this.$emit("close");
+      this.form = {
+        month: 3,
+        packageType: "1"
+      };
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.tag {
+  margin-right: 15px;
+  width: 90px;
+  text-align: center;
+  cursor: pointer;
+}
+
+.tag-select {
+  background-color: #409eff !important;
+  border-color: #409eff !important;
+  color: #fff !important;
+}
+
+.cover-content-item {
+  width: 220px;
+  float: left;
+  position: relative;
+  .cover-img {
+    width: 200px;
+    height: 100px;
+  }
+  .cover-mark {
+    position: absolute;
+    top: 0px;
+    right: 28px;
+    z-index: 1;
+    color: red;
+    cursor: pointer;
+    visibility: hidden;
+  }
+  .select {
+    visibility: visible !important;
+  }
+}
+
+.dialog-footer {
+  text-align: center;
+}
+</style>
+
+<style lang="scss">
+.ygp-form-items {
+  .el-form-item {
+    padding: 0 5px;
+  }
+  .el-form-item__label {
+    line-height: 1.2;
+    padding-bottom: 8px;
+    word-break: break-all;
+    word-wrap: break-word;
+    color: #333;
+  }
+
+  .el-form-item__error {
+    position: relative;
+    top: auto;
+    left: auto;
+  }
+
+  .el-form-item.is-desc_text {
+    .el-form-item__label {
+      display: none;
+    }
+  }
+}
+</style>

+ 279 - 0
src/views/business/salesite/components/Detail.vue

@@ -0,0 +1,279 @@
+<template>
+ <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    width="750px"
+    :append-to-body="true"
+    :before-close="close"
+    :destroy-on-close="true"
+    :close-on-click-modal="false">
+   <el-form ref="form" label-width="120px" label-position="top" >
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+        <el-form-item label="经销商名称" style="width: 50%; margin-bottom:5px;" >
+          <span :class="loading?'el-icon-loading':''"></span>
+          {{detail.name}}
+        </el-form-item>
+         <el-form-item label="手机号码" style="width: 50%; margin-bottom:5px;" >
+            <span :class="loading?'el-icon-loading':''"></span>
+            {{detail.mobile}}
+         </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="上级渠道" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.parentName}}
+         </el-form-item>
+         <el-form-item label="联系人" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.contact}}
+         </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+          <el-form-item label="佣金比例" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             {{detail.commRate}}%
+          </el-form-item>
+          <el-form-item label="门店类型" style="width: 50%;margin-bottom:5px;" >
+            <span :class="loading?'el-icon-loading':''"></span>
+            <span v-if="detail.siteType">{{detail.siteType && JSON.parse(detail.siteType).desc}}</span>
+            <span v-else>--</span>
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+          <el-form-item label="门店名称" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             <span v-if="detail.siteName">{{detail.siteName}}</span>
+             <span v-else>--</span>
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="所在地区" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.province}} {{detail.city}} {{detail.area}}
+         </el-form-item>
+          <el-form-item label="详细地址" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"> </span>
+             <span v-if="detail.address">{{ detail.address }}</span>
+             <span v-else>--</span>
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="认证状态" style="width: 100%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+           {{detail.certifyStatus && JSON.parse(detail.certifyStatus).desc}}
+         </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+         <el-form-item label="营业执照" style="width: 50%;margin-bottom:5px;" >
+           <span :class="loading?'el-icon-loading':''"></span>
+            <a target="_black" v-if="detail.bizLicensePic" :href="`${IMG_URL + detail.bizLicensePic}`"><img :src="`${IMG_URL + detail.bizLicensePic}`" style="max-height: 37px;max-width: 54px"></a>
+            <span v-else>无</span>
+         </el-form-item>
+          <el-form-item label="门头照片" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"> </span>
+            <a target="_black" v-if="detail.doorPic" :href="`${IMG_URL + detail.doorPic}`"><img :src="`${IMG_URL + detail.doorPic}`" style="max-height: 37px;max-width: 54px"></a>
+            <span v-else>无</span>
+          </el-form-item>
+     </flexbox>
+      <el-divider></el-divider>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+          <el-form-item label="用户数" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>
+             {{detail.userCnt}}
+          </el-form-item>
+          <el-form-item label="订单数" style="width: 50%;margin-bottom:5px;" >
+            <span :class="loading?'el-icon-loading':''"> </span>
+            {{detail.operData && detail.operData.orderCnt}}
+          </el-form-item>
+     </flexbox>
+     <flexbox
+         class="ygp-form-items"
+         align="flex-start"
+         justify="flex-start">
+          <el-form-item label="营业额" style="width: 50%;margin-bottom:5px;" >
+             <span :class="loading?'el-icon-loading':''"></span>{{detail.operData &&detail.operData.saleAmt/100}}
+          </el-form-item>
+          <el-form-item label="佣金" style="width: 50%;margin-bottom:5px;" >
+            <span :class="loading?'el-icon-loading':''">{{detail.operData && detail.operData.commAmt/100}} </span>
+          </el-form-item>
+     </flexbox>
+   </el-form>
+   <div slot="footer" class="dialog-footer">
+     <el-button @click="close">关 闭</el-button>
+   </div>
+ </el-dialog>
+</template>
+<script>
+
+import { mapGetters } from 'vuex'
+import { getSaleSiteDetail} from "@/api/admin/salesite";
+export default {
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    },
+    editId: [Number, String] //  编辑用
+  },
+
+  data() {
+    return {
+      IMG_URL: process.env.VUE_APP_IMG_URL,
+      loading: false,
+      // 详情数据
+      detail: {}
+    }
+  },
+
+  computed: {
+    ...mapGetters(['userInfo']),
+    title() {
+      return '经销商详情'
+    }
+  },
+
+  created() {
+    // 是编辑
+    if (this.editId) {
+      this.getDetail()
+    }
+  },
+
+  mounted() {
+    document.body.appendChild(this.$el)
+  },
+
+  destroyed() {
+    // if appendToBody is true, remove DOM node after destroy
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el)
+    }
+  },
+
+  methods: {
+
+    /**
+     * 获取项目详情
+     */
+    getDetail() {
+      this.loading = true
+      getSaleSiteDetail(this.editId).then(res => {
+          this.loading = false
+          const data = res.data || {}
+          // console.log("getDetail :" + JSON.stringify(data))
+          this.detail = data
+        })
+        .catch(() => {
+          this.loading = false
+        })
+    },
+
+    /**
+     * 关闭窗口
+     */
+    close() {
+      this.$emit('close')
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.tag{
+  margin-right: 15px;
+  width: 90px;
+  text-align: center;
+  cursor: pointer;
+
+}
+
+.tag-select{
+  background-color: #409eff!important;
+  border-color: #409eff!important;
+  color: #fff!important;
+}
+
+.cover-content-item{
+  width: 220px;
+  float: left;
+  position: relative;
+  .cover-img {
+    width: 200px;
+    height: 100px;
+  }
+  .cover-mark {
+    position: absolute;
+    top: 0px;
+    right: 28px;
+    z-index: 1;
+    color: red;
+    cursor: pointer;
+    visibility: hidden;
+  }
+  .select {
+      visibility: visible!important;
+  }
+}
+
+.dialog-footer{
+  text-align: center;
+}
+
+</style>
+
+<style lang="scss">
+
+.el-dialog__body {
+    padding: 15px 20px;
+}
+
+.ygp-form-items {
+   .el-form-item{
+     padding: 0 5px;
+   }
+
+  .el-form-item__label {
+    line-height: 1.2;
+    padding-bottom: 0px;
+    word-break: break-all;
+    word-wrap: break-word;
+    color: #333;
+  }
+
+  .el-form-item__error {
+    position: relative;
+    top: auto;
+    left: auto;
+  }
+
+  .el-form-item.is-desc_text {
+    .el-form-item__label {
+      display: none;
+    }
+  }
+}
+</style>

+ 429 - 0
src/views/business/salesite/index.vue

@@ -0,0 +1,429 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="75px">
+      <el-form-item label="上级渠道" prop="parentId">
+        <el-select
+          v-model="queryParams.parentId"
+          placeholder="请选择上级渠道"
+          style="width: 100%;"
+          filterable
+          clearable
+          :filter-method="dataFilter"
+          @change="handleQuery"
+        >
+          <el-option
+            v-for="(item, index) in channelList"
+            :key="item.channelId"
+            :label="item.name"
+            :value="item.channelId">
+            <div>
+              <span style="float: left;">{{item.name}} </span>
+              <span style="float: right;">{{item.mobile}}</span>
+            </div>
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="经销商名称" prop="name" label-width="90px">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入经销商名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="手机号码" prop="mobile">
+        <el-input
+          v-model="queryParams.mobile"
+          placeholder="请输入手机号码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="地区">
+        <el-select
+          v-model="queryParams.provinceId"
+          placeholder="选择省份"
+          clearable
+          size="small"
+          @change="getCityList()"
+          style="width: 120px;margin-right: 1px;">
+          <el-option
+            v-for="item in provinceList"
+            :key="item.areaId"
+            :label="item.areaName"
+            :value="item.areaId"
+          />
+        </el-select>
+        <el-select
+          v-model="queryParams.cityId"
+          placeholder="选择市"
+          clearable
+          size="small"
+           @change="getAreaList()"
+          style="width: 120px;margin-right: 1px;">
+          <el-option
+            v-for="item in cityList"
+            :key="item.areaId"
+            :label="item.areaName"
+            :value="item.areaId"
+          />
+        </el-select>
+        <el-select v-model="queryParams.areaId" placeholder="选择区" clearable size="small" style="width: 120px;margin-right: 1px;">
+          <el-option
+            v-for="item in areaList"
+            :key="item.areaId"
+            :label="item.areaName"
+            :value="item.areaId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <!-- <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          disabled
+          @click="handleAdd"
+          v-hasPermi="['system:post:add']"
+        >添加经销商</el-button>
+      </el-col> -->
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="siteList" @selection-change="handleSelectionChange">
+     <!-- <el-table-column type="selection" width="55"  /> -->
+      <el-table-column label="经销商编号" prop="channelId" width="100px"/>
+      <el-table-column label="经销商名称" show-overflow-tooltip  prop="name" max-width="100px" />
+      <el-table-column label="手机号码"  prop="mobile" />
+      <el-table-column label="佣金比例" prop="commRate">
+        <template slot-scope="{ row, column }">
+           <span>{{row.commRate}}%</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="上级渠道" prop="parentName" />
+      <el-table-column label="用户数" prop="userCnt" />
+      <el-table-column label="认证状态" prop="certifyStatus" >
+        <template slot-scope="{ row, column }">
+             <span v-if="getValue(row.certifyStatus) == 'y'" style="color: blue;"> {{getDesc(row.certifyStatus)}}</span>
+             <span v-if="getValue(row.certifyStatus) == 'n'" style="color: red;"> {{getDesc(row.certifyStatus)}}</span>
+         </template>
+      </el-table-column>
+      <el-table-column label="状态" key="statusV">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.statusV"
+            active-value="1"
+            inactive-value="2"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleDetail(scope.row)"
+            v-hasPermi="['admin:salesite:read']"
+          >查看</el-button>
+         <!-- <el-button
+            size="mini"
+            type="text"
+            disabled
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['admin:salesite:edit']"
+          >编辑</el-button> -->
+        <!--  <el-button
+            size="mini"
+            type="text"
+            @click="handleStatusChange(scope.row)"
+            v-hasPermi="['admin:salesite:remove']"
+          >
+            <span v-if="getValue(scope.row.status) == 1"> 停用</span>
+            <span v-if="getValue(scope.row.status) == 2"> 启用</span>
+          </el-button> -->
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="pageParams.pageNum"
+      :limit.sync="pageParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 新建 -->
+    <site-create
+      v-if="createShow"
+      :dialog-visible="createShow"
+      :edit-id="editId"
+      @saveSuccess="submitSuccess"
+      @close="hideDialog"
+    />
+
+    <!-- 详情 -->
+    <site-detail
+      v-if="detailShow"
+      :dialog-visible="detailShow"
+      :edit-id="editId"
+      @close="hideDialog"
+    />
+  </div>
+</template>
+
+<script>
+import { listAllChannel} from "@/api/admin/channel";
+import { listAreaByPid} from "@/api/admin/area";
+import { listSaleSite, updateSaleSiteStatus} from "@/api/admin/salesite";
+import SiteCreate from './components/Create'
+import SiteDetail from './components/Detail'
+export default {
+  name: "SaleSiteIndex",
+  components: {
+   SiteCreate,
+   SiteDetail
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 创建编辑
+      createShow: false,
+      // 详情
+      detailShow: false,
+      // 编辑项ID
+      editId: null,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 上级渠道列表
+      channelList:[],
+      channelCopyList:[],
+      provinceList:[],
+      cityList:[],
+      areaList:[],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 经销商表格数据
+      siteList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态数据字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        parentId: "",
+        name: "",
+        mobile: "",
+        provinceId:"",
+        cityId:"",
+        areaId:""
+      },
+      pageParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      // 表单参数
+      form: {},
+
+    };
+  },
+  mounted() {
+    this.getChannelList()
+    this.getProvinceList()
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_normal_disable").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    /** 查询经销商列表 */
+    getList() {
+      this.loading = true;
+      listSaleSite('pageNum='+this.pageParams.pageNum + '&pageSize='+this.pageParams.pageSize+'&',this.queryParams).then(response => {
+        this.siteList = response.rows;
+        this.siteList.forEach(item => {
+            item.statusV = JSON.parse(item.status).value+""
+        })
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 获取上级渠道下拉列表
+    getChannelList(){
+      listAllChannel().then(response => {
+         this.channelList = response.data || [];
+         this.channelCopyList = response.data || [];
+      });
+    },
+
+    // 省
+    getProvinceList(){
+      this.cityList = []
+      this.areaList = []
+      this.queryParams.cityId = ""
+      this.queryParams.areaId = ""
+      listAreaByPid(0).then(response => {
+        // console.log("getProvinceList"+JSON.stringify(response))
+         this.provinceList = response || [];
+      });
+    },
+
+    getCityList(){
+      this.cityList = []
+      this.areaList = []
+      this.queryParams.cityId = ""
+      this.queryParams.areaId = ""
+      var provinceId = this.queryParams.provinceId
+      listAreaByPid(provinceId).then(response => {
+        // console.log("getCityList"+JSON.stringify(response))
+         this.cityList = response || [];
+      });
+    },
+
+    getAreaList(){
+      var cityId = this.queryParams.cityId
+      listAreaByPid(cityId).then(response => {
+        // console.log("getAreaList"+JSON.stringify(response))
+         this.areaList = response || [];
+      });
+    },
+
+
+    dataFilter(val) {
+      this.value = val;
+      if (val) { //val存在
+        this.channelList = this.channelCopyList.filter((item) => {
+          // console.log("dataFilter item"+JSON.stringify(item))
+          if (!!~item.mobile.indexOf(val) || !!~item.mobile.toUpperCase().indexOf(val.toUpperCase())) {
+             return true
+          }
+        })
+      } else { //val为空时,还原数组
+        this.channelList = this.channelCopyList;
+      }
+    },
+
+
+    // 岗位状态字典翻译
+    statusFormat(row, column) {
+      return this.selectDictLabel(this.statusOptions, row.status);
+    },
+
+    getDesc(val) {
+      // console.log("val == "+val);
+      const dataObj = JSON.parse(val);
+      return (dataObj && dataObj.desc) || "";
+    },
+
+    getValue(val) {
+      // console.log("val == "+val);
+      const dataObj = JSON.parse(val);
+      return (dataObj && dataObj.value) || "";
+    },
+
+    submitSuccess(){
+      this.getList();
+    },
+
+    hideDialog(){
+      this.createShow = false
+      this.detailShow = false
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.channelList = this.channelCopyList;
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.queryParams= {
+         parentId: "",
+         name: "",
+         mobile: "",
+         provinceId:"",
+         cityId:"",
+         areaId:""
+      },
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.postId)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    handleDetail(row){
+      this.detailShow = true
+      this.editId = row.channelId
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+       this.createShow = true
+       this.editId = null
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.createShow = true
+      this.editId = row.channelId
+    },
+
+    // 保存后的操作
+    submitSuccess(){
+      this.getList();
+    },
+
+    /**关闭弹窗*/
+    hideDialog(){
+      this.createShow = false
+      this.detailShow = false
+    },
+
+    // 状态修改
+    handleStatusChange(row) {
+      var newStatus = this.getValue(row.status) == 2?1:2
+      let text = this.getValue(row.status) == 2 ? "启用" : "停用";
+      this.$confirm('确认要"' + text + '""' + row.name + '"经销商吗?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          var params={
+            channelId:row.channelId,
+            status: newStatus
+          }
+          return updateSaleSiteStatus(params);
+        }).then(() => {
+          this.msgSuccess(text + "成功");
+          this.getList();
+        }).catch(function() {
+           row.statusV = row.statusV === "1" ? "2" : "1";
+        });
+    }
+  }
+};
+</script>

+ 923 - 0
src/views/business/ticket/create.vue

@@ -0,0 +1,923 @@
+<template>
+  <div class="app-container">
+    <div class="base-info">
+      <div class="base-info-title">基础信息</div>
+      <!-- 基础信息 -->
+      <div class="base-info-form">
+        <el-form :model="form" :rules="rules" ref="form" label-width="100px">
+          <el-form-item label="盲票类型" prop="type">
+            <el-radio-group v-model="form.type" size="small">
+              <el-radio label="online">线上盲票</el-radio>
+              <el-radio label="offline">线下盲票</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="盲票组名称" prop="title">
+            <el-input
+              v-model="form.title"
+              style="width: 587px"
+              size="small"
+              placeholder="请输入盲票组名称"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="图片" prop="picUrl">
+            <image-upload
+              :limit="1"
+              :value="form.picUrl"
+              :file-size="50"
+              :is-public="true"
+              @input="pictureSelect"
+            />
+          </el-form-item>
+          <div class="tip">
+            上传1张图片,支持jpg、png格式上传,建议使用尺寸800x800像素以上、大小不超过1M的正方形图片;
+          </div>
+          <el-form-item label="面值" prop="facePrice">
+            <el-input
+              v-model="form.facePrice"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入面值"
+            >
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+          <el-form-item label="售价" prop="salePrice">
+            <el-input
+              v-model="form.salePrice"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入售价"
+            >
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+          <el-form-item label="盲票总数" prop="quantity">
+            <el-input
+              v-model="form.quantity"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入盲票总数"
+            >
+              <template slot="append">张</template>
+            </el-input>
+          </el-form-item>
+          <el-form-item label="每包张数" prop="pkgUnit">
+            <el-input
+              v-model="form.pkgUnit"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入每包张数"
+            >
+              <template slot="append">张</template>
+            </el-input>
+          </el-form-item>
+          <el-form-item
+            label="采购单价"
+            prop="pkgSalePrice"
+            v-if="form.type == 'offline'"
+          >
+            <el-input
+              v-model="form.pkgSalePrice"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入采购单价"
+            >
+              <template slot="append">元/包</template>
+            </el-input>
+          </el-form-item>
+          <el-form-item label="分佣基数" prop="saleCommRate">
+            <el-input
+              v-model="form.saleCommRate"
+              style="width: 240px"
+              size="small"
+              placeholder="请输入分佣基数"
+            >
+              <template slot="append">%</template>
+            </el-input>
+          </el-form-item>
+          <div class="tip">
+            例如:盲票面值10元,分佣基数90%,经销商佣金比例20%,那么每卖一张票,经销商佣金为10元*90%*20%=1.8元
+          </div>
+        </el-form>
+      </div>
+      <div class="base-info-title">奖级设置</div>
+      <!-- 奖级设置 -->
+      <div class="prize" v-for="(item, index) in awardsList" :key="index">
+        <div class="prize-top">
+          <div>奖级名称:{{ item.name }}</div>
+          <div>奖级:{{ item.sort }}</div>
+          <div>
+            奖级数量:
+            <el-input-number
+              v-model="item.quantity"
+              controls-position="right"
+              @change="handleChangeAll($event, item)"
+              :min="0"
+              size="small"
+            ></el-input-number>
+          </div>
+        </div>
+        <div class="prize-table">
+          <el-table :data="item.prizeList" class="el-table">
+            <el-table-column label="奖品图片" prop="storeName">
+              <template slot-scope="scope">
+                <el-image
+                  style="width: 70px; height: 70px"
+                  :src="scope.row.picUrl"
+                  :preview-src-list="[scope.row.picUrl]"
+                >
+                </el-image>
+              </template>
+            </el-table-column>
+            <el-table-column label="奖品名称" prop="title" />
+            <el-table-column label="奖品类型">
+              <template slot-scope="scope">
+                <div v-if="scope.row.prizeType == 'goods'">商品</div>
+                <div v-if="scope.row.prizeType == 'coupon'">券</div>
+                <div v-if="scope.row.prizeType == 'coin'">盲豆</div>
+              </template>
+            </el-table-column>
+            <!-- <el-table-column label="奖品数量" prop="storeName">
+              <template slot-scope="scope">
+                <div>
+                  <el-input-number
+                    v-model="scope.row.quantity"
+                    controls-position="right"
+                    @change="handleChange($event, index)"
+                    :min="1"
+                    size="small"
+                  ></el-input-number>
+                </div>
+              </template>
+            </el-table-column> -->
+            <el-table-column label="操作" align="center">
+              <template slot-scope="scope">
+                <el-button
+                  size="mini"
+                  type="text"
+                  @click="handleDel(scope.$index, item)"
+                  >删除</el-button
+                >
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div class="prize-btn">
+          <el-dropdown @command="handleCommand($event, index)">
+            <el-button type="primary" size="small">
+              添加奖品<i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="goods">商品</el-dropdown-item>
+              <el-dropdown-item command="coupon">券</el-dropdown-item>
+              <el-dropdown-item command="coin">盲豆</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </div>
+      </div>
+      <!-- 保存 -->
+      <div class="save-btn">
+        <el-button size="small" @click="back"> 取 消 </el-button>
+        <div class="ge"></div>
+        <el-button type="primary" size="small" @click="submitForm">
+          保 存
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 添加商品 -->
+    <el-dialog
+      title="添加实物奖品"
+      width="1000px"
+      :visible.sync="goodsTableVisible"
+      :before-close="close"
+    >
+      <div class="dialog-search">
+        <div>商品名称:</div>
+        <el-input
+          v-model="goodsTitle"
+          placeholder="请输入商品名称"
+          clearable
+          size="small"
+          style="width: 240px"
+          @keyup.enter.native="handleQueryGoods"
+        />
+        <div class="ge"></div>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQueryGoods"
+          >查询</el-button
+        >
+      </div>
+      <el-table
+        v-loading="loading"
+        :data="goodsList"
+        @selection-change="handleSelectionGoods"
+        class="el-table"
+      >
+        <el-table-column
+          type="selection"
+          width="55"
+          align="center"
+          fixed="left"
+        />
+        <el-table-column label="商品ID" prop="goodsId" />
+        <el-table-column label="商品图片">
+          <template slot-scope="{ row }">
+            <div v-if="row.picUrl">
+              <el-image
+                style="width: 100px; height: 100px"
+                :src="row.picUrl.split(',')[0]"
+                :preview-src-list="row.picUrl.split(',')"
+              >
+              </el-image>
+            </div>
+            <p v-else>-</p>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" prop="title" min-width="85" />
+
+        <el-table-column label="商品价格" min-width="85">
+          <template slot-scope="scope">
+            <div>¥{{ $numberFormat(scope.row.value) }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品库存" prop="quantity" width="80" />
+      </el-table>
+      <pagination
+        v-show="goodsTotal > 0"
+        :total="goodsTotal"
+        :page.sync="pageParams.pageNum"
+        :limit.sync="pageParams.pageSize"
+        @pagination="getGoodsList"
+      />
+      <div class="dialog-btn">
+        <el-button size="small" @click="close"> 取 消 </el-button>
+        <div class="ge"></div>
+        <el-button type="primary" size="small" @click="confirmGoods">
+          确 认
+        </el-button>
+      </div>
+    </el-dialog>
+    <!-- 添加卡券 -->
+    <el-dialog
+      title="添加券奖品"
+      width="1000px"
+      :visible.sync="couponTableVisible"
+      :before-close="close"
+    >
+      <div class="dialog-search">
+        <div>券名称:</div>
+        <el-input
+          v-model="couponTitle"
+          placeholder="请输入券名称"
+          clearable
+          size="small"
+          style="width: 240px"
+          @keyup.enter.native="handleQueryCoupon"
+        />
+        <div class="ge"></div>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQueryCoupon"
+          >查询</el-button
+        >
+      </div>
+      <el-table
+        v-loading="loading"
+        :data="couponList"
+        @selection-change="handleSelectionCoupon"
+        class="el-table"
+      >
+        <el-table-column
+          type="selection"
+          width="55"
+          align="center"
+          fixed="left"
+        />
+        <el-table-column label="券ID" prop="couponId" />
+        <el-table-column label="券图片">
+          <template slot-scope="scope">
+            <div>
+              <el-image
+                style="width: 100px; height: 100px"
+                :src="scope.row.picUrl"
+                :preview-src-list="[scope.row.picUrl]"
+              >
+              </el-image>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="券名称" prop="title" min-width="85" />
+        <el-table-column label="使用场景" min-width="85">
+          <template slot-scope="scope">
+            <div>{{ scope.row.type.desc }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column label="券价格" min-width="85">
+          <template slot-scope="scope">
+            <div>¥{{ $numberFormat(scope.row.discount) }}</div>
+          </template>
+        </el-table-column>
+        <el-table-column label="有效期限" min-width="85">
+          <template slot-scope="scope">
+            <div>{{ scope.row.useArea.desc }}</div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="couponTotal > 0"
+        :total="couponTotal"
+        :page.sync="pageParams.pageNum"
+        :limit.sync="pageParams.pageSize"
+        @pagination="getCouponList"
+      />
+      <div class="dialog-btn">
+        <el-button size="small" @click="close"> 取 消 </el-button>
+        <div class="ge"></div>
+        <el-button type="primary" size="small" @click="confirmCoupon">
+          确 认
+        </el-button>
+      </div>
+    </el-dialog>
+    <!-- 添加盲豆 -->
+    <el-dialog
+      title="添加盲豆奖品"
+      :visible.sync="coinTableVisible"
+      :before-close="close"
+    >
+      <el-form
+        :model="coinForm"
+        :rules="coinRules"
+        ref="coinForm"
+        label-width="100px"
+      >
+        <el-form-item label="盲豆数量" prop="quantity">
+          <el-input
+            v-model="coinForm.quantity"
+            size="small"
+            placeholder="请输入盲豆数量"
+          />
+        </el-form-item>
+      </el-form>
+      <div class="dialog-btn">
+        <el-button size="small" @click="close"> 取 消 </el-button>
+        <div class="ge"></div>
+        <el-button type="primary" size="small" @click="confirmCoin">
+          确 认
+        </el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import CustomFieldsMixin from "@/mixins/CustomFields";
+import { getGoodsList } from "@/api/business/goods";
+import { getCouponList } from "@/api/business/coupon";
+import { ticketBoxCreate } from "@/api/business/ticket";
+import { publicFileGetUrl } from "@/api/common";
+import { accMul } from "@/utils/util";
+export default {
+  name: "TicketCreate",
+  mixins: [CustomFieldsMixin],
+  data() {
+    return {
+      loading: false,
+      form: {
+        type: "online", //盲票类型
+        title: "", // 盲票名称
+        picUrl: [], // 图片
+        facePrice: "", // 面值
+        salePrice: "", // 售价
+        quantity: "", // 数量
+        pkgUnit: 200, // 张数
+        pkgSalePrice: 0, // 单价
+        saleCommRate: "", // 基数
+      },
+      rules: {
+        type: [
+          { required: true, message: "请选择盲票类型", trigger: "change" },
+        ],
+        title: [
+          { required: true, message: "请输入盲票组名称", trigger: "blur" },
+        ],
+        picUrl: [
+          {
+            type: "array",
+            required: true,
+            message: "请上传盲票图片",
+            trigger: "change",
+          },
+        ],
+        facePrice: [
+          { required: true, message: "请输入面值", trigger: "blur" },
+          {
+            pattern:
+              /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/,
+            message: "请输入合法的金额数字,最多两位小数",
+            trigger: ["blur", "change"],
+          },
+        ],
+        salePrice: [
+          { required: true, message: "请输入售价", trigger: "blur" },
+          {
+            pattern:
+              /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/,
+            message: "请输入合法的金额数字,最多两位小数",
+            trigger: ["blur", "change"],
+          },
+        ],
+        quantity: [
+          { required: true, message: "请输入数量", trigger: "blur" },
+          {
+            pattern: /^([1-9]\d*)$/,
+            message: "请输入合法的数字",
+            trigger: ["blur", "change"],
+          },
+        ],
+        pkgUnit: [
+          { required: true, message: "请输入每包张数", trigger: "blur" },
+          {
+            pattern: /^([1-9]\d*)$/,
+            message: "请输入合法的数字",
+            trigger: ["blur", "change"],
+          },
+        ],
+        pkgSalePrice: [
+          { required: true, message: "请输入采购单价", trigger: "blur" },
+          {
+            pattern:
+              /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/,
+            message: "请输入合法的金额数字,最多两位小数",
+            trigger: ["blur", "change"],
+          },
+        ],
+        saleCommRate: [
+          { required: true, message: "请输入分佣基数", trigger: "blur" },
+          {
+            pattern:
+              /^([1-9]\d*(\.\d{1,2})?|([0](\.([0][1-9]|[1-9]\d{0,1}))))$/,
+            message: "请输入合法的数字,最多两位小数",
+            trigger: ["blur", "change"],
+          },
+        ],
+      },
+      // 奖级列表
+      awardsList: [
+        {
+          name: "奖品一",
+          sort: 1,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品二",
+          sort: 2,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品三",
+          sort: 3,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品四",
+          sort: 4,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品五",
+          sort: 5,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品六",
+          sort: 6,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品七",
+          sort: 7,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品八",
+          sort: 8,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品九",
+          sort: 9,
+          quantity: 0,
+          prizeList: [],
+        },
+        {
+          name: "奖品十",
+          sort: 10,
+          quantity: 0,
+          prizeList: [],
+        },
+      ],
+
+      prizeIndex: null, // 奖级下标
+
+      goodsTitle: "", // 商品名称
+      goodsTableVisible: false, // 添加商品弹框
+      goodsList: [], // 商品列表
+      goodsTotal: 0, // 商品总数
+      selectGoodsList: [], // 选中商品
+
+      couponTitle: "", // 券名称
+      couponTableVisible: false, // 添加卡券弹框
+      couponList: [], // 卡券列表
+      couponTotal: 0, // 卡券总数
+      selectCouponList: [], // 选中卡券
+
+      coinTableVisible: false, // 添加盲豆弹框
+      coinForm: { quantity: "" },
+      coinRules: {
+        quantity: [
+          { required: true, message: "请输入数量", trigger: "blur" },
+          {
+            pattern: /^([1-9]\d*)$/,
+            message: "请输入合法的数字",
+            trigger: ["blur", "change"],
+          },
+        ],
+      },
+
+      pageParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+    };
+  },
+  created() {
+    this.getGoodsList();
+    this.getCouponList();
+  },
+  methods: {
+    // 商品列表
+    getGoodsList() {
+      this.loading = true;
+      getGoodsList(
+        "pageNum=" +
+          this.pageParams.pageNum +
+          "&pageSize=" +
+          this.pageParams.pageSize +
+          "&",
+        { title: this.goodsTitle, status: "on" }
+      ).then((res) => {
+        this.goodsList = res.rows.map((item) => {
+          return {
+            ...item,
+            picUrl: publicFileGetUrl + item.picUrl.split(',')[0],
+          };
+        });
+        this.goodsTotal = res.total;
+        this.loading = false;
+      });
+    },
+
+    // 卡券列表
+    getCouponList() {
+      this.loading = true;
+      getCouponList(
+        "pageNum=" +
+          this.pageParams.pageNum +
+          "&pageSize=" +
+          this.pageParams.pageSize +
+          "&",
+        { title: this.couponTitle, status: "on" }
+      ).then((res) => {
+        this.couponList = res.rows.map((item) => {
+          return {
+            ...item,
+            type: JSON.parse(item.type),
+            useArea: JSON.parse(item.useArea),
+            picUrl: publicFileGetUrl + item.picUrl,
+          };
+        });
+        this.couponTotal = res.total;
+        this.loading = false;
+      });
+    },
+
+    // 保存
+    submitForm() {
+      const subForm = this.$refs["form"];
+      subForm.validate((valid) => {
+        if (valid) {
+          // let prizeIndex = this.awardsList.findIndex((item) => {
+          //   return !item.prizeList.length;
+          // });
+          // // 判断没有设置奖品的奖级
+          // if (prizeIndex != -1) {
+          //   this.$message.error(
+          //     `请设置${this.awardsList[prizeIndex].name}的奖品!`
+          //   );
+          //   return;
+          // }
+
+          let prizeIndex = this.awardsList.findIndex((item) => {
+            return !item.prizeList.length && item.quantity > 0;
+          });
+
+          // 判断没有设置奖品的奖级
+          if (prizeIndex != -1) {
+            this.$message.error(
+              `请设置${this.awardsList[prizeIndex].name}的奖品!`
+            );
+            return;
+          }
+
+          if (this.form.quantity % this.form.pkgUnit != 0) {
+            this.$message.error("每包数量错误!");
+            return;
+          }
+
+          let quantityTotal = 0;
+          this.awardsList.forEach((item) => {
+            quantityTotal += item.quantity;
+          });
+          if (this.form.quantity != quantityTotal) {
+            this.$message.error("盲票数量和奖品数量不一致!");
+            return;
+          }
+
+          // if (this.form.saleCommRate > 100) {
+          //   this.$message.error("分佣基数不能大于100!");
+          //   return;
+          // }
+
+          this.awardsList.forEach((item) => {
+            item.prizeList = item.prizeList.map((ele) => {
+              return {
+                ...ele,
+                refId: ele.goodsId || ele.couponId,
+                prizeType: ele.prizeType,
+                quantity: ele.quantity,
+                value: Number(ele.coinValue),
+              };
+            });
+          });
+
+          let filterArr = this.awardsList.filter((item) => {
+            return item.prizeList.length != 0;
+          });
+
+          let filterArr2 = filterArr.filter((item) => {
+            return item.quantity != 0;
+          });
+
+          let data = {
+            ...this.form,
+            picUrl: this.form.picUrl[0].fileName,
+            facePrice: accMul(this.form.facePrice, 100),
+            salePrice: accMul(this.form.salePrice, 100),
+            pkgSalePrice: accMul(this.form.pkgSalePrice, 100),
+            awardsList: filterArr2,
+          };
+
+          const loading = this.$loading({
+            lock: true,
+            text: "保存中",
+            spinner: "el-icon-loading",
+            background: "rgba(0, 0, 0, 0.4)",
+          });
+
+          ticketBoxCreate(data)
+            .then((res) => {
+              loading.close();
+              if (res.code == 0) {
+                this.msgSuccess("保存成功");
+                this.$store.dispatch("tagsView/delView", this.$route);
+                this.$router.go(-1);
+              }
+            })
+            .catch(() => {
+              loading.close();
+            });
+        } else {
+          this.getFormErrorMessage(subForm);
+          return false;
+        }
+      });
+    },
+
+    // 添加图片
+    pictureSelect(data) {
+      this.form.picUrl = data;
+    },
+
+    // 改变奖级数量
+    handleChangeAll(e, item) {
+      this.$set(item, "quantity", e);
+      this.$forceUpdate();
+      // this.getQuantity();
+    },
+
+    // 改变奖品数量
+    handleChange(e, index) {
+      this.prizeIndex = index;
+      this.$forceUpdate();
+      // this.getQuantity();
+    },
+
+    // 奖级商品删除
+    handleDel(index, item) {
+      this.prizeIndex = item.sort - 1;
+      let list = item.prizeList;
+      list.splice(index, 1);
+      this.$set(item, "prizeList", list);
+      // this.getQuantity();
+    },
+
+    // 关闭弹框
+    close() {
+      this.goodsTableVisible = false;
+      this.couponTableVisible = false;
+      this.coinTableVisible = false;
+      this.coinForm.quantity = "";
+    },
+
+    // 添加奖品种类
+    handleCommand(e, index) {
+      this.prizeIndex = index;
+      if (e == "goods") {
+        this.goodsTableVisible = true;
+      } else if (e == "coupon") {
+        this.couponTableVisible = true;
+      } else if (e == "coin") {
+        this.coinTableVisible = true;
+      }
+    },
+
+    // 查询商品
+    handleQueryGoods() {
+      this.getGoodsList();
+    },
+
+    // 查询券
+    handleQueryCoupon() {
+      this.getCouponList();
+    },
+
+    // 选中商品
+    handleSelectionGoods(e) {
+      this.selectGoodsList = e.map((item) => {
+        return {
+          prizeType: "goods",
+          // quantity: 1,
+          goodsId: item.goodsId,
+          picUrl: item.picUrl,
+          title: item.title,
+        };
+      });
+    },
+
+    // 选中卡券
+    handleSelectionCoupon(e) {
+      this.selectCouponList = e.map((item) => {
+        return {
+          prizeType: "coupon",
+          // quantity: 1,
+          couponId: item.couponId,
+          picUrl: item.picUrl,
+          title: item.title,
+        };
+      });
+    },
+
+    // 确认选中商品
+    confirmGoods() {
+      this.awardsList[this.prizeIndex].prizeList = this.awardsList[
+        this.prizeIndex
+      ].prizeList.concat(this.selectGoodsList);
+      // this.getQuantity();
+      this.getGoodsList();
+      this.close();
+    },
+
+    // 确认选中卡券
+    confirmCoupon() {
+      this.awardsList[this.prizeIndex].prizeList = this.awardsList[
+        this.prizeIndex
+      ].prizeList.concat(this.selectCouponList);
+      // this.getQuantity();
+      this.getCouponList();
+      this.close();
+    },
+
+    // 确认输入盲豆
+    confirmCoin() {
+      let coin = {
+        prizeType: "coin",
+        // quantity: 1,
+        title: `盲豆 x${this.coinForm.quantity}`,
+        picUrl: publicFileGetUrl + "md.jpeg",
+        coinValue: this.coinForm.quantity,
+      };
+      this.$refs["coinForm"].validate((valid) => {
+        if (valid) {
+          this.awardsList[this.prizeIndex].prizeList.push(coin);
+          // this.getQuantity();
+          this.close();
+        } else {
+          return false;
+        }
+      });
+    },
+
+    // 计算奖级数量
+    getQuantity() {
+      let num = 0;
+      this.awardsList[this.prizeIndex].prizeList.forEach((item) => {
+        num += item.quantity;
+      });
+      this.awardsList[this.prizeIndex].quantity = num;
+    },
+
+    // 取消
+    back() {
+      this.$store.dispatch("tagsView/delView", this.$route);
+      this.$router.go(-1);
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.base-info-title {
+  padding: 10px;
+  border-bottom: 1px solid #eaeaea;
+  margin-bottom: 20px;
+}
+.tip {
+  padding-left: 100px;
+  height: 32px;
+  margin-bottom: 20px;
+  color: #828282;
+  font-size: 14px;
+}
+.save-btn {
+  display: flex;
+  align-content: center;
+  justify-content: center;
+  margin-bottom: 100px;
+  .ge {
+    width: 100px;
+  }
+}
+.prize {
+  width: 1000px;
+  margin-bottom: 50px;
+  background: #f9f9f9;
+  border: 1px solid #bbbbbb;
+  font-size: 14px;
+  &-top {
+    padding: 10px 20px;
+    margin-bottom: 10px;
+    display: flex;
+    align-content: center;
+    justify-content: space-around;
+    border-bottom: 1px solid #bbbbbb;
+    div {
+      line-height: 36px;
+    }
+  }
+  &-btn {
+    border-top: 1px solid #bbbbbb;
+    padding: 10px;
+  }
+}
+
+.dialog-search {
+  display: flex;
+  line-height: 32px;
+  margin-bottom: 20px;
+  .ge {
+    width: 40px;
+  }
+}
+
+.dialog-btn {
+  display: flex;
+  align-content: center;
+  justify-content: flex-end;
+  padding: 40px 0 0;
+  .ge {
+    width: 40px;
+  }
+}
+</style>

+ 0 - 0
src/views/business/ticket/detail.vue


+ 262 - 0
src/views/business/ticket/import.vue

@@ -0,0 +1,262 @@
+<template>
+ <el-dialog
+    :title="title"
+    :visible.sync="dialogVisible"
+    width="400px"
+    :append-to-body="true"
+    :before-close="close"
+    :destroy-on-close="true"
+    :close-on-click-modal="false">
+     <el-upload
+       ref="upload"
+       :limit="1"
+       accept=".xlsx, .xls"
+       :headers="upload.headers"
+       :data="reqData"
+       :action="upload.url"
+       :disabled="upload.isUploading"
+       :before-upload="handleBeforeUpload"
+       :on-progress="handleFileUploadProgress"
+       :on-success="handleFileSuccess"
+       :http-request="reqUploadFile"
+       :auto-upload="false"
+       drag
+     >
+       <i class="el-icon-upload"></i>
+       <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+       <div class="el-upload__tip text-center" slot="tip">
+         <div class="el-upload__tip" slot="tip">
+           <!-- <el-checkbox v-model="upload.updateSupport" /> 是否更新已经存在的用户数据 -->
+         </div>
+         <span>仅允许导入xls、xlsx格式文件。</span>
+         <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+       </div>
+     </el-upload>
+     <div slot="footer" class="dialog-footer">
+       <el-button @click="close">取 消</el-button>
+       <el-button type="primary" @click="submitFileForm">确 定</el-button>
+     </div>
+ </el-dialog>
+</template>
+<script>
+
+import { mapGetters } from 'vuex'
+import { getToken, getSign } from "@/utils/auth"
+import { randomStr20 } from '@/utils/util'
+import { importTicket, importTemplate} from "@/api/business/ticket";
+export default {
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    }
+  },
+
+  data() {
+    return {
+      loading: false,
+      // 表单参数
+      form: {},
+      reqData: {},
+      // 盲票导入参数
+      upload: {
+        // 是否显示弹出层(盲票导入)
+        open: false,
+        // 弹出层标题(盲票导入)
+        title: "",
+        // 是否禁用上传
+        isUploading: false,
+        // 设置上传的请求头部
+        headers: { Authorization: "Bearer " + getToken() },
+        // 上传的地址
+        url: process.env.VUE_APP_BASE_API + "/api/v1/mp/admin/ticket/box/importTicket"
+      },
+    }
+  },
+
+  computed: {
+    ...mapGetters(['userInfo']),
+    title() {
+      return '盲票导入'
+    }
+  },
+
+  created() {
+    // 是编辑
+    // if (this.editId) {
+    //    this.getDetail()
+    // }
+  },
+
+  mounted() {
+    document.body.appendChild(this.$el)
+  },
+
+  destroyed() {
+    // if appendToBody is true, remove DOM node after destroy
+    if (this.appendToBody && this.$el && this.$el.parentNode) {
+      this.$el.parentNode.removeChild(this.$el)
+    }
+  },
+
+  methods: {
+
+    // 上传前loading加载
+    handleBeforeUpload(file) {
+      this.getHttpHeader()
+      this.vloading = this.$loading({
+        lock: true,
+        text: "正在导入.....",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+    },
+
+    /** 下载模板操作 */
+    importTemplate() {
+      console.log("importTemplate ====================")
+      importTemplate().then(response => {
+        this.download(response.msg);
+      });
+    },
+    // 文件上传中处理
+    handleFileUploadProgress(event, file, fileList) {
+      this.upload.isUploading = true;
+    },
+    // 文件上传成功处理
+    handleFileSuccess(response, file, fileList) {
+      this.upload.open = false;
+      this.upload.isUploading = false;
+      this.$refs.upload.clearFiles();
+      this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+      this.getList();
+    },
+    // 提交上传文件
+    submitFileForm() {
+      this.$refs.upload.submit();
+    },
+
+    // 自定义文件上传的实现
+    reqUploadFile(param) {
+      var data = this.reqData || {}
+      var params = {
+        file: param.file,
+        ...data
+      }
+      importTicket(params, this.headers).then(response => {
+       //  console.log("importTicket============ response.msg"+response.msg)
+        this.upload.open = false;
+        this.upload.isUploading = false;
+        this.$refs.upload.clearFiles();
+        this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+        // this.$emit('saveSuccess')
+        this.vloading.close();
+        this.close()
+
+      }).catch(response => {
+        console.log("导入 shibia==========")
+        this.upload.open = false;
+        this.upload.isUploading = false;
+        this.$refs.upload.clearFiles();
+        this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+        this.vloading.close();
+        //param.onError()
+      })
+    },
+
+    // 请求头
+    getHttpHeader() {
+      let timestamp = parseInt(new Date().getTime()),
+        nonce = randomStr20();
+      var sign = getSign(this.reqData || {}, timestamp)
+      let url = this.uploadImgUrl + '?sign=' + sign + '&nonce=' + nonce;
+      this.fileSaveUrl = url
+      var headers = {
+        "Authorization": "Bearer " + getToken(),
+        "x-zz-timestamp": timestamp
+      }
+      this.headers = headers
+    },
+
+    /**
+     * 关闭窗口
+     */
+    close() {
+      this.$emit('close')
+      this.form = {
+        month:3,
+        packageType:'1'
+      }
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.tag{
+  margin-right: 15px;
+  width: 90px;
+  text-align: center;
+  cursor: pointer;
+
+}
+
+.tag-select{
+  background-color: #409eff!important;
+  border-color: #409eff!important;
+  color: #fff!important;
+}
+
+.cover-content-item{
+  width: 220px;
+  float: left;
+  position: relative;
+  .cover-img {
+    width: 200px;
+    height: 100px;
+  }
+  .cover-mark {
+    position: absolute;
+    top: 0px;
+    right: 28px;
+    z-index: 1;
+    color: red;
+    cursor: pointer;
+    visibility: hidden;
+  }
+  .select {
+      visibility: visible!important;
+  }
+}
+
+.dialog-footer{
+  text-align: center;
+}
+
+</style>
+
+<style lang="scss">
+.ygp-form-items {
+   .el-form-item{
+     padding: 0 5px;
+   }
+  .el-form-item__label {
+    line-height: 1.2;
+    padding-bottom: 8px;
+    word-break: break-all;
+    word-wrap: break-word;
+    color: #333;
+  }
+
+  .el-form-item__error {
+    position: relative;
+    top: auto;
+    left: auto;
+  }
+
+  .el-form-item.is-desc_text {
+    .el-form-item__label {
+      display: none;
+    }
+  }
+}
+</style>

+ 458 - 0
src/views/business/ticket/index.vue

@@ -0,0 +1,458 @@
+<template>
+  <div class="app-container">
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      :inline="true"
+      v-show="showSearch"
+      label-width="90px"
+    >
+      <el-form-item label="盲票组名称">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入盲票组名称"
+          clearable
+          size="small"
+          style="width: 240px"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="盲票组类型">
+        <el-select
+          v-model="queryParams.ticketType"
+          placeholder="请选择盲票组类型"
+          clearable
+          size="small"
+          @change="handleQuery"
+        >
+          <el-option
+            v-for="item in ticketTypeArr"
+            :key="item.areaId"
+            :label="item.areaName"
+            :value="item.areaId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="盲票组状态">
+        <el-select
+          v-model="queryParams.ticketState"
+          placeholder="请选择盲票组状态"
+          clearable
+          size="small"
+          @change="handleQuery"
+        >
+          <el-option
+            v-for="item in ticketStateArr"
+            :key="item.areaId"
+            :label="item.areaName"
+            :value="item.areaId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button
+          type="primary"
+          icon="el-icon-search"
+          size="mini"
+          @click="handleQuery"
+          >搜索</el-button
+        >
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['business:ticket:add']"
+          >添加盲票组</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="info"
+          plain
+          icon="el-icon-upload2"
+          size="mini"
+          @click="handleImport"
+          v-hasPermi="['business:ticket:import']"
+          >导入</el-button
+        >
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['business:ticket:export']"
+          >导出</el-button
+        >
+      </el-col>
+
+      <el-col :span="1.5">
+        <el-button
+          type="infor"
+          plain
+          icon="el-icon-printer"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExportDraw"
+          v-hasPermi="['business:ticket:export']"
+          >导出印刷</el-button
+        >
+      </el-col>
+
+
+      <right-toolbar
+        :showSearch.sync="showSearch"
+        @queryTable="getList"
+      ></right-toolbar>
+    </el-row>
+
+    <el-table
+      ref="table"
+      v-loading="loading"
+      :data="list"
+      @select="handleSelect"
+      class="ticket-table"
+    >
+      <el-table-column
+        type="selection"
+        width="55"
+        align="center"
+        fixed="left"
+      />
+      <el-table-column label="盲票组ID" prop="boxId" min-width="80" />
+      <el-table-column label="盲票图片" prop="picUrl" min-width="100">
+        <template slot-scope="scope">
+          <div>
+            <el-image
+              style="width: 100px; height: 100px"
+              :src="scope.row.picUrl"
+              :preview-src-list="[scope.row.picUrl]"
+            />
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="盲票组名称" prop="title" min-width="85" />
+      <el-table-column label="盲票组面值" prop="facePrice" min-width="85">
+        <template slot-scope="scope">
+          <div>¥{{ $numberFormat(scope.row.facePrice) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="盲票总数" prop="quantity" width="90">
+        <template slot-scope="scope">
+          <div>{{ scope.row.quantity }}张</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="已售张数" prop="saleQty" min-width="90">
+        <template slot-scope="scope">
+          <div>{{ scope.row.saleQty }}张</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="盲票类型" prop="type" min-width="80">
+        <template slot-scope="scope">
+          {{ scope.row.type.desc }}
+        </template>
+      </el-table-column>
+      <el-table-column label="采购单价" prop="pkgSalePrice" min-width="90">
+        <template slot-scope="scope">
+          <div>¥{{ $numberFormat(scope.row.pkgSalePrice) }}/包</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="已采购数量" prop="salePkgQty" min-width="90">
+        <template slot-scope="scope">
+          <div>{{ scope.row.salePkgQty }}包</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" prop="status" min-width="80">
+        <template slot-scope="scope">
+          <el-tag
+            :type="scope.row.status.value === 'on' ? 'success' : 'info'"
+            >{{ scope.row.status.desc }}</el-tag
+          >
+        </template>
+      </el-table-column>
+
+      <el-table-column
+        label="操作"
+        align="center"
+        show-overflow-tooltip
+        class-name="small-padding  fixed-width"
+        min-width="150"
+        fixed="right"
+      >
+        <template slot-scope="scope">
+          <div>
+            <el-button
+              v-if="
+                scope.row.status.value === 'off' ||
+                scope.row.status.value === 'done'
+              "
+              v-hasPermi="['business:ticket:on']"
+              type="text"
+              @click="setStatus(scope.row, 'on')"
+              >上架</el-button
+            >
+            <el-button
+              v-if="scope.row.status.value === 'on'"
+              v-hasPermi="['business:ticket:off']"
+              type="text"
+              @click="setStatus(scope.row, 'off')"
+              >下架</el-button
+            >
+            <el-button
+              v-if="scope.row.status.value === 'done'"
+              v-hasPermi="['business:ticket:remove']"
+              type="text"
+              class="del"
+              @click="del(scope.row)"
+              >删除</el-button
+            >
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total > 0"
+      :total="total"
+      :page.sync="pageParams.pageNum"
+      :limit.sync="pageParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 导入 -->
+    <ticket-import
+      v-if="importShow"
+      :dialog-visible="importShow"
+      @close="hideDialog"
+    />
+  </div>
+</template>
+<script>
+import {
+  getTicketList,
+  exportTicket,
+  exportDraw,
+  ticketBoxPut,
+  ticketBoxRemove,
+} from "@/api/business/ticket";
+import { publicFileGetUrl } from "@/api/common";
+import TicketImport from "./import";
+export default {
+  name: "Ticket",
+  components: {
+    TicketImport,
+  },
+  data() {
+    return {
+      loading: false,
+      exportLoading: false,
+      showSearch: true,
+      queryParams: {
+        title: "",
+        ticketType: "",
+        ticketState: "",
+      },
+      pageParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+      ticketTypeArr: [],
+      ticketStateArr: [],
+      // 总条数
+      total: 0,
+      list: [],
+      ids: [],
+      importShow: false,
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    // 盲票组列表
+    getList() {
+      this.loading = true;
+      getTicketList(
+        "pageNum=" +
+          this.pageParams.pageNum +
+          "&pageSize=" +
+          this.pageParams.pageSize +
+          "&",
+        this.queryParams
+      )
+        .then((res) => {
+          this.loading = false;
+          if (res.code == 0) {
+            res.rows.forEach((item) => {
+              let picUrlArr = item.picUrl.split(",");
+              item.picUrl = publicFileGetUrl + picUrlArr[0];
+              item.status = JSON.parse(item.status);
+              item.type = JSON.parse(item.type);
+            });
+            this.total = res.total;
+            this.list = res.rows;
+          }
+        })
+        .catch(() => {
+          this.loading = false;
+        });
+    },
+
+    setStatus(item, status) {
+      this.$confirm(
+        `确认${status === "on" ? "上架" : "下架"}盲票 “${item.title}” 吗?`,
+        `${status === "on" ? "上架" : "下架"}盲票`,
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        }
+      ).then(() => {
+        ticketBoxPut({
+          boxId: item.boxId,
+          status,
+        }).then((res) => {
+          if (res.code === 0) {
+            this.$message.success("操作已完成!");
+            this.getList();
+          }
+        });
+      });
+    },
+
+    del(item) {
+      this.$confirm(`确认删除盲票 “${item.title}” 吗?`, "删除盲票", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      }).then(() => {
+        ticketBoxRemove({ boxId: item.boxId }).then((res) => {
+          if (res.code === 0) {
+            this.$message.success("操作已完成!");
+            this.getList();
+          }
+        });
+      });
+    },
+
+    // 搜索
+    handleQuery() {
+      this.getList();
+    },
+    // 重置
+    resetQuery() {},
+    // 添加盲票组
+    handleAdd() {
+      this.$router.push({ name: "TicketCreate" });
+    },
+    handleSelect(selection, row) {
+      const isSelect = selection.find((item) => item.boxId === row.boxId);
+      this.$refs.table.clearSelection();
+      if (isSelect) {
+        this.$refs.table.toggleRowSelection(row, true);
+        this.ids = row.boxId;
+      } else {
+        this.ids = "";
+      }
+    },
+
+    handleDetail() {},
+
+    handleImport() {
+      console.log("handleImport ====================");
+      this.importShow = true;
+    },
+
+    hideDialog() {
+      this.importShow = false;
+    },
+
+    /** 导出按钮操作 */
+    handleExport() {
+      const boxIds = this.ids || [];
+
+      if (boxIds.length == 0) {
+        this.$alert("请选择你要导出的盲票组!", "提示", { type: "warning" });
+      } else {
+        this.$confirm("是否确认导出所选盲票数据项?", "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        })
+          .then(() => {
+            this.vloading = this.$loading({
+              lock: true,
+              text: "正在导出.....",
+              background: "rgba(0, 0, 0, 0.7)",
+            });
+            return exportTicket({ ids: boxIds });
+          })
+          .then((response) => {
+            this.vloading.close();
+            this.download(response.msg);
+            this.exportLoading = false;
+          })
+          .catch(() => {
+            this.vloading.close();
+            this.exportLoading = false;
+          });
+      }
+    },
+
+    /** 导出按钮操作 */
+    handleExportDraw() {
+      const boxIds = this.ids || [];
+
+      if (boxIds.length == 0) {
+        this.$alert("请选择你要导出的盲票组!", "提示", { type: "warning" });
+      } else {
+        this.$confirm("是否确认导出所选盲票数据项?", "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        })
+          .then(() => {
+            this.vloading = this.$loading({
+              lock: true,
+              text: "正在导出印刷.....",
+              background: "rgba(0, 0, 0, 0.7)",
+            });
+            return exportDraw({ ids: boxIds });
+          })
+          .then((response) => {
+            this.vloading.close();
+            this.download(response.msg);
+            this.exportLoading = false;
+          })
+          .catch(() => {
+            this.vloading.close();
+            this.exportLoading = false;
+          });
+      }
+    },
+  },
+  mounted() {},
+  destroyed() {},
+};
+</script>
+<style lang="scss">
+.ticket-table {
+  thead {
+    .el-checkbox {
+      display: none !important;
+    }
+  }
+}
+</style>

+ 78 - 0
src/views/skip.vue

@@ -0,0 +1,78 @@
+<template>
+  <div>
+    <div class="skip-wrap">
+      <div class="skip-theme">
+        <div class="skip-theme-iamge">logo</div>
+        <div class="skip-theme-txt">小程序</div>
+        <div>{{ id }}</div>
+      </div>
+      <div class="skip-btn">
+        <p @click="showWechat()">打开微信小程序</p>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { urlschemaGeneratee } from "@/api/business/ticket";
+export default {
+  data() {
+    return {
+      id: this.$route.query.id,
+    };
+  },
+
+  methods: {
+    showWechat() {
+        
+      window.location.href = "weixin://dl/business/?t=ngsLEPp3hTi";
+    },
+  },
+  components: {},
+  mounted() {},
+  destroyed() {},
+};
+</script>
+<style lang="scss">
+.skip-wrap {
+  height: 100vh;
+  width: 100%;
+  position: relative;
+}
+.skip-theme {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  padding: 200px 0 0;
+}
+.skip-theme-iamge {
+  width: 60px;
+  height: 60px;
+  line-height: 60px;
+  text-align: center;
+  color: #fff;
+  background-color: #e96737;
+  border-radius: 50%;
+  margin-bottom: 10px;
+}
+.skip-theme-txt {
+  color: rgb(194, 184, 184);
+  text-align: center;
+}
+.skip-btn {
+  position: absolute;
+  bottom: 50px;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.skip-btn p {
+  height: 30px;
+  width: 200px;
+  background-color: rgb(7, 192, 88);
+  border-radius: 8px;
+  line-height: 30px;
+  color: #fff;
+  text-align: center;
+}
+</style>

+ 4 - 4
src/views/system/role/index.vue

@@ -110,8 +110,8 @@
       <el-table-column label="角色类型" width="150">
       <el-table-column label="角色类型" width="150">
         <template slot-scope="scope">
         <template slot-scope="scope">
             <span v-if="scope.row.roleType == '1'">系统角色</span>
             <span v-if="scope.row.roleType == '1'">系统角色</span>
-            <span v-if="scope.row.roleType == '2'">门店角色</span>
-            <span v-if="scope.row.roleType == '3'">商角色</span>
+            <span v-if="scope.row.roleType == '2'">渠道角色</span>
+            <span v-if="scope.row.roleType == '3'">经销商角色</span>
         </template>
         </template>
       </el-table-column>
       </el-table-column>
       <el-table-column label="显示顺序" prop="roleSort" width="100" />
       <el-table-column label="显示顺序" prop="roleSort" width="100" />
@@ -358,11 +358,11 @@ export default {
         },
         },
         {
         {
           value: "2",
           value: "2",
-          label: "门店角色"
+          label: "渠道商角色"
         },
         },
         {
         {
           value: "3",
           value: "3",
-          label: "商角色"
+          label: "经销商角色"
         }
         }
       ],
       ],
       isDefaultOptions: [
       isDefaultOptions: [

Some files were not shown because too many files changed in this diff