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