Browse Source

Merge branch 'developerC' of http://git.caimei365.com/caimei365/caimei365-manager-ui into developer

xiebaomin 1 year ago
parent
commit
5217b6b3b0

+ 20 - 0
src/api/serviceSettlement/contract.js

@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+
+export const getContractList = (params) => request({
+  url: '/providers/contract/list',
+  method: 'get',
+  params
+})
+
+export const addContract = (data) => request({
+  url: '/providers/contract/add',
+  method: 'post',
+  data
+})
+
+export const getContractDetail = (params) => request({
+  url: '/providers/contract/from',
+  method: 'get',
+  params
+})
+

+ 37 - 0
src/api/serviceSettlement/service.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export const getAddress = (params) => request({
+  url: 'https://zplma.caimei365.com' + '/address/select',
+  method: 'get',
+  params
+})
+
+export const getProviderList = (params) => request({
+  url: '/providers/list',
+  method: 'get',
+  params
+})
+
+export const addProvider = (data) => request({
+  url: '/providers/add ',
+  method: 'post',
+  data
+})
+
+export const getProviderDetail = (params) => request({
+  url: '/providers/from',
+  method: 'get',
+  params
+})
+
+export const updatedStatus = (data) => request({
+  url: `/providers/updateStatus?userIds=${data.userIds}&status=${data.status}`,
+  method: 'post',
+  data
+})
+
+export const selectUploadImage = (data) => request({
+  url: '/providers/download/image',
+  method: 'post',
+  data
+})

+ 32 - 7
src/components/UploadImage/index.vue

@@ -18,10 +18,10 @@
     >
       <div v-if="tip" slot="tip" class="el-upload__tip">{{ tip }}</div>
       <button v-if="trigger" ref="trigger" slot="trigger" size="small" type="primary">选取文件</button>
-      <i slot="default" class="el-icon-plus" />
+      <i slot="default" class="el-icon-plus"></i>
     </el-upload>
     <el-dialog :visible.sync="dialogVisible">
-      <img width="100%" :src="dialogImageUrl">
+      <img width="100%" :src="dialogImageUrl" />
     </el-dialog>
   </div>
 </template>
