Переглянути джерело

Merge branch 'develop_fybx02' of jianguan-web/jg-ocs-html into pre-release

李磊 3 роки тому
батько
коміт
694ee3dbd5
38 змінених файлів з 4722 додано та 47 видалено
  1. 5 0
      package-lock.json
  2. 1 0
      package.json
  3. 1 1
      public/version.json
  4. 12 0
      src/api/dowloadFile.js
  5. 186 0
      src/api/expenseManagement.js
  6. 28 8
      src/components/Menu/SideMenu.vue
  7. 6 1
      src/components/UploadFile/index.vue
  8. 96 0
      src/config/router.config.js
  9. 93 0
      src/libs/JGPrint.js
  10. 187 0
      src/libs/LodopFuncs.js
  11. 3 1
      src/main.js
  12. 102 0
      src/views/common/area.js
  13. 23 2
      src/views/common/custList.vue
  14. 120 0
      src/views/common/dingDepartUser.js
  15. 7 1
      src/views/common/productBrand.js
  16. 9 1
      src/views/common/productCodeList.vue
  17. 5 2
      src/views/common/productType.js
  18. 92 0
      src/views/common/productTypeAll.js
  19. 26 5
      src/views/common/subarea.js
  20. 78 0
      src/views/common/supplier.js
  21. 12 1
      src/views/dealerManagement/merchantInfoManagement/edit.vue
  22. 12 2
      src/views/dowloadFile/list.vue
  23. 346 0
      src/views/expenseManagement/expenseReimbursement/add.vue
  24. 267 0
      src/views/expenseManagement/expenseReimbursement/baseDataModal.vue
  25. 152 0
      src/views/expenseManagement/expenseReimbursement/chooseDepartUserModal.vue
  26. 275 0
      src/views/expenseManagement/expenseReimbursement/chooseImportModal.vue
  27. 71 0
      src/views/expenseManagement/expenseReimbursement/department.js
  28. 206 0
      src/views/expenseManagement/expenseReimbursement/detail.vue
  29. 71 0
      src/views/expenseManagement/expenseReimbursement/employee.js
  30. 328 0
      src/views/expenseManagement/expenseReimbursement/epenseCdfModal.vue
  31. 359 0
      src/views/expenseManagement/expenseReimbursement/epenseDetailModal.vue
  32. 201 0
      src/views/expenseManagement/expenseReimbursement/importGuideModal.vue
  33. 311 0
      src/views/expenseManagement/expenseReimbursement/list.vue
  34. 185 0
      src/views/expenseManagement/expenseReimbursement/previewModal.vue
  35. 331 0
      src/views/expenseManagement/expenseReimbursement/productInfoModal.vue
  36. 98 0
      src/views/expenseManagement/expenseReimbursement/verifyModal.vue
  37. 398 0
      src/views/expenseManagement/expenseReimbursementDetail/list.vue
  38. 19 22
      src/views/productManagement/productInfo/edit.vue

+ 5 - 0
package-lock.json

@@ -17214,6 +17214,11 @@
         "vue": "^2.0"
       }
     },
+    "vuescroll": {
+      "version": "4.17.3",
+      "resolved": "https://registry.npmjs.org/vuescroll/-/vuescroll-4.17.3.tgz",
+      "integrity": "sha512-5P3hje/fYjo1eAB0Y8zboLbaq/mnPNczYyZ1aPHHM6wIFKcqal9Mi42CBCZizUnEtoXtbsGlnyLcVbnalTNptA=="
+    },
     "vuex": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz",

+ 1 - 0
package.json

@@ -39,6 +39,7 @@
     "vue-router": "^3.1.2",
     "vue-svg-component-runtime": "^1.0.1",
     "vue2.0-zoom": "^2.1.1",
+    "vuescroll": "^4.17.3",
     "vuex": "^3.1.1",
     "wangeditor": "^3.1.1"
   },

+ 1 - 1
public/version.json

@@ -1,4 +1,4 @@
 {
-    "version": "1.0.9",
+    "version": "1.1.2",
     "message": "发现有新版本发布,确定更新系统?"
 }

+ 12 - 0
src/api/dowloadFile.js

@@ -11,3 +11,15 @@ export const taskList = (params) => {
     method: 'post'
   })
 }
+
+// 下载文件
+export const dowloadFile = (url, label) => {
+  axios.get(url, { responseType: "blob" }).then(response => {
+     const blob = new Blob([response.data]);
+     const link = document.createElement("a");
+     link.href = URL.createObjectURL(blob);
+     link.download = label;
+     link.click();
+     URL.revokeObjectURL(link.href);
+   }).catch(console.error);
+}

+ 186 - 0
src/api/expenseManagement.js

