Ver Fonte

Merge branch 'main'

xiaodai2017 há 1 ano atrás
pai
commit
c4028e0ca6
34 ficheiros alterados com 2208 adições e 435 exclusões
  1. 1 0
      public/index.html
  2. 7 0
      src/apis/components/letter.js
  3. 5 0
      src/apis/components/search.js
  4. 6 0
      src/components/flow-chart/style.css
  5. 1 1
      src/components/manage/index.js
  6. 187 0
      src/components/manage/src/letter/_columns.js
  7. 134 0
      src/components/manage/src/letter/_template.js
  8. 111 0
      src/components/manage/src/letter/index.vue
  9. 50 0
      src/components/manage/src/letter/previewModal.vue
  10. 247 0
      src/components/manage/src/letter/xlsxUploadModal.vue
  11. 0 120
      src/components/manage/src/settlementBudgetLetter/_template.js
  12. 0 53
      src/components/manage/src/settlementBudgetLetter/index.vue
  13. 0 187
      src/components/manage/src/settlementBudgetLetter/xlsxUploadModal.vue
  14. 0 0
      src/components/manage/src/settlementBudgetLetter/评估(结算or预算)函管理
  15. 7 0
      src/components/search/index.js
  16. 48 0
      src/components/search/src/store.vue
  17. 6 1
      src/plugins/components.js
  18. 127 0
      src/utils/fileType.js
  19. 1 1
      src/views/customerService/demandOrder/components/settlementLetter/xlsxUploadModal.vue
  20. 1 0
      src/views/customerService/demandOrder/components/waitCustomerConfirm.vue
  21. 123 9
      src/views/customerService/demandOrder/components/waitSupplierConfirm.vue
  22. 3 3
      src/views/customerService/demandOrder/detail.vue
  23. 1 4
      src/views/customerService/invoiceApply/detail.vue
  24. 5 9
      src/views/customerService/invoiceApply/index.vue
  25. 1 1
      src/views/customerService/workbench/components/activityLetter/letterEditing.vue
  26. 2 14
      src/views/customerService/workbench/components/baseForm.vue
  27. 8 3
      src/views/customerService/workbench/components/evaluateBudgetLetter/index.vue
  28. 3 3
      src/views/customerService/workbench/components/evaluateBudgetLetter/letterEditing.vue
  29. 1 1
      src/views/customerService/workbench/components/planBudgetLetter/xlsxUploadModal.vue
  30. 113 19
      src/views/customerService/workbench/detail.vue
  31. 10 6
      src/views/customerService/workbench/index.vue
  32. 610 0
      src/views/operate/shop/components/baseForm.vue
  33. 164 0
      src/views/operate/shop/components/mapModal.vue
  34. 225 0
      src/views/operate/shop/detail.vue

+ 1 - 0
public/index.html

@@ -34,6 +34,7 @@
   <script src="./static/vue-router.min.js" rel="preload" as="script"></script>
   <script src="./static/axios@0.21.0.min.js" rel="preload" as="script"></script>
   <script src="./static/dingtalk.open.2.10.3.js" rel="preload" as="script"></script>
+  <script src="//api.map.baidu.com/api?v=2.0&ak=KNAs4HdhPV2Nek2CF4NTGXPvwzDZbfSe"></script>
   <script src="./static/crypto.js"></script>
   <meta name="wpk-bid" content="dta_1_1184953963">
   <script>

+ 7 - 0
src/apis/components/letter.js

@@ -0,0 +1,7 @@
+import http from '@/apis/axios'
+const api = 'admin/planOrder/'
+export default {
+  // 列表
+  list: (data, params) => http(api + 'list', data, 'post', params)
+}
+

+ 5 - 0
src/apis/components/search.js

@@ -0,0 +1,5 @@
+import http from "@/apis/axios";
+const api = "admin/";
+export default {
+    store: (data, params) => http("admin/supplierStore/index", data, "post", params),
+};

+ 6 - 0
src/components/flow-chart/style.css

@@ -52,3 +52,9 @@
 .ol-zoom-in,.ol-zoom-out{
   display: none !important;
 }
+
+
+.tangram-suggestion-main{
+  position: fixed;
+  z-index: 9999999;
+}

+ 1 - 1
src/components/manage/index.js

