528 lines
17 KiB
Vue
528 lines
17 KiB
Vue
<template>
|
||
<el-drawer v-model="isShow" direction="rtl" size="80%">
|
||
<template #header>
|
||
<h4>订单号:{{ item.orderNo }}</h4>
|
||
</template>
|
||
<template #default>
|
||
<!-- <div class="order_info">
|
||
<div class="row"><span>订单类型:</span>{{ filterLable('orderType', item.orderType) }}</div>
|
||
<div class="row"><span>平台类型:</span>{{ filterLable('platformType', item.platformType) }}</div>
|
||
<div class="row"><span>用餐模式:</span>{{ filterLable('dineMode', item.dineMode) }}</div>
|
||
<div class="row"><span>订单备注:</span>{{ item.remark }}</div>
|
||
<div class="row"><span>支付类型:</span>{{ filterLable('payType', item.payType) }}</div>
|
||
<div class="row"><span>支付单号:</span>{{ item.payOrderNo }}</div>
|
||
<div class="row"><span>支付金额:</span>¥{{ item.payAmount }}</div>
|
||
<div class="row"><span>支付时间:</span>{{ item.paidTime }}</div>
|
||
<div class="row"><span>订单金额(含折扣):</span>¥{{ item.orderAmount }}</div>
|
||
<div class="row"><span>订单原金额(不含折扣):</span>¥{{ item.originAmount }}</div>
|
||
</div> -->
|
||
<div class="table">
|
||
<div class="tab_head">
|
||
<el-radio-group v-model="refundType" @change="refundTypeChange">
|
||
<el-radio-button label="整单退" :value="1" />
|
||
<el-radio-button label="部分退" :value="2" />
|
||
<el-radio-button label="自定义" :value="3" />
|
||
</el-radio-group>
|
||
<div class="amount">
|
||
<el-input ref="amountInputRef" v-model="refundAmount" style="width: 400px;height: 42px;"
|
||
:readonly="refundType != 3" placeholder="请输入退款金额" @input="inputChange">
|
||
<template #prepend>¥</template>
|
||
<template #append>最多可退¥{{ formatDecimal(item.payAmount - item.refundAmount, 2) }}</template>
|
||
</el-input>
|
||
<!-- <template>
|
||
退款金额:¥{{ formatDecimal(refundType == 1 ? item.originAmount - item.refundAmount : amount) }}
|
||
</template> -->
|
||
</div>
|
||
</div>
|
||
<el-table ref="tableRef" :data="item.onGoods" brder stripe @selection-change="tabSelectChange">
|
||
<el-table-column type="selection" width="35" :selectable="selectable"></el-table-column>
|
||
<el-table-column label="商品信息">
|
||
<template v-slot="scope">
|
||
<div class="goods_info">
|
||
<el-image :src="scope.row.productImg" style="width: 50px;height: 50px;"></el-image>
|
||
<div class="info">
|
||
<div class="name">
|
||
<span>{{ scope.row.productName }}</span>
|
||
</div>
|
||
<div class="sku" v-if="scope.row.skuName">{{ scope.row.skuName }}</div>
|
||
<div class="sku">¥{{ formatDecimal(+scope.row.unitPrice) }}</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="订单数量" prop="num" width="100"></el-table-column>
|
||
<el-table-column label="支付金额" width="100">
|
||
<template v-slot="scope">
|
||
¥{{ formatDecimal(+scope.row.payAmount) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="退款数量" width="170">
|
||
<template v-slot="scope">
|
||
<el-input-number v-model="scope.row.refund_number" :disabled="refundType != 2" :min="0"
|
||
:max="formatDecimal(scope.row.num - scope.row.refundNum - scope.row.returnNum, 2, true)"
|
||
style="width: 130px;" @change="numberChange">
|
||
</el-input-number>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<template v-if="item.returnGoods.length">
|
||
<div class="tips" style="margin-top: 20px;padding-bottom: 10px;">以下为已退部分退单/退菜</div>
|
||
<el-table :data="item.returnGoods" brder stripe>
|
||
<el-table-column label="商品信息">
|
||
<template v-slot="scope">
|
||
<div class="goods_info">
|
||
<el-image :src="scope.row.productImg" style="width: 50px;height: 50px;"></el-image>
|
||
<div class="info">
|
||
<div class="name">
|
||
<span>{{ scope.row.productName }}</span>
|
||
</div>
|
||
<div class="sku" v-if="scope.row.skuName">{{ scope.row.skuName }}</div>
|
||
<div class="sku">¥{{ formatDecimal(+scope.row.unitPrice) }}</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="订单数量" prop="num" width="100"></el-table-column>
|
||
<el-table-column label="支付金额" width="100">
|
||
<template v-slot="scope">
|
||
¥{{ formatDecimal(+scope.row.payAmount) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="已退款数量" width="170">
|
||
<template v-slot="scope">
|
||
<div class="column">
|
||
<div class="row">退单数量:{{ scope.row.refundNum }}</div>
|
||
<div class="row">退菜数量:{{ scope.row.returnNum }}</div>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</template>
|
||
<div class="ipt">
|
||
<el-input type="textarea" :rows="4" v-model="remark" placeholder="请输入退单原因" />
|
||
</div>
|
||
<div class="remark_tag">
|
||
<div class="item" v-for="(item, index) in remarkTagList" :key="index" @click="addRmarkHandle(item)">
|
||
{{ item }}
|
||
</div>
|
||
<div class="item" @click="remark = ''">
|
||
<el-icon>
|
||
<CircleClose />
|
||
</el-icon>
|
||
清空
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template #footer>
|
||
<div class="drawer_footer">
|
||
<div class="btn">
|
||
<el-button type="danger" style="width: 100%;" :loading="loading"
|
||
@click="handleRefund">手动退款</el-button>
|
||
</div>
|
||
<div class="btn">
|
||
<el-button type="primary" style="width: 100%;" :loading="loading"
|
||
@click="refundHandle()">原路退回</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</el-drawer>
|
||
<takeFoodCode ref="takeFoodCodeRef" title="退款密码" :type="2" input-type="password" placeholder="请输入退款密码"
|
||
@success="passwordSuccess" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import _ from 'lodash'
|
||
import { ref } from 'vue'
|
||
import { useGlobal } from '@/store/global.js'
|
||
import { formatDecimal, inputFilterFloat } from "@/utils/index.js";
|
||
import { refundOrder, orderPrint } from '@/api/order.js'
|
||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||
import { usePrint } from "@/store/print.js";
|
||
import { useUser } from '@/store/user.js'
|
||
import dayjs from 'dayjs'
|
||
import takeFoodCode from "@/components/takeFoodCode.vue";
|
||
|
||
const emits = defineEmits(['success'])
|
||
|
||
const store = useUser()
|
||
const printStore = usePrint();
|
||
const globalStore = useGlobal()
|
||
const isShow = ref(false)
|
||
const item = ref({})
|
||
const tableRef = ref(null)
|
||
const modify = ref(false)
|
||
const refundAmount = ref('')
|
||
const refundType = ref(1)
|
||
const remark = ref('')
|
||
const remarkTagList = ref([
|
||
'顾客取消',
|
||
'等待时间长',
|
||
'支付错误',
|
||
'商品不满意',
|
||
'服务态度不满意',
|
||
'打包费'
|
||
])
|
||
const loading = ref(false)
|
||
const takeFoodCodeRef = ref(null)
|
||
const cash = ref(false)
|
||
const amountInputRef = ref(null)
|
||
|
||
// 退款密码
|
||
async function passwordSuccess(e = '') {
|
||
try {
|
||
loading.value = true
|
||
let rows = tableRef.value.getSelectionRows()
|
||
let refundDetails = []
|
||
if (refundType.value != 1) {
|
||
refundDetails = tableRef.value.getSelectionRows().map(val => {
|
||
return {
|
||
id: val.id,
|
||
returnAmount: val.payAmount,
|
||
num: val.refund_number
|
||
}
|
||
})
|
||
}
|
||
|
||
let data = {
|
||
orderId: item.value.id,
|
||
refundAmount: formatDecimal(+refundAmount.value),
|
||
modify: modify.value,
|
||
cash: cash.value,
|
||
refundReason: remark.value,
|
||
refundDetails: refundDetails,
|
||
pwd: e,
|
||
};
|
||
|
||
await refundOrder(data)
|
||
ElMessage.success('退款成功')
|
||
await printRefund(rows)
|
||
isShow.value = false
|
||
emits('success')
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
loading.value = false
|
||
}
|
||
|
||
// 显示手动退款
|
||
function handleRefund() {
|
||
ElMessageBox.confirm('请线下手动转账给客户或现金,一旦操作完成无法修改订单状态,请慎重操作!', '注意', {
|
||
confirmButtonText: '已在线下完成退款'
|
||
}).then(() => {
|
||
cash.value = true
|
||
refundHandle()
|
||
}).catch(() => { })
|
||
}
|
||
|
||
// 开始退款
|
||
async function refundHandle(cash = false) {
|
||
try {
|
||
let rows = tableRef.value.getSelectionRows()
|
||
if (refundType.value == 2) {
|
||
if (!rows.length) {
|
||
ElMessage.error('请选择退款商品')
|
||
return
|
||
}
|
||
}
|
||
|
||
if (!refundAmount.value) {
|
||
ElMessage.error('请请输入退款金额')
|
||
return
|
||
}
|
||
|
||
if (!remark.value) {
|
||
ElMessage.error('请请输入退款原因')
|
||
return
|
||
}
|
||
|
||
if (refundAmount.value <= 0) {
|
||
ElMessage.error('无可退金额')
|
||
return
|
||
}
|
||
|
||
loading.value = true
|
||
await store.getShopInfo()
|
||
if (store.shopInfo.isReturnPwd == 1) {
|
||
takeFoodCodeRef.value.show();
|
||
} else {
|
||
passwordSuccess()
|
||
}
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
loading.value = false
|
||
}
|
||
|
||
// 打印退款小票
|
||
async function printRefund(rows) {
|
||
try {
|
||
if (printStore.deviceNoteList.length) {
|
||
// 本地打印
|
||
const data = {
|
||
shop_name: store.shopInfo.shopName,
|
||
loginAccount: store.userInfo.name,
|
||
carts: [],
|
||
amount: formatDecimal(+refundAmount.value),
|
||
remark: remark.value,
|
||
orderInfo: item.value,
|
||
outNumber: item.value.id,
|
||
createdAt: item.value.createTime,
|
||
printTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
||
}
|
||
|
||
rows.map(item => {
|
||
data.carts.push(
|
||
{
|
||
name: item.productName,
|
||
number: item.num,
|
||
skuName: item.skuName,
|
||
salePrice: formatDecimal(+item.unitPrice),
|
||
totalAmount: formatDecimal(+item.payAmount)
|
||
}
|
||
)
|
||
})
|
||
|
||
printStore.printRefund(data);
|
||
} else {
|
||
// 云打印
|
||
await orderPrint({ id: item.value.id, type: 2 })
|
||
ElMessage.success('云打印退款单成功')
|
||
}
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
}
|
||
|
||
// 数量变化
|
||
function numberChange() {
|
||
let rows = tableRef.value.getSelectionRows()
|
||
tabSelectChange(rows)
|
||
}
|
||
|
||
// 选择退款项
|
||
const tabSelectChange = _.debounce(function (val) {
|
||
refundAmount.value = ''
|
||
let num = 0
|
||
val.map(item => {
|
||
if ((+item.refundNum + +item.returnNum) < item.num) {
|
||
num += item.refund_number * item.unitPrice
|
||
}
|
||
})
|
||
if (num) {
|
||
if (num >= item.value.payAmount) {
|
||
refundAmount.value = formatDecimal(item.value.payAmount - item.value.refundAmount)
|
||
} else {
|
||
refundAmount.value = formatDecimal(num)
|
||
}
|
||
}
|
||
}, 100)
|
||
|
||
// 禁用所有行的选择
|
||
const selectable = (row, index) => {
|
||
return refundType.value == 2;
|
||
};
|
||
|
||
// 初始化抽屉
|
||
function resetDrawer() {
|
||
modify.value = false
|
||
refundAmount.value = ''
|
||
refundType.value = 1
|
||
remark.value = ''
|
||
cash.value = false
|
||
}
|
||
|
||
function show(row) {
|
||
resetDrawer()
|
||
|
||
isShow.value = true
|
||
let newRow = { ...row }
|
||
remark.value = ''
|
||
|
||
let onGoods = []
|
||
let returnGoods = []
|
||
newRow.goods.map(item => {
|
||
// 可退的最大数量,下单数量 - 已退数量 - 退菜数量
|
||
let refundMaxNum = item.num - item.refundNum - item.returnNum
|
||
|
||
if (refundMaxNum <= 0) {
|
||
item.refund_number = item.num
|
||
// 已经退过,不在允许操作
|
||
returnGoods.push(item)
|
||
} else {
|
||
// 可以操作的退款数量
|
||
item.refund_number = item.num
|
||
onGoods.push(item)
|
||
}
|
||
})
|
||
|
||
newRow.onGoods = onGoods
|
||
newRow.returnGoods = returnGoods
|
||
|
||
item.value = newRow
|
||
|
||
setTimeout(() => {
|
||
tableRef.value.clearSelection()
|
||
refundTypeChange(1)
|
||
}, 100);
|
||
}
|
||
|
||
// 筛选类型
|
||
function filterLable(key, type) {
|
||
let item = globalStore[key].find(item => item.type == type)
|
||
if (item && item.type) {
|
||
return item.label
|
||
} else {
|
||
return type
|
||
}
|
||
}
|
||
|
||
// 切换退单类型
|
||
function refundTypeChange(val) {
|
||
switch (val) {
|
||
case 1:
|
||
modify.value = false
|
||
// tableRef.value.toggleAllSelection()
|
||
// 遍历数据,将每一行设置为选中状态
|
||
item.value.onGoods.forEach((row) => {
|
||
tableRef.value.toggleRowSelection(row, true);
|
||
});
|
||
break;
|
||
case 2:
|
||
modify.value = false
|
||
if (item.value.onGoods.length == 1) {
|
||
item.value.onGoods.forEach((row) => {
|
||
tableRef.value.toggleRowSelection(row, true);
|
||
});
|
||
} else {
|
||
tableRef.value.clearSelection()
|
||
}
|
||
break;
|
||
case 3:
|
||
tableRef.value.clearSelection()
|
||
refundAmount.value = ''
|
||
modify.value = true
|
||
amountInputRef.value.focus()
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 自定义退款金额输入
|
||
const inputChange = _.debounce(function (n) {
|
||
if (n > item.value.payAmount - item.value.refundAmount) {
|
||
refundAmount.value = formatDecimal(item.value.payAmount - item.value.refundAmount, 2)
|
||
} else {
|
||
refundAmount.value = inputFilterFloat(n)
|
||
}
|
||
}, 300)
|
||
|
||
// 添加备注
|
||
function addRmarkHandle(item) {
|
||
if (remark.value.length) {
|
||
remark.value += `,${item}`
|
||
} else {
|
||
remark.value = item
|
||
}
|
||
}
|
||
|
||
defineExpose({
|
||
show
|
||
})
|
||
|
||
// onMounted(() => {
|
||
// setTimeout(() => {
|
||
// refundTypeChange(1)
|
||
// }, 100)
|
||
// })
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.drawer_footer {
|
||
display: flex;
|
||
gap: var(--el-font-size-base);
|
||
|
||
.btn {
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.order_info {
|
||
.row {
|
||
margin-bottom: 4px;
|
||
|
||
span {
|
||
color: #555;
|
||
}
|
||
}
|
||
}
|
||
|
||
.table {
|
||
padding: var(--el-font-size-base) 0;
|
||
|
||
.ipt {
|
||
padding: var(--el-font-size-base) 0;
|
||
}
|
||
|
||
.tab_head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding-bottom: var(--el-font-size-base);
|
||
|
||
.amount {
|
||
color: var(--el-color-danger);
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
.remark_tag {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
|
||
.item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 10px;
|
||
padding: 6px 12px;
|
||
border: 1px solid #ddd;
|
||
background-color: #fff;
|
||
border-radius: 4px;
|
||
color: #555;
|
||
|
||
&:active {
|
||
background-color: #ddd;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.goods_info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-left: 6px;
|
||
|
||
.name {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
.n {
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.sku {
|
||
color: #999;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
</style> |