Browse Source

feat:开票申请-选择需求订单

snow 1 year ago
parent
commit
64cf659126

+ 2 - 1
src/apis/components/letter.js

@@ -3,6 +3,7 @@ const api = 'admin/planOrder/'
 export default {
   // 列表
   list: (data, params) => http(api + 'list', data, 'post', params),
-  delete: (data, params) => http(api + 'delete', data, 'post', params)
+  delete: (data, params) => http(api + 'delete', data, 'post', params),
+  detail: (data, params) => http(api + 'info', data, 'post', params)
 }
 

+ 3 - 1
src/apis/components/search.js

@@ -3,5 +3,7 @@ import http from "@/apis/axios";
 export default {
     store: (data, params) => http("admin/supplierStore/index", data, "post", params),
     customer: (data, params) => http("admin/customer/query", data, "post", params),
-    demand: (data, params) => http("admin/service_item/index", data, "post", params)
+    demand: (data, params) => http("admin/service_item/index", data, "post", params),
+    addr: (data, params) => http("admin/login/area", data, "post", params),
+    addr: (data, params) => http("admin/invoiceTitle/index", data, "post", params),
 };

+ 2 - 0
src/apis/service/customerService/workbench/index.js

@@ -17,6 +17,8 @@ export default {
   planCreate:(data, params) => http(planAPI + 'create', data, 'post', params),
   //预算函状态
   planStatus:(data, params) => http(planAPI + 'status', data, 'post', params),
+  planDetail:(data, params) => http(planAPI + 'info', data, 'post', params),
+  planDelete:(data, params) => http(planAPI + 'deleteinfo', data, 'post', params),
 
   // 采返商品详情
   good_detail: (data, params) =>

+ 5 - 1
src/components/globalComponents/ex-table/main.vue

@@ -10,7 +10,7 @@
           class="setcol-table-lie"
           :hide-on-click="false"
         >
-          <el-button size="mini">列设置</el-button>
+          <el-button size="mini" v-if="!noSetCol">列设置</el-button>
           <el-dropdown-menu slot="dropdown" :class="'ex-table-setcol-dropdown'">
             <template v-for="(column, index) in setColumns">
               <el-dropdown-item v-if="!column._noset_" :key="'setColumns' + index">
@@ -199,6 +199,10 @@ export default {
      *   <el-button size="small" @click="click(slotProps)">扩展1</el-button>
      * </template>
      */
+    noSetCol:{
+      type:Boolean,
+      default:false
+    },
     table: {
       type: Object,
       default: () => {},

+ 6 - 6
src/components/manage/src/letter/_columns.js

@@ -120,12 +120,12 @@ export const project = [
         label: '评估预算函编号',
         width: '120px',
     },
-    {
-        prop: mapFields.status,
-        label: '是否选中',
-        width: '120px',
-        _slot_:'is_check'
-    },
+    // {
+    //     prop: mapFields.status,
+    //     label: '是否选中',
+    //     width: '120px',
+    //     _slot_:'is_check'
+    // },
     {
         prop: mapFields.letter,
         label: '计划预算函预览',

+ 17 - 6
src/components/manage/src/letter/index.vue

@@ -1,12 +1,16 @@
 <template>
   <div>
+
     <div style="display:flex;justify-content:flex-end" v-if="!readonly">
+      <el-button size="mini" @click="linkToLetterUpdate" v-if="type === 'project'">编辑预算函</el-button>
       <el-button size="mini" @click="onDownloadTemplate">下载{{mapTitle[type]}}模板</el-button>
       <el-button size="mini" type="primary" @click="handleUploadModalVisible">上传{{mapTitle[type]}}</el-button>
     </div>
     <ex-table
-      :columns="[...(isSelection ? [{ type: 'selection' }] : []),
-         ...mapColumns[type || 'budget'], { label: '操作', _slot_: 'action', width: '100px',fixed:'right'}]"
+      :columns="[
+        ...(isSelection ? [{ type: 'selection' }] : []),
+        ...mapColumns[type || 'budget'], { label: '操作', _slot_: 'action', width: '100px',fixed:'right'}
+      ]"
       @selection="handleSelection"
       :table="table"
       :data="tableData"
@@ -14,7 +18,7 @@
     >
       <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  ?  "已选择" : "未选择"}}