@@ -1,4 +1,4 @@
-import SettlementBudgetLetter from "./src/settlementBudgetLetter/index.vue"
+import SettlementBudgetLetter from "./src/letter/index.vue"
 import ActivityScheme from "./src/activityScheme/index.vue"
 
 export {

+ 187 - 0
src/components/manage/src/letter/_columns.js

@@ -0,0 +1,187 @@
+const mapFields = {
+  letterNo:'planCode', //评估预算函编号
+  status:'status',  // 状态
+  letter:'plan_file', //评估预算函
+  budgetAmount:'plan_total_price', //评估预算金额
+  supplierStoreName:'store_name', //供应商店铺名称
+  supplierCompanyName:'supplierName', //供应商公司名称
+  creator:'nickname', //创建人
+  createTime:'create_time' //创建时间
+}
+
+export const budget = [
+  {
+      type: 'index',
+      label: '序号',
+      width: '70px'
+  },
+  {
+      prop: mapFields.letterNo,
+      label: '评估预算函编号',
+      width: '120px',
+  },
+  {
+      prop: mapFields.status,
+      label: '是否选中',
+      width: '120px',
+      _slot_:'is_check'
+  },
+  {
+      prop: mapFields.letter,
+      label: '评估预算函',
+      width: '156px',
+  },
+  {
+      prop: mapFields.budgetAmount,
+      label: '评估预算金额',
+      width: '180px',
+  },
+  {
+      prop: mapFields.supplierStoreName,
+      label: '供应商店铺名称',
+      width: '156px',
+  },
+  {
+      prop: mapFields.supplierCompanyName,
+      label: '供应商公司名称',
+      width: '156px',
+  },
+  {
+      prop: mapFields.creator,
+      label: '创建人',
+      width: '150px',
+  },
+  {
+      prop: mapFields.createTime,
+      label: '创建时间',
+      minWidth: '150px',
+  },
+]
+
+export const activity = [
+    {
+        type: 'index',
+        label: '序号',
+        width: '70px'
+    },
+    {
+        prop: mapFields.letterNo,
+        label: '评估预算函编号',
+        width: '120px',
+    },
+    {
+        prop: mapFields.status,
+        label: '是否选中',
+        width: '120px',
+        _slot_:'is_check'
+    },
+    {
+        prop: mapFields.letter,
+        label: '评估预算函',
+        width: '156px',
+    },
+    {
+        prop: mapFields.budgetAmount,
+        label: '评估预算金额',
+        width: '180px',
+    },
+    {
+        prop: mapFields.supplierStoreName,
+        label: '供应商店铺名称',
+        width: '156px',
+    },
+    {
+        prop: mapFields.supplierCompanyName,
+        label: '供应商公司名称',
+        width: '156px',
+    },
+    {
+        prop: mapFields.creator,
+        label: '创建人',
+        width: '150px',
+    },
+    {
+        prop: mapFields.createTime,
+        label: '创建时间',
+        minWidth: '150px',
+    },
+]
+
+export const project = [
+    {
+        type: 'index',
+        label: '序号',
+        width: '70px'
+    },
+    {
+        prop: mapFields.letterNo,
+        label: '评估预算函编号',
+        width: '120px',
+    },
+    {
+        prop: mapFields.status,
+        label: '是否选中',
+        width: '120px',
+        _slot_:'is_check'
+    },
+    {
+        prop: mapFields.letter,
+        label: '评估预算函',
+        width: '156px',
+    },
+    {
+        prop: mapFields.budgetAmount,
+        label: '评估预算金额',
+        width: '180px',
+    },
+    {
+        prop: mapFields.supplierStoreName,
+        label: '供应商店铺名称',
+        width: '156px',
+    },
+    {
+        prop: mapFields.supplierCompanyName,
+        label: '供应商公司名称',
+        width: '156px',
+    },
+    {
+        prop: mapFields.creator,
+        label: '创建人',
+        width: '150px',
+    },
+    {
+        prop: mapFields.createTime,
+        label: '创建时间',
+        minWidth: '150px',
+    },
+]
+
+const mapColumns = {
+    budget,
+    activity,
+    project
+}
+
+
+const mapCheck = {
+    budget:{
+      ok:1,
+      no:0
+    },
+    activity:{
+      ok:2,
+      no:1
+    },
+    project:{
+      ok:3,
+      no:2
+    }
+}
+
+
+
+
+export  {
+  mapColumns,
+  mapCheck
+}

+ 134 - 0
src/components/manage/src/letter/_template.js

@@ -0,0 +1,134 @@
+const mapFields = {
+    name:"name",
+    primaryClassification:'primaryClassification', // 一级分类
+    secondaryClassification:'secondaryClassification',  // 二级分类
+    threeLevelClassification:'service_cat', // 三级分类
+    price:'price', // 单价
+    unit:'unit', // 单位
+    nonTaxQuotes:'assess_price', // 非税报价
+    taxRate:'tax', // 税率
+    tax:'assess_tax_price', // 税额
+    taxIncludedQuote:'assess_total_price', //含税报价
+    num:'num'
+  }
+  
+  const mapTemplateToTable = {
+    '服务名称': mapFields.name,
+    '一级服务分类': mapFields.primaryClassification,
+    '二级服务分类': mapFields.secondaryClassification,
+    '三级服务分类': mapFields.threeLevelClassification,
+    '单价': mapFields.price,
+    '单位': mapFields.unit,
+    '数量': mapFields.num,
+    '非税报价': mapFields.nonTaxQuotes,
+    '税率': mapFields.taxRate,
+    '税额': mapFields.tax,
+    '含税报价': mapFields.taxIncludedQuote,
+  }
+  
+  export const columns = [
+    {
+        type: 'index',
+        label: '序号',
+        width: '70px'
+    },
+     {
+       prop: mapFields.name,
+       label: '服务名称',
+       width: '120px',
+       required: true
+     },
+    {
+      prop: mapFields.primaryClassification,
+      label: '一级服务分类',
+      width: '120px',
+      required: false
+    },
+    {
+      prop: mapFields.secondaryClassification,
+      label: '二级服务分类',
+      width: '120px',
+      required: false
+    },
+    {
+      prop: mapFields.threeLevelClassification,
+      label: '三级服务分类',
+      minWidth: '120px',
+      required: true
+    },
+    {
+      prop: mapFields.price,
+      label: '单价',
+      minWidth: '120px',
+      required: true
+    },
+    {
+      prop: mapFields.unit,
+      label: '单位',
+      minWidth: '120px',
+      required: true
+    },
+    {
+      prop:mapFields.num,
+      label:'数量',
+      minWidth:'100px',
+      required:true
+    },
+    {
+      prop: mapFields.nonTaxQuotes,
+      label: '非税报价',
+      width: '126px',
+      required: false
+    },
+    {
+      prop: mapFields.taxRate,
+      label: '税率',
+      minWidth: '126px',
+      required: true
+    },
+    {
+      prop: mapFields.tax,
+      label: '税额',
+      width: '126px',
+      required: false
+    },
+    {
+      prop: mapFields.taxIncludedQuote,
+      label: '含税报价',
+      width: '120px',
+      required: false
+    }
+  ]
+  
+  export const getTableProperty = (key) => mapTemplateToTable[key]
+  export const getColumn = (key) => columns.find(column => key === column.prop) 
+  
+  export const template = Array(1).fill(1).map(() => Object.keys(mapTemplateToTable).reduce((prev, property) => {
+    prev[property] = ''
+    return prev
+  }, {}))
+  
+  
+  export const requiredFields = [
+    mapFields.name,
+    mapFields.threeLevelClassification,
+    mapFields.price,
+    mapFields.unit,
+    mapFields.num,
+    mapFields.taxRate,
+    mapFields.nonTaxQuotes,
+    mapFields.taxIncludedQuote
+  ]
+  
+  export const requsetFields = [
+    mapFields.name,
+    mapFields.threeLevelClassification,
+    mapFields.price,
+    mapFields.unit,
+    mapFields.num,
+    mapFields.taxRate,
+    // mapFields.tax,
+    // mapFields.nonTaxQuotes,
+    // mapFields.taxIncludedQuote
+  ]
+  

+ 111 - 0
src/components/manage/src/letter/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div>
+    <div style="display:flex;justify-content:flex-end" v-if="!readonly">
+      <el-button size="mini" @click="onDownloadTemplate">下载{{title}}模板</el-button>
+      <el-button size="mini" type="primary" @click="handleUploadModalVisible">上传{{title}}</el-button>
+    </div>
+    <ex-table
+      :columns="[...(isSelection ? [{type:'selection'}] : []), ...mapColumns[type || 'budget'], { label: '操作', _slot_: 'action', width:'80px',fixed:'right'}]"
+      @selection="handleSelection"
+      :table="table"
+      :data="tableData"
+      style="margin: 15px 0 0 0"
+    >
+      <template #is_check="{scope}">
+        <el-tag size="mini" :type="Number(scope.row.is_check) === mapCheck[type || 'budget'].ok ? '' :'info'">
+         {{Number(scope.row.is_check) === mapCheck[type || 'budget'].ok  ?  "已选择" : "未选择"}}
+        </el-tag>
+      </template>
+
+      <template #action="{scope}">
+        <el-button type="text" size="mini" @click="openModal(scope.row)">查看</el-button>
+      </template>
+    </ex-table>
+
+    <excel-upload-modal 
+      :visible.sync="xlsxVisible"
+      :requsetMethod="requsetMethod"
+      @submit="handleSubmit"
+      @refresh="onSearch"
+      :title="title" 
+      :isFile="isFile"
+    />
+
+    <preview-modal 
+      :sitem="sitem"
+      :visible.sync="previewVisible"
+    />
+  </div>
+</template>
+
+
+<script>
+import ExcelUploadModal from "./xlsxUploadModal.vue"
+import PreviewModal from "./previewModal.vue"
+import asyncRequest from "@/apis/components/letter"
+import { writeFile, utils } from "xlsx";
+import { template } from "./_template"
+import { mapColumns, mapCheck } from "./_columns"
+
+export default {
+  name:'Letter',
+  /* type budget预算函 activity活动方案 project计划 **/
+  props:['title', 'requsetMethod', 'status', 'id', 'isSelection', 'readonly','type', 'beforeModalVisible', 'isFile'],
+  components:{ ExcelUploadModal, PreviewModal },
+  data(){
+    return {
+      table: {
+        stripe: true,
+        border: true,
+        'max-height': '800px'
+      },
+      sitem:{},
+      mapCheck,
+      mapColumns,
+      tableData:[],
+      xlsxVisible:false,
+      previewVisible:false
+    }
+  },
+  mounted(){
+    this.onSearch()
+  },
+  methods:{
+    handleSelection({list}){
+      this.$emit('selection', list.map(({id}) => id))
+    },
+    openModal(row){
+      console.log(this.sitem)
+      this.sitem = row
+      this.previewVisible = true
+    },
+    onDownloadTemplate(){
+      const workBook = utils.book_new()
+      const workSheet = utils.json_to_sheet(template)
+      utils.book_append_sheet(workBook,workSheet,"sheet")
+      writeFile(workBook,`${this.title}模板.xlsx`,{
+        bookType:'xlsx'
+      })
+    },
+    handleUploadModalVisible(){
+      if(this.beforeModalVisible && !this.beforeModalVisible()){
+        return
+      }
+
+      this.xlsxVisible = true
+    },
+    async onSearch(){
+      this.loading = true
+      const { code, data } = await asyncRequest.list({
+        status:this.status,
+        req_id: this.id
+      })
+      this.loading = false
+
+      if(code !== 1) return
+
+      this.tableData = data.list
+    }
+  }
+}
+</script>

+ 50 - 0
src/components/manage/src/letter/previewModal.vue

@@ -0,0 +1,50 @@
+<template>
+  <el-dialog :visible="innerVisible" title="查看预算函" center :close-on-click-modal="false" @close="handleClose">
+    <el-form disabled label-width="110px" size="mini">
+      <el-form-item label="评估预算函编号">
+        <el-input v-model="sitem.planCode" />
+      </el-form-item>
+      <el-form-item label="评估预算金额">
+        <el-input v-model="sitem.plan_total_price" />
+      </el-form-item>
+      <el-form-item label="供应商店铺名称">
+        <el-input v-model="sitem.store_name" />
+      </el-form-item>
+      <el-form-item label="供应商名称">
+        <el-input v-model="sitem.supplierName" />
+      </el-form-item>
+      <el-form-item label="创建人">
+        <el-input v-model="sitem.nickname" />
+      </el-form-item>
+       <el-form-item label="创建时间">
+        <el-input v-model="sitem.create_time" />
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props:['visible','sitem'],
+  computed:{
+    innerVisible:{
+      get(){
+        return this.visible
+      },
+      set(newVal){
+        this.$emit('update:visible',newVal)
+      }
+    }
+  },
+  data(){
+    return {
+
+    }
+  },
+  methods:{
+    handleClose(){
+      this.innerVisible = false
+    }
+  }
+}
+</script>

+ 247 - 0
src/components/manage/src/letter/xlsxUploadModal.vue

