shish 3 дней назад
Родитель
Сommit
c3ad921116

+ 141 - 64
ghsApp/src/admin/billing/freeRefundConfirm.vue

@@ -1,7 +1,7 @@
 <!--
 <!--
   无原单退款确认页
   无原单退款确认页
-  用途:工作台「退款」选客户、选花材后,确认金额/资金方式并提交;生成无关联销售单的售后单(sameDay=0)
-  谁用:admin/billing/freeRefund → 本页 → forwardResult 或成功提示
+  用途:选花材后确认金额,并选择退款方式 / 退货方式 / 是否返充余额,提交生成无关联销售单的售后单
+  谁用:admin/billing/freeRefund → 本页 → forwardResult。
 -->
 -->
 <template>
 <template>
   <view class="app-content">
   <view class="app-content">
@@ -56,8 +56,50 @@
           </tui-list-cell>
           </tui-list-cell>
         </view>
         </view>
 
 
+        <!-- 退款方式 / 退货方式 / 返充到余额(对齐业务确认图) -->
+        <view class="module-com option-module">
+          <view class="option-block">
+            <view class="option-title">退款方式</view>
+            <view class="option-row">
+              <view
+                v-for="item in payWayList"
+                :key="'pay-' + item.id"
+                class="option-btn"
+                :class="{ active: form.payWay === item.id }"
+                @click="form.payWay = item.id"
+              >{{ item.name }}</view>
+            </view>
+          </view>
+
+          <view class="option-block">
+            <view class="option-title">退货方式</view>
+            <view class="option-row">
+              <view
+                v-for="item in returnWayList"
+                :key="'ret-' + item.id"
+                class="option-btn"
+                :class="{ active: form.returnWay === item.id }"
+                @click="form.returnWay = item.id"
+              >{{ item.name }}</view>
+            </view>
+          </view>
+
+          <view class="option-block">
+            <view class="option-title">返充到余额</view>
+            <view class="option-row">
+              <view
+                v-for="item in toBalanceList"
+                :key="'bal-' + item.id"
+                class="option-btn"
+                :class="{ active: form.toBalance === item.id }"
+                @click="form.toBalance = item.id"
+              >{{ item.name }}</view>
+            </view>
+          </view>
+        </view>
+
         <view class="tip-box">
         <view class="tip-box">
-          此退款不关联销售单:退货将回库;资金可选返充客户余额或仅记账。
+          无关联销售单。「库存退回」会加库存;「返充到余额」会增加客户余额,选「否」则仅记账。
         </view>
         </view>
       </view>
       </view>
 
 
@@ -66,40 +108,47 @@
         <button class="admin-button-com blue big" style="width:220upx;" @click="onSubmitTap">确认退款</button>
         <button class="admin-button-com blue big" style="width:220upx;" @click="onSubmitTap">确认退款</button>
       </view>
       </view>
     </view>
     </view>
-
-    <!-- 无原单:不可原路退,只选返余额/仅记账 -->
-    <forward-options-popup
-      :show="showFundPop"
-      :allow-original="false"
-      :force-balance="false"
-      mode="free"
-      @cancel="showFundPop = false"
-      @confirm="onFundConfirm"
-    />
   </view>
   </view>
 </template>
 </template>
 
 
 <script>
 <script>
 import TuiListCell from '@/components/plugin/list-cell'
 import TuiListCell from '@/components/plugin/list-cell'
 import productMins from '@/mixins/product2'
 import productMins from '@/mixins/product2'
