Kaynağa Gözat

Merge branch 'after-sale' into redesign‌-260706

shish 11 saat önce
ebeveyn
işleme
92eaa6182e

+ 166 - 77
ghsApp/src/admin/billing/forwardResult.vue

@@ -1,13 +1,33 @@
 <!--
-  隔天冲销结果页 / 退款凭证海报
-  用途:供货商端隔天冲销(fundType=2 返充余额)成功后,用 canvas 绘制退款凭证并展示、保存相册;
-        支持 mode=repost 从详情页重新生成凭证。
-  谁用:admin/billing 冲销流程、售后转冲销、凭证详情重发入口。
+  退款结果页 / 资金落地 / 返充凭证海报
+  用途:
+    1) 无原单退款成功后:选手选「我已线下转账」或「返充到客户余额」
+    2) 已返充余额:绘制退款凭证海报
+    3) 已原路退回:简单成功提示
+  谁用:freeRefundConfirm、售后详情/列表补发、隔天售后待落地资金。
 -->
 <template>
   <view class="forward-result-page">
-    <!-- fundType=2:展示凭证海报 -->
-    <appResult v-if="Number(fundType) === 2" title="退款成功">
+    <!-- 待资金落地:原路确认 or 返充余额 -->
+    <appResult v-if="showFundActions" title="退款成功">
+      <text class="simple-success">已退款 ¥{{ formatMoney(amount) }},请选择资金退还方式</text>
+      <text class="fund-tip">原单:{{ payWayLabel }}</text>
+      <button
+        class="admin-button-com big blue action-btn"
+        :disabled="fundActing"
+        @click="onMarkHasReturn"
+      >已线下转账</button>
+      <button
+        v-if="Number(couldReturn) === 1"
+        class="admin-button-com big action-btn"
+        :disabled="fundActing"
+        @click="onReturnBalance"
+      >不想转账,返充到客户余额</button>
+      <button class="admin-button-com big action-btn" @click="goBack">返回首页</button>
+    </appResult>
+
+    <!-- 已返充余额:凭证海报 -->
+    <appResult v-else-if="showPoster" title="退款成功">
       <view v-if="posterLoading" class="status-text">凭证生成中...</view>
       <view v-else-if="posterFail" class="fail-box">
         <text class="status-text fail-text">凭证生成失败,请重试</text>
@@ -37,7 +57,7 @@
       </button>
     </appResult>
 
-    <!-- 非返余额:简单成功提示 -->
+    <!-- 已原路 / 其它终态 -->
     <appResult v-else :title="pageTitle">
       <text class="simple-success">{{ simpleSuccessText }}</text>
       <button class="admin-button-com big action-btn" @click="goBack">
@@ -59,6 +79,7 @@ import dayjs from 'dayjs'
 import appResult from '@/components/app-result'
 import { getGatheringMiniCode } from '@/api/custom'
 import { getUserDet } from '@/api/member'
+import { markHasReturn, returnBalance } from '@/api/refund'
 
 /** 本地缓存键:跳转本页前写入,onLoad 与 URL 参数合并 */
 const STORAGE_KEY = 'forwardResultPayload'
