shish 11 hours ago
parent
commit
14098c16fc
2 changed files with 325 additions and 66 deletions
  1. 102 28
      ghs/src/views/order/refund-list.vue
  2. 223 38
      ghsApp/src/admin/billing/freeRefundList.vue

+ 102 - 28
ghs/src/views/order/refund-list.vue

@@ -1,7 +1,10 @@
 <!--
   PC销售售后记录列表:展示售后类型后增加「原单时间」(当天订单/历史订单),对齐小程序 refundList。
   支持 query.fromAffect:从跨天售后影响带入订单日期、售后申请日、sameDay、status。
-  列表走自定义 page(),每次请求都带上售后申请日,避免 x-crud 默认刷新丢掉导致金额对不上。
+  列表走自定义 page(),每次请求都带上申请日/通过日,避免 x-crud 默认刷新丢掉导致金额对不上。
+  状态 Tab:全部 / 待审核 / 已通过。
+  日期口径跟 Tab:待审核只申请日;已通过默认通过日仍可切回;全部两个都能选。
+  申请日 / 通过日互斥,共用一个日期范围;通过日只认 passTime。
 -->
 <template>
 	<div class="app-list-content">
@@ -77,8 +80,13 @@
 				<el-date-picker v-model="currentDateList" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" @change="getSearchDate" value-format="yyyy-MM-dd"
 					class="custom-date-picker" end-placeholder="结束日期" :picker-options="pickerOptions" style="margin-right:8px;">
 				</el-date-picker>
-				<span class="search-field-label">售后申请日期:</span>
-				<el-date-picker v-model="currentRefundDateList" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" @change="getSearchRefundDate" value-format="yyyy-MM-dd"
+				<!-- 待审核没有通过日,只留申请日期,避免筛空;全部/已通过才给切换 -->
+				<span v-if="!showAfterSaleDateToggle" class="search-field-label">申请日期:</span>
+				<el-button-group v-else style="margin-left:8px;margin-right:8px;">
+					<el-button :type="afterSaleDateType === 'apply' ? 'primary' : 'default'" @click="changeAfterSaleDateType('apply')">申请日期</el-button>
+					<el-button :type="afterSaleDateType === 'pass' ? 'primary' : 'default'" @click="changeAfterSaleDateType('pass')">通过日期</el-button>
+				</el-button-group>
+				<el-date-picker v-model="currentAfterSaleDateList" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" @change="applyAfterSaleDate" value-format="yyyy-MM-dd"
 					class="custom-date-picker" end-placeholder="结束日期" :picker-options="pickerOptions" style="margin-right:8px;">
 				</el-date-picker>
 				</div>
@@ -109,24 +117,14 @@ export default {
 					val: 0
 				},
 				{
-					text: '待审',
+					text: '待审',
 					key: '0',
 					val: 0
 				},
 				{
-					text: '通过',
+					text: '通过',
 					key: '1',
 					val: 0
-				},
-				{
-					text: '驳回',
-					key: '2',
-					val: 0
-				},
-				{
-					text: '取消',
-					key: '3',
-					val: 0
 				}
 			],
 			modalData: {},
@@ -172,7 +170,12 @@ export default {
 			refundStartTime:'',
 			refundEndTime:'',
 			refundSearchTime:'',
-			currentRefundDateList:[],
+			passStartTime:'',
+			passEndTime:'',
+			passSearchTime:'',
+			// apply=售后申请日 / pass=售后通过日,两边只能选一个
+			afterSaleDateType: 'apply',
+			currentAfterSaleDateList:[],
 			status:'-1',
 			/** 原单时间筛选:null=全部订单,1=当天订单,0=历史订单 */
 			sameDayFilter: null,
@@ -186,7 +189,11 @@ export default {
 		};
 	},
 	computed: {
-		...mapGetters(['token'])
+		...mapGetters(['token']),
+		/** 待审核不给通过日切换,避免「待审核+通过日期」筛空 */
+		showAfterSaleDateToggle() {
+			return this.tabIndex !== '0'
+		}
 	},
 	watch: {
 		/** 从跨天售后影响再次点日期时,keepAlive 不会重跑 onLoad,要跟 query 刷新 */
@@ -257,16 +264,57 @@ export default {
 			}
 			this.refreshData()
 		},