-import ForwardOptionsPopup from '@/components/forward-options-popup'
 import { createFreeRefund } from '@/api/refund'
 import { createFreeRefund } from '@/api/refund'
 
 
 export default {
 export default {
   name: 'freeRefundConfirm',
   name: 'freeRefundConfirm',
-  components: { TuiListCell, ForwardOptionsPopup },
+  components: { TuiListCell },
   mixins: [productMins],
   mixins: [productMins],
   data() {
   data() {
     return {
     return {
       autoLoad: false,
       autoLoad: false,
       selectJobType: 'freeRefund',
       selectJobType: 'freeRefund',
-      showFundPop: false,
+      // 与开单/支出一致:0微信 1支付宝 4现金 5银行卡
+      payWayList: [
+        { name: '线下微信', id: 0 },
+        { name: '线下支付宝', id: 1 },
+        { name: '现金', id: 4 },
+        { name: '银行卡', id: 5 }
+      ],
+      // 0库存不退(默认) 1库存退回
+      returnWayList: [
+        { name: '库存不退', id: 0 },
+        { name: '库存退回', id: 1 }
+      ],
+      // 无默认,须选手选
+      toBalanceList: [
+        { name: '是', id: 1 },
+        { name: '否', id: 0 }
+      ],
       form: {
       form: {
         customId: '',
         customId: '',
         customName: '',
         customName: '',
         price: '',
         price: '',
         remark: '',
         remark: '',
-        fundType: 2
+        payWay: -1,
+        returnWay: 0,
+        toBalance: -1
       }
       }
     }
     }
   },
   },
@@ -115,18 +164,15 @@ export default {
     } else {
     } else {
       uni.setNavigationBarTitle({ title: '退款确认' })
       uni.setNavigationBarTitle({ title: '退款确认' })
     }
     }
-    // 默认退款金额=已选花材合计
     this.$nextTick(() => {
     this.$nextTick(() => {
       this.form.price = this.allPrice > 0 ? String(this.allPrice) : ''
       this.form.price = this.allPrice > 0 ? String(this.allPrice) : ''
     })
     })
   },
   },
   methods: {
   methods: {
-    /** 行单价展示 */
     getUnitPrice(item) {
     getUnitPrice(item) {
       if (item.userPrice > 0) return parseFloat(item.userPrice).toFixed(2)
       if (item.userPrice > 0) return parseFloat(item.userPrice).toFixed(2)
       return parseFloat(item.bigPrice || item.price || 0).toFixed(2)
       return parseFloat(item.bigPrice || item.price || 0).toFixed(2)
     },
     },
-    /** 行小计 */
     getLineAmount(item) {
     getLineAmount(item) {
       const unit = Number(this.getUnitPrice(item))
       const unit = Number(this.getUnitPrice(item))
       const big = Number(item.bigCount) || 0
       const big = Number(item.bigCount) || 0
@@ -136,7 +182,10 @@ export default {
     gobackEvent() {
     gobackEvent() {
       uni.navigateBack({})
       uni.navigateBack({})
     },
     },
-    /** 先弹资金选项再提交 */
+    /**
+     * 校验三组选项后提交
+     * returnWay→refundType;toBalance→fundType;payWay原样上报
+     */
     onSubmitTap() {
     onSubmitTap() {
       if (this.$util.isEmpty(this.selectList)) {
       if (this.$util.isEmpty(this.selectList)) {
         this.$msg('请选择花材')
         this.$msg('请选择花材')
@@ -151,18 +200,17 @@ export default {
         this.$msg('请填写退款金额')
         this.$msg('请填写退款金额')
         return
         return
       }
       }
-      this.showFundPop = true
-    },
-    /**
-     * 资金确认后提交无原单退款
-     * @param {{fundType:number}} opt
-     */
-    onFundConfirm(opt) {
-      this.showFundPop = false
-      this.form.fundType = opt.fundType
+      if (this.form.payWay === -1 || this.form.payWay === '' || this.form.payWay === null) {
+        this.$msg('请选择退款方式')
+        return
+      }
+      if (this.form.toBalance === -1 || this.form.toBalance === '' || this.form.toBalance === null) {
+        this.$msg('请选择是否返充到余额')
+        return
+      }
       this.submitFreeRefund()
       this.submitFreeRefund()
     },
     },
-    /** 组装花材明细并调 create-free-refund */
+    /** 组装明细并调 create-free-refund */
     submitFreeRefund() {
     submitFreeRefund() {
       const that = this
       const that = this
       const product = []
       const product = []
@@ -191,59 +239,52 @@ export default {
         this.$msg('请选择花材数量')
         this.$msg('请选择花材数量')
         return
         return
       }
       }
+      // 库存退回=退货退款(1);库存不退=仅退款(2)
+      const refundType = Number(this.form.returnWay) === 1 ? 1 : 2
+      // 返充是=fundType2;否=仅记账3
+      const fundType = Number(this.form.toBalance) === 1 ? 2 : 3
+
       uni.showLoading({ title: '提交中', mask: true })
       uni.showLoading({ title: '提交中', mask: true })
       createFreeRefund({
       createFreeRefund({
         customId: this.form.customId,
         customId: this.form.customId,
         price: this.form.price,
         price: this.form.price,
         remark: this.form.remark,
         remark: this.form.remark,
-        refundType: 1,
-        fundType: this.form.fundType,
+        payWay: this.form.payWay,
+        returnWay: this.form.returnWay,
+        toBalance: this.form.toBalance,
+        refundType: refundType,
+        fundType: fundType,
         product: JSON.stringify(product)
         product: JSON.stringify(product)
       }).then((res) => {
       }).then((res) => {
         uni.hideLoading()
         uni.hideLoading()
         if (res.code != 1) {
         if (res.code != 1) {
           return
           return
         }
         }
-        // 清空购物车记忆,避免下次误带
         that.removeFromSaveDirect && that.removeFromSaveDirect()
         that.removeFromSaveDirect && that.removeFromSaveDirect()
         const data = res.data || {}
         const data = res.data || {}
-        if (Number(data.fundType) === 2) {
-          uni.setStorageSync('forwardResultPayload', {
-            fundType: data.fundType,
-            amount: data.refundPrice || that.form.price,
-            customId: data.customId || that.form.customId,
-            customBalance: data.customBalance != null ? data.customBalance : '',
-            customName: data.customName || that.form.customName || '',
-            forwardSn: data.orderSn || '',
-            forwardId: data.id || 0,
-            orderId: 0
-          })
-          that.$util.pageTo({
-            url: '/admin/billing/forwardResult',
-            type: 2,
-            query: {
-              fundType: data.fundType,
-              amount: data.refundPrice || that.form.price,
-              customId: data.customId || that.form.customId,
-              customBalance: data.customBalance != null ? data.customBalance : '',
-              customName: encodeURIComponent(data.customName || that.form.customName || ''),
-              forwardSn: data.orderSn || '',
-              forwardId: data.id || 0,
-              orderId: 0
-            }
-          })
-          return
-        }
+        const goFundType = data.fundType != null ? data.fundType : fundType
+        uni.setStorageSync('forwardResultPayload', {
+          fundType: goFundType,
+          amount: data.refundPrice || that.form.price,
+          customId: data.customId || that.form.customId,
+          customBalance: data.customBalance != null ? data.customBalance : '',
+          customName: data.customName || that.form.customName || '',
+          forwardSn: data.orderSn || '',
+          forwardId: data.id || 0,
+          orderId: 0
+        })
         that.$util.pageTo({
         that.$util.pageTo({
           url: '/admin/billing/forwardResult',
           url: '/admin/billing/forwardResult',
           type: 2,
           type: 2,
           query: {
           query: {
-            fundType: data.fundType || that.form.fundType,
+            fundType: goFundType,
             amount: data.refundPrice || that.form.price,
             amount: data.refundPrice || that.form.price,
             customId: data.customId || that.form.customId,
             customId: data.customId || that.form.customId,
+            customBalance: data.customBalance != null ? data.customBalance : '',
             customName: encodeURIComponent(data.customName || that.form.customName || ''),
             customName: encodeURIComponent(data.customName || that.form.customName || ''),
             forwardSn: data.orderSn || '',
             forwardSn: data.orderSn || '',
-            forwardId: data.id || 0
+            forwardId: data.id || 0,
+            orderId: 0
           }
           }
         })
         })
       }).catch(() => {
       }).catch(() => {
@@ -332,8 +373,44 @@ export default {
   font-size: 28upx;
   font-size: 28upx;
   color: #333;
   color: #333;
 }
 }
+.option-module {
+  padding: 24upx 30upx 10upx;
+}
+.option-block {
+  margin-bottom: 28upx;
+}
+.option-title {
+  font-size: 30upx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 16upx;
+}
+.option-row {
+  display: flex;
+  flex-wrap: wrap;
+}
+.option-btn {
+  min-width: 140upx;
+  height: 64upx;
+  line-height: 64upx;
+  padding: 0 24upx;
+  margin-right: 16upx;
+  margin-bottom: 16upx;
+  text-align: center;
+  font-size: 26upx;
+  color: #333;
+  background: #fff;
+  border: 1upx solid #cccccc;
+  border-radius: 8upx;
+  box-sizing: border-box;
+}
+.option-btn.active {
+  color: #fff;
+  background: #3385ff;
+  border-color: #3385ff;
+}
 .tip-box {
 .tip-box {
-  margin: 20upx 30upx;
+  margin: 10upx 30upx 20upx;
   font-size: 24upx;
   font-size: 24upx;
   color: #999;
   color: #999;
   line-height: 1.5;
   line-height: 1.5;

+ 210 - 0
ghsApp/src/admin/billing/freeRefundDetail.vue

@@ -0,0 +1,210 @@
+<!--
+  无原单退款明细
+  用途:退款记录列表点进查看客户、金额、花材、资金方式等;无关联销售单。
+-->
+<template>
+  <view class="refund-page">
+    <view class="refund-card" v-if="info && info.id">
+      <view class="refund-row">
+        <view class="refund-label">客户</view>
+        <view class="refund-value">{{ info.customName || '-' }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">退款单号</view>
+        <view class="refund-value">{{ info.orderSn || '-' }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">退货方式</view>
+        <view class="refund-value">{{ Number(info.refundType) === 1 ? '库存退回' : '库存不退' }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">资金处理</view>
+        <view class="refund-value">{{ fundText }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">退款金额</view>
+        <view class="refund-value amount">¥{{ formatMoney(info.refundPrice) }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">状态</view>
+        <view class="refund-value">{{ statusText }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">操作人</view>
+        <view class="refund-value">{{ info.shopAdminName || '-' }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">时间</view>
+        <view class="refund-value">{{ info.passTime || info.addTime || '-' }}</view>
+      </view>
+      <view class="refund-row refund-row--top" v-if="!$util.isEmpty(info.remark)">
+        <view class="refund-label">备注</view>
+        <view class="refund-value refund-value--multiline">{{ info.remark }}</view>
+      </view>
+
+      <view class="refund-section" v-if="!$util.isEmpty(product)">
+        <view class="refund-label refund-section__title">花材明细</view>
+        <view class="refund-product-item" v-for="(res, i) in product" :key="i">
+          {{ res.name }} ¥{{ formatMoney(res.xhUnitPrice) }}×{{ res.xhNum }}{{ res.xhUnitName }}=¥{{ formatMoney(res.xhPrice) }}
+        </view>
+      </view>
+    </view>
+
+    <view class="refund-actions">
+      <button
+        v-if="Number(info.fundType) === 2"
+        class="admin-button-com big blue refund-actions__btn"
+        @click="goPoster"
+      >查看凭证</button>
+      <button class="admin-button-com big default refund-actions__btn" @click="goBack">返回</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import { refundDetail } from '@/api/refund'
+
+export default {
+  name: 'freeRefundDetail',
+  data() {
+    return {
+      info: {},
+      product: []
+    }
+  },
+  computed: {
+    fundText() {
+      const t = Number(this.info.fundType)
+      if (t === 2) return '返充到余额'
+      if (t === 3) return '仅记账(不动资金)'
+      if (t === 1) return '原路退回'
+      return '-'
+    },
+    statusText() {
+      const map = { 0: '待审核', 1: '已通过', 2: '已驳回', 3: '已取消' }
+      return map[this.info.status] || ''
+    }
+  },
+  onLoad() {
+    this.loadDetail()
+  },
+  methods: {
+    formatMoney(v) {
+      return v != null ? parseFloat(v) : 0
+    },
+    loadDetail() {
+      const id = this.option.id
+      if (!id) {
+        this.$msg('缺少退款单')
+        return
+      }
+      refundDetail({ id }).then((res) => {
+        if (res.code != 1) return
+        this.info = res.data.info || {}
+        this.product = res.data.itemList || []
+      })
+    },
+    /** 返余额凭证补发 */
+    goPoster() {
+      uni.setStorageSync('forwardResultPayload', {
+        fundType: this.info.fundType,
+        amount: this.info.refundPrice,
+        customId: this.info.customId,
+        customName: this.info.customName || '',
+        forwardSn: this.info.orderSn || '',
+        forwardId: this.info.id || 0,
+        orderId: 0,
+        mode: 'repost'
+      })
+      this.$util.pageTo({
+        url: '/admin/billing/forwardResult',
+        query: {
+          fundType: this.info.fundType,
+          amount: this.info.refundPrice,
+          customId: this.info.customId,
+          customName: encodeURIComponent(this.info.customName || ''),
+          forwardSn: this.info.orderSn || '',
+          forwardId: this.info.id || 0,
+          orderId: 0,
+          mode: 'repost'
+        }
+      })
+    },
+    goBack() {
+      uni.navigateBack({})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.refund-page {
+  min-height: 100%;
+  background: #f5f5f5;
+  padding-bottom: 160upx;
+}
+.refund-card {
+  background: #fff;
+  margin: 20upx;
+  border-radius: 12upx;
+  padding: 10upx 24upx 24upx;
+}
+.refund-row {
+  display: flex;
+  padding: 18upx 0;
+  border-bottom: 1upx solid #f0f0f0;
+}
+.refund-row--top {
+  align-items: flex-start;
+}
+.refund-label {
+  width: 160upx;
+  font-size: 28upx;
+  color: #666;
+  flex-shrink: 0;
+}
+.refund-value {
+  flex: 1;
+  font-size: 28upx;
+  color: #333;
+}
+.refund-value.amount {
+  color: #ff2842;
+  font-weight: bold;
+}
+.refund-value--multiline {
+  line-height: 1.5;
+}
+.refund-section {
+  margin-top: 20upx;
+}
+.refund-section__title {
+  margin-bottom: 12upx;
+  font-weight: bold;
+  color: #333;
+}
+.refund-product-item {
+  font-size: 26upx;
+  color: #333;
+  line-height: 44upx;
+  padding: 6upx 0;
+}
+.refund-actions {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  padding: 20upx 30upx;
+  padding-bottom: calc(20upx + env(safe-area-inset-bottom));
+  background: #fff;
+  box-shadow: 0 -2upx 10upx rgba(0, 0, 0, 0.06);
+}
+.refund-actions__btn {
+  flex: 1;
+  margin-left: 16upx;
+}
+.refund-actions__btn:first-child {
+  margin-left: 0;
+}
+</style>

+ 603 - 0
ghsApp/src/admin/billing/freeRefundList.vue

@@ -0,0 +1,603 @@
+<!--
+  售后/退款记录列表
+  用途:工作台「退款记录」;顶部交互对齐 home/order.vue(日期+搜客户+筛选面板)。
+  接口:GET /refund/list?useDefaultLast30=1&sameDay=&customId=
+-->
+<template>
+  <view class="app-content">
+    <!-- 客户搜索下拉,对齐订单列表 -->
+    <view class="search-dropdown-wrap" v-if="showSearch">
+      <view class="search-dropdown">
+        <block v-if="!$util.isEmpty(searchList)">
+          <view
+            class="search-item"
+            v-for="(item, index) in searchList"
+            :key="index"
+            @click="getCustom(item)"
+          >
+            <view class="search-name">{{ item.name }}</view>
+            <view class="search-sub">
+              {{ item.visitTime ? item.visitTime.substr(5, 11) : '' }}
+            </view>
+          </view>
+        </block>
+      </view>
+    </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>
+      </view>
+      <view class="filter-btn-wrap">
+        <button class="admin-button-com filter-btn" @click="openFilterPanel">筛选</button>
+        <view v-if="hasActiveFilters" class="filter-dot"></view>
+      </view>
+    </view>
+
+    <view class="total-bar">合计 ¥{{ totalAmount }}</view>
+
+    <view class="list-wrap">
+      <block v-if="!$util.isEmpty(list.data)">
+        <view
+          class="refund-card"
+          v-for="(item, index) in list.data"
+          :key="index"
+          @click="goDetail(item)"
+        >
+          <view class="row-top">
+            <text class="custom-name">{{ item.customName || '客户' }}</text>
+            <text class="amount">¥{{ formatMoney(item.refundPrice) }}</text>
+          </view>
+          <view class="row">
+            <text class="tag" :class="{ next: item.sameDay == 0 }">
+              {{ item.sameDay == 0 ? '隔天售后' : '当天售后' }}
+            </text>
+            <text v-if="!item.relateOrderSn" class="tag free">无原单</text>
+          </view>
+          <view class="row">单号:{{ item.orderSn || '-' }}</view>
+          <view class="row" v-if="item.relateOrderSn">原单:{{ item.relateOrderSn }}</view>
+          <view class="row">
+            类型:{{ item.refundType == 1 ? '退货退款' : '仅退款' }}
+            <text class="split">·</text>
+            {{ fundText(item) }}
+          </view>
+          <view class="row">操作:{{ item.shopAdminName || '-' }}</view>
+          <view class="row">时间:{{ item.addTime ? item.addTime.substr(5, 11) : '' }}</view>
+          <view class="row remark" v-if="!$util.isEmpty(item.remark)">备注:{{ item.remark }}</view>
+          <view
+            class="status"
+            :class="{
+              pending: item.status == 0,
+              pass: item.status == 1,
+              reject: item.status == 2,
+              cancel: item.status == 3
+            }"
+          >{{ statusText(item.status) }}</view>
+          <view class="arrow iconfont iconxiangyou"></view>
+        </view>
+      </block>
+      <block v-else>
+        <app-wrapper-empty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
+      </block>
+    </view>
+
+    <!-- 筛选面板:售后类型 -->
+    <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">
+            <view class="filter-title">售后类型</view>
+            <view class="filter-options">
+              <view
+                v-for="(item, index) in sameDayOptions"
+                :key="index"
+                class="filter-option"
+                :class="{ active: sameDay === item.value }"
+                @click="onSelectSameDay(index)"
+              >
+                {{ item.label }}
+              </view>
+            </view>
+          </view>
+          <view class="filter-section">
+            <view class="filter-title">状态</view>
+            <view class="filter-options">
+              <view
+                v-for="(item, index) in statusOptions"
+                :key="index"
+                class="filter-option"
+                :class="{ active: filterStatus === item.value }"
+                @click="onSelectStatus(index)"
+              >
+                {{ item.label }}
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+        <view class="filter-actions">
+          <button class="filter-action-btn clear-btn" @click="clearFilters">重置</button>
+          <button class="filter-action-btn confirm-btn" @click="confirmFilters">确定</button>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import AppWrapperEmpty from '@/components/app-wrapper-empty'
+import AppSearchModule from '@/components/module/app-search'
+import DateSelect from '@/components/module/dateSelect'
+import { list } from '@/mixins'
+import { refundList } from '@/api/refund'
+import { getList } from '@/api/member'
+
+export default {
+  name: 'freeRefundList',
+  components: { AppWrapperEmpty, AppSearchModule, DateSelect },
+  mixins: [list],
+  data() {
+    return {
+      searchTime: 'last30Days',
+      startTime: '',
+      endTime: '',
+      totalAmount: 0,
+      // -1全部 1当天售后 0隔天售后
+      sameDay: -1,
+      filterStatus: -1,
+      customId: 0,
+      searchList: [],
+      showSearch: false,
+      showFilterPanel: false,
+      sameDayOptions: [
+        { label: '全部', value: -1 },
+        { label: '当天售后', value: 1 },
+        { label: '隔天售后', value: 0 }
+      ],
+      statusOptions: [
+        { label: '全部', value: -1 },
+        { label: '待审核', value: 0 },
+        { label: '已通过', value: 1 },
+        { label: '已驳回', value: 2 },
+        { label: '已取消', value: 3 }
+      ]
+    }
+  },
+  computed: {
+    hasActiveFilters() {
+      return this.sameDay !== -1 || this.filterStatus !== -1
+    }
+  },
+  onLoad() {
+    this.resetList()
+    this._list()
+  },
+  onPullDownRefresh() {
+    this.resetList()
+    this._list().then(() => {
+      uni.stopPullDownRefresh()
+    })
+  },
+  onReachBottom() {
+    if (!this.list.finished) {
+      this._list()
+    }
+  },
+  methods: {
+    formatMoney(v) {
+      return v != null ? parseFloat(v) : 0
+    },
+    fundText(item) {
+      if (Number(item.sameDay) !== 0) {
+        return '常规售后'
+      }
+      const t = Number(item.fundType)
+      if (t === 2) return '返充余额'
+      if (t === 3) return '仅记账'
+      if (t === 1) return '原路退回'
+      return '隔天售后'
+    },
+    statusText(status) {
+      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()
+    },
+    /** 搜索客户:弹出联想列表 */
+    searchFn(e) {
+      const that = this
+      if (that.$util.isEmpty(e)) {
+        that.showSearch = false
+        that.searchList = []
+        return
+      }
+      getList({ page: 1, name: e }).then((res) => {
+        if (!that.$util.isEmpty(res.data) && !that.$util.isEmpty(res.data.list)) {
+          that.showSearch = true
+          that.searchList = res.data.list
+        } else {
+          that.showSearch = false
+          that.searchList = []
+        }
+      })
+    },
+    getCustom(item) {
+      this.showSearch = false
+      this.customId = item.id
+      this.resetList()
+      this._list()
+    },
+    clearSearch() {
+      if (this.$refs.appSearch && typeof this.$refs.appSearch.inputEmptyFn === 'function') {
+        this.$refs.appSearch.inputEmptyFn()
+      }
+      this.showSearch = false
+      this.searchList = []
+      this.customId = 0
+      this.resetList()
+      this._list()
+    },
+    openFilterPanel() {
+      this.showFilterPanel = true
+    },
+    closeFilterPanel() {
+      this.showFilterPanel = false
+    },
+    onSelectSameDay(index) {
+      const item = this.sameDayOptions[index]
+      if (item) {
+        this.sameDay = item.value
+      }
+    },
+    onSelectStatus(index) {
+      const item = this.statusOptions[index]
+      if (item) {
+        this.filterStatus = item.value
+      }
+    },
+    clearFilters() {
+      this.sameDay = -1
+      this.filterStatus = -1
+      this.resetDefaultDateRange()
+      this.closeFilterPanel()
+      this.resetList()
+      this._list()
+    },
+    resetDefaultDateRange() {
+      this.searchTime = 'last30Days'
+      this.startTime = ''
+      this.endTime = ''
+      if (this.$refs.dateSelectRef && this.$refs.dateSelectRef.reset) {
+        this.$refs.dateSelectRef.reset()
+      }
+    },
+    confirmFilters() {
+      this.closeFilterPanel()
+      this.resetList()
+      this._list()
+    },
+    goDetail(item) {
+      if (!item.relateOrderSn) {
+        this.$util.pageTo({
+          url: '/admin/billing/freeRefundDetail',
+          query: { id: item.id }
+        })
+        return
+      }
+      this.$util.pageTo({
+        url: '/pagesOrder/refundDetail',
+        query: { id: item.id, from: 1 }
+      })
+    },
+    _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 || ''
+      }
+      return refundList(params).then((res) => {
+        this.completes(res)
+        this.totalAmount = res.data && res.data.totalRefundPrice != null
+          ? parseFloat(res.data.totalRefundPrice)
+          : 0
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-content {
+  padding-top: 100upx;
+}
+.search-dropdown-wrap {
+  position: fixed;
+  top: 100upx;
+  left: 0;
+  right: 0;
+  z-index: 999999;
+  background-color: white;
+}
+.search-dropdown {
+  width: 80%;
+  border: 1upx solid #cccccc;
+  margin: 0 auto;
+  padding: 10upx 0 0 20upx;
+  max-height: 60vh;
+  overflow: scroll;
+}
+.search-item {
+  margin-bottom: 30upx;
+}
+.search-name {
+  font-size: 37upx;
+  margin-bottom: 10upx;
+}
+.search-sub {
+  font-size: 29upx;
+  color: #666;
+}
+.input-wrap_box {
+  background-color: #fff;
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 10;
+  width: 100%;
+  height: 100upx;
+  display: flex;
+  align-items: center;
+  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;
+}
+.search-clear-overlay {
+  position: absolute;
+  top: 50%;
+  right: 20upx;
+  transform: translateY(-50%);
+  z-index: 11;
+  padding: 10upx 20upx;
+  border-radius: 24upx;
+  font-size: 24upx;
+  color: #3385ff;
+  border: 1upx solid #3385ff;
+  background-color: rgba(255, 255, 255, 0.9);
+}
+.filter-btn-wrap {
+  position: relative;
+  margin-left: 16upx;
+  flex-shrink: 0;
+}
+.filter-btn {
+  width: 120upx;
+  height: 80upx;
+  line-height: 80upx;
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+  text-align: center;
+  font-size: 28upx;
+}
+.filter-dot {
+  position: absolute;
+  top: 6upx;
+  right: 6upx;
+  width: 14upx;
+  height: 14upx;
+  border-radius: 50%;
+  background-color: #3385ff;
+}
+.total-bar {
+  padding: 16upx 30upx;
+  font-size: 26upx;
+  color: #333;
+  font-weight: bold;
+  background: #fff;
+  margin-bottom: 16upx;
+}
+.list-wrap {
+  padding-bottom: 30upx;
+}
+.refund-card {
+  position: relative;
+  background: #fff;
+  margin-bottom: 16upx;
+  padding: 24upx 30upx;
+}
+.row-top {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 10upx;
+}
+.custom-name {
+  font-size: 30upx;
+  font-weight: bold;
+  color: #333;
+}
+.amount {
+  font-size: 32upx;
+  font-weight: bold;
+  color: #ff2842;
+  margin-right: 36upx;
+}
+.row {
+  font-size: 26upx;
+  color: #666;
+  line-height: 42upx;
+}
+.tag {
+  display: inline-block;
+  font-size: 22upx;
+  color: #3385ff;
+  border: 1upx solid #3385ff;
+  border-radius: 6upx;
+  padding: 0 10upx;
+  margin-right: 10upx;
+  line-height: 36upx;
+}
+.tag.next {
+  color: #ff9900;
+  border-color: #ff9900;
+}
+.tag.free {
+  color: #19be6b;
+  border-color: #19be6b;
+}
+.split {
+  margin: 0 8upx;
+}
+.remark {
+  color: #e54d42;
+}
+.status {
+  position: absolute;
+  right: 50upx;
+  top: 70upx;
+  font-size: 24upx;
+}
+.status.pass {
+  color: #19be6b;
+}
+.status.pending {
+  color: #3385ff;
+}
+.status.reject,
+.status.cancel {
+  color: #999;
+}
+.arrow {
+  position: absolute;
+  right: 20upx;
+  top: 50%;
+  margin-top: -16upx;
+  color: #ccc;
+  font-size: 28upx;
+}
+.filter-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 9999999;
+  display: flex;
+  align-items: flex-start;
+  justify-content: center;
+  background-color: rgba(0, 0, 0, 0.5);
+}
+.filter-panel {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 90vh;
+  max-height: 90vh;
+  overflow: hidden;
+  border-radius: 0 0 24upx 24upx;
+  background: #fff;
+}
+.filter-header {
+  flex-shrink: 0;
+  padding: 24upx 0;
+  text-align: center;
+  color: #333;
+  font-size: 32upx;
+  font-weight: 700;
+  border-bottom: 1upx solid #f0f0f0;
+}
+.filter-body {
+  flex: 1;
+  min-height: 0;
+  height: 0;
+  width: 100%;
+  box-sizing: border-box;
+}
+.filter-section {
+  padding: 30upx 40upx;
+  border-bottom: 1upx solid #f0f0f0;
+}
+.filter-title {
+  margin-bottom: 20upx;
+  color: #333;
+  font-size: 28upx;
+  font-weight: 700;
+}
+.filter-options {
+  display: flex;
+  flex-wrap: wrap;
+}
+.filter-option {
+  min-width: 120upx;
+  padding: 15upx 28upx;
+  margin-right: 18upx;
+  margin-top: 18upx;
+  border: 1upx solid #dce2e0;
+  border-radius: 28upx;
+  color: #60666d;
+  background: #fff;
+  font-size: 26upx;
+  text-align: center;
+}
+.filter-option.active {
+  color: #3385ff;
+  border-color: #3385ff;
+  background: #eef5ff;
+  font-weight: 700;
+}
+.filter-actions {
+  flex-shrink: 0;
+  display: flex;
+  padding: 30upx 40upx 40upx;
+  padding-bottom: calc(40upx + env(safe-area-inset-bottom));
+  border-top: 1upx solid #f0f0f0;
+  background: #fff;
+}
+.filter-action-btn {
+  flex: 1;
+  height: 80upx;
+  line-height: 80upx;
+  font-size: 28upx;
+  border-radius: 40upx;
+}
+.filter-action-btn::after {
+  border: none;
+}
+.clear-btn {
+  margin-right: 20upx;
+  color: #60666d;
+  background: #f5f5f5;
+}
+.confirm-btn {
+  color: #fff;
+  background: #3385ff;
+}
+</style>

+ 3 - 1
ghsApp/src/pages.json

@@ -337,7 +337,9 @@
 				{"path": "affirmGhsBook","style": {"navigationBarTitleText": "等待付款"}},
 				{"path": "affirmGhsBook","style": {"navigationBarTitleText": "等待付款"}},
 				{ "path": "forwardResult", "style": { "navigationBarTitleText": "冲销凭证" } },
 				{ "path": "forwardResult", "style": { "navigationBarTitleText": "冲销凭证" } },
 				{ "path": "freeRefund", "style": { "navigationBarTitleText": "退款选花材" } },
 				{ "path": "freeRefund", "style": { "navigationBarTitleText": "退款选花材" } },
-				{ "path": "freeRefundConfirm", "style": { "navigationBarTitleText": "退款确认" } }
+				{ "path": "freeRefundConfirm", "style": { "navigationBarTitleText": "退款确认" } },
+				{ "path": "freeRefundList", "style": { "navigationBarTitleText": "退款记录", "enablePullDownRefresh": true } },
+				{ "path": "freeRefundDetail", "style": { "navigationBarTitleText": "退款明细" } }
 			]
 			]
 		},
 		},
 		{
 		{