feat:购物车商品级退款

This commit is contained in:
dzc committed 2025-08-27 20:15:28 +08:00
1 parent 8648d8d8a3
commit ca11691c03
7 files changed
+182 -62

No files matched your search

+8
View File
@@ -8,6 +8,14 @@ export function adminRefund(data?: Object) {
})
}
export function adminReinforceRefund(data?: Object) {
return request({
url: "/hard/hard-after-sale/admin-reinforce-refund",
method: "post",
data,
})
}
export function history(query?: Object) {
return request({
url: "/hard/hard-after-sale/history",
+42 -35
View File
@@ -1,57 +1,64 @@
import request from "/@/utils/request"
export function getCarByOrderId(query?: Object) {
return request({
url: '/hard/hard-car/order-id',
method: 'get',
params: query
})
return request({
url: "/hard/hard-car/order-id",
method: "get",
params: query,
})
}
export function refundCar(query?: Object) {
return request({
url: "/hard/hard-car/refund-car",
method: "get",
params: query,
})
}
export function pageJoin(query?: Object) {
return request({
url: '/hard/hard-car/page-join',
method: 'get',
params: query
})
return request({
url: "/hard/hard-car/page-join",
method: "get",
params: query,
})
}
export function fetchList(query?: Object) {
return request({
url: '/hard/hard-car/page',
method: 'get',
params: query
})
return request({
url: "/hard/hard-car/page",
method: "get",
params: query,
})
}
export function addObj(obj?: Object) {
return request({
url: '/hard/hard-car',
method: 'post',
data: obj
})
return request({
url: "/hard/hard-car",
method: "post",
data: obj,
})
}
export function getObj(id?: string) {
return request({
url: '/hard/hard-car/' + id,
method: 'get'
})
return request({
url: "/hard/hard-car/" + id,
method: "get",
})
}
export function delObjs(ids?: Object) {
return request({
url: '/hard/hard-car',
method: 'delete',
data: ids
})
return request({
url: "/hard/hard-car",
method: "delete",
data: ids,
})
}
export function putObj(obj?: Object) {
return request({
url: '/hard/hard-car',
method: 'put',
data: obj
})
return request({
url: "/hard/hard-car",
method: "put",
data: obj,
})
}
+2 -2
View File
@@ -203,13 +203,13 @@ export function useTable(options?: BasicTableProps) {
* 刷新后不跳转第一页,则入参 getDataList(false)
* @param refresh 是否刷新当前页码
*/
const getDataList = (refresh?: any) => {
const getDataList = async (refresh?: any) => {
// 如果需要刷新,则将state.pagination.current重置为1
if (refresh !== false) {
state.pagination!.current = 1
}
// 再次发起查询操作
query()
await query()
}
/**
@@ -33,6 +33,7 @@
</template>
<script setup lang="ts" name="RefundForm">
import { useMessage } from "/@/hooks/message"
import * as walletSettingApi from "/@/api/wallet/wallet-setting"
// 定义变量内容
@@ -80,7 +81,12 @@ const getSetting = async () => {
// 提交
const validate = async () => {
const valid = await dataFormRef.value.validate().catch(() => { })
if (!valid) return false
if (!valid) {
if (!form.applyReason) {
useMessage().error("请选择退款原因")
}
return false
}
return form
}
@@ -4,8 +4,8 @@
<el-tab-pane v-for="(v) in data.tabs" :key="v.name" :label="v.label" :name="v.name" />
</el-tabs>
<div class="container" v-loading="loading">
<refund-form ref="refundFormRef" v-if="state.currentTab === 'INFO'" />
<refund-table ref="refundTableRef" v-if="state.currentTab === 'CAR'" />
<refund-form ref="refundFormRef" v-show="state.currentTab === 'INFO'" />
<refund-table ref="refundTableRef" v-show="state.currentTab === 'CAR'" />
</div>
<template #footer>
<span class="dialog-footer">
@@ -80,11 +80,20 @@ function tabClick(e: any) {
// 提交
const onSubmit = async () => {
// 校验
const formValidate = await refundFormRef.value?.validate()
if (formValidate === false) return
// 判断有没有选择退款商品
const tableValidate = await refundTableRef.value?.validate()
if (tableValidate === false) return
try {
loading.value = true
const _data = {}
await hardAfterSaleApi.adminRefund(_data)
const _data = {
...formValidate,
orderId: state.orderId,
carList: tableValidate
}
await hardAfterSaleApi.adminReinforceRefund(_data)
useMessage().success("退款成功")
visible.value = false
emit("refresh")
@@ -27,7 +27,37 @@
</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="1" title="已退款商品">
<el-descriptions-item labelClassName="no-colon">
<el-table :data="refundSuccessList" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @sort-change="sortChangeHandle" show-summary
:summary-method="getSummaries">
<el-table-column label="商品名" align="center" prop="productName" />
<el-table-column label="商品图片" align="center" prop="code">
<template #default="{ row }">
<preview-image v-if="row.productPic" :src="row.productPic" />
<div v-else class="w-[50px] h-[50px]" />
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="productPrice">
<template #default="{ row }">
<span>{{ displayPrice(row.productPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="已退款商品数量" align="center" prop="productCount" />
<el-table-column label="已退款商品总额" align="center" prop="totalPrice">
<template #default="{ row }">
<span>{{ displayPrice(row.totalPrice) }}</span>
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
</el-descriptions>
<el-descriptions :column="1" title="退款商品">
<template #extra>
<el-button type="primary" text @click="handleAllRefund">全部退款</el-button>
</template>
<el-descriptions-item labelClassName="no-colon">
<el-table :data="disposeDataList" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @sort-change="sortChangeHandle" show-summary
@@ -47,11 +77,16 @@
<el-table-column label="退款商品数量" align="center" prop="refundProductCount">
<template #default="{ row }">
<div>
<el-input-number v-if="row.productNature == 2" v-model="row.refundProductCount" :min="0"
:max="row.productCount" :step="0.1"
@change="handleRefundProductCount($event, row)" />
<el-input-number v-else v-model="row.refundProductCount" :min="0"
:max="row.productCount" @change="handleRefundProductCount($event, row)" />
<div v-if="row.refundProductCount == row.productCount" class="color-danger text-sm">
该商品已全部退款
</div>
<div v-else>
<el-input-number size="small" v-if="row.productNature == 2"
v-model="row.refundProductCount" :min="0" :max="row.productCount" :step="0.1"
@change="handleRefundProductCount($event, row)" />
<el-input-number size="small" v-else v-model="row.refundProductCount" :min="0"
:max="row.productCount" @change="handleRefundProductCount($event, row)" />
</div>
</div>
</template>
</el-table-column>
@@ -67,6 +102,7 @@
</template>
<script setup lang="ts" name="RefundTable">
import { useMessage } from "/@/hooks/message"
import { BasicTableProps, useTable } from "/@/hooks/table"
import { displayPrice } from "/@/utils/formatMoney"
@@ -79,11 +115,36 @@ const state: BasicTableProps = reactive<BasicTableProps>({
pageList: hardCar.getCarByOrderId,
})
const disposeDataList = computed(() => {
const refundSuccessList = ref<any>([])
const disposeDataList = ref<any>([])
// table hook
const { getDataList, sortChangeHandle, tableStyle } = useTable(state)
const getCarList = async (orderId: string) => {
state.queryForm.orderId = orderId
// 获取订单商品列表
await getDataList()
if (!state.dataList || !state.dataList.length) {
return []
return
}
return state.dataList.map((item) => {
// 获取已退款的商品
const { data } = await hardCar.refundCar({ orderId })
if (!data || !data.length) {
return
}
refundSuccessList.value = data
// 计算出需要退款的商品
const refundViewList = [...state.dataList]
for (const item of refundViewList) {
const target = data.find((v: any) => v.productId == item.productId)
if (!target) continue
// 购物车商品 - 已退款商品
item.productCount = item.productCount - target.productCount
item.totalPrice = item.totalPrice - target.totalPrice
}
disposeDataList.value = refundViewList.map((item) => {
if (!item) return null
return {
...item,
@@ -91,14 +152,6 @@ const disposeDataList = computed(() => {
refundTotalPrice: 0,
}
}).filter(Boolean)
})
// table hook
const { getDataList, sortChangeHandle, tableStyle } = useTable(state)
const getCarList = (orderId: string) => {
state.queryForm.orderId = orderId
getDataList()
}
const handleRefundProductCount = (value: any, row: any) => {
@@ -108,6 +161,20 @@ const handleRefundProductCount = (value: any, row: any) => {
target.refundTotalPrice = productPrice * value
}
// 全部退款
const handleAllRefund = () => {
if (!state.dataList || !state.dataList.length) return
const _list = [...disposeDataList.value]
for (const v of _list) {
const target = state.dataList.find((item: any) => item.id == v.id)
if (!target) continue
v.refundProductCount = target.productCount
v.refundTotalPrice = target.totalPrice
}
disposeDataList.value = _list
}
const getSummaries = (param: any) => {
const { columns, data } = param
const totalColumns = ["productCount", "totalPrice", "totalPrice", "refundProductCount", "refundTotalPrice"]
@@ -129,9 +196,33 @@ const getSummaries = (param: any) => {
return totalRow
}
// 提交
const validate = async () => {
let valid = true
for (const v of disposeDataList.value) {
if (v.refundProductCount <= 0) {
valid = false
break
}
}
if (!valid) {
useMessage().error("退款商品列表不能为空")
return false
}
// 处理一下
return disposeDataList.value.map((item: any) => {
return {
...item,
productCount: item.refundProductCount,
totalPrice: item.refundTotalPrice,
}
})
}
// 暴露变量
defineExpose({
getCarList,
validate,
})
</script>
+2 -3
View File
@@ -224,8 +224,7 @@
v-auth="'hard_hardwareOrder_edit'" @click="formDialogRef.openDialog(row.id)">
编辑
</el-button>
<el-button
v-if="!row.payRefundId && row.orderType !== '5' && !['5', '6', '8'].includes(row.payType)"
<el-button v-if="row.orderType !== '5' && !['5', '6', '8'].includes(row.payType)"
icon="stamp" text type="primary" v-auth="'hard_hardAfterSale_adminRefund'"
@click="adminRefundDailogRef.openDialog(row.id)">
退款
@@ -402,7 +401,7 @@ const getSummaries = (param: SummaryMethodProps) => {
{ column: "totalPrice", columnName: "商品总价", color: "#67C260" },
{ column: "extraPrice", columnName: "额外支付", color: "#F56E9B" },
{ column: "payPrice", columnName: "实际支付", color: "#355CF6" },
].forEach(({ column, columnName, color }: any) => {
].forEach(({ column, columnName, color }: any, index: number) => {
const values = data.map((item) => Number(item[column]))
if (!values.every((value) => Number.isNaN(value))) {
priceVNode.push(