feat(商品详情): 添加借还记录功能

- 在商品详情页新增借还记录标签页
- 添加借还记录列表组件,支持分页查询和状态筛选
- 实现借还记录相关接口和类型定义
This commit is contained in:
dzq 2025-11-27 10:11:16 +08:00
parent 59b19cd9e3
commit 59f8a9f744
3 changed files with 239 additions and 0 deletions

View File

@ -137,3 +137,56 @@ export const exportOrderExcelApi = (params: OrderQuery, fileName: string) => {
} }
}); });
}; };
export interface BorrowReturnRecordQuery extends BasePageQuery {
/** 商品ID */
goodsId?: number;
/**
*
* @remarks
* 0-退 | 1- | 2- | 3-
*/
status?: number;
}
export interface BorrowReturnRecordDTO {
/** 订单ID */
orderId: number;
/** 审批ID */
approvalId?: number;
/** 订单创建时间 */
orderTime?: Date;
/** 归还图片 */
returnImages?: string;
/** 审核图片 */
auditImages?: string;
/** 订单商品价格 */
goodsPrice: number;
/** 支付方式 */
paymentMethod?: string;
/** 订单姓名 */
orderName?: string;
/** 订单手机号 */
orderMobile?: string;
/** 订单商品数量 */
quantity: number;
/** 审批人 */
auditName?: string;
/** 审核说明 */
auditRemark?: string;
/**
*
* @remarks
* 0-退 | 1- | 2- | 3-
*/
status: number;
/** 状态描述 */
statusStr: string;
}
/** 获取借还记录分页列表 */
export const getBorrowReturnRecordListApi = (params?: BorrowReturnRecordQuery) => {
return http.request<ResponseData<PageDTO<BorrowReturnRecordDTO>>>("get", "/shop/order/borrow-return-record", {
params
});
};

View File

@ -0,0 +1,180 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { getBorrowReturnRecordListApi, type BorrowReturnRecordDTO, type BorrowReturnRecordQuery } from "@/api/shop/order";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import Search from "@iconify-icons/ep/search";
import Refresh from "@iconify-icons/ep/refresh";
defineOptions({
name: "BorrowReturnRecord"
});
const props = defineProps<{
goodsId: number;
}>();
const formRef = ref();
const tableRef = ref();
const searchFormParams = ref<BorrowReturnRecordQuery>({
goodsId: props.goodsId,
status: null
});
const pagination = ref({
pageSize: 8,
currentPage: 1,
total: 0
});
const loading = ref(false);
const dataList = ref<BorrowReturnRecordDTO[]>([]);
const getList = async () => {
try {
loading.value = true;
const { data } = await getBorrowReturnRecordListApi({
...searchFormParams.value,
pageSize: pagination.value.pageSize,
pageNum: pagination.value.currentPage
});
dataList.value = data.rows;
pagination.value.total = data.total;
} finally {
loading.value = false;
}
};
const onSearch = () => {
pagination.value.currentPage = 1;
getList();
};
const resetForm = () => {
formRef.value.resetFields();
onSearch();
};
const onSizeChange = (val: number) => {
pagination.value.pageSize = val;
getList();
};
const onCurrentChange = (val: number) => {
pagination.value.currentPage = val;
getList();
};
onMounted(() => {
getList();
});
</script>
<template>
<div class="borrow-return-record">
<el-form ref="formRef" :inline="true" :model="searchFormParams"
class="search-form bg-bg_color flex w-[99/100] pl-[22px] pt-[12px]">
<el-form-item prop="status">
<el-select v-model="searchFormParams.status" placeholder="请选择状态" clearable class="!w-[180px]">
<el-option label="未退还" :value="0" />
<el-option label="待审批" :value="1" />
<el-option label="已通过" :value="2" />
<el-option label="已驳回" :value="3" />
</el-select>
</el-form-item>
</el-form>
<el-form :inline="true" class="search-form bg-bg_color flex w-[99/100] pl-[22px] pt-0">
<el-form-item>
<el-button type="primary" :icon="useRenderIcon(Search)" @click="onSearch" style="margin-right: 10px;">
搜索
</el-button>
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm">
重置
</el-button>
</el-form-item>
</el-form>
<div class="table-container">
<el-table ref="tableRef" v-loading="loading" :data="dataList" row-key="orderId" border>
<el-table-column label="订单ID" prop="orderId" width="120" />
<el-table-column label="订单创建时间" prop="orderTime" width="180">
<template #default="{ row }">
{{ row.orderTime ? new Date(row.orderTime).toLocaleString() : '-' }}
</template>
</el-table-column>
<el-table-column label="商品价格" prop="goodsPrice" width="120">
<template #default="{ row }">{{ row.goodsPrice }}</template>
</el-table-column>
<el-table-column label="商品数量" prop="quantity" width="100" />
<el-table-column label="订单姓名" prop="orderName" width="120" />
<el-table-column label="订单手机号" prop="orderMobile" width="120" />
<el-table-column label="支付方式" prop="paymentMethod" width="120">
<template #default="{ row }">
{{ { wechat: '微信支付', balance: '借呗支付' }[row.paymentMethod] || row.paymentMethod }}
</template>
</el-table-column>
<el-table-column label="状态" prop="statusStr" width="120">
<template #default="{ row }">
<el-tag :type="row.status === 2 ? 'success' : row.status === 3 ? 'danger' : 'info'">
{{ row.statusStr }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="审批人" prop="auditName" width="120" />
<el-table-column label="归还图片" prop="returnImages" width="120">
<template #default="{ row }">
<div v-if="row.returnImages" class="flex gap-2">
<el-image v-for="(img, index) in row.returnImages.split(',')" :key="index" :src="img"
:preview-src-list="row.returnImages.split(',')" :z-index="9999" :preview-teleported="true"
:hide-on-click-modal="true" fit="cover" class="rounded" width="40" height="40" />
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="审核图片" prop="auditImages" width="120">
<template #default="{ row }">
<div v-if="row.auditImages" class="flex gap-2">
<el-image v-for="(img, index) in row.auditImages.split(',')" :key="index" :src="img"
:preview-src-list="row.auditImages.split(',')" :z-index="9999" :preview-teleported="true"
:hide-on-click-modal="true" fit="cover" class="rounded" width="40" height="40" />
</div>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="审核说明" prop="auditRemark" min-width="150" />
</el-table>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize"
:page-sizes="[8, 10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"
@size-change="onSizeChange" @current-change="onCurrentChange" class="pagination" />
</div>
</div>
</template>
<style scoped lang="scss">
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
margin-right: 12px;
}
}
.table-container {
margin-top: 8px;
background-color: var(--el-bg-color);
border-radius: 4px;
padding: 16px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
.el-table {
flex: 1;
overflow: hidden;
}
.pagination {
margin-top: 10px;
}
}
</style>