+         {{ Number(scope.row.is_check) === mapCheck[type || 'budget'].ok  ?  "已选择" : "未选择" }}
         </el-tag>
       </template>
 
@@ -52,16 +56,16 @@
 
 
 <script>
+import { mapColumns, mapCheck, mapTitle } from "./_columns"
 import ExcelUploadModal from "./xlsxUploadModal.vue"
-import PreviewModal from "./previewModal.vue"
 import asyncRequest from "@/apis/components/letter"
+import PreviewModal from "./previewModal.vue"
 import { writeFile, utils } from "xlsx";
 import { template } from "./_template"
-import { mapColumns, mapCheck, mapTitle } from "./_columns"
 
 export default {
   name:'Letter',
-  /* type budget预算函 activity活动方案 project计划 **/
+  /* type:budget预算函 activity活动方案 project计划 **/
   props:['title', 'requsetMethod', 'status', 'id', 'isSelection', 'readonly','type', 'beforeModalVisible', 'isFile'],
   components:{ ExcelUploadModal, PreviewModal },
   data(){
@@ -121,6 +125,13 @@ export default {
 
       this.xlsxVisible = true
     },
+    linkToLetterUpdate(){
+      if(this.beforeModalVisible && !this.beforeModalVisible()){
+        return
+      }
+
+      this.$emit('updateLetter')
+    },
     async onSearch(){
       if(this.type === 'settlement') return
       this.loading = true

+ 3 - 1
src/components/search/index.js

@@ -1,11 +1,13 @@
 import SearchStore from "./src/store.vue"
 import SearchCustomer from "./src/customer.vue"
 import SearchDemand from "./src/demand.vue"
+import SearchTitle from "./src/title.vue"
 
 const searchComponents = [
   SearchStore,
   SearchCustomer,
-  SearchDemand
+  SearchDemand,
+  SearchTitle
 ]
 
 export default searchComponents

+ 54 - 0
src/components/search/src/addr.vue

@@ -0,0 +1,54 @@
+<template>
+  <el-select
+    v-model="_value"
+    :size="size" 
+    remote
+    :remote-method="onFetch"
+    :placeholder="placeholder || '请选择客户公司'"
+    :loading="loading"
+    reserve-keyword
+    filterable
+    clearable
+  >
+    <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:'SearchCustomer',
+  props:['size', 'value' , 'placeholder', 'level'],
+  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(){
+      const {code, data} = await asyncRequest.addr({ level: this.level })
+      if(code !== 1) return
+      this.list = data
+    }
+  }
+}
+</script>

+ 54 - 0
src/components/search/src/title.vue

@@ -0,0 +1,54 @@
+<template>
+  <el-select
+    v-model="_value"
+    :size="size" 
+    remote
+    :remote-method="onFetch"
+    :placeholder="placeholder || '请选择客户公司'"
+    :loading="loading"
+    reserve-keyword
+    filterable
+    clearable
+  >
+    <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:'SearchInvoiceTitle',
+  props:['size', 'value' , 'placeholder', 'level'],
+  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(){
+      const {code, data} = await asyncRequest.addr({ level: this.level })
+      if(code !== 1) return
+      this.list = data
+    }
+  }
+}
+</script>

+ 6 - 4
src/views/customerService/demandOrder/detail.vue

@@ -5,16 +5,16 @@
         <el-tab-pane label="需求订单详情" name="1">
           <el-collapse v-model="activeNames" style="margin: -18px 0 0 0">
            <el-collapse-item title="需求订单详情" name="1">