@@ -0,0 +1,247 @@
+<template>
+  <el-dialog :title="`上传${title}方案`" :visible="innerVisible" width="1024px" @close="handleClose" center>
+
+    <div v-if="file_url && isFile" style="margin-left:20px">
+      <a :href="file_url">点击下载</a>
+      <el-link
+        :underline="false"
+        @click="deleteUrl('3')"
+        type="warning"
+        style="margin: 0 0 0 16px"
+        >删除</el-link
+      >
+    </div>
+
+    <div class="activity-upload" v-if="!file_url && isFile">
+      <div class="btnupload" style="position: relative">
+        <i class="el-icon-plus avatar-uploader-icon"></i>
+        <file-upload-pdf
+          class="Upload"
+          :accept="'.xlsx,.xls,.pdf,.zip,.rar,.7z'"
+          :multiple="false"
+          :uploadcondition="beforeOtherUpload"
+          @UploadErrorEvent="UploadErrorOtherproof_url"
+          @UploadSuccessEvent="UploadSuccessOtherproof_url"
+        />
+      </div>
+      <div class="txt-tips fl">
+        <p>建议大小:小于5MB</p>
+        <p>文件格式:.xlsx,.xls,.pdf,.zip,.rar,.7z</p>
+      </div>
+    </div>
+
+    <div style="padding:10px" v-loading="loading">
+     <div v-if="tableData && tableData.length > 0" class="tr" style="padding: 10px 0 0 0">
+      <el-button :size="'mini'" @click="() => tableData = []">取消</el-button>
+      <el-button type="primary" :size="'mini'" @click="onSubmit">提交</el-button>
+      </div>
+      <div v-else>
+        <upload-excel :on-success="onSuccess" :before-upload="beforeUpload" />
+      </div>
+
+      <ex-table
+        :columns="columns"
+        :table="table"
+        :data="tableData"
+        style="margin: 15px 0 0 0"
+      />
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import companyHelper from '@/mixins/companyHelper'
+import { template } from "./_template"
+import { utils, writeFile } from "xlsx"
+
+import {
+  columns,
+  getColumn,
+  requsetFields,
+  requiredFields,
+  getTableProperty,
+} from './_template'
+
+export default {
+  mixins: [companyHelper],
+  props: ['visible', 'title', 'requsetMethod','isFile'],
+  data() {
+    return {
+      columns,
+      loading: false,
+      tableData: [],
+      file_url:"",
+      isInit:true,
+      table: {
+        stripe: true,
+        border: true,
+        'max-height': '800px'
+      }
+    }
+  },
+  computed: {
+    innerVisible: {
+      get() {
+        return this.visible
+      },
+      set(newVal) {
+        this.$emit('update:visible', newVal)
+      }
+    }
+  },
+  methods: {
+    handleClose(){
+      this.tableData = []
+      this.loading = false
+      this.file_url = ""
+      this.innerVisible = false
+    },
+    downloadTemplate(){
+      const workBook = utils.book_new()
+      const workSheet = utils.json_to_sheet(template)
+      utils.book_append_sheet(workBook,workSheet,"sheet")
+      writeFile(workBook,"预算函模板.xlsx",{
+        bookType:'xlsx'
+      })
+    },
+    validateTableHeader(header, importHeader) {
+      let isHeaderOk = true
+      console.log(header,importHeader)
+      if (header.length !== importHeader.length) return false
+      for (const index in header) {
+        const field = header[index]
+        const importField = importHeader[index]
+        if (field !== importField) {
+          isHeaderOk = false
+          break
+        }
+      }
+
+      return isHeaderOk
+    },
+    beforeOtherUpload(file) {
+      let isJPG = false;
+      if (
+        file.type === "application/vnd.ms-excel" ||
+        file.type ===
+          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
+        file.type === "application/pdf" ||
+        file.type === "application/x-zip-compressed"
+      ) {
+        isJPG = true;
+      }
+      let name = file.name;
+      let list = name.split(".");
+      if (list[list.length - 1] === "rar" || list[list.length - 1] === "7z" || list[list.length - 1] === "zip") {
+        isJPG = true;
+      }
+
+      const isLt2M = file.size / 1024 / 1024 < 5;
+      if (!isJPG) {
+        this.$message.error("文件格式不正确!");
+      }
+      if (!isLt2M) {
+        this.$message.error("文件大小不能超过 5MB!");
+      }
+      return isJPG && isLt2M;
+    },
+        //其他文件上传失败
+    UploadErrorOtherproof_url(res) {
+      if (res !== "break") {
+        this.$message.error("文件上传失败!");
+      }
+    },
+    async UploadSuccessOtherproof_url(data) {
+      console.log(data)
+      const { url } = data;
+      this.file_url = url;
+      this.$message.success("文件成功!");
+    },
+    isValueNotNull(value){
+      if(!value) return
+      const _value = String(value)
+      return _value.trim() !== ""
+    },
+    /* 校验导入的数据 **/
+    validateFields(tableData = []) {
+      for (const index in tableData) {
+        const line = Number(index) + 1
+        const tableItem = tableData[index]
+        const properties = Object.keys(tableItem)
+        for (const property of properties) {
+          if(requiredFields.includes(property) && !this.isValueNotNull(tableItem[property])){
+            this.$message.warning(`第 ${line} 行 ${getColumn(property).label} 不能为空`)
+            return false
+          }
+        }
+      }
+
+      return true
+    },
+    mapTemplateItemToTableItem(templateItem) {
+      const tableItem = {}
+      const templatePropertys = Object.keys(templateItem)
+      templatePropertys.forEach(templateProperty => {
+        const tableproperty = getTableProperty(templateProperty)
+        tableItem[tableproperty] = templateItem[templateProperty]
+      })
+      return tableItem
+    },
+    onSuccess({ results: templateItems, header: templateHeader }) {
+      const isHeaderValid = this.validateTableHeader(
+        this.columns.map(({ label }) => label).slice(1),
+        templateHeader
+      )
+
+      if (!isHeaderValid) {
+        this.$message.warning('表格与导入的表头不一致!')
+        return
+      }
+      if (templateItems.length === 0) {
+        this.$message.warning('导入的表格没有数据!')
+        return
+      }
+      templateItems.forEach(templateItem => {
+        const tableItem = this.mapTemplateItemToTableItem(templateItem)
+        this.tableData.push(tableItem)
+      })
+      this.validateFields(this.tableData)
+    },
+    async onSubmit() {
+      if(!this.validateFields(this.tableData)){
+        return
+      }
+
+      if(this.isFile && !this.file_url){
+        this.$message.warning('请上传文件!')
+        return
+      }
+
+      
+      const planinfo = this.tableData.map(tableItem => requsetFields.reduce((prev, currentKey) => ({
+        ...prev,
+        [currentKey]: tableItem[currentKey]
+      }), {}))
+
+      this.loading = true
+      const { code } = await this.requsetMethod(planinfo, this.file_url)
+      this.loading = false
+
+      if(code !== 1) return
+      this.$emit('refresh')
+      this.innerVisible = false
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.error-message__wrapper{
+  width: 1024px;
+}
+
+.activity-upload{
+  margin-bottom: 50px;
+  margin-left: 10px;
+}
+</style>

+ 0 - 120
src/components/manage/src/settlementBudgetLetter/_template.js

@@ -1,120 +0,0 @@
-const mapFields = {
-    letterNo:'letterNo', //评估预算函编号
-    status:'status',  // 状态
-    letter:'letter', //评估预算函
-    budgetAmount:'budgetAmount', //评估预算金额
-    supplierStoreName:'supplierStoreName', //供应商店铺名称
-    supplierCompanyName:'supplierCompanyName', //供应商公司名称
-    creator:'creator', //创建人
-    createTime:'createTime' //创建时间
-}
-
-const mapTemplateToTable = {
-    '评估预算函': mapFields.letterNo,
-    '状态': mapFields.status,
-    '评估预算函': mapFields.letter,
-    '评估预算金额': mapFields.budgetAmount,
-    '供应商店铺名称': mapFields.supplierStoreName,
-    '供应商公司名称': mapFields.supplierCompanyName,
-    '创建人': mapFields.creator,
-    '创建时间': mapFields.createTime
-}
-
-export const getTableProperty = (key) => mapTemplateToTable[key]
-
-export const columns = [
-    {
-        type: 'index',
-        label: '序号',
-        width: '70px'
-    },
-    {
-        prop: mapFields.letterNo,
-        label: '评估预算函编号',
-        width: '120px',
-        required: true
-    },
-    {
-        prop: mapFields.status,
-        label: '状态',
-        width: '120px',
-        required: true
-    },
-    {
-        prop: mapFields.letter,
-        label: '评估预算函',
-        width: '156px',
-        required: true
-    },
-    {
-        prop: mapFields.budgetAmount,
-        label: '评估预算金额',
-        width: '180px',
-        required: true
-    },
-    {
-        prop: mapFields.supplierStoreName,
-        label: '供应商店铺名称',
-        width: '156px',
-        required: true
-    },
-    {
-        prop: mapFields.supplierCompanyName,
-        label: '供应商公司名称',
-        width: '156px',
-        required: false
-    },
-    {
-        prop: mapFields.creator,
-        label: '创建人',
-        width: '150px',
-        required: false
-    },
-    {
-        prop: mapFields.createTime,
-        label: '创建时间',
-        minWidth: '150px',
-        required: false
-    },
-]
-
-export function createErrorMessage(messages) {
-    const rows = Object.keys(messages)
-    return rows.reduce((prev, row, index) => {
-        const suffix = index === rows.length - 1 ? '</ul>' : ''
-        const content = prev + ` <li><strong style="font-weight:700">第${row}行</strong>,${messages[row]} </li> `
-        return content + suffix
-    }, '<ul>')
-}
-
-export const createFieldVerification = (message) => ({
-    notValidRows: [],
-    isValid: true,
-    message
-})
-
-export const template = Array(1).fill(1).map(() => Object.keys(mapTemplateToTable).reduce((prev, property) => {
-    prev[property] = ''
-    return prev
-}, {}))
-
-
-export const requiredFields = []
-
-
-export const helper = {
-    fields(sourceObject) {
-        return Object.keys(sourceObject)
-    },
-    values(sourceObject, fields, index = 0) {
-        return fields.map(field => {
-            return sourceObject[field][index]
-        })
-    },
-    write(sourceObject, fields) {
-        return fields.reduce((prev, currentKey) => ({
-            ...prev,
-            [currentKey]: sourceObject[currentKey]
-        }), {})
-    }
-}

+ 0 - 53
src/components/manage/src/settlementBudgetLetter/index.vue

@@ -1,53 +0,0 @@
-<template>
-  <div>
-    <div style="display:flex;justify-content:flex-end">
-      <el-button size="mini" @click="onDownloadTemplate">下载{{title}}模板</el-button>
-      <el-button size="mini" type="primary" @click="xlsxVisible = true">上传{{title}}函</el-button>
-    </div>
-    <ex-table
-      :columns="columns"
-      :table="table"
-      :data="tableData"
-      style="margin: 15px 0 0 0"
-    />
-    <div style="display:flex;justify-content:flex-end;margin:10px 0px">
-      <el-button size="mini" type="primary">保存</el-button>
-    </div>
-
-    <excel-upload-modal :visible.sync="xlsxVisible" :title="title" />
-  </div>
-</template>
-
-
-<script>
-import ExcelUploadModal from "./xlsxUploadModal.vue"
-import { template, columns } from "./_template"
-import { writeFile, utils } from "xlsx";
-export default {
-  name:'SettlementBudgetLetter',
-  props:['title'],
-  components:{ ExcelUploadModal },
-  data(){
-    return {
-      table: {
-        stripe: true,
-        border: true,
-        'max-height': '800px'
-      },
-      columns,
-      tableData:[],
-      xlsxVisible:false
-    }
-  },
-  methods:{
-    onDownloadTemplate(){
-      const workBook = utils.book_new()
-      const workSheet = utils.json_to_sheet(template)
-      utils.book_append_sheet(workBook,workSheet,"sheet")
-      writeFile(workBook,`${this.title}模板.xlsx`,{
-        bookType:'xlsx'
-      })
-    }
-  }
-}
-</script>

+ 0 - 187
src/components/manage/src/settlementBudgetLetter/xlsxUploadModal.vue

@@ -1,187 +0,0 @@
-<template>
-  <el-dialog
-    center
-    width="1024px"
-    :title="`导入${title}函`"
-    :visible="innerVisible"
-    :close-on-click-modal="false"
-    @close="() => innerVisible = false"
-  >
-    <div v-if="tableData && tableData.length > 0" class="tr" style="padding: 10px 0 0 0">
-      <el-button :size="'mini'" @click="() => tableData = []">取消</el-button>
-      <el-button type="primary" :size="'mini'" @click="onSubmit">提交</el-button>
-    </div>
-    <div v-else>
-      <upload-excel :on-success="onSuccess" :before-upload="beforeUpload" />
-    </div>
-
-    <ex-table
-      :columns="columns"
-      :table="table"
-      :data="tableData"
-      style="margin: 15px 0 0 0"
-    />
-
-    <div style="width:100%;display:flex;justify-content:flex-end;margin-top:10px;padding:0px 10px">
-      <el-button size="mini" type="primary">保存</el-button>
-    </div>
-  </el-dialog>
-</template>
-
-<script>
-import companyHelper from '@/mixins/companyHelper'
-import { MessageBox } from 'element-ui'
-import dayjs from 'dayjs'
-
-import {
-  helper,
-  columns,
-  getTableProperty,
-  createErrorMessage,
-  createFieldVerification,
-  requiredFields,
-} from './_template'
-
-export default {
-  mixins: [companyHelper],
-  props: ['visible','title'],
-  data() {
-    return {
-      columns,
-      loading: false,
-      tableData: [],
-      table: {
-        stripe: true,
-        border: true,
-        'max-height': '800px'
-      }
-    }
-  },
-  computed: {
-    innerVisible: {
-      get() {
-        return this.visible
-      },
-      set(newVal) {
-        this.$emit('update:visible', newVal)
-      }
-    }
-  },
-  methods: {
-    validateTableHeader(header, importHeader) {
-      let isHeaderOk = true
-      if (header.length !== importHeader.length) return false
-      for (const index in header) {
-        const field = header[index]
-        const importField = importHeader[index]
-        if (field !== importField) {
-          console.log(field, importField)
-          isHeaderOk = false
-          break
-        }
-      }
-
-      return isHeaderOk
-    },
-    validateRequiredField(requiredFields) {
-        const verification = createFieldVerification('发票申请编号、发票类型、发票号码、开票日期、税后金额不能为空')
-      const fields = helper.fields(requiredFields)
-      requiredFields[fields[0]].forEach((_, index) => {
-        if (!helper.values(requiredFields, fields, index).every(value => value && String(value).trim() !== '')) {
-          verification.isValid = false
-          verification.notValidRows.push(index + 1)
-        }
-      })
-      return verification
-    },
-    /* 处理不合法的值,提示错误信息,并返回最终的验证状态 **/
-    handleNotValidFields(...validStates) {
-      const messages = {}
-      let isFinalValid = true
-
-      for (const validState of validStates) {
-        const { message, isValid, notValidRows } = validState
-        if (isValid) continue
-        isFinalValid = false
-
-        notValidRows.forEach(row => {
-          if (!messages[row]) messages[row] = []
-          messages[row].push(message)
-        })
-      }
-
-      return {
-        isFinalValid,
-        message: () => !isFinalValid && MessageBox({
-          type: 'warning',
-          title: '数据填写错误',
-          dangerouslyUseHTMLString: true,
-          message: createErrorMessage(messages),
-          customClass: 'error-message__wrapper'
-        })
-      }
-    },
-    /* 校验导入的数据 **/
-    validateFields(tableData = []) {
-      const mapTableFieldToTableData = {}
-      for (const tableItem of tableData) {
-        const propertys = Object.keys(tableItem)
-        for (const property of propertys) {
-          const value = tableItem[property]
-          if (!mapTableFieldToTableData[property]) mapTableFieldToTableData[property] = []
-          mapTableFieldToTableData[property].push(value)
-        }
-      }
-
-      return this.handleNotValidFields(
-        this.validateRequiredField(helper.write(mapTableFieldToTableData, requiredFields))
-      )
-    },
-    mapTemplateItemToTableItem(templateItem) {
-      const tableItem = {}
-      const templatePropertys = Object.keys(templateItem)
-      templatePropertys.forEach(templateProperty => {
-        const tableproperty = getTableProperty(templateProperty)
-        tableItem[tableproperty] = templateItem[templateProperty]
-      })
-      return tableItem
-    },
-    onSuccess({ results: templateItems, header: templateHeader }) {
-      const isHeaderValid = this.validateTableHeader(
-        this.columns.map(({ label }) => label).slice(1),
-        templateHeader
-      )
-      if (!isHeaderValid) {
-        this.$message.warning('表格与导入的表头不一致!')
-        return
-      }
-      if (templateItems.length === 0) {
-        this.$message.warning('导入的表格没有数据!')
-        return
-      }
-      templateItems.forEach(templateItem => {
-        const tableItem = this.mapTemplateItemToTableItem(templateItem)
-        this.tableData.push(tableItem)
-      })
-      this.validateFields(this.tableData).message()
-    },
-    async onSubmit() {
-      const { message, isFinalValid } = this.validateFields(this.tableData)
-      if (!isFinalValid) return message()
-
-      const list = this.tableData.map(tableItem => requsetFields.reduce((prev, currentKey) => ({
-        ...prev,
-        [currentKey]: currentKey === PROPERTYS.SEND_TIME ? dayjs(tableItem[currentKey] * 1000).format('YYYY-MM-DD HH:mm:ss') : tableItem[currentKey]
-      }), {}))
-
-      this.loading = true
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.error-message__wrapper{
-  width: 1024px;
-}
-</style>

+ 0 - 0
src/components/manage/src/settlementBudgetLetter/评估(结算or预算)函管理


+ 7 - 0
src/components/search/index.js

@@ -0,0 +1,7 @@
+import SearchStore from "./src/store.vue"
+
+const searchComponents = [
+  SearchStore
+]
+
+export default searchComponents

+ 48 - 0
src/components/search/src/store.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-select
+    v-model="_value"
+    :size="size" 
+    remote
+    :remote-method="onFetch"
+    placeholder="请选择店铺"
+    :loading="loading"
+    reserve-keyword
+    filterable
+  >
+    <el-option v-for="item in list" :key="item.id" :value="item.id" :label="item.store_name" />
+  </el-select>
+</template>
+
+
+<script>
+import asyncRequest from "@/apis/components/search"
+export default {
+  name:'SearchStore',
+  props:['size', 'value'],
+  data(){
+    return {
+      loading: false,
+      list: []
+    }
+  },
+  computed:{
+    _value:{
+      get(){
+        return this.value
+      },
+      set(newVal){
+        const current = this.list.find(({id}) => id === newVal)
+        this.$emit('change', current)
+        this.$emit('update:value', newVal)
+      }
+    }
+  },
+  methods:{
+    async onFetch(keyword){
+      const {code, data} = await asyncRequest.store({store_name: keyword})
+      if(code !== 1) return
+      this.list = data.list
+    }
+  }
+}
+</script>

+ 6 - 1
src/plugins/components.js

@@ -2,6 +2,8 @@ import { CredentialsNode, NormalNode, CreateInviceNode, UploadInvoiceNode} from
 import { SettlementBudgetLetter, ActivityScheme } from "@/components/manage"
 import { TableEditor } from "@/components/tableEditor"
 
+import searchComponents from "@/components/search"
+
 const components = [
   NormalNode,
   CredentialsNode,
@@ -15,7 +17,10 @@ const components = [
 ]
 
 export const setupComponets = function(app){
-  components.forEach(component => {
+  [
+    ...components,
+    ...searchComponents
+  ].forEach(component => {
     app.component(component.name,component)
   })
 }

+ 127 - 0
src/utils/fileType.js

@@ -0,0 +1,127 @@
+/**
+* @param: fileName - 文件名称
+* @param: 数据返回 1) 无后缀匹配 - false
+* @param: 数据返回 2) 匹配图片 - image
+* @param: 数据返回 3) 匹配 txt - txt
+* @param: 数据返回 4) 匹配 excel - excel
+* @param: 数据返回 5) 匹配 word - word
+* @param: 数据返回 6) 匹配 pdf - pdf
+* @param: 数据返回 7) 匹配 ppt - ppt
+* @param: 数据返回 8) 匹配 视频 - video
+* @param: 数据返回 9) 匹配 音频 - radio
+* @param: 数据返回 10) 其他匹配项 - other
+* @author: ljw
+**/
+
+const mapFileType = {
+  'image': "图片类型",
+  'text': "文本类型",
+  'excel': "excel类型",
+  'word': "word类型",
+  'pdf': "pdf类型",
+  'ppf': "ppt类型",
+  'video': "视频类型",
+  'radio': "音频类型"
+}
+
+export const fileSuffixTypeUtil = {
+  matchFileSuffixType (fileName) {
+    // 后缀获取
+    var suffix = ''
+    // 获取类型结果
+    var result = ''
+    try {
+      var flieArr = fileName.split('.')
+      suffix = flieArr[flieArr.length - 1]
+    } catch (err) {
+      suffix = ''
+    }
+    // fileName无后缀返回 false
+    if (!suffix) {
+      result = false
+      return result
+    }
+    // 图片格式
+    var imglist = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
+    // 进行图片匹配
+    result = imglist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'image'
+      return result
+    }
+    // 匹配txt
+    var txtlist = ['txt']
+    result = txtlist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'txt'
+      return result
+    }
+    // 匹配 excel
+    var excelist = ['xls', 'xlsx']
+    result = excelist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'excel'
+      return result
+    }
+    // 匹配 word
+    var wordlist = ['doc', 'docx']
+    result = wordlist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'word'
+      return result
+    }
+    // 匹配 pdf
+    var pdflist = ['pdf']
+    result = pdflist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'pdf'
+      return result
+    }
+    // 匹配 ppt
+    var pptlist = ['ppt']
+    result = pptlist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'ppt'
+      return result
+    }
+    // 匹配 视频
+    var videolist = ['mp4', 'm2v', 'mkv']
+    result = videolist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'video'
+      return result
+    }
+    // 匹配 音频
+    var radiolist = ['mp3', 'wav', 'wmv']
+    result = radiolist.some(function (item) {
+      return item == suffix
+    })
+    if (result) {
+      result = 'radio'
+      return result
+    }
+    // 其他 文件类型
+    result = 'other'
+    return result
+  },
+  getFIieTypeName(type){
+    console.log(type)
+    let typeName = mapFileType[type]
+    typeName = typeName || '其他类型'
+    return typeName
+  }
+}

+ 1 - 1
src/views/customerService/demandOrder/components/settlementLetter/xlsxUploadModal.vue

@@ -231,7 +231,7 @@ export default {
         this.$emit("refresh")
       }
 
-      this.loading = true
+      this.loading = false
     }
   }
 }