View File

@ -7,6 +7,7 @@ import { ElMessage } from "element-plus";
import { useRenderIcon } from '@/components/ReIcon/src/hooks'; import { useRenderIcon } from '@/components/ReIcon/src/hooks';
import EditPen from '@iconify-icons/ep/edit-pen'; import EditPen from '@iconify-icons/ep/edit-pen';
import GoodsEditModal from "./goods-edit-modal.vue"; import GoodsEditModal from "./goods-edit-modal.vue";
import BorrowReturnRecord from "./borrow-return-record.vue";
import { useBtnPermissionStore } from "@/store/modules/btnPermission"; import { useBtnPermissionStore } from "@/store/modules/btnPermission";
const handleEdit = (row: GoodsDTO) => { const handleEdit = (row: GoodsDTO) => {
@ -120,6 +121,7 @@ watch(goodsId, () => {
<el-tabs type="card" v-model="activeTab" class="tab-header-card"> <el-tabs type="card" v-model="activeTab" class="tab-header-card">
<el-tab-pane label="基本信息" name="basic"></el-tab-pane> <el-tab-pane label="基本信息" name="basic"></el-tab-pane>
<el-tab-pane label="购买记录" name="order"></el-tab-pane> <el-tab-pane label="购买记录" name="order"></el-tab-pane>
<el-tab-pane label="借还记录" name="borrowReturn"></el-tab-pane>
</el-tabs> </el-tabs>
<el-button v-if="goodsInfo.belongType == 0 && hasPermission('shop:goods:write')" type="primary" <el-button v-if="goodsInfo.belongType == 0 && hasPermission('shop:goods:write')" type="primary"
@click="handleEdit(goodsInfo)" style="margin-bottom: 12px" :size="'default'"> @click="handleEdit(goodsInfo)" style="margin-bottom: 12px" :size="'default'">
@ -197,6 +199,10 @@ watch(goodsId, () => {
<div class="sales-details" v-if="activeTab === 'sales'"> <div class="sales-details" v-if="activeTab === 'sales'">
<!-- 销售记录表格 --> <!-- 销售记录表格 -->
</div> </div>
<div class="borrow-return-details" v-if="activeTab === 'borrowReturn'">
<BorrowReturnRecord :goods-id="goodsId" />
</div>
</el-card> </el-card>
</div> </div>
</div> </div>