-             <demand-detail :sitem="sitem" @refresh="refresh" @switch-node="handleSwitchNode" />
+             <demand-detail :sitem="sitem" @refresh="refresh" @switchNode="handleSwitchNode" />
            </el-collapse-item>
            <el-collapse-item title="评估预算函管理" name="2" v-if="Number(sitem.status) === 1">
-             <manage-budgeet-letter :sitem="sitem" @selected-change="handleSelectedChange($event,'budget')" />
+             <manage-budgeet-letter :sitem="sitem" @selectedChange="handleSelectedChange($event,'budget')" />
            </el-collapse-item> 
            <el-collapse-item title="活动方案管理" name="3" v-if="Number(sitem.status) === 2">
-             <manage-activity-scheme :sitem="sitem" @selected-change="handleSelectedChange($event,'activity')" />
+             <manage-activity-scheme :sitem="sitem" @selectedChange="handleSelectedChange($event,'activity')" />
            </el-collapse-item> 
            <el-collapse-item title="计划预算函管理" name="10" v-if="Number(sitem.status) === 3">
-             <manage-project-letter :sitem="sitem" @selected-change="handleSelectedChange($event,'project')" />
+             <manage-project-letter :sitem="sitem" @selectedChange="handleSelectedChange($event,'project')" />
            </el-collapse-item> 
             <el-collapse-item title="待客户上传确认凭证" name="4" v-if="Number(sitem.status) === 4">
               <credentials-node @confirm="handleCustomerConfirm($event)" />
