소스 검색

机构授权牌接口调试通过,待测

yuwenjun1997 2 년 전
부모
커밋
0e0c309292

+ 4 - 4
.env.development

@@ -4,12 +4,12 @@ NODE_ENV = development
 ENV = 'development'
 
 # 测试地址 API接口
-VUE_APP_BASE_API = 'https://zplma-b.caimei365.com'
-# VUE_APP_BASE_API = 'http://192.168.2.68:8012'
+# VUE_APP_BASE_API = 'https://zplma-b.caimei365.com'
+VUE_APP_BASE_API = 'http://192.168.2.68:8012'
 
 # 文件上传 API接口地址
-VUE_APP_UPLOAD_API='https://zplma-b.caimei365.com'
-# VUE_APP_UPLOAD_API='http://192.168.2.68:8012'
+# VUE_APP_UPLOAD_API='https://zplma-b.caimei365.com'
+VUE_APP_UPLOAD_API='http://192.168.2.68:8012'
 
 # 二维码生成链接location
 VUE_APP_BASE_SERVER = 'https://www-b.caimei365.com'

+ 27 - 0
src/api/system.js

@@ -147,3 +147,30 @@ export function sysMenuTree(params) {
     params
   })
 }
+
+/** 获取授权牌模板列表 */
+export function fetchAuthTempList(params) {
+  return request({
+    url: '/auth/template/list',
+    method: 'get',
+    params
+  })
+}
+
+/** 添加编辑保存授权牌模板 */
+export function authTempSave(data) {
+  return request({
+    url: '/auth/template/save',
+    method: 'post',
+    data
+  })
+}
+
+/** 添加编辑保存授权牌模板 */
+export function authTempUpdate(data) {
+  return request({
+    url: '/shop/authImage/update/all',
+    method: 'post',
+    data
+  })
+}

BIN
src/assets/img/drag-auth-temp.jpg


BIN
src/assets/img/drag-qrcode-temp.png


+ 89 - 0
src/components/DragLayout/dragmove.js

@@ -0,0 +1,89 @@
+// https://github.com/knadh/dragmove.js
+// Kailash Nadh (c) 2020.
+// MIT License.
+
+let _loaded = false
+const _callbacks = []
+const _isTouch = window.ontouchstart !== undefined
+
+export const dragmove = function(target, handler, onStart, onEnd) {
+  // Register a global event to capture mouse moves (once).
+  if (!_loaded) {
+    document.addEventListener(_isTouch ? 'touchmove' : 'mousemove', function(e) {
+      let c = e
+      if (e.touches) {
+        c = e.touches[0]
+      }
+
+      // On mouse move, dispatch the coords to all registered callbacks.
+      for (var i = 0; i < _callbacks.length; i++) {
+        _callbacks[i](c.clientX, c.clientY)
+      }
+    })
+  }
+
+  _loaded = true
+  let isMoving = false
+  let hasStarted = false
+  let startX = 0
+  let startY = 0
+  let lastX = 0
+  let lastY = 0
+
+  // On the first click and hold, record the offset of the pointer in relation
+  // to the point of click inside the element.
+  handler.addEventListener(_isTouch ? 'touchstart' : 'mousedown', function(e) {
+    e.stopPropagation()
+    e.preventDefault()
+    if (target.dataset.dragEnabled === 'false') {
+      return
+    }
+
+    let c = e
+    if (e.touches) {
+      c = e.touches[0]
+    }
+
+    isMoving = true
+    startX = target.offsetLeft - c.clientX
+    startY = target.offsetTop - c.clientY
+  })
+
+  // On leaving click, stop moving.
+  document.addEventListener(_isTouch ? 'touchend' : 'mouseup', function(e) {
+    if (onEnd && hasStarted) {
+      onEnd(target, parseInt(target.style.left), parseInt(target.style.top))
+    }
+
+    isMoving = false
+    hasStarted = false
+  })
+
+  // Register mouse-move callback to move the element.
+  _callbacks.push(function move(x, y) {
+    if (!isMoving) {
+      return
+    }
+
+    if (!hasStarted) {
+      hasStarted = true
+      if (onStart) {
+        onStart(target, lastX, lastY)
+      }
+    }
+
+    lastX = x + startX
+    lastY = y + startY
+
+    // If boundary checking is on, don't let the element cross the viewport.
+    if (target.dataset.dragBoundary === 'true') {
+      lastX = Math.min(window.innerWidth - target.offsetWidth, Math.max(0, lastX))
+      lastY = Math.min(window.innerHeight - target.offsetHeight, Math.max(0, lastY))
+    }
+
+    target.style.left = lastX + 'px'
+    target.style.top = lastY + 'px'
+  })
+}
+
+export { dragmove as default }

