lilei 3 tahun lalu
induk
melakukan
c832ce0896

+ 10 - 1
src/api/allocateReturn.js

@@ -15,6 +15,14 @@ export const allocateReturnDelete = params => {
     method: 'post'
   })
 }
+// 品检 提交
+export const allocateReturnCheckSubmit = params => {
+  return axios({
+    url: '/allocateReturn/check',
+    data: params,
+    method: 'post'
+  })
+}
 // 财务审核调拨退货
 export const allocateReturnFinanceAudit = params => {
   return axios({
@@ -35,7 +43,8 @@ export const allocateReturnExcel = params => {
 export const allocateReturnPrint = params => {
   return axios({
     url: `/allocateReturn/print/${params.allocateReturnSn}`,
-    method: 'get'
+    method: 'get',
+    responseType: 'blob'
   })
 }
 // 查询调拨退货详情

+ 2 - 0
src/components/MultiTab/MultiTab.vue

@@ -64,6 +64,7 @@ export default {
       if (!this.tabsList.includes(this.activeKey)) {
         this.selectedLastPath()
       }
+      this.$store.state.app.closeTabPages.push(targetKey)
     },
     selectedLastPath () {
       this.activeKey = this.tabsList[this.tabsList.length - 1]
@@ -201,6 +202,7 @@ export default {
           } else {
             // this.$message.info('您当前打开窗口太多,请关闭一些,窗口不能超过' + this.maxTabNums + '个')
             const _this = this
+            this.$store.state.app.closeTabPages.push(newVal.name)
             this.$warning({
               title: '提示',
               content: '您当前打开窗口太多,请关闭一些,窗口不能超过' + this.maxTabNums + '个',

+ 2 - 1
src/layouts/BasicLayout.vue

@@ -41,7 +41,8 @@
       <!-- layout content -->
       <a-layout-content :style="{ height: '100%', margin: '31px 20px 0', paddingTop: multiTab&&fixedHeader ? '61px' : (!multiTab&&fixedHeader?'33px':'0') }">
         <transition name="page-transition">
-          <route-view v-if="isRouterAlive"/>
+          <!-- <route-view v-if="isRouterAlive"/> -->
+          <router-view ></router-view>
         </transition>
       </a-layout-content>
 

+ 3 - 1
src/layouts/BlankLayout.vue

@@ -1,6 +1,8 @@
 <template>
   <div>
-    <router-view />
+    <keep-alive>
+      <router-view :key="$route.name"></router-view>
+    </keep-alive>
   </div>
 </template>
 

+ 17 - 78
src/layouts/PageView.vue

@@ -1,51 +1,10 @@
 <template>
   <div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null">
-    <!-- pageHeader , route meta :true on hide -->
-    <!-- <page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar"> -->
-    <page-header v-if="false" :title="pageTitle" :logo="logo" :avatar="avatar">
-      <slot slot="action" name="action"></slot>
-      <slot slot="content" name="headerContent"></slot>
-      <div slot="content" v-if="!this.$slots.headerContent && description">
-        <p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p>
-        <div class="link">
-          <template v-for="(link, index) in linkList">
-            <a :key="index" :href="link.href">
-              <a-icon :type="link.icon" />
-              <span>{{ link.title }}</span>
-            </a>
-          </template>
-        </div>
-      </div>
-      <slot slot="extra" name="extra">
-        <div class="extra-img">
-          <img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/>
-        </div>
-      </slot>
-      <div slot="pageMenu">
-        <div class="page-menu-search" v-if="search">
-          <a-input-search
-            style="width: 80%; max-width: 522px;"
-            placeholder="请输入..."
-            size="large"
-            enterButton="搜索"
-          />
-        </div>
-        <div class="page-menu-tabs" v-if="tabs && tabs.items">
-          <!-- @change="callback" :activeKey="activeKey" -->
-          <a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback">
-            <a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane>
-          </a-tabs>
-        </div>
-      </div>
-    </page-header>
+    <PageHeader v-if="!multiTab"></PageHeader>
     <div class="content" :style="{ height: pageHeight+'px' }">
       <div class="page-header-index-wide">
         <slot>
-          <!-- keep-alive  -->
-          <keep-alive v-if="multiTab">
-            <router-view ref="content" />
-          </keep-alive>
-          <router-view v-else ref="content" />
+          <router-view ref="content" />
         </slot>
       </div>
     </div>
@@ -53,9 +12,9 @@
 </template>
 
 <script>
+import PageHeader from '@/components/PageHeader/PageHeader.vue'
 import { mapState } from 'vuex'
-import PageHeader from '@/components/PageHeader'
-
+import { on, off } from '@/libs/tools'
 export default {
   name: 'PageView',
   components: {
@@ -92,47 +51,27 @@ export default {
   },
   computed: {
     ...mapState({
-      multiTab: state => state.app.multiTab
+      multiTab: state => state.app.multiTab,
+      closeTabPages: state => state.app.closeTabPages
     })
-    // contHeight () {
-    //   return window.innerHeight - 80
-    // }
   },
   mounted () {
     this.tabs = this.directTabs
-    this.getPageMeta()
     this.setpageH()
-    const _this = this
-    window.onresize = () => {
-      return (() => {
-        _this.$store.dispatch('SetWinHeight', window.innerHeight)
-        _this.setpageH()
-      })()
-    }
+    on(window, 'resize', this.setpageH)
+  },
+  beforeDestroy () {
+    off(window, 'resize', this.setpageH)
   },
-  updated () {
-    this.getPageMeta()
+  activated () {
+    this.setpageH()
   },
   methods: {
     setpageH () {
-      this.pageHeight = window.innerHeight - 80
-    },
-    getPageMeta () {
-      // eslint-disable-next-line
-      this.pageTitle = (typeof(this.title) === 'string' || !this.title) ? this.title : this.$route.meta.title
-
-      const content = this.$refs.content
-      if (content) {
-        if (content.pageMeta) {
-          Object.assign(this, content.pageMeta)
-        } else {
-          this.description = content.description
-          this.linkList = content.linkList
-          this.extraImage = content.extraImage
-          this.search = content.search === true
-          this.tabs = content.tabs
-        }
-      }
+      this.$nextTick(() => {
+        this.$store.dispatch('SetWinHeight', window.innerHeight)
+        this.pageHeight = window.innerHeight - 85
+      })
     }
   }
 }
@@ -140,7 +79,7 @@ export default {
 
 <style lang="less" scoped>
   .content {
-    margin: 12px 12px 0;
+    margin: 8px 12px 0;
     .page-header-index-wide{
       height: 100%;
       >div{

+ 454 - 0
src/libs/tools.js

@@ -3,3 +3,457 @@ export const getOperationalPrecision = (num1, num2) => {
   const val = ((num1 * 10000) * (num2 * 10000) / 100000000).toFixed(2) || 0
   return val != 0 ? val : 0
 }
+export const forEach = (arr, fn) => {
+  if (!arr.length || !fn) return
+  let i = -1
+  const len = arr.length
+  while (++i < len) {
+    const item = arr[i]
+    fn(item, i, arr)
+  }
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的交集, 两个数组的元素为数值或字符串
+ */
+export const getIntersection = (arr1, arr2) => {
+  const len = Math.min(arr1.length, arr2.length)
+  let i = -1
+  const res = []
+  while (++i < len) {
+    const item = arr2[i]
+    if (arr1.indexOf(item) > -1) res.push(item)
+  }
+  return res
+}
+
+/**
+ * @param {Array} arr1
+ * @param {Array} arr2
+ * @description 得到两个数组的并集, 两个数组的元素为数值或字符串
+ */
+export const getUnion = (arr1, arr2) => {
+  return Array.from(new Set([...arr1, ...arr2]))
+}
+
+/**
+ * @param {Array} target 目标数组
+ * @param {Array} arr 需要查询的数组
+ * @description 判断要查询的数组是否至少有一个元素包含在目标数组中
+ */
+export const hasOneOf = (targetarr, arr) => {
+  if (!targetarr) return true
+  if (!arr) return true
+  return targetarr.some(_ => arr.indexOf(_) > -1)
+}
+
+/**
+ * @param {String|Number} value 要验证的字符串或数值
+ * @param {*} validList 用来验证的列表
+ */
+export function oneOf (value, validList) {
+  for (let i = 0; i < validList.length; i++) {
+    if (value === validList[i]) {
+      return true
+    }
+  }
+  return false
+}
+
+/**
+ * @param {Number} timeStamp 判断时间戳格式是否是毫秒
+ * @returns {Boolean}
+ */
+const isMillisecond = timeStamp => {
+  const timeStr = String(timeStamp)
+  return timeStr.length > 10
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} currentTime 当前时间时间戳
+ * @returns {Boolean} 传入的时间戳是否早于当前时间戳
+ */
+const isEarly = (timeStamp, currentTime) => {
+  return timeStamp < currentTime
+}
+
+/**
+ * @param {Number} num 数值
+ * @returns {String} 处理后的字符串
+ * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0
+ */
+const getHandledValue = num => {
+  return num < 10 ? '0' + num : num
+}
+
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间
+ */
+const getDate = (timeStamp, startType) => {
+  const d = new Date(timeStamp * 1000)
+  const year = d.getFullYear()
+  const month = getHandledValue(d.getMonth() + 1)
+  const date = getHandledValue(d.getDate())
+  const hours = getHandledValue(d.getHours())
+  const minutes = getHandledValue(d.getMinutes())
+  const second = getHandledValue(d.getSeconds())
+  let resStr = ''
+  if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
+  else resStr = month + '-' + date + ' ' + hours + ':' + minutes
+  return resStr
+}
+/**
+ * @param {Number} timeStamp 传入的时间戳
+ * @param {Number} fmt 格式化字符串
+ */
+export const formtDate = (timeStamp, fmt) => {
+  const d = new Date(timeStamp)
+  var o = {
+    'M+': d.getMonth() + 1, // 月份
+    'd+': d.getDate(), // 日
+    'h+': d.getHours(), // 小时
+    'm+': d.getMinutes(), // 分
+    's+': d.getSeconds(), // 秒
+    'q+': Math.floor((d.getMonth() + 3) / 3), // 季度
+    'S': d.getMilliseconds() // 毫秒
+  }
+  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length))
+  for (var k in o) { if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) }
+  return fmt
+}
+//  获取三个月后的时间戳
+export const getThreeMonthsAfter = (dtstr) => {
+  var s = dtstr.split('-')
+  var yy = parseInt(s[0])
+  var mm = parseInt(s[1])
+  var dd = parseInt(s[2])
+  var dt = new Date(yy, mm + 2, dd)
+  return dt.valueOf()
+}
+/**
+ * @param {String|Number} timeStamp 时间戳
+ * @returns {String} 相对时间字符串
+ */
+export const getRelativeTime = timeStamp => {
+  // 判断当前传入的时间戳是秒格式还是毫秒
+  const IS_MILLISECOND = isMillisecond(timeStamp)
+  // 如果是毫秒格式则转为秒格式
+  if (IS_MILLISECOND) Math.floor(timeStamp /= 1000)
+  // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
+  timeStamp = Number(timeStamp)
+  // 获取当前时间时间戳
+  const currentTime = Math.floor(Date.parse(new Date()) / 1000)
+  // 判断传入时间戳是否早于当前时间戳
+  const IS_EARLY = isEarly(timeStamp, currentTime)
+  // 获取两个时间戳差值
+  let diff = currentTime - timeStamp
+  // 如果IS_EARLY为false则差值取反
+  if (!IS_EARLY) diff = -diff
+  let resStr = ''
+  const dirStr = IS_EARLY ? '前' : '后'
+  // 少于等于59秒
+  if (diff <= 59) resStr = diff + '秒' + dirStr
+  // 多于59秒,少于等于59分钟59秒
+  else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr
+  // 多于59分钟59秒,少于等于23小时59分钟59秒
+  else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr
+  // 多于23小时59分钟59秒,少于等于29天59分钟59秒
+  else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr
+  // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
+  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp)
+  else resStr = getDate(timeStamp, 'year')
+  return resStr
+}
+
+// 日期格式化
+export const formatSubmitDate = (val, type) => {
+  if (val == null || val == '' || val == undefined) {
+    return ''
+  } else {
+    const _date = new Date(val)
+    const _year = _date.getFullYear()
+    const _montn = (_date.getMonth() + 1) < 10 ? '0' + (_date.getMonth() + 1) : (_date.getMonth() + 1)
+    const _day = _date.getDate() < 10 ? '0' + _date.getDate() : _date.getDate()
+    const _hour = _date.getHours() < 10 ? '0' + _date.getHours() : _date.getHours()
+    const _minutes = _date.getMinutes() < 10 ? '0' + _date.getMinutes() : _date.getMinutes()
+    const _seconds = _date.getSeconds() < 10 ? '0' + _date.getSeconds() : _date.getSeconds()
+
+    if (type == 'minutes') return _year + '-' + _montn + '-' + _day + ' ' + _hour + ':' + _minutes
+    else if (type == 'seconds') return _year + '-' + _montn + '-' + _day + ' ' + _hour + ':' + _minutes + ':' + _seconds
+    else return _year + '-' + _montn + '-' + _day
+  }
+}
+// 正则验证车牌,验证通过返回true,不通过返回false
+export const isLicensePlate = function (str) {
+  return /^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(str)
+}
+// 车牌可输入字符
+export const isCarNumber = function (str) {
+  let _value = str + ''
+  _value = _value.replace(/[^\w\.挂学警港澳使领]/ig, '')
+
+  return _value
+}
+// 小数点后两位
+export const numberToFixed = function (val, num) {
+  let _value = val + ''
+  _value = _value.replace(/[^\d.]/g, '')// 清楚数字和.以外的字数
+  _value = _value.replace(/^\./g, '')
+  _value = _value.replace(/\.{2,}/g, '')// 保留第一个,清楚多余的
+
+  if (num == 1)_value = _value.replace(/^(\-)*(\d+)\.(\d).*$/, '$1$2.$3')
+  else if (num == 3)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d).*$/, '$1$2.$3')
+  else if (num == 4)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d\d).*$/, '$1$2.$3')
+  else if (num == 5)_value = _value.replace(/^(\-)*(\d+)\.(\d\d\d\d\d).*$/, '$1$2.$3')
+  else if (num == 0)_value = _value.replace(/^(\-)*(\d+)\.*$/, '$1$2')
+  else _value = _value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
+
+  return _value
+}
+// 保留decimal位小数(不四舍五入)  num 数值,decimal要保留的小数位数
+export const formatDecimal = function (num, decimal) {
+  num = num.toString()
+  const index = num.indexOf('.')
+  if (index !== -1) {
+    num = num.substring(0, decimal + index + 1)
+  } else {
+    num = num.substring(0)
+  }
+  return parseFloat(num).toFixed(decimal)
+}
+// 处理数字千位分隔符  num 数值,decimal要保留的小数位数
+export const toThousands = (num, decimal) => {
+  if (num == undefined) {
+    return '--'
+  }
+  if (decimal) {
+    num = formatDecimal(num, decimal)
+  }
+  return num.toString().replace(/\d+/, function (n) { // 先提取整数部分
+    return n.replace(/(\d)(?=(\d{3})+$)/g, function ($1) {
+      return $1 + ','
+    })
+  })
+}
+// 只能输入数字
+export const justNumber = function (val) {
+  let _value = val + ''
+  _value = _value.replace(/\D/g, '')
+
+  return _value
+}
+
+/**
+ * @returns {String} 当前浏览器名称
+ */
+export const getExplorer = () => {
+  const ua = window.navigator.userAgent
+  const isExplorer = (exp) => {
+    return ua.indexOf(exp) > -1
+  }
+  if (isExplorer('MSIE')) return 'IE'
+  else if (isExplorer('Firefox')) return 'Firefox'
+  else if (isExplorer('Chrome')) return 'Chrome'
+  else if (isExplorer('Opera')) return 'Opera'
+  else if (isExplorer('Safari')) return 'Safari'
+}
+
+/**
+ * @description 绑定事件 on(element, event, handler)
+ */
+export const on = (function () {
+  if (document.addEventListener) {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.addEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.attachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * @description 解绑事件 off(element, event, handler)
+ */
+export const off = (function () {
+  if (document.removeEventListener) {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.removeEventListener(event, handler, false)
+      }
+    }
+  } else {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.detachEvent('on' + event, handler)
+      }
+    }
+  }
+})()
+
+/**
+ * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
+ * 如果没有传入key这个参数,则判断obj对象是否有键值对
+ */
+export const hasKey = (obj, key) => {
+  if (key) return key in obj
+  else {
+    const keysArr = Object.keys(obj)
+    return keysArr.length
+  }
+}
+
+/**
+ * @param {*} obj1 对象
+ * @param {*} obj2 对象
+ * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
+ */
+export const objEqual = (obj1, obj2) => {
+  const keysArr1 = Object.keys(obj1)
+  const keysArr2 = Object.keys(obj2)
+  if (keysArr1.length !== keysArr2.length) return false
+  else if (keysArr1.length === 0 && keysArr2.length === 0) return true
+  /* eslint-disable-next-line */
+  else return !keysArr1.some(key => obj1[key] != obj2[key])
+}
+
+/*
+ * @param {*} id 数字
+ * @param {*} list 数组
+ * @description 根据id从数组列表中删除某一项
+*/
+export const removeListById = (id, list) => {
+  list.splice(list.findIndex(item => item.id === id), 1)
+}
+
+/**
+ * @param {*} obj1 对象
+ * @param {*} obj2 对象
+ * @description 遍历赋值
+*/
+export const objExtend = (obj1, obj2) => {
+  for (var a in obj1) {
+    obj2[a] = obj1[a]
+  }
+  return obj2
+}
+/**
+ * @param {*} obj 对象
+ * @description 浅拷贝
+*/
+export const cloneObj = (obj) => {
+  const ret = {}
+  for (var a in obj) {
+    ret[a] = obj[a]
+  }
+  return ret
+}
+
+/**
+ * 校验身份证号合法性
+ */
+export const checkIdNumberValid = (tex) => {
+  // var tip = '输入的身份证号有误,请检查后重新输入!'
+  let num = tex
+  num = num.toUpperCase()
+
+  const len = num.length
+  let re
+  if (len == 0) return true
+
+  // 身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X。
+  if (!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(num))) {
+    return false
+  }
+
+  // 验证前两位地区是否有效
+  const aCity = { 11: '北京',
+    12: '天津',
+    13: '河北',
+    14: '山西',
+    15: '内蒙古',
+    21: '辽宁',
+    22: '吉林',
+    23: '黑龙江',
+    31: '上海',
+    32: '江苏',
+    33: '浙江',
+    34: '安徽',
+    35: '福建',
+    36: '江西',
+    37: '山东',
+    41: '河南',
+    42: '湖北',
+    43: '湖南',
+    44: '广东',
+    45: '广西',
+    46: '海南',
+    50: '重庆',
+    51: '四川',
+    52: '贵州',
+    53: '云南',
+    54: '西藏',
+    61: '陕西',
+    62: '甘肃',
+    63: '青海',
+    64: '宁夏',
+    65: '新疆',
+    71: '台湾',
+    81: '香港',
+    82: '澳门',
+    91: '国外' }
+
+  if (aCity[parseInt(num.substr(0, 2))] == null) {
+    return false
+  }
+
+  // 当身份证为15位时的验证出生日期。
+  if (len == 15) {
+    re = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/)
+    const arrSplit = num.match(re)
+
+    // 检查生日日期是否正确
+    const dtmBirth = new Date('19' + arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+    const bGoodDay = (dtmBirth.getYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4]))
+    if (!bGoodDay) {
+      return false
+    }
+  }
+
+  // 当身份证号为18位时,校验出生日期和校验位。
+  if (len == 18) {
+    re = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/)
+    const arrSplit = num.match(re)
+    // 检查生日日期是否正确
+    const dtmBirth = new Date(arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4])
+    const bGoodDay = (dtmBirth.getFullYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4]))
+    if (!bGoodDay) {
+      return false
+    } else {
+      // 检验18位身份证的校验码是否正确。
+      // 校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。
+      let valnum
+      const arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+      const arrCh = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
+      let nTemp = 0
+      let i
+      for (i = 0; i < 17; i++) {
+        nTemp += num.substr(i, 1) * arrInt[i]
+      }
+      valnum = arrCh[nTemp % 11]
+      if (valnum != num.substr(17, 1)) {
+        return false
+      }
+    }
+  }
+  return true
+}