@@ -165,6 +165,8 @@ export default {
     async handleSwitchNode(mode){
       const minuend = mode === 'next' ? 1 : -1
       const updatedStatus = this.sitem.status + minuend
+
+      console.log(this.selected)
       
       if(mode === 'next'){
         if(updatedStatus === 2 && this.selected.budget === 0){

+ 169 - 0
src/views/customerService/invoiceApply/components/_columns.js

@@ -0,0 +1,169 @@
+const columns = [
+  {
+    type:'selection'
+  },
+  {
+    prop: 'reqCode',
+    label: '需求订单编号',
+    width: '185'
+  },
+  {
+    prop: 'shortname',
+    label: '活动城市',
+    width: '90'
+  },
+
+  {
+    prop: 'req_demand',
+    label: '项目需求',
+    width: '120px'
+  },
+  {
+    prop: 'participant',
+    label: '活动人数',
+    width: '155'
+  },
+
+  {
+    prop: 'require_item',
+    label: '活动需求',
+    width: '155px'
+  },
+  {
+    prop: 'act_time',
+    label: '活动时间',
+    width: '155px'
+  },
+  {
+    prop: 'act_day_count',
+    label: '活动总天数',
+    'width': '90'
+  },
+  {
+    prop: 'req_endtime',
+    label: '咨询截止时间',
+    'min-width': '125'
+  },
+  {
+    prop: 'status',
+    label: '状态',
+    minWidth: '170px',
+    _slot_:'status'
+  },
+  {
+    prop: 'companyName',
+    label: '需求公司',
+    'width': '150'
+  },
+  {
+    prop: 'req_tel',
+    label: '联系电话',
+    'width': '120'
+  },
+  {
+    prop: 'req_user_name',
+    label: '创建人',
+    'width': '120'
+  },
+  {
+    prop: 'update_time',
+    label: '创建时间',
+    'width': '120'
+  }
+]
+
+const demandDetailColumns = [
+  {
+    prop:'reqCode',
+    label:"需求订单编号",
+    span:8
+  },
+  {
+    prop:'req_user_name',
+    label:'创建人',
+    span:8
+  },
+  {
+    prop:'create_time',
+    label:'创建时间',
+    span:8
+  },
+  {
+    prop:'shortname',
+    label:'活动城市',
+    span:8
+  },
+  {
+    prop:'require_item',
+    label:'活动需求',
+    span:8
+  },
+  {
+    prop:'act_time',
+    label:'活动时间',
+    span:8
+  },  
+  {
+    prop:'participant',
+    label:'活动人数',
+    span:8
+  },
+  {
+    prop:'req_demand',
+    label:'项目需求',
+    span:8
+  },
+  {
+    prop:'req_endtime',
+    label:'招标截止时间',
+    span:8
+  },
+  {
+    prop: 'companyName',
+    label: '需求公司',
+    span:8
+  },
+  {
+    prop:'flow_stage',
+    label:'当前阶段',
+    span:8
+  },
+  {
+    prop:'supplierName',
+    label:'活动执行供应商',
+    span:8
+  },
+  {
+    prop:'budget',
+    label:'需求预算',
+    span:8
+  },
+  {
+    prop:'TODO',
+    label:'预付类型',
+    span:8,
+    _slot_:'prepaymentType'
+  },
+  {
+    prop:'TODO',
+    label:'预付金额',
+    span:8,
+    _slot_:'prepaymentAmount'
+  },
+  {
+    prop:'settle_amount',
+    label:'活动结算金额',
+    span:8
+  },
+  {
+    prop:'TODO',
+    label:'当前状态',
+    span:16,
+    _slot_:'currentStatus'
+  }
+]
+
+export {
+  columns,
+  demandDetailColumns
+}

+ 96 - 36
src/views/customerService/invoiceApply/components/createInvoiceApply.vue

@@ -1,54 +1,95 @@
 <template>
   <el-form ref="ruleForm" :rules="rules" :model="ruleForm" label-position="left" label-width="80px" size="mini">
-    <ul style="display:flex">
-      <li style="width:500px;margin-right:10px">
-        <el-form-item label="发票抬头" prop="invoiceTitle">
-          <el-input v-model="ruleForm.invoiceTitle" style="width:400px" />
-        </el-form-item>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="需求订单">
+            <el-input placeholder="请选择需求订单" @focus="demandVisible = true" v-if="!demandInfo"/>
 
-        <show-data-table 
-          :sitem="{}"
-          style="padding: -5px 0 10px 0"
-          :newTime="newTime"
-          :columns="invoiceColumns"
-        />
-      </li>
+            <div style="display:flex" v-else>
+              <show-data-table
+                style="padding: -5px 0 10px 0"
+                :newTime="newTime"
+                :sitem="demandInfo"
+                :columns="demandDetailColumns"
+              >
+                <template #currentStatus>
+                  <el-tag size="mini" style="margin-left:5px">
+                    {{(demandStatusOptions.find(({value}) => value === String(demandInfo.status)) || {}).label || '--'}}
+                  </el-tag>
+                </template>
 
-      <li style="500px">
-        <el-form-item label="申请总金额" label-width="95px" prop="applyAmount">
-          <digital-input
-            :values="ruleForm.applyAmount"
-            :placeholder="'采购毛利率'"
-            :min="0"
-            :position="'right'"
-            :precision="2"
-            :size="'mini'"
-            :controls="false"
-            :append="'元'"
-            @reschange="number_change($event)"
+                <template #prepaymentType>
+                  <p style="margin-left:5px">
+                    {{demandInfo.prepay_type}}
+                  </p>
+                </template>
+              </show-data-table>
+
+              <el-tooltip placement="top" content="重新选择需求订单">
+                <i class="el-icon-circle-close" style="font-size:18px;margin:0px 10px;cursor:pointer" @click="handleDemandChange(null)" />
+              </el-tooltip>
+            </div>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row gutter="20">
+        <el-col :span="12">
+          <el-form-item label="发票抬头" prop="invoiceTitle">
+            <!-- <el-input v-model="ruleForm.invoiceTitle" placeholder="客户公司抬头" /> -->
+            <search-invoice-title />
+          </el-form-item>
+
+          <show-data-table 
+            :sitem="{}"
+            style="padding: -5px 0 10px 0"
+            :newTime="newTime"
+            :columns="invoiceColumns"
           />
-        </el-form-item>
+        </el-col>
+
+        <el-col :span="12">
+          <el-form-item label="申请总金额" label-width="95px" prop="applyAmount">
+            <digital-input
+              :values="ruleForm.applyAmount"
+              :placeholder="'采购毛利率'"
+              :min="0"
+              :position="'right'"
+              :precision="2"
+              :size="'mini'"
+              :controls="false"
+              :append="'元'"
+              @reschange="number_change($event)"
+            />
+          </el-form-item>
 
-        <el-form-item label="发票类型" prop="invoiceType">
-          <el-select v-model="ruleForm.invoiceType" style="width:400px" placeholder="发票类型">
-            <el-option />
-          </el-select>
-        </el-form-item>
+          <el-form-item label="发票类型" prop="invoiceType">
+            <el-select v-model="ruleForm.invoiceType" style="width:100%" placeholder="发票类型">
+              <el-option />
+            </el-select>
+          </el-form-item>
 
-        <el-form-item label="备注" prop="remark">
-          <el-input placeholder="备注" v-model="ruleForm.remark" style="width:400px" type="textarea" />
-        </el-form-item>
-      </li>
-    </ul>
+          <el-form-item label="备注" prop="remark">
+            <el-input placeholder="备注" v-model="ruleForm.remark" style="width:100%" type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
 
-    <div style="width:980px;display:flex;justify-content:flex-end;margin-bottom:10px">
+    <div style="display:flex;justify-content:flex-end;margin-bottom:10px">
       <el-button size="mini" type="primary" @click="onSubmit">保存</el-button>
     </div>
+
+    <demand-modal :visible.sync="demandVisible" @change="handleDemandChange" />
     
   </el-form>
 </template>
 
 <script>
+import DemandModal from "./demandModal.vue"
+import { demandDetailColumns } from "./_columns"
+import demandRequest from "@/apis/service/customerService/demandOrder";
+import { demandStatusOptions } from '@/assets/js/statusList'
+
   /** 
   * @props
   invoiceTitle /发票抬头
@@ -60,8 +101,13 @@
 export default {
   name:'CreateInvoiceNode',
   props:['isApply'],  //isApply 是否为创建开票申请
+  components:{ DemandModal },
   data(){
       return {
+        demandStatusOptions,
+        demandInfo : null,
+        demandDetailColumns,
+        demandVisible:false,
           invoiceColumns:[
             {
               prop: 'todo',
@@ -85,6 +131,7 @@ export default {
             }
           ],
           newTime:"",
+          demandVisible: false,
           ruleForm:{
             invoiceTitle:"", //发票抬头
             invoiceType:"", //发票类型
@@ -135,6 +182,19 @@ export default {
     number_change(e) {
       this.ruleForm.applyAmount = e || 0
     },
+    async handleDemandChange(demandInfo){
+      if(!demandInfo){
+        this.demandInfo = null
+        this.ruleForm['需求订单属性'] = ""
+        return
+      }
+      
+      this.demandLoading = true
+      const { data, code } = await demandRequest.detail({ id: demandInfo.id })
+      this.demandLoading = false
+      if(code !== 1) return
+      this.demandInfo = data
+    } 
   }
 }
 </script>

+ 139 - 0
src/views/customerService/invoiceApply/components/demandModal.vue

@@ -0,0 +1,139 @@
+<template>
+  <el-dialog :visible="visible" title="选择需求订单" center width="1024px" top="60px" @close="handleClose" :close-on-click-modal="false">
+    <div class="demand-order">
+      <ex-table
+        :table="table"
+        v-loading="loading"
+        :data="tableData"
+        :columns="columns"
+        :page="pageInfo"
+        size="mini"
+        @page-curr-change="handlePageChange"
+        @page-size-change="handleSizeChange"
+        @selection-change="list => selected = list"
+        @screen-reset="
+          pageInfo.curr = 1;
+          parmValue.page = 1;
+          searchList();
+        "
+        @screen-submit="
+          pageInfo.curr = 1;
+          parmValue.page = 1;
+          searchList();
+        "
+      >
+        <template #status="{ scope }">
+          <el-tag size="mini"
+            :type="(demandStatusOptions.find((item) => item.value == scope.row.status) || {}) .type || ''"
+            v-text="(demandStatusOptions.find((item) => item.value == String(scope.row.status)) || {}) .label || '--'"
+          />
+        </template>
+      </ex-table>
+
+      <div class="flex-end">
+        <el-button type="primary" size="mini" @click="onConfirmDemand">保存</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import pageInfo from "@/mixins/elPaginationHandle"
+import asyncRequest from "@/apis/service/customerService/demandOrder";
+import { demandStatusOptions } from '@/assets/js/statusList'
+import { columns } from "./_columns"
+export default {
+  props:['visible'],
+  mixins:[pageInfo],
+  computed:{
+    _visible:{
+      get(){
+        return this.visible
+      },
+      set(val){
+        this.$emit('update:visible', val)
+      }
+    }
+  },
+  watch:{
+    visible(value){
+      if(!value) return
+      this.searchList()
+    }
+  },
+  data(){
+    return {
+      demandStatusOptions,
+      columns,
+      size: 'mini',
+      loading: false,
+      tableData: [],
+      selected: [],
+      table: {},
+      parmValue: {
+        page: 1
+      },
+      pageInfo: {
+        size:15,
+        curr:1,
+        total:0
+      },
+      table: {
+        stripe: true,
+        border: true,
+      }
+    }
+  },
+  methods:{
+    handleClose(){
+      this.pageInfo = {
+        curr: 1,
+        size: 15,
+        total: 0
+      }
+
+      this.parmValue = {
+        page: 1
+      }
+      this._visible = false
+    },
+    async searchList() {
+      this.loading = true;
+      const res = await asyncRequest.list({
+        ...this.parmValue,
+      });
+      if (res && res.code === 1 && res.data) {
+        this.tableData = res.data.list;
+        this.pageInfo.total = Number(res.data.count);
+      } else {
+        this.tableData = [];
+        this.pageInfo.total = 0;
+      }
+      this.loading = false;
+    },
+    onConfirmDemand(){
+      if(this.selected.length === 0){
+        this.$message.warning('请选择一个需求订单!')
+        return
+      }
+
+      if(this.selected.length > 1){
+        this.$message.warning('只能选择一个需求订单!')
+        return
+      }
+
+
+      this.$emit('change',this.selected[0])
+      this._visible = false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.demand-order{
+  /deep/ .el-pagination{
+    float: left !important;
+  }
+}
+</style>

+ 9 - 50
src/views/customerService/invoiceApply/detail.vue

@@ -4,23 +4,23 @@
       <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">
-            <el-collapse-item :title="'需求订单详情'" name="1">
-              <show-data-table
+            <el-collapse-item :title="'需求订单详情'" name="1" v-if="id !== 'add'">
+              <!-- <show-data-table
                 style="padding: -5px 0 10px 0"
                 :newTime="newTime"
                 v-if="newTime !== ''"
                 :sitem="sitem"
                 :columns="demandColumns"
-              />
+              /> -->
             </el-collapse-item>
 
-            <el-collapse-item title="创建开票申请" name="2">
+            <el-collapse-item title="创建开票申请" name="2" v-else>
               <create-invoice-apply />
             </el-collapse-item>
 
-            <el-collapse-item title="发票申请管理" name="3">
+            <!-- <el-collapse-item title="发票申请管理" name="3">
               <upload-invoice-node />
-            </el-collapse-item>
+            </el-collapse-item> -->
           </el-collapse>
         </el-tab-pane>
       </el-tabs>
@@ -43,6 +43,9 @@ export default {
   components:{ CreateInvoiceApply },
   computed: {
     ...mapGetters(["tablebtnSize", "searchSize", "size", "private_field", "isSupertube"]),
+    id(){
+      return this.$route.query.id
+    },
     powers() {
       const { btnList } = this.$store.getters;
       const tran = btnList.find((item) => item.menu_route == "invoiceApplyDetail") || {};
@@ -100,51 +103,7 @@ export default {
     },
     async initData() {
       this.loading = true;
-      const { code, message, data } = await asyncRequest.detail({
-        bargainNo: this.queryId,
-      });
-
-      this.eaxmList = [];
       this.loading = false;
-      if (code === 0) {
-        this.sitem = JSON.parse(JSON.stringify(data));
-
-        const { status, exam_info } = this.sitem;
-        this.orderItem = {};
-        this.moneyDirItem = {};
-        this.moneyItem = {};
-        this.BossItem = {};
-        if (exam_info && exam_info.length > 0) {
-          exam_info.forEach((e) => {
-            console.log(e.status);
-            switch (e.info_status + "") {
-              case "0":
-                this.orderItem = JSON.parse(JSON.stringify(e));
-                break;
-              case "2":
-                this.moneyDirItem = JSON.parse(JSON.stringify(e));
-                break;
-              case "4":
-                this.moneyItem = JSON.parse(JSON.stringify(e));
-                break;
-              case "7":
-                this.BossItem = JSON.parse(JSON.stringify(e));
-                break;
-              default:
-                this.orderItem = JSON.parse(JSON.stringify(e));
-            }
-          });
-          this.eaxmList = JSON.parse(JSON.stringify(exam_info));
-        }
-
-        this.status = status;
-
-        this.getNewTime();
-      } else if (code >= 100 && code <= 104) {
-        await this.logout();
-      } else {
-        this.$message.warning(message);
-      }
     },
     getNewTime() {
       this.newTime = new Date().valueOf();

+ 7 - 0
src/views/customerService/invoiceApply/index.vue

@@ -138,6 +138,10 @@
                   "
                 /> 
               </el-col>
+
+              <div class="fr">
+                <el-button type="primary" size="mini" @click="linkToCreateInvoice">添加</el-button>
+              </div>
             </el-row>
 
             <el-row style="margin-top:10px">
@@ -311,6 +315,9 @@ export default {
   },
 
   methods: {
+    linkToCreateInvoice(){
+      this.$router.push(`/customerService/invoiceApplyDetail?id=add`)
+    },
     getRouter(toRouter, queryId){
       if (toRouter && queryId) {
         let model = {

+ 134 - 0
src/views/customerService/workbench/_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
+  ]
+  

+ 6 - 1
src/views/customerService/workbench/detail.vue

@@ -67,6 +67,7 @@
                 :readonly="Number(sitem.status) !== 2"
                 :beforeModalVisible="beforeActivityModalVisible"
                 @selection="handleSelection($event, 'activity')"
+                @updateLetter="linkToLetterUpdate('evalute')"
                 :requsetMethod="reqsetActivityLetter"
               />
             </el-collapse-item>
@@ -78,7 +79,8 @@
                 :isFile="true"
                 :readonly="Number(sitem.status) !== 3"
                 :beforeModalVisible="beforeProjectModalVisible"
-                @selection="handleSelection($event, 'activity')"
+                @selection="handleSelection($event, 'project')"
+                @updateLetter="linkToLetterUpdate('activity')"
                 :requsetMethod="requsetProjectLetter"
               />
             </el-collapse-item>
@@ -310,6 +312,9 @@ export default {
       }
 
       return asyncRequest.planCreate(params)
+    },
+    linkToLetterUpdate(property){
+      this.$router.push('/customerService/letterEditing?id=' + this.selected[property][0])
     }
   },
 };

+ 264 - 0
src/views/customerService/workbench/letterEditing.vue

@@ -0,0 +1,264 @@
+<template>
+  <div style="padding:10px">
+    <ul style="width:100%;margin-bottom:10px;display:flex;justify-content:flex-end;margin-top:10px;align-items:center;">
+      <li style="line-height:30px;margin-right:20px">
+       <span>非税总报价 : </span>
+       <span>{{tableData.assess_price}}</span>
+      </li>
+
+      <li style="line-height:30px;margin-right:20px">
+       <span>含税总报价 : </span>
+       <span>{{tableData.assess_total_price}}</span>
+      </li>
+
+      <li style="line-height:30px;margin-right:20px">
+       <span>总税额 : </span>
+       <span>{{tableData.assess_price}}</span>
+      </li>
+
+
+      <!-- <li style="margin-right: 10px">
+        <el-button size="mini" type="primary" @click="downloadTemplate">下载模板</el-button>
+      </li> -->
+
+      <li>
+        <el-button size="mini" type="primary" @click="onSubmit">保存</el-button>
+      </li>
+    </ul>
+
+    <!-- <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> -->
+    <!-- <upload-excel :on-success="onSuccess" :before-upload="beforeUpload" /> -->
+
+    <ex-table
+      :columns="[ ...columns,{ _slot_: 'action', label: '操作', width: '160px' } ]"
+      :table="table"
+      :data="tableData.assessinfo"
+      style="margin: 15px 0 0 0"
+    >
+      <template #action={scope}>
+        <el-button size="mini" type="text" @click="copyItem(scope.row)">复制</el-button>
+        <el-button size="mini" type="text">编辑</el-button>
+        <el-button size="mini" type="text" @click="deleteItem(scope.row)">删除</el-button>
+        <!-- <el-button size="mini" type="text">保存</el-button> -->
+      </template>
+    </ex-table>
+  </div>
+</template>
+
+<script>
+import asyncRequest from "@/apis/service/customerService/workbench";
+import companyHelper from '@/mixins/companyHelper'
+import { MessageBox } from 'element-ui'
+import { template } from "./_template"
+import { utils, writeFile } from "xlsx"
+// import dayjs from 'dayjs'
+
+import {
+  helper,
+  columns,
+  getTableProperty,
+  createErrorMessage,
+  createFieldVerification,
+  requiredFields,
+} from './_template'
+
+export default {
+  mixins: [companyHelper],
+  props: ['visible'],
+  data() {
+    return {
+      columns,
+      loading: false,
+      tableData: [],
+      table: {
+        stripe: true,
+        border: true,
+        'max-height': '800px'
+      }
+    }
+  },
+  computed: {
+    total:{
+      handler(newVal){
+        newVal.reduce(item => ({
+
+        }),{
+          total:0,
+          includeTaxTotal: 0,
+          notTaxTotal: 0
+        })
+      },
+      deep: true,
+      immediate: true
+    },
+    innerVisible: {
+      get() {
+        return this.visible
+      },
+      set(newVal) {
+        this.$emit('update:visible', newVal)
+      }
+    }
+  },
+  mounted(){
+    this.onSearch()
+  },
+  methods: {
+    async onSearch(){
+      this.loading = true
+      
+      const { code, data } = await asyncRequest.planDetail({
+        id: this.$route.query.id
+      });
+
+      this.loading = false
+      if(code !== 1) return
+
+      this.tableData = data
+    },
+    async copyItem(row){
+      this.loading = true
+      const { code } = await asyncRequest.planCreate({
+        req_id: this.tableData.req_id,
+        plan_info: [ row ],
+        store_id: 3
+      })
+      this.loading = false
+
+      if(code !== 1) return
+      this.onSearch()
+    },
+    async deleteItem(row){
+      this.loading = true
+      const { code } = await asyncRequest.planDelete({id: row.id})
+      this.loading = false
+      if(code !== 1) return
+      this.onSearch()
+    },
+    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
+      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() {
+      this.loading = true
+      const param = {
+        plan_info:[],
+        plan_id:7,
+      }
+      this.loading = false
+      const { code } = await asyncRequest.planUpdate(param)
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.error-message__wrapper{
+  width: 1024px;
+}
+</style>