+ 193 - 0
src/components/DragLayout/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="drag-layout">
+    <div class="drag-content" :class="{ padding: isDrag }">
+      <div id="drag__container" :style="imageStyle">
+        <div id="drag__move" :style="ewmStyle">
+          <el-tooltip v-model="tooltipShow" effect="dark" content="二维码在这里!" placement="top-start" :manual="true">
+            <div style="width: 100%; height: 100%" />
+          </el-tooltip>
+        </div>
+      </div>
+    </div>
+    <div class="drag__control">
+      <el-button @click="onCancel">取 消</el-button>
+      <el-button type="primary" @click="onConfirm">确 定</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { dragmove } from './dragmove.js'
+export default {
+  name: 'DragLayout',
+  props: {
+    qw: {
+      type: Number,
+      default: 53
+    },
+    qh: {
+      type: Number,
+      default: 53
+    },
+    oldX: {
+      type: Number,
+      default: 0
+    },
+    oldY: {
+      type: Number,
+      default: 0
+    },
+    image: {
+      type: String,
+      default: ''
+    },
+    size: {
+      type: String,
+      default: ''
+    },
+    isDrag: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      tooltipShow: false,
+      dragContainer: null,
+      snapThreshold: 0, // 边界
+      x: 0,
+      y: 0
+    }
+  },
+  computed: {
+    imageWidth() {
+      return this.size.split(',')[0] + 'px'
+    },
+    imageHeight() {
+      return this.size.split(',')[1] + 'px'
+    },
+    imageStyle() {
+      return {
+        width: this.imageWidth,
+        height: this.imageHeight,
+        background: `url(${this.image}) no-repeat center`,
+        'background-size': `${this.imageWidth} ${this.imageHeight}`
+      }
+    },
+    ewmStyle() {
+      console.log({
+        left: this.oldX + 'px',
+        top: this.oldY + 'px',
+        width: this.qw + 'px',
+        height: this.qh + 'px',
+        'background-size': `${this.qw + 'px'} ${this.qh + 'px'}`
+      })
+
+      return {
+        // cursor: this.isDrag ? 'move' : '',
+        left: this.oldX + 'px',
+        top: this.oldY + 'px',
+        width: this.qw + 'px',
+        height: this.qh + 'px',
+        'background-size': `${this.qw + 'px'} ${this.qh + 'px'}`
+      }
+    }
+  },
+  mounted() {
+    if (!this.isDrag) return
+    this.dragContainer = document.querySelector('#drag__container')
+    dragmove(
+      document.querySelector('#drag__container #drag__move'),
+      document.querySelector('#drag__container #drag__move'),
+      this.onStart,
+      this.onEnd
+    )
+    setTimeout(() => {
+      this.tooltipShow = true
+    }, 2000)
+  },
+  methods: {
+    // 取消
+    onCancel() {
+      this.x = this.oldX
+      this.y = this.oldY
+      this.$emit('cancel', { x: this.x, y: this.y })
+      this.tooltipShow = false
+    },
+
+    // 确认
+    onConfirm() {
+      this.$emit('confirm', { x: this.x, y: this.y })
+      this.tooltipShow = false
+    },
+
+    // 开始拖动
+    onStart(el, x, y) {
+      this.tooltipShow = false
+    },
+
+    // 结束拖动
+    onEnd(el, x, y) {
+      this.tooltipShow = true
+      if (x < this.snapThreshold) {
+        el.style.left = 0
+        el.style.right = 'auto'
+      }
+      if (y < 0) {
+        el.style.top = 0
+        el.style.bottom = 'auto'
+      }
+      if (x > this.dragContainer.offsetWidth - el.offsetWidth) {
+        el.style.right = 0
+        el.style.left = 'auto'
+      }
+      if (y > this.dragContainer.offsetHeight - el.offsetHeight) {
+        el.style.bottom = 0
+        el.style.top = 'auto'
+      }
+
+      const { offsetWidth, offsetHeight } = this.dragContainer
+
+      if (x < 0) {
+        this.x = 0
+      } else if (x > offsetWidth - this.qw) {
+        this.x = offsetWidth - this.qw
+      } else {
+        this.x = x
+      }
+
+      if (y < 0) {
+        this.y = 0
+      } else if (y > offsetHeight - this.qh) {
+        this.y = offsetHeight - this.qh
+      } else {
+        this.y = y
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.drag-content {
+  background: #f1f1f1;
+  &.padding {
+    padding: 50px 0;
+  }
+  #drag__container {
+    position: relative;
+    margin: 0 auto;
+    overflow: hidden;
+    background: #fff;
+  }
+  #drag__move {
+    position: absolute;
+    background-color: skyblue;
+    background: url(~@/assets/img/drag-qrcode-temp.png) no-repeat center;
+  }
+}
+
+.drag__control {
+  margin-top: 24px;
+  text-align: right;
+}
+</style>

+ 2 - 2
src/router/module/normal/settings.js

@@ -3,9 +3,9 @@ import Layout from '@/layout'
 
 const settingRoutes = [
   {
-    path: '/settings',
+    path: '/normal/settings',
     component: Layout,
-    redirect: '/settings/menus',
+    redirect: '/normal/settings/auth-card',
     name: 'NormalSettings',
     meta: { title: '系统设置', icon: 'el-icon-s-tools', noCache: true },
     children: [

+ 7 - 2
src/styles/index.scss

@@ -186,10 +186,10 @@ aside {
 // 状态颜色
 .status {
   &.success {
-    color: #409EFF;
+    color: #409eff;
   }
   &.danger {
-    color: #F56C6C;
+    color: #f56c6c;
     cursor: help;
   }
   &.warning {
@@ -350,3 +350,8 @@ aside {
     margin-top: 0 !important;
   }
 }
+
+.form-label-tip {
+  font-size: 14px;
+  color: #999;
+}

+ 15 - 0
src/utils/index.js

@@ -224,6 +224,21 @@ export function objectMerge(target, source) {
   return target
 }
 
+/**
+ * Merges one object to another object
+ * @param {Object} target
+ * @param {(Object)} source
+ * @returns {Object}
+ */
+export function objectCover(target, source) {
+  for (const key in source) {
+    if (Object.hasOwnProperty.call(target, key)) {
+      target[key] = source[key]
+    }
+  }
+  return target
+}
+
 /**
  * @param {HTMLElement} element
  * @param {string} className

+ 35 - 10
src/views/admin/settings/auth-card/components/supplier-list.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="supplier-list">
     <div class="filter-container">
       <div class="filter-control">
         <span>供应商名称:</span>
@@ -7,8 +7,7 @@
           v-model="listQuery.shopName"
           size="mini"
           placeholder="供应商名称"
-          clearable
-          @keyup.enter.native="fetchSupplierList"
+          @keyup.enter.native="filterSupplierList"
         />
       </div>
       <div class="filter-control">
@@ -18,7 +17,7 @@
           placeholder="供应商类型"
           size="mini"
           clearable
-          @change="fetchSupplierList"
+          @change="filterSupplierList"
         >
           <el-option label="所有类型" value="" />
           <el-option label="代理商" :value="2" />
@@ -29,10 +28,9 @@
         <span>手机号:</span>
         <el-input
           v-model="listQuery.mobile"
-          clearable
           size="mini"
           placeholder="手机号"
-          @keyup.enter.native="fetchSupplierList"
+          @keyup.enter.native="filterSupplierList"
         />
       </div>
       <div class="filter-control">
@@ -74,10 +72,15 @@
       <el-table-column prop="mobile" label="手机号" align="center" width="160" />
       <el-table-column prop="linkMan" label="联系人" align="center" />
     </el-table>
+    <div class="control">
+      <el-button @click="onCancel">取 消</el-button>
+      <el-button type="primary" @click="onConfirm">确定</el-button>
+    </div>
   </div>
 </template>
 <script>
 import { fetchSupplierList } from '@/api/supplier'
+import { deepClone } from '@/utils'
 export default {
   name: 'SupplierList',
   data() {
@@ -90,13 +93,26 @@ export default {
         pageSize: 10
       },
       supplierList: [],
-      selectedAuthUserId: ''
+      selectedAuthUserId: '',
+      selectedSupplierInfo: null
     }
   },
   created() {
     this.filterSupplierList()
   },
   methods: {
+    // 取消
+    onCancel() {
+      this.selectedAuthUserId = ''
+      this.selectedSupplierInfo = null
+      this.$emit('cancel', this.selectedSupplierInfo)
+    },
+    // 确认
+    onConfirm() {
+      this.$emit('confirm', deepClone(this.selectedSupplierInfo))
+      this.selectedAuthUserId = ''
+      this.selectedSupplierInfo = null
+    },
     // 确认选中的供应商
     handleSubmitSupplier() {
       this.dialogTableVisible = false
@@ -106,7 +122,6 @@ export default {
     handleSelectSupplier(e) {
       this.selectedAuthUserId = e.authUserId
       this.selectedSupplierInfo = e
-      this.$emit('selected', e)
     },
     // 选择供应商对话框关闭事件
     handleDialogClosed() {
@@ -118,7 +133,7 @@ export default {
     },
     // 筛选供应商
     filterSupplierList() {
-      this.pageNum = 1
+      this.listQuery.pageNum = 1
       this.supplierList = []
       this.fetchSupplierList()
     },
@@ -139,4 +154,14 @@ export default {
   }
 }
 </script>
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.supplier-list {
+  .control {
+    text-align: right;
+    margin-top: 12px;
+  }
+  .infinite-list {
+    height: 400px;
+  }
+}
+</style>

+ 209 - 41
src/views/admin/settings/auth-card/index.vue

@@ -4,19 +4,20 @@
       <div class="filter-control">
         <span>所属供应商:</span>
         <el-select
-          v-model="listQuery.shopId"
+          v-model="listQuery.authUserId"
           placeholder="请选择供应商"
           clearable
           style="width: 200px"
           class="filter-item"
           @change="getList"
         >
-          <el-option label="已启用" :value="0" />
-          <el-option label="未启用" :value="1" />
+          <template v-for="item in supplierList">
+            <el-option :key="item.authUserId" :label="item.name" :value="item.authUserId" />
+          </template>
         </el-select>
       </div>
       <div class="filter-control">
-        <el-button type="primary">查询</el-button>
+        <el-button type="primary" @click="getList">查询</el-button>
         <el-button type="primary" @click="addCardVisible = true">添加</el-button>
       </div>
     </div>
@@ -25,48 +26,62 @@
       <el-table-column label="授权牌模板" align="center" width="200px">
         <template slot-scope="{ row }">
           <el-tooltip effect="dark" content="点击查看全图" placement="top-start">
-            <el-image :src="row.image" :preview-src-list="[row.image]" fit="cover" />
+            <el-image :src="row.templateImage" :preview-src-list="[row.templateImage]" fit="cover" />
           </el-tooltip>
         </template>
       </el-table-column>
       <el-table-column prop="shopName" label="所属供应商" align="center" />
       <el-table-column label="操作" align="center" width="280px">
-        <template>
-          <el-button type="primary" size="mini" @click="selectSupplierVisible = true">更换供应商</el-button>
-          <el-button type="primary" size="mini">停用</el-button>
+        <template slot-scope="{ row }">
+          <el-button type="primary" size="mini" @click="onChangeSupplier(row)">更换供应商</el-button>
+          <!-- 隐藏其一 -->
+          <el-button v-if="row.status === 0" type="info" size="mini" @click="onTempStatusChange(row)">启用</el-button>
+          <el-button v-else type="danger" size="mini" @click="onTempStatusChange(row)">停用</el-button>
         </template>
       </el-table-column>
     </el-table>
     <!-- 供应商列表 -->
-    <el-dialog title="提示" :visible.sync="selectSupplierVisible" width="66%">
-      <supplier-list @selected="onSupplierSelected" />
-      <span slot="footer" class="dialog-footer">
-        <el-button @click="selectSupplierVisible = false">取 消</el-button>
-        <el-button type="primary" @click="selectSupplierVisible = false">确 定</el-button>
-      </span>
+    <el-dialog title="选择供应商" :visible.sync="selectSupplierVisible" width="66%" @close="onSelectSupplierClose">
+      <supplier-list @cancel="selectSupplierVisible = false" @confirm="onSupplierConfirm('edit', $event)" />
     </el-dialog>
     <!-- 上传图片 -->
-    <el-dialog title="添加授权牌" :visible.sync="addCardVisible" width="30%">
+    <el-dialog title="添加授权牌模板" :visible.sync="addCardVisible" width="40%" @close="onAddCardClose">
       <el-dialog width="66%" title="选择供应商" :visible.sync="addCardInnerVisible" append-to-body>
-        <supplier-list @selected="onSupplierSelected" />
-        <div slot="footer" class="dialog-footer">
-          <el-button @click="addCardInnerVisible = false">取 消</el-button>
-          <el-button type="primary" @click="addCardInnerVisible = false">确 定</el-button>
-        </div>
+        <supplier-list @cancel="addCardInnerVisible = false" @confirm="onSupplierConfirm('add', $event)" />
       </el-dialog>
-      <el-form :model="formData" label-width="100px">
-        <el-form-item label="授权牌模板:">
-          <el-input v-show="false" v-model="formData.image" />
-          <upload-image />
+      <el-form ref="addCardForm" :model="formData" label-width="120px" :rules="rules">
+        <el-form-item label="授权牌模板:" prop="image">
+          <el-input v-show="false" v-model="formData.templateImage" />
+          <upload-image
+            :limit="1"
+            tip="建议尺寸:200px * 200px"
+            :image-list="tempList"
+            @success="uploadTempImageSuccess"
+            @remove="handleLogoImageRemove"
+          />
+        </el-form-item>
+        <el-form-item label="二维码位置:" prop="qrPosition">
+          <el-input
+            v-model="formData.qrPosition"
+            placeholder="请输入二维码位置坐标,以英文逗号隔开,分别为二维码左上角X,Y轴坐标(例如100,100)"
+          />
+        </el-form-item>
+        <el-form-item label="二维码大小:" prop="qrSize">
+          <el-input v-model.number="formData.qrSize" placeholder="请输入二维码大小(例如:120)" />
         </el-form-item>
-        <el-form-item label="所属供应商:">
-          <el-input v-show="false" v-model="formData.shopId" />
-          <el-button type="primary" size="mini" @click="addCardInnerVisible = true">选择供应商</el-button>
+        <el-form-item label="所属供应商:" prop="authUserId">
+          <el-input v-show="false" v-model="formData.authUserId" />
+          <div class="current-info">
+            <span v-if="selectSupplier" class="supplier-name">{{ selectSupplier.name }}</span>
+            <span class="supplier-change" @click="addCardInnerVisible = true">
+              {{ selectSupplier ? '更换供应商' : '选择供应商' }}
+            </span>
+          </div>
         </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button @click="addCardVisible = false">取 消</el-button>
-        <el-button type="primary" @click="addCardVisible = false">确定</el-button>
+        <el-button type="primary" :loading="isLoading" @click="onConfirm">确定</el-button>
       </div>
     </el-dialog>
     <!-- 页码 -->
@@ -81,48 +96,188 @@
 <script>
 import UploadImage from '@/components/UploadImage'
 import SupplierList from './components/supplier-list.vue'
+import { authTempSave, fetchAuthTempList } from '@/api/system'
+import { fetchSupplierList } from '@/api/supplier'
+import { objectCover } from '@/utils'
 export default {
   components: {
     UploadImage,
     SupplierList
   },
   data() {
+    const validateQrPosition = (rule, value, callback) => {
+      if (!value) return callback()
+      if (value.split(',').length === 2) {
+        return callback()
+      }
+      callback(new Error('二维码坐标格式不正确,请改为(X坐标,Y坐标) => (100,100)'))
+    }
+
     return {
       listLoading: false,
+      isLoading: false,
       listQuery: {
-        shopId: '',
+        listType: 1,
+        authUserId: '',
         pageSize: 15,
         pageNum: 1
       },
       list: [],
       total: 0,
+      supplierList: [],
       formData: {
-        image: '',
-        shopId: ''
+        templateImage: '',
+        authUserId: '',
+        qrPosition: '',
+        qrSize: ''
       },
       addCardVisible: false,
       addCardInnerVisible: false,
-      selectSupplierVisible: false
+      selectSupplierVisible: false,
+      selectSupplier: null,
+      // 授权牌模板列表
+      tempList: [],
+      // 需要更换供应商的授权牌
+      currentCard: null,
+      rules: {
+        templateImage: [{ required: true, message: '请上传授权牌模板', trigger: ['change'] }],
+        authUserId: [{ required: true, message: '请选择供应商', trigger: ['change'] }],
+        qrPosition: [{ validator: validateQrPosition, trigger: ['blur'] }],
+        qrSize: [{ required: true, message: '请填写二维码大小', trigger: ['blur'] }]
+      }
     }
   },
   created() {
     this.getList()
+    this.fetchSupplierList()
   },
   methods: {
     // 获取授权牌模板列表
     getList() {
-      this.list = [
-        {
-          shopName: 'ROSS',
-          image: 'https://picsum.photos/500/400'
-        }
-      ]
-      console.log(123)
+      this.list = []
+      this.listQuery.pageNum = 1
+      this.fetchAuthTempList(this.listQuery)
+    },
+
+    // 获取授权牌模板列表
+    async fetchAuthTempList() {
+      this.listLoading = true
+      try {
+        const res = await fetchAuthTempList(this.listQuery)
+        this.list = res.data.list
+        this.total = res.data.total
+        this.listLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取供应商列表
+    async fetchSupplierList() {
+      try {
+        const res = await fetchSupplierList({
+          listType: 1,
+          brandId: '', // 品牌id
+          linkMan: '', // 联系人
+          mobile: '', // 手机号
+          pageNum: 1, // 页码
+          pageSize: 1000, // 分页大小
+          shopName: '', // 供应商名称
+          shopType: '', // 供应商类型
+          loginAccount: '' // 登录账号
+        })
+        this.supplierList = res.data.list
+      } catch (error) {
+        console.log(error)
+      }
     },
 
     // 选中供应商
-    onSupplierSelected(e) {
-      console.log(e)
+    onSupplierConfirm(type, e) {
+      type === 'add' ? (this.addCardInnerVisible = false) : (this.selectSupplierVisible = false)
+      this.selectSupplier = e
+      this.formData.authUserId = e.authUserId
+      if (type === 'edit') {
+        this.onChangeSupplierSubmit()
+      }
+    },
+
+    // 修改供应商操作
+    onChangeSupplier(row) {
+      this.currentCard = row
+      this.selectSupplierVisible = true
+    },
+
+    // 修改模板启用状态
+    onTempStatusChange(row) {
+      row.status = row.status === 0 ? 1 : 0
+      this.authTempSave(row)
+    },
+
+    // 修改供应商提交
+    onChangeSupplierSubmit() {
+      this.currentCard.authUserId = this.formData.authUserId
+      this.authTempSave(this.currentCard)
+    },
+
+    // 修改授权牌供应商弹窗关闭
+    onSelectSupplierClose() {
+      this.onAddCardClose()
+    },
+
+    // 添加授权牌弹窗关闭
+    onAddCardClose() {
+      this.selectSupplier = null
+      this.formData.authUserId = ''
+      this.formData.templateImage = ''
+      this.tempList = []
+      this.$refs.addCardForm?.clearValidate()
+    },
+
+    // 添加授权牌提交
+    async onConfirm() {
+      try {
+        this.isLoading = true
+        await this.$refs.addCardForm.validate()
+        await this.authTempSave(this.formData)
+      } catch (error) {
+        console.log(error)
+      } finally {
+        this.isLoading = false
+      }
+    },
+
+    // 授权牌信息保存
+    async authTempSave(data) {
+      const submitData = objectCover(
+        {
+          templateId: '',
+          templateImage: '',
+          authUserId: '',
+          status: 1,
+          qrPosition: '',
+          qrSize: ''
+        },
+        data
+      )
+      try {
+        await authTempSave(submitData)
+        this.$message.success('操作成功')
+        this.getList()
+        this.addCardVisible = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 授权牌模板上传
+    uploadTempImageSuccess({ response, file, fileList }) {
+      this.tempList = fileList
+      this.formData.templateImage = response.data
+    },
+    handleLogoImageRemove({ file, fileList }) {
+      this.tempList = fileList
+      this.formData.templateImage = ''
     },
 
     // 表格序号
@@ -139,4 +294,17 @@ export default {
   display: block;
   margin: 0 auto;
 }
+
+.current-info {
+  font-size: 14px;
+  .supplier-name {
+    color: #333;
+    margin-right: 6px;
+  }
+  .supplier-change {
+    color: #409eff;
+    text-decoration: underline;
+    cursor: pointer;
+  }
+}
 </style>

+ 36 - 8
src/views/normal/club/device/edit.vue

@@ -45,9 +45,36 @@
         <el-input v-model="formData.productImage" class="hiddenInput" />
       </el-form-item>
        -->
-      <el-form-item label="授权牌:" prop="certificateImage">
+
+      <!-- **************** 新方法配置授权牌 START ******************* -->
+      <!-- <el-form-item label="授权牌:">
+        <el-radio-group v-model="formData.addQrCodeFlag" size="mini">
+          <el-radio :label="0" border>模板库生成</el-radio>
+          <el-radio :label="1" border>自定义上传</el-radio>
+        </el-radio-group>
+        <div style="margin-top: 12px">
+
+          <template v-if="formData.addQrCodeFlag === 1">
+            <upload-image
+              tip="建议尺寸:150px * 112px"
+              :image-list="certificateImageList"
+              :before-upload="beforeCertificateImageUpload"
+              @success="uploadCertificateImageSuccess"
+              @remove="handleCertificateImageRemove"
+            />
+            <el-input v-model="formData.certificateImage" class="hiddenInput" />
+          </template>
+          <el-image v-else style="width:148px;height:148px" />
+        </div>
+      </el-form-item> -->
+
+      <!-- **************** 新方法配置授权牌 End ******************* -->
+
+      <!-- **************** 旧方法配置授权牌 START ******************* -->
+      <!-- 上传授权牌 -->
+      <el-form-item v-if="formData.addQrCodeFlag === 1" label="授权牌:" prop="certificateImage">
         <upload-image
-          tip="建议尺寸:150px * 112px"
+          tip="建议尺寸:542px * 546px"
           :image-list="certificateImageList"
           :before-upload="beforeCertificateImageUpload"
           @success="uploadCertificateImageSuccess"
@@ -56,16 +83,17 @@
         <el-input v-model="formData.certificateImage" class="hiddenInput" />
       </el-form-item>
       <!-- 是否生成二维码授权牌 -->
-      <el-form-item label="生成二维码授权牌:">
+      <!-- <el-form-item label="生成二维码授权牌:">
         <el-radio-group v-model="formData.addQrCodeFlag" size="mini">
           <el-radio :label="0" border>否</el-radio>
           <el-radio :label="1" border :disabled="certificateImageList.length <= 0">是</el-radio>
         </el-radio-group>
-      </el-form-item>
+      </el-form-item> -->
       <!-- 二维码授权牌模板 -->
-      <el-form-item v-if="formData.addQrCodeFlag === 1" label="选择模板:">
+      <!-- <el-form-item v-if="formData.addQrCodeFlag === 1" label="选择模板:">
         <auth-card-template v-model="formData.addTemplateType" :image-list="certificateImageList" />
-      </el-form-item>
+      </el-form-item> -->
+      <!-- **************** 旧方法配置授权牌 END ******************* -->
 
       <el-form-item label="购买渠道:">
         <el-input v-model="formData.purchaseWay" placeholder="请输入购买渠道" />
@@ -105,10 +133,10 @@ import { saveProduct, getProductById, fetchProductSelectList } from '@/api/produ
 import { fetchBrandList } from '@/api/brand'
 import { mapGetters } from 'vuex'
 import { isSnCode } from '@/utils/validate'
-import AuthCardTemplate from '@/views/components/AuthCardTemplate/index.vue'
+// import AuthCardTemplate from '@/views/components/AuthCardTemplate/index.vue'
 import { getStorage } from '@/utils/storage'
 export default {
-  components: { UploadImage, AuthCardTemplate },
+  components: { UploadImage },
   data() {
     const valideSNcode = (rules, value, callback) => {
       if (!isSnCode(value)) {

+ 143 - 12
src/views/normal/club/edit.vue

@@ -32,6 +32,14 @@
       <el-form-item label="联系电话:" prop="mobile">
         <el-input v-model="formData.mobile" placeholder="请输入联系方式" clearable />
       </el-form-item>
+
+      <el-form-item label="认证编号:" prop="authCode">
+        <el-input v-model="formData.authCode" placeholder="请输认证编号" clearable />
+      </el-form-item>
+
+      <el-form-item label="认证日期:" prop="authDate">
+        <el-date-picker v-model="formData.authDate" type="date" placeholder="选择日期" style="width: 100%" />
+      </el-form-item>
       <!-- <el-form-item label="手机号:" prop="userMobile">
         <el-input v-model="formData.userMobile" placeholder="请输入手机号" clearable maxlength="11" show-word-limit />
       </el-form-item> -->
@@ -46,6 +54,7 @@
         />
       </el-form-item>
       <el-form-item label="门头照:" prop="banner">
+        <div class="form-label-tip">至少上传一张机构门店图,最多上传6张)</div>
         <el-input v-show="false" v-model="formData.banner" />
         <upload-image
           tip="至少上传一张,最多6张;建议尺寸:542px * 542px"
@@ -57,6 +66,50 @@
         />
       </el-form-item>
 
+      <!-- **************** 新方法配置授权牌 START ******************* -->
+      <el-form-item label="授权牌:">
+        <el-radio-group v-model="formData.authImageType" size="mini">
+          <el-radio :label="1" border>模板库生成</el-radio>
+          <el-radio :label="2" border>自定义上传</el-radio>
+        </el-radio-group>
+        <div style="margin-top: 12px">
+          <template v-if="formData.authImageType === 2">
+            <upload-image
+              tip="建议尺寸:542px * 546px"
+              :image-list="authImageList"
+              :before-upload="beforeAuthImageUpload"
+              @success="uploadAuthImageSuccess"
+              @remove="handleAuthImageRemove"
+            />
+            <el-input v-show="false" v-model="formData.authImage" />
+          </template>
+          <template v-else>
+            <el-image
+              v-if="formData.authImage"
+              style="width: 148px; height: 148px"
+              :src="formData.authImage"
+              :preview-src-list="[formData.authImage]"
+            />
+          </template>
+        </div>
+      </el-form-item>
+
+      <el-form-item v-if="formData.authImageType === 1" label="授权牌logo:">
+        <div class="form-label-tip">授权牌logo(提示:授权牌logo与机构名称组合)</div>
+        <div style="margin-top: 8px">
+          <upload-image
+            tip="建议尺寸:212px * 98px"
+            :image-list="authImageLogoList"
+            :before-upload="beforeAuthImageLogoUpload"
+            @success="uploadAuthImageLogoSuccess"
+            @remove="handleAuthImageLogoRemove"
+          />
+          <el-input v-show="false" v-model="formData.authImageLogo" />
+        </div>
+      </el-form-item>
+
+      <!-- **************** 新方法配置授权牌 End ******************* -->
+
       <el-form-item label="机构类型:">
         <el-radio-group v-model="formData.firstClubType">
           <el-radio :label="1">医美</el-radio>
@@ -146,6 +199,7 @@ import { mapGetters } from 'vuex'
 import { saveBrandAuth, getAuthFormData } from '@/api/auth'
 import { getAddress } from '@/api/common'
 import { isPoint, isMobile, isNumber } from '@/utils/validate'
+import { formatDate } from '@/utils'
 
 export default {
   components: {
@@ -212,7 +266,13 @@ export default {
         empNum: '',
         firstClubType: 1,
         secondClubType: 1,
-        medicalLicenseImage: ''
+        medicalLicenseImage: '',
+        // 新增授权牌字段
+        authCode: '',
+        authDate: '',
+        authImageType: '',
+        authImageLogo: '',
+        authImage: ''
       },
       rules: {
         name: [{ required: true, message: '机构名称不能为空', trigger: ['blur', 'change'] }],
@@ -225,7 +285,8 @@ export default {
         remarks: [{ required: true, message: '店铺备注不能为空', trigger: 'blur' }],
         medicalLicenseImage: [{ required: true, message: '请上传医疗许可证', trigger: 'change' }],
         empNum: [{ required: true, message: '员工人数不能为空', trigger: 'change' }],
-        userMobile: [{ validator: validatePhoneNumber, trigger: 'change' }]
+        userMobile: [{ validator: validatePhoneNumber, trigger: 'change' }],
+        authImageLogo: [{ required: true, message: '请上传授权牌logo', trigger: 'change' }]
       },
       // logo图片列表
       logoList: [],
@@ -234,7 +295,11 @@ export default {
       // 级联选择的地址
       address: '',
       // 医疗许可证图片
-      licenseImageList: []
+      licenseImageList: [],
+      // 授权牌照图片列表
+      authImageList: [],
+      // 授权牌logo列表
+      authImageLogoList: []
     }
   },
   computed: {
@@ -320,8 +385,13 @@ export default {
         this.formData.customFlag = res.data.customFlag
         this.formData.remarks = res.data.remarks
 
-        this.logoList = [{ name: '', url: res.data.logo }]
-        this.bannerList = res.data.bannerList.map((item) => ({ name: '', url: item }))
+        if (res.data.logo) {
+          this.logoList = [{ name: '机构logo', url: res.data.logo }]
+        }
+
+        if (res.data.bannerList.length > 0) {
+          this.bannerList = res.data.bannerList.map((item, index) => ({ name: `门头照${index}`, url: item }))
+        }
 
         this.formData.address = [res.data.provinceId, res.data.cityId, res.data.townId]
         // this.formData.address = '广东省/深圳市/福田区'
@@ -331,11 +401,28 @@ export default {
         this.formData.empNum = res.data.empNum
         this.formData.firstClubType = res.data.firstClubType
         this.formData.secondClubType = res.data.secondClubType
+
         this.formData.medicalLicenseImage = res.data.medicalLicenseImage
+        if (res.data.medicalLicenseImage) {
+          this.licenseImageList = [{ name: '医疗许可证', url: res.data.medicalLicenseImage }]
+        }
 
         this.formData.userMobile = res.data.userMobile
 
-        this.licenseImageList = [{ name: '', url: res.data.medicalLicenseImage }]
+        // 授权牌相关字段
+        this.formData.authCode = res.data.authCode
+        this.formData.authDate = res.data.authDate
+        this.formData.authImageType = res.data.authImageType
+
+        this.formData.authImageLogo = res.data.authImageLogo
+        if (res.data.authImageLogo) {
+          this.authImageLogoList = [{ name: '授权牌logo', url: res.data.authImageLogo }]
+        }
+
+        this.formData.authImage = res.data.authImage
+        if (res.data.authImage) {
+          this.authImageList = [{ name: '自定义授权牌', url: res.data.authImage }]
+        }
       })
     },
 
@@ -357,7 +444,12 @@ export default {
           firstClubType,
           secondClubType,
           medicalLicenseImage,
-          userMobile
+          userMobile,
+          authCode,
+          authDate,
+          authImageType,
+          authImageLogo,
+          authImage
         } = this.formData
 
         const authUserId = this.proxyInfo?.authUserId || this.authUserId
@@ -380,11 +472,17 @@ export default {
           firstClubType,
           secondClubType,
           medicalLicenseImage,
-          userMobile
+          userMobile,
+          authCode,
+          authDate: authDate ? formatDate(authDate, 'yyyy.MM.DD') : '',
+          authImageType,
+          authImageLogo,
+          authImage
         }
 
         data.bannerList = this.bannerList.map((item) => (item.response ? item.response.data : item.url))
-
+        console.log(data)
+        // return
         saveBrandAuth(data)
           .then((res) => {
             const h = this.$createElement
@@ -393,12 +491,12 @@ export default {
               message: h('i', { style: 'color: #333' }, `已修改授权机构:"${this.formData.name}"`),
               duration: 3000
             })
-            this.$refs.submitForm.resetFields()
+            // this.$refs.submitForm.resetFields()
             this.$store.dispatch('tagsView/delView', this.$route)
             this.$router.push('/club/list')
           })
           .catch((err) => {
-            this.$message.danger(err.msg)
+            console.log(err)
           })
       })
     },
@@ -458,6 +556,40 @@ export default {
         this.$message.error('医疗许可证图片大小不能超过 1MB!')
       }
       return flag
+    },
+
+    // 授权牌照上传
+    beforeAuthImageUpload(file) {
+      const flag = file.size / 1024 / 1024 < 5
+      if (!flag) {
+        this.$message.error('上传授权牌图片大小不能超过 5MB!')
+      }
+      return flag
+    },
+    uploadAuthImageSuccess({ response, file, fileList }) {
+      this.authImageList = fileList
+      this.formData.authImage = response.data
+    },
+    handleAuthImageRemove({ file, fileList }) {
+      this.authImageList = fileList
+      this.formData.authImage = ''
+    },
+
+    // 授权牌logo上传
+    beforeAuthImageLogoUpload(file) {
+      const flag = file.size / 1024 / 1024 < 5
+      if (!flag) {
+        this.$message.error('上传授权牌图片大小不能超过 5MB!')
+      }
+      return flag
+    },
+    uploadAuthImageLogoSuccess({ response, file, fileList }) {
+      this.authImageLogoList = fileList
+      this.formData.authImageLogo = response.data
+    },
+    handleAuthImageLogoRemove({ file, fileList }) {
+      this.authImageLogoList = fileList
+      this.formData.authImageLogo = ''
     }
   }
 }
@@ -484,7 +616,6 @@ export default {
     width: 140px;
   }
 }
-
 .attributes {
   padding-top: 16px;
 }

+ 137 - 26
src/views/normal/settings/auth-card/index.vue

@@ -1,33 +1,53 @@
 <template>
   <div class="app-container">
+    <!-- 筛选 -->
     <div class="filter-container">
       <div class="filter-control">
-        <el-button type="primary">一键生效</el-button>
+        <el-button type="primary" @click="authTempUpdate">一键生效</el-button>
       </div>
     </div>
     <div class="tip">提示:请勿频繁更新授权牌模板,如有需要,在更新授权牌模板后点击左上角一键生效按钮即可</div>
+
+    <!-- 列表 -->
     <el-table v-loading="listLoading" style="width: 100%" header-row-class-name="tableHeader" :data="list" border fit>
       <el-table-column :index="indexMethod" label="序号" type="index" width="80" align="center" />
       <el-table-column label="授权牌模板" align="center" width="200px">
         <template slot-scope="{ row }">
-          <el-tooltip effect="dark" content="点击查看全图" placement="top-start">
-            <el-image :src="row.image" :preview-src-list="[row.image]" fit="cover" />
-          </el-tooltip>
+          <!-- <el-tooltip effect="dark" content="点击查看全图" placement="top-start">
+            <el-image :src="row.templateImage" :preview-src-list="[row.templateImage]" fit="cover" />
+          </el-tooltip> -->
+          <el-image :src="row.templateImage" fit="cover" />
         </template>
       </el-table-column>
       <el-table-column label="使用位置" align="center">
         <template slot-scope="{ row }">
-          <el-checkbox v-model="row.isClubAuth">机构认证</el-checkbox>
-          <el-checkbox v-model="row.isDeviceAuth">设备认证</el-checkbox>
+          <el-radio v-model="row.authFlag" :label="1" @change="onClubAuthTempChange(row)">机构认证</el-radio>
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" width="280px">
-        <template>
-          <el-button type="primary" size="mini">查看</el-button>
-          <el-button type="primary" size="mini">编辑</el-button>
+        <template slot-scope="{ row }">
+          <el-button type="primary" size="mini" @click="onPreview(row)">查看</el-button>
+          <el-button type="primary" size="mini" @click="onDragQrcode(row)">编辑</el-button>
         </template>
       </el-table-column>
     </el-table>
+
+    <!-- 拖拽选取二维码位置 -->
+    <el-dialog :title="dialogTitle" :visible.sync="dragDialogVisible" :fullscreen="fullscreen" @closed="onDragClose">
+      <drag-layout
+        v-if="dragVisible"
+        :qw="current.qw"
+        :qh="current.qh"
+        :old-x="current.oldX"
+        :old-y="current.oldY"
+        :image="current.templateImage"
+        :size="current.templateSize"
+        :is-drag="isDrag"
+        @confirm="onDragConfirm"
+        @cancel="dragDialogVisible = false"
+      />
+    </el-dialog>
+
     <!-- 页码 -->
     <pagination
       :total="total"
@@ -38,43 +58,134 @@
   </div>
 </template>
 <script>
+import DragLayout from '@/components/DragLayout/index.vue'
+import { authTempSave, authTempUpdate, fetchAuthTempList } from '@/api/system'
+import { mapGetters } from 'vuex'
+
 export default {
+  components: {
+    DragLayout
+  },
   data() {
     return {
+      dragDialogVisible: false,
+      dragVisible: false,
       listLoading: false,
       listQuery: {
-        shopId: '',
+        listType: 2,
+        authUserId: '',
         pageSize: 15,
         pageNum: 1
       },
       list: [],
       total: 0,
-      formData: {
-        image: '',
-        shopId: '',
-        isClubAuth: false,
-        isDeviceAuth: false
-      }
+      current: null,
+      dialogTitle: '设置二维码位置',
+      fullscreen: true,
+      isDrag: true
     }
   },
+  computed: {
+    ...mapGetters(['authUserId'])
+  },
   created() {
+    this.listQuery.authUserId = this.authUserId
     this.getList()
   },
   methods: {
+    // 一键生效
+    async authTempUpdate() {
+      try {
+        await authTempUpdate({ authUserId: this.authUserId })
+        this.$message.success('模板更新成功')
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 确定二维码坐标
+    async onDragConfirm(e) {
+      if (!this.isDrag) return (this.dragDialogVisible = false)
+      try {
+        await authTempSave({
+          templateId: this.current.templateId,
+          qrPosition: [e.x, e.y].join(',')
+        })
+        this.$message.success('模板修改成功')
+        this.getList()
+        this.dragDialogVisible = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 选中模板切换
+    async onClubAuthTempChange(row) {
+      this.list.forEach((item) => {
+        if (item.templateId !== row.templateId) item.authFlag = 0
+      })
+      try {
+        await authTempSave({
+          authUserId: this.authUserId,
+          templateId: row.templateId,
+          authFlag: 1
+        })
+        this.getList()
+        this.$message.success('模板使用成功')
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 预览图片
+    onPreview(row) {
+      this.fullscreen = false
+      this.dialogTitle = '预览'
+      this.isDrag = false
+      this.onDragQrcode(row)
+    },
+
+    // 编辑二维码
+    onDragQrcode(row) {
+      this.current = row
+      if (row.qrSize) {
+        this.current.qw = parseFloat(row.qrSize)
+        this.current.qh = parseFloat(row.qrSize)
+      }
+      const arrs = row.qrPosition ? row.qrPosition.split(',') : [0, 0]
+      this.current.oldX = parseFloat(arrs[0])
+      this.current.oldY = parseFloat(arrs[1])
+      this.dragDialogVisible = true
+      this.dragVisible = true
+    },
+
+    // 拖拽框关闭
+    onDragClose() {
+      console.log(123)
+      this.fullscreen = true
+      this.dialogTitle = '设置二维码位置'
+      this.isDrag = true
+      this.dragVisible = false
+    },
+
     // 获取授权牌模板列表
     getList() {
-      this.list = [
-        {
-          shopName: 'ROSS',
-          image: 'https://picsum.photos/500/400'
-        }
-      ]
-      console.log(123)
+      this.list = []
+      this.listQuery.pageNum = 1
+      this.fetchAuthTempList(this.listQuery)
     },
 
-    // 选中供应商
-    onSupplierSelected(e) {
-      console.log(e)
+    // 获取授权牌模板列表
+    async fetchAuthTempList() {
+      this.listLoading = true
+      try {
+        const res = await fetchAuthTempList(this.listQuery)
+        this.list = res.data.list
+        this.total = res.data.total
+        this.listLoading = false
+      } catch (error) {
+        console.log(error)
+      }
     },
 
     // 表格序号