@@ -70,7 +91,6 @@ export default {
   },
   data() {
     return {
-      fundType: 0,
       amount: 0,
       customId: 0,
       customName: '',
@@ -79,6 +99,12 @@ export default {
       forwardId: 0,
       orderId: 0,
       mode: '',
+      payWay: -1,
+      hasReturn: 0,
+      couldReturn: 1,
+      returnBalance: 0,
+      needFundAction: 0,
+      fundActing: false,
       posterUrl: '',
       posterLoading: false,
       posterFail: false,
@@ -87,27 +113,37 @@ export default {
     }
   },
   computed: {
-    /** 非返余额时的页面标题 */
+    /** 待选手选资金落地 */
+    showFundActions() {
+      return Number(this.hasReturn) === 0
+        && Number(this.returnBalance) === 0
+        && (Number(this.needFundAction) === 1 || Number(this.orderId) === 0)
+    },
+    /** 已返充:展示海报 */
+    showPoster() {
+      return Number(this.returnBalance) === 1
+    },
     pageTitle() {
-      const t = Number(this.fundType)
-      // 无原单退款(orderId=0)用「退款」文案
-      const free = Number(this.orderId) === 0
-      if (t === 1) return '原路退回成功'
-      if (t === 3) return free ? '退款成功' : '退款成功'
-      return free ? '退款成功' : '退款成功'
+      if (Number(this.hasReturn) === 1) return '原路退回成功'
+      return '退款成功'
     },
-    /** 非返余额时的说明文案 */
     simpleSuccessText() {
-      const t = Number(this.fundType)
       const amt = this.formatMoney(this.amount)
-      const free = Number(this.orderId) === 0
-      if (t === 1) return `已原路退回 ¥${amt}`
-      if (t === 3) {
-        return free
-          ? `已退款 ¥${amt},请线下转账`
-          : `已退款 ¥${amt},请线下转账`
+      if (Number(this.hasReturn) === 1) {
+        return `已原路退回 ¥${amt}`
+      }
+      return `退款成功,金额 ¥${amt}`
+    },
+    payWayLabel() {
+      const map = {
+        0: '线下微信',
+        1: '线下支付宝',
+        2: '余额',
+        3: '挂账',
+        4: '现金',
+        5: '银行卡'
       }
-      return free ? `退款成功,金额 ¥${amt}` : `退款成功,金额 ¥${amt}`
+      return map[Number(this.payWay)] || '线下转账'
     }
   },
   onLoad(option) {
@@ -130,23 +166,51 @@ export default {
       }
       const payload = Object.assign({}, cached, option || {})
 
-      this.fundType = Number(payload.fundType || 0)
       this.amount = parseFloat(payload.amount || payload.actPrice || 0) || 0
       this.customId = Number(payload.customId || 0)
       this.customName = this.decodeName(payload.customName || '')
       this.customBalance = parseFloat(payload.customBalance || 0) || 0
-      // forwardSn 优先取冲销单号,售后场景可传 refund orderSn
       this.forwardSn = payload.forwardSn || payload.orderSn || ''
       this.forwardId = Number(payload.forwardId || payload.refundId || 0)
       this.orderId = Number(payload.orderId || 0)
       this.mode = payload.mode || ''
+      this.payWay = Number(payload.payWay != null ? payload.payWay : -1)
+      this.hasReturn = Number(payload.hasReturn || 0)
+      this.couldReturn = payload.couldReturn != null ? Number(payload.couldReturn) : 1
+      this.returnBalance = Number(payload.returnBalance || 0)
+      // 兼容旧缓存:fundType=2 视为已返充
+      if (!this.returnBalance && Number(payload.fundType) === 2) {
+        this.returnBalance = 1
+      }
+      this.needFundAction = Number(payload.needFundAction || 0)
+      if (!this.needFundAction && Number(this.hasReturn) === 0 && Number(this.returnBalance) === 0 && Number(this.orderId) === 0) {
+        this.needFundAction = 1
+      }
 
-      // 返余额且有客户 id 时自动生成海报
-      if (Number(this.fundType) === 2 && this.customId > 0) {
+      if (this.showPoster && this.customId > 0) {
         this.generatePoster()
       }
     },
 
+    /** 同步缓存,避免返回再进丢状态 */
+    persistPayload() {
+      uni.setStorageSync(STORAGE_KEY, {
+        amount: this.amount,
+        customId: this.customId,
+        customBalance: this.customBalance,
+        customName: this.customName,
+        forwardSn: this.forwardSn,
+        forwardId: this.forwardId,
+        orderId: this.orderId,
+        mode: this.mode,
+        payWay: this.payWay,
+        hasReturn: this.hasReturn,
+        couldReturn: this.couldReturn,
+        returnBalance: this.returnBalance,
+        needFundAction: this.needFundAction
+      })
+    },
+
     /** 安全解码客户名 */
     decodeName(name) {
       if (!name) return ''
@@ -158,9 +222,7 @@ export default {
     },
 
     /**
-     * 金额展示:最多两位小数,去掉末尾多余的 0(如 10.00→10、10.50→10.5)
-     * @param {Number|String} val 金额
-     * @returns {String}
+     * 金额展示:最多两位小数,去掉末尾多余的 0
      */
     formatMoney(val) {
       const n = parseFloat(val)
@@ -168,9 +230,73 @@ export default {
       return String(parseFloat(n.toFixed(2)))
     },
 
+    /** 我已线下转账 → hasReturn=1 */
+    onMarkHasReturn() {
+      const that = this
+      if (!that.forwardId) {
+        that.$msg && that.$msg('缺少退款单')
+        return
+      }
+      that.$util.confirmModal({
+        content: '确认已线下转账给客户?',
+        okText: '已转账',
+        cancelText: '取消'
+      }, () => {
+        that.fundActing = true
+        uni.showLoading({ title: '提交中', mask: true })
+        markHasReturn({ id: that.forwardId }).then((res) => {
+          uni.hideLoading()
+          that.fundActing = false
+          if (res.code != 1) return
+          const data = res.data || {}
+          that.hasReturn = Number(data.hasReturn != null ? data.hasReturn : 1)
+          that.returnBalance = Number(data.returnBalance || 0)
+          that.needFundAction = 0
+          that.persistPayload()
+          that.$msg && that.$msg('已标记原路退回')
+        }).catch(() => {
+          uni.hideLoading()
+          that.fundActing = false
+        })
+      })
+    },
+
+    /** 不想转账,返充到客户余额 → returnBalance=1 + 海报 */
+    onReturnBalance() {
+      const that = this
+      if (!that.forwardId) {
+        that.$msg && that.$msg('缺少退款单')
+        return
+      }
+      that.$util.confirmModal({
+        content: '确认不线下转账,将退款返充到客户余额?',
+        okText: '确认返充',
+        cancelText: '取消'
+      }, () => {
+        that.fundActing = true
+        uni.showLoading({ title: '返充中', mask: true })
+        returnBalance({ id: that.forwardId }).then((res) => {
+          uni.hideLoading()
+          that.fundActing = false
+          if (res.code != 1) return
+          const data = res.data || {}
+          that.returnBalance = Number(data.returnBalance != null ? data.returnBalance : 1)
+          that.hasReturn = Number(data.hasReturn || 0)
+          that.needFundAction = 0
+          if (data.customBalance != null) {
+            that.customBalance = parseFloat(data.customBalance) || 0
+          }
+          that.persistPayload()
+          that.generatePoster()
+        }).catch(() => {
+          uni.hideLoading()
+          that.fundActing = false
+        })
+      })
+    },
+
     /**
      * 拉取小程序码与客户最新余额,下载二维码后绘制海报
-     * 副作用:更新 posterUrl / posterLoading / posterFail / customBalance
      */
     generatePoster() {
       const that = this
@@ -193,7 +319,6 @@ export default {
           if (!userRes || userRes.code != 1) {
             throw new Error((userRes && userRes.msg) || '获取客户信息失败')
           }
-          // 用接口最新余额覆盖路由传入值
           if (userRes.data && userRes.data.balance !== undefined && userRes.data.balance !== null) {
             that.customBalance = parseFloat(userRes.data.balance) || 0
           }
@@ -212,11 +337,6 @@ export default {
         })
     },
 
-    /**
-     * 下载远程图片到本地临时路径,供 canvas drawImage 使用
-     * @param {String} url 图片地址
-     * @returns {Promise<String>}
-     */
     downloadImage(url) {
       return new Promise((resolve, reject) => {
         uni.downloadFile({
@@ -235,22 +355,14 @@ export default {
       })
     },
 
-    /**
-     * 在 canvas 上绘制退款凭证海报并导出临时图片
-     * @param {String} qrPath 小程序码本地路径
-     * @returns {Promise<void>}
-     */
     drawPoster(qrPath) {
       const that = this
       const ctx = uni.createCanvasContext('forwardPosterCanvas', this)
       const w = that.canvasW
       const h = that.canvasH
-      // 顶部蓝区上下各收 4upx(画布宽 360 ≈ 750 设计宽一半,4upx≈2px)
       const headerH = 126
       const padX = 24
-      const contentW = w - padX * 2
 
-      // 整页白底 + 顶部蓝色标题区
       ctx.setFillStyle('#ffffff')
       ctx.fillRect(0, 0, w, h)
       ctx.setFillStyle('#3385ff')
@@ -264,34 +376,28 @@ export default {
       const nameText = that.customName || '客户'
       ctx.fillText(nameText.length > 14 ? nameText.substr(0, 14) + '…' : nameText, w / 2, 86)
 
-      // 退款金额(红色大号)
       ctx.setFillStyle('#e02020')
       ctx.setFontSize(44)
-      // 金额距顶再下移 4upx(≈2px),与蓝区收紧后的间距对齐
       ctx.fillText('¥' + that.formatMoney(that.amount), w / 2, 180)
 
       ctx.setFillStyle('#333333')
       ctx.setFontSize(22)
       ctx.fillText('已返充到余额', w / 2, 208)
 
-      // 信息区起始 Y
       let rowY = 252
       const labelX = padX
       const valueX = w - padX
       const rowGap = 36
 
       ctx.setTextAlign('left')
-      // 最新余额数字用主题蓝,突出返充后的可用余额
       that.drawInfoRow(ctx, '最新余额', '¥' + that.formatMoney(that.customBalance), labelX, valueX, rowY, false, '#3385ff')
       rowY += rowGap
 
-      // 冲销单号:标签左、单号右对齐;单号过长时缩小字号保证同一行完整展示
       ctx.setFillStyle('#666666')
       ctx.setFontSize(20)
       ctx.setTextAlign('left')
       ctx.fillText('退款单号', labelX, rowY)
       const snText = that.forwardSn || '-'
-      // 标签约占 88px,右侧留给单号;右对齐贴 valueX,与余额/日期一致
       const snMaxW = valueX - (labelX + 88)
       ctx.setFillStyle('#333333')
       const snSize = that.fitSnFontSize(ctx, snText, snMaxW, 16)
@@ -304,13 +410,11 @@ export default {
       that.drawInfoRow(ctx, '日期', dateStr, labelX, valueX, rowY, true)
       rowY += rowGap + 16
 
-      // 小程序码
       const qrSize = 180
       const qrX = (w - qrSize) / 2
       const qrY = rowY
       ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize)
 
-      // 底部提示
       ctx.setFillStyle('#999999')
       ctx.setFontSize(18)
       ctx.setTextAlign('center')
@@ -343,17 +447,6 @@ export default {
       })
     },
 
-    /**
-     * 绘制一行 label + value(value 右对齐)
-     * @param {Object} ctx canvas 上下文
-     * @param {String} label 左侧标签
-     * @param {String} value 右侧值
-     * @param {Number} labelX 标签 x
-     * @param {Number} valueX 值右边界 x
-     * @param {Number} y 基线 y
-     * @param {Boolean} valueSmall 值是否用小号字(16)
-     * @param {String} [valueColor='#333333'] 右侧数值颜色(如余额高亮蓝)
-     */
     drawInfoRow(ctx, label, value, labelX, valueX, y, valueSmall, valueColor) {
       ctx.setFillStyle('#666666')
       ctx.setFontSize(20)
@@ -365,14 +458,6 @@ export default {
       ctx.fillText(value, valueX, y)
     },
 
-    /**
-     * 冲销单号过长时缩小字号,保证同一行展示完整
-     * @param {Object} ctx canvas 上下文
-     * @param {String} text 单号文本
-     * @param {Number} maxWidth 可用宽度(px)
-     * @param {Number} startSize 起始字号
-     * @returns {Number} 最终使用的字号
-     */
     fitSnFontSize(ctx, text, maxWidth, startSize) {
       let size = startSize || 16
       const minSize = 10
@@ -384,7 +469,6 @@ export default {
       return size
     },
 
-    /** 保存海报到系统相册 */
     saveToAlbum() {
       const that = this
       if (!that.posterUrl) {
@@ -405,7 +489,6 @@ export default {
       })
     },
 
-    /** 返回上一页或工作台首页 */
     goBack() {
       if (this.mode === 'repost') {
         uni.navigateBack({ delta: 1 })
@@ -468,19 +551,25 @@ export default {
 }
 
 .simple-success {
-  margin-bottom: 40upx;
+  margin-bottom: 24upx;
   font-size: 30upx;
   color: #333333;
   text-align: center;
   line-height: 1.6;
 }
 
+.fund-tip {
+  margin-bottom: 40upx;
+  font-size: 34upx;
+  color: #505050;
+  text-align: center;
+}
+
 .action-btn {
   margin-bottom: 30upx;
   width: 100%;
 }
 
-/* 离屏 canvas,不参与页面布局 */
 .poster-canvas {
   position: fixed;
   left: -9999px;

+ 63 - 56
ghsApp/src/admin/billing/freeRefundConfirm.vue

@@ -1,8 +1,8 @@
 <!--
   无原单退款确认页
-  用途:选花材后确认金额,并选择退款方式 / 退货方式 / 是否返充余额,提交生成无关联销售单的售后单。
+  用途:选花材后确认金额,选择线下原路退款方式 / 退货方式;提交生成售后单。
+  资金落地(已线下转账 / 返充余额)放到成功页 forwardResult,本页不再选手选返充。
   谁用:admin/billing/freeRefund → 本页 → forwardResult。
-  样式:对齐 admin/billing/affirm.vue(商品区、表单行、底部栏)。
 -->
 <template>
   <view class="app-content">
@@ -39,7 +39,7 @@
                 返回修改
               </view>
               <view class="describe-view">
-                <text>合计 ¥{{ allPrice }}</text>
+                <text>合计 ¥{{ refundTotalPrice }}</text>
                 <text>,{{ selectList.length }}种</text>
                 <text v-if="Number(totalItemNum.big) > 0">,{{ parseFloat(totalItemNum.big) }}扎</text>
                 <text v-if="Number(totalItemNum.small) > 0">,{{ parseFloat(totalItemNum.small) }}支</text>
@@ -63,7 +63,7 @@
           </tui-list-cell>
         </view>
 
-        <!-- 三组选项:交互对齐 affirm 收款方式按钮行 -->
+        <!-- 退款方式=线下原路通道;返充改到成功页 -->
         <view class="module-com input-line-wrap option-module">
           <tui-list-cell class="line-cell option-cell" :hover="false" :arrow="false">
             <view class="tui-title">退款方式</view>
@@ -89,29 +89,13 @@
               >{{ item.name }}</button>
             </view>
           </tui-list-cell>
-          <tui-list-cell class="line-cell option-cell" :hover="false" :arrow="false">
-            <view class="tui-title">返充余额</view>
-            <view class="option-btns">
-              <button
-                v-for="item in toBalanceList"
-                :key="'bal-' + item.id"
-                class="admin-button-com middle pay-way-btn"
-                :class="form.toBalance === item.id ? 'blue' : 'default'"
-                @click="form.toBalance = item.id"
-              >{{ item.name }}</button>
-            </view>
-          </tui-list-cell>
-        </view>
-
-        <view class="tip-box">
-          无关联销售单。「库存退回」会加库存;「返充到余额」会返充到客户余额,选「否」请线下转账给客户。
         </view>
       </view>
 
       <view class="under-bar">
         <view class="price-view">
           <text class="price-title"></text>
-          <text class="price-number"><text class="large">¥{{ form.price || '0' }}</text></text>
+          <text class="price-number"><text class="large">¥{{ form.price || refundTotalPrice || '0' }}</text></text>
         </view>
         <button class="admin-button-com blue big" @click="onSubmitTap">确认退款</button>
       </view>
@@ -132,22 +116,17 @@ export default {
     return {
       autoLoad: false,
       selectJobType: 'freeRefund',
-      // 与开单/支出一致:0微信 1支付宝 4现金 5银行卡
+      // 与开单/支出一致:0微信 1支付宝 4现金 5银行卡(均为线下原路)
       payWayList: [
         { name: '线下微信', id: 0 },
         { name: '线下支付宝', id: 1 },
         { name: '现金', id: 4 },
         { name: '银行卡', id: 5 }
       ],
-      // 0库存不退(默认) 1库存退回
+      // 0库存不退 1库存退回;无默认,须选手选
       returnWayList: [
-        { name: '库存不退', id: 0 },
-        { name: '库存退回', id: 1 }
-      ],
-      // 无默认,须选手选
-      toBalanceList: [
-        { name: '是', id: 1 },
-        { name: '否', id: 0 }
+        { name: '库存不退回', id: 0 },
+        { name: '库存要退回', id: 1 }
       ],
       form: {
         customId: '',
@@ -155,8 +134,7 @@ export default {
         price: '',
         remark: '',
         payWay: -1,
-        returnWay: 0,
-        toBalance: -1
+        returnWay: -1
       }
     }
   },
@@ -172,6 +150,20 @@ export default {
         })
       }
       return { big, small }
+    },
+    /**
+     * 确认页合计:直接用已选花材单价×数量,不走 mixin allPrice。
+     * 原因:mixin 的 allPrice 要查 globalItemData,本页未加载花材目录会得到 0。
+     */
+    refundTotalPrice() {
+      let price = 0
+      if (!this.selectList) {
+        return 0
+      }
+      this.selectList.forEach((item) => {
+        price += Number(this.getLineAmount(item)) || 0
+      })
+      return Math.round(price * 100) / 100
     }
   },
   onLoad(option) {
@@ -187,7 +179,7 @@ export default {
       uni.setNavigationBarTitle({ title: '退款确认' })
     }
     this.$nextTick(() => {
-      this.form.price = this.allPrice > 0 ? String(this.allPrice) : ''
+      this.syncRefundPrice()
     })
   },
   methods: {
@@ -202,12 +194,16 @@ export default {
       const small = Number(item.smallCount) || 0
       return ((big + small) * unit).toFixed(2)
     },
+    /** 用已选花材合计回填退款金额 */
+    syncRefundPrice() {
+      const total = this.refundTotalPrice
+      this.form.price = total > 0 ? String(total) : ''
+    },
     gobackEvent() {
       uni.navigateBack({})
     },
     /**
-     * 校验三组选项后提交
-     * returnWay→refundType;toBalance→fundType;payWay原样上报
+     * 校验选项后弹框确认,再提交;资金三态在成功页落地
      */
     onSubmitTap() {
       if (this.$util.isEmpty(this.selectList)) {
@@ -227,13 +223,20 @@ export default {
         this.$msg('请选择退款方式')
         return
       }
-      if (this.form.toBalance === -1 || this.form.toBalance === '' || this.form.toBalance === null) {
-        this.$msg('请选择是否返充到余额')
+      if (this.form.returnWay === -1 || this.form.returnWay === '' || this.form.returnWay === null) {
+        this.$msg('请选择退货方式')
         return
       }
-      this.submitFreeRefund()
+      const that = this
+      const returnLabel = Number(this.form.returnWay) === 1 ? '库存退回' : '库存不退'
+      that.$util.confirmModal({
+        title: '确认退款',
+        content: `退款 ¥${price},${returnLabel},确认提交?`
+      }, () => {
+        that.submitFreeRefund()
+      })
     },
-    /** 组装明细并调用 create-free-refund */
+    /** 组装明细并调用 create-free-refund,跳转成功页待选资金动作 */
     submitFreeRefund() {
       const that = this
       const product = []
@@ -264,8 +267,6 @@ export default {
       }
       // 库存退回=退货退款(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 })
       createFreeRefund({
@@ -274,9 +275,7 @@ export default {
         remark: this.form.remark,
         payWay: this.form.payWay,
         returnWay: this.form.returnWay,
-        toBalance: this.form.toBalance,
         refundType: refundType,
-        fundType: fundType,
         product: JSON.stringify(product)
       }).then((res) => {
         uni.hideLoading()
@@ -285,29 +284,37 @@ export default {
         }
         that.removeFromSaveDirect && that.removeFromSaveDirect()
         const data = res.data || {}
-        const goFundType = data.fundType != null ? data.fundType : fundType
-        uni.setStorageSync('forwardResultPayload', {
-          fundType: goFundType,
+        const payload = {
           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
-        })
+          orderId: 0,
+          payWay: data.payWay != null ? data.payWay : that.form.payWay,
+          hasReturn: data.hasReturn != null ? data.hasReturn : 0,
+          couldReturn: data.couldReturn != null ? data.couldReturn : 1,
+          returnBalance: data.returnBalance != null ? data.returnBalance : 0,
+          needFundAction: data.needFundAction != null ? data.needFundAction : 1
+        }
+        uni.setStorageSync('forwardResultPayload', payload)
         that.$util.pageTo({
           url: '/admin/billing/forwardResult',
           type: 2,
           query: {
-            fundType: goFundType,
-            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
+            amount: payload.amount,
+            customId: payload.customId,
+            customBalance: payload.customBalance,
+            customName: encodeURIComponent(payload.customName || ''),
+            forwardSn: payload.forwardSn,
+            forwardId: payload.forwardId,
+            orderId: 0,
+            payWay: payload.payWay,
+            hasReturn: payload.hasReturn,
+            couldReturn: payload.couldReturn,
+            returnBalance: payload.returnBalance,
+            needFundAction: payload.needFundAction
           }
         })
       }).catch(() => {

+ 64 - 21
ghsApp/src/admin/billing/freeRefundDetail.vue

@@ -18,7 +18,11 @@
         <view class="refund-value">{{ Number(info.refundType) === 1 ? '库存退回' : '库存不退' }}</view>
       </view>
       <view class="refund-row">
-        <view class="refund-label">资金处理</view>
+        <view class="refund-label">原路通道</view>
+        <view class="refund-value">{{ payWayText || '-' }}</view>
+      </view>
+      <view class="refund-row">
+        <view class="refund-label">返充</view>
         <view class="refund-value">{{ fundText }}</view>
       </view>
       <view class="refund-row">
@@ -51,12 +55,17 @@
     </view>
 
     <view class="refund-actions">
+      <button class="admin-button-com big default refund-actions__btn" @click="goBack">返回</button>
+      <button
+        v-if="canFundAction"
+        class="admin-button-com big blue refund-actions__btn"
+        @click="goFundAction"
+      >退还方式</button>
       <button
-        v-if="Number(info.fundType) === 2"
+        v-if="canShowPoster"
         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>
@@ -73,13 +82,34 @@ export default {
     }
   },
   computed: {
+    /** 原路状态 */
     fundText() {
-      const t = Number(this.info.fundType)
-      if (t === 2) return '返充到余额'
-      if (t === 3) return '请线下转账'
-      if (t === 1) return '原路退回'
+      if (Number(this.info.returnBalance) === 1) return '已返充到余额'
+      if (Number(this.info.hasReturn) === 1) return '已原路退回'
+      if (Number(this.info.status) === 1) return '待确认'
       return '-'
     },
+    /** 原路通道 */
+    payWayText() {
+      const map = {
+        0: '线下微信',
+        1: '线下支付宝',
+        2: '余额',
+        3: '挂账',
+        4: '现金',
+        5: '银行卡'
+      }
+      const pw = Number(this.info.payWay)
+      return map[pw] || ''
+    },
+    canFundAction() {
+      return Number(this.info.status) === 1
+        && Number(this.info.hasReturn) === 0
+        && Number(this.info.returnBalance) === 0
+    },
+    canShowPoster() {
+      return Number(this.info.status) === 1 && Number(this.info.returnBalance) === 1
+    },
     statusText() {
       const map = { 0: '待审核', 1: '已通过', 2: '已驳回', 3: '已取消' }
       return map[this.info.status] || ''
@@ -105,30 +135,43 @@ export default {
         this.product = res.data.itemList || []
       })
     },
-    /** 返余额凭证补发 */
-    goPoster() {
-      uni.setStorageSync('forwardResultPayload', {
-        fundType: this.info.fundType,
+    buildPayload(mode) {
+      return {
         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'
+        payWay: this.info.payWay,
+        hasReturn: this.info.hasReturn || 0,
+        couldReturn: this.info.couldReturn != null ? this.info.couldReturn : 1,
+        returnBalance: this.info.returnBalance || 0,
+        needFundAction: this.canFundAction ? 1 : 0,
+        mode: mode || ''
+      }
+    },
+    /** 待落地资金:进结果页二选一 */
+    goFundAction() {
+      const payload = this.buildPayload('')
+      uni.setStorageSync('forwardResultPayload', payload)
+      this.$util.pageTo({
+        url: '/admin/billing/forwardResult',
+        query: Object.assign({}, payload, {
+          customName: encodeURIComponent(payload.customName || '')
+        })
       })
+    },
+    /** 返余额凭证补发 */
+    goPoster() {
+      const payload = this.buildPayload('repost')
+      uni.setStorageSync('forwardResultPayload', payload)
       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,
+        query: Object.assign({}, payload, {
+          customName: encodeURIComponent(payload.customName || ''),
           mode: 'repost'
-        }
+        })
       })
     },
     goBack() {

+ 105 - 39
ghsApp/src/admin/billing/freeRefundList.vue

@@ -90,7 +90,11 @@
               <text class="meta-value" v-else style="color: #3385FF;font-weight: bold;">没有订单</text>
             </view>
             <view class="meta-item">
-              <text class="meta-label">资金</text>
+              <text class="meta-label">原单</text>
+              <text class="meta-value">{{ payWayText(item) }}</text>
+            </view>
+            <view class="meta-item">
+              <text class="meta-label">退还</text>
               <text class="meta-value">{{ fundText(item) }}</text>
             </view>
             <view class="meta-item" v-if="item.sameDay == 0">
@@ -116,16 +120,27 @@
             <text class="remark-text">{{ item.remark }}</text>
           </view>
 
-          <!-- 底栏:凭证(左,非返余额/未通过时灰色不可点)+ 状态(右下角) -->
+          <!-- 底栏:确认退还方式 + 查看凭证(不可用时灰色)+ 状态 -->
           <view class="card-foot">
-            <view
-              class="poster-btn-wrap"
-              @click.stop="onPosterClick(item)"
-            >
-              <button
-                class="admin-button-com middle poster-btn"
-                :class="canViewPoster(item) ? 'blue' : 'disable'"
-              >查看凭证</button>
+            <view class="card-foot-btns">
+              <view
+                class="poster-btn-wrap"
+                @click.stop="onFundClick(item)"
+              >
+                <button
+                  class="admin-button-com middle poster-btn"
+                  :class="canFundAction(item) ? 'blue' : 'disable'"
+                >退还方式</button>
+              </view>
+              <view
+                class="poster-btn-wrap"
+                @click.stop="onPosterClick(item)"
+              >
+                <button
+                  class="admin-button-com middle poster-btn"
+                  :class="canViewPoster(item) ? 'blue' : 'disable'"
+                >查看凭证</button>
+              </view>
             </view>
             <text
               class="status-chip"
@@ -244,15 +259,28 @@ export default {
       return v != null ? parseFloat(v) : 0
     },
     init(){},
+    /** 原路状态:已原路 / 已返充 / 待确认 */
     fundText(item) {
-      if (Number(item.sameDay) !== 0) {
-        return '原路退回'
+      if (Number(item.returnBalance) === 1) return '已返充余额'
+      if (Number(item.hasReturn) === 1) return '已原路退回'
+      if (Number(item.status) === 1) return '待确认'
+      return '-'
+    },
+    /** 原路通道:售后单 payWay 快照 */
+    payWayText(item) {
+      const map = {
+        0: '线下微信',
+        1: '线下支付宝',
+        2: '余额',
+        3: '挂账',
+        4: '现金',
+        5: '银行卡'
+      }
+      const pw = Number(item && item.payWay)
+      if (map[pw] != null) {
+        return map[pw]
       }
-      const t = Number(item.fundType)
-      if (t === 2) return '返充余额'
-      if (t === 3) return '线下自行转账'
-      if (t === 1) return '原路退回'
-      return '隔天售后'
+      return '-'
     },
     statusText(status) {
       const map = { 0: '待审核', 1: '已通过', 2: '已驳回', 3: '已取消' }
@@ -348,22 +376,23 @@ export default {
       this.resetList()
       this._list()
     },
+    /** 统一进售后详情;无原单/有原单都由 refundDetail 按数据展示 */
     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 }
       })
     },
-    /** 已通过且返充余额才可查看凭证 */
+    /** 已通过且已返充余额才可查看凭证 */
     canViewPoster(item) {
-      return item && Number(item.status) === 1 && Number(item.fundType) === 2
+      return item && Number(item.status) === 1 && Number(item.returnBalance) === 1
+    },
+    /** 已通过且尚未原路/返充时才可确认退还方式 */
+    canFundAction(item) {
+      return item
+        && Number(item.status) === 1
+        && Number(item.hasReturn) === 0
+        && Number(item.returnBalance) === 0
     },
     /** 灰色态不可点;可查看时跳转补发海报 */
     onPosterClick(item) {
@@ -372,38 +401,69 @@ export default {
       }
       this.goPoster(item)
     },
+    /** 灰色态不可点;可操作时进结果页二选一 */
+    onFundClick(item) {
+      if (!this.canFundAction(item)) {
+        return
+      }
+      this.goFundAction(item)
+    },
     /**
      * 返充余额凭证:跳转 forwardResult 补发海报
-     * 有原单带 orderId=0 亦可生成(海报不依赖销售单 id)
      */
     goPoster(item) {
       if (!item || !item.id) {
         return
       }
       const payload = {
-        fundType: item.fundType,
         amount: item.refundPrice,
         customId: item.customId,
         customName: item.customName || '',
         forwardSn: item.orderSn || '',
         forwardId: item.id || 0,
-        // 无原单=0 显示「退款成功」;有原单列表未带销售单 id,同样用 0 不影响凭证内容
         orderId: 0,
+        payWay: item.payWay,
+        hasReturn: item.hasReturn || 0,
+        couldReturn: item.couldReturn != null ? item.couldReturn : 1,
+        returnBalance: item.returnBalance || 1,
+        needFundAction: 0,
         mode: 'repost'
       }
       uni.setStorageSync('forwardResultPayload', payload)
       this.$util.pageTo({
         url: '/admin/billing/forwardResult',
-        query: {
-          fundType: payload.fundType,
-          amount: payload.amount,
-          customId: payload.customId,
+        query: Object.assign({}, payload, {
           customName: encodeURIComponent(payload.customName || ''),
-          forwardSn: payload.forwardSn,
-          forwardId: payload.forwardId,
-          orderId: payload.orderId,
           mode: 'repost'
-        }
+        })
+      })
+    },
+    /**
+     * 待落地资金:跳转 forwardResult 选「已线下转账 / 返充余额」
+     */
+    goFundAction(item) {
+      if (!item || !item.id) {
+        return
+      }
+      const payload = {
+        amount: item.refundPrice,
+        customId: item.customId,
+        customName: item.customName || '',
+        forwardSn: item.orderSn || '',
+        forwardId: item.id || 0,
+        orderId: 0,
+        payWay: item.payWay,
+        hasReturn: item.hasReturn || 0,
+        couldReturn: item.couldReturn != null ? item.couldReturn : 1,
+        returnBalance: item.returnBalance || 0,
+        needFundAction: 1
+      }
+      uni.setStorageSync('forwardResultPayload', payload)
+      this.$util.pageTo({
+        url: '/admin/billing/forwardResult',
+        query: Object.assign({}, payload, {
+          customName: encodeURIComponent(payload.customName || '')
+        })
       })
     },
     _list() {
@@ -691,12 +751,18 @@ export default {
   padding-top: 16upx;
   border-top: 1upx solid #f0f2f5;
 }
+.card-foot-btns {
+  display: flex;
+  align-items: center;
+  flex: 1;
+  margin-right: 12upx;
+}
 .poster-btn-wrap {
   flex-shrink: 0;
-  margin-right: 16upx;
+  margin-right: 28upx;
 }
 .poster-btn {
-  width: 168upx;
+  width: 200upx;
   height: 56upx;
   line-height: 56upx;
   margin: 0;

+ 10 - 0
ghsApp/src/api/refund/index.js

@@ -53,4 +53,14 @@ export const checkNextDayEligible = data => {
 /** 无原单退款(工作台「退款」:选客户+花材,不关联销售单) */
 export const createFreeRefund = data => {
 	return https.post('/refund/create-free-refund', data)
+}
+
+/** 确认已线下原路退回(现金/银行卡等人工确认 hasReturn=1) */
+export const markHasReturn = data => {
+	return https.post('/refund/mark-has-return', data)
+}
+
+/** 不想线下转账:返充到客户余额(returnBalance=1) */
+export const returnBalance = data => {
+	return https.post('/refund/return-balance', data)
 }

+ 27 - 97
ghsApp/src/components/forward-options-popup.vue

@@ -1,22 +1,12 @@
 <!--
-  隔天售后 / 无原单退款 资金选项弹框
-  用途:隔天售后/无原单退款时选择资金方式(原路/返余额/线下自行转账)
+  隔天售后确认弹框
+  用途:隔天/已结账售后二次确认;资金默认原路,不再选手选 fundType。
 -->
 <template>
 	<view v-if="show" class="mask" @click="onCancel">
 		<view class="panel" @click.stop>
 			<view class="title">退款确认</view>
 			<view class="tip">{{ tipText }}</view>
-			<view class="label">资金退回方式</view>
-			<view
-				v-for="(opt, idx) in fundOptions"
-				:key="idx"
-				class="opt"
-				:class="{ active: fundType === opt.value }"
-				@click="fundType = opt.value"
-			>
-				<text>{{ opt.label }}</text>
-			</view>
 			<view class="btns">
 				<button class="popup-btn cancel" @click="onCancel">取消</button>
 				<button class="popup-btn confirm" @click="onOk">确认退款</button>
@@ -30,49 +20,20 @@ export default {
 	name: 'forward-options-popup',
 	props: {
 		show: { type: Boolean, default: false },
-		// 是否在线支付原单(可原路退)
-		allowOriginal: { type: Boolean, default: false },
-		// 挂账/余额强制返余额时隐藏其它选项
-		forceBalance: { type: Boolean, default: false },
-		// 已结账单当天售后也走售后
+		// 已结账单当天售后也走隔天口径
 		orderCleared: { type: Boolean, default: false },
-		// free=无原单退款文案;默认隔天售后
+		// free=无原单退款文案(兼容旧调用)
 		mode: { type: String, default: '' }
 	},
-	data() {
-		return {
-			fundType: 2
-		}
-	},
 	computed: {
 		tipText() {
 			if (this.mode === 'free') {
-				return '无关联销售单:退货回库;资金按下方选择处理。'
+				return '无关联销售单:退货回库;资金请在成功页确认原路或返充余额。'
 			}
 			if (this.orderCleared) {
-				return '订单已结账,本次售后不减订单实付,只会算作今天退款'
-			}
-			return '本次售后不减订单实付,只会算作今天退款'
-		},
-		fundOptions() {
-			if (this.forceBalance) {
-				return [{ value: 2, label: '返充到客户余额' }]
-			}
-			const list = [
-				{ value: 2, label: '返充到客户余额' },
-				{ value: 3, label: '线下自行转账' }
-			]
-			if (this.allowOriginal) {
-				list.unshift({ value: 1, label: '原路退回' })
-			}
-			return list
-		}
-	},
-	watch: {
-		show(v) {
-			if (v) {
-				this.fundType = this.forceBalance || !this.allowOriginal ? 2 : 1
+				return '订单已结账,本次售后不减订单实付,只会算作今天退款;资金按原支付方式原路处理。'
 			}
+			return '本次售后不减订单实付,只会算作今天退款;资金按原支付方式原路处理。'
 		}
 	},
 	methods: {
@@ -80,7 +41,7 @@ export default {
 			this.$emit('cancel')
 		},
 		onOk() {
-			this.$emit('confirm', { fundType: this.fundType })
+			this.$emit('confirm', {})
 		}
 	}
 }
@@ -90,84 +51,53 @@ export default {
 .mask {
 	position: fixed;
 	left: 0;
-	top: 0;
 	right: 0;
+	top: 0;
 	bottom: 0;
 	background: rgba(0, 0, 0, 0.45);
-	z-index: 999;
+	z-index: 1000;
 	display: flex;
 	align-items: flex-end;
+	justify-content: center;
 }
 .panel {
 	width: 100%;
 	background: #fff;
 	border-radius: 24upx 24upx 0 0;
-	padding: 30upx;
-	padding-bottom: calc(30upx + env(safe-area-inset-bottom));
+	padding: 36upx 32upx calc(24upx + env(safe-area-inset-bottom));
 	box-sizing: border-box;
 }
 .title {
-	font-size: 38upx;
+	font-size: 34upx;
 	font-weight: bold;
 	color: #333;
+	text-align: center;
+	margin-bottom: 16upx;
 }
 .tip {
-	margin-top: 12upx;
-	font-size: 30upx;
-	color: #999;
-}
-.label {
-	margin-top: 28upx;
-	margin-bottom: 12upx;
-	font-size: 28upx;
-	color: black;
-}
-.opt {
-	padding: 22upx 24upx;
-	margin-top: 12upx;
-	border: 1upx solid #e5e5e5;
-	border-radius: 12upx;
-	font-size: 38upx;
-	color: #333;
-}
-.opt.active {
-	border-color: #3385ff;
-	color: #3385ff;
-	background: #f0f7ff;
+	font-size: 26upx;
+	color: #666;
+	line-height: 1.5;
+	margin-bottom: 28upx;
 }
 .btns {
 	display: flex;
-	flex-direction: row;
-	align-items: stretch;
-	margin-top: 30upx;
+	justify-content: space-between;
 }
-/* 底部按钮固定高度,避免 uni button 默认过扁 */
 .popup-btn {
 	flex: 1;
-	height: 88upx;
-	line-height: 88upx;
-	padding: 0;
-	margin: 0;
-	margin-left: 20upx;
-	font-size: 30upx;
+	height: 80upx;
+	line-height: 80upx;
 	border-radius: 12upx;
-	box-sizing: border-box;
-	text-align: center;
-}
-.popup-btn:first-child {
-	margin-left: 0;
-}
-.popup-btn::after {
-	border: none;
+	font-size: 30upx;
+	margin: 0 10upx;
 }
 .popup-btn.cancel {
-	color: #3385ff;
-	background: #fff;
-	border: 2upx solid #3385ff;
+	background: #f5f5f5;
+	color: #666;
 }
 .popup-btn.confirm {
-	color: #fff;
 	background: #3385ff;
-	border: 2upx solid #3385ff;
+	color: #fff;
 }
 </style>

+ 21 - 41
ghsApp/src/pagesOrder/refund.vue

@@ -202,11 +202,9 @@
 			<button class="action-btn confirm-btn" @tap="confirmRefund">确定</button>
 		</view>
 
-		<!-- 隔天冲销资金选项 -->
+		<!-- 隔天售后确认(不再选手选资金方式,默认原路) -->
 		<forward-options-popup
 			:show="showForwardPop"
-			:allow-original="allowOriginalFund"
-			:force-balance="forceBalanceFund"
 			:order-cleared="orderCleared"
 			@cancel="showForwardPop = false"
 			@confirm="onForwardConfirm"
@@ -241,8 +239,6 @@ export default {
 			// 隔天冲销预检
 			nextDayEligible: false,
 			showForwardPop: false,
-			allowOriginalFund: false,
-			forceBalanceFund: false,
 			// 已结账(当天也走冲销)
 			orderCleared: false
 		};
@@ -356,10 +352,8 @@ export default {
 			};
 			// 非当天 / 已结清 → 必须隔天售后(接口预检 + 本地兜底)
 			if (this.nextDayEligible || this.isMustNextDayOrder()) {
-				// 预检失败时用订单字段补齐资金弹窗参数
 				if (!this.nextDayEligible) {
 					this.orderCleared = Number(this.orderInfo.clearId) > 0
-					this.applyNextDayFundOptions(this.orderInfo)
 				}
 				this.showForwardPop = true
 				return
@@ -381,18 +375,6 @@ export default {
 			const payTime = (this.orderInfo && this.orderInfo.payTime) || ''
 			return !!payTime && payTime !== '0000-00-00 00:00:00'
 		},
-		/**
-		 * 隔天资金弹窗选项:余额/挂账强制返余额;微信/支付宝线上付允许原路(onlinePay=2)
-		 * @param {Object} info 订单或预检返回字段(需含 payWay、onlinePay)
-		 */
-		applyNextDayFundOptions(info) {
-			const payWay = Number((info && info.payWay) || 0)
-			const online = Number((info && info.onlinePay) || 0)
-			// payWay: 2余额 3挂账 → 只能返余额
-			this.forceBalanceFund = (payWay === 2 || payWay === 3)
-			// onlinePay: 1非线上 2线上;payWay: 0微信 1支付宝
-			this.allowOriginalFund = online === 2 && (payWay === 0 || payWay === 1) && !this.forceBalanceFund
-		},
 		/**
 		 * 本地判断是否必须隔天售后:非当天支付 / 已结账单 / 挂账已结清
 		 * 仅用 payTime;无支付时间由 hasValidPayTime 先行拦截
@@ -418,13 +400,12 @@ export default {
 			return payDay !== today
 		},
 		/**
-		 * 隔天冲销确认:写入 sameDay=0 + fundType 后提交
+		 * 隔天售后确认:仅写 sameDay=0,资金按原单原路处理
 		 */
-		onForwardConfirm(opt) {
+		onForwardConfirm() {
 			this.showForwardPop = false
 			const params = Object.assign({}, this._pendingRefundParams || {}, {
-				sameDay: 0,
-				fundType: opt.fundType
+				sameDay: 0
 			})
 			this.submitRefund(params)
 		},
@@ -439,31 +420,32 @@ export default {
 						return
 					}
 					const data = res.data || {}
-					// 隔天返余额:进凭证海报页
-					if (Number(data.sameDay) === 0 && Number(data.fundType) === 2) {
-						uni.setStorageSync('forwardResultPayload', {
-							fundType: data.fundType,
+					const needFund = Number(data.needFundAction) === 1
+						|| (Number(data.hasReturn) === 0 && Number(data.returnBalance) === 0 && Number(data.sameDay) === 0 && Number(data.couldReturn) === 1)
+					const showPoster = Number(data.returnBalance) === 1
+					// 待资金落地或已返充:进结果页
+					if (needFund || showPoster) {
+						const payload = {
 							amount: data.refundPrice || refundParams.price,
 							customId: data.customId || that.orderInfo.customId,
 							customBalance: data.customBalance != null ? data.customBalance : '',
 							customName: data.customName || that.orderInfo.customName || '',
 							forwardSn: data.orderSn || '',
 							forwardId: data.id || 0,
-							orderId: data.orderId || that.option.id
-						})
+							orderId: data.orderId || that.option.id,
+							payWay: data.payWay != null ? data.payWay : that.orderInfo.payWay,
+							hasReturn: data.hasReturn || 0,
+							couldReturn: data.couldReturn != null ? data.couldReturn : 1,
+							returnBalance: data.returnBalance || 0,
+							needFundAction: needFund ? 1 : 0
+						}
+						uni.setStorageSync('forwardResultPayload', payload)
 						that.$util.pageTo({
 							url: '/admin/billing/forwardResult',
 							type: 2,
-							query: {
-								fundType: data.fundType,
-								amount: data.refundPrice || refundParams.price,
-								customId: data.customId || that.orderInfo.customId,
-								customBalance: data.customBalance != null ? data.customBalance : '',
-								customName: encodeURIComponent(data.customName || that.orderInfo.customName || ''),
-								forwardSn: data.orderSn || '',
-								forwardId: data.id || 0,
-								orderId: data.orderId || that.option.id
-							}
+							query: Object.assign({}, payload, {
+								customName: encodeURIComponent(payload.customName || '')
+							})
 						})
 						return
 					}
@@ -508,8 +490,6 @@ export default {
 				if (r.code == 1 && r.data && r.data.ok) {
 					this.nextDayEligible = true
 					this.orderCleared = Number(r.data.orderCleared) === 1
-					// 预检成功:按原单支付方式决定弹窗是否展示「原路退回」
-					this.applyNextDayFundOptions(r.data)
 				} else {
 					this.nextDayEligible = false
 					this.orderCleared = false

+ 9 - 11
ghsApp/src/pagesOrder/refundAllList.vue

@@ -23,7 +23,7 @@
               <text>状态:{{ item.status==0?'待审核':item.status==1?'已通过':item.status==2?'已驳回':item.status==3 ? '已取消':'' }}</text>
               <!-- 已通过且返充余额:列表可直接查看凭证 -->
               <text
-                v-if="Number(item.refundInfo.status) === 1 && Number(item.refundInfo.fundType) === 2"
+                v-if="Number(item.refundInfo.status) === 1 && Number(item.refundInfo.returnBalance) === 1"
                 class="poster-link"
                 @click.stop="goPoster(item.refundInfo)"
               >查看凭证</text>
@@ -182,35 +182,33 @@ export default {
     },
     /**
      * 返充余额凭证:跳转 forwardResult 补发海报
-     * @param {Object} refundInfo 售后主单(含 fundType/customId/orderSn)
+     * @param {Object} refundInfo 售后主单(含 returnBalance/customId/orderSn)
      */
     goPoster(refundInfo) {
       if (!refundInfo || !refundInfo.id) {
         return
       }
       const payload = {
-        fundType: refundInfo.fundType,
         amount: refundInfo.refundPrice,
         customId: refundInfo.customId,
         customName: refundInfo.customName || '',
         forwardSn: refundInfo.orderSn || '',
         forwardId: refundInfo.id || 0,
         orderId: 0,
+        payWay: refundInfo.payWay,
+        hasReturn: refundInfo.hasReturn || 0,
+        couldReturn: refundInfo.couldReturn != null ? refundInfo.couldReturn : 1,
+        returnBalance: refundInfo.returnBalance || 1,
+        needFundAction: 0,
         mode: 'repost'
       }
       uni.setStorageSync('forwardResultPayload', payload)
       this.$util.pageTo({
         url: '/admin/billing/forwardResult',
-        query: {
-          fundType: payload.fundType,
-          amount: payload.amount,
-          customId: payload.customId,
+        query: Object.assign({}, payload, {
           customName: encodeURIComponent(payload.customName || ''),
-          forwardSn: payload.forwardSn,
-          forwardId: payload.forwardId,
-          orderId: payload.orderId,
           mode: 'repost'
-        }
+        })
       })
     },
     getRefundData() {

+ 99 - 55
ghsApp/src/pagesOrder/refundDetail.vue

@@ -1,17 +1,22 @@
 <!--
-  售后详情页:展示退款申请信息、图片预览与审核操作
-  供供货商在 ghsApp 查看/处理客户售后单
+  售后详情页:展示退款申请信息、图片预览与审核操作;无原单退款也走本页。
+  供供货商在 ghsApp 查看/处理客户售后单(含工作台「退款记录」入口)
 -->
 <template>
 	<view class="refund-page">
 		<view class="refund-card">
+			<view class="refund-row" v-if="isFreeRefund">
+				<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">{{ refundType == 1 ? '退货并退款' : '只退款' }}</view>
+				<view class="refund-label">{{ isFreeRefund ? '退货方式' : '退款方式' }}</view>
+				<view class="refund-value">{{ refundTypeLabel }}</view>
 			</view>
 
-			<view class="refund-section" v-if="refundType == 1">
-				<view class="refund-label refund-section__title">退货商品</view>
+			<view class="refund-section" v-if="!$util.isEmpty(product)">
+				<view class="refund-label refund-section__title">{{ isFreeRefund ? '花材明细' : '退货商品' }}</view>
 				<view class="refund-product-list">
 					<view class="refund-product-item" v-for="(res, i) in product" :key="i">
 						<view class="refund-product-item__name">
@@ -29,7 +34,7 @@
 				<view class="refund-value">{{ info.addTime ? info.addTime.substr(5, 11) : '' }}</view>
 			</view>
 
-			<view class="refund-row">
+			<view class="refund-row" v-if="!isFreeRefund">
 				<view class="refund-label">申请原因</view>
 				<view class="refund-value refund-value--inline">
 					<text>{{ causeText }}</text>
@@ -37,7 +42,7 @@
 				</view>
 			</view>
 
-			<view class="refund-row" v-if="showChoice">
+			<view class="refund-row" v-if="!isFreeRefund && showChoice">
 				<view class="refund-label">选择原因</view>
 				<view class="refund-value refund-cause-btns">
 					<button class="admin-button-com mini-btn" @click="changeCause(0)">质量</button>
@@ -53,7 +58,7 @@
 					<text class="refund-amount">¥{{ refundPrice }}</text>
 					<text class="refund-extra" v-if="Number(info.refundPackCost) > 0">(含退打包费{{ info.refundPackCost ? parseFloat(info.refundPackCost) : 0 }}元)</text>
 					<text class="refund-extra" v-if="Number(info.refundSendCost) > 0">(含退运费{{ info.refundSendCost ? parseFloat(info.refundSendCost) : 0 }}元)</text>
-					<text class="refund-link" @click="changeAmount()" style="margin-left: 26upx;" v-if="info.status == 0">修改</text>
+					<text class="refund-link" @click="changeAmount()" style="margin-left: 26upx;" v-if="!isFreeRefund && info.status == 0">修改</text>
 				</view>
 			</view>
 
@@ -66,33 +71,15 @@
 
 			<view class="refund-row" v-if="Number(info.sameDay) === 0">
 				<view class="refund-label">售后类型</view>
-				<view class="refund-value" style="color: #3385ff;">隔天售后</view>
+				<view class="refund-value" style="color: #3385ff;">{{ isFreeRefund ? '无原单退款' : '隔天售后' }}</view>
 			</view>
-
 			<view class="refund-row">
-				<view class="refund-label">返回方式</view>
-				<view class="refund-value">
-					{{ info.fundType==1 ? '原路退回' : info.fundType==2 ? '返充到余额' : info.fundType == 3 ? '线下自行转账' : '其他' }}
-					<block v-if="info.fundType!=2">
-						-
-						<block v-if="info.payWay == 0">
-							{{info.onlinePay == 2?'线上':'线下'}}微信
-						</block>
-						<block v-else-if="info.payWay == 1">
-							{{info.onlinePay == 2?'线上':'线下'}}支付宝
-						</block>
-						<block v-else-if="info.payWay == 2">
-							余额
-						</block>
-						<block v-else-if="info.payWay == 4">
-							现金
-						</block>
-						<block v-else-if="info.payWay == 5">
-							银行卡
-						</block>
-						<block v-else>其它</block>
-					</block>
-				</view>
+				<view class="refund-label">原单付款</view>
+				<view class="refund-value">{{ payWayChannelText }}</view>
+			</view>
+			<view class="refund-row">
+				<view class="refund-label">退回状态</view>
+				<view class="refund-value">{{ fundStatusText }}</view>
 			</view>
 
 			<view class="refund-row">
@@ -110,7 +97,7 @@
 				<view class="refund-value refund-value--multiline">{{ info.remark || '' }}</view>
 			</view>
 
-			<view class="refund-row" v-if="from == 1">
+			<view class="refund-row" v-if="from == 1 && !isFreeRefund && Number(info.relateOrderId) > 0">
 				<view class="refund-label">订单信息</view>
 				<view class="refund-value">
 					<text class="refund-link refund-link--bold" @click="goToOrder(info)">查看订单</text>
@@ -134,28 +121,32 @@
 
 		<view class="refund-actions">
 			<button
-				v-if="info.status == 0"
+				v-if="!isFreeRefund && info.status == 0"
 				class="admin-button-com big refund-actions__btn refund-actions__btn--pass"
 				@click="pass(0)"
 			>通过</button>
 			<button
-				v-if="info.status == 0"
+				v-if="!isFreeRefund && info.status == 0"
 				class="admin-button-com big refund-actions__btn refund-actions__btn--reject"
 				@click="reject()"
 			>驳回</button>
-			<!-- 返充余额:查看/补发退款凭证海报 -->
+			<button
+				v-if="canFundAction"
+				class="admin-button-com big blue refund-actions__btn"
+				@click="goFundAction()"
+			>确认退回方式</button>
 			<button
 				v-if="canShowPoster"
 				class="admin-button-com big blue refund-actions__btn"
 				@click="goPoster()"
 			>查看凭证</button>
 			<button
-				v-if="info.status == 1"
+				v-if="!isFreeRefund && info.status == 1"
 				class="admin-button-com big blue refund-actions__btn"
 				@click="goWaste()"
 			>去报损</button>
 			<button
-				v-if="info.status == 1"
+				v-if="!isFreeRefund && info.status == 1"
 				class="admin-button-com big default refund-actions__btn"
 				@click="goWaste()"
 			>去减库存</button>
@@ -208,9 +199,48 @@ export default {
 		}
 	},
 	computed: {
+		/** 无关联销售单:工作台无原单退款 */
+		isFreeRefund() {
+			const info = this.info || {}
+			if (Number(info.isFreeRefund) === 1) {
+				return true
+			}
+			const sn = info.relateOrderSn
+			return !sn
+		},
+		refundTypeLabel() {
+			if (this.isFreeRefund) {
+				return Number(this.refundType) === 1 ? '库存退回' : '库存不退'
+			}
+			return Number(this.refundType) === 1 ? '退货并退款' : '只退款'
+		},
+		fundStatusText() {
+			if (Number(this.info.returnBalance) === 1) return '已返充到余额'
+			if (Number(this.info.hasReturn) === 1) return '已原路退回'
+			if (Number(this.info.status) === 1) return '待确认'
+			return '-'
+		},
+		/** 原路通道文案 */
+		payWayChannelText() {
+			const payWay = Number(this.info.payWay)
+			const online = Number(this.info.onlinePay) === 2 ? '线上' : '线下'
+			if (payWay === 0) return online + '微信'
+			if (payWay === 1) return online + '支付宝'
+			if (payWay === 2) return '余额'
+			if (payWay === 3) return '挂账'
+			if (payWay === 4) return '现金'
+			if (payWay === 5) return '银行卡'
+			return '-'
+		},
+		/** 已通过且待资金落地 */
+		canFundAction() {
+			return Number(this.info.status) === 1
+				&& Number(this.info.hasReturn) === 0
+				&& Number(this.info.returnBalance) === 0
+		},
 		/** 已通过且返充余额时可查看凭证 */
 		canShowPoster() {
-			return Number(this.info.status) === 1 && Number(this.info.fundType) === 2
+			return Number(this.info.status) === 1 && Number(this.info.returnBalance) === 1
 		},
 		/** 申请原因文案 */
 		causeText() {
@@ -288,6 +318,10 @@ export default {
 			})
 		},
 		goToOrder(info) {
+			if (!info || !info.relateOrderId) {
+				this.$msg('没有关联订单')
+				return
+			}
 			this.$util.pageTo({ url: '/pagesOrder/detail?id=' + info.relateOrderId })
 		},
 		cancelReject() {
@@ -343,37 +377,47 @@ export default {
 			})
 		},
 		/**
-		 * 跳转退款凭证海报(mode=repost 从详情补发)
-		 * fundType=2 返充余额才有凭证
+		 * 跳转结果页处理资金或补发海报
 		 */
-		goPoster() {
+		buildForwardPayload(mode) {
 			const info = this.info || {}
 			const orderId = info.relateOrderId
 				|| (info.orderInfo && info.orderInfo.id)
 				|| 0
-			const payload = {
-				fundType: info.fundType,
+			return {
 				amount: info.refundPrice,
 				customId: info.customId,
 				customName: info.customName || '',
 				forwardSn: info.orderSn || '',
 				forwardId: info.id || 0,
 				orderId: orderId,
-				mode: 'repost'
+				payWay: info.payWay,
+				hasReturn: info.hasReturn || 0,
+				couldReturn: info.couldReturn != null ? info.couldReturn : 1,
+				returnBalance: info.returnBalance || 0,
+				needFundAction: this.canFundAction ? 1 : 0,
+				mode: mode || ''
 			}
+		},
+		goFundAction() {
+			const payload = this.buildForwardPayload('')
 			uni.setStorageSync('forwardResultPayload', payload)
 			this.$util.pageTo({
 				url: '/admin/billing/forwardResult',
-				query: {
-					fundType: payload.fundType,
-					amount: payload.amount,
-					customId: payload.customId,
+				query: Object.assign({}, payload, {
+					customName: encodeURIComponent(payload.customName || '')
+				})
+			})
+		},
+		goPoster() {
+			const payload = this.buildForwardPayload('repost')
+			uni.setStorageSync('forwardResultPayload', payload)
+			this.$util.pageTo({
+				url: '/admin/billing/forwardResult',
+				query: Object.assign({}, payload, {
 					customName: encodeURIComponent(payload.customName || ''),
-					forwardSn: payload.forwardSn,
-					forwardId: payload.forwardId,
-					orderId: payload.orderId,
 					mode: 'repost'
-				}
+				})
 			})
 		},
 		/** 预览售后图片:urls 与列表缩略图同源,current 传 URL 避免 App 端滑动加载失败 */

+ 11 - 11
hdApp/src/pagesPurchase/refundDetail.vue

@@ -102,22 +102,20 @@ export default {
       return Number(info.sameDay) === 0
     },
     /**
-     * 返回方式:读售后单资金快照 fundType/onlinePay/payWay(对齐表字段优化)
-     * fundType 1原路 2返余额 3线下自行转账;返余额不拼渠道
+     * 返回方式:hasReturn / returnBalance + onlinePay/payWay
      */
     fundReturnText() {
       const info = (this.refundData && this.refundData.info) || {}
-      const fundType = Number(info.fundType)
-      const fundLabel = fundType === 1
-        ? '原路退回'
-        : fundType === 2
-          ? '返充到余额'
-          : fundType === 3
-            ? '线下自行转账'
-            : '其他'
-      if (fundType === 2) {
+      let fundLabel = '其他'
+      if (Number(info.returnBalance) === 1) {
+        fundLabel = '已返充到余额'
         return fundLabel
       }
+      if (Number(info.hasReturn) === 1) {
+        fundLabel = '已原路退回'
+      } else if (Number(info.status) === 1) {
+        fundLabel = '待确认资金'
+      }
       const payWay = Number(info.payWay)
       const online = Number(info.onlinePay) === 2 ? '线上' : '线下'
       let channel = '其它'
@@ -127,6 +125,8 @@ export default {
         channel = online + '支付宝'
       } else if (payWay === 2) {
         channel = '余额'
+      } else if (payWay === 3) {
+        channel = '挂账'
       } else if (payWay === 4) {
         channel = '现金'
       } else if (payWay === 5) {