@@ -0,0 +1,186 @@
+import { axios } from '@/utils/request'
+
+// 申请部门
+export const departmentQueryList = (params) => {
+  return axios({
+    url: `/department/queryList`,
+    data: params,
+    method: 'post'
+  })
+}
+//  申请人
+export const employeeQueryList = (params) => {
+  return axios({
+    url: `/employee/queryList`,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 费用报销单列表
+export const expenseAccountList = (params) => {
+  const url = `/expenseAccount/queryPage/${params.pageNo}/${params.pageSize}`
+  delete params.pageNo
+  delete params.pageSize
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+// 报销单详情
+export const expenseAccountDetail = (params) => {
+  return axios({
+    url: `/expenseAccount/queryBySn/${params.expenseAccountSn}`,
+    data: params,
+    method: 'get'
+  })
+}
+// 保存
+export const expenseAccountSave = (params) => {
+  return axios({
+    url: `/expenseAccount/save`,
+    data: params,
+    method: 'post'
+  })
+}
+// 删除
+export const expenseAccountDelete = (params) => {
+  return axios({
+    url: `/expenseAccount/deleteBySn/${params.expenseAccountSn}`,
+    data: params,
+    method: 'post'
+  })
+}
+// 提交
+export const expenseAccountSubmit = (params) => {
+  return axios({
+    url: `/expenseAccount/submit`,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 删除费用明细
+export const expenseAcctDetailDelete = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/deleteDetail/${params.detailSn}`,
+    method: 'get'
+  })
+}
+
+// 根据sn查询费用明细
+export const expenseAcctDetailFindBySn = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/findBySn/${params.detailSn}`,
+    method: 'get'
+  })
+}
+
+// 根据sn查询费用明细产品
+export const expenseAcctDetailFindProductsBySn = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/findDetailProductsBySn/${params.detailSn}`,
+    method: 'get'
+  })
+}
+
+// 根据sn查询费用明细列表
+export const expenseAcctDetailFindList = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/findList`,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 保存费用明细
+export const expenseAcctDetailSave = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/saveDetail`,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 导入费用报销明细
+export const expenseImportDetail = (params) => {
+  return axios({
+    url: '/expenseAcctDetail/importDetail',
+    data: params,
+    method: 'post',
+  })
+}
+// 确认导入明细
+export const expenseInsertImport = (params) => {
+  return axios({
+    url: '/expenseAcctDetail/insertImport',
+    data: params,
+    method: 'post',
+  })
+}
+// 报销明细错误导出
+export const expenseFailExcel = (params) => {
+  return axios({
+    url: '/expenseAcctDetail/exportImportDetailError',
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}
+
+// 保存费用明细产品
+export const expenseAcctDetailSaveProducts = (params) => {
+  return axios({
+    url: `/expenseAcctDetail/saveDetailProducts`,
+    data: params,
+    method: 'post'
+  })
+}
+
+// 费用报销明细单列表
+export const expenseAccountDetailList = (params) => {
+  const url = `/report/expenseAcctDetailReport/queryDetailReport/${params.pageNo}/${params.pageSize}`
+  delete params.pageNo
+  delete params.pageSize
+  return axios({
+    url: url,
+    data: params,
+    method: 'post'
+  })
+}
+
+//  费用报销明细单 导出
+export const expenseAcctDetailReport = (params) => {
+  return axios({
+    url: '/report/expenseAcctDetailReport/exportDetailReport',
+    data: params,
+    method: 'post',
+    responseType: 'blob'
+  })
+}
+
+// 费用报销单审批进度
+export const getProcessInstance = (params) => {
+  return axios({
+    url: `/dingTalk/getProcessInstance/${params.businessType}/${params.businessSn}`,
+    data: params,
+    method: 'post'
+  })
+}
+// 钉钉部门
+export const getDingTalkDepartmentList = (params) => {
+  return axios({
+    url: `/dingTalk/getDingTalkDepartmentList/${params.deptId}`,
+    data: params,
+    method: 'post'
+  })
+}
+// 钉钉人员
+export const queryDingTalkDeptUser = (params) => {
+  return axios({
+    url: `/dingTalk/queryDingTalkDeptUser/${params.deptId}/${params.pageNo}/${params.pageSize}`,
+    data: params,
+    method: 'post'
+  })
+}

+ 28 - 8
src/components/Menu/SideMenu.vue

@@ -5,14 +5,16 @@
     :collapsible="collapsible"
     v-model="collapsed"
     :trigger="null">
-    <logo />
-    <s-menu
-      :collapsed="collapsed"
-      :menu="menus"
-      :theme="theme"
-      :mode="mode"
-      @select="onSelect"
-      style="padding: 10px 0px;"></s-menu>
+    <vue-scroll :ops="ops">
+      <logo />
+      <s-menu
+        :collapsed="collapsed"
+        :menu="menus"
+        :theme="theme"
+        :mode="mode"
+        @select="onSelect"
+        style="padding: 10px 0px;"></s-menu>
+    </vue-scroll>
   </a-layout-sider>
 
 </template>
@@ -52,6 +54,24 @@ export default {
       required: true
     }
   },
+  data () {
+    return {
+      ops: {
+        vuescroll: {},
+        scrollPanel: {},
+        rail: {
+          keepShow: true
+        },
+        bar: {
+          hoverStyle: true,
+          onlyShowBarOnScroll: false, // 是否只有滚动的时候才显示滚动条
+          background: '#F5F5F5', // 滚动条颜色
+          opacity: 0.5, // 滚动条透明度
+          'overflow-x': 'hidden'
+        }
+      }
+    }
+  },
   methods: {
     onSelect (obj) {
       this.$emit('menuSelect', obj)

+ 6 - 1
src/components/UploadFile/index.vue

@@ -182,9 +182,14 @@ export default {
         } else if (this.uploadType == 'attach') { // 附件    将接口的返回数据返回页面
           a.map(item => {
             if (item.response && item.status == 'done') {
-              temp = [...temp, ...item.response.data]
+              if (item.response.data instanceof Array) {
+                temp = [...temp, ...item.response.data]
+              } else {
+                temp = [...temp, ...[item.response.data]]
+              }
             }
           })
+          console.log(temp)
           obj = JSON.stringify(temp)
         }
       }

+ 96 - 0
src/config/router.config.js

@@ -591,6 +591,102 @@ export const asyncRouterMap = [
           }
         ]
       },
+      // 费用报销
+      {
+        path: '/expenseManagement',
+        redirect: '/expenseManagement/expenseReimbursement',
+        component: PageView,
+        meta: {
+          title: '费用报销',
+          icon: 'account-book'
+          // permission: 'M_salesManagement'
+        },
+        children: [
+          {
+            path: '/expenseManagement/expenseReimbursement',
+            redirect: '/expenseManagement/expenseReimbursement/list',
+            name: 'expenseReimbursement',
+            component: BlankLayout,
+            meta: {
+              title: '费用报销单',
+              icon: 'monitor'
+              // permission: 'M_salesQueryList'
+            },
+            hideChildrenInMenu: true,
+            children: [
+              {
+                path: 'list',
+                name: 'expenseReimbursementList',
+                component: () => import(/* webpackChunkName: "expenseManagement" */ '@/views/expenseManagement/expenseReimbursement/list.vue'),
+                meta: {
+                  title: '费用报销单列表',
+                  icon: 'monitor',
+                  hidden: true
+                  // permission: 'M_salesQueryList'
+                }
+              },
+              {
+                path: 'detail/:sn',
+                name: 'expenseReimbursementListDetail',
+                component: () => import(/* webpackChunkName: "expenseManagement" */ '@/views/expenseManagement/expenseReimbursement/detail.vue'),
+                meta: {
+                  title: '费用报销单详情',
+                  icon: 'monitor',
+                  hidden: true
+                  // permission: 'M_salesDetail'
+                }
+              },
+              {
+                path: 'add',
+                name: 'expenseReimbursementAdd',
+                component: () => import(/* webpackChunkName: "expenseManagement" */ '@/views/expenseManagement/expenseReimbursement/add.vue'),
+                meta: {
+                  title: '新增费用报销单',
+                  icon: 'monitor',
+                  hidden: true
+                  // permission: 'M_salesNew'
+                }
+              },
+              {
+                path: 'edit/:sn',
+                name: 'expenseReimbursementEdit',
+                component: () => import(/* webpackChunkName: "expenseManagement" */ '@/views/expenseManagement/expenseReimbursement/add.vue'),
+                meta: {
+                  title: '编辑费用报销单',
+                  icon: 'monitor',
+                  hidden: true
+                  // permission: 'M_salesNew'
+                }
+              }
+            ]
+          },
+          {
+            path: '/expenseManagement/expenseReimbursementDetail',
+            redirect: '/expenseManagement/expenseReimbursementDetail/list',
+            name: 'expenseReimbursementDetail',
+            component: BlankLayout,
+            meta: {
+              title: '费用报销明细统计',
+              icon: 'audit'
+              // permission: 'M_examineVerifyList'
+            },
+            hideChildrenInMenu: true,
+            children: [
+              {
+                path: 'list',
+                name: 'expenseReimbursementDetailList',
+                component: () => import(/* webpackChunkName: "expenseManagement" */ '@/views/expenseManagement/expenseReimbursementDetail/list.vue'),
+                meta: {
+                  title: '费用报销明细统计列表',
+                  icon: 'audit',
+                  hidden: true
+                  // permission: 'M_examineVerifyList'
+                }
+              }
+            ]
+          }
+        ]
+      },
       // 库存管理
       {
         path: '/inventoryManagement',

+ 93 - 0
src/libs/JGPrint.js

@@ -0,0 +1,93 @@
+import confirm from 'ant-design-vue/es/modal/confirm'
+import notification from 'ant-design-vue/es/notification'
+import { getLodop } from '@/libs/LodopFuncs'
+import moment from 'moment'
+// 打印页签,支持批量打印
+export const JGPrintTag = function (html, width, height, data) {
+  const LODOP = getLodop(document.getElementById('LODOP_OB'), document.getElementById('LODOP_EM'))
+  if (!LODOP) {
+    confirm({
+      title: '提示?',
+      content: h => <div>打印控件未安装,请先下载并安装。安装完成后,刷新页面即可打印。</div>,
+      okText: '立即下载',
+      okType: 'danger',
+      cancelText: '暂不打印',
+      onOk () {
+        var agent = navigator.userAgent.toLowerCase();
+        if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
+            window.open('http://www.lodop.net/demolist/CLodop_Setup_for_Win32NT.zip')
+        }else if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
+            window.open('http://www.lodop.net/download/CLodop_Setup_for_Win64NT_4.155EN.zip')
+        }
+      }
+    })
+    return
+  }
+  LODOP.PRINT_INIT("")
+  LODOP.SET_SHOW_MODE('HIDE_PAPER_BOARD', 1) //  隐藏底图上有模拟走纸板的条纹线
+  LODOP.SET_PRINT_MODE('POS_BASEON_PAPER', true) // 可使输出以纸张边缘为基点
+  LODOP.ADD_PRINT_HTML(0, 0, '100%', '100%', html)
+  LODOP.ADD_PRINT_BARCODE('36%','65%',90,90,"QRCode",data.qrCodeContent)
+  LODOP.SET_PRINT_STYLEA(0,"QRCodeVersion",5)
+  // LODOP.SET_PRINT_STYLEA(0,"QRCodeErrorLevel",'H')
+  LODOP.SET_PRINT_COPIES(data.printQty)// 指定份数
+  LODOP.SET_PRINT_PAGESIZE(1, width, height)
+  LODOP.PRINT()
+}
+ 
+// 导出下载excel
+export const downloadExcel = function (data, fileName) {
+  if (!data) { return }
+  const a = moment().format('YYYYMMDDHHmmss')
+  const fname = fileName + a
+  const blob = new Blob([data], { type: 'application/vnd.ms-excel' })
+  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
+    navigator.msSaveBlob(blob, fname + '.xlsx')
+  } else {
+    const link = document.createElement('a')
+    link.style.display = 'none'
+    var href = URL.createObjectURL(blob)
+    link.href = href
+    link.setAttribute('download', fname + '.xlsx')
+    document.body.appendChild(link)
+    link.click()
+    document.body.removeChild(link)
+    window.URL.revokeObjectURL(href) // 释放掉blob对象
+  }
+}
+
+// 打印控件
+export const jGPrint = function (data, type) {
+    if (!data) {
+      return
+    }
+    const LODOP = getLodop(document.getElementById('LODOP_OB'), document.getElementById('LODOP_EM'))
+    if (!LODOP) {
+      confirm({
+        title: '提示?',
+        content: h => <div>打印控件未安装,请先下载并安装。安装完成后,刷新页面即可打印。</div>,
+        okText: '立即下载',
+        okType: 'danger',
+        cancelText: '暂不打印',
+        onOk () {
+          var agent = navigator.userAgent.toLowerCase();
+          if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
+              window.open('http://www.lodop.net/demolist/CLodop_Setup_for_Win32NT.zip')
+          }else if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
+              window.open('http://www.lodop.net/download/CLodop_Setup_for_Win64NT_4.155EN.zip')
+          }
+        }
+      })
+      return
+    }
+    LODOP.SET_SHOW_MODE('HIDE_PAPER_BOARD', 1) //  隐藏底图上有模拟走纸板的条纹线
+    // LODOP.SET_PRINT_MODE('POS_BASEON_PAPER', true) // 可使输出以纸张边缘为基点
+    // LODOP.SET_PRINT_STYLEA(0,"TableRowThickNess",50)
+    LODOP.ADD_PRINT_HTM(10, 2, '98%', '100%', data)
+    LODOP.SET_PRINT_PAGESIZE(1,2100,0,"");
+    if (type == 'preview') { //  预览
+      LODOP.PREVIEW()
+    } else if (type == 'print') { //  打印
+      LODOP.PRINT()
+    }
+}

+ 187 - 0
src/libs/LodopFuncs.js

@@ -0,0 +1,187 @@
+//= =本JS是加载Lodop插件及CLodop服务的综合示例,可直接使用,建议看懂后融进自己页面程序==
+
+var CreatedOKLodopObject, CLodopIsLocal, CLodopJsState
+
+//= =判断是否需要CLodop(那些不支持插件的浏览器):==
+function needCLodop () {
+  try {
+    var ua = navigator.userAgent
+    if (ua.match(/Windows\sPhone/i)) { return true }
+    if (ua.match(/iPhone|iPod|iPad/i)) { return true }
+    if (ua.match(/Android/i)) { return true }
+    if (ua.match(/Edge\D?\d+/i)) { return true }
+
+    var verTrident = ua.match(/Trident\D?\d+/i)
+    var verIE = ua.match(/MSIE\D?\d+/i)
+    var verOPR = ua.match(/OPR\D?\d+/i)
+    var verFF = ua.match(/Firefox\D?\d+/i)
+    var x64 = ua.match(/x64/i)
+    if ((!verTrident) && (!verIE) && (x64)) { return true } else if (verFF) {
+      verFF = verFF[0].match(/\d+/)
+      if ((verFF[0] >= 41) || (x64)) { return true }
+    } else if (verOPR) {
+      verOPR = verOPR[0].match(/\d+/)
+      if (verOPR[0] >= 32) { return true }
+    } else if ((!verTrident) && (!verIE)) {
+      var verChrome = ua.match(/Chrome\D?\d+/i)
+      if (verChrome) {
+        verChrome = verChrome[0].match(/\d+/)
+        if (verChrome[0] >= 41) { return true }
+      }
+    }
+    return false
+  } catch (err) {
+    return true
+  }
+}
+
+// 加载CLodop时用双端口(http是8000/18000,而https是8443/8444)以防其中某端口被占,
+// 主JS文件“CLodopfuncs.js”是固定文件名,其内容是动态的,与当前打印环境有关:
+function loadCLodop () {
+  if (CLodopJsState == 'loading' || CLodopJsState == 'complete') return
+  CLodopJsState = 'loading'
+  var head = document.head || document.getElementsByTagName('head')[0] || document.documentElement
+  var JS1 = document.createElement('script')
+  var JS2 = document.createElement('script')
+
+  if (window.location.protocol == 'https:') {
+    JS1.src = 'https://localhost.lodop.net:8443/CLodopfuncs.js'
+    JS2.src = 'https://localhost.lodop.net:8444/CLodopfuncs.js'
+  } else {
+    JS1.src = 'http://localhost:8000/CLodopfuncs.js'
+    JS2.src = 'http://localhost:18000/CLodopfuncs.js'
+  }
+  JS1.onload = JS2.onload = function () { CLodopJsState = 'complete' }
+  JS1.onerror = JS2.onerror = function (evt) { CLodopJsState = 'complete' }
+  head.insertBefore(JS1, head.firstChild)
+  head.insertBefore(JS2, head.firstChild)
+  CLodopIsLocal = !!((JS1.src + JS2.src).match(/\/\/localho|\/\/127.0.0./i))
+}
+
+if (needCLodop()) { loadCLodop() }// 开始加载
+
+//= =获取LODOP对象主过程,判断是否安装、需否升级:==
+function getLodop (oOBJECT, oEMBED) {
+  var strHtmInstall = "<br><font color='#FF00FF'>打印控件未安装!点击这里<a href='http://www.lodop.net/demolist/install_lodop32.zip' target='_self'>执行安装</a>,安装后请刷新页面或重新进入。</font>"
+  var strHtmUpdate = "<br><font color='#FF00FF'>打印控件需要升级!点击这里<a href='http://www.lodop.net/demolist/install_lodop32.zip' target='_self'>执行升级</a>,升级后请重新进入。</font>"
+  var strHtm64_Install = "<br><font color='#FF00FF'>打印控件未安装!点击这里<a href='http://www.lodop.net/demolist/install_lodop64.zip' target='_self'>执行安装</a>,安装后请刷新页面或重新进入。</font>"
+  var strHtm64_Update = "<br><font color='#FF00FF'>打印控件需要升级!点击这里<a href='http://www.lodop.net/demolist/install_lodop64.zip' target='_self'>执行升级</a>,升级后请重新进入。</font>"
+  var strHtmFireFox = "<br><br><font color='#FF00FF'>(注意:如曾安装过Lodop旧版附件npActiveXPLugin,请在【工具】->【附加组件】->【扩展】中先卸它)</font>"
+  var strHtmChrome = "<br><br><font color='#FF00FF'>(如果此前正常,仅因浏览器升级或重安装而出问题,需重新执行以上安装)</font>"
+  var strCLodopInstall_1 = "<br><font color='#FF00FF'>Web打印服务CLodop未安装启动,点击这里<a href='http://www.lodop.net/demolist/CLodop_Setup_for_Win32NT.zip' target='_self'>下载执行安装</a>"
+  var strCLodopInstall_2 = "<br>(若此前已安装过,可<a href='CLodop.protocol:setup' target='_self'>点这里直接再次启动</a>)"
+  var strCLodopInstall_3 = ',成功后请刷新本页面。</font>'
+  var strCLodopUpdate = "<br><font color='#FF00FF'>Web打印服务CLodop需升级!点击这里<a href='http://www.lodop.net/demolist/CLodop_Setup_for_Win32NT.zip' target='_self'>执行升级</a>,升级后请刷新页面。</font>"
+  var msgHtml = ''
+  var LODOP
+  try {
+    var ua = navigator.userAgent
+    var isIE = !!(ua.match(/MSIE/i)) || !!(ua.match(/Trident/i))
+    if (needCLodop()) {
+      try {
+        LODOP = getCLodop()
+      } catch (err) {}
+      if (!LODOP && CLodopJsState !== 'complete') {
+        if (CLodopJsState == 'loading') alert('网页还没下载完毕,请稍等一下再操作.'); else alert('没有加载CLodop的主js,请先调用loadCLodop过程.')
+        return
+      }
+      if (!LODOP) {
+        msgHtml = strCLodopInstall_1 + (CLodopIsLocal ? strCLodopInstall_2 : '') + strCLodopInstall_3
+        return
+      } else {
+        if (CLODOP.CVERSION < '4.0.9.9') {
+          msgHtml = strCLodopUpdate
+        }
+        if (oEMBED && oEMBED.parentNode) { oEMBED.parentNode.removeChild(oEMBED) } // 清理旧版无效元素
+        if (oOBJECT && oOBJECT.parentNode) { oOBJECT.parentNode.removeChild(oOBJECT) }
+      }
+    } else {
+      var is64IE = isIE && !!(ua.match(/x64/i))
+      //= =如果页面有Lodop就直接使用,否则新建:==
+      if (oOBJECT || oEMBED) {
+        if (isIE) { LODOP = oOBJECT } else { LODOP = oEMBED }
+      } else if (!CreatedOKLodopObject) {
+        LODOP = document.createElement('object')
+        LODOP.setAttribute('width', 0)
+        LODOP.setAttribute('height', 0)
+        LODOP.setAttribute('style', 'position:absolute;left:0px;top:-100px;width:0px;height:0px;')
+        if (isIE) { LODOP.setAttribute('classid', 'clsid:2105C259-1E0C-4534-8141-A753534CB4CA') } else { LODOP.setAttribute('type', 'application/x-print-lodop') }
+        document.documentElement.appendChild(LODOP)
+        CreatedOKLodopObject = LODOP
+      } else { LODOP = CreatedOKLodopObject }
+      //= =Lodop插件未安装时提示下载地址:==
+      if ((!LODOP) || (!LODOP.VERSION)) {
+        if (ua.indexOf('Chrome') >= 0) { msgHtml = strHtmChrome }
+        if (ua.indexOf('Firefox') >= 0) { msgHtml = strHtmFireFox }
+        msgHtml = (is64IE ? strHtm64_Install : strHtmInstall)
+        console.log(msgHtml)
+        return LODOP
+      }
+    }
+    if (LODOP.VERSION < '6.2.2.6') {
+      if (!needCLodop()) { msgHtml = (is64IE ? strHtm64_Update : strHtmUpdate) }
+    }
+    //= ==如下空白位置适合调用统一功能(如注册语句、语言选择等):==
+    LODOP.SET_LICENSES('', 'E89B8C436467D381F32C0B7F5674838FA59', '', '')
+    //= ==============================================.com//.com台级//.net
+    // alert('SET_LICENSES执行了')
+    console.log(msgHtml)
+    return LODOP
+  } catch (err) {
+    alert('getLodop出错:' + err)
+  }
+}
+
+/**
+ * 根据已有的html url进行打印
+ * @param type
+ * 是打印还是打印预览:preview-打印预览;其它值为打印。
+ * @param config
+ * 打印内容配置:
+ * 1、为字符串时,表示打印的html所在的远程url地址。
+ * 2、为对象时:{
+ * url:打印的html所在的远程url地址,
+ * width:html内容的实际宽度(单位像素)
+ * height:html内容的实际高度(单位像素)
+ * }
+ *
+ * 说明:
+ * JasperReports设计模板的像素和实际纸张的尺寸之比为72pt=1英寸(in)。
+ * Lodop打印的内容是按照一个dpi占96px=72pt=1in=2.54cm(厘米)=25.4mm(毫米)打印,JasperReports的html结果内容是按照一个dpi占72pt生成。
+ *
+ */
+function doPrintByHtml (type, config) {
+  if (!window.LODOP) {
+    alert('打印插件不可用,请先到首页进行下载安装,然后刷新浏览器!')
+    return
+  }
+  if (window.top && window.top.waltz && window.top.waltz.app && window.top.waltz.app.globalOption) {
+    var globalOption = window.top.waltz.app.globalOption
+    if (globalOption.lodopLicenseAuthor) {
+      LODOP.SET_LICENSES(globalOption.lodopLicenseAuthor, globalOption.lodopLicenseKey, '', '')
+    }
+  }
+  LODOP.PRINT_INIT('printTask')
+
+  var url
+  if ((typeof config === 'string') && config.constructor == String) {
+    url = config
+  } else {
+    url = config.url
+    var width = Math.round((config.width / 72) * 25.4) * 10 // 1 inch=25.4mm,Lodop默认单位为0.1mm
+    var height = Math.round((config.height / 72) * 25.4) * 10
+    // 设置纸张大小,设置后不允许用户手动修改纸张大小
+    LODOP.SET_PRINT_PAGESIZE(0, width, height, '')
+  }
+  // 指定按实际宽度缩放比例
+  LODOP.SET_PRINT_MODE('PRINT_PAGE_PERCENT', 'Full-Width')
+  LODOP.ADD_PRINT_HTM(0, 0, '100%', '100%', 'URL:' + url)
+  LODOP.SET_PRINT_STYLE('Stretch', 2)
+  if (type == 'preview') {
+    LODOP.PREVIEW()
+  } else {
+    LODOP.PRINT()
+  }
+}
+
+export { getLodop }

+ 3 - 1
src/main.js

@@ -48,7 +48,9 @@ Vue.prototype.$hasPermissions = function (value) {
   }
   return isExist
 }
-
+// vuescroll
+import vuescroll from 'vuescroll'
+Vue.use(vuescroll)
 // 禁止输入框输入空格,换行符,<,>
 Vue.prototype.$filterEmpty = function (e) {
   return e.target.value.replace(/\s|\r\n|\n|<|>/g, '')

+ 102 - 0
src/views/common/area.js

@@ -0,0 +1,102 @@
+import { getArea } from '@/api/data'
+const Area = {
+  template: `
+        <a-select
+          :id="id"
+          :placeholder="placeholder"
+          allowClear
+          :value="defaultVal"
+          :showSearch="true"
+          @change="handleChange"
+          :filter-option="filterOption">
+          <a-select-option v-for="item in list" :key="item[defValKey]" :value="item[defValKey]">{{ item.name }}</a-select-option>
+        </a-select>
+    `,
+  props: {
+    value: {
+      type: String,
+      defatut: ''
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    defValKey: {
+      type: String,
+      default: 'areaSn'
+    },
+    placeholder: {
+      type: String,
+      default: '请选择'
+    },
+    leve: {
+      type: String,
+      default: 'province'
+    },
+    parentId: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      defaultVal: this.value,
+      list: []
+    };
+  },
+  watch: {
+    value(newValue, oldValue) {
+      console.log(newValue)
+      this.defaultVal = newValue
+    },
+    parentId(newValue,oldValue){
+      if(this.leve != 'province'&&newValue){
+        this.getList(this.leve)
+      }
+    }
+  },
+  mounted() {
+    if(this.leve == 'province'){
+      this.getList(this.leve)
+    }
+  },
+  methods: {
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    handleChange(value) {
+      console.log(value)
+      this.defaultVal = value;
+      const item = this.list.find(item => item[this.defValKey] == value)
+      this.$emit('change', this.value, item);
+      this.$emit('input', value);
+    },
+    //  省/市/区
+    getList (leve) {
+      let params
+      if (leve == 'province') {
+        params = { type: '2' }
+      } else {
+        params = { parentId: this.parentId, type: leve == 'city' ? '3' : '4' }
+      }
+      console.log(params)
+      getArea(params).then(res => {
+        if (res.status == 200) {
+          if (leve == 'province') {
+            this.list = res.data || []
+          } else if (leve == 'city') {
+            this.list = res.data || []
+          } else if (leve == 'district') {
+            this.list = res.data || []
+          }
+        } else {
+          this.list = []
+        }
+      })
+    },
+  },
+};
+
+export default Area

+ 23 - 2
src/views/common/custList.vue

@@ -4,7 +4,7 @@
     label-in-value
     :size="size"
     :value="dealerName"
-    placeholder="请输入名称搜索"
+    :placeholder="placeholder"
     style="width: 100%"
     :filter-option="false"
     :not-found-content="fetching ? undefined : null"
@@ -24,6 +24,10 @@ import debounce from 'lodash/debounce'
 import { dealerQueryList, dealerFindUpdateInfoBySn } from '@/api/dealer'
 export default {
   props: {
+    id: {
+      type: String,
+      default: ''
+    },
     size: {
       type: String,
       default: 'default'
@@ -31,6 +35,14 @@ export default {
     itemSn: {
       type: String || Number,
       default: undefined
+    },
+    placeholder: {
+      type: String,
+      default: '请输入名称搜索'
+    },
+    cooperateFlag: {
+      type: String,
+      default: undefined
     }
   },
   data () {
@@ -55,6 +67,9 @@ export default {
         params.dealerSn = this.itemSn
       } else {
         params.nameLike = dealerName
+        if (this.cooperateFlag) {
+          params.cooperateFlag = this.cooperateFlag
+        }
       }
       dealerQueryList(params).then(res => {
         if (fetchId !== this.lastFetchId) {
@@ -65,12 +80,14 @@ export default {
       })
     },
     handleChange (value) {
+      const row = this.data.filter(item => item.dealerSn == value.key)
       Object.assign(this, {
         dealerName: value,
         data: [],
         fetching: false
       })
-      this.$emit('change', value || { key: undefined })
+      this.$emit('change', value || { key: undefined }, row[0], this.id)
+      // this.$emit('input', value.key)
     },
     resetForm () {
       this.dealerName = []
@@ -87,6 +104,10 @@ export default {
         data: [],
         fetching: false
       })
+    },
+    setDufaultVal (itemSn) {
+      this.fetchUser(itemSn)
+      this.setData({ key: itemSn })
     }
   },
   mounted () {

+ 120 - 0
src/views/common/dingDepartUser.js

@@ -0,0 +1,120 @@
+import { getDingTalkDepartmentList, queryDingTalkDeptUser } from '@/api/expenseManagement'
+const DingDepartUser = {
+  template: `
+      <a-tree-select
+          :value="defaultVal"
+          tree-data-simple-mode
+          style="width: 100%"
+          :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
+          :tree-data="treeData"
+          :placeholder="placeholder"
+          :load-data="onLoadData"
+          multiple
+          treeCheckable
+          :treeCheckStrictly="true"
+          treeNodeFilterProp="title"
+          @change="handleChange"
+        />
+    `,
+  props: {
+    value: {
+      type: Array,
+      defatut: function(){
+        return []
+      }
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: '请选择人员'
+    }
+  },
+  data() {
+    return {
+      defaultVal: this.value,
+      treeData: []
+    };
+  },
+  mounted() {
+    this.genTreeNode(1)
+  },
+  watch: {
+    value(newValue, oldValue) {
+      this.defaultVal = newValue
+    },
+  },
+  methods: {
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    handleChange(value) {
+      console.log(value);
+      this.defaultVal = value;
+      this.$emit('change', value, this.id);
+      this.$emit('input', value);
+    },
+    // 格式化数据
+    formatData(data,type){
+      const ret = []
+      data.map(item => {
+        if(type == 'bm'){
+          ret.push({
+            title: item.name,
+            id:item.deptId,
+            value: item.deptId,
+            pId: item.parentId,
+            autoAddUser: item.autoAddUser,
+            createDeptGroup: item.createDeptGroup,
+            disableCheckbox: true,
+            checkable: false
+          })
+        }else{
+          ret.push({
+            title: item.name,
+            id:item.userid,
+            value: item.userid,
+            pId: item.parentId,
+            avatar: item.avatar,
+            leader: item.leader,
+            boss: item.boss,
+            admin: item.admin,
+            active: item.active,
+            userid: item.userid,
+            isLeaf: true
+          })
+        }
+      })
+      return ret
+    },
+    // 获取部门
+    genTreeNode(parentId, isLeaf = false) {
+       Promise.all([
+         getDingTalkDepartmentList({deptId:parentId}),
+         queryDingTalkDeptUser({deptId:parentId,pageNo:1,pageSize:10})
+         ]).then(res => {
+          const res1 = res[0].data||[]
+          const res2 = res[1].data||[]
+          this.treeData = this.treeData.concat(this.formatData(res1,'bm')).concat(this.formatData(res2,'yg'));
+          if(res1.length==0&&res2.length==0){
+            this.$message.info("没有下一级了")
+          }
+       })
+    },
+    onLoadData(treeNode) {
+      return new Promise(resolve => {
+        const { value } = treeNode.dataRef;
+         this.genTreeNode(value)
+         setTimeout(()=>{
+           resolve();
+         },1000)
+      });
+    },
+  },
+};
+
+export default DingDepartUser

+ 7 - 1
src/views/common/productBrand.js

@@ -7,6 +7,7 @@ const ProductBrand = {
         allowClear
         :value="defaultVal"
         :showSearch="true"
+        :disabled="disabled"
         @change="handleChange"
         option-filter-prop="children"
         :filter-option="filterOption">
@@ -29,6 +30,10 @@ const ProductBrand = {
     brandType: {
       type: String,
       default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -57,7 +62,8 @@ const ProductBrand = {
     },
     handleChange(value) {
       this.defaultVal = value;
-      this.$emit('change', value);
+      const row = this.productBrandList.find(item => item.brandSn == value)
+      this.$emit('change', value, this.id, row);
       this.$emit('input', value);
     },
     //  产品品牌列表, sysFlag: 1 箭冠 0 自建

+ 9 - 1
src/views/common/productCodeList.vue

@@ -26,6 +26,10 @@ export default {
     mode: {
       type: String,
       default: 'default'
+    },
+    id: {
+      type: String,
+      default: ''
     }
   },
   data () {
@@ -79,11 +83,15 @@ export default {
         data: [],
         fetching: false
       })
-      this.$emit('change', value || { key: undefined })
+      this.$emit('change', value || { key: undefined }, this.id)
     },
     resetForm () {
       this.dealerName = undefined
     },
+    setDufaultVal (itemSn) {
+      this.fetchUser(itemSn)
+      this.setData({ key: itemSn })
+    },
     setData (value) {
       Object.assign(this, {
         dealerName: value,

+ 5 - 2
src/views/common/productType.js

@@ -39,9 +39,12 @@ const ProductType = {
     this.getProductType()
   },
   methods: {
-    handleChange (value) {
+    setDefValue(defaultVal){
+      this.handleChange(defaultVal)
+    },
+    handleChange (value,row) {
       this.defaultVal = value
-      this.$emit('change', this.defaultVal)
+      this.$emit('change', this.defaultVal, this.id,row)
       this.$emit('input', this.defaultVal)
     },
     //  产品分类列表

+ 92 - 0
src/views/common/productTypeAll.js

@@ -0,0 +1,92 @@
+import { productTypeQueryAll } from '@/api/productType'
+const ProductType = {
+  template: `
+      <a-cascader
+        @change="handleChange"
+        change-on-select
+        :value="defaultVal"
+        expand-trigger="hover"
+        :options="productTypeList"
+        :disabled="disabled"
+        :fieldNames="{ label: 'productTypeName', value: 'productTypeSn', children: 'children' }"
+        :id="id"
+        placeholder="请选择产品分类"
+        allowClear />
+    `,
+  props: {
+    value: {
+      type: Array,
+      defatut: function () {
+        return []
+      }
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      defaultVal: this.value,
+      productTypeList: []
+    }
+  },
+  watch: {
+    value (newValue, oldValue) {
+      this.defaultVal = newValue
+    }
+  },
+  mounted () {
+    this.getProductType()
+  },
+  methods: {
+    setDefValue(defaultVal){
+      this.handleChange(defaultVal)
+    },
+    handleChange (value,row) {
+      this.defaultVal = value
+      this.$emit('change', this.defaultVal, this.id,row)
+      this.$emit('input', this.defaultVal)
+    },
+    //  产品分类  列表
+    getProductType () {
+      productTypeQueryAll({}).then(res => {
+        if (res.status == 200) {
+          if (res.data && res.data.length > 0) {
+            //  递归处理disabled禁用
+            this.recursionFun(res.data, false)
+            this.productTypeList = res.data
+          } else {
+            this.productTypeList = []
+          }
+        } else {
+          this.productTypeList = []
+        }
+      })
+    },
+    //  递归函数
+    recursionFun (data, state) {
+      if (data) {
+        data.map((item, index) => {
+          // enabledFlag为0表示禁用,父级禁用则子级也为禁用
+          if ((item.enabledFlag && item.enabledFlag == 0) || state) {
+            item.disabled = true
+          } else {
+            item.disabled = false
+          }
+          if (item.children && item.children.length == 0) {
+            delete item.children
+          } else {
+            this.recursionFun(item.children, item.disabled)
+          }
+        })
+      }
+    },
+  }
+}
+
+export default ProductType

+ 26 - 5
src/views/common/subarea.js

@@ -3,9 +3,10 @@ const Subarea = {
   template: `
         <a-select
           :id="id"
-          placeholder="请选择分区"
+          :placeholder="placeholder"
           allowClear
           :value="defaultVal"
+          :mode="mode"
           :showSearch="true"
           @change="handleChange"
           :filter-option="filterOption">
@@ -14,9 +15,13 @@ const Subarea = {
     `,
   props: {
     value: {
-      type: String,
+      type: [String,Array],
       defatut: ''
     },
+    mode: {
+      type: String,
+      default:'default'
+    },
     id: {
       type: String,
       default: ''
@@ -24,6 +29,10 @@ const Subarea = {
     defValKey: {
       type: String,
       default: 'subareaSn'
+    },
+    placeholder: {
+      type: String,
+      default: '请选择分区'
     }
   },
   data() {
@@ -34,7 +43,6 @@ const Subarea = {
   },
   watch: {
     value(newValue, oldValue) {
-      console.log(newValue)
       this.defaultVal = newValue
     }
   },
@@ -50,7 +58,14 @@ const Subarea = {
     handleChange(value) {
       console.log(value)
       this.defaultVal = value;
-      this.$emit('change', this.value);
+      const item = this.list.filter(item =>{
+        if(this.mode !== 'multiple'){
+          return item[this.defValKey] == value
+        }else{
+          return value.includes(item[this.defValKey])
+        }
+      })
+      this.$emit('change', value, item);
       this.$emit('input', value);
     },
     // 列表数据
@@ -58,12 +73,18 @@ const Subarea = {
       const _this = this
       subareaQueryAll().then(res => {
         if (res.status == 200) {
-          _this.list = res.data || []
+          _this.list = res.data.reverse() || []
         } else {
           _this.list = []
         }
       })
     },
+    // 根据名称数组 返回对应的 sn 数组
+    getValByName(data){
+      let ret = []
+      this.list.filter(item => data.includes(item.subareaName)).map(item => ret.push(item.subareaSn))
+      return ret
+    }
   },
 };
 

+ 78 - 0
src/views/common/supplier.js

@@ -0,0 +1,78 @@
+import { supplierAllList } from '@/api/supplier.js'
+const Employee = {
+  template: `
+      <a-select
+        :placeholder="placeholder"
+        :id="id"
+        allowClear
+        :value="defaultVal"
+        :showSearch="true"
+        @change="handleChange"
+        option-filter-prop="children"
+        :filter-option="filterOption">
+        <a-select-option v-for="item in list" :key="item.supplierSn" :value="item.supplierSn">
+        {{ item.supplierName }}
+        </a-select-option>
+      </a-select>
+    `,
+  props: {
+    value: {
+      type: String,
+      defatut: ''
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: '请选择供应商'
+    },
+    enableFlag: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      defaultVal: this.value,
+      list: []
+    };
+  },
+  mounted() {
+    this.getList()
+  },
+  watch: {
+    value(newValue, oldValue) {
+      this.defaultVal = newValue
+    },
+  },
+  methods: {
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    handleChange(value) {
+      this.defaultVal = value;
+      const item = this.list.find(item => item.supplierSn == value)
+      this.$emit('change', value, item);
+      this.$emit('input', value);
+    },
+    getList () {
+      supplierAllList().then(res => {
+        if (res.status == 200) {
+          if(this.enableFlag!=''){
+            this.list = res.data.filter(item => item.enableFlag == this.enableFlag)
+          }else{
+            this.list = res.data
+          }
+        } else {
+          this.list = []
+        }
+      })
+    },
+  },
+};
+
+export default Employee

+ 12 - 1
src/views/dealerManagement/merchantInfoManagement/edit.vue

@@ -107,6 +107,16 @@
                       placeholder="请选择授权类型"></v-select>
                   </a-form-model-item>
                 </a-col>
+                <a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+                  <a-form-model-item label="财务核算维度编码" prop="kdMidCustomerFnumber">
+                    <a-input
+                      v-model.trim="form.kdMidCustomerFnumber"
+                      id="merchantInfoManagementEdit-kdMidCustomerFnumber"
+                      :maxLength="30"
+                      allowClear
+                      placeholder="请输入财务核算维度编码(最多30个字符)" />
+                  </a-form-model-item>
+                </a-col>
               </a-row>
             </a-collapse-panel>
           </a-collapse>
@@ -439,7 +449,8 @@ export default {
         jgAutoPartsCode: '',
         jgDoorHeaderChangeTime: '',
         isShowSpecialPrice: 0,
-        remark: ''
+        remark: '',
+        kdMidCustomerFnumber: ''
       },
       rules: {
         dealerName: [

+ 12 - 2
src/views/dowloadFile/list.vue

@@ -45,7 +45,7 @@
         bordered>
         <!-- 操作 -->
         <template slot="action" slot-scope="text, record">
-          <a :href="record.fileUrl" :download="record.fileUrl" target="_blank" style="padding: 5px 0 0 23px;display: block;" v-if="record.taskState=='SUCCESS'">下载文件</a>
+          <a href="javascript:void(0)" @click="dowloadExcel(record.fileUrl)" style="padding: 5px 0 0 23px;display: block;" v-if="record.taskState=='SUCCESS'">下载文件</a>
           <span v-else>--</span>
         </template>
       </s-table>
@@ -55,7 +55,7 @@
 
 <script>
 import { STable, VSelect } from '@/components'
-import { taskList } from '@/api/dowloadFile'
+import { taskList, dowloadFile } from '@/api/dowloadFile'
 import rangeDate from '@/views/common/rangeDate.vue'
 export default {
   components: { STable, VSelect, rangeDate },
@@ -103,6 +103,16 @@ export default {
     }
   },
   methods: {
+    dowloadExcel (url) {
+      // dowloadFile(url, '')
+      const elt = document.createElement('a')
+      elt.setAttribute('href', url)
+      elt.setAttribute('download', '')
+      elt.style.display = 'none'
+      document.body.appendChild(elt)
+      elt.click()
+      document.body.removeChild(elt)
+    },
     //  时间  change
     dateChange (date) {
       this.queryParam.beginDate = date[0] ? date[0] + ' 00:00:00' : ''

+ 346 - 0
src/views/expenseManagement/expenseReimbursement/add.vue

@@ -0,0 +1,346 @@
+<template>
+  <div class="expenseReimbursementAddwrap">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <a-page-header :ghost="false" :backIcon="false" class="expenseReimbursementAddcont" >
+        <!-- 自定义的二级文字标题 -->
+        <template slot="subTitle">
+          <a id="purchaseOrderEdit-back-btn" href="javascript:;" @click="handleBack(0)"><a-icon type="left" /> 返回列表</a>
+        </template>
+      </a-page-header>
+      <a-collapse :activeKey="['1']" class="expenseReimbursementAddcont" v-if="detailData">
+        <a-collapse-panel key="1" header="基础信息">
+          <div slot="extra" style="padding: 0 20px; color: #00aaff;" @click.stop="handleEditBase" >
+            <a-icon type="edit" /> 编辑基础信息
+          </div>
+          <a-descriptions :column="{ xs: 2, sm: 3, md: 3}">
+            <a-descriptions-item label="费用单号">{{ detailData&&detailData.expenseAccountNo || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="申请人">{{ detailData&&detailData.applyPersonName || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="申请部门">{{ detailData&&detailData.applyDepartmentName || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="费用类型">{{ detailData&&detailData.expenseTypeDictValue || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="费用发生月份">{{ detailData&&detailData.expenseDate?detailData.expenseDate.substring(0, 7) : '--' }}</a-descriptions-item>
+            <a-descriptions-item label="合计金额">{{ (detailData.applyExpenseTotal || detailData.applyExpenseTotal == 0) ? detailData.applyExpenseTotal+' 元' : '--' }}</a-descriptions-item>
+            <a-descriptions-item label="状态">{{ detailData&&detailData.stateDictValue || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="创建时间">{{ detailData&&detailData.createDate || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="审核时间">{{ detailData&&detailData.finishDate || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="主题" :span="3">{{ detailData&&detailData.title || '--' }}</a-descriptions-item>
+            <a-descriptions-item label="详细说明" :span="3">{{ detailData&&detailData.content.join(',')||'' }}</a-descriptions-item>
+            <a-descriptions-item label="附件" :span="3">
+              <a target="_blank" style="color: #00aaff;text-decoration: underline;margin-right: 15px;" :href="item.filePath" v-for="item in detailData.expenseAccountFilesList" :key="item.id">
+                {{ item.fileName }}
+              </a>
+            </a-descriptions-item>
+          </a-descriptions>
+        </a-collapse-panel>
+      </a-collapse>
+      <!-- 费用明细 -->
+      <a-card :bordered="false" size="small" class="expenseReimbursementAddcont" v-if="$route.params.sn">
+        <div slot="title">
+          <div style="display: flex;justify-content: space-between;align-items: center;">
+            <div>费用明细</div>
+            <div>
+              <a-button size="small" class="button-info" type="primary" @click="openGuideModal=true">导入费用明细</a-button>
+              <a-button size="small" class="button-error" type="primary" @click="newExpenseDetail">新增费用明细</a-button>
+            </div>
+          </div>
+        </div>
+        <a-alert type="info" style="margin-bottom:10px">
+          <div slot="message">
+            共{{ total }}条费用明细,合计金额¥{{ detailData&&detailData.applyExpenseTotal||0 }}元
+          </div>
+        </a-alert>
+        <!-- 列表 -->
+        <s-table
+          class="sTable fixPagination"
+          ref="table"
+          size="small"
+          :rowKey="(record) => record.id"
+          :columns="columns"
+          :data="loadData"
+          :defaultLoadData="false"
+          :showPagination="false"
+          bordered>
+          <template slot="action" slot-scope="text,record">
+            <a-button
+              size="small"
+              type="link"
+              class="button-info"
+              @click="setProduct(record)"
+            >设置产品信息</a-button>
+            <a-button
+              size="small"
+              type="link"
+              class="button-info"
+              @click="handleEdit(record)"
+            >编辑</a-button>
+            <a-button
+              size="small"
+              type="link"
+              class="button-error"
+              @click="handleDel(record)"
+            >删除</a-button>
+          </template>
+          <!-- 费用承担方 -->
+          <template slot="fycdf" slot-scope="text,record">
+            <div v-for="(item,index) in record.detailSplitList" :key="item.id+'-'+index">
+              {{ item.splitObjName }}
+              <span v-if="item.childDetailSplit&&item.childDetailSplit.splitObjName">/</span>
+              {{ item.childDetailSplit?item.childDetailSplit.splitObjName:'' }}: ¥{{ item.childDetailSplit?item.childDetailSplit.splitAmount:item.splitAmount }}
+            </div>
+          </template>
+          <!-- 产品信息 -->
+          <template slot="cpxx" slot-scope="text,record">
+            <div v-for="item in record.detailProductsList" :key="item.id">
+              <div v-if="item.productCode">{{ item.productCode }}: ¥{{ item.expense }}</div>
+              <div v-else-if="item.productBrandName">{{ item.productBrandName }}<span v-if="item.productTypeShowName">/</span>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+              <div v-else>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+            </div>
+            <span v-if="!record.detailProductsList||record.detailProductsList.length==0">--</span>
+          </template>
+        </s-table>
+      </a-card>
+    </a-spin>
+    <div class="affix-cont">
+      <a-button
+        type="primary"
+        class="button-primary"
+        :disabled="spinning"
+        id="expenseReimbursementAddsubmit-btn"
+        size="large"
+        @click="beforeSubmit"
+        style="padding: 0 60px;">提交</a-button>
+    </div>
+    <!-- 费用明细 -->
+    <epenseDetailModal
+      :openModal="openEpenseDetailModal"
+      :expenseAccountSn="$route.params.sn"
+      :expenseAccountNo="detailData?detailData.expenseAccountNo:''"
+      :expenseAccountDetailSn="expenseAccountDetailSn"
+      @ok="epenseDetailOk"
+      @close="openEpenseDetailModal=false"></epenseDetailModal>
+    <!-- 产品信息设置 -->
+    <productInfoModal
+      ref="productInfo"
+      :openModal="openProductInfoModal"
+      :expenseAccountSn="$route.params.sn"
+      :expenseAccountNo="detailData?detailData.expenseAccountNo:''"
+      :expenseAccountDetailSn="expenseAccountDetailSn"
+      @ok="productInfoOk"
+      @close="openProductInfoModal=false"></productInfoModal>
+    <!-- 基础信息编辑 -->
+    <baseDataModal :show="openBaseModal" @close="openBaseModal=false" :itemSn="$route.params.sn" @refashData="refashData"></baseDataModal>
+    <!-- 导入费用明细 -->
+    <ImportGuideModal :openModal="openGuideModal" :params="{expenseAccountSn: $route.params.sn, expenseAccountNo:detailData?detailData.expenseAccountNo:''}" @close="openGuideModal=false" @ok="hanldeImprotOk" />
+    <!-- 选择审核人员 -->
+    <chooseDepartUserModal :openModal="openDepartUserModal" @close="openDepartUserModal=false" @submit="handleSubmit"></chooseDepartUserModal>
+  </div>
+</template>
+
+<script>
+import { VSelect, Upload, STable } from '@/components'
+import epenseDetailModal from './epenseDetailModal.vue'
+import productInfoModal from './productInfoModal.vue'
+import baseDataModal from './baseDataModal.vue'
+import ImportGuideModal from './importGuideModal.vue'
+import chooseDepartUserModal from './chooseDepartUserModal.vue'
+import { expenseAccountDetail, expenseAcctDetailDelete, expenseAcctDetailFindList, expenseAccountSubmit, expenseInsertImport } from '@/api/expenseManagement.js'
+export default {
+  components: { VSelect, Upload, STable, epenseDetailModal, productInfoModal, baseDataModal, ImportGuideModal, chooseDepartUserModal },
+  data () {
+    return {
+      spinning: false,
+      detailData: null,
+      openBaseModal: false,
+      openGuideModal: false,
+      openDepartUserModal: false,
+      openEpenseDetailModal: false, // 费用明细弹框
+      expenseAccountDetailSn: '',
+      openProductInfoModal: false, // 产品信息弹框
+      total: 0,
+      loadData: parameter => {
+        const params = Object.assign(parameter, { expenseAccountSn: this.$route.params.sn })
+        return expenseAcctDetailFindList(params).then(res => {
+          let data
+          if (res.status == 200) {
+            data = res.data
+            const no = 0
+            for (var i = 0; i < data.length; i++) {
+              data[i].no = no + i + 1
+            }
+            this.total = data.length
+            this.disabled = false
+            this.spinning = false
+          }
+          return data
+        })
+      },
+      columns: [
+        { title: '序号', dataIndex: 'no', width: '5%', align: 'center' },
+        { title: '记账类型', dataIndex: 'accountTypeDictValue', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '记账名称', dataIndex: 'accountName', align: 'center', width: '10%', customRender: function (text) { return text || '--' } },
+        { title: '所属区域', dataIndex: 'subareaNames', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '所属省份', dataIndex: 'provinceName', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '所属城市', dataIndex: 'cityName', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '记账费用', dataIndex: 'expense', align: 'center', width: '10%', customRender: function (text) { return (text == 0 || text) ? text : '--' } },
+        { title: '费用承担方', scopedSlots: { customRender: 'fycdf' }, align: 'center', width: '20%' },
+        { title: '产品信息', scopedSlots: { customRender: 'cpxx' }, align: 'center', width: '15%' },
+        { title: '备注', dataIndex: 'remarks', align: 'center', width: '10%', customRender: function (text) { return text || '--' } },
+        { title: '操作', scopedSlots: { customRender: 'action' }, width: '100px', align: 'center' }
+      ]
+    }
+  },
+  methods: {
+    // 编辑基础信息
+    handleEditBase (event) {
+      this.openBaseModal = true
+    },
+    refashData () {
+      this.getDetail(1)
+    },
+    //  费用单基础详情
+    getDetail (type) {
+      this.spinning = true
+      this.disabled = true
+      expenseAccountDetail({ expenseAccountSn: this.$route.params.sn }).then(res => {
+        if (res.status == 200) {
+          this.detailData = res.data
+          this.detailData.content = res.data.content.replace(/\n+/, '\n').split('\n')
+          if (!type) {
+            // 获取费用明细列表
+            this.getExpenseDetailList()
+          } else {
+            this.spinning = false
+            this.disabled = false
+          }
+        }
+      })
+    },
+    getExpenseDetailList () {
+      this.$refs.table.refresh()
+    },
+    // 确认导入明细
+    hanldeImprotOk (obj) {
+      console.log(obj)
+      expenseInsertImport(obj).then(res => {
+        if (res.status == 200) {
+          this.getDetail(false)
+        }
+      })
+    },
+    // 新增明细
+    newExpenseDetail () {
+      this.expenseAccountDetailSn = ''
+      this.openEpenseDetailModal = true
+    },
+    // 新增明细成功
+    epenseDetailOk () {
+      // 刷新列表
+      this.openEpenseDetailModal = false
+      this.getDetail(false)
+    },
+    // 设置产品
+    setProduct (row) {
+      this.openProductInfoModal = true
+      this.expenseAccountDetailSn = row.expenseAccountDetailSn
+      this.$refs.productInfo.setBaseData(row)
+    },
+    // 新增产品成功
+    productInfoOk () {
+      // 刷新列表
+      this.openProductInfoModal = false
+      this.getDetail(false)
+    },
+    // 编辑明细
+    handleEdit (row) {
+      this.openEpenseDetailModal = true
+      this.expenseAccountDetailSn = row.expenseAccountDetailSn
+    },
+    // 删除明细
+    handleDel (row) {
+      const _this = this
+      this.$confirm({
+        title: '提示',
+        content: '确认要删除吗?',
+        centered: true,
+        closable: true,
+        onOk () {
+          _this.spinning = true
+          expenseAcctDetailDelete({ detailSn: row.expenseAccountDetailSn }).then(res => {
+            if (res.status == 200) {
+              _this.$message.success(res.message)
+              _this.getDetail(false)
+              _this.spinning = false
+            } else {
+              _this.spinning = false
+            }
+          })
+        }
+      })
+    },
+    beforeSubmit () {
+      if (this.total) {
+        this.openDepartUserModal = true
+      } else {
+        this.$message.info('请新增费用明细')
+      }
+    },
+    // 提交信息
+    handleSubmit (data) {
+      this.spinning = true
+      expenseAccountSubmit({ expenseAccountSn: this.$route.params.sn, ...data }).then(res => {
+        if (res.status == '200') {
+          this.$message.success(res.message)
+          this.handleBack()
+        }
+        this.spinning = false
+      })
+    },
+    // 返回
+    handleBack (data) {
+      this.$router.push({ name: 'expenseReimbursementList' })
+    },
+    pageInit () {
+      if (this.$route.params.sn) { //  编辑页
+        this.getDetail()
+      }
+    }
+  },
+  mounted () {
+    if (!this.$store.state.app.isNewTab) { // 页签刷新时调用
+      this.pageInit()
+    }
+  },
+  activated () {
+    // 如果是新页签打开或者进入新的子页(例:存在列表第2条数据编辑页页签时再打开第4条数据的编辑页),则重置当前页面
+    if (this.$store.state.app.isNewTab || !this.$store.state.app.isNewSubTab) {
+      this.pageInit()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {})
+  }
+}
+</script>
+
+<style lang="less">
+  .expenseReimbursementAddwrap{
+    position: relative;
+    height: 100%;
+    padding-bottom: 51px;
+    box-sizing: border-box;
+    >.ant-spin-nested-loading{
+      overflow-y: scroll;
+      height: 100%;
+    }
+    .expenseReimbursementAddcont{
+      margin-bottom: 10px;
+    }
+    .upload{
+      width: 100%!important;
+    }
+    .affix{
+      .ant-affix{
+        z-index: 101;
+      }
+    }
+  }
+</style>

+ 267 - 0
src/views/expenseManagement/expenseReimbursement/baseDataModal.vue

@@ -0,0 +1,267 @@
+<template>
+  <a-modal
+    v-model="opened"
+    :title="title"
+    centered
+    :maskClosable="false"
+    :width="800"
+    :footer="null"
+    @cancel="cancel"
+  >
+    <a-spin :spinning="spinning" tip="Loading...">
+      <a-form-model
+        id="expenseReimbursementAddform"
+        ref="ruleForm"
+        :model="form"
+        :rules="rules"
+        :label-col="formItemLayout.labelCol"
+        :wrapper-col="formItemLayout.wrapperCol">
+        <a-row>
+          <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+            <a-form-model-item label="申请人" prop="applyPersonSn">
+              <employee style="width: 100%;" @change="employeeChange" id="expenseReimbursement-Employee" v-model="form.applyPersonSn"></employee>
+            </a-form-model-item>
+          </a-col>
+          <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+            <a-form-model-item label="申请部门" prop="applyDepartmentSn">
+              <department style="width: 100%;" @change="departementChange" id="expenseReimbursement-Department" v-model="form.applyDepartmentSn"></department>
+            </a-form-model-item>
+          </a-col>
+          <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+            <a-form-model-item label="费用类型" prop="expenseType">
+              <v-select
+                code="EXPENSE_TYPE"
+                id="expenseReimbursement-expenseType"
+                v-model="form.expenseType"
+                allowClear
+                placeholder="请选择费用类型"
+              ></v-select>
+            </a-form-model-item>
+          </a-col>
+          <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+            <a-form-model-item label="费用产生月" prop="expenseDate">
+              <a-month-picker
+                placeholder="请选择月份"
+                v-model="form.expenseDate"
+                locale="zh-cn"
+                @change="expenseDateChange"
+                :disabled-date="disabledDate"
+                style="width: 100%;"/>
+            </a-form-model-item>
+          </a-col>
+          <a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <a-form-model-item label="主题" prop="title" :label-col="{span:3}" :wrapper-col="{span:19}">
+              <a-textarea
+                id="expenseReimbursementAdd-title"
+                :maxLength="100"
+                :rows="3"
+                v-model.trim="form.title"
+                placeholder="请输入费用报销主题(最多100个字符)"
+                allowClear />
+            </a-form-model-item>
+          </a-col>
+          <a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <a-form-model-item label="详细说明" prop="content" :label-col="{span:3}" :wrapper-col="{span:19}">
+              <a-textarea
+                id="expenseReimbursementAdd-content"
+                placeholder="请详细描述费用报销内容(最多500个字符)"
+                v-model.trim="form.content"
+                :rows="3"
+                :maxLength="500"
+                allowClear/>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+        <a-row>
+          <a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+            <a-form-model-item label="附件" help="(支持图片、word、excel、PDF等格式,最多10个附件)" :label-col="{span:3}" :wrapper-col="{span:19}">
+              <Upload
+                style="height: 100%;"
+                id="noticeEdit-attachList"
+                v-model="form.expenseAccountFilesList"
+                ref="attachList"
+                :fileSize="10"
+                :maxNums="10"
+                fileType="*"
+                uploadType="attach"
+                :action="attachAction"
+                @change="changeAttach"
+                upText="上传附件"></Upload>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+        <div style="text-align: center;margin-top: 20px;">
+          <a-button type="primary" :loading="spinning" @click="handleSubmit" id="chooseCustom-btn-submit">保存</a-button>
+          <a-button @click="cancel" style="margin-left: 15px" id="chooseCustom-btn-back">取消</a-button>
+        </div>
+      </a-form-model>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+import { VSelect, Upload, STable } from '@/components'
+import moment from 'moment'
+import department from './department.js'
+import employee from './employee.js'
+import { expenseAccountDetail, expenseAccountSave } from '@/api/expenseManagement.js'
+export default {
+  name: 'BaseDataModal',
+  components: { VSelect, Upload, STable, department, employee },
+  props: {
+    show: [Boolean],
+    itemSn: {
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      opened: this.show,
+      expenseAccountSn: this.itemSn,
+      spinning: false,
+      title: '新增费用报销单',
+      formItemLayout: {
+        labelCol: { span: 6 },
+        wrapperCol: { span: 14 }
+      },
+      attachList: [],
+      attachAction: process.env.VUE_APP_API_BASE_URL + '/uploadGetFileInfo',
+      form: {
+        applyPersonSn: undefined, // 申请人
+        applyDepartmentSn: undefined, //  申请部门
+        expenseType: undefined, //  费用类型
+        expenseDate: moment().locale('zh-cn').format('YYYY-MM'), //  费用产生月
+        title: '', // 主题
+        content: '' // 费用说明
+      },
+      rules: {
+        applyPersonSn: [
+          { required: true, message: '请选择申请人', trigger: 'change' }
+        ],
+        applyDepartmentSn: [
+          { required: true, message: '请选择申请部门', trigger: 'change' }
+        ],
+        expenseType: [
+          { required: true, message: '请选择费用类型', trigger: 'change' }
+        ],
+        expenseDate: [
+          { required: true, message: '请选择费用产生月', trigger: 'change' }
+        ],
+        title: [
+          { required: true, message: '请输入主题', trigger: 'blur' }
+        ],
+        content: [
+          { required: true, message: '请输入详细说明', trigger: 'blur' }
+        ]
+      }
+    }
+  },
+  methods: {
+    cancel () {
+      this.$emit('close')
+    },
+    pageInit () {
+      this.$nextTick(() => {
+        this.attachList = []
+        this.form.expenseAccountFilesList = ''
+        this.$refs.attachList.setFileList('')
+        this.$refs.ruleForm.resetFields()
+        if (this.expenseAccountSn) { //  编辑页
+          this.getDetail()
+          this.title = '编辑费用报销单'
+        }
+      })
+    },
+    // 申请人员
+    employeeChange (v, r) {
+      if (r.departmentSn) {
+        this.form.applyDepartmentSn = r.departmentSn
+      }
+      this.$nextTick(() => {
+        this.$refs.ruleForm.validateField(['applyPersonSn', 'applyDepartmentSn'])
+      })
+    },
+    departementChange () {
+      this.$nextTick(() => {
+        this.$refs.ruleForm.validateField('applyDepartmentSn')
+      })
+    },
+    expenseDateChange (v, d) {
+      this.form.expenseDate = d
+    },
+    disabledDate (current) {
+      return current && current > moment().endOf('day')
+    },
+    //  附件上传
+    changeAttach (file) {
+      this.attachList = JSON.parse(file)
+      this.attachList.map(item => {
+        item.fileType = item.extName
+      })
+    },
+    //  详情
+    getDetail () {
+      this.spinning = true
+      this.disabled = true
+      expenseAccountDetail({ expenseAccountSn: this.expenseAccountSn }).then(res => {
+        if (res.status == 200) {
+          const data = res.data
+          this.form = Object.assign(this.form, data)
+          // 获取附件列表
+          this.form.expenseAccountFilesList = ''
+          this.attachList = res.data.expenseAccountFilesList
+          this.$refs.attachList.setFileList(this.attachList)
+        } else {
+          this.$refs.ruleForm.resetFields()
+        }
+        this.spinning = false
+        this.disabled = false
+      })
+    },
+    // 保存基础信息
+    handleSubmit (e) {
+      e.preventDefault()
+      const _this = this
+      this.$refs.ruleForm.validate(valid => {
+        if (valid) {
+          const form = JSON.parse(JSON.stringify(_this.form))
+          form.expenseDate = form.expenseDate + '-01'
+          form.expenseAccountFilesList = this.attachList
+          _this.spinning = true
+          console.log(form)
+          expenseAccountSave(form).then(res => {
+            if (res.status == 200) {
+              _this.$message.success(res.message)
+              setTimeout(() => {
+                if (this.expenseAccountSn) {
+                  _this.$emit('refashData')
+                } else {
+                  _this.$router.push({ name: 'expenseReimbursementEdit', params: { sn: res.data.expenseAccountSn } })
+                }
+                _this.spinning = false
+                _this.cancel()
+              }, 200)
+            } else {
+              _this.spinning = false
+            }
+          })
+        } else {
+          return false
+        }
+      })
+    }
+  },
+  watch: {
+    show (newValue, oldValue) {
+      this.opened = newValue
+      if (newValue) {
+        this.pageInit()
+      }
+    },
+    itemSn (a, b) {
+      this.expenseAccountSn = a
+    }
+  }
+}
+</script>

+ 152 - 0
src/views/expenseManagement/expenseReimbursement/chooseDepartUserModal.vue

@@ -0,0 +1,152 @@
+<template>
+  <a-modal
+    centered
+    class="departUser-modal"
+    :footer="null"
+    :maskClosable="false"
+    :title="title+'费用明细'"
+    v-model="isShow"
+    @cancle="isShow=false"
+    width="60%">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <div>
+        <div class="departUser-con">
+          <a-form-model
+            id="departUser-form"
+            ref="ruleForm"
+            :model="form"
+            :rules="rules"
+            :label-col="formItemLayout.labelCol"
+            :wrapper-col="formItemLayout.wrapperCol">
+            <a-form-model-item label="审批人员" prop="approvers">
+              <dingDepartUser
+                id="departUser-approvers"
+                v-model="form.approvers"
+                @change="changeApprovers"
+                allowClear
+                placeholder="请选择审批人员"
+              ></dingDepartUser>
+            </a-form-model-item>
+            <a-form-model-item label="抄送人" prop="ccList">
+              <dingDepartUser
+                id="departUser-ccList"
+                v-model="form.ccList"
+                allowClear
+                placeholder="请选择抄送人"
+              ></dingDepartUser>
+            </a-form-model-item>
+          </a-form-model>
+        </div>
+        <div class="btn-cont">
+          <a-button type="primary" id="departUser-save" @click="handleSave">确定</a-button>
+          <a-button id="departUser-back" @click="isShow = false" style="margin-left: 15px;">取消</a-button>
+        </div>
+      </div>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+import dingDepartUser from '@/views/common/dingDepartUser.js'
+export default {
+  name: 'ChooseDepartUserModal',
+  components: { dingDepartUser },
+  props: {
+    openModal: { //  弹框显示状态
+      type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      spinning: false,
+      isShow: this.openModal, //  是否打开弹框
+      title: '',
+      formItemLayout: {
+        labelCol: { span: 4 },
+        wrapperCol: { span: 16 }
+      },
+      form: {
+        approvers: [], // 审批人
+        ccList: [] // 抄送人
+      },
+      rules: {
+        approvers: [
+          { required: true, type: 'array', message: '请选择审批人', trigger: 'change' }
+        ]
+        // ccList: [
+        //   { required: true, message: '请选择抄送人', trigger: 'change' }
+        // ]
+      }
+    }
+  },
+  methods: {
+    changeApprovers (value) {
+      this.form.approvers = value
+      this.$refs.ruleForm.clearValidate()
+    },
+    getTreeVal (data) {
+      const ret = []
+      data.map(item => {
+        ret.push(item.value)
+      })
+      return ret.join(',')
+    },
+    // 确定
+    handleSave (e) {
+      e.preventDefault()
+      const _this = this
+      this.$refs.ruleForm.validate(valid => {
+        if (valid) {
+          const form = JSON.parse(JSON.stringify(_this.form))
+          if (form.approvers) {
+            form.approvers = this.getTreeVal(form.approvers)
+          }
+          if (form.ccList) {
+            form.ccList = this.getTreeVal(form.ccList)
+          }
+          console.log(form)
+          this.$emit('submit', form)
+          this.$emit('close')
+        } else {
+          return false
+        }
+      })
+    },
+    resetForm () {
+      this.$nextTick(() => {
+        this.$refs.ruleForm.resetFields()
+      })
+      this.form = {
+        ccList: undefined,
+        approvers: undefined
+      }
+    }
+  },
+  watch: {
+    //  父页面传过来的弹框状态
+    openModal (newValue, oldValue) {
+      this.isShow = newValue
+    },
+    //  重定义的弹框状态
+    isShow (newValue, oldValue) {
+      if (!newValue) {
+        this.$emit('close')
+        this.resetForm()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  .departUser-modal{
+    .ant-modal-body {
+      padding: 30px 40px 24px;
+    }
+    .btn-cont {
+      text-align: center;
+      margin: 35px 0 10px;
+    }
+  }
+</style>

+ 275 - 0
src/views/expenseManagement/expenseReimbursement/chooseImportModal.vue

@@ -0,0 +1,275 @@
+<template>
+  <a-modal
+    centered
+    class="chooseImport-modal"
+    :footer="null"
+    :maskClosable="false"
+    title="确认导入"
+    v-model="isShow"
+    @cancle="isShow=false"
+    width="80%">
+    <div class="chooseImport-con">
+      <!-- 可导入数据 -->
+      <p class="">可导入数据{{ loadData.length }}条</p>
+      <a-table
+        class="sTable"
+        ref="table"
+        size="small"
+        :rowKey="(record) => record.allocateSn"
+        :columns="nowColumns"
+        :dataSource="loadData"
+        :loading="loading"
+        :scroll="{ y: 200 }"
+        :pagination="false"
+        bordered>
+        <!-- 费用承担方 -->
+        <template slot="fycdf" slot-scope="text,record">
+          <div v-for="(item,index) in record.detailSplitList" :key="item.id+'-'+index">
+            {{ item.splitObjName }}<span v-if="item.childDetailSplit&&item.childDetailSplit.splitObjName">/</span>{{ item.childDetailSplit?item.childDetailSplit.splitObjName:'' }}: ¥{{ item.splitAmount }}
+          </div>
+        </template>
+        <!-- 产品信息 -->
+        <template slot="cpxx" slot-scope="text,record">
+          <div v-for="item in record.detailProductsList" :key="item.id">
+            <div v-if="item.productCode">{{ item.productCode }}: ¥{{ item.expense }}</div>
+            <div v-else-if="item.productBrandName">{{ item.productBrandName }}<span v-if="item.productTypeShowName">/</span>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+            <div v-else>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+          </div>
+          <span v-if="!record.detailProductsList||record.detailProductsList.length==0">--</span>
+        </template>
+      </a-table>
+      <a-divider />
+      <!-- 不可导入数据 -->
+      <p class="red">不可导入数据{{ unLoadData.length }}条</p>
+      <a-table
+        class="unTable"
+        ref="unTable"
+        size="small"
+        :rowKey="(record) => record.errorDesc"
+        :columns="nowUnColumns"
+        :dataSource="unLoadData"
+        :loading="loading"
+        :scroll="{ y: 200 }"
+        :pagination="false"
+        bordered>
+        <!-- 费用承担方 -->
+        <template slot="fycdf" slot-scope="text,record">
+          <div v-for="(item,index) in record.detailSplitList" :key="item.no+'-'+index">
+            {{ item.splitObjName }}<span v-if="item.childDetailSplit&&item.childDetailSplit.splitObjName">/</span>{{ item.childDetailSplit?item.childDetailSplit.splitObjName:'' }}: ¥{{ item.splitAmount }}
+          </div>
+        </template>
+        <!-- 产品信息 -->
+        <template slot="cpxx" slot-scope="text,record">
+          <div v-for="(item,index) in record.detailProductsList" :key="item.no+'-'+index">
+            <div v-if="item.productCode">{{ item.productCode }}: ¥{{ item.expense }}</div>
+            <div v-else-if="item.productBrandName">{{ item.productBrandName }}<span v-if="item.productTypeShowName">/</span>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+            <div v-else>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+          </div>
+          <span v-if="!record.detailProductsList||record.detailProductsList.length==0">--</span>
+        </template>
+        <template slot="errorMsg" slot-scope="text,record">
+          <a-popover placement="topRight">
+            <template slot="content">
+              <p v-for="d in record.importErrorMsgList">{{ d }}</p>
+            </template>
+            <a-button type="link">
+              查看
+            </a-button>
+          </a-popover>
+        </template>
+      </a-table>
+      <!-- 按钮 -->
+      <div class="btn-con">
+        <a-button
+          type="primary"
+          id="chooseImport-submit"
+          size="large"
+          :class="loadData.length==0?'button-grey':'button-primary'"
+          @click="handleSubmit"
+          style="padding: 0 40px;">确认导入</a-button>
+        <a-button
+          id="chooseImport-cancel"
+          size="large"
+          class="button-cancel"
+          @click="isShow=false"
+          style="padding: 0 40px;margin-left: 15px;">取消</a-button>
+        <a-button
+          type="primary"
+          id="chooseImport-error"
+          size="large"
+          class="button-error"
+          @click="handleError"
+          style="padding: 0 40px;margin-left: 15px;">导出错误项</a-button>
+      </div>
+    </div>
+  </a-modal>
+</template>
+
+<script>
+import { hdExportExcel } from '@/libs/exportExcel'
+import { expenseFailExcel } from '@/api/expenseManagement'
+export default {
+  name: 'ChooseImportModal',
+  props: {
+    openModal: { //  弹框显示状态
+      type: Boolean,
+      default: false
+    },
+    paramsData: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    }
+  },
+  data () {
+    return {
+      isShow: this.openModal, //  是否打开弹框
+      nowColumns: [
+        { title: '序号', dataIndex: 'no', width: '8%', align: 'center' },
+        { title: '记账类型', dataIndex: 'importAccountType', width: '25%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '记账名称', dataIndex: 'importAccountName', width: '25%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '所属区域', dataIndex: 'subareaNames', width: '14%', align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '所属省份', dataIndex: 'provinceName', width: '14%', align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '所属城市', dataIndex: 'cityName', width: '14%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '备注', dataIndex: 'remarks', width: '14%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用承担方', scopedSlots: { customRender: 'fycdf' }, align: 'center', width: '15%' },
+        { title: '产品信息', scopedSlots: { customRender: 'cpxx' }, align: 'center', width: '15%' }
+      ],
+      loadData: [],
+      nowUnColumns: [
+        { title: '序号', dataIndex: 'no', width: '9%', align: 'center' },
+        { title: '记账类型', dataIndex: 'importAccountType', width: '20%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '记账名称', dataIndex: 'importAccountName', width: '21%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '所属区域', dataIndex: 'subareaNames', width: '10%', align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '所属省份', dataIndex: 'provinceName', width: '10%', align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '所属城市', dataIndex: 'cityName', width: '10%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '备注', dataIndex: 'remarks', width: '20%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用承担方', scopedSlots: { customRender: 'fycdf' }, align: 'center', width: '15%' },
+        { title: '产品信息', scopedSlots: { customRender: 'cpxx' }, align: 'center', width: '15%' },
+        { title: '错误说明', scopedSlots: { customRender: 'errorMsg' }, width: '10%', align: 'center' }
+      ],
+      unLoadData: [],
+      loading: false
+    }
+  },
+  methods: {
+    // 格式化数据
+    formatProduct (list) {
+      const ret = []
+      list.map(item => {
+        ret.push({
+          productCode: item.productCode,
+          expense: item.expense,
+          productBrandName: item.productBrandName,
+          productTypeShowName: (item.productTypeName3 ? (item.productTypeName2 + '>' + item.productTypeName3) : (item.productTypeName1 || item.productTypeName2 || ''))
+        })
+      })
+      return ret
+    },
+    formatSplit (list) {
+      const ret = []
+      list.map(item => {
+        // 有承担人员
+        if (item.childDetailSplitList) {
+          item.childDetailSplitList.map(k => {
+            ret.push({
+              splitObjName: item.importSplitObjName,
+              splitObjType: item.importSplitObjType,
+              splitAmount: k.splitAmount,
+              childDetailSplit: {
+                splitObjName: k.importSplitObjName
+              }
+            })
+          })
+        } else {
+          ret.push({
+            splitObjName: item.importSplitObjName,
+            splitObjType: item.importSplitObjType,
+            splitAmount: item.splitAmount
+          })
+        }
+      })
+      return ret
+    },
+    getData () {
+      const paramsData = JSON.parse(JSON.stringify(this.paramsData))
+      this.loadData = paramsData.rightList || []
+      this.unLoadData = paramsData.errorList || []
+      this.loadData.map((item, index) => {
+        item.no = index + 1
+        item.detailProductsList = this.formatProduct(item.detailProductsList)
+        item.detailSplitList = this.formatSplit(item.detailSplitList)
+        item.expenseAccountNo = paramsData.expenseAccountNo
+        item.expenseAccountSn = paramsData.expenseAccountSn
+      })
+      this.unLoadData.map((item, index) => {
+        item.no = index + 1
+        item.detailProductsList = this.formatProduct(item.detailProductsList)
+        item.detailSplitList = this.formatSplit(item.detailSplitList)
+        item.expenseAccountNo = paramsData.expenseAccountNo
+        item.expenseAccountSn = paramsData.expenseAccountSn
+      })
+    },
+    // 确认导入
+    handleSubmit () {
+      if (this.paramsData.rightList.length == 0) {
+        this.$message.warning('无可导入费用明细!')
+      } else {
+        const data = this.paramsData.rightList
+        data.map(item => {
+          item.expenseAccountNo = this.paramsData.expenseAccountNo
+          item.expenseAccountSn = this.paramsData.expenseAccountSn
+        })
+        this.$emit('ok', data)
+        this.isShow = false
+      }
+    },
+    // 导出
+    handleError () {
+      const _this = this
+      if (_this.paramsData.errorList.length < 1) {
+        _this.$message.info('暂无可导出错误项~')
+        return
+      }
+      _this.spinning = true
+      hdExportExcel(expenseFailExcel, _this.paramsData.errorList, '费用明细导入错误项', function () {
+        _this.spinning = false
+      })
+    }
+  },
+  watch: {
+    //  父页面传过来的弹框状态
+    openModal (newValue, oldValue) {
+      this.isShow = newValue
+    },
+    //  重定义的弹框状态
+    isShow (newValue, oldValue) {
+      if (!newValue) {
+        this.$emit('close')
+        this.loadData = []
+        this.unLoadData = []
+      } else {
+        this.getData()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .chooseImport-modal{
+    .chooseImport-con{
+      .red{
+        color: #f30;
+      }
+      .btn-con{
+        text-align: center;
+        margin: 30px 0 20px;
+        .button-cancel,.button-error{
+          font-size: 12px;
+        }
+      }
+    }
+  }
+</style>

+ 71 - 0
src/views/expenseManagement/expenseReimbursement/department.js

@@ -0,0 +1,71 @@
+import { departmentQueryList } from '@/api/expenseManagement'
+const Department = {
+  template: `
+      <a-select
+        :placeholder="placeholder"
+        :id="id"
+        allowClear
+        :value="defaultVal"
+        :showSearch="true"
+        @change="handleChange"
+        option-filter-prop="children"
+        :filter-option="filterOption">
+        <a-select-option v-for="item in list" :key="item.departmentSn" :value="item.departmentSn">{{ item.name }}</a-select-option>
+      </a-select>
+    `,
+  props: {
+    value: {
+      type: String,
+      defatut: ''
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    sysFlag: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: '请选择申请部门'
+    }
+  },
+  data() {
+    return {
+      defaultVal: this.value,
+      list: []
+    };
+  },
+  mounted() {
+    this.getList()
+  },
+  watch: {
+    value(newValue, oldValue) {
+      this.defaultVal = newValue
+    },
+  },
+  methods: {
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    handleChange(value) {
+      this.defaultVal = value;
+      this.$emit('change', value);
+      this.$emit('input', value);
+    },
+    getList () {
+      departmentQueryList({}).then(res => {
+        if (res.status == 200) {
+          this.list = res.data
+        } else {
+          this.list = []
+        }
+      })
+    },
+  },
+};
+
+export default Department

+ 206 - 0
src/views/expenseManagement/expenseReimbursement/detail.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="expenseManagementDetail-wrap">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <a-page-header :ghost="false" :backIcon="false" class="expenseManagementDetail-cont">
+        <template slot="subTitle">
+          <a href="javascript:;" @click="handleBack">
+            <a-icon type="left"></a-icon>
+            返回列表
+          </a>
+        </template>
+        <!-- 操作区,位于 title 行的行尾 -->
+        <template slot="extra">
+          <a-button
+            :disabled="detailData==null"
+            key="1"
+            type="default"
+            id="expenseManagementDetail-print-btn"
+            @click="handlePrint('preview')">打印预览</a-button>
+          <a-button
+            :disabled="detailData==null"
+            key="2"
+            type="primary"
+            class="button-error"
+            id="expenseManagementDetail-print-btn"
+            @click="handlePrint('print')">快捷打印</a-button>
+        </template>
+      </a-page-header>
+      <!-- 基础信息 -->
+      <a-card size="small" :bordered="false" class="expenseManagementDetail-cont" v-if="detailData">
+        <a-collapse :activeKey="['1']">
+          <a-collapse-panel key="1" header="基础信息">
+            <a-descriptions size="small" :column="{ xs: 2, sm: 3, md: 4}">
+              <a-descriptions-item label="费用单号">{{ detailData&&detailData.expenseAccountNo || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="申请人">{{ detailData&&detailData.applyPersonName || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="申请部门">{{ detailData&&detailData.applyDepartmentName || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="费用类型">{{ detailData&&detailData.expenseTypeDictValue || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="费用发生月份">{{ detailData&&detailData.expenseDate?detailData.expenseDate.substring(0, 7) : '--' }}</a-descriptions-item>
+              <a-descriptions-item label="合计金额">{{ (detailData.applyExpenseTotal || detailData.applyExpenseTotal == 0) ? detailData.applyExpenseTotal+' 元' : '--' }}</a-descriptions-item>
+              <a-descriptions-item label="状态">{{ detailData&&detailData.stateDictValue || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="创建时间">{{ detailData&&detailData.createDate || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="审核时间">{{ detailData&&detailData.finishDate || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="主题" :span="3">{{ detailData&&detailData.title || '--' }}</a-descriptions-item>
+              <a-descriptions-item label="详细说明" :span="4">
+                {{ detailData.content.join(',')||'' }}
+              </a-descriptions-item>
+              <a-descriptions-item label="附件" :span="4">
+                <a target="_blank" style="color: #00aaff;text-decoration: underline;margin-right: 15px;" :href="item.filePath" v-for="item in detailData.expenseAccountFilesList" :key="item.id">
+                  {{ item.fileName }}
+                </a>
+              </a-descriptions-item>
+            </a-descriptions>
+          </a-collapse-panel>
+        </a-collapse>
+      </a-card>
+      <a-card size="small" :bordered="false" class="pages-wrap">
+        <a-alert type="info" style="margin-bottom:10px">
+          <div slot="message">
+            共<strong> {{ count }} </strong> 条费用明细,合计金额¥<strong v-if="detailData"> {{ (detailData.applyExpenseTotal || detailData.applyExpenseTotal == 0) ? detailData.applyExpenseTotal+' 元' : '--' }} </strong>
+          </div>
+        </a-alert>
+        <!-- 列表 -->
+        <s-table
+          class="sTable"
+          ref="table"
+          size="small"
+          :rowKey="record => record.id"
+          :columns="columns"
+          :data="loadData"
+          :showPagination="false"
+          :defaultLoadData="true"
+          bordered>
+          <!-- 费用承担方 -->
+          <template slot="fycdf" slot-scope="text,record">
+            <div v-for="(item,index) in record.detailSplitList" :key="item.id+'-'+index">
+              {{ item.splitObjName }}
+              <span v-if="item.childDetailSplit&&item.childDetailSplit.splitObjName">/</span>
+              {{ item.childDetailSplit?item.childDetailSplit.splitObjName:'' }}: ¥{{ item.childDetailSplit?item.childDetailSplit.splitAmount:item.splitAmount }}
+            </div>
+          </template>
+          <!-- 产品信息 -->
+          <template slot="cpxx" slot-scope="text,record">
+            <div v-for="item in record.detailProductsList" :key="item.id">
+              <div v-if="item.productCode">{{ item.productCode }}: ¥{{ item.expense }}</div>
+              <div v-else-if="item.productBrandName">{{ item.productBrandName }}<span v-if="item.productTypeShowName">/</span>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+              <div v-else>{{ item.productTypeShowName }}: ¥{{ item.expense }}</div>
+            </div>
+            <span v-if="!record.detailProductsList||record.detailProductsList.length==0">--</span>
+          </template>
+        </s-table>
+      </a-card>
+    </a-spin>
+    <!-- 打印预览 -->
+    <previewModal :showModal="showModal" @close="showModal=false" :detailSn="$route.params.sn" :detailData="detailData"></previewModal>
+    <iframe id="printfsqd" name="printfsqd" hidden></iframe>
+  </div>
+</template>
+
+<script>
+import { STable } from '@/components'
+import { expenseAccountDetail, expenseAcctDetailFindList } from '@/api/expenseManagement'
+import previewModal from './previewModal.vue'
+import { jGPrint } from '@/libs/JGPrint.js'
+export default {
+  name: 'ExpenseReimbursementDetail',
+  components: { STable, previewModal },
+  data () {
+    return {
+      spinning: false,
+      disabled: false,
+      showModal: false, // 打印弹窗初始状态
+      baseData: null,
+      count: 0,
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        this.spinning = true
+        const params = Object.assign({ expenseAccountSn: this.$route.params.sn })
+        return expenseAcctDetailFindList(params).then(res => {
+          let data
+          if (res.status == 200) {
+            data = res.data
+            this.count = res.data.length
+            const no = 0
+            for (var i = 0; i < data.length; i++) {
+              data[i].no = no + i + 1
+            }
+          }
+          this.spinning = false
+          return data
+        })
+      },
+      columns: [
+        { title: '序号', dataIndex: 'no', width: '5%', align: 'center' },
+        { title: '记账类型', dataIndex: 'accountTypeDictValue', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '记账名称', dataIndex: 'accountName', align: 'center', width: '14%', customRender: function (text) { return text || '--' } },
+        { title: '所属区域', dataIndex: 'subareaNames', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '所属省份', dataIndex: 'provinceName', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '所属城市', dataIndex: 'cityName', align: 'center', width: '8%', customRender: function (text) { return text || '--' } },
+        { title: '记账费用', dataIndex: 'expense', align: 'center', width: '10%', customRender: function (text) { return text || '--' } },
+        { title: '费用承担方', scopedSlots: { customRender: 'fycdf' }, align: 'center', width: '15%' },
+        { title: '产品信息', scopedSlots: { customRender: 'cpxx' }, align: 'center', width: '15%' },
+        { title: '备注', dataIndex: 'remarks', align: 'center', width: '10%', customRender: function (text) { return text || '--' } }
+      ],
+      chooseLoadData: [],
+      detailData: null, //  详情数据
+      total: 0
+    }
+  },
+  methods: {
+    //  返回
+    handleBack () {
+      this.$router.push({ name: 'expenseReimbursementList' })
+    },
+    // 打印
+    handlePrint (type) {
+      const html = document.getElementById('printTest').innerHTML
+      // var iframe = document.getElementById('printfsqd')
+      // var iframedoc = iframe.contentDocument || iframe.contentWindow.document
+      // iframedoc.body.innerHTML = html
+      // window.frames['printfsqd'].focus()
+      // window.frames['printfsqd'].print()
+      jGPrint(html, type)
+    },
+    //  详情
+    getDetail () {
+      this.spinning = true
+      this.detailData = null
+      expenseAccountDetail({ expenseAccountSn: this.$route.params.sn }).then(res => {
+        if (res.status == 200) {
+          this.spinning = false
+          this.detailData = res.data
+          this.detailData.content = res.data.content.replace(/\n+/, '\n').split('\n')
+          this.$refs.table.refresh()
+        } else {
+          this.detailData = null
+        }
+      })
+    }
+  },
+  mounted () {
+    if (!this.$store.state.app.isNewTab) { // 页签刷新时调用
+      this.getDetail()
+    }
+  },
+  activated () {
+    // 如果是新页签打开或者进入新的子页(例:存在列表第2条数据编辑页页签时再打开第4条数据的编辑页),则重置当前页面
+    if (this.$store.state.app.isNewTab || !this.$store.state.app.isNewSubTab) {
+      this.getDetail()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {})
+  }
+}
+</script>
+
+<style lang="less">
+.expenseManagementDetail-wrap {
+  .expenseManagementDetail-cont {
+    margin-bottom: 10px;
+  }
+  .footer-cont{
+    margin-top: 5px;
+    text-align: center;
+  }
+}
+</style>

+ 71 - 0
src/views/expenseManagement/expenseReimbursement/employee.js

@@ -0,0 +1,71 @@
+import { employeeQueryList } from '@/api/expenseManagement'
+const Employee = {
+  template: `
+      <a-select
+        :placeholder="placeholder"
+        :id="id"
+        allowClear
+        :value="defaultVal"
+        :showSearch="true"
+        @change="handleChange"
+        option-filter-prop="children"
+        :filter-option="filterOption">
+        <a-select-option v-for="item in list" :key="item.employeeSn" :value="item.employeeSn">
+        {{ item.name }}
+        <span v-if="item.kdDepartmentAllName">--{{item.kdDepartmentAllName}}</span>
+        </a-select-option>
+      </a-select>
+    `,
+  props: {
+    value: {
+      type: String,
+      defatut: ''
+    },
+    id: {
+      type: String,
+      default: ''
+    },
+    placeholder: {
+      type: String,
+      default: '请选择申请人(输入名称搜索)'
+    },
+  },
+  data() {
+    return {
+      defaultVal: this.value,
+      list: []
+    };
+  },
+  mounted() {
+    this.getList()
+  },
+  watch: {
+    value(newValue, oldValue) {
+      this.defaultVal = newValue
+    },
+  },
+  methods: {
+    filterOption (input, option) {
+      return (
+        option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+      )
+    },
+    handleChange(value) {
+      this.defaultVal = value;
+      const item = this.list.find(item => item.employeeSn == value)
+      this.$emit('change', value, item);
+      this.$emit('input', value);
+    },
+    getList () {
+      employeeQueryList({}).then(res => {
+        if (res.status == 200) {
+          this.list = res.data
+        } else {
+          this.list = []
+        }
+      })
+    },
+  },
+};
+
+export default Employee

+ 328 - 0
src/views/expenseManagement/expenseReimbursement/epenseCdfModal.vue

@@ -0,0 +1,328 @@
+<template>
+  <div>
+    <a-spin :spinning="spinning" tip="Loading...">
+      <div>
+        <div class="epenseCdfModal-con">
+          <!-- 列表 -->
+          <a-table
+            class="sTable fixPagination"
+            ref="table"
+            :scroll="{ y: 300 }"
+            size="small"
+            :rowKey="(record,index) => 'cdfTable-'+index"
+            :columns="columns"
+            :data-source="dataList"
+            :pagination="false"
+            bordered>
+            <div slot="customTitle1"><span style="color: red;">*</span>费用承担方类型</div>
+            <div slot="customTitle2"><span style="color: red;">*</span>费用承担方</div>
+            <div slot="customTitle3"><span style="color: red;">*</span>费用承担金额</div>
+            <!-- 费用承担方类型 -->
+            <template slot="fycdftype" slot-scope="text,record,index">
+              <v-select
+                code="EXPENSE_ACCOUNT_DETAIL_SPLIT_OBJ_TYPE"
+                allowClear
+                style="width: 100%;"
+                :isEnable="true"
+                v-model="record.splitObjType"
+                @change="splitObjTypeChange(index)"
+                placeholder="请选择承担方类型"
+              ></v-select>
+            </template>
+            <!-- 费用承担方 -->
+            <template slot="fycdf" slot-scope="text,record,index">
+              <!-- 部门 -->
+              <department v-show="record.splitObjType=='DEPARTMENT'" style="width: 100%;" placeholder="请选择承担方" v-model="record.splitObjSn"></department>
+              <!-- 经销商 -->
+              <custList
+                v-show="record.splitObjType=='CUSTOMER'"
+                cooperateFlag="1"
+                @change="custChange"
+                :id="'cust-'+index"
+                :ref="'custList-'+record.no"
+                placeholder="请选择(输入名称搜索)"></custList>
+              <span v-if="!record.splitObjType">--</span>
+            </template>
+            <!-- 费用承担金额 -->
+            <template slot="amount" slot-scope="text,record,index">
+              <a-input-number
+                v-show="record.splitObjType"
+                style="width: 100%;"
+                v-model="record.splitAmount"
+                @change="splitAmountChange"
+                :precision="2"
+                :min="0"
+                :max="999999"
+                placeholder="请输入"
+              />
+              <span v-if="!record.splitObjType">--</span>
+            </template>
+            <!-- 承担人员 -->
+            <template slot="cdry" slot-scope="text,record,index">
+              <div
+                style="display: flex;align-items: center;width: 100%;padding: 5px 0;border-bottom: 1px solid #eee;"
+                v-for="(item,idx) in record.childDetailSplitList"
+                :key="item.id">
+                <employee style="width: 40%;" placeholder="请选择承担人员" v-model="item.splitObjSn"></employee>
+                -
+                <a-input-number
+                  style="width: 40%;"
+                  v-model="item.splitAmount"
+                  :precision="2"
+                  :min="0"
+                  :max="999999"
+                  placeholder="请输入费用"
+                />
+                <div style="padding-left: 15px;" v-if="record.childDetailSplitList&&record.childDetailSplitList.length>1">
+                  <a-button
+                    @click="handleDelCdry(record,idx)"
+                    title="删除"
+                    size="small"
+                    type="danger"
+                    shape="circle"
+                    icon="delete" />
+                </div>
+                <div v-if="record.childDetailSplitList&&idx == record.childDetailSplitList.length - 1">
+                  <a-button
+                    @click="handleAddCdry(record,idx)"
+                    title="添加承担人员"
+                    size="small"
+                    type="primary"
+                    class="button-info"
+                    shape="circle"
+                    icon="plus" />
+                </div>
+              </div>
+              <span v-if="record.childDetailSplitList&&record.childDetailSplitList.length==0">--</span>
+            </template>
+            <template slot="action" slot-scope="text,record,index">
+              <a-button
+                size="small"
+                type="link"
+                class="button-error"
+                @click="handleDelRow(record,index)"
+              >删除</a-button>
+            </template>
+            <template slot="footer" slot-scope="currentPageData">
+              <a-button
+                type="link"
+                class="button-info"
+                block
+                @click="handleAddRow()"
+              >+ 新增费用承担方</a-button>
+            </template>
+          </a-table>
+        </div>
+      </div>
+    </a-spin>
+  </div>
+</template>
+
+<script>
+import { VSelect } from '@/components'
+import department from './department.js'
+import employee from './employee.js'
+import custList from '@/views/common/custList.vue'
+export default {
+  components: { VSelect, department, employee, custList },
+  name: 'EpenseCdfModal',
+  props: {
+    list: { //  弹框显示状态
+      type: Array,
+      default: () => { return [] }
+    }
+  },
+  watch: {
+    list (newValue, oldValue) {
+      this.dataList = JSON.parse(JSON.stringify(newValue))
+      this.dataList.map(item => {
+        item.no = item.id
+        if (item.splitObjType == 'DEPARTMENT' && (!item.childDetailSplitList || item.childDetailSplitList.length == 0)) {
+          this.$set(item, 'childDetailSplitList', [{
+            splitObjType: 'EMPLOYEE',
+            splitObjSn: undefined,
+            splitAmount: 0
+          }])
+        }
+        if (item.splitObjType == 'CUSTOMER') {
+          this.$nextTick(() => {
+            this.$refs['custList-' + item.no].setDufaultVal(item.splitObjName)
+          })
+        }
+      })
+      if (this.dataList.length == 0) {
+        this.handleAddRow()
+      }
+    }
+  },
+  data () {
+    return {
+      spinning: false,
+      dataList: [],
+      columns: [
+        { slots: { title: 'customTitle1' }, scopedSlots: { customRender: 'fycdftype' }, align: 'center', width: '15%' },
+        { slots: { title: 'customTitle2' }, scopedSlots: { customRender: 'fycdf' }, align: 'center', width: '20%' },
+        { slots: { title: 'customTitle3' }, scopedSlots: { customRender: 'amount' }, align: 'center', width: '15%' },
+        { title: '承担人员-人员平摊费用', scopedSlots: { customRender: 'cdry' }, align: 'center', width: '40%' },
+        { title: '操作', scopedSlots: { customRender: 'action' }, width: '10%', align: 'center' }
+      ]
+    }
+  },
+  mounted () {
+    this.handleAddRow()
+  },
+  methods: {
+    // 费用承担方类型
+    splitObjTypeChange (index) {
+      const row = this.dataList[index]
+      console.log(index, row.splitObjType)
+      // 只有部门有承担人员
+      this.$set(row, 'childDetailSplitList', row.splitObjType == 'DEPARTMENT' ? [{
+        splitObjType: 'EMPLOYEE',
+        splitObjSn: undefined,
+        splitAmount: Number(row.splitAmount).toFixed(2)
+      }] : [])
+      if (row.splitObjType == 'CUSTOMER') {
+        this.$set(row, 'splitObjSn', undefined)
+        this.$set(row, 'splitObjName', undefined)
+        this.$refs['custList-' + row.no].dealerName = []
+      }
+      this.$emit('fyTotal', this.fyTotal())
+    },
+    // 经销商
+    custChange (v, obj, id) {
+      const row = this.dataList[id.split('-')[1]]
+      this.$set(row, 'splitObjSn', v.key)
+      this.$set(row, 'splitObjName', obj.dealerName)
+      this.$emit('fyTotal', this.fyTotal())
+    },
+    // 承担费用
+    splitAmountChange () {
+      this.$emit('fyTotal', this.fyTotal())
+    },
+    // 清空数据
+    clearList () {
+      this.dataList = []
+    },
+    // 获取数据
+    getList () {
+      let hasError = 0
+      const list = this.dataList.filter(item => item.splitObjType && item.splitObjSn && item.splitAmount)
+      const ret = JSON.parse(JSON.stringify(list))
+      if (ret.length != this.dataList.length) {
+        hasError = 1
+      } else {
+        ret.map(item => {
+          if (item.splitObjType == 'DEPARTMENT') {
+            const al = item.childDetailSplitList.filter(a => !a.splitObjSn && a.splitAmount > 0)
+            if (al.length) {
+              hasError = 3
+            } else {
+              const dl = item.childDetailSplitList.filter(a => a.splitObjSn) || []
+              // 有承担人员
+              if (dl && dl.length) {
+                let t = 0
+                dl.map(b => t = Number(t) + Number(b.splitAmount))
+                // console.log(Number(t), Number(item.splitAmount))
+                hasError = (Number(t) != Number(item.splitAmount)) ? 2 : 0
+              }
+              item.childDetailSplitList = dl
+            }
+          }
+        })
+      }
+      if (ret.length == 0) {
+        hasError = 1
+      }
+      return [hasError, ret]
+    },
+    // 添加人员
+    handleAddCdry (row, idx) {
+      const cur = row.childDetailSplitList[idx]
+      if (!cur.splitObjSn) {
+        this.$message.info('请选择承担人员')
+        return
+      }
+      if (cur.splitAmount <= 0) {
+        this.$message.info('请输入平摊费用')
+        return
+      }
+      // 校验承担费用合计是否超过费用承担金额
+      let totalFy = 0
+      row.childDetailSplitList.map(item => {
+        totalFy = totalFy + Number(item.splitAmount)
+      })
+      // console.log(totalFy, row.splitAmount)
+      if (totalFy >= Number(row.splitAmount).toFixed(2)) {
+        this.$warning({
+          title: '提示',
+          content: '人员平摊费用合计必需等于费用承担金额'
+        })
+        return
+      }
+      // 已使用费用
+      let ysyAmount = 0
+      row.childDetailSplitList.map(item => {
+        ysyAmount = ysyAmount + Number(item.splitAmount)
+      })
+      // 添加
+      row.childDetailSplitList.push({
+        splitObjType: 'EMPLOYEE',
+        splitObjSn: undefined,
+        splitAmount: Number(row.splitAmount - ysyAmount).toFixed(2)
+      })
+    },
+    // 删除人员
+    handleDelCdry (row, index) {
+      row.childDetailSplitList.splice(index, 1)
+    },
+    // 添加承担费用
+    handleAddRow () {
+      this.dataList.push({
+        no: new Date().getTime(),
+        splitObjType: '',
+        splitObjSn: undefined,
+        splitAmount: 0,
+        childDetailSplitList: []
+      })
+    },
+    // 删除承担费用
+    handleDelRow (a, index) {
+      this.dataList.splice(index, 1)
+      this.$emit('fyTotal', this.fyTotal())
+      this.dataList.map(item => {
+        if (item.splitObjType == 'CUSTOMER') {
+          this.$nextTick(() => {
+            this.$refs['custList-' + item.no].setDufaultVal(item.splitObjName)
+          })
+        }
+      })
+    },
+    fyTotal: function () {
+      let total = 0
+      this.dataList.map(item => { total = total + item.splitAmount })
+      return total.toFixed(2)
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  .epenseCdfModal-modal{
+    .ant-modal-body {
+      padding: 30px 40px 24px;
+    }
+    .epenseCdfModal-con{
+      text-align: center;
+      h3{
+        font-size: 16px;
+        font-weight: bold;
+        margin-bottom: 25px;
+      }
+    }
+    .btn-cont {
+      text-align: center;
+      margin: 35px 0 10px;
+    }
+  }
+</style>

+ 359 - 0
src/views/expenseManagement/expenseReimbursement/epenseDetailModal.vue

@@ -0,0 +1,359 @@
+<template>
+  <a-modal
+    centered
+    class="epenseDetailModal-modal"
+    :footer="null"
+    :maskClosable="false"
+    :title="title+'费用明细'"
+    v-model="isShow"
+    @cancle="isShow=false"
+    width="60%">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <div>
+        <div class="epenseDetailModal-con">
+          <a-form-model
+            id="epenseDetailModal-form"
+            ref="ruleForm"
+            :model="form"
+            :rules="rules"
+            :label-col="formItemLayout.labelCol"
+            :wrapper-col="formItemLayout.wrapperCol">
+            <a-row>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="记账类型" prop="accountType">
+                  <v-select
+                    code="EXPENSE_ACCOUNT_TYPE"
+                    id="epenseDetailModal-accountType"
+                    v-model="form.accountType"
+                    allowClear
+                    placeholder="请选择记账类型"
+                  ></v-select>
+                </a-form-model-item>
+              </a-col>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="记账名称" prop="accountNameSn">
+                  <!-- 部门 -->
+                  <department v-show="form.accountType=='INNER_ACCOUNT'" @change="accountNameSnChange" v-model="form.accountNameSn" placeholder="请选择记账名称(输入名称搜索)"></department>
+                  <!-- 经销商 -->
+                  <custList v-show="form.accountType=='CUSTOMER'" cooperateFlag="1" ref="custList" @change="accountNameSnChange" placeholder="请选择记账名称(输入名称搜索)"></custList>
+                  <!-- 供应商 -->
+                  <supplier v-show="form.accountType=='OUT_ACCOUNT'" enableFlag="1" @change="accountNameSnChange" v-model="form.accountNameSn" placeholder="请选择记账名称(输入名称搜索)"></supplier>
+                  <span v-if="!form.accountType">
+                    <a-select style="width: 100%" placeholder="请选择记账名称">
+                      <a-select-option value="" :disabled="true">
+                        请先选择记账类型
+                      </a-select-option>
+                    </a-select>
+                  </span>
+                </a-form-model-item>
+              </a-col>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="所属区域" prop="subareaSn">
+                  <subarea
+                    id="epenseDetailModal-subarea"
+                    ref="subareaBox"
+                    mode="multiple"
+                    @change="subareaChange"
+                    placeholder="请选择所属区域"
+                    v-model="form.subareaSnList"></subarea>
+                </a-form-model-item>
+              </a-col>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="所属省份" prop="provinceSn">
+                  <Area id="epenseDetailModal-area" @change="provinceChange" placeholder="请选择所属省份" v-model="form.provinceSn"></Area>
+                </a-form-model-item>
+              </a-col>
+            </a-row>
+            <a-row>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="所属城市" prop="citySn">
+                  <Area
+                    id="epenseDetailModal-area"
+                    @change="cityChange"
+                    placeholder="请选择所属城市"
+                    leve="city"
+                    :parentId="form.provinceSn"
+                    v-model="form.citySn"></Area>
+                </a-form-model-item>
+              </a-col>
+              <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+                <a-form-model-item label="记账费用">
+                  <a-input
+                    style="width: 100%;color: red;font-weight: bold;"
+                    id="epenseDetailModal-expense"
+                    :disabled="true"
+                    v-model="form.expense"
+                  />
+                </a-form-model-item>
+              </a-col>
+            </a-row>
+            <a-row>
+              <a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+                <a-form-model-item label="备注" prop="remarks" :label-col="{span:2}" :wrapper-col="{span:20}">
+                  <a-textarea placeholder="请输入备注(最多500个字符)" v-model="form.remarks" :rows="4" :maxLength="500"/>
+                </a-form-model-item>
+              </a-col>
+            </a-row>
+          </a-form-model>
+          <!-- 费用承担方 -->
+          <epenseCdfModal ref="epenseCdf" :list="form.detailSplitList" @fyTotal="getFyTotal"></epenseCdfModal>
+        </div>
+        <div class="btn-cont">
+          <a-button type="primary" id="epenseDetailModal-save" @click="handleSave">确定</a-button>
+          <a-button id="epenseDetailModal-back" @click="isShow = false" style="margin-left: 15px;">取消</a-button>
+        </div>
+      </div>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+import { VSelect } from '@/components'
+import epenseCdfModal from './epenseCdfModal.vue'
+import subarea from '@/views/common/subarea.js'
+import Area from '@/views/common/area.js'
+import custList from '@/views/common/custList.vue'
+import supplier from '@/views/common/supplier.js'
+import department from './department.js'
+import { expenseAcctDetailSave, expenseAcctDetailFindBySn } from '@/api/expenseManagement.js'
+export default {
+  name: 'EpenseDetailModal',
+  components: { VSelect, subarea, Area, epenseCdfModal, custList, supplier, department },
+  props: {
+    openModal: { //  弹框显示状态
+      type: Boolean,
+      default: false
+    },
+    expenseAccountSn: { // 报销单sn
+      type: String,
+      default: ''
+    },
+    expenseAccountNo: { // 报销单号
+      type: String,
+      default: ''
+    },
+    expenseAccountDetailSn: {
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      spinning: false,
+      isShow: this.openModal, //  是否打开弹框
+      title: '',
+      formItemLayout: {
+        labelCol: { span: 4 },
+        wrapperCol: { span: 16 }
+      },
+      form: {
+        id: undefined,
+        accountType: undefined, // 记帐类型
+        accountNameSn: undefined, //  记账名称
+        subareaSnList: undefined, //  所属区域
+        subareaNameList: undefined,
+        provinceSn: undefined, // 省份
+        provinceName: '',
+        citySn: undefined, // 市
+        cityName: '',
+        remarks: '', // 备注
+        expense: 0, // 记账费用
+        expenseAccountSn: '', // 报销单SN
+        expenseAccountNo: '', // 报销单号
+        expenseAccountDetailSn: '', // 报销明细sn
+        detailSplitList: []
+      },
+      rules: {
+        accountType: [
+          { required: true, message: '请选择记帐类型', trigger: 'change' }
+        ],
+        accountNameSn: [
+          { required: true, message: '请选择记账名称', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  methods: {
+    // 费用合计
+    getFyTotal (expense) {
+      this.form.expense = expense
+    },
+    // 记账名称变化时
+    accountNameSnChange (v, row) {
+      console.log(v, row)
+      if (this.form.accountType == 'CUSTOMER') {
+        this.form.accountNameSn = v.key
+        if (row) {
+          this.form.provinceSn = row.provinceSn
+          this.form.citySn = row.citySn
+          this.form.provinceName = row.provinceName
+          this.form.cityName = row.cityName
+          if (row.subareaNameSet) {
+            this.form.subareaNameList = row.subareaNameSet
+            this.form.subareaSnList = this.$refs.subareaBox.getValByName(row.subareaNameSet)
+          }
+        }
+      }
+      this.$nextTick(() => {
+        this.$refs.ruleForm.validateField('accountNameSn')
+      })
+    },
+    // 分区
+    subareaChange (v, row) {
+      const arr = []
+      row.map(item => arr.push(item.subareaName))
+      this.form.subareaNameList = arr
+    },
+    // 省
+    provinceChange (v, row) {
+      console.log(v, row)
+      this.form.provinceName = row ? row.name : ''
+      this.form.provinceSn = v || ''
+      this.form.citySn = undefined
+      this.form.cityName = ''
+    },
+    // 市
+    cityChange (v, row) {
+      this.form.cityName = row ? row.name : ''
+      this.form.citySn = v || ''
+    },
+    // 详情
+    getExpenseAccountDetail () {
+      this.spinning = true
+      expenseAcctDetailFindBySn({ detailSn: this.expenseAccountDetailSn }).then(res => {
+        this.$nextTick(() => {
+          this.form = Object.assign(this.form, res.data || {})
+          this.form.provinceSn = this.form.provinceSn ? this.form.provinceSn : undefined
+          this.form.citySn = this.form.citySn ? this.form.citySn : undefined
+          setTimeout(() => {
+            this.form.accountNameSn = res.data.accountNameSn
+            if (res.data.accountType == 'CUSTOMER') {
+              this.$refs.custList.setDufaultVal(res.data.accountName)
+            }
+          }, 200)
+          this.spinning = false
+        })
+      })
+    },
+    // 确定
+    handleSave (e) {
+      e.preventDefault()
+      const _this = this
+      this.$refs.ruleForm.validate(valid => {
+        if (valid) {
+          const form = JSON.parse(JSON.stringify(_this.form))
+          const cdfList = this.$refs.epenseCdf.getList()
+          if (cdfList[0] == 1) {
+            this.$warning({
+              title: '是否存在以下情况,请按照要求填写',
+              content: <div><p>1、费用承担方至少有一个</p><p>2、带星号的列为必填项,不能为空</p></div>
+            })
+            return
+          }
+          if (cdfList[0] == 3) {
+            this.$warning({
+              title: '提示',
+              content: '请检查每行承担人员是否已经选择'
+            })
+            return
+          }
+          if (cdfList[0] == 2) {
+            this.$warning({
+              title: '提示',
+              content: '请检查每行人员平摊费用合计是否等于承担金额'
+            })
+            return
+          }
+          // 获取承担人列表数据
+          form.detailSplitList = cdfList[1]
+          form.provinceName = form.provinceName ? form.provinceName : ''
+          form.provinceSn = form.provinceSn ? form.provinceSn : ''
+          form.citySn = form.citySn ? form.citySn : ''
+          form.cityName = form.cityName ? form.cityName : ''
+          console.log(form)
+          delete form.subareaNames
+          delete form.subareaSns
+          _this.spinning = true
+          expenseAcctDetailSave(form).then(res => {
+            if (res.status == 200) {
+              _this.$message.success(res.message)
+              setTimeout(() => {
+                _this.$emit('ok')
+                _this.spinning = false
+              }, 200)
+            } else {
+              _this.spinning = false
+            }
+          })
+        } else {
+          return false
+        }
+      })
+    },
+    resetForm () {
+      this.$nextTick(() => {
+        this.$refs.ruleForm.resetFields()
+      })
+      this.form = {
+        id: undefined,
+        accountType: undefined, // 记帐类型
+        accountNameSn: undefined, //  记账名称
+        subareaSnList: undefined, //  所属区域
+        subareaNameList: undefined,
+        provinceSn: undefined, // 省份
+        provinceName: '',
+        citySn: undefined, // 市
+        cityName: '',
+        remarks: '', // 备注
+        expense: 0, // 记账费用
+        expenseAccountSn: '', // 报销单SN
+        expenseAccountNo: '', // 报销单号
+        expenseAccountDetailSn: '', // 报销明细sn
+        detailSplitList: []
+      }
+      this.form.expenseAccountSn = this.expenseAccountSn
+      this.form.expenseAccountNo = this.expenseAccountNo
+    }
+  },
+  watch: {
+    'form.accountType' (newValue, oldValue) {
+      this.form.accountNameSn = undefined
+      this.$refs.custList.dealerName = []
+    },
+    //  父页面传过来的弹框状态
+    openModal (newValue, oldValue) {
+      this.isShow = newValue
+    },
+    //  重定义的弹框状态
+    isShow (newValue, oldValue) {
+      if (!newValue) {
+        this.$emit('close')
+        this.resetForm()
+      } else {
+        this.form.expenseAccountSn = this.expenseAccountSn
+        this.form.expenseAccountNo = this.expenseAccountNo
+        if (this.expenseAccountDetailSn) {
+          this.title = '编辑'
+          this.form.expenseAccountDetailSn = this.expenseAccountDetailSn || ''
+          this.getExpenseAccountDetail()
+        } else {
+          this.resetForm()
+          this.title = '新增'
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  .epenseDetailModal-modal{
+    .ant-modal-body {
+      padding: 30px 40px 24px;
+    }
+    .btn-cont {
+      text-align: center;
+      margin: 35px 0 10px;
+    }
+  }
+</style>

+ 201 - 0
src/views/expenseManagement/expenseReimbursement/importGuideModal.vue

@@ -0,0 +1,201 @@
+<template>
+  <a-modal
+    centered
+    class="importGuide-modal"
+    :footer="null"
+    :maskClosable="false"
+    title="导入费用明细"
+    v-model="isShow"
+    @cancle="isShow=false"
+    :width="800">
+    <div class="importGuide-con">
+      <div class="explain-con">
+        <div class="explain-item">
+          <div class="explain-tit">
+            <span>1</span>准备导入
+          </div>
+          <ul>
+            <li>1) 导入的Excel文件中必须包含名为“记账类型”、“记账名称”、“费用承担方类型”、“费用承担方”、“费用承担金额”的列,且名称必须相同</li>
+            <li>2) 其他列可根据实际情况填写,如果符合规则,系统会一起导入</li>
+          </ul>
+          <a-button type="link" icon="download" style="padding: 0 0 0 23px;" @click="handleExport">下载导入模板</a-button>
+        </div>
+        <div class="explain-item">
+          <div class="explain-tit">
+            <span>2</span>上传数据文件
+          </div>
+          <p>目前支持的文件类型*.xls,*.xlsx.</p>
+          <div style="overflow: hidden;padding-left: 23px;">
+            <Upload
+              id="importGuide-attachList"
+              ref="importUpload"
+              :maxNums="1"
+              fileType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
+              uploadType="attach"
+              :action="attachAction"
+              :uploadParams="uploadParams"
+              upText="选择导入文件"
+              @remove="resetSearchForm"
+              @change="changeImport"></Upload>
+          </div>
+        </div>
+      </div>
+      <!-- 按钮 -->
+      <div class="btn-con">
+        <a-button
+          type="primary"
+          id="importGuide-nextStep"
+          size="large"
+          class="button-primary"
+          @click="handlerNextStep"
+          style="padding: 0 60px;">下一步</a-button>
+        <a-button
+          id="importGuide-cancel"
+          size="large"
+          class="button-cancel"
+          @click="isShow=false"
+          style="padding: 0 60px;margin-left: 15px;">取消</a-button>
+      </div>
+    </div>
+    <!-- 导入 -->
+    <chooseImportModal :openModal="openImportModal" :paramsData="paramsData" @close="handleClose" @ok="hanldeOk" />
+  </a-modal>
+</template>
+
+<script>
+import ChooseImportModal from './chooseImportModal.vue'
+import { Upload } from '@/components'
+export default {
+  name: 'ImportGuideModal',
+  components: { ChooseImportModal, Upload },
+  props: {
+    openModal: { //  弹框显示状态
+      type: Boolean,
+      default: false
+    },
+    params: {
+      type: Object,
+      default: null
+    }
+  },
+  data () {
+    return {
+      isShow: this.openModal, //  是否打开弹框
+      openImportModal: false, //  导入
+      attachAction: process.env.VUE_APP_API_BASE_URL + '/expenseAcctDetail/importDetail',
+      paramsData: null,
+      uploadParams: {
+        savePathType: 'local'
+      }
+    }
+  },
+  methods: {
+    // 清空数据
+    resetSearchForm () {
+      this.$refs.importUpload.setFileList() //  清空导入文件
+      this.paramsData = null //  清空上传数据
+    },
+    // 下一步
+    handlerNextStep () {
+      if (this.paramsData) {
+        this.openImportModal = true
+      } else {
+        this.$message.warning('添加文件后再进行下一步操作!')
+      }
+    },
+    // 上传文件  change
+    changeImport (file) {
+      console.log(file, JSON.parse(file))
+      if (file && JSON.parse(file).length > 0) {
+        this.paramsData = Object.assign({}, JSON.parse(file)[0] || null, this.params)
+      } else {
+        this.paramsData = null
+      }
+      console.log(this.paramsData)
+    },
+    // 导入
+    hanldeOk (obj) {
+      if (obj && obj.length > 0) {
+        this.$emit('ok', obj)
+        this.isShow = false
+      }
+    },
+    // 关闭
+    handleClose () {
+      this.openImportModal = false
+      this.isShow = false
+    },
+    // 下载模板
+    handleExport () {
+      const link = document.createElement('a')
+      link.style.display = 'none'
+      link.href = 'https://jg-ocs.oss-cn-beijing.aliyuncs.com/templ/promo/expenseAccountDetailImport.xlsx'
+      link.setAttribute('download', '费用明细模板' + '.xlsx')
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+    }
+  },
+  watch: {
+    //  父页面传过来的弹框状态
+    openModal (newValue, oldValue) {
+      this.isShow = newValue
+    },
+    //  重定义的弹框状态
+    isShow (newValue, oldValue) {
+      if (!newValue) {
+        this.$emit('close')
+        this.resetSearchForm()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+  .importGuide-modal{
+    .importGuide-con{
+      .explain-con{
+        .explain-item{
+          margin-bottom: 10px;
+          .explain-tit{
+            font-weight: bold;
+            span{
+              display: inline-block;
+              width: 18px;
+              height: 18px;
+              line-height: 16px;
+              text-align: center;
+              margin-right: 5px;
+              border-radius: 50%;
+              border: 1px solid rgba(0, 0, 0, 0.3);
+            }
+          }
+          p{
+            margin: 8px 0;
+            padding-left: 23px;
+          }
+          ul{
+            list-style: none;
+            padding: 0;
+            padding-left: 23px;
+            margin: 0;
+            li{
+              line-height: 26px;
+            }
+          }
+        }
+        #importGuide-attachList{
+          width: 100%;
+        }
+      }
+      .btn-con{
+        margin: 10px 0 20px;
+        text-align: center;
+        .button-cancel{
+          font-size: 12px;
+        }
+      }
+    }
+  }
+</style>

+ 311 - 0
src/views/expenseManagement/expenseReimbursement/list.vue

@@ -0,0 +1,311 @@
+<template>
+  <a-card size="small" :bordered="false" class="salesManagementList-wrap">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <!-- 搜索条件 -->
+      <div ref="tableSearch" class="table-page-search-wrapper">
+        <a-form layout="inline">
+          <a-row :gutter="15">
+            <a-col :md="6" :sm="24">
+              <a-form-item label="费用单号" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                <a-input id="salesManagementList-expenseAccountNo" v-model.trim="queryParam.expenseAccountNo" allowClear placeholder="请输入销售单号"/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="费用类型">
+                <v-select
+                  v-model="queryParam.expenseType"
+                  ref="expenseType"
+                  id="salesManagementList-expenseType"
+                  code="EXPENSE_TYPE"
+                  placeholder="请选择费用类型"
+                  allowClear></v-select>
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="费用发生月份">
+                <a-month-picker
+                  placeholder="请选择月份"
+                  locale="zh-cn"
+                  v-model="queryParam.expenseDate"
+                  @change="onChange"
+                  :disabled-date="disabledDate"
+                  style="width: 100%;"/>
+              </a-form-item>
+            </a-col>
+            <template v-if="advanced">
+              <a-col :md="6" :sm="24">
+                <a-form-item label="主题">
+                  <a-input id="salesManagementList-title" v-model.trim="queryParam.title" allowClear placeholder="请输入主题"/>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="状态">
+                  <v-select
+                    v-model="queryParam.state"
+                    ref="state"
+                    id="salesManagementList-state"
+                    code="EXPENSE_STATE"
+                    placeholder="请选择状态"
+                    allowClear></v-select>
+                </a-form-item>
+              </a-col>
+            </template>
+            <a-col :md="6" :sm="24">
+              <span class="table-page-search-submitButtons">
+                <a-button type="primary" :disabled="disabled" @click="$refs.table.refresh(true)">查询</a-button>
+                <a-button style="margin-left: 8px" :disabled="disabled" @click="resetSearchForm()">重置</a-button>
+                <a @click="advanced=!advanced" style="margin-left: 8px">
+                  {{ advanced ? '收起' : '展开' }}
+                  <a-icon :type="advanced ? 'up' : 'down'"/>
+                </a>
+              </span>
+            </a-col>
+          </a-row>
+        </a-form>
+      </div>
+      <!-- 操作按钮 -->
+      <div class="table-operator">
+        <a-button type="primary" class="button-error" @click="handleEdit()">新增</a-button>
+      </div>
+      <!-- 列表 -->
+      <s-table
+        class="sTable fixPagination"
+        ref="table"
+        :style="{ height: tableHeight+70.5+'px' }"
+        size="small"
+        :rowKey="(record) => record.id"
+        :columns="columns"
+        :data="loadData"
+        :scroll="{ y: tableHeight }"
+        :defaultLoadData="false"
+        bordered>
+        <!-- 销售单号 -->
+        <template slot="expenseAccountNo" slot-scope="text, record">
+          <div style="color: #00aaff;cursor: pointer;" @click="handleDetail(record)">{{ record.expenseAccountNo||'--' }}</div>
+        </template>
+        <!-- 总数量 -->
+        <template slot="totalQty" slot-scope="text, record">
+          {{ record.totalQty }}
+        </template>
+        <!-- 操作 -->
+        <template slot="action" slot-scope="text, record">
+          <a-button
+            v-if="record.state=='WAIT_SUBMIT'"
+            size="small"
+            type="link"
+            class="button-info"
+            @click="handleEdit(record)"
+          >
+            编辑
+          </a-button>
+          <a-button
+            v-if="record.state=='WAIT_SUBMIT'"
+            size="small"
+            type="link"
+            class="button-error"
+            @click="handleDel(record)"
+          >
+            删除
+          </a-button>
+          <a-button
+            v-if="record.state!='WAIT_SUBMIT'"
+            size="small"
+            type="link"
+            class="button-success"
+            @click="handleSH(record)"
+          >
+            审核进度
+          </a-button>
+          <!-- <span v-if="record.state=='AUDIT_REJECT' || record.state=='AUDIT_PASS'">--</span> -->
+        </template>
+      </s-table>
+    </a-spin>
+    <!-- 审核进度 -->
+    <verifyModal :openModal="openModal" :itemSn="itemSn" @close="openModal=false"></verifyModal>
+    <!-- 新增 -->
+    <baseDataModal :show="openBaseModal" @close="openBaseModal=false"></baseDataModal>
+  </a-card>
+</template>
+
+<script>
+import locale from 'ant-design-vue/es/date-picker/locale/zh_CN'
+import moment from 'moment'
+import subarea from '@/views/common/subarea.js'
+import { STable, VSelect } from '@/components'
+import verifyModal from './verifyModal.vue'
+import { expenseAccountList, expenseAccountDelete } from '@/api/expenseManagement'
+import baseDataModal from './baseDataModal.vue'
+export default {
+  name: 'TableList',
+  components: { STable, VSelect, subarea, verifyModal, baseDataModal },
+  data () {
+    return {
+      locale,
+      spinning: false,
+      advanced: false, // 高级搜索 展开/关闭
+      disabled: false, //  查询、重置按钮是否可操作
+      openModal: false, // 审核进度弹框是否显示
+      openBaseModal: false, // 基础信息新建
+      itemSn: '', // 费用单sn
+      tableHeight: 0,
+      // 查询参数
+      queryParam: {
+        expenseDate: undefined, // 月份
+        expenseAccountNo: '', // 费用单号
+        title: '', //  主题
+        expenseType: undefined, // 费用类型
+        state: undefined // 状态
+      },
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        this.disabled = true
+        this.spinning = true
+        delete parameter.tableId
+        delete parameter.index
+        const params = Object.assign(parameter, this.queryParam)
+        if (params.expenseDate) {
+          params.expenseDate = params.expenseDate + '-01'
+        }
+        return expenseAccountList(params).then(res => {
+          let data
+          if (res.status == 200) {
+            data = res.data
+            const no = (data.pageNo - 1) * data.pageSize
+            for (var i = 0; i < data.list.length; i++) {
+              data.list[i].no = no + i + 1
+            }
+            this.disabled = false
+          }
+          this.spinning = false
+          return data
+        })
+      },
+      columns: [{ title: '序号', dataIndex: 'no', width: '4%', align: 'center' },
+        { title: '费用单号', width: '12%', align: 'center', scopedSlots: { customRender: 'expenseAccountNo' } },
+        { title: '申请人', dataIndex: 'applyPersonName', width: '8%', align: 'left', customRender: function (text) { return text || '--' }, ellipsis: true },
+        { title: '申请部门', dataIndex: 'applyDepartmentName', width: '8%', align: 'left', customRender: function (text) { return text || '--' } },
+        { title: '费用类型', dataIndex: 'expenseTypeDictValue', width: '6%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用发生月份', dataIndex: 'expenseDate', width: '6%', align: 'center', customRender: function (text) { return moment(text).format('YYYY年MM月') || '--' } },
+        { title: '主题', dataIndex: 'title', width: '10%', align: 'left', customRender: function (text) { return text || '--' }, ellipsis: true },
+        { title: '合计金额', dataIndex: 'applyExpenseTotal', width: '8%', align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '创建时间', dataIndex: 'createDate', width: '8%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '审核时间', dataIndex: 'finishDate', width: '8%', align: 'center', customRender: function (text, record) { return record.state == 'AUDIT_REJECT' || record.state == 'AUDIT_PASS' ? text : '--' } },
+        { title: '状态', dataIndex: 'stateDictValue', width: '6%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '操作', scopedSlots: { customRender: 'action' }, width: '10%', align: 'center' }]
+    }
+  },
+  methods: {
+    onChange (date, dateString) {
+      this.queryParam.expenseDate = dateString
+    },
+    disabledDate (current) {
+      return current && current > moment().endOf('day')
+    },
+    // 详情
+    handleDetail (row) {
+      this.$router.push({ name: 'expenseReimbursementListDetail', params: { sn: row.expenseAccountSn } })
+    },
+    // 新增、编辑
+    handleEdit (row) {
+      if (row && row.expenseAccountSn) {
+        this.$router.push({ name: 'expenseReimbursementEdit', params: { sn: row.expenseAccountSn } })
+      } else {
+        this.openBaseModal = true
+      }
+    },
+    // 删除
+    handleDel (row) {
+      const _this = this
+      this.$confirm({
+        title: '提示',
+        content: '确认要删除吗?',
+        centered: true,
+        closable: true,
+        onOk () {
+          _this.spinning = true
+          expenseAccountDelete({ expenseAccountSn: row.expenseAccountSn }).then(res => {
+            if (res.status == 200) {
+              _this.$message.success(res.message)
+              _this.$refs.table.refresh()
+              _this.spinning = false
+            } else {
+              _this.spinning = false
+            }
+          })
+        }
+      })
+    },
+    // 审核进度
+    handleSH (row) {
+      this.openModal = true
+      this.itemSn = row.expenseAccountSn
+    },
+    // 重置
+    resetSearchForm () {
+      this.queryParam = {
+        expenseDate: '', // 月份
+        expenseAccountNo: '', // 费用单号
+        title: '', //  主题
+        expenseType: undefined, // 费用类型
+        state: undefined // 状态
+      }
+      this.$refs.table.refresh(true)
+    },
+    pageInit () {
+      const _this = this
+      this.$nextTick(() => { // 页面渲染完成后的回调
+        _this.setTableH()
+      })
+    },
+    setTableH () {
+      const tableSearchH = this.$refs.tableSearch.offsetHeight
+      this.tableHeight = window.innerHeight - tableSearchH - 235
+    }
+  },
+  watch: {
+    advanced (newValue, oldValue) {
+      const _this = this
+      this.$nextTick(() => { // 页面渲染完成后的回调
+        _this.setTableH()
+      })
+    },
+    '$store.state.app.winHeight' (newValue, oldValue) { //  窗口变更时,需同时更改表格高度
+      this.setTableH()
+    }
+  },
+  mounted () {
+    if (!this.$store.state.app.isNewTab) { // 页签刷新时调用
+      this.pageInit()
+      this.resetSearchForm()
+    }
+  },
+  activated () {
+    // 如果是新页签打开,则重置当前页面
+    if (this.$store.state.app.isNewTab) {
+      this.pageInit()
+      this.resetSearchForm()
+    }
+    // 仅刷新列表,不重置页面
+    if (this.$store.state.app.updateList) {
+      this.pageInit()
+      this.$refs.table.refresh()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {})
+  }
+}
+</script>
+<style lang="less">
+  .salesManagementList-wrap{
+    .sTable{
+      margin-top: 10px;
+      .badge-con-t{
+        .ant-badge-count{
+          transform: scale(0.8);
+          font-size: 13px;
+        }
+      }
+    }
+  }
+</style>

+ 185 - 0
src/views/expenseManagement/expenseReimbursement/previewModal.vue

@@ -0,0 +1,185 @@
+<template>
+  <!-- <a-modal :footer="null" v-model="isShow" width="850px"> -->
+  <div class="container-print-view" style="width:100%;font-size: 12pt;position: fixed;font-family: auto;left:10000000px;padding:0;margin:0 auto;" id="printTest">
+    <p class="container-title" style="text-align: center;font-size: 24px;font-weight: 600;">箭冠汽配企业费用报销单</p>
+    <div class="container-info" style="width: 100%;border: 1px solid #000000;">
+      <div class="container-info-basics" v-if="expenseInfo">
+        <div class="basics-item" style="width: 100%;border-bottom: 1px solid #000000;padding:0;margin:0;">
+          <span style="display: inline-block;width: 50%;padding: 10px;border-right: 1px solid #000000;">申请人:{{ expenseInfo&&expenseInfo.applyPersonName?expenseInfo.applyPersonName:'' }}</span>
+          <span style="display: inline-block;border-right: none;padding: 10px;">申请部门:{{ expenseInfo&&expenseInfo.applyDepartmentName?expenseInfo.applyDepartmentName:'' }}</span>
+        </div>
+        <div class="basics-item" style="width: 100%;border-bottom: 1px solid #000000;padding:0;margin:0;">
+          <span style="display: inline-block;width: 50%;padding: 10px;border-right: 1px solid #000000;">费用类型:{{ expenseInfo&&expenseInfo.expenseTypeDictValue?expenseInfo.expenseTypeDictValue:'' }}</span>
+          <span style="display: inline-block;border-right: none;padding: 10px;">费用发生月份:{{ expenseInfo&&expenseInfo.expenseDate?expenseInfo.expenseDate.substring(0, 7):'' }}</span>
+        </div>
+        <div class="basics-item" style="width: 100%;border-bottom: 1px solid #000000;padding:0;margin:0;">
+          <span style="display: inline-block;width: 50%;padding: 10px;border-right: 1px solid #000000;">费用单号:{{ expenseInfo&&expenseInfo.expenseAccountNo?expenseInfo.expenseAccountNo:'' }}{{ expenseInfo&&expenseInfo.stateDictValue?'('+expenseInfo.stateDictValue+')':'' }}</span>
+          <span style="display: inline-block;border-right: none;padding: 10px;">合计金额:{{ (expenseInfo&&expenseInfo.applyExpenseTotal ||expenseInfo&&expenseInfo.applyExpenseTotal==0)?expenseInfo.applyExpenseTotal+'元':'' }}</span>
+        </div>
+        <div class="basics-item" style="width: 100%;border-bottom: 1px solid #000000;overflow: hidden;">
+          <div style="border-right: none;float: left;padding: 10px 0 10px 10px;">主题:</div>
+          <div style="padding: 10px;text-align: center;font-weight: bold;">
+            {{ expenseInfo&&expenseInfo.title?expenseInfo.title:'' }}
+          </div>
+        </div>
+        <div class="basics-item" style="width: 100%;border-bottom: 1px solid #000000;padding:0;margin:0;">
+          <span style="display: inline-block;width: 50%;padding: 10px;border-right: 1px solid #000000;border-right: none;">详细说明:</span>
+          <div style="padding:0 20px 20px;">
+            <div v-for="(item,index) in expenseInfo.content" :key="index+'-content'">
+              {{ item || '--' }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="container-fytable" style="padding:0 10px;" v-if="fyListData">
+        <p class="table-title">费用明细:</p>
+        <table
+          border="1"
+          cellspacing="0"
+          cellpadding="0"
+          style="width: 100%;border-collapse: collapse;border: 1px solid black;font-size:10pt;text-align: center;"
+          align="center">
+          <thead>
+            <tr>
+              <th style="padding:5px 10px;width:8%;">序号</th>
+              <th style="padding:5px 10px;width:17%;">记账方</th>
+              <th style="padding:5px 10px;width:15%;">区域/省/市</th>
+              <th style="padding:5px 10px;width:12%;">记账费用</th>
+              <th style="padding:5px 10px;width:28%;">费用承担方</th>
+              <th style="padding:5px 10px;width:20%;">产品信息</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(item,index) in fyListData" :key="index">
+              <td style="text-align: center;width:8%;">{{ index+1 }}</td>
+              <td style="text-align: left;width:17%;padding:5px 10px;">
+                <div>{{ item.accountTypeDictValue }}</div>
+                <div>{{ item.accountName||'' }}</div>
+                <!-- <div>{{ item.expenseAccountNo }}</div> -->
+              </td>
+              <td style="text-align: left;width:15%;padding:5px 10px;">
+                <div>{{ item.subareaNames }}</div>
+                <div>{{ item.provinceName }}</div>
+                <div>{{ item.cityName }}</div>
+              </td>
+              <td style="text-align: right;width:12%;padding:5px 10px;">{{ item.expense||item.expense==0?item.expense.toFixed(2):'' }}</td>
+              <td style="text-align: left;width:28%;padding:5px 10px;vertical-align: top;">
+                <div v-for="(ditem,index) in item.detailSplitList" :key="ditem.id+'-'+index" style="padding: 3px 0;" >
+                  {{ ditem.splitObjName }}
+                  <span v-if="ditem.childDetailSplit&&ditem.childDetailSplit.splitObjName">/</span>
+                  {{ ditem.childDetailSplit?ditem.childDetailSplit.splitObjName:'' }}: {{ ditem.childDetailSplit?ditem.childDetailSplit.splitAmount:ditem.splitAmount }}元
+                </div>
+              </td>
+              <td style="text-align: left;width:20%;padding:5px 10px;vertical-align: top;">
+                <div v-for="ditem in item.detailProductsList" :key="ditem.id" style="padding: 3px 0;">
+                  <div v-if="ditem.productCode">{{ ditem.productCode }}: {{ ditem.expense }}元</div>
+                  <div v-else-if="ditem.productBrandName">{{ ditem.productBrandName }}<span v-if="ditem.productTypeShowName">/</span>{{ ditem.productTypeShowName }}: {{ ditem.expense.toFixed(2) }}元</div>
+                  <div v-else>{{ ditem.productTypeShowName }}: {{ ditem.expense }}元</div>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="container-fytable" style="padding:0 10px 10px;" v-if="spListData">
+        <p class="table-title">审批信息:</p>
+        <table
+          border="1"
+          cellspacing="0"
+          cellpadding="0"
+          style="width: 100%;border-collapse: collapse;border: 1px solid black;font-size:10pt;text-align: center;"
+          align="center">
+          <thead>
+            <tr style="height: 30px;padding: 5px;">
+              <th style="padding:5px 10px;width:8%;">序号</th>
+              <th style="padding:5px 10px;width:15%;text-align: left;">人员姓名</th>
+              <th style="padding:5px 10px;width:15%;">人员类型</th>
+              <th style="padding:5px 10px;width:10%;">状态</th>
+              <th style="padding:5px 10px;width:17%;">审批意见</th>
+              <th style="padding:5px 10px;width:15%;">附件</th>
+              <th style="padding:5px 10px;width:20%;">时间</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr style="height: 30px;padding: 5px;" v-for="(item,index) in spListData" :key="index">
+              <td style="padding:5px 10px;">{{ index+1||'' }}</td>
+              <td style="padding:5px 10px;text-align: left;">{{ item.userName||'' }}</td>
+              <td style="padding:5px 10px;">{{ item.userType||'' }}</td>
+              <td style="padding:5px 10px;">{{ item.state||'' }}</td>
+              <td style="padding:5px 10px;">{{ item.remark||'' }}</td>
+              <td style="padding:5px 10px;">{{ item.hasAttachments!= '0' ? '请在钉钉中查看附件内容' : '--' }}</td>
+              <td style="padding:5px 10px;">{{ item.date||'' }}</td>
+            </tr></tbody>
+        </table>
+      </div>
+    </div>
+  </div>
+  <!-- </a-modal> -->
+</template>
+
+<script>
+import { expenseAcctDetailFindList, getProcessInstance } from '@/api/expenseManagement'
+export default {
+  props: {
+    showModal: {
+      type: Boolean,
+      default: false
+    },
+    detailSn: {
+      type: [Number, String],
+      default: ''
+    },
+    detailData: {
+      type: Object,
+      default: () => { return null }
+    }
+  },
+  data () {
+    return {
+      isShow: this.showModal,
+      fyListData: [],
+      spListData: [],
+      expenseInfo: null
+    }
+  },
+  methods: {
+    pageInfo () {
+      this.getFyList()
+      if (this.expenseInfo.state != 'WAIT_SUBMIT') {
+        this.getSpList()
+      }
+    },
+    getFyList () {
+      expenseAcctDetailFindList({ expenseAccountSn: this.detailSn }).then(res => {
+        if (res.status == 200) {
+          this.fyListData = res.data
+        } else {
+          this.fyListData = []
+        }
+      })
+    },
+    getSpList () {
+      getProcessInstance({ businessType: 'EXPENSES', businessSn: this.detailSn }).then(res => {
+        if (res.status == 200) {
+          this.spListData = res.data
+        } else {
+          this.spListData = []
+        }
+      })
+    }
+  },
+  // mounted () {
+  //   this.pageInfo()
+  // },
+  watch: {
+    detailData (nVal, oVal) {
+      if (nVal) {
+        this.expenseInfo = nVal
+        this.fyListData = []
+        this.spListData = []
+        this.pageInfo()
+      }
+    }
+  }
+}
+</script>

+ 331 - 0
src/views/expenseManagement/expenseReimbursement/productInfoModal.vue

@@ -0,0 +1,331 @@
+<template>
+  <a-modal
+    centered
+    class="productInfoModal-modal"
+    :footer="null"
+    :maskClosable="false"
+    title="新增产品信息"
+    v-model="isShow"
+    @cancle="isShow=false"
+    width="60%">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <div>
+        <a-descriptions v-if="baseData">
+          <a-descriptions-item label="记账类型">
+            {{ baseData.accountTypeDictValue }}
+          </a-descriptions-item>
+          <a-descriptions-item label="记账名称">
+            {{ baseData.accountName }}
+          </a-descriptions-item>
+          <a-descriptions-item label="记账费用">
+            ¥{{ baseData.expense }}
+          </a-descriptions-item>
+        </a-descriptions>
+        <div class="productInfoModal-con">
+          <!-- 列表 -->
+          <a-table
+            class="sTable fixPagination"
+            ref="table"
+            :scroll="{ y: 500 }"
+            size="small"
+            :rowKey="(record) => record.id"
+            :columns="columns"
+            :data-source="dataList"
+            :pagination="false"
+            bordered>
+            <div slot="customTitle"><span style="color: red;">*</span>费用金额</div>
+            <template slot="action" slot-scope="text,record,index">
+              <a-button
+                size="small"
+                type="link"
+                class="button-error"
+                @click="handleDelRow(record,index)"
+              >删除</a-button>
+            </template>
+            <!-- 产品品牌 -->
+            <template slot="productBrand" slot-scope="text,record,index">
+              <ProductBrand style="width: 100%;" :disabled="!!record.productCode" :id="'productBrand-'+index" @change="productBrandChange" v-model="record.productBrandSn"></ProductBrand>
+            </template>
+            <!-- 产品分类 -->
+            <template slot="productType" slot-scope="text,record,index">
+              <ProductTypeAll
+                style="width: 100%;"
+                :disabled="!!record.productCode"
+                :ref="'productType-'+record.no"
+                :id="'productType-'+index"
+                v-model="record.productType"
+                @change="changeProductType"></ProductTypeAll>
+            </template>
+            <!-- 产品编码 -->
+            <template slot="productCode" slot-scope="text,record,index">
+              <productCodeList :ref="'productCode-'+record.no" :key="'productCode-'+index" :id="'productCode-'+index" @change="e => productCodeChange(e, record)" />
+            </template>
+            <!-- 费用金额 -->
+            <template slot="expense" slot-scope="text,record">
+              <a-input-number
+                style="width: 100%;"
+                v-model="record.expense"
+                :precision="2"
+                :min="0"
+                :max="999999"
+                placeholder="请输入"
+              />
+            </template>
+            <template slot="footer" slot-scope="currentPageData">
+              <a-button
+                type="link"
+                class="button-info"
+                block
+                @click="handleAddRow()"
+              >+ 新增产品信息</a-button>
+            </template>
+          </a-table>
+        </div>
+        <div class="btn-cont">
+          <a-button type="primary" id="productInfoModal-save" @click="handleSave">确定</a-button>
+          <a-button id="productInfoModal-back" @click="isShow = false" style="margin-left: 15px;">取消</a-button>
+        </div>
+      </div>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+import { VSelect, STable } from '@/components'
+import ProductTypeAll from '@/views/common/productTypeAll.js'
+import ProductBrand from '@/views/common/productBrand.js'
+import productCodeList from '@/views/common/productCodeList.vue'
+import { expenseAcctDetailSaveProducts, expenseAcctDetailFindProductsBySn } from '@/api/expenseManagement.js'
+export default {
+  components: { VSelect, STable, ProductTypeAll, ProductBrand, productCodeList },
+  name: 'ProductInfoModal',
+  props: {
+    openModal: { //  弹框显示状态
+      type: Boolean,
+      default: false
+    },
+    expenseAccountSn: { // 报销单sn
+      type: String,
+      default: ''
+    },
+    expenseAccountNo: { // 报销单号
+      type: String,
+      default: ''
+    },
+    expenseAccountDetailSn: {
+      type: String,
+      default: ''
+    }
+  },
+  data () {
+    return {
+      spinning: false,
+      isShow: this.openModal, //  是否打开弹框
+      dataList: [],
+      baseData: null,
+      columns: [
+        { title: '产品品牌', scopedSlots: { customRender: 'productBrand' }, align: 'center', width: '20%' },
+        { title: '产品分类', scopedSlots: { customRender: 'productType' }, align: 'center', width: '30%' },
+        { title: '产品编码', scopedSlots: { customRender: 'productCode' }, align: 'center', width: '20%' },
+        { slots: { title: 'customTitle' }, scopedSlots: { customRender: 'expense' }, align: 'center', width: '20%' },
+        { title: '操作', scopedSlots: { customRender: 'action' }, width: '10%', align: 'center' }
+      ]
+    }
+  },
+  methods: {
+    // 产品编码
+    productCodeChange (val, row) {
+      console.log(val, row)
+      row.productSn = val.key || undefined
+      row.productCode = val.row && val.row.code ? val.row.code : undefined
+      row.productName = val.row && val.row.name ? val.row.name : undefined
+      row.productBrandSn = val.row && val.row.productBrandSn ? val.row.productBrandSn : undefined
+      row.productBrandName = val.row && val.row.productBrandName ? val.row.productBrandName : undefined
+      row.productTypeSn1 = val.row && val.row.productTypeSn1 ? val.row.productTypeSn1 : undefined
+      row.productTypeSn2 = val.row && val.row.productTypeSn2 ? val.row.productTypeSn2 : undefined
+      row.productTypeSn3 = val.row && val.row.productTypeSn3 ? val.row.productTypeSn3 : undefined
+      row.productTypeName1 = val.row && val.row.productTypeName1 ? val.row.productTypeName1 : undefined
+      row.productTypeName2 = val.row && val.row.productTypeName2 ? val.row.productTypeName2 : undefined
+      row.productTypeName3 = val.row && val.row.productTypeName3 ? val.row.productTypeName3 : undefined
+      // 设置默认值
+      const defval = row.productTypeSn1 || row.productTypeSn2 || row.productTypeSn3 ? [row.productTypeSn1, row.productTypeSn2, row.productTypeSn3] : []
+      this.$set(row, 'productType', defval)
+      this.$nextTick(() => {
+        this.$refs['productType-' + row.no].setDefValue(defval)
+      })
+    },
+    // 产品品牌
+    productBrandChange (val, id, option) {
+      const index = id.split('-')[1]
+      const row = this.dataList[index]
+      row.productBrandName = option && option.brandName ? option.brandName : undefined
+    },
+    //  产品分类  change
+    changeProductType (val, id, options) {
+      console.log(val, id, options)
+      const index = id.split('-')[1]
+      const row = this.dataList[index]
+      row.productTypeSn1 = val[0] ? val[0] : ''
+      row.productTypeSn2 = val[1] ? val[1] : ''
+      row.productTypeSn3 = val[2] ? val[2] : ''
+      if (options) {
+        row.productTypeName1 = options[0] ? options[0].productTypeName : ''
+        row.productTypeName2 = options[1] ? options[1].productTypeName : ''
+        row.productTypeName3 = options[2] ? options[2].productTypeName : ''
+      }
+    },
+    // 添加
+    handleAddRow () {
+      this.dataList.push({
+        'no': new Date().getTime(),
+        'expense': 0,
+        'expenseAccountDetailSn': this.expenseAccountDetailSn,
+        'expenseAccountSn': this.expenseAccountSn,
+        'productBrandName': '',
+        'productBrandSn': undefined,
+        'productCode': undefined,
+        'productName': '',
+        'productSn': '',
+        'productType': [],
+        'productTypeName1': '',
+        'productTypeName2': '',
+        'productTypeName3': '',
+        'productTypeSn1': '',
+        'productTypeSn2': '',
+        'productTypeSn3': ''
+      })
+    },
+    // 删除
+    handleDelRow (record, index) {
+      console.log(record, this.$refs)
+      this.dataList.splice(index, 1)
+      this.dataList.map(item => {
+        this.$nextTick(() => {
+          const refDom = this.$refs['productCode-' + item.no]
+          if (refDom) {
+            refDom.dealerName = ''
+            refDom.setDufaultVal(item.productCode || '')
+          }
+        })
+      })
+    },
+    // 确定
+    handleSave () {
+      // 校验数据
+      const retList = JSON.parse(JSON.stringify(this.dataList))
+      console.log(retList)
+      if (retList.length == 0) {
+        this.$message.info('请添加产品信息')
+        return
+      }
+      let hasError = 0
+      retList.map(item => {
+        if (!item.expense) {
+          hasError = 1
+        }
+        if (!(item.productTypeSn1 || item.productCode || item.productBrandSn)) {
+          hasError = 2
+        }
+        delete item.productType
+        delete item.no
+      })
+      if (hasError == 1) {
+        this.$message.info('请输入费用金额')
+        return
+      }
+      if (hasError == 2) {
+        this.$message.info('产品品牌、产品分类、产品编码,三个至少填一个')
+        return
+      }
+      this.spinning = true
+      expenseAcctDetailSaveProducts(retList).then(res => {
+        this.spinning = false
+        if (res.status == 200) {
+          this.$message.success(res.message)
+          this.$emit('ok', res.data)
+          this.isShow = false
+        }
+      })
+    },
+    // 设置基础数据
+    setBaseData (data) {
+      this.baseData = data
+    },
+    // 查询详情
+    getDetail () {
+      expenseAcctDetailFindProductsBySn({ detailSn: this.expenseAccountDetailSn }).then(res => {
+        if (res.data && res.data.length) {
+          this.dataList = res.data
+          this.dataList.map(item => {
+            item.no = item.id
+            this.$nextTick(() => {
+              if (item.productTypeSn1 || item.productTypeSn2 || item.productTypeSn3) {
+                item.productType = [item.productTypeSn1, item.productTypeSn2, item.productTypeSn3]
+                this.$refs['productType-' + item.no].setDefValue([item.productTypeSn1, item.productTypeSn2, item.productTypeSn3])
+              }
+              if (item.productCode) {
+                this.$refs['productCode-' + item.no].setDufaultVal(item.productCode)
+              }
+            })
+          })
+        } else {
+          this.dataList = [{
+            'no': new Date().getTime(),
+            'expense': 0,
+            'expenseAccountDetailSn': this.expenseAccountDetailSn,
+            'expenseAccountSn': this.expenseAccountSn,
+            'productBrandName': '',
+            'productBrandSn': undefined,
+            'productCode': '',
+            'productName': '',
+            'productSn': '',
+            'productType': [],
+            'productTypeName1': '',
+            'productTypeName2': '',
+            'productTypeName3': '',
+            'productTypeSn1': '',
+            'productTypeSn2': '',
+            'productTypeSn3': ''
+          }]
+        }
+      })
+    }
+  },
+  watch: {
+    //  父页面传过来的弹框状态
+    openModal (newValue, oldValue) {
+      this.isShow = newValue
+    },
+    //  重定义的弹框状态
+    isShow (newValue, oldValue) {
+      if (!newValue) {
+        this.dataList = []
+        this.baseData = null
+        this.$emit('close')
+      } else {
+        this.getDetail()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="less">
+  .productInfoModal-modal{
+    .ant-modal-body {
+      padding: 30px 40px 24px;
+    }
+    .productInfoModal-con{
+      text-align: center;
+      h3{
+        font-size: 16px;
+        font-weight: bold;
+        margin-bottom: 25px;
+      }
+    }
+    .btn-cont {
+      text-align: center;
+      margin: 35px 0 10px;
+    }
+  }
+</style>

+ 98 - 0
src/views/expenseManagement/expenseReimbursement/verifyModal.vue

@@ -0,0 +1,98 @@
+<template>
+  <a-modal
+    centered
+    :footer="null"
+    :maskClosable="false"
+    title="费用报销单审核进度"
+    v-model="isShow"
+    @cancle="isShow=false"
+    width="50%">
+    <p>说明:以下信息来自钉钉</p>
+    <a-spin :spinning="spinning" tip="Loading...">
+      <s-table
+        class="sTable fixPagination"
+        ref="table"
+        size="small"
+        :rowKey="(record) => record.id"
+        :columns="columns"
+        :data="loadData"
+        :scroll="{ y: 500 }"
+        :showPagination="false"
+        :defaultLoadData="true"
+        bordered>
+      </s-table>
+    </a-spin>
+  </a-modal>
+</template>
+
+<script>
+import { STable } from '@/components'
+import { getProcessInstance } from '@/api/expenseManagement'
+export default {
+  name: 'VerifyModal',
+  components: { STable },
+  props: {
+    openModal: {
+      type: Boolean,
+      default: false
+    },
+    itemSn: {
+      type: [Number, String],
+      default: ''
+    }
+  },
+  data () {
+    return {
+      spinning: false,
+      isShow: this.openModal,
+      columns: [{ title: '序号', dataIndex: 'no', width: '8%', align: 'center' },
+        { title: '人员姓名', width: '12%', dataIndex: 'userName', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '人员类型', dataIndex: 'userType', width: '15%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '状态', dataIndex: 'state', width: '10%', align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '审批意见', dataIndex: 'remark', width: '20%', align: 'center', customRender: function (text) { return text || '--' }, ellipsis: true },
+        { title: '附件', dataIndex: 'hasAttachments', width: '20%', align: 'center', customRender: function (text) { return text != '0' ? '请在钉钉中查看附件内容' : '--' } },
+        { title: '操作时间', dataIndex: 'date', width: '20%', align: 'center', customRender: function (text) { return text || '--' } }],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        this.spinning = true
+        delete parameter.tableId
+        delete parameter.index
+        return getProcessInstance({ businessType: 'EXPENSES', businessSn: this.itemSn }).then(res => {
+          let data
+          if (res.status == 200) {
+            data = res.data
+            const no = 0
+            for (var i = 0; i < data.length; i++) {
+              data[i].no = no + i + 1
+            }
+            this.disabled = false
+          }
+          this.spinning = false
+          return data
+        })
+      }
+    }
+  },
+  watch: {
+    openModal (nVal, oVal) {
+      this.isShow = nVal
+    },
+    isShow (nVal, oVal) {
+      if (!nVal) {
+        this.$emit('close')
+        this.$refs.table.clearTable()
+      } else {
+        this.$nextTick(() => {
+          this.$refs.table.refresh()
+        })
+      }
+    },
+    itemSn (nVal, oVal) {
+
+    }
+  }
+}
+</script>
+
+<style>
+</style>

+ 398 - 0
src/views/expenseManagement/expenseReimbursementDetail/list.vue

@@ -0,0 +1,398 @@
+<template>
+  <a-card size="small" :bordered="false" class="expenseReimbursementDetailList-wrap">
+    <a-spin :spinning="spinning" tip="Loading...">
+      <!-- 搜索条件 -->
+      <div ref="tableSearch" class="table-page-search-wrapper">
+        <a-form layout="inline" ref="ruleForm">
+          <a-row :gutter="15">
+            <a-col :md="6" :sm="24">
+              <a-form-item label="审核时间">
+                <rangeDate ref="rangeDate" :value="time" @change="dateChange" />
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="费用单号" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                <a-input id="expenseReimbursementDetailList-expenseAccountNo" v-model.trim="queryParam.expenseAccountNo" allowClear placeholder="请输入销售单号"/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="申请人" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                <employee style="width: 100%;" id="expenseReimbursementDetailList-applyPersonSn" @change="employeeChange" v-model="queryParam.applyPersonSn"></employee>
+              </a-form-item>
+            </a-col>
+            <template v-if="advanced">
+              <a-col :md="6" :sm="24">
+                <a-form-item label="申请部门" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                  <department style="width: 100%;" id="expenseReimbursementDetailList-applyDepartmentSn" @change="applyDepartmentChange" v-model="queryParam.applyDepartmentSn"></department>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="费用类型">
+                  <v-select
+                    v-model="queryParam.expenseType"
+                    ref="expenseType"
+                    id="expenseReimbursementDetailList-expenseType"
+                    code="EXPENSE_TYPE"
+                    placeholder="请选择费用类型"
+                    allowClear></v-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="费用发生月份">
+                  <a-month-picker
+                    placeholder="请选择月份"
+                    v-model="queryParam.expenseDate"
+                    @change="onChange"
+                    :disabled-date="disabledDate"
+                    style="width: 100%;"/>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="记账类型">
+                  <v-select
+                    v-model="queryParam.accountType"
+                    ref="accountType"
+                    id="expenseReimbursementDetailList-accountType"
+                    code="EXPENSE_ACCOUNT_TYPE"
+                    placeholder="请选择状态"
+                    allowClear></v-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="记账名称">
+                  <!-- 部门 -->
+                  <department v-show="queryParam.accountType=='INNER_ACCOUNT'" @change="accountNameSnChange" v-model="queryParam.accountNameSn" placeholder="请选择记账名称(输入名称搜索)"></department>
+                  <!-- 经销商 -->
+                  <custList v-show="queryParam.accountType=='CUSTOMER'" ref="custList" @change="accountNameSnChange" placeholder="请选择记账名称(输入名称搜索)"></custList>
+                  <!-- 供应商 -->
+                  <supplier v-show="queryParam.accountType=='OUT_ACCOUNT'" @change="accountNameSnChange" v-model="queryParam.accountNameSn" placeholder="请选择记账名称(输入名称搜索)"></supplier>
+                  <a-select style="width: 100%" v-if="!queryParam.accountType" placeholder="请选择">
+                    <a-select-option value="" :disabled="true">
+                      请先选择记账类型
+                    </a-select-option>
+                  </a-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="所属区域">
+                  <subarea id="expenseReimbursementDetailList-subarea" v-model="queryParam.subareaSn"></subarea>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-model-item label="所属省份">
+                  <Area id="expenseReimbursementDetailList-area" placeholder="请选择所属省份" v-model="queryParam.provinceSn"></Area>
+                </a-form-model-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-model-item label="所属城市">
+                  <Area id="expenseReimbursementDetailList-area" placeholder="请选择所属城市" leve="city" :parentId="queryParam.provinceSn" v-model="queryParam.citySn"></Area>
+                </a-form-model-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="费用承担方类型" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                  <v-select
+                    code="EXPENSE_ACCOUNT_DETAIL_SPLIT_OBJ_TYPE"
+                    allowClear
+                    style="width: 100%;"
+                    v-model="queryParam.splitObjType"
+                    @change="splitObjTypeChange"
+                    placeholder="请选择承担方类型"
+                  ></v-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24">
+                <a-form-item label="费用承担方" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                  <!-- 部门 -->
+                  <department v-show="queryParam.splitObjType=='DEPARTMENT'" style="width: 100%;" placeholder="请选择承担方" v-model="queryParam.splitObjSn"></department>
+                  <!-- 经销商 -->
+                  <custList v-show="queryParam.splitObjType=='CUSTOMER'" ref="custList" @change="custChange" placeholder="请选择(输入名称搜索)"></custList>
+                  <a-select style="width: 100%" v-if="!queryParam.splitObjType" placeholder="请选择">
+                    <a-select-option value="" :disabled="true">
+                      请先选择承担方类型
+                    </a-select-option>
+                  </a-select>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24" v-show="queryParam.splitObjType=='DEPARTMENT'">
+                <a-form-item label="费用承担人" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                  <employee style="width: 100%;" placeholder="请选择承担人员" v-model="queryParam.childSplitObjSn"></employee>
+                </a-form-item>
+              </a-col>
+              <a-col :md="6" :sm="24" v-show="queryParam.splitObjType==undefined">
+                <a-form-item label="费用承担人" :label-col="{ span:7 }" :wrapper-col="{ span:17}">
+                  <a-select style="width: 100%" placeholder="请选择">
+                    <a-select-option value="" :disabled="true">
+                      请先选择承担方类型
+                    </a-select-option>
+                  </a-select>
+                </a-form-item>
+              </a-col>
+            </template>
+            <a-col :md="6" :sm="24">
+              <span class="table-page-search-submitButtons">
+                <a-button type="primary" :disabled="disabled" @click="$refs.table.refresh(true)">查询</a-button>
+                <a-button style="margin-left: 8px" :disabled="disabled" @click="resetSearchForm()">重置</a-button>
+                <a-button
+                  style="margin-left: 10px"
+                  type="primary"
+                  class="button-warning"
+                  @click="handleExport"
+                  :disabled="disabled"
+                  :loading="exportLoading"
+                  id="actualSalesReportList-export">导出</a-button>
+                <a @click="advanced=!advanced" style="margin-left: 8px">
+                  {{ advanced ? '收起' : '展开' }}
+                  <a-icon :type="advanced ? 'up' : 'down'"/>
+                </a>
+              </span>
+            </a-col>
+          </a-row>
+        </a-form>
+      </div>
+      <!-- 列表 -->
+      <s-table
+        class="sTable fixPagination"
+        ref="table"
+        :style="{ height: tableHeight+70+'px' }"
+        size="small"
+        :rowKey="(record) => record.id"
+        :columns="columns"
+        :data="loadData"
+        :scroll="{ y: tableHeight,x:1770 }"
+        :defaultLoadData="false"
+        bordered>
+      </s-table>
+    </a-spin>
+  </a-card>
+</template>
+
+<script>
+import moment from 'moment'
+import 'moment/locale/zh-cn'
+import getDate from '@/libs/getDate.js'
+import rangeDate from '@/views/common/rangeDate.vue'
+import subarea from '@/views/common/subarea.js'
+import { STable, VSelect } from '@/components'
+import Area from '@/views/common/area.js'
+import department from '../expenseReimbursement/department.js'
+import employee from '../expenseReimbursement/employee.js'
+import custList from '@/views/common/custList.vue'
+import supplier from '@/views/common/supplier.js'
+import { expenseAccountDetailList, expenseAcctDetailReport } from '@/api/expenseManagement'
+import { hdExportExcel } from '@/libs/exportExcel'
+moment.locale('zh-cn')
+export default {
+  name: 'TableList',
+  components: { STable, VSelect, department, employee, subarea, rangeDate, Area, custList, supplier },
+  data () {
+    return {
+      spinning: false,
+      advanced: true, // 高级搜索 展开/关闭
+      disabled: false, //  查询、重置按钮是否可操作
+      openModal: false, // 选择客户弹框是否显示
+      exportLoading: false,
+      tableHeight: 0,
+      time: [
+        moment(getDate.getCurrMonthDays().starttime, 'YYYY-MM-DD'),
+        moment(getDate.getCurrMonthDays().endtime, 'YYYY-MM-DD')
+      ],
+      // 查询参数
+      queryParam: {
+        expenseDate: undefined, // 月份
+        beginDate: getDate.getCurrMonthDays().starttime,
+        endDate: getDate.getCurrMonthDays().endtime,
+        expenseAccountNo: '', // 费用单号
+        applyPersonSn: undefined, // 申请人
+        applyDepartmentSn: undefined, // 申请部门
+        expenseType: undefined, //  费用类型
+        accountType: undefined, // 记账类型
+        accountNameSn: undefined, // 记账名称
+        splitObjType: undefined, // 费用承担方类型
+        splitObjSn: undefined, //  费用承担方
+        childSplitObjSn: undefined, // 费用承担人
+        subareaSn: undefined,
+        provinceName: '',
+        cityName: '',
+        provinceSn: undefined,
+        citySn: undefined
+      },
+      addrProvinceList: [], // 省列表
+      addrCityList: [], // 城市列表数据
+      // 加载数据方法 必须为 Promise 对象
+      loadData: parameter => {
+        this.disabled = true
+        this.spinning = true
+        delete parameter.tableId
+        delete parameter.index
+        const params = Object.assign(parameter, this.queryParam)
+        if (params.expenseDate) {
+          params.expenseDate = params.expenseDate + '-01'
+        }
+        return expenseAccountDetailList(params).then(res => {
+          let data
+          if (res.status == 200) {
+            data = res.data
+            const no = (data.pageNo - 1) * data.pageSize
+            for (var i = 0; i < data.list.length; i++) {
+              data.list[i].no = no + i + 1
+            }
+            this.disabled = false
+          }
+          this.spinning = false
+          return data
+        })
+      },
+      columns: [{ title: '序号', dataIndex: 'no', width: 60, align: 'center' },
+        { title: '审核时间', dataIndex: 'finishDate', width: 120, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用单号', dataIndex: 'expenseAccountNo', width: 120, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '申请人', dataIndex: 'applyPersonName', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '申请部门', dataIndex: 'applyDepartmentName', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用类型', dataIndex: 'expenseTypeDictValue', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '费用发生月份', dataIndex: 'expenseDate', width: 90, align: 'center', customRender: function (text) { return moment(text).format('YYYY年MM月') || '--' } },
+        { title: '主题', dataIndex: 'title', align: 'center', width: 150, customRender: function (text) { return text || '--' }, ellipsis: true },
+        { title: '记账类型', dataIndex: 'accountTypeDictValue', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '记账名称', dataIndex: 'accountName', width: 120, align: 'center', customRender: function (text) { return text || '--' }, ellipsis: true },
+        { title: '所属区域', dataIndex: 'subareaNames', width: 120, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '所属省份', dataIndex: 'provinceName', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '所属城市', dataIndex: 'cityName', width: 90, align: 'center', customRender: function (text) { return text || '--' } },
+        { title: '记账费用', dataIndex: 'expense', width: 120, align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '费用承担方', dataIndex: 'splitObjName', width: 120, align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '费用承担金额', dataIndex: 'splitAmount', width: 120, align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '费用承担人', dataIndex: 'childSplitObjName', width: 120, align: 'center', customRender: function (text) { return ((text || text == 0) ? text : '--') } },
+        { title: '人员平摊费用', dataIndex: 'childSplitAmount', width: 120, align: 'center', customRender: function (text) { return text || '--' } }
+      ]
+    }
+  },
+  methods: {
+    //  时间  change
+    dateChange (date) {
+      this.queryParam.beginDate = date[0]
+      this.queryParam.endDate = date[1]
+    },
+    onChange (date, dateString) {
+      this.queryParam.expenseDate = dateString
+    },
+    custChange (val) {
+      this.queryParam.splitObjSn = val.key
+    },
+    disabledDate (current) {
+      return current && current > moment().endOf('day')
+    },
+    employeeChange (v) {
+      this.queryParam.employeeSn = v.key
+    },
+    applyDepartmentChange (v) {
+      this.queryParam.applyDepartmentSn = v.key
+    },
+    // 记账名称变化时
+    accountNameSnChange (v, row) {
+      console.log(v, row)
+      if (v) {
+        this.queryParam.accountNameSn = v.key
+      }
+    },
+    splitObjTypeChange (v) {
+      this.queryParam.splitObjSn = undefined
+      this.queryParam.childSplitObjSn = undefined
+      this.$refs.custList.dealerName = []
+    },
+    //  导出
+    handleExport () {
+      const _this = this
+      const params = JSON.parse(JSON.stringify(_this.queryParam))
+      if (params.expenseDate) {
+        params.expenseDate = params.expenseDate + '-01'
+      }
+      _this.exportLoading = true
+      _this.spinning = true
+      hdExportExcel(expenseAcctDetailReport, params, '费用报销明细统计报表', function () {
+        _this.exportLoading = false
+        _this.spinning = false
+      })
+    },
+    // 重置
+    resetSearchForm () {
+      this.$refs.rangeDate.resetDate(this.time)
+      this.queryParam.beginDate = getDate.getCurrMonthDays().starttime
+      this.queryParam.endDate = getDate.getCurrMonthDays().endtime
+      this.queryParam = {
+        queryParam: {
+          expenseDate: '', // 月份
+          beginDate: getDate.getCurrMonthDays().starttime,
+          endDate: getDate.getCurrMonthDays().endtime,
+          expenseAccountNo: '', // 费用单号
+          applyPersonSn: undefined, // 申请人
+          applyDepartmentSn: undefined, // 申请部门
+          expenseType: undefined, //  费用类型
+          accountType: undefined, // 记账类型
+          accountNameSn: undefined, // 记账名称
+          splitObjType: undefined, // 费用承担方类型
+          splitObjSn: undefined, //  费用承担方
+          childSplitObjSn: undefined, // 费用承担人
+          subareaSn: undefined,
+          provinceName: '',
+          cityName: '',
+          provinceSn: undefined,
+          citySn: undefined
+        }
+      }
+      this.$refs.table.refresh(true)
+    },
+    pageInit () {
+      const _this = this
+      this.$nextTick(() => { // 页面渲染完成后的回调
+        _this.setTableH()
+      })
+    },
+    setTableH () {
+      const tableSearchH = this.$refs.tableSearch.offsetHeight
+      this.tableHeight = window.innerHeight - tableSearchH - 195
+    }
+  },
+  watch: {
+    advanced (newValue, oldValue) {
+      const _this = this
+      this.$nextTick(() => { // 页面渲染完成后的回调
+        _this.setTableH()
+      })
+    },
+    '$store.state.app.winHeight' (newValue, oldValue) { //  窗口变更时,需同时更改表格高度
+      this.setTableH()
+    }
+  },
+  mounted () {
+    if (!this.$store.state.app.isNewTab) { // 页签刷新时调用
+      this.pageInit()
+      this.resetSearchForm()
+    }
+  },
+  activated () {
+    // 如果是新页签打开,则重置当前页面
+    if (this.$store.state.app.isNewTab) {
+      this.pageInit()
+      this.resetSearchForm()
+    }
+    // 仅刷新列表,不重置页面
+    if (this.$store.state.app.updateList) {
+      this.pageInit()
+      this.$refs.table.refresh()
+    }
+  },
+  beforeRouteEnter (to, from, next) {
+    next(vm => {})
+  }
+}
+</script>
+<style lang="less">
+  .expenseReimbursementDetailList-wrap{
+    .sTable{
+      margin-top: 10px;
+      .badge-con-t{
+        .ant-badge-count{
+          transform: scale(0.8);
+          font-size: 13px;
+        }
+      }
+    }
+  }
+</style>

+ 19 - 22
src/views/productManagement/productInfo/edit.vue

@@ -93,27 +93,23 @@
             </a-col>
             <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
               <a-form-model-item label="包装数" prop="packQty" :label-col="{span:4}" :wrapper-col="{span:16}">
-                <a-row>
-                  <a-col :span="14">
-                    <a-input-number
-                      id="productInfoEdit-packQty"
-                      v-model="form.packQty"
-                      :precision="0"
-                      :min="1"
-                      :max="999999"
-                      placeholder="请输入"
-                      style="width: 70%;" /><span>{{ form.unit }}/</span>
-                  </a-col>
-                  <a-col :span="10">
-                    <v-select
-                      code="PRODUCT_PACK_UNIT"
-                      id="productLevelEdit-packQtyUnit"
-                      v-model="form.packQtyUnit"
-                      allowClear
-                      placeholder="包装数单位"
-                    ></v-select>
-                  </a-col>
-                </a-row>
+                <a-input-number
+                  id="productInfoEdit-packQty"
+                  v-model="form.packQty"
+                  :precision="0"
+                  :min="1"
+                  :max="999999"
+                  placeholder="请输入"
+                />
+                <span style="margin: 0 5px;">/</span>
+                <v-select
+                  code="PRODUCT_PACK_UNIT"
+                  id="productLevelEdit-packQtyUnit"
+                  v-model="form.packQtyUnit"
+                  allowClear
+                  style="width: 100px;"
+                  placeholder="单位"
+                ></v-select>
               </a-form-model-item>
             </a-col>
             <a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
@@ -215,7 +211,7 @@
         id="productInfoEdit-submit-btn"
         size="large"
         @click="handleSubmit"
-        style="padding: 0 60px;">提交</a-button>
+        style="padding: 0 60px;">保存</a-button>
     </div>
   </div>
 </template>
@@ -442,6 +438,7 @@ export default {
       this.$refs.editor.setHtml('')
       this.$refs.otherDesc.setHtml('')
       this.$refs.ruleForm.resetFields()
+      this.form.packQtyUnit = undefined
       this.getProductBrand()
       this.getProductType()
       if (this.$route.params.id) { //  编辑页