+ 1 - 0
src/views/customerService/demandOrder/components/waitCustomerConfirm.vue

@@ -121,6 +121,7 @@ export default {
       }
     },
     beforeVideoUpload(file) {
+      console.log(file.type)
       let isJPG = false;
       if (file.type === "video/mp4" || file.type === "video/avi") {
         isJPG = true;

+ 123 - 9
src/views/customerService/demandOrder/components/waitSupplierConfirm.vue

@@ -1,18 +1,132 @@
 <template>
-  <el-table border size="mini">
-    <el-table-column label="计划编号" />
-    <el-table-column label="类型" />
-    <el-table-column label="预览" />
-    <el-table-column label="创建人" />
-    <el-table-column label="创建时间" />
-    <el-table-column label="操作" />
-  </el-table>
+  <div>
+    <div class="activity-upload" style="padding-bottom:20px">
+      <div class="btnupload">
+         <i class="el-icon-plus avatar-uploader-icon"></i>
+         <file-upload-pdf
+           class="Upload"
+           :accept="'.xlsx,.xls,.pdf,.zip,.rar,.7z'"
+           :multiple="false"
+           :uploadcondition="beforeOtherUpload"
+           @UploadErrorEvent="UploadErrorOtherproof_url"
+           @UploadSuccessEvent="UploadSuccessOtherproof_url"
+         />
+       </div>
+       <div class="txt-tips fl">
+         <p>建议大小:小于5MB</p>
+         <p>文件格式:.xlsx,.xls,.pdf,.zip,.rar,.7z</p>
+      </div>
+    </div>
+
+    <div style="display:flex">
+      <p style="font-size:14px;margin-left:5px;margin-right:10px">现场资料:</p>
+      <el-table border size="mini" :data="proof_url">
+        <!-- <el-table-column label="计划编号" /> -->
+        <el-table-column label="文件名" prop="name" />
+        <el-table-column label="类型">
+          <template slot-scope="scope">
+            {{ getFIieTypeName(matchFileSuffixType(scope.row.name)) }}
+          </template>
+        </el-table-column>
+        <!-- <el-table-column label="预览" /> -->
+        <!-- <el-table-column label="创建人" /> -->
+        <!-- <el-table-column label="创建时间" /> -->
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <el-button type="text" size="mini" @click="deleteItem(scope.$index)">删除</el-button>
+            <el-button type="text" size="mini" @click="previewItem(scope.row.url)">预览</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <div style="display:flex;justify-content:flex-end;margin:10px 0px">
+      <el-button size="mini" type="primary" @click="onSubmit" :loading="loading">保 存</el-button>
+    </div>
+  </div>
 </template>
 
 <script>
+import { fileSuffixTypeUtil } from "@/utils/fileType"
+import asyncRequest from "@/apis/service/customerService/demandOrder";
+const { matchFileSuffixType,getFIieTypeName } = fileSuffixTypeUtil
 export default {
+  props: ['sitem'],
   data(){
-    return {}
+    return {
+      loading:false,
+      proof_url:[]
+    }
+  },
+  methods:{
+    matchFileSuffixType,
+    getFIieTypeName,
+    deleteItem(index){
+      this.proof_url.splice(index,1)
+    },
+    previewItem(url){
+      console.log(url)
+      window.open(url)
+    },
+    beforeOtherUpload(file) {
+      let isJPG = false;
+      if (
+        file.type === "application/vnd.ms-excel" ||
+        file.type ===
+          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
+        file.type === "application/pdf" ||
+        file.type === "application/x-zip-compressed"
+      ) {
+        isJPG = true;
+      }
+      let name = file.name;
+      let list = name.split(".");
+      if (list[list.length - 1] === "rar" || list[list.length - 1] === "7z" || list[list.length - 1] === "zip") {
+        isJPG = true;
+      }
+
+      const isLt2M = file.size / 1024 / 1024 < 5;
+      if (!isJPG) {
+        this.$message.error("文件格式不正确!");
+      }
+      if (!isLt2M) {
+        this.$message.error("文件大小不能超过 5MB!");
+      }
+      return isJPG && isLt2M;
+    },
+        //其他文件上传失败
+    UploadErrorOtherproof_url(res) {
+      if (res !== "break") {
+        this.$message.error("文件上传失败!");
+      }
+    },
+    async UploadSuccessOtherproof_url(data) {
+      const { url, name } = data;
+      this.proof_url.push({ url, name });
+
+      console.log(this.proof_url)
+      this.$message.success("文件成功!");
+    },
+
+    async onSubmit(){
+      if(this.proof_url.length === 0){
+        this.$message.warning('请上传现场资料')
+        return
+      }
+
+      this.loading = true
+      
+      const params = {
+        id: this.sitem.id, 
+        proof_url: this.proof_url.map(({url}) => url),
+        status: 7 
+      }
+
+      const { code } = await asyncRequest.proofCheck(params)
+      this.loading = false
+      if(code !== 1) return
+      this.$emit('refresh')
+    }
   }
 }
 </script>

+ 3 - 3
src/views/customerService/demandOrder/detail.vue

@@ -28,8 +28,8 @@
             </el-collapse-item>
 
             <el-collapse-item title="待供应商确认已执行" name="6" v-if="Number(sitem.status) === 6">
-              <!-- <wait-supplier-confirm /> -->
-              <credentials-node @confirm="handleSupplierConfirm($event)" />
+              <wait-supplier-confirm :sitem="sitem" @refresh="initData"  />
+              <!-- <credentials-node @confirm="handleSupplierConfirm($event)" /> -->
             </el-collapse-item>
 
             <el-collapse-item title="待客户确认已执行" name="7" v-if="Number(sitem.status) === 7">
@@ -37,7 +37,7 @@
             </el-collapse-item>
 
             <el-collapse-item title="待供应商上传结算函" name="8" v-if="Number(sitem.status) === 9">
-              <!-- <settlement-budget-letter title="结算" /> -->
+              <!-- <letter title="结算" /> -->
               <settlement-letter :sitem="sitem"  @refresh="initData" />
             </el-collapse-item>
 

+ 1 - 4
src/views/customerService/invoiceApply/detail.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="bargainListDetail">
-    <div class="bargainListDetail-main" v-if="powers.some((i) => i == '007')">
+    <div class="bargainListDetail-main">
       <el-tabs v-model="projectTabs" v-loading="loading">
         <el-tab-pane label="需求订单详情" name="1">
           <el-collapse v-model="activeNames" style="margin: -18px 0 0 0">
@@ -25,9 +25,6 @@
         </el-tab-pane>
       </el-tabs>
     </div>
-    <div v-else>
-      <no-auth></no-auth>
-    </div>
   </div>
 </template>
 <script>

+ 5 - 9
src/views/customerService/invoiceApply/index.vue

@@ -1,8 +1,6 @@
 <template>
   <div class="bargainList pagePadding">
-    <div
-      v-if=" powers.some((i) => i == '001')"
-    >
+    <div>
       <ex-table
         v-loading="loading"
         :table="table"
@@ -206,9 +204,6 @@
         </template>
       </ex-table>
     </div>
-    <div v-else>
-      <no-auth></no-auth>
-    </div>
   </div>
 </template>
 <script>
@@ -249,7 +244,7 @@ export default {
       customerCode: [], //客户公司code
       statusList,
       sitem: null,
-      loading: true,
+      loading: false,
       showModel: false,
       isDetail: false,
       modelId: 0,
@@ -327,11 +322,11 @@ export default {
         this.parmValue.select = this.select ;
         // this.parmValue.sselect = this.sselect ;
         this.parmValue.s_input= this.s_input;
-        //
-         let routerModel = {
+        let routerModel = {
           options: JSON.parse(JSON.stringify(this.parmValue)),
           router: this.$route.path,
         };
+
         model.preModel = JSON.stringify(routerModel);
         
         this.routeGoto(toRouter, model);
@@ -399,6 +394,7 @@ export default {
     },
     // 列表搜索
     async searchList() {
+      return
       if (
         (this.parmValue.createStart !== "" && this.parmValue.createEnd === "") ||
         (this.parmValue.createStart === "" && this.parmValue.createEnd !== "")

+ 1 - 1
src/views/customerService/workbench/components/activityLetter/letterEditing.vue

@@ -233,7 +233,7 @@ export default {
         this.$emit("refresh")
       }
 
-      this.loading = true
+      this.loading = false
     }
   }
 }

+ 2 - 14
src/views/customerService/workbench/components/baseForm.vue

@@ -180,6 +180,7 @@
               />
             </el-form-item>
           </el-col>
+
           <el-col :span="12" style="padding: 0 0 0 15px">
             <el-table
               :data="spec_tableData"
@@ -222,7 +223,7 @@
                     <i
                       class="el-icon-delete tb-icon"
                       @click="openDelete(scope.$index)"
-                    ></i>
+                    />
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -634,18 +635,6 @@
             </i>
           </div>
         </el-form-item>
-        <!-- <i class="el-icon-plus avatar-uploader-icon">
-          
-        </i>
-            <file-upload
-              class="Upload"
-              :accept="'.jpg,.png,.jpeg'"
-              :multiple="true"
-              :disabled="false"
-              :uploadcondition="beforeAvatarUpload"
-              @UploadErrorEvent="UploadErrorEventgood_img"
-              @UploadSuccessEvent="UploadSuccessEventgood_img"
-            ></file-upload> -->
       </el-col>
     </el-row>
   </el-form>
@@ -694,7 +683,6 @@ export default {
       }
     },
   },
-
   data() {
     return {
       areaInfo:{

+ 8 - 3
src/views/customerService/workbench/components/evaluateBudgetLetter/index.vue

@@ -23,7 +23,13 @@
       </template>
     </ex-table>
 
-    <letter-modal :visible.sync="xlsxVisible" :sitem="sitem" :title="title" @refresh="refresh" />
+    <letter-modal 
+      :visible.sync="xlsxVisible" 
+      :sitem="sitem" 
+      :title="title" 
+      :storeId="storeId" 
+      @refresh="refresh" 
+    />
   </div>
 </template>
 
@@ -37,7 +43,7 @@ import asyncRequest from "@/apis/service/customerService/workbench";
 
 export default {
   name:'SettlementBudgetLetter',
-  props:["sitem","readonly"],
+  props:["sitem", "readonly", "storeId"],
   components:{ LetterModal },
   data(){
     return {
@@ -81,7 +87,6 @@ export default {
 
       if(code === 1){
         const { list = [], count = 0 } = data
-        console.log(this.planList)
         this.planList = list;
         this.pageInfo.total = count
       }

+ 3 - 3
src/views/customerService/workbench/components/evaluateBudgetLetter/letterEditing.vue

@@ -35,7 +35,7 @@ import {
 
 export default {
   mixins: [companyHelper],
-  props: ['visible', 'sitem'],
+  props: ['visible', 'sitem', 'storeId'],
   data() {
     return {
       columns,
@@ -147,7 +147,7 @@ export default {
       }), {}))
 
       const params = {
-        store_id: 1,
+        store_id: this.storeId,
         req_id:this.sitem.id,
         plan_info
       }
@@ -160,7 +160,7 @@ export default {
         this.$emit("refresh")
       }
 
-      this.loading = true
+      this.loading = false
     }
   }
 }

+ 1 - 1
src/views/customerService/workbench/components/planBudgetLetter/xlsxUploadModal.vue

@@ -233,7 +233,7 @@ export default {
         this.$emit("refresh")
       }
 
-      this.loading = true
+      this.loading = false
     }
   }
 }

+ 113 - 19
src/views/customerService/workbench/detail.vue

@@ -41,35 +41,59 @@
           </div>
         </div>
         <div class="p-right">
-          <el-collapse v-model="actives">
+          <div style="display:flex;align-items:center;margin:10px 0px">
+            <p style="margin-right:10px">店铺:</p>
+            <search-store size="mini" :value.sync="storeId" />
+          </div>
+
+          <el-collapse v-model="actives" v-if="storeId">
             <el-collapse-item title="评估预算函" name="1">
-              <evalute-budget-letter 
-                :sitem="sitem" 
+              <letter 
+                :status="1"
+                type="budget"
+                title="预算函"
+                :id="sitem.id"
+                :isSelection="true"
                 :readonly="Number(sitem.status) !== 1"
-                @selectionChange="handleSelection($event, 'evalute')"
-                @refresh="initForm" 
+                @selection="handleSelection($event, 'evalute')"
+                :requsetMethod="requestBudgetLetter"
               />
             </el-collapse-item>
 
             <el-collapse-item title="活动方案管理" name="2">
-              <activity-letter 
-                :sitem="sitem" 
-                :readonly="Number(sitem.status) !== 2" 
-                :evalute="selected.evalute"
-                @selectionChange="handleSelection($event, 'activity')"
-                 @refresh="initForm"
+              <letter 
+                :status="2"
+                type="activity"
+                title="活动方案"
+                :id="sitem.id"
+                :isFile="true"
+                :isSelection="true"
+                :readonly="Number(sitem.status) !== 2"
+                :beforeModalVisible="beforeActivityModalVisible"
+                @selection="handleSelection($event, 'activity')"
+                :requsetMethod="reqsetActivityLetter"
               />
             </el-collapse-item>
 
             <el-collapse-item title="计划预算函管理" name="3">
-              <plan-budget-letter                 
-                :sitem="sitem" 
-                :readonly="Number(sitem.status) !== 3" 
-                :activity="selected.activity"
-                @refresh="initForm"
-            />
+              <letter 
+                :status="3"
+                type="project"
+                title="计划预算函"
+                :id="sitem.id"
+                :isFile="true"
+                :readonly="Number(sitem.status) !== 3"
+                :beforeModalVisible="beforeProjectModalVisible"
+                @selection="handleSelection($event, 'activity')"
+                :requsetMethod="requsetProjectLetter"
+              />
             </el-collapse-item>
           </el-collapse>
+
+          <div class="empty-warning" v-else>
+            <i class="el-icon-warning" style="margin-right:10px" />
+            请选择店铺
+          </div>
         </div>
       </div>
     </div>
@@ -77,12 +101,12 @@
 </template>
 <script>
 import asyncRequest from "@/apis/service/customerService/workbench";
-import resToken from "@/mixins/resToken";
-import { mapGetters } from "vuex";
 import baseForm from "./components/baseForm";
 import EvaluteBudgetLetter from "./components/evaluateBudgetLetter/index.vue"
 import PlanBudgetLetter from "./components/planBudgetLetter/index.vue"
 import ActivityLetter from "./components/activityLetter/index.vue"
+import resToken from "@/mixins/resToken";
+import { mapGetters } from "vuex";
 import dayjs from "dayjs"
 
 export default {
@@ -107,6 +131,7 @@ export default {
   },
   data() {
     return {
+      storeId: "",
       countdown:'00:00:00',
       timer:null,
       actives:["1","2","3"],
@@ -189,6 +214,8 @@ export default {
       return result;
     },
     async initData() {
+      console.log(asyncRequest)
+
       const { code, message, data } = await asyncRequest.detail({
         id: this.queryId,
       });
@@ -212,6 +239,7 @@ export default {
       }
     },
     handleSelection(val, key){
+      console.log(val)
       this.selected[key] = val
     },
     async repeat_initData() {
@@ -231,6 +259,61 @@ export default {
       this.editId = bidNo;
       this.editType = type;
       this.getNewTime();
+    },
+    beforeActivityModalVisible(){
+      if(this.selected.evalute.length === 0){
+        this.$message.warning('必须选择一个评估预算函')
+        return false
+      }
+
+      if(this.selected.evalute.length > 1){
+        this.$message.warning('只能选择一个评估预算函')
+        return false
+      }
+
+      return true
+    },
+    beforeProjectModalVisible(){
+      if(this.selected.activity.length === 0){
+        this.$message.warning('必须选择一个活动方案')
+        return false
+      }
+
+      if(this.selected.activity.length > 1){
+        this.$message.warning('只能选择一个活动方案')
+        return false
+      }
+
+      return true
+    },
+    async requsetProjectLetter(planinfo, file_url){
+      const params = {
+        status: 3,
+        id: this.selected.activity[0],
+        planinfo,
+        file_url
+      }
+
+      return asyncRequest.planStatus(params)
+    },
+    async reqsetActivityLetter(planinfo, file_activty_url){
+      const params = {
+        status: 2,
+        id: this.selected.evalute[0],
+        planinfo,
+        file_activty_url
+      }
+
+      return asyncRequest.planStatus(params)
+    },
+    async requestBudgetLetter(plan_info){
+      const params = {
+        store_id: this.storeId,
+        req_id:this.sitem.id,
+        plan_info
+      }
+
+      return asyncRequest.planCreate(params)
     }
   },
 };
@@ -346,4 +429,15 @@ export default {
     }
   }
 }
+
+.empty-warning{
+  display: flex;
+  justify-content: center;
+  align-content: center;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  // margin-top: 100px;
+}
 </style>

+ 10 - 6
src/views/customerService/workbench/index.vue

@@ -506,12 +506,17 @@ export default {
         }
       }
 
-      var result = "" + parseInt(secondTime) + ":";
+      const s = String(secondTime)
+      var result = s.length === 1 ? '0' + s : s
+
+
       if(minuteTime > 0) {
-        result = "" + parseInt(minuteTime) + ":" + result;
+        const m = String(minuteTime)
+        result = "" + (m.length === 1 ? '0' + m : m) + ":" + result;
       }
       if(hourTime > 0) {
-        result = "" + parseInt(hourTime) + ":" + result;
+        const h =  String(hourTime)
+        result = "" + (h.length === 1 ? '0' + h : h) + ":" + result;
       }
       return result;
     },
@@ -534,12 +539,11 @@ export default {
         this.loading = true;
         const { code, data } = await asyncRequest.list({
           ...this.parmValue,
-          size:15
+          size: 15
         });
 
         if (code === 1) {
           const { list, count } = data;
-
           
           this.countdownMaps = list.reduce((prev,current) => {
             return {
@@ -553,7 +557,7 @@ export default {
               ...prev,
               [current.id]:setInterval(() => {
                 let now = dayjs(new Date())
-                const minuend = now.diff(current.req_end, 'second')
+                const minuend = now.diff(current.req_endtime, 'second')
                 this.countdownMaps[current.id] = minuend < 0 ? this.getTime(Math.abs(minuend)) : '00:00:00'
               },1000)
             }

+ 610 - 0
src/views/operate/shop/components/baseForm.vue

@@ -0,0 +1,610 @@
+<template>
+  <el-form
+    ref="ruleForm"
+    v-loading="loading"
+    :model="ruleForm"
+    status-icon
+    :rules="rulesThis"
+    label-width="90px"
+    class="supplierAdd"
+  >
+    <el-row>
+      <el-col :span="24">
+        <div class="supplierAdd-title">基础信息</div>
+      </el-col>
+      <el-col :span="24" class="clear">
+        <div class="supplierAdd-title-left fl">
+          <el-form-item
+            label="门店主图"
+            prop="storeImage"
+            :disabled="id == 'view'"
+            class="activity-upload"
+          >
+            <div v-if="ruleForm.storeImage" class="img-find">
+              <img
+                v-viewer
+                :src="ruleForm.storeImage"
+                class="avatar hover fl"
+                style="width: 40px; height: 40px"
+              />
+              <i class="el-icon-close img-close" />
+              <!-- <el-link
+              v-if="type !== 'view'"
+              class="fl"
+              :underline="false"
+              type="warning"
+              style="margin: 0 0 0 16px"
+              @click="deleteimg()"
+            >删除</el-link> -->
+            </div>
+            <div v-else class="btnupload" style="position: relative">
+              <i class="el-icon-plus avatar-uploader-icon" />
+              <file-upload
+                v-if="type !== 'view'"
+                class="Upload"
+                :disabled="id == 'view'"
+                :accept="'.jpg,.png,.jpeg'"
+                :multiple="true"
+                :uploadcondition="beforeAvatarUpload"
+                @UploadErrorEvent="imgUploadError"
+                @UploadSuccessEvent="UploadSuccessEvent"
+              />
+            </div>
+          </el-form-item>
+        </div>
+        <div class="supplierAdd-title-right fr">
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="供应商" prop="supplier">
+                <el-select
+                  v-model="ruleForm.supplier"
+                  style="width: 100%"
+                  :disabled="type !== 'add' && type !== 'edit'"
+                  placeholder="供应商"
+                >
+                  <el-option
+                    v-for="item in supplierOptions"
+                    :key="item.code"
+                    :label="item.name"
+                    :value="item.code"
+                    :disabled="Number(item.status) === 0"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="8">
+              <el-form-item label="门店名称" prop="storeName">
+                <el-input
+                  v-model="ruleForm.storeName"
+                  style="width: 100%"
+                  :disabled="type !== 'add' && type !== 'edit'"
+                  placeholder="门店名称"
+                />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="8">
+              <el-form-item label="门店类型" prop="storeType">
+                <el-select
+                  v-model="ruleForm.storeType"
+                  style="width: 100%"
+                  :disabled="type !== 'add' && type !== 'edit'"
+                  placeholder="门店类型"
+                >
+                  <el-option
+                    v-for="item in storeTypeOptions"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                    :disabled="Number(item.status) === 0"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </el-col>
+
+      <el-col :span="24"><div class="supplierAdd-title">联系人信息</div></el-col>
+      <el-col :span="6">
+        <el-form-item label="姓名" prop="contactor" label-width="75px">
+          <el-input
+            v-model="ruleForm.contactor"
+            :disabled="type !== 'add' && type !== 'edit'"
+            placeholder="姓名"
+            maxlength="20"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="手机号" prop="mobile" label-width="75px">
+          <el-input
+            v-model="ruleForm.mobile"
+            :disabled="type !== 'add' && type !== 'edit'"
+            placeholder="手机号"
+            maxlength="20"
+          />
+        </el-form-item>
+      </el-col>
+      <el-col :span="6">
+        <el-form-item label="邮箱" prop="email" label-width="75px">
+          <el-input
+            v-model="ruleForm.email"
+            :disabled="type !== 'add' && type !== 'edit'"
+            placeholder="邮箱"
+            maxlength="50"
+          />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="6">
+        <el-form-item label="职位" prop="position" label-width="75px">
+          <el-input
+            v-model="ruleForm.position"
+            :disabled="type !== 'add' && type !== 'edit'"
+            placeholder="职位"
+            maxlength="20"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+    <el-col :span="24"><div class="supplierAdd-title">门店信息</div></el-col>
+    <el-col :span="24">
+      <el-form-item label="门店地址" prop="storeAddr">
+        <div style="display:flex">
+          <el-input :value="toponym" />
+          <el-button style="margin-left:10px" type="primary" @click="visible = true">选择地址</el-button>
+        </div>
+      </el-form-item>
+    </el-col>
+    <el-col :span="24" class="flex-end">
+      <el-button type="primary" @click="save" size="mini">保存</el-button>
+    </el-col>
+
+    <map-modal :visible.sync="visible" :toponym="toponym" @save="handleMapSelection" />
+  </el-form>
+</template>
+<script>
+import asyncRequest from "@/apis/service/operate/shop";
+import asyncRequestStoreType from "@/apis/service/serviceParam/storeType";
+import asyncRequestSupplier from "@/apis/service/operate/supplier";
+import MapModal from "./mapModal.vue"
+
+import Map from "ol/Map"; //地图初始化
+import * as source from "ol/source";
+import Feature from "ol/Feature";
+import Point from "ol/geom/Point";
+import TileLayer from "ol/layer/Tile";
+import * as olProj from "ol/proj";
+import * as style from "ol/style";
+import * as layer from "ol/layer";
+import "ol/ol.css";
+import View from "ol/View";
+import resToken from "@/mixins/resToken";
+import { formItem } from "../columns";
+import {
+  isLicense,
+  isMobile,
+  isqz,
+  isSpecialSymbol,
+  hasSpace,
+  isAddr,
+  validEmail,
+} from "@/utils/validate";
+
+let key = "4c3306473a0aa048196208f774b491a5";
+
+/***
+ * @props
+ *  供应商   supplier
+ *  门店名称 storeName
+ *  门店类型 storeType
+ *  门店主图 storeImage
+ *  门店地址 storeAddr
+ *  姓名 contactor
+ *  手机号 mobile
+ *  电子邮箱 email
+ *  职位 position
+ */
+export default {
+  name: "SupplierAdd",
+  mixins: [resToken],
+  components:{ MapModal },
+  props: ["showModel", "id", "type", "sitem"],
+  data() {
+    const validatemobile = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("手机号不能为空!"));
+      } else {
+        if (!isMobile(value)) {
+          callback(new Error("手机号格式不正确!"));
+        } else {
+          callback();
+        }
+      }
+    };
+    const validateAddr = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("详细地址不能为空!"));
+      } else {
+        if (hasSpace(value)) {
+          callback(new Error("不能出现/回车/换行符!"));
+        } else if (isSpecialSymbol(value)) {
+          callback(new Error("不能使用英文特殊字符!"));
+        } else if (isAddr(value)) {
+          callback();
+        } else {
+          callback(new Error("详细地址填写不规范!"));
+        }
+      }
+    };
+    const validateEmail = (rule, value, callback) => {
+      if (value === "") {
+        callback(new Error("邮箱不能为空!"));
+      } else {
+        if (!validEmail(value)) {
+          callback(new Error("邮箱格式不正确!"));
+        } else {
+          callback();
+        }
+      }
+    };
+    return {
+      visible:false,
+      parmValue: {
+        page: 1, // 页码
+        size: 99999, // 每页显示条数
+        limit: 99999,
+      },
+      toponym:"",
+      map: null,
+      feature: null,
+      loading: false,
+      hand_name: "",
+      storeTypeOptions: [],
+      supplierOptions: [],
+      title: "添加供应商",
+      showModelThis: this.showModel,
+      ruleForm: {},
+      rulesThis: this.rules,
+      rules: {
+        supplier: [{ required: true, message: "请选择供应商", trigger: "change" }],
+        storeName: [{ required: true, message: "请输入门店名称", trigger: "change" }],
+        storeType: [{ required: true, message: "请选择门店类型", trigger: "change" }],
+        storeImage: [{ required: true, message: "请上传门店主图", trigger: "change" }],
+        storeAddr: [
+          {
+            required: true,
+            message: "请选择门店地址",
+            trigger: "change",
+            validator(rule, value, callback) {
+              console.log(value);
+              if (
+                (!Array.isArray(value) && !value) ||
+                (Array.isArray(value) && value.length === 0)
+              ) {
+                callback(new Error("门店地址不能为空!"));
+              } else {
+                callback();
+              }
+            },
+          },
+        ],
+        contactor: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
+        mobile: [{ required: true, validator: validatemobile, trigger: "blur" }],
+        email: [{ required: true, validator: validEmail, trigger: "blur" }],
+        position: [{ required: true, message: "职位不能为空", trigger: "blur" }],
+      },
+    };
+  },
+  mounted() {
+    // this.initMap();
+    // this.registerMapEvent();
+    this.initForm();
+  },
+  methods: {
+    initMap() {
+      this.map = new Map({
+        layers: [
+          new TileLayer({
+            source: new source.XYZ({
+              url:
+                "http://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" +
+                key,
+            }),
+          }),
+          new TileLayer({
+            source: new source.XYZ({
+              url:
+                "http://t3.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=" + key,
+            }),
+          }),
+        ],
+        target: "map",
+        view: new View({
+          projection: "EPSG:4326",
+          center: [115.7, 39.4],
+          zoom: 16,
+        }),
+      });
+
+      const iconFeature = new Feature();
+      const feature = new Feature({
+        geometry: new Point([0, 0]),
+      });
+
+      feature.setStyle(this.createLabelStyle());
+
+      const vectorSource = new source.Vector({
+        features: [iconFeature, feature],
+      });
+      const vectorLayer = new layer.Vector({
+        source: vectorSource,
+      });
+
+      this.feature = feature;
+      this.map.addLayer(vectorLayer);
+    },
+    createLabelStyle() {
+      return new style.Style({
+        image: new style.Icon({
+          anchor: [95, 45],
+          scale: 0.2, // 图标缩小显示
+          anchorOrigin: "bottom-right", // 标注样式的起点位置
+          anchorXUnits: "pixels", // X方向单位:分数
+          anchorYUnits: "pixels", // Y方向单位:像素
+          offsetOrigin: "bottom-left", // 偏移起点位置的方向
+          opacity: 1, // 透明度
+          src: "dian.png", //图标的URL
+        }),
+        text: new style.Text({
+          textAlign: "center", //位置
+          textBaseline: "middle", //基准线
+          font: "normal 14px 微软雅黑", //文字样式
+          fill: new style.Fill({
+            //文本填充样式(即文字颜色)
+            color: "#000",
+          }),
+          stroke: new style.Stroke({
+            color: "#F00",
+            width: 2,
+          }),
+        }),
+      });
+    },
+    registerMapEvent() {
+      this.map.on("click", (evt) => {
+        const { coordinate } = evt;
+        this.ruleForm.storeAddr = [...coordinate];
+        this.feature.set("geometry", new Point(coordinate));
+      });
+    },
+    async initForm() {
+      this.hand_name = "";
+      this.loading = true;
+      await this.searchTypeList();
+      await this.searchSupplierList();
+      this.resetFormData();
+      this.rulesThis = this.rules;
+      await this.resetForm();
+      this.loading = false;
+    },
+    async resetForm() {
+      // 重置
+      await this.$nextTick(() => {
+        if (this.$refs.ruleForm) {
+          this.$refs.ruleForm.resetFields();
+          this.$refs.ruleForm.clearValidate();
+          this.resetFormData();
+        }
+      });
+    },
+    handleMapSelection({ name, point }){
+      this.toponym = name
+    },
+    resetFormData() {
+      this.ruleForm = {
+        ...formItem,
+      };
+      if (this.sitem) {
+        const {
+          id,
+          code,
+          supplier,
+          storeName,
+          type,
+          category,
+          delivery_way,
+          supplier_type,
+          level,
+          pay_type,
+          storeImage,
+          prove_img,
+          contactor,
+          mobile,
+          position,
+          registercode,
+          name,
+          nature,
+          addr,
+          legaler,
+          registertime,
+          scope,
+          personid,
+          person,
+          telephone,
+          email,
+        } = this.sitem;
+        this.hand_name = person || "";
+      }
+    },
+    closeImg(index) {
+      this.ruleForm.prove_img.splice(index, 1);
+      this.$refs.ruleForm.validateField("prove_img");
+    },
+    // 刷新表格
+    async searchTypeList() {
+      const { code, data } = await asyncRequestStoreType.list(this.parmValue);
+      this.storeTypeOptions = code === 1 ? data.list : [];
+    },
+    // 刷新表格
+    async searchSupplierList() {
+      const { code, data } = await asyncRequestSupplier.list(this.parmValue);
+      this.supplierOptions = code === 1 ? data.list : [];
+    },
+    async save() {
+      console.log(111);
+      await this.$refs.ruleForm.validate(async (valid) => {
+        console.log(valid);
+        if (valid) {
+          console.log(this.ruleForm);
+        }
+      });
+      // try {
+
+      //   await this.$refs.ruleForm.validate();
+      //   console.log(this.ruleForm);
+      //   //TODO....
+      // } catch (err) {
+      //   console.log(err);
+      // }
+    },
+    deleteimg() {
+      this.ruleForm.storeImage = "";
+      this.$refs.ruleForm.validateField("storeImage");
+    },
+
+    // 图片上传成功
+    async UploadSuccessEvent(data) {
+      const { url } = data;
+      if (url === "noToken") {
+        await this.logout();
+      } else {
+        this.ruleForm.storeImage = url;
+        this.$refs.ruleForm.validateField("storeImage");
+        this.$message.success("图片上传成功!");
+      }
+    },
+
+    // 图片上传失败
+    imgUploadError(res) {
+      if (res !== "break") {
+        this.$message.error("图片上传失败!");
+        this.$refs.ruleForm.validateField("storeImage");
+      }
+    },
+    // 判断图片规格
+    beforeAvatarUpload(file) {
+      let isJPG = false;
+      if (
+        file.type === "image/jpg" ||
+        file.type === "image/png" ||
+        file.type === "image/jpeg"
+      ) {
+        isJPG = true;
+      }
+      const isLt2M = file.size / 1024 / 1024 < 1;
+      if (!isJPG) {
+        this.$message.error("图片格式不正确!");
+      }
+      if (!isLt2M) {
+        this.$message.error("图片大小不能超过 1MB!");
+      }
+      return isJPG && isLt2M;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.supplierAdd {
+  .good_info_img_div {
+    ul {
+      width: 100%;
+      li {
+        float: left;
+        width: 55px;
+        height: 55px;
+        padding: 0 5px 0 0;
+        .img-show-li-div {
+          width: 50px;
+          height: 50px;
+          border: 1px solid #dfe4ed;
+          background: #dfe4ed;
+          position: relative;
+          img.img-show {
+            width: 100%;
+            height: 100%;
+            position: relative;
+            display: inline-block;
+          }
+          i.el-icon-close {
+            position: absolute;
+            z-index: 2;
+            top: 0;
+            right: 0;
+            color: #dfe4ed;
+          }
+          &:hover {
+            i.el-icon-close {
+              cursor: pointer;
+              color: #6954f0;
+            }
+          }
+        }
+      }
+    }
+  }
+  .supplierAdd-title {
+    border-left: 5px solid #006eff;
+    margin: 6px 0 25px 10px;
+    width: 100%;
+    box-sizing: border-box;
+    padding: 0 0 0 10px;
+  }
+  .supplierAdd-title-left {
+    width: 120px;
+    .btnupload {
+      width: 40px;
+      height: 40px;
+      line-height: 40px;
+    }
+    .avatar-uploader-icon {
+      width: 40px;
+      height: 40px;
+      line-height: 40px;
+    }
+    .img-find {
+      position: relative;
+      width: 40px;
+      height: 40px;
+      line-height: 40px;
+      &:hover {
+        .img-close {
+          color: #409eff;
+        }
+      }
+    }
+    .img-close {
+      position: absolute;
+      top: 0;
+      right: 0;
+      z-index: 999999;
+      &:hover {
+        cursor: pointer;
+      }
+    }
+  }
+  .supplierAdd-title-right {
+    width: calc(100% - 120px);
+  }
+}
+
+#map {
+  width: 100%;
+  height: 400px;
+
+  cursor: pointer;
+}
+</style>

+ 164 - 0
src/views/operate/shop/components/mapModal.vue

@@ -0,0 +1,164 @@
+<template>
+  <el-dialog :visible="_visible" title="选择地址" @close="_visible = false">
+	  <div id="r-result">
+      <p>请输入:</p>
+      <input ref="input" size="mini" type="text" id="suggestId" :value="value"/>
+    </div>
+	  <div id="l-map" ref="map" />
+	  <div 
+      id="searchResultPanel" 
+      style="border:1px solid #C0C0C0;width:150px;height:auto; display:none;" 
+    />
+
+    <div class="flex-end" style="margin-top:10px">
+      <el-button type="primary" size="mini" @click="save">保存</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name:'MapModal',
+  props:['visible', 'toponym'],
+  data(){
+    return {
+      isInit: true,
+      value: "",
+      point: []
+    }
+  },
+  computed:{
+    _visible:{
+      get(){
+        return this.visible
+      },
+      set(newVal){
+        this.$emit('update:visible',newVal)
+      }
+    }
+  },
+  watch:{
+    visible(newVal){
+      if(!newVal) return
+      this.$nextTick(() => {
+        if(this.isInit){
+          this.initMap()
+          this.isInit = false
+          return
+        }
+
+        this.initValue()
+      })  
+    }
+  },
+  methods:{
+    initMap(){
+      this.createMap()
+      this.createAutoComplete()
+      this.registerHightlightEvent()
+      this.registerConfirmEvent()
+      this.registerConfirmEvent()
+    },
+    createMap(){
+      this.map = new BMap.Map('l-map')
+      this.map.centerAndZoom('北京',28)
+    },
+    createAutoComplete(){
+      this.ac = new BMap.Autocomplete({
+        input:'suggestId',
+        localtion: this.map
+      })
+    },
+    registerHightlightEvent(){
+      this.ac.addEventListener('onhighlight',(e) => {
+         var str = "";
+		     var _value = e.fromitem.value;
+		     var value = "";
+		     if (e.fromitem.index > -1) {
+		     	value = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
+		     }    
+		     str = "FromItem<br />index = " + e.fromitem.index + "<br />value = " + value;
+     
+		     value = "";
+		     if (e.toitem.index > -1) {
+		     	_value = e.toitem.value;
+		     	value = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
+		     }    
+		     str += "<br />ToItem<br />index = " + e.toitem.index + "<br />value = " + value;
+		     document.getElementById("searchResultPanel").innerHTML = str;
+      })
+    },
+    registerConfirmEvent(){
+      this.ac.addEventListener("onconfirm", (e) => {   //鼠标点击下拉列表后的事件
+        var _value = e.item.value;
+	    	this.value = _value.province +  _value.city +  _value.district +  _value.street +  _value.business;
+	    	document.getElementById("searchResultPanel").innerHTML ="onconfirm<br />index = " + e.item.index + "<br />myValue = " + this.value;
+	    	this.setPlace();
+	    });
+    },
+    setPlace(){
+	    this.map.clearOverlays();    //清除地图上所有覆盖物
+	    
+      const onSearchComplete = () => {
+        const { point} = local.getResults().getPoi(0)
+
+        this.point = [point.lat , point.lng]
+        // const pp = local.getResults().getPoi(0).point;    //获取第一个智能搜索的结果
+        
+	    	this.map.centerAndZoom(point, 18);
+	    	this.map.addOverlay(new BMap.Marker(point));    //添加标注
+	    }
+
+	    var local = new BMap.LocalSearch(this.map, { //智能搜索
+	      onSearchComplete
+	    });
+
+	    local.search(this.value);
+    },
+    initValue(){
+      if(this.toponym) return
+      this.value = this.toponym
+      this.setPlace()
+    },
+    save(){
+      console.log(this.point,this.value)
+      if(this.point.length !== 2 || !this.value){
+        this.$message.warning('请选择地址')
+        return
+      }
+
+      this.$emit('save',{ point:this.point, name:this.value })
+      this._visible = false
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+#l-map{
+  width: 100%;
+  height: 300px;
+}
+
+#r-result{
+  width: 100%;
+  height: 34px;
+  margin-bottom: 20px;
+  font-size: 16px;
+  display: flex;
+  align-items: center;
+
+  p{
+    width:60px;
+  }
+
+  input {
+    border:1px solid #DCDFE6;
+    width: 100%;
+    height: 100%;
+    border-radius: 5px;
+    box-sizing: border-box;
+    padding: 5px;
+  }
+}
+</style>

+ 225 - 0
src/views/operate/shop/detail.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="supplierDetail">
+    <div class="supplierDetail-main" v-if="powers.some((i) => i == '001')">
+      <el-tabs v-model="activeTabs">
+        <el-tab-pane :label="'新建' + title" name="0" v-if="queryType === 'add'">
+          <base-form
+            v-if="newTime !== ''"
+            :type="queryType"
+            :id="queryId"
+            :newTime="newTime"
+            :sitem="sitem"
+            @refresh="refresh"
+          />
+        </el-tab-pane>
+        <el-tab-pane :label="title + '详情'" name="1" v-if="queryType !== 'add'">
+          <base-form
+            v-if="newTime !== ''"
+            :type="queryType"
+            :id="queryId"
+            :newTime="newTime"
+            :sitem="sitem"
+            @refresh="refresh"
+          />
+        </el-tab-pane>
+      </el-tabs>
+
+    </div>
+    <div v-else>
+      <no-auth></no-auth>
+    </div>
+  </div>
+</template>
+<script>
+import asyncRequest from "@/apis/service/operate/shop";
+import resToken from "@/mixins/resToken";
+import { mapGetters } from "vuex";
+import baseForm from "./components/baseForm";
+
+export default {
+  name: "shopDetail",
+  mixins: [resToken],
+  components: {
+    baseForm,
+  },
+  computed: {
+    ...mapGetters(["tablebtnSize", "searchSize", "size"]),
+    powers() {
+      const tran =
+        this.$store.getters.btnList.find((item) => item.menu_route == "shopDetail") || {};
+      const { action } = tran ?? {};
+      return action ?? [];
+    },
+  },
+
+  data() {
+    return {
+      visible: false,
+      title: "供应商店铺",
+      size: "small",
+      activeTabs: "1",
+      activeNames: ["0", "1"],
+      editColumns: [
+        {
+          prop: "code",
+          label: "供应商编号",
+          span: 6,
+        },
+        {
+          prop: "status",
+          label: "使用状态",
+          _slot_: "status",
+          span: 4,
+        },
+        {
+          prop: "ocr_status",
+          label: "执照识别状态",
+          _slot_: "ocr_status",
+          span: 5,
+        },
+
+        {
+          prop: "creater",
+          label: "创建人",
+          span: 4,
+        },
+        {
+          prop: "addtime",
+          label: "创建时间",
+          span: 5,
+        },
+      ],
+      ocr_status: [
+        { id: "0", label: "未上传" },
+        { id: "1", label: "识别成功" },
+        { id: "2", label: "识别失败" },
+      ],
+      newTime: "",
+      loading: false,
+      queryType: "",
+      queryId: "",
+      status: "",
+      sitem: null,
+    };
+  },
+  mounted() {
+    this.initForm();
+  },
+  methods: {
+    async initForm() {
+      const { id, type } = this.$route.query;
+      this.queryId = id;
+      this.queryType = type;
+      this.activeTabs = type === "add" ? "0" : "1";
+      this.loading = true;
+      if (this.queryType === "add") {
+        this.sitem = {};
+        this.getNewTime();
+      } else {
+        await this.initData();
+      }
+
+      this.loading = false;
+    },
+    // 点击业务审核的保存按钮
+
+    // async examForm(e) {
+    //   console.log(e);
+    //   if (!this.loading) {
+    //     let type = e.state === "1" ? "1" : e.rebut;
+    //     await this.setstatus(type, "提交采购部门审核", e.remark);
+    //   }
+    // },
+    async setstatus(type, detail, remark) {
+      await this.$confirm(`确定要${detail}?`, {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(async () => {
+          let _model = {
+            spuCode: this.queryId,
+            status: type,
+            remark: remark,
+          };
+          let res = await asyncRequest.status(_model);
+          if (res && res.code === 1) {
+            this.$notify.success({
+              title: "提交成功!",
+              message: "",
+            });
+            await this.initForm();
+          }
+        })
+        .catch(() => {
+          console.log("取消");
+        });
+    },
+    handleClick(row) {
+      console.log(row);
+    },
+    async refresh(e) {
+      this.routeReGoto("supplier", {});
+    },
+    async initData() {
+      this.loading = true;
+      const { code, data } = await asyncRequest.detail({
+        code: this.queryId,
+      });
+      this.loading = false;
+      if (code === 1) {
+        this.sitem = JSON.parse(JSON.stringify(data));
+        const { status } = this.sitem;
+        this.status = status;
+        this.getNewTime();
+      }
+    },
+    getNewTime() {
+      this.newTime = new Date().valueOf();
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.supplierDetail {
+  position: relative;
+  height: 100%;
+  width: 100%;
+  box-sizing: border-box;
+  .supplierDetail-main {
+    position: relative;
+    padding: 10px;
+    height: 100%;
+    width: 100%;
+  }
+  .supplierDetail-title {
+    border-top: 1px solid #ebeef5;
+    span {
+      height: 50px;
+      line-height: 50px;
+      font-family: "微软雅黑", sans-serif;
+      font-weight: 400;
+      font-style: normal;
+      font-size: 16fpx;
+      text-align: left;
+    }
+  }
+  /deep/ .ddiv {
+    border-top: 1px solid #dcdfe6;
+  }
+  /deep/ .dtitle {
+    width: 40px;
+    text-align: center;
+    height: 100%;
+    min-height: 100%;
+    ul {
+      padding: 12px 0 0 0;
+    }
+  }
+  /deep/ .dmain {
+    padding: 20px 0 0 0;
+    width: calc(100% - 40px);
+    border-left: 1px solid #dcdfe6;
+  }
+}
+</style>