@@ -70,16 +70,14 @@ export default {
   data() {
     return {
       dialogVisible: false,
-      dialogImageUrl: ''
+      dialogImageUrl: '',
+      chooseState: true
     }
   },
   computed: {
     ...mapGetters(['token']),
-    chooseState() {
-      return this.imageList.length < this.limit
-    },
     action() {
-      return process.env.VUE_APP_CORE_API + '/tools/image/upload/multi'
+      return process.env.VUE_APP_BASE_API + '/formData/MultiPictareaddData'
     },
     headers() {
       return {
@@ -87,13 +85,32 @@ export default {
       }
     }
   },
+  watch: {
+    'imageList': {
+      handler(val) {
+        if (val.length === this.limit) {
+          this.chooseState = false
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
   methods: {
     // 上传成功
     uploadImageSuccess(response, file, fileList) {
+      if (fileList.length === this.limit) {
+        this.chooseState = false
+      }
       this.$emit('success', { response, file, fileList })
     },
     // 删除
     handleImageRemove(file, fileList) {
+      if (fileList.length === this.limit) {
+        this.chooseState = false
+      } else {
+        this.chooseState = true
+      }
       this.$emit('remove', { file, fileList })
     },
     // 上传失败
@@ -110,3 +127,11 @@ export default {
   }
 }
 </script>
+
+<style lang="scss" scoped>
+.el-upload-hidden {
+  ::v-deep .el-upload--picture-card {
+    display: none !important;
+  }
+}
+</style>

+ 3 - 1
src/router/index.js

@@ -16,6 +16,7 @@ import financeRouter from './modules/finance'
 import keywordLibraryRouter from './modules/keywordLibrary'
 import memberRouter from './modules/member'
 import mallProtraitRouter from './modules/mallPortrait'
+import serviceSettlement from './modules/serviceSettlement'
 // import tableRouter from './modules/table'
 // import nestedRouter from './modules/nested'
 
@@ -103,7 +104,8 @@ export const asyncRoutes = [
   wechatRouter,
   memberRouter,
   keywordLibraryRouter,
-  mallProtraitRouter
+  mallProtraitRouter,
+  serviceSettlement
 ]
 
 /**

+ 59 - 0
src/router/modules/serviceSettlement.js

@@ -0,0 +1,59 @@
+import Layout from '@/layout'
+
+export default {
+  path: '/service',
+  component: Layout,
+  meta: { title: '服务商入驻', icon: 'link' },
+  name: 'Service',
+  redirect: '/service/settlement',
+  alwaysShow: true,
+  children: [
+    {
+      path: 'settlement',
+      name: 'Settlement',
+      meta: { title: '入驻列表', icon: 'component' },
+      redirect: '/service/settlement/list',
+      component: () => import('@/views/index'),
+      children: [
+        {
+          path: 'list',
+          hidden: true,
+          component: () => import('@/views/serviceSettlement/list/index.vue'),
+          name: 'SettlementList',
+          meta: { title: '入驻列表', activeMenu: '/service/settlement' }
+        },
+        {
+          path: 'edit',
+          hidden: true,
+          component: () => import('@/views/serviceSettlement/list/edit.vue'),
+          name: 'SettlementEdit',
+          meta: { title: '编辑列表', noCache: true, activeMenu: '/service/settlement' }
+        },
+        {
+          path: 'contract',
+          hidden: true,
+          component: () => import('@/views/index'),
+          name: 'Contract',
+          redirect: '/service/settlement/contract/list',
+          meta: { title: '合同管理', noCache: true, activeMenu: '/service/settlement' },
+          children: [
+            {
+              path: 'edit',
+              hidden: true,
+              component: () => import('@/views/serviceSettlement/contract/edit.vue'),
+              name: 'Edit',
+              meta: { title: '合同编辑', noCache: true, activeMenu: '/service/settlement' }
+            },
+            {
+              path: 'list',
+              hidden: true,
+              component: () => import('@/views/serviceSettlement/contract/index.vue'),
+              name: 'ContractList',
+              meta: { title: '合同列表', noCache: true, activeMenu: '/service/settlement' }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}

+ 109 - 0
src/views/serviceSettlement/components/CmQrcode/index.vue

@@ -0,0 +1,109 @@
+<template>
+  <div>
+    <el-dialog :visible.sync="dialogTableVisible" :width="width + 'px'">
+      <!--<vue-qr
+        ref="qrCode"
+        :text="qrUrl"
+        :size="width"
+        :correct-level="3"
+        :margin="5"
+        :callback="qrCodeCallback"
+      />-->
+      <div class="qrCode">
+        <el-image :src="itemObj.qrCodeImage" style="width: 200px;" />
+        <div class="btn">
+          <el-button type="primary" @click="downLoad">下载</el-button>
+          <el-button @click="dialogTableVisible = false">关闭</el-button>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+// import VueQr from 'vue-qr'
+import serviceMixin from '../../mixin/index'
+
+export default {
+  components: {
+    // VueQr
+  },
+  mixins: [serviceMixin],
+  props: {
+    showQrCode: {
+      type: Boolean,
+      default: () => false
+    },
+    width: {
+      type: Number,
+      default: () => 300
+    },
+    itemObj: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      dialogTableVisible: false,
+      dataUrl: ''
+    }
+  },
+  watch: {
+    showQrCode(val) {
+      if (val) {
+        this.dialogTableVisible = true
+      }
+    },
+    dialogTableVisible(val) {
+      if (!val) {
+        this.$emit('showQrCode')
+      }
+    }
+  },
+  methods: {
+    // qrCodeCallback(val) {
+    //  console.log(val)
+    //  this.dataUrl = val
+    // },
+    downLoad() {
+      this.$confirm('是否下载二维码?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async() => {
+        this.isLoading = true
+        // 使用a链接下载
+        const downUrl = `${process.env.VUE_APP_BASE_API}/providers/download/image?ids=${this.itemObj.id}&type=1`
+        this.downloadWithUrl(downUrl, this.itemObj.name)
+          .catch((err) => {
+            console.log(err)
+            this.$message.error(`下载${this.itemObj.name}二维码失败`)
+          })
+          .finally(() => {
+            this.$message({
+              message: '下载成功',
+              type: 'success'
+            })
+            this.dialogTableVisible = false
+          })
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.qrCode {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  .btn {
+    width: 100%;
+    display: flex;
+    justify-content: space-around;
+    margin-top: 30px;
+  }
+}
+</style>

+ 139 - 0
src/views/serviceSettlement/components/ServiceListSelector.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="supplier-list-selector">
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>服务商名称:</span>
+        <el-input v-model="listQuery.name" size="mini" placeholder="服务商名称" @keyup.enter.native="filterList" />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" size="mini" @click="filterList">查询</el-button>
+      </div>
+    </div>
+    <el-table
+      ref="multipleTable"
+      :data="list"
+      border
+      fit
+      highlight-current-row
+      header-row-class-name="tableHeader"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column prop="name" label="服务商名称" align="center" />
+      <el-table-column prop="serviceLevel" label="服务商级别" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{ scope.row.serviceLevel == '1' ? '一级' : scope.row.serviceLevel == '2' ? '二级' : '三级' }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="serviceCategory" label="服务商类型" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{ scope.row.serviceLevel == '1' ? '个人' : '企业' }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="contractStartDate" label="授权起始日期" align="center" />
+      <el-table-column prop="contractEndDate" label="授权截止日期" align="center" />
+      <el-table-column prop="status" label="状态" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{ scope.row.status === 90 ? '上线' : '下线' }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="updateTime" label="添加时间" align="center" />
+      <el-table-column prop="createBy" label="创建人" align="center" />
+    </el-table>
+    <!-- 页码 -->
+    <pagination :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList" />
+    <div class="control-footer">
+      <el-button type="primary" @click="onCancel">取消</el-button>
+      <el-button type="primary" @click="onConfirm">确认</el-button>
+    </div>
+  </div>
+</template>
+<script>
+import { getProviderList } from '@/api/serviceSettlement/service'
+
+export default {
+  name: 'ClubListSelector',
+  data() {
+    return {
+      listQuery: {
+        authUserId: '',
+        authParty: '',
+        auditStatus: '',
+        status: '',
+        name: '',
+        pageNum: 1, // 页码
+        pageSize: 10 // 分页
+      },
+      list: [],
+      total: 0,
+      multipleSelection: []
+    }
+  },
+  computed: {
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 取消
+    onCancel() {
+      this.multipleSelection = []
+      this.$emit('cancel', [])
+    },
+
+    // 确认
+    onConfirm() {
+      if (this.multipleSelection.length === 0) {
+        this.$message.warning('选择列表不能为空')
+        return
+      }
+      this.$emit('confirm', this.multipleSelection)
+    },
+
+    // 获取机构列表
+    async getList() {
+      const { data } = await getProviderList(this.listQuery)
+      this.list = data.results
+      this.total = data.totalRecord
+    },
+
+    // 筛选机构列表
+    filterList() {
+      this.listQuery.pageNum = 1
+      this.getList()
+    },
+
+    // 设置选中的行
+    setSelection() {
+      this.list.forEach((row) => {})
+    },
+
+    // 表格列选择
+    handleSelectionChange(rows) {
+      this.multipleSelection = rows
+    },
+
+    // 表格索引
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+::v-deep {
+  .pagination-container {
+    padding: 0 !important;
+  }
+}
+.control-footer {
+  text-align: right;
+  margin-top: 15px;
+}
+</style>

+ 164 - 0
src/views/serviceSettlement/components/UploadFile.vue

@@ -0,0 +1,164 @@
+<template>
+  <div>
+    <el-upload
+      ref="fileUpload"
+      :data="data"
+      :auto-upload="autoUpload"
+      :class="{ 'el-upload-hidden': !chooseState }"
+      :action="action"
+      :headers="headers"
+      :on-success="uploadSuccess"
+      :before-upload="beforeUpload"
+      :on-error="uploadError"
+      :limit="limit"
+      :multiple="multiple"
+      :accept="accept"
+      :file-list="fileList"
+    >
+      <div v-if="tip" slot="tip" class="el-upload__tip">{{ tip }}</div>
+      <el-button size="mini" type="primary">选择文件</el-button>
+    </el-upload>
+    <div v-for="(file, index) in dataList" :key="index" class="file-list">
+      <span>{{ file.name }} </span>
+      <div>
+        <el-button type="text" size="small" @click="handlePreview(file.response)">预览</el-button>
+        <el-button type="text" size="small" style="color: red" @click="handleRemove(index)">删除</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+export default {
+  name: 'UploadFile',
+  props: {
+    tip: {
+      type: String,
+      default: ''
+    },
+    multiple: {
+      type: Boolean,
+      default: false
+    },
+    limit: {
+      type: Number,
+      default: 1
+    },
+    accept: {
+      type: String,
+      default: '.docx,.pptx,.pdf'
+    },
+    listType: {
+      type: String,
+      default: 'picture-card'
+    },
+    fileList: {
+      type: Array,
+      default: () => []
+    },
+    uuid: {
+      type: Number,
+      default: 0
+    },
+    beforeUpload: {
+      type: Function,
+      default: () => true
+    },
+    autoUpload: {
+      type: Boolean,
+      default: true
+    },
+    data: {
+      type: Object,
+      default: () => ({})
+    },
+    mode: {
+      type: String,
+      default: 'brand'
+    },
+    list: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      dialogImageUrl: '',
+      dataList: []
+    }
+  },
+  computed: {
+    ...mapGetters(['token']),
+    chooseState() {
+      return this.dataList.length < this.limit
+    },
+    action() {
+      return process.env.VUE_APP_BASE_API + '/formData/MultiPictareaddData'
+    },
+    headers() {
+      return {
+        'X-Token': this.token
+      }
+    }
+  },
+  watch: {
+    list: {
+      handler(val) {
+        console.log(val)
+        this.dataList = val
+      },
+      deep: true
+    }
+  },
+  methods: {
+    // 上传成功
+    uploadSuccess(response, file, fileList) {
+      if (file.response.data) {
+        this.dataList.push(file)
+        console.log(response, file)
+        this.$emit('success', { fileList: this.dataList })
+      } else {
+        this.$message({
+          type: 'error',
+          message: '上传失败'
+        })
+      }
+    },
+    // 删除
+    handleRemove(index) {
+      this.dataList = this.dataList.filter((_, i) => index !== i)
+      this.$emit('remove', { fileList: this.dataList })
+    },
+    // 变化
+    handleChange(file, fileList) {
+      this.dataList = fileList
+      this.$emit('change', { fileList: this.dataList })
+    },
+    // 上传失败
+    uploadError(err, file, fileList) {
+      this.dataList = fileList
+      this.$emit('error', { err, file, fileList })
+    },
+    handlePreview(f) {
+      window.open(`https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(f.data)}`)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep .el-upload-list {
+  display: none;
+}
+::v-deep .el-upload__tip {
+  position: static;
+}
+.file-list {
+  display: flex;
+  justify-content: space-between;
+  padding-right: 30px;
+  margin-top: 10px;
+}
+</style>

+ 127 - 0
src/views/serviceSettlement/contract/edit.vue

@@ -0,0 +1,127 @@
+<template>
+  <div class="app-container">
+    <el-form ref="formLabelAlign" label-width="120px" :model="formLabelAlign" :rules="rules">
+      <el-form-item label="合同编号:" prop="contractId">
+        <el-input v-model="formLabelAlign.contractId" />
+      </el-form-item>
+      <el-form-item label="合同起始日期:" prop="contractStartDate">
+        <el-date-picker
+          v-model="formLabelAlign.contractStartDate"
+          type="date"
+          placeholder="选择日期"
+          value-format="yyyy-MM-dd"
+        />
+      </el-form-item>
+      <el-form-item label="合同截止日期:" prop="contractEndDate">
+        <el-date-picker
+          v-model="formLabelAlign.contractEndDate"
+          type="date"
+          placeholder="选择日期"
+          value-format="yyyy-MM-dd"
+        />
+      </el-form-item>
+      <el-form-item label="上传合同:" prop="contractRelateds">
+        <file-upload
+          :list="contractList"
+          :limit="20"
+          :before-upload="beforeUpload"
+          :tip="'支持上传pdf、docx、 pptx文件格式'"
+          @success="uploadSuccess($event, 'contractRelateds')"
+          @change="uploadChange($event, 'contractRelateds')"
+          @remove="handleRemove($event, 'contractRelateds')"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="submitForm('formLabelAlign')">保存</el-button>
+        <el-button @click="$router.back()">返回</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { getContractDetail, addContract } from '@/api/serviceSettlement/contract'
+import FileUpload from '../components/UploadFile.vue'
+import serviceMixin from '../mixin/index'
+import { mapGetters } from 'vuex'
+
+export default {
+  components: {
+    FileUpload
+  },
+  mixins: [serviceMixin],
+  data() {
+    return {
+      formLabelAlign: {},
+      ruleForm: {},
+      contractList: [],
+      serviceLevel: [{ id: 1, value: '一级' }, { id: 2, value: '二级' }, { id: 3, value: '三级' }],
+      rules: {
+        contractId: [{ required: true, message: '请输入合同编号', trigger: 'blur' }],
+        contractStartDate: [
+          { required: true, message: '请选择合同起始时间', trigger: 'change' }
+        ],
+        contractEndDate: [
+          { required: true, message: '请选择合同结束时间', trigger: 'change' }
+        ],
+        contractRelateds: [
+          { required: true, message: '请上传合同文件', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(['userId', 'username', 'fullName', 'phone']),
+    id() {
+      return this.$route.query.id || false
+    },
+    providersId() {
+      return Number(this.$route.query.providersId) || false
+    }
+  },
+  mounted() {
+    if (this.id) {
+      this.getInfo()
+    }
+  },
+  methods: {
+    async getInfo() {
+      const { data } = await getContractDetail({ providersContractId: this.id })
+      this.formLabelAlign = data
+      this.contractList = data.contractRelateds.map(e => ({ name: e.fileName, response: { data: e.image }}))
+    },
+    submitForm(form) {
+      this.$refs[form].validate(async valid => {
+        if (valid) {
+          const form = Object.assign(this.formLabelAlign, { providersId: this.providersId, createBy: this.fullName })
+          await addContract(form)
+          this.$router.back()
+        }
+      })
+    },
+    beforeUpload(file) {
+      console.log(file)
+      if (file.size / 1024 / 1024 < 50) {
+        return true
+      } else {
+        this.$message({
+          type: 'error',
+          message: '文件大小限制在50M以内'
+        })
+        return false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep .el-input,
+::v-deep .el-textarea__inner {
+  width: 500px;
+}
+::v-deep .el-form {
+  width: 650px;
+  margin: 0 auto;
+}
+</style>

+ 116 - 0
src/views/serviceSettlement/contract/index.vue

@@ -0,0 +1,116 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>合同编号:</span>
+        <el-input
+          v-model="listQuery.contractId"
+          placeholder=""
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>授权起止日期:</span>
+        <el-date-picker
+          v-model="time"
+          type="daterange"
+          unlink-panels
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          @change="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+        <el-button type="primary" @click="$router.push('/service/settlement/contract/edit?providersId=' + providersId)">添加</el-button>
+      </div>
+    </div>
+    <el-table
+      ref="table"
+      v-loading="isLoading"
+      :data="list"
+      border
+      :default-sort="{ prop: 'frequency', order: 'descending' }"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column
+        label="序号"
+        type="index"
+        sortable="custom"
+        align="center"
+        width="80"
+      />
+      <el-table-column prop="contractId" label="合同编号" align="center" />
+      <el-table-column prop="contractStartDate" label="合同起始日期" align="center" />
+      <el-table-column prop="contractEndDate" label="合同截止日期" align="center" />
+      <el-table-column prop="updateTime" label="添加时间" align="center" />
+      <el-table-column prop="createBy" label="创建人" align="center" />
+      <el-table-column label="操作" align="center" width="200">
+        <template slot-scope="{ row }">
+          <el-button
+            type="primary"
+            size="mini"
+            @click="$router.push('/service/settlement/contract/edit?id=' + row.id + '&providersId=' + providersId)"
+          >编辑</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { getContractList } from '@/api/serviceSettlement/contract'
+
+export default {
+  data() {
+    return {
+      listQuery: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      time: '',
+      list: [],
+      indexMethod: 0,
+      total: 10,
+      isLoading: false
+    }
+  },
+  computed: {
+    providersId() {
+      return this.$route.query.providersId || false
+    }
+  },
+  mounted() {
+    if (this.providersId) {
+      this.getList()
+    }
+  },
+  methods: {
+    async getList(obj) {
+      const form = Object.assign(this.listQuery, {
+        contractStartDate: obj ? obj[0] : '',
+        contractEndDate: obj ? obj[1] : '',
+        providersId: this.providersId || ''
+      })
+      const { data } = await getContractList(form)
+      this.list = data.results
+    },
+    handleSelectionChange() {}
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 298 - 0
src/views/serviceSettlement/list/edit.vue

@@ -0,0 +1,298 @@
+<template>
+  <div class="app-container">
+    <el-form
+      ref="formLabelAlign"
+      label-width="120px"
+      :model="formLabelAlign"
+      :rules="rules"
+    >
+      <el-form-item label="服务商名称:" prop="name">
+        <el-input v-model="formLabelAlign.name" placeholder="请输入服务商名称" />
+      </el-form-item>
+      <el-form-item label="服务商级别:" prop="serviceLevel">
+        <el-select v-model="formLabelAlign.serviceLevel" placeholder="请选择服务商级别">
+          <el-option
+            v-for="i in serviceLevel"
+            :key="i.id"
+            :label="i.value"
+            :value="i.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="服务商地区:" prop="area">
+        <el-cascader
+          ref="cascader"
+          :key="areaKey"
+          v-model="formLabelAlign.area"
+          :class="{ display: cascaderDisplay }"
+          clearable
+          :props="cascaderProps"
+          style="width: 100%"
+          :placeholder="cascaderPlaceholder"
+          @change="handleChange"
+        />
+      </el-form-item>
+      <el-form-item label="服务商类型:" prop="serviceCategory">
+        <el-select v-model="formLabelAlign.serviceCategory" placeholder="服务商类型">
+          <el-option
+            v-for="i in serviceCategoryList"
+            :key="i.id"
+            :label="i.value"
+            :value="i.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="联系人:" prop="linkMan">
+        <el-input v-model="formLabelAlign.linkMan" placeholder="请输入联系人" />
+      </el-form-item>
+      <el-form-item label="联系电话:" prop="mobile">
+        <el-input
+          v-model="formLabelAlign.mobile"
+          placeholder="请输入联系电话"
+          :maxlength="11"
+          oninput="value=value.replace(/[^\d]/g, '')"
+        />
+      </el-form-item>
+      <el-form-item label="简介图:" prop="textRelateds">
+        <image-upload
+          :limit="6"
+          :image-list="textRelateds"
+          @success="uploadSuccess($event, 'textRelateds')"
+          @remove="handleImageRemove($event, 'textRelateds')"
+        />
+      </el-form-item>
+      <el-form-item label="简介:" prop="text">
+        <el-input
+          v-model="formLabelAlign.text"
+          type="textarea"
+          :autosize="{ minRows: 8, maxRows: 10 }"
+          placeholder="请输入内容"
+        />
+      </el-form-item>
+      <el-form-item v-if="id" label="资格证书:">
+        <el-image style="width: 200px;" :src="formLabelAlign.qualificationImage" :preview-src-list="[formLabelAlign.qualificationImage]" />
+      </el-form-item>
+      <el-form-item label="资料上传:" prop="infoRelateds">
+        <file-upload
+          :list="infoList"
+          :limit="20"
+          :tip="'支持上传pdf、docx、 pptx文件格式'"
+          :before-upload="beforeUpload"
+          @success="uploadSuccess($event, 'infoRelateds')"
+          @change="uploadChange($event, 'infoRelateds')"
+          @remove="handleRemove($event, 'infoRelateds')"
+        />
+      </el-form-item>
+      <div v-if="!id">
+        <el-form-item label="合同:">
+          (由于是添加新服务商,必须添加合同,后续可去合同列表编辑及添加)
+        </el-form-item>
+        <el-form-item label="合同编号:" prop="contract.contractId">
+          <el-input v-model="formLabelAlign.contract.contractId" />
+        </el-form-item>
+        <el-form-item label="合同起始日期:" prop="contract.contractStartDate">
+          <el-date-picker
+            v-model="formLabelAlign.contract.contractStartDate"
+            type="date"
+            value-format="yyyy-MM-dd"
+            placeholder="选择日期"
+          />
+        </el-form-item>
+        <el-form-item label="合同截止日期:" prop="contract.contractEndDate">
+          <el-date-picker
+            v-model="formLabelAlign.contract.contractEndDate"
+            type="date"
+            placeholder="选择日期"
+            value-format="yyyy-MM-dd"
+          />
+        </el-form-item>
+        <el-form-item label="上传合同:" prop="contract.contractRelateds">
+          <file-upload
+            :limit="20"
+            :file-list="contractList"
+            :tip="'支持上传pdf、docx、 pptx文件格式'"
+            @success="contractUploadSuccess($event)"
+            @remove="contractHandleRemove($event)"
+          />
+        </el-form-item>
+      </div>
+      <el-form-item>
+        <el-button type="primary" @click="submitForm('formLabelAlign')">保存</el-button>
+        <el-button @click="$router.back()">返回</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import ImageUpload from '@/components/UploadImage/index.vue'
+import FileUpload from '../components/UploadFile.vue'
+import {
+  getAddress,
+  addProvider,
+  getProviderDetail
+} from '@/api/serviceSettlement/service'
+import serviceMixin from '../mixin/index'
+import { mapGetters } from 'vuex'
+
+export default {
+  components: {
+    ImageUpload,
+    FileUpload
+  },
+  mixins: [serviceMixin],
+  data() {
+    return {
+      formLabelAlign: {
+        address: '',
+        infoRelateds: [],
+        area: [],
+        textRelateds: [],
+        contract: {
+          contractRelateds: []
+        }
+      },
+      infoList: [],
+      contractList: [],
+      textRelateds: [],
+      areaKey: 0,
+      rules: {
+        name: [
+          { required: true, message: '请输入服务商名称', trigger: 'blur' },
+          { max: 50, message: '长度最多50个汉字', trigger: 'blur' }
+        ],
+        area: [{ required: true, message: '请选择地址', trigger: 'change' }],
+        serviceLevel: [
+          { required: true, message: '请选择服务商级别', trigger: 'change' }
+        ],
+        mobile: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
+        linkMan: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
+        text: [{ required: true, message: '请输入简介', trigger: 'blur' }],
+        serviceCategory: [
+          { required: true, message: '请选择服务商类型', trigger: 'change' }
+        ],
+        textRelateds: [{ required: true, message: '请上传简介图', trigger: 'change' }],
+        contract: {
+          contractId: [{ required: true, message: '请输入合同编号', trigger: 'blur' }],
+          contractStartDate: [
+            { required: true, message: '请选择合同起始时间', trigger: 'change' }
+          ],
+          contractEndDate: [
+            { required: true, message: '请选择合同结束时间', trigger: 'change' }
+          ],
+          contractRelateds: [
+            { required: true, message: '请上传合同文件', trigger: 'change' }
+          ]
+        },
+        infoRelateds: [
+          { required: true, message: '请上传资料文件', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(['userId', 'username', 'fullName', 'phone']),
+    id() {
+      return this.$route.query.id || false
+    },
+    cascaderPlaceholder() {
+      return this.cascaderDisplay ? this.formLabelAlign.address : '请选择所在区域'
+    },
+    cascaderDisplay() {
+      return this.formLabelAlign.area.length > 0
+    },
+    // 级联选择器
+    cascaderProps() {
+      return {
+        lazy: true,
+        lazyLoad: async(node, resolve) => {
+          const { level, data } = node
+          console.log(node)
+          const result = await getAddress({ parentId: data?.vId || 0, type: level })
+          const nodes = result.data.map((item) => ({
+            value: item.name,
+            label: item.name,
+            leaf: level >= 1,
+            vId: item.id
+          }))
+          resolve(nodes)
+        }
+      }
+    }
+  },
+  mounted() {
+    if (this.id) {
+      this.getInfo()
+    }
+  },
+  methods: {
+    async getInfo() {
+      const { data } = await getProviderDetail({ providersId: this.id })
+      this.formLabelAlign = data
+      this.infoList = data.infoRelateds.map((e, i) => ({ name: e.fileName, response: { data: e.image }}))
+      this.textRelateds = data.textRelateds.map((e, i) => ({ uid: i, url: e.image }))
+      this.formLabelAlign.area = data.address.split('/').map(e => e.trim())
+      setTimeout(() => {
+        this.areaKey = Math.random() * 1000
+      }, 500)
+    },
+    // 地区选择
+    handleChange($event) {
+      console.log($event)
+      this.formLabelAlign.address = $event.join(' / ')
+    },
+    // 合同上传
+    contractUploadSuccess($event) {
+      this.formLabelAlign.contract.contractRelateds = $event.fileList.map(e => ({ image: e.response?.data || '', fileName: e.name }))
+    },
+    contractHandleRemove($event) {
+      this.formLabelAlign.contract.contractRelateds = $event.fileList.map(e => ({ image: e.response?.data || '', fileName: e.name }))
+    },
+    submitForm(form) {
+      console.log(this.formLabelAlign)
+      this.$refs[form].validate(async(valid) => {
+        if (valid) {
+          try {
+            delete this.formLabelAlign.area
+            const form = Object.assign(this.formLabelAlign, { createBy: this.fullName })
+            await addProvider(form)
+            this.$message({ type: 'success', message: '添加成功' })
+            this.$router.back()
+          } catch (e) {
+            console.warn(e)
+          }
+        }
+      })
+    },
+    beforeUpload(file) {
+      console.log(file)
+      if (file.size / 1024 / 1024 < 50) {
+        return true
+      } else {
+        this.$message({
+          type: 'error',
+          message: '文件大小限制在50M以内'
+        })
+        return false
+      }
+    },
+    handleImageRemove($event, name) {
+      console.log($event, name)
+      this.formLabelAlign[name] = $event.fileList.map(e => ({
+        image: e.url
+      }))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep .el-input,
+::v-deep .el-textarea__inner {
+  width: 500px;
+}
+::v-deep .el-form {
+  width: 650px;
+  margin: 0 auto;
+}
+</style>

+ 312 - 0
src/views/serviceSettlement/list/index.vue

@@ -0,0 +1,312 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>服务商名称:</span>
+        <el-input
+          v-model="listQuery.name"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>服务商级别:</span>
+        <el-select v-model="listQuery.serviceLevel" placeholder="请选择服务商级别" @change="getList">
+          <el-option
+            v-for="(i, index) in serviceLevel"
+            :key="index"
+            :label="i.value"
+            :value="i.id"
+          />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>服务商类型:</span>
+        <el-select v-model="listQuery.serviceCategory" placeholder="服务商类型" @change="getList">
+          <el-option
+            v-for="(i, index) in serviceCategoryList"
+            :key="index"
+            :label="i.value"
+            :value="i.id"
+            @change="getList"
+          />
+        </el-select>
+      </div>
+      <div class="filter-control">
+        <span>资格证书编号:</span>
+        <el-input
+          v-model="listQuery.qualificationId"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>授权起止日期:</span>
+        <el-date-picker
+          v-model="time"
+          type="daterange"
+          unlink-panels
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          @change="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+        <el-button type="primary" @click="$router.push('/service/settlement/edit')">添加</el-button>
+        <el-button type="primary" @click="batchOffline(3)">下线</el-button>
+        <el-button type="primary" @click="batchOffline(1)">下载资格证书</el-button>
+        <el-button type="primary" @click="batchOffline(2)">下载二维码</el-button>
+      </div>
+    </div>
+
+    <el-table
+      ref="table"
+      v-loading="isLoading"
+      :data="list"
+      border
+      :default-sort="{ prop: 'frequency', order: 'descending' }"
+      @selection-change="handleSelectionChange"
+    >
+      <!--<el-table-column type="selection" width="55" align="center" />-->
+      <el-table-column
+        label="序号"
+        type="index"
+        sortable="custom"
+        align="center"
+        width="80"
+      />
+      <el-table-column prop="name" label="服务商名称" align="center" />
+      <el-table-column prop="serviceLevel" label="服务商级别" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{
+              scope.row.serviceLevel == "1"
+                ? "一级"
+                : scope.row.serviceLevel == "2"
+                  ? "二级"
+                  : "三级"
+            }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="serviceCategory" label="服务商类型" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{ scope.row.serviceCategory == "1" ? "个人" : "企业" }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="contractStartDate" label="授权起始日期" align="center" />
+      <el-table-column prop="contractEndDate" label="授权截止日期" align="center" />
+      <el-table-column prop="status" label="状态" align="center">
+        <template slot-scope="scope">
+          <div>
+            {{ scope.row.status === 90 ? "上线" : "下线" }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="updateTime" label="添加时间" align="center" />
+      <el-table-column prop="createBy" label="创建人" align="center" />
+      <el-table-column label="操作" align="center" width="200">
+        <template slot-scope="{ row }">
+          <div class="labelCenter">
+            <el-button
+              type="primary"
+              size="mini"
+              @click="handleStatus({ userIds: row.userId, status: row.status })"
+            >{{ row.status === 91 ? "上线" : "下线" }}</el-button>
+            <el-button
+              type="primary"
+              size="mini"
+              @click="$router.push('/service/settlement/edit?id=' + row.id)"
+            >编辑</el-button>
+            <el-button type="primary" size="mini" @click="showQrCode(row)">二维码</el-button>
+            <el-button
+              type="primary"
+              size="mini"
+              @click="
+                $router.push('/service/settlement/contract/list?providersId=' + row.id)
+              "
+            >合同管理</el-button>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <qr-code
+      ref="qrCode"
+      :item-obj="providerItem"
+      :show-qr-code="showQrImag"
+      @showQrCode="showQrCode"
+    />
+
+    <el-dialog title="选择服务商" :visible.sync="exportDialogVisible" width="70%">
+      <service-list-selector
+        v-if="exportDialogVisible"
+        @cancel="onSelectorCancel"
+        @confirm="onSelectorConfirm"
+      />
+    </el-dialog>
+
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import QrCode from '../components/CmQrcode/index.vue'
+import ServiceListSelector from '../components/ServiceListSelector.vue'
+import { getProviderList, updatedStatus } from '@/api/serviceSettlement/service'
+import serviceMixin from '../mixin/index'
+
+export default {
+  components: {
+    QrCode,
+    ServiceListSelector
+  },
+  mixins: [serviceMixin],
+  data() {
+    return {
+      listQuery: {
+        pageNum: 1,
+        pageSize: 10,
+        serviceCategory: '',
+        serviceLevel: ''
+      },
+      time: '',
+      showQrImag: false,
+      isLoading: false,
+      exportDialogVisible: false,
+      list: [],
+      indexMethod: 1,
+      total: 0,
+      selectType: 0,
+      providerItem: {}
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList(obj) {
+      const form = Object.assign(this.listQuery, {
+        contractStartDate: obj ? obj[0] : '',
+        contractEndDate: obj ? obj[1] : '',
+        providersId: this.providersId || ''
+      })
+      const { data } = await getProviderList(form)
+      this.list = data.results
+      this.total = data.totalRecord
+    },
+    showQrCode($event) {
+      if ($event) {
+        this.providerItem = $event
+      }
+      this.showQrImag = !this.showQrImag
+    },
+    // 批量下线
+    batchOffline(type) {
+      this.selectType = type
+      this.exportDialogVisible = true
+    },
+    handleSelectionChange() {},
+    async handleStatus($event) {
+      this.$confirm('是否下线该服务商?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async() => {
+        try {
+          const data = await updatedStatus({
+            userIds: $event.userIds,
+            status: $event.status === 91 ? 90 : 91
+          })
+          if (data.code === 0) {
+            this.$message({
+              message: '更新成功',
+              type: 'success'
+            })
+            this.getList()
+          }
+        } catch (error) {
+          console.log(error)
+        }
+      })
+    },
+    onSelectorCancel() {
+      this.exportDialogVisible = false
+    },
+    downLoadImage(options) {
+      try {
+        this.$confirm('是否下载所选的内容?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(async() => {
+          this.isLoading = true
+          // 使用a链接下载
+          const downUrl = `${process.env.VUE_APP_BASE_API}/providers/download/image?ids=${options.ids}&type=${options.type}`
+          const confirmText = options.type === 1 ? '服务商资格证书' : '服务商二维码'
+          this.downloadWithUrl(downUrl, confirmText)
+            .catch((err) => {
+              console.log(err)
+              this.$message.error(`下载${confirmText}失败`)
+            })
+            .finally(() => {
+              this.isLoading = false
+            })
+        })
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    onSelectorConfirm($event) {
+      this.exportDialogVisible = false
+      const toSelect = {
+        3: () =>
+          this.handleStatus({
+            userIds: $event.map((e) => e.userId).join(','),
+            status: 90
+          }),
+        1: () =>
+          this.downLoadImage({
+            ids: $event.map((e) => e.id).join(','),
+            type: this.selectType
+          }),
+        2: () =>
+          this.downLoadImage({
+            ids: $event.map((e) => e.id).join(','),
+            type: this.selectType
+          })
+      }
+      toSelect[this.selectType]()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.labelCenter {
+  display: grid;
+  grid-template-rows: repeat(2, 1fr);
+  grid-template-columns: repeat(2, 1fr);
+  grid-gap: 10px;
+  .el-button {
+    width: 80px;
+    margin-left: 0;
+  }
+}
+</style>

+ 35 - 0
src/views/serviceSettlement/mixin/index.js

@@ -0,0 +1,35 @@
+import { getToken } from '@/utils/auth'
+
+export default {
+  data() {
+    return {
+      serviceLevel: [{ id: '', value: '请选择' }, { id: 1, value: '一级' }, { id: 2, value: '二级' }, { id: 3, value: '三级' }],
+      serviceCategoryList: [{ id: '', value: '请选择' }, { id: 1, value: '个人' }, { id: 2, value: '企业' }]
+    }
+  },
+  methods: {
+    downloadWithUrl(url, name, options = {}) {
+      return fetch(url, {
+        headers: {
+          'x-token': getToken()
+        },
+        ...options
+      })
+        .then((data) => data.blob())
+        .then((res) => {
+          const a = document.createElement('a')
+          a.href = URL.createObjectURL(res)
+          a.download = name
+          a.click()
+        })
+    },
+    uploadSuccess($event, name) {
+      console.log($event)
+      this.formLabelAlign[name] = $event.fileList.map(e => ({ image: e.response?.data || '', fileName: e.name }))
+    },
+    handleRemove($event, name) {
+      console.log($event, name)
+      this.formLabelAlign[name] = $event.fileList.map(e => ({ image: e.response?.data || '', fileName: e.name }))
+    }
+  }
+}