+ 3 - 1
src/store/getters.js

@@ -13,7 +13,9 @@ const getters = {
   multiTab: state => state.app.multiTab,
   lang: state => state.i18n.lang,
   isNewTab: state => state.app.isNewTab,
-  isNewSubTab: state => state.app.isNewSubTab
+  isNewSubTab: state => state.app.isNewSubTab,
+  closeTabPages: state => state.app.closeTabPages,
+  loadingStatus: state => state.app.loadingStatus
 }
 
 export default getters

+ 7 - 2
src/store/modules/app.js

@@ -28,7 +28,9 @@ const app = {
     isNewTab: false,
     isNewSubTab: false,
     updateList: false,
-    winHeight: 0 //  窗口高度
+    winHeight: 0 ,//  窗口高度
+    loadingStatus: false,
+    closeTabPages: [], // 已关闭的页面
   },
   mutations: {
     SET_SIDEBAR_TYPE: (state, type) => {
@@ -81,7 +83,10 @@ const app = {
     },
     SET_WIN_HEIGHT: (state, val) => {
       state.winHeight = val
-    }
+    },
+    SET_loadingStatus: (state, val) => {
+      state.loadingStatus = val
+    },
   },
   actions: {
     setSidebar ({ commit }, type) {

+ 77 - 1
src/utils/mixin.js

@@ -75,4 +75,80 @@ const AppDeviceEnquire = {
   }
 }
 
-export { mixin, AppDeviceEnquire, mixinDevice }
+// 所有页
+const commonMixin = {
+  watch: {
+    '$store.state.app.loadingStatus': function (a, b) {
+      if (!a) {
+        if (this.disabled !== undefined) {
+          this.disabled = a
+        }
+        if (this.spinning !== undefined) {
+          this.spinning = a
+        }
+      }
+    }
+  },
+  methods: {
+    // 清除组件缓存
+    clearCompCache (cache, keys, key) {
+      if (cache[key] && cache[key].componentInstance) {
+        cache[key].componentInstance.$destroy()
+        if (keys.length) {
+          var index = keys.indexOf(key)
+          if (index > -1) {
+            keys.splice(index, 1)
+          }
+        }
+        delete cache[key]
+      }
+    }
+  },
+  beforeRouteLeave (to, from, next) {
+    const newVal = to
+    const oldVal = from
+    // 是否时同一个父级菜单下的页面
+    const mnlen = newVal.matched.length < 3
+    const newMatched = newVal.matched[mnlen ? 1 : 2]
+    const molen = oldVal.matched.length < 3
+    const oldMatched = oldVal.matched[molen ? 1 : 2]
+    const hasChildren = newMatched.name == oldMatched.name
+    // 判断是否是新增,编辑,详情页
+    const oa = oldVal.meta.title.indexOf('详情') >= 0
+    const ob = oldVal.meta.title.indexOf('新增') >= 0
+    const oc = oldVal.meta.title.indexOf('编辑') >= 0
+    const oxt = oldVal.meta.replaceTab
+    // console.log(hasChildren,oa||ob||oc||oxt)
+    const key = this.$vnode.key
+    const parentNode = this.$vnode.parent
+    const cache = parentNode && parentNode.componentInstance ? parentNode.componentInstance.cache : null
+    const keys = parentNode && parentNode.componentInstance ? parentNode.componentInstance.keys : null
+    const closeTabPages = this.$store.state.app.closeTabPages
+    const multiTab = this.$store.state.app.multiTab
+    // console.log(closeTabPages,key)
+    // 清空同级路由的组件缓存
+    if (hasChildren && (oa || ob || oc || oxt) && cache && keys) {
+      this.clearCompCache(cache, keys, key)
+      // 单页签模式
+      if (!multiTab) {
+        this.$store.state.app.isNewTab = false
+        this.$store.state.app.updateList = true
+      }
+    }
+    // 单页签模式,并且不是同级菜单的清空组件
+    if (!multiTab && !hasChildren) {
+      this.clearCompCache(cache, keys, key)
+    }
+    // 批量清空组件缓存
+    if (closeTabPages && closeTabPages.length && cache && keys) {
+      for (let i = 0; i < closeTabPages.length; i++) {
+        this.clearCompCache(cache, keys, closeTabPages[i])
+      }
+      this.$store.state.app.closeTabPages = []
+    }
+    // console.log(cache,keys)
+    next()
+  }
+}
+
+export { mixin, AppDeviceEnquire, mixinDevice, commonMixin }

+ 57 - 8
src/utils/request.js

@@ -1,11 +1,10 @@
-// import Vue from 'vue'
+import Vue from 'vue'
 import axios from 'axios'
 import store from '@/store'
 import notification from 'ant-design-vue/es/notification'
 import modal from 'ant-design-vue/es/modal'
 import { VueAxios } from './axios'
-// import { ACCESS_TOKEN } from '@/store/mutation-types'
-// console.log(process.env.VUE_APP_PRO_NAME)
+ 
 // 创建 axios 实例
 const service = axios.create({
   baseURL: process.env.VUE_APP_API_BASE_URL, // api base_url
@@ -13,26 +12,72 @@ const service = axios.create({
 })
 
 const err = (error) => {
-  console.log(error, 'error')
+  console.log(error, error.response,error.message,'error')
+  const config = error.config
+  // 网络连接出错
+  if (error.message.indexOf('Network Error') >= 0) {
+    notification.destroy()
+    notification.error({
+      message: '提示',
+      description: '无网络连接,请检查网络是否连接正常'
+    })
+    store.commit('SET_loadingStatus', false)
+    return Promise.reject(err)
+  }
+  
+  // 业务错误提示
   if (error.response) {
     const status = error.response.status
-    if ((status == 503 || status == 500) && window.location.pathname != '/user/login') {
+    if ((status == 503 || status == 500 || status == 404)) {
       notification.destroy()
       notification.error({
         message: '提示',
         description: error.response.data.message
       })
     }
+    store.commit('SET_loadingStatus', false)
+    return Promise.reject(err)
   }
+  
   // 超时无法访问服务
-  if (error.message.indexOf('timeout') >= 0 && window.location.pathname != '/user/login') {
+  if (error.message.indexOf('timeout') >= 0) {
     notification.destroy()
     notification.error({
       message: '提示',
-      description: '请求超时'
+      description: '网络连接超时,正在重连...'
+    })
+    // If config does not exist or the retry option is not set, reject
+    if (!config || !config.retry) return Promise.reject(err)
+    // Set the variable for keeping track of the retry count
+    config.__retryCount = Vue.ls.get(config.url + '__retryCount') || 0
+    // Check if we've maxed out the total number of retries
+    if (config.__retryCount >= config.retry) {
+      Vue.ls.remove(config.url + '__retryCount')
+      notification.destroy()
+      notification.error({
+        message: '提示',
+        description: '网络重连失败,请检测网络连接是否正常'
+      })
+      store.commit('SET_loadingStatus', false)
+      // Reject with the error
+      return Promise.reject(err)
+    }
+    // Increase the retry count
+    config.__retryCount += 1
+    Vue.ls.set(config.url + '__retryCount', config.__retryCount)
+    // Create new promise to handle exponential backoff
+    config.url = config.url.replace('/api', '')
+    const backoff = new Promise(function (resolve) {
+      setTimeout(function () {
+        resolve()
+      }, config.retryDelay || 1)
+    })
+  
+    // Return the promise in which recalls axios to retry the request
+    return backoff.then(function () {
+      return service(config)
     })
   }
-  return Promise.reject(error)
 }
 
 // request interceptor
@@ -41,6 +86,9 @@ service.interceptors.request.use(config => {
   if (token) {
     config.headers['access-token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
   }
+  config.retry = 3
+  config.retryDelay = 1000
+  store.commit('SET_loadingStatus', true)
   return config
 }, err)
 
@@ -76,6 +124,7 @@ service.interceptors.response.use((response) => {
       description: response.data.code
     })
   }
+  store.commit('SET_loadingStatus', false)
   return response.data
 }, err)
 

+ 3 - 1
src/views/allocationManagement/transferOut/basicInfoModal.vue

@@ -67,6 +67,7 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import moment from 'moment'
 import debounce from 'lodash/debounce'
 import { VSelect } from '@/components'
@@ -75,7 +76,8 @@ import { dealerSubareaScopeList } from '@/api/dealer'
 import { getLookUpData } from '@/api/data'
 import { allocateTypeAllList } from '@/api/allocateType'
 export default {
-  name: 'StoreTransferOutBasicInfoModal',
+  name: 'TransferOutBasicInfoModal',
+  mixins: [commonMixin],
   components: { VSelect },
   props: {
     openModal: {

+ 3 - 1
src/views/allocationManagement/transferOut/chooseImportModal.vue

@@ -66,10 +66,12 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { hdExportExcel } from '@/libs/exportExcel'
 import { allocateBillParseProducts, allocateBillFailExcel } from '@/api/allocateBill'
 export default {
-  name: 'ChooseImportModal',
+  name: 'TransferOutChooseImportModal',
+  mixins: [commonMixin],
   props: {
     openModal: { //  弹框显示状态
       type: Boolean,

+ 3 - 0
src/views/allocationManagement/transferOut/detail.vue

@@ -89,11 +89,14 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 import printModal from './printModal.vue'
 import { hdExportExcel } from '@/libs/exportExcel'
 import { allocateBillDetailList, allocateBillDetail, allocateBillDetailCount, allocateBillDetailPrint, allocateBillDetailExcel } from '@/api/allocateBill'
 export default {
+  name: 'TransferOutDetail',
+  mixins: [commonMixin],
   components: { STable, VSelect, printModal },
   props: {
     outBizSn: { //  有值则为弹框,无值则为页面

+ 3 - 0
src/views/allocationManagement/transferOut/edit.vue

@@ -225,6 +225,7 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 import ImportGuideModal from './importGuideModal.vue'
 import printModal from './printModal.vue'
@@ -235,6 +236,8 @@ import AllocateType from '@/views/common/allocateType.js'
 import { allocateBillSave, allocateBillDetailList, allocateBillDetailCount, allocateBillDetail, allocateBillDetailSave, allocateBillSubmit, allocateBillDetailDel, allocateBillDetailDelAll, allocateBillDetailPrint, allocateBillBatchInsert, allocateBillDetailExcel } from '@/api/allocateBill'
 
 export default {
+  name: 'TransferOutEdit',
+  mixins: [commonMixin],
   components: { STable, VSelect, ImportGuideModal, AllocateType, printModal },
   data () {
     return {

+ 3 - 1
src/views/allocationManagement/transferOut/importGuideModal.vue

@@ -63,11 +63,13 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import ChooseImportModal from './chooseImportModal.vue'
 import { Upload } from '@/components'
 import { allocateBillDownload } from '@/api/allocateBill'
 export default {
-  name: 'ImportGuideModal',
+  name: 'TransferOutImportGuideModal',
+  mixins: [commonMixin],
   components: { ChooseImportModal, Upload },
   props: {
     openModal: { //  弹框显示状态

+ 3 - 0
src/views/allocationManagement/transferOut/list.vue

@@ -120,6 +120,7 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import moment from 'moment'
 import getDate from '@/libs/getDate.js'
 import { STable, VSelect } from '@/components'
@@ -130,6 +131,8 @@ import { allocateBillList, allocateBillDel, allocateBillAudit, allocateBillExpor
 import AllocateType from '@/views/common/allocateType.js'
 import { hdExportExcel } from '@/libs/exportExcel'
 export default {
+  name: 'TransferOutList',
+  mixins: [commonMixin],
   components: { STable, VSelect, basicInfoModal, rangeDate, auditModal, AllocateType },
   data () {
     return {

+ 15 - 2
src/views/allocationManagement/transferReturn/basicInfoModal.vue

@@ -72,14 +72,16 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import debounce from 'lodash/debounce'
 import { VSelect } from '@/components'
 import { allocateReturnSave } from '@/api/allocateReturn'
 import { dealerSubareaScopeList } from '@/api/dealer'
 import { allocateTypeAllList } from '@/api/allocateType'
 export default {
-  name: 'StoreTransferOutBasicInfoModal',
+  name: 'TransferOutBasicInfoModal',
   components: { VSelect },
+  mixins: [commonMixin],
   props: {
     openModal: {
       //  弹框显示状态
@@ -187,6 +189,17 @@ export default {
           this.allocateTypeList = []
         }
       })
+    },
+    resetForm () {
+      this.$refs.ruleForm.resetFields()
+      this.form = {
+        targetType: 'DEALER',
+        targetSn: undefined,
+        targetName: '',
+        allocateReturnTypeSn: undefined,
+        grabFlag: '1',
+        remarks: ''
+      }
     }
   },
   watch: {
@@ -197,8 +210,8 @@ export default {
     //  重定义的弹框状态
     isShow (newValue, oldValue) {
       if (!newValue) {
+        this.resetForm()
         this.$emit('close')
-        this.$refs.ruleForm.resetFields()
       } else {
         this.getAllocateTypeAllList()
       }

+ 8 - 5
src/views/allocationManagement/transferReturn/check.vue

@@ -11,7 +11,8 @@
       <a-card size="small" :bordered="false" class="salesReturnCheck-cont">
         <div>
           <a-button type="primary" :loading="loading" class="button-info" @click="backStock('BATCH_BACK','批量返库')">批量返库</a-button>
-          <a-button v-if="grabFlag==1" type="default" :loading="loading" class="button-info" @click="backStock('BATCH_BAD','批量坏件')">批量坏件</a-button>
+          <a-button v-if="grabFlag==1" type="default" :loading="loading" style="margin-right: 15px;" @click="backStock('BATCH_BAD','批量坏件')">批量坏件</a-button>
+          <span>已选 {{ rowSelectionInfo&&rowSelectionInfo.selectedRows.length }} 项</span>
         </div>
         <!-- 已选配件列表 -->
         <s-table
@@ -58,15 +59,17 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 import {
   allocReturnDetailQueryPage,
   allocateReturnQueryBySn,
-  allocateReturnSubmit,
+  allocateReturnCheckSubmit,
   allocateReturnCheck
 } from '@/api/allocateReturn'
 export default {
-  name: 'SalesReturnEdit',
+  name: 'TransferReturnCheck',
+  mixins: [commonMixin],
   components: {
     STable,
     VSelect
@@ -99,7 +102,7 @@ export default {
             const no = (data.pageNo - 1) * data.pageSize
             for (var i = 0; i < data.list.length; i++) {
               data.list[i].no = no + i + 1
-              data.list[i].badQty = data.list[i].badQty || data.list[i].badQty == 0 ? data.list[i].badQty : data.list[i].returnQty
+              data.list[i].badQty = (data.list[i].badQty || data.list[i].badQty == 0) ? data.list[i].badQty : data.list[i].returnQty
               data.list[i].backStockQty = data.list[i].backStockQty || 0
               data.list[i].backStockQtyBackups = data.list[i].backStockQty
             }
@@ -218,7 +221,7 @@ export default {
     // 品检提交
     handleSubmit () {
       this.spinning = true
-      allocateReturnSubmit({ allocateReturnSn: this.orderSn }).then(res => {
+      allocateReturnCheckSubmit({ allocateReturnSn: this.orderSn }).then(res => {
         if (res.status == 200) {
           this.handleBack()
           this.$message.success(res.message)

+ 3 - 1
src/views/allocationManagement/transferReturn/chooseImportModal.vue

@@ -66,10 +66,12 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { hdExportExcel } from '@/libs/exportExcel'
 import { allocateBillParseProducts, allocateBillFailExcel } from '@/api/allocateBill'
 export default {
-  name: 'ChooseImportModal',
+  name: 'TransferReturnImportModal',
+  mixins: [commonMixin],
   props: {
     openModal: { //  弹框显示状态
       type: Boolean,

+ 3 - 0
src/views/allocationManagement/transferReturn/detail.vue

@@ -58,10 +58,13 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 import print from './print.vue'
 import { allocReturnDetailQueryPage, allocateReturnQueryBySn } from '@/api/allocateReturn'
 export default {
+  name: 'TransferReturnDetail',
+  mixins: [commonMixin],
   components: { STable, VSelect, print },
   props: {
     outBizSn: { //  有值则为弹框,无值则为页面

+ 3 - 0
src/views/allocationManagement/transferReturn/edit.vue

@@ -228,6 +228,7 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 // import ImportGuideModal from './importGuideModal.vue'
 import print from './print.vue'
@@ -245,6 +246,8 @@ import {
 } from '@/api/allocateReturn'
 
 export default {
+  name: 'TransferReturnEdit',
+  mixins: [commonMixin],
   components: { STable, VSelect, print, AllocateType },
   data () {
     return {

+ 3 - 0
src/views/allocationManagement/transferReturn/editGrp.vue

@@ -215,6 +215,7 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 // import ImportGuideModal from './importGuideModal.vue'
 import print from './print.vue'
@@ -232,6 +233,8 @@ import {
 } from '@/api/allocateReturn'
 
 export default {
+  name: 'TransferReturnGrpEdit',
+  mixins: [commonMixin],
   components: { STable, VSelect, AllocateType, print },
   data () {
     return {

+ 3 - 1
src/views/allocationManagement/transferReturn/importGuideModal.vue

@@ -63,11 +63,13 @@
 </template>
 
 <script>
+import { commonMixin } from '@/utils/mixin'
 import ChooseImportModal from './chooseImportModal.vue'
 import { Upload } from '@/components'
 import { allocateBillDownload } from '@/api/allocateBill'
 export default {
-  name: 'ImportGuideModal',
+  name: 'TransferReturnImportGuideModal',
+  mixins: [commonMixin],
   components: { ChooseImportModal, Upload },
   props: {
     openModal: { //  弹框显示状态

+ 3 - 0
src/views/allocationManagement/transferReturn/list.vue

@@ -125,6 +125,7 @@
 <script>
 // import moment from 'moment'
 // import getDate from '@/libs/getDate.js'
+import { commonMixin } from '@/utils/mixin'
 import { STable, VSelect } from '@/components'
 import basicInfoModal from './basicInfoModal.vue'
 import rangeDate from '@/views/common/rangeDate.vue'
@@ -133,6 +134,8 @@ import { allocateReturnQueryPage, allocateReturnAudit, allocateReturnDelete, all
 import AllocateType from '@/views/common/allocateType.js'
 import { hdExportExcel } from '@/libs/exportExcel'
 export default {
+  name: 'TransferReturnList',
+  mixins: [commonMixin],
   components: { STable, VSelect, basicInfoModal, rangeDate, auditModal, AllocateType },
   data () {
     return {

+ 1 - 2
src/views/allocationManagement/transferReturn/print.vue

@@ -49,8 +49,7 @@ export default {
     handlePrint (type) {
       const _this = this
       const params = {
-        allocateReturnSn: this.params.allocateReturnSn || '',
-        printType: 'ALLOCATE_BILL'
+        allocateReturnSn: this.params.allocateReturnSn || ''
       }
       this.$emit('loading')
       allocateReturnPrint(params).then(res => {