-		getSearchRefundDate(){
-			if (this.$util.isEmpty(this.currentRefundDateList)) {
-				this.refundSearchTime = ''
-				this.refundStartTime = ''
-				this.refundEndTime = ''
-			}else{
-				this.refundSearchTime = 'custom'
-				this.refundStartTime = this.currentRefundDateList[0]
-				this.refundEndTime = this.currentRefundDateList[1]
+		/**
+		 * 申请日 / 通过日互斥:切按钮时清掉另一边参数,日期范围复用。
+		 * @param {string} type apply | pass
+		 */
+		changeAfterSaleDateType(type) {
+			if (this.afterSaleDateType === type) {
+				return
+			}
+			this.afterSaleDateType = type
+			this.applyAfterSaleDate()
+		},
+		/**
+		 * Tab 带着日期口径走:待审核强制申请日,已通过默认通过日,全部保持当前选择。
+		 * 日期范围不丢,只换字段,避免切 Tab 后对不上或筛空。
+		 */
+		syncDateTypeByTab() {
+			if (this.tabIndex === '0') {
+				this.afterSaleDateType = 'apply'
+			} else if (this.tabIndex === '1') {
+				this.afterSaleDateType = 'pass'
+			}
+		},
+		/**
+		 * 只写申请日/通过日参数,不刷列表;切 Tab 时和刷新拆开,避免连打两次。
+		 */
+		writeAfterSaleDateParams() {
+			this.refundSearchTime = ''
+			this.refundStartTime = ''
+			this.refundEndTime = ''
+			this.passSearchTime = ''
+			this.passStartTime = ''
+			this.passEndTime = ''
+			if (!this.$util.isEmpty(this.currentAfterSaleDateList)) {
+				const start = this.currentAfterSaleDateList[0]
+				const end = this.currentAfterSaleDateList[1]
+				if (this.afterSaleDateType === 'pass') {
+					this.passSearchTime = 'custom'
+					this.passStartTime = start
+					this.passEndTime = end
+				} else {
+					this.refundSearchTime = 'custom'
+					this.refundStartTime = start
+					this.refundEndTime = end
+				}
 			}
+		},
+		/**
+		 * 按当前按钮把日期写到申请日或通过日,另一组必清空,避免两边同时传。
+		 */
+		applyAfterSaleDate(){
+			this.writeAfterSaleDateParams()
 			this.refreshData()
 		},
 		/** 列表/导出共用筛选:申请日必须每次都带上,不能依赖 x-crud 缓存的 params */
@@ -279,6 +327,9 @@ export default {
 				refundSearchTime: this.refundSearchTime,
 				refundStartTime: this.refundStartTime,
 				refundEndTime: this.refundEndTime,
+				passSearchTime: this.passSearchTime,
+				passStartTime: this.passStartTime,
+				passEndTime: this.passEndTime,
 				shopAdminId: this.currentStaffId,
 				customId: this.currentCustomId,
 				sameDay: this.sameDayQuery(),
@@ -305,6 +356,8 @@ export default {
 		},
 		handleClick() {
 			this.status = this.tabIndex
+			this.syncDateTypeByTab()
+			this.writeAfterSaleDateParams()
 			this.refreshData()
 		},
 		/** 从跨天售后影响/客户页带入筛选:订单日期、售后申请日、历史订单、已通过 */
@@ -324,11 +377,24 @@ export default {
 				this.endTime = q.endTime || ''
 				this.currentDateList = (this.startTime && this.endTime) ? [this.startTime, this.endTime] : []
 			}
-			if (q.refundSearchTime || q.refundStartTime) {
+			if (q.passSearchTime || q.passStartTime) {
+				this.afterSaleDateType = 'pass'
+				this.passSearchTime = q.passSearchTime || ''
+				this.passStartTime = q.passStartTime || ''
+				this.passEndTime = q.passEndTime || ''
+				this.refundSearchTime = ''
+				this.refundStartTime = ''
+				this.refundEndTime = ''
+				this.currentAfterSaleDateList = (this.passStartTime && this.passEndTime) ? [this.passStartTime, this.passEndTime] : []
+			} else if (q.refundSearchTime || q.refundStartTime) {
+				this.afterSaleDateType = 'apply'
 				this.refundSearchTime = q.refundSearchTime || ''
 				this.refundStartTime = q.refundStartTime || ''
 				this.refundEndTime = q.refundEndTime || ''
-				this.currentRefundDateList = (this.refundStartTime && this.refundEndTime) ? [this.refundStartTime, this.refundEndTime] : []
+				this.passSearchTime = ''
+				this.passStartTime = ''
+				this.passEndTime = ''
+				this.currentAfterSaleDateList = (this.refundStartTime && this.refundEndTime) ? [this.refundStartTime, this.refundEndTime] : []
 			}
 			if (q.sameDay === '0' || q.sameDay === 0) {
 				this.sameDayFilter = 0
@@ -339,6 +405,14 @@ export default {
 				this.status = q.status.toString()
 				this.tabIndex = this.status
 			}
+			// 待审核没有通过日,路由即使带了通过日也改回申请日
+			if (this.tabIndex === '0') {
+				this.afterSaleDateType = 'apply'
+			} else if (!q.passSearchTime && !q.passStartTime && !q.refundSearchTime && !q.refundStartTime) {
+				// 没指定日期类型时按 Tab 给默认口径
+				this.syncDateTypeByTab()
+			}
+			this.writeAfterSaleDateParams()
 			return true
 		},
 		onLoad({ ctx, app }) {

+ 223 - 38
ghsApp/src/admin/billing/freeRefundList.vue

@@ -1,7 +1,9 @@
 <!--
   售后/退款记录列表
-  用途:工作台「退款记录」;顶部对齐 home/order.vue(日期+搜客户+状态Tab+筛选)。
-  接口:GET /refund/list?useDefaultLast30=1&sameDay=&status=&customId=
+  用途:工作台「售后记录」;顶栏搜客户+筛选,日期进筛选面板,对齐 PC refund-list。
+  筛选:售后申请日/通过日互斥、近30天默认、原单时间、订单日期、操作人。
+  Tab 带着日期口径:待审核只申请日;已通过默认通过日仍可切回;全部两个都能选。
+  Tab:全部 / 待审核 / 已通过。接口 GET /refund/list。
 -->
 <template>
   <view class="app-content">
@@ -30,15 +32,6 @@
     </view>
 
     <view class="input-wrap_box">
-      <view class="select-dn_bx">
-        <DateSelect
-          ref="dateSelectRef"
-          class="bar"
-          top="0"
-          defaultShowName="近30天"
-          @selectDateFn="selectDateFn"
-        />
-      </view>
       <view class="search-container">
         <AppSearchModule ref="appSearch" placeholder="这里搜索" @input="searchFn" />
         <view v-if="customId != 0" class="search-clear-overlay" @click.stop="clearSearch">清空</view>
@@ -49,7 +42,7 @@
       </view>
     </view>
 
-    <!-- 状态 Tab:对齐 home/order.vue -->
+    <!-- 状态 Tab:对齐 PC,全部/待审核/已通过 -->
     <view class="app-tabs">
       <app-tabs
         :tabs="tabs"
@@ -57,7 +50,7 @@
         :top="50"
         :currentTab="tabIndex"
         :height="100"
-        itemWidth="20%"
+        itemWidth="33.33%"
         @change="onTabChange"
       />
     </view>
@@ -109,10 +102,14 @@
               <text class="meta-label">操作</text>
               <text class="meta-value">{{ item.shopAdminName || '-' }}</text>
             </view>
-            <view class="meta-item meta-item--full">
-              <text class="meta-label">日期</text>
+            <view class="meta-item">
+              <text class="meta-label">申请</text>
               <text class="meta-value">{{ item.addTime ? item.addTime.substr(5, 11) : '-' }}</text>
             </view>
+            <view class="meta-item" v-if="filterStatus !== 0">
+              <text class="meta-label">通过</text>
+              <text class="meta-value">{{ item.passTime ? item.passTime.substr(5, 11) : '-' }}</text>
+            </view>
           </view>
 
           <view class="remark-box" v-if="!$util.isEmpty(item.remark)">
@@ -159,13 +156,39 @@
       </block>
     </view>
 
-    <!-- 筛选面板:仅售后类型(状态已提到顶部 Tab) -->
+    <!-- 筛选:申请日/通过日互斥 + 近30天、原单时间、订单日期、操作人 -->
     <view v-if="showFilterPanel" class="filter-overlay" @click="closeFilterPanel">
       <view class="filter-panel" @click.stop>
         <view class="filter-header">筛选售后</view>
         <scroll-view class="filter-body" scroll-y>
+          <view class="filter-section" v-if="showAfterSaleDateToggle">
+            <view class="filter-title">售后时间</view>
+            <view class="filter-options">
+              <view
+                v-for="(item, index) in afterSaleDateTypeOptions"
+                :key="index"
+                class="filter-option"
+                :class="{ active: afterSaleDateType === item.value }"
+                @click="onSelectAfterSaleDateType(index)"
+              >
+                {{ item.label }}
+              </view>
+            </view>
+          </view>
           <view class="filter-section">
-            <view class="filter-title">售后类型</view>
+            <view class="filter-title">{{ afterSaleDateTitle }}</view>
+            <view class="filter-date-wrap">
+              <DateSelect
+                ref="dateSelectRef"
+                class="bar"
+                top="0"
+                :defaultShowName="dateRangeLabel"
+                @selectDateFn="selectDateFn"
+              />
+            </view>
+          </view>
+          <view class="filter-section">
+            <view class="filter-title">原单时间</view>
             <view class="filter-options">
               <view
                 v-for="(item, index) in sameDayOptions"
@@ -178,6 +201,37 @@
               </view>
             </view>
           </view>
+          <view class="filter-section">
+            <view class="filter-title">订单日期</view>
+            <view class="filter-date-wrap">
+              <DateSelect
+                ref="orderDateSelectRef"
+                class="bar"
+                top="0"
+                :defaultShowName="orderDateLabel"
+                @selectDateFn="selectOrderDateFn"
+              />
+            </view>
+          </view>
+          <view class="filter-section">
+            <view class="filter-title">操作人</view>
+            <view class="filter-options">
+              <view
+                class="filter-option"
+                :class="{ active: filterShopAdminId === 0 }"
+                @click="onSelectStaff(0)"
+              >全部</view>
+              <view
+                v-for="(item, index) in staffList"
+                :key="index"
+                class="filter-option"
+                :class="{ active: filterShopAdminId === item.id }"
+                @click="onSelectStaff(item.id)"
+              >
+                {{ item.name }}
+              </view>
+            </view>
+          </view>
         </scroll-view>
         <view class="filter-actions">
           <button class="filter-action-btn clear-btn" @click="clearFilters">重置</button>
@@ -196,6 +250,8 @@ import DateSelect from '@/components/module/dateSelect'
 import { list } from '@/mixins'
 import { refundList } from '@/api/refund'
 import { getList } from '@/api/member'
+import { getStaffList } from '@/api/staff'
+import { mapGetters } from 'vuex'
 
 export default {
   name: 'freeRefundList',
@@ -203,43 +259,78 @@ export default {
   mixins: [list],
   data() {
     return {
+      // 售后时间:默认近30天,申请日/通过日互斥共用这一份
+      afterSaleDateType: 'apply',
       searchTime: 'last30Days',
       startTime: '',
       endTime: '',
+      dateRangeLabel: '近30天',
+      // 订单日期:不限时不传 searchTime,避免和售后日抢参数
+      orderSearchTime: '',
+      orderStartTime: '',
+      orderEndTime: '',
+      orderDateLabel: '不限',
       totalAmount: 0,
-      // -1全部 1当天售后 0跨天售后
+      // -1全部 1当天订单 0历史订单,对齐 PC 原单时间
       sameDay: -1,
+      filterShopAdminId: 0,
+      staffList: [],
       customId: 0,
       searchList: [],
       showSearch: false,
       showFilterPanel: false,
       tabIndex: 0,
-      // id=-1 全部;其余与售后单 status 一致
+      // id=-1 全部;其余与售后单 status 一致;驳回/取消仍可在「全部」里看到
       tabs: [
         { name: '全部', value: 0, id: -1 },
         { name: '待审核', value: 0, id: 0 },
-        { name: '已通过', value: 0, id: 1 },
-        { name: '已驳回', value: 0, id: 2 },
-        { name: '已取消', value: 0, id: 3 }
+        { name: '已通过', value: 0, id: 1 }
+      ],
+      afterSaleDateTypeOptions: [
+        { label: '申请日期', value: 'apply' },
+        { label: '通过日期', value: 'pass' }
       ],
       sameDayOptions: [
         { label: '全部', value: -1 },
-        { label: '当天售后', value: 1 },
-        { label: '跨天售后', value: 0 }
+        { label: '当天订单', value: 1 },
+        { label: '历史订单', value: 0 }
       ]
     }
   },
   computed: {
+    ...mapGetters(['getDictionariesInfo']),
+    /**
+     * 红点:相对当前 Tab 的默认口径才算「有筛选」。
+     * 已通过默认就是通过日,不能因为 pass 一直亮点。
+     */
     hasActiveFilters() {
-      return this.sameDay !== -1
+      const defaultDateType = this.filterStatus === 1 ? 'pass' : 'apply'
+      return (
+        this.sameDay !== -1
+        || this.afterSaleDateType !== defaultDateType
+        || this.searchTime !== 'last30Days'
+        || !!this.startTime
+        || !!this.endTime
+        || !!this.orderSearchTime
+        || this.filterShopAdminId !== 0
+      )
     },
     /** 当前 Tab 对应的 status,全部为 -1 */
     filterStatus() {
       const tab = this.tabs[this.tabIndex]
       return tab ? tab.id : -1
+    },
+    /** 待审核不给通过日切换,避免筛空 */
+    showAfterSaleDateToggle() {
+      return this.filterStatus !== 0
+    },
+    /** 时间范围标题:跟着当前口径走,待审核没有切换按钮时也能看懂 */
+    afterSaleDateTitle() {
+      return this.afterSaleDateType === 'pass' ? '通过日期' : '申请日期'
     }
   },
   onLoad() {
+    this.loadStaffList()
     this.resetList()
     this._list()
   },
@@ -282,12 +373,48 @@ export default {
       const map = { 0: '待审核', 1: '已通过', 2: '已驳回', 3: '已取消' }
       return map[status] || ''
     },
+    /**
+     * 售后时间范围:只记参数,点「确定」再刷列表,避免面板里选日期立刻关面板。
+     */
     selectDateFn(val) {
       this.searchTime = val.searchTime || 'last30Days'
       this.startTime = val.startTime || ''
       this.endTime = val.endTime || ''
-      this.resetList()
-      this._list()
+      this.dateRangeLabel = this.resolveDateLabel(val, '近30天')
+    },
+    /**
+     * 订单日期:单独一组 searchTime,和售后日互不影响。
+     */
+    selectOrderDateFn(val) {
+      this.orderSearchTime = val.searchTime || ''
+      this.orderStartTime = val.startTime || ''
+      this.orderEndTime = val.endTime || ''
+      this.orderDateLabel = this.resolveDateLabel(val, '不限')
+    },
+    /** 根据 DateSelect 回传还原展示文案 */
+    resolveDateLabel(val, fallback) {
+      if (!val || !val.searchTime) {
+        return fallback
+      }
+      if (val.searchTime === 'custom' && val.startTime) {
+        const s = String(val.startTime).substring(5)
+        const e = String(val.endTime || val.startTime).substring(5)
+        return s === e ? s : s + '/' + e
+      }
+      if (val.searchTime === 'byMonth' && val.startTime) {
+        return val.startTime
+      }
+      const opts = (this.getDictionariesInfo && this.getDictionariesInfo.dateDefaultOption) || []
+      const hit = opts.find((o) => o.value === val.searchTime)
+      return hit ? hit.name : fallback
+    },
+    /** 拉员工列表,给筛选「操作人」用 */
+    loadStaffList() {
+      getStaffList().then((res) => {
+        if (res.code == 1) {
+          this.staffList = (res.data && res.data.list) || []
+        }
+      })
     },
     /**
      * 搜索客户:本页下拉联想(对齐 order.vue),不跳转 selectCustom
@@ -343,18 +470,52 @@ export default {
         this.sameDay = item.value
       }
     },
-    /** 切换状态 Tab,刷新列表 */
+    /** 申请日 / 通过日二选一,日期范围复用 */
+    onSelectAfterSaleDateType(index) {
+      const item = this.afterSaleDateTypeOptions[index]
+      if (item) {
+        this.afterSaleDateType = item.value
+      }
+    },
+    /**
+     * Tab 带着日期口径走:待审核强制申请日,已通过默认通过日,全部保持当前选择。
+     */
+    syncDateTypeByTab() {
+      if (this.filterStatus === 0) {
+        this.afterSaleDateType = 'apply'
+      } else if (this.filterStatus === 1) {
+        this.afterSaleDateType = 'pass'
+      }
+    },
+    onSelectStaff(id) {
+      this.filterShopAdminId = id
+    },
+    /** 订单日期改回不限,避免误把原单日当成售后日 */
+    clearOrderDate() {
+      this.orderSearchTime = ''
+      this.orderStartTime = ''
+      this.orderEndTime = ''
+      this.orderDateLabel = '不限'
+      if (this.$refs.orderDateSelectRef && this.$refs.orderDateSelectRef.reset) {
+        this.$refs.orderDateSelectRef.reset()
+      }
+    },
+    /** 切换状态 Tab,先对齐日期口径再刷列表 */
     onTabChange(e) {
       if (this.tabIndex === e.index) {
         return
       }
       this.tabIndex = e.index
+      this.syncDateTypeByTab()
       this.resetList()
       this._list()
     },
     clearFilters() {
       this.sameDay = -1
+      this.afterSaleDateType = this.filterStatus === 1 ? 'pass' : 'apply'
+      this.filterShopAdminId = 0
       this.resetDefaultDateRange()
+      this.clearOrderDate()
       this.closeFilterPanel()
       this.resetList()
       this._list()
@@ -363,6 +524,7 @@ export default {
       this.searchTime = 'last30Days'
       this.startTime = ''
       this.endTime = ''
+      this.dateRangeLabel = '近30天'
       if (this.$refs.dateSelectRef && this.$refs.dateSelectRef.reset) {
         this.$refs.dateSelectRef.reset()
       }
@@ -464,16 +626,31 @@ export default {
         })
       })
     },
+    /**
+     * 申请日走 refundSearchTime,通过日走 passSearchTime;自定义区间用 refund/pass 的 Start/End,
+     * 不占用 startTime(那是订单日期),否则自定义售后日对不上。
+     */
     _list() {
       const params = {
         page: this.list.page,
-        useDefaultLast30: 1,
-        refundSearchTime: this.searchTime,
-        startTime: this.startTime,
-        endTime: this.endTime,
         sameDay: this.sameDay,
         status: this.filterStatus,
-        customId: this.customId || ''
+        customId: this.customId || '',
+        shopAdminId: this.filterShopAdminId || ''
+      }
+      if (this.afterSaleDateType === 'pass') {
+        params.passSearchTime = this.searchTime
+        params.passStartTime = this.startTime
+        params.passEndTime = this.endTime
+      } else {
+        params.refundSearchTime = this.searchTime
+        params.refundStartTime = this.startTime
+        params.refundEndTime = this.endTime
+      }
+      if (this.orderSearchTime) {
+        params.searchTime = this.orderSearchTime
+        params.startTime = this.orderStartTime
+        params.endTime = this.orderEndTime
       }
       return refundList(params).then((res) => {
         this.completes(res)
@@ -545,11 +722,6 @@ export default {
   padding: 0 10upx;
   box-sizing: border-box;
 }
-.select-dn_bx {
-  padding-left: 10upx;
-  padding-right: 10upx;
-  flex-shrink: 0;
-}
 .search-container {
   position: relative;
   flex: 1;
@@ -815,6 +987,19 @@ export default {
   font-size: 28upx;
   font-weight: 700;
 }
+/* 筛选内 DateSelect:抬高层级,避免被筛选遮罩挡住 */
+.filter-date-wrap {
+  padding: 10upx 0;
+  ::v-deep .select-mark-view {
+    z-index: 10000001 !important;
+  }
+  ::v-deep .select-fixed_bx {
+    z-index: 10000002 !important;
+  }
+  ::v-deep .picker {
+    z-index: 10000003 !important;
+  }
+}
 .filter-options {
   display: flex;
   flex-wrap: wrap;