feat(订单): 新增借还动态功能
- 在订单模块中添加借还动态接口及相关类型定义 - 创建借还动态接口文档,详细说明接口使用方式 - 在欢迎页面新增借还动态展示区域,使用时间轴形式展示借出和归还记录 - 实现动态记录的筛选、状态显示和图片预览功能
This commit is contained in:
parent
5e5f2a5fb3
commit
3e47804f7e
|
|
@ -0,0 +1,245 @@
|
|||
# 借还动态接口文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档介绍新增加的借还动态列表接口,该接口将借出和归还操作分别作为独立记录返回,相比原有的 `getBorrowReturnRecordList` 接口,能更清晰地展示订单的生命周期。
|
||||
|
||||
## 接口信息
|
||||
|
||||
### 新增接口
|
||||
|
||||
**接口地址**: `GET /shop/order/borrow-return-dynamic`
|
||||
|
||||
**功能描述**: 查询借还动态分页列表,返回借出和归还的独立记录
|
||||
|
||||
## 数据结构
|
||||
|
||||
### 请求参数
|
||||
|
||||
#### SearchBorrowReturnDynamicQuery
|
||||
|
||||
| 字段名 | 类型 | 是否必填 | 说明 |
|
||||
|--------|------|----------|------|
|
||||
| goodsId | Long | 否 | 商品ID,精确筛选 |
|
||||
| status | Integer | 否 | 状态筛选(仅对归还记录有效)<br>- 0: 未退还<br>- 1: 待审批<br>- 2: 已通过<br>- 3: 已驳回<br>- 4: 开柜中 |
|
||||
| dynamicType | Integer | 否 | 动态类型筛选<br>- 0: 借出记录<br>- 1: 归还记录 |
|
||||
| pageNum | Integer | 否 | 页码(默认1) |
|
||||
| pageSize | Integer | 否 | 每页大小(默认10) |
|
||||
|
||||
### 响应数据
|
||||
|
||||
#### BorrowReturnDynamicDTO
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| orderGoodsId | Long | 订单商品ID |
|
||||
| dynamicType | Integer | 动态类型(0-借出 1-归还) |
|
||||
| dynamicTypeStr | String | 动态类型描述 |
|
||||
| orderId | Long | 订单ID |
|
||||
| orderTime | Date | 订单创建时间/借出时间 |
|
||||
| goodsId | Long | 商品ID |
|
||||
| goodsName | String | 商品名称 |
|
||||
| goodsPrice | BigDecimal | 商品单价 |
|
||||
| quantity | Integer | 数量 |
|
||||
| paymentMethod | String | 支付方式 |
|
||||
| orderName | String | 订单姓名 |
|
||||
| orderMobile | String | 订单手机号 |
|
||||
| cellId | Long | 格口ID |
|
||||
| cellNo | Integer | 格口号 |
|
||||
| cabinetId | Long | 柜机ID |
|
||||
| cabinetName | String | 柜机名称 |
|
||||
| operateTime | Date | 归还/审批时间(归还记录时有效) |
|
||||
| approvalId | Long | 审批ID(归还记录时有效) |
|
||||
| status | Integer | 审批状态(归还记录时有效) |
|
||||
| statusStr | String | 状态描述 |
|
||||
| auditName | String | 审批人(归还记录时有效) |
|
||||
| auditRemark | String | 审核说明(归还记录时有效) |
|
||||
| images | String | 归还图片(归还记录时有效) |
|
||||
|
||||
## 业务说明
|
||||
|
||||
### 记录分类
|
||||
|
||||
每条订单商品会产生两条动态记录:
|
||||
|
||||
1. **借出记录** (`dynamicType = 0`)
|
||||
- 记录时间:`orderTime` = 订单创建时间
|
||||
- 状态:固定为 0(未退还)
|
||||
- 审批相关字段:均为 null
|
||||
|
||||
2. **归还记录** (`dynamicType = 1`)
|
||||
- 记录时间:`operateTime` = 审批时间(若无则为创建时间)
|
||||
- 状态:实际的审批状态
|
||||
- 审批相关字段:审批人、审核说明、归还图片等
|
||||
|
||||
### 与原接口对比
|
||||
|
||||
| 特性 | getBorrowReturnRecordList | getBorrowReturnDynamicList |
|
||||
|------|---------------------------|----------------------------|
|
||||
| 记录方式 | 一条记录包含借出和归还信息 | 借出和归还分别独立记录 |
|
||||
| 时间展示 | 只显示一个时间 | 分别显示借出时间和归还时间 |
|
||||
| 状态展示 | 状态混合显示 | 归还记录显示审批状态,借出记录固定为未退还 |
|
||||
| 数据清晰度 | 信息集中但复杂 | 信息分离但清晰 |
|
||||
| 适用场景 | 需要快速查看整体情况 | 需要追踪完整操作流程 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1: 查询所有动态记录
|
||||
|
||||
**请求**:
|
||||
```http
|
||||
GET /shop/order/borrow-return-dynamic
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "成功",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"orderGoodsId": 1001,
|
||||
"dynamicType": 0,
|
||||
"dynamicTypeStr": "借出",
|
||||
"orderId": 1000,
|
||||
"orderTime": "2025-11-28 10:00:00",
|
||||
"goodsId": 200,
|
||||
"goodsName": "iPad",
|
||||
"goodsPrice": 3000.00,
|
||||
"quantity": 1,
|
||||
"paymentMethod": "wechat",
|
||||
"orderName": "张三",
|
||||
"orderMobile": "13800138000",
|
||||
"cellId": 1,
|
||||
"cellNo": 5,
|
||||
"cabinetId": 1,
|
||||
"cabinetName": "主柜A",
|
||||
"operateTime": null,
|
||||
"approvalId": null,
|
||||
"status": 0,
|
||||
"statusStr": "未退还",
|
||||
"auditName": null,
|
||||
"auditRemark": null,
|
||||
"images": null
|
||||
},
|
||||
{
|
||||
"orderGoodsId": 1001,
|
||||
"dynamicType": 1,
|
||||
"dynamicTypeStr": "归还",
|
||||
"orderId": 1000,
|
||||
"orderTime": "2025-11-28 10:00:00",
|
||||
"goodsId": 200,
|
||||
"goodsName": "iPad",
|
||||
"goodsPrice": 3000.00,
|
||||
"quantity": 1,
|
||||
"paymentMethod": "wechat",
|
||||
"orderName": "张三",
|
||||
"orderMobile": "13800138000",
|
||||
"cellId": 1,
|
||||
"cellNo": 5,
|
||||
"cabinetId": 1,
|
||||
"cabinetName": "主柜A",
|
||||
"operateTime": "2025-11-28 14:00:00",
|
||||
"approvalId": 500,
|
||||
"status": 2,
|
||||
"statusStr": "已通过",
|
||||
"auditName": "李四",
|
||||
"auditRemark": "商品完好",
|
||||
"images": "/images/return/123.jpg"
|
||||
}
|
||||
],
|
||||
"total": 2,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"orders": [],
|
||||
"hitCount": false,
|
||||
"searchCount": true,
|
||||
"pages": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2: 仅查询借出记录
|
||||
|
||||
**请求**:
|
||||
```http
|
||||
GET /shop/order/borrow-return-dynamic?dynamicType=0
|
||||
```
|
||||
|
||||
### 示例3: 查询已通过的归还记录
|
||||
|
||||
**请求**:
|
||||
```http
|
||||
GET /shop/order/borrow-return-dynamic?dynamicType=1&status=2
|
||||
```
|
||||
|
||||
### 示例4: 按商品筛选
|
||||
|
||||
**请求**:
|
||||
```http
|
||||
GET /shop/order/borrow-return-dynamic?goodsId=200
|
||||
```
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 数据库查询
|
||||
|
||||
接口使用 `UNION ALL` 查询实现:
|
||||
|
||||
1. 第一部分:查询借出记录
|
||||
- 来源:`shop_order` + `shop_order_goods` + `cabinet_cell` + `smart_cabinet`
|
||||
- 时间字段:`so.create_time`
|
||||
- 状态:固定值 0
|
||||
|
||||
2. 第二部分:查询归还记录
|
||||
- 来源:`shop_order` + `shop_order_goods` + `return_approval` + `cabinet_cell` + `smart_cabinet`
|
||||
- 时间字段:`COALESCE(ra.approval_time, ra.create_time)`
|
||||
- 状态:实际审批状态
|
||||
|
||||
### 服务层实现
|
||||
|
||||
- **Mapper**: `ShopOrderMapper.getBorrowReturnDynamicList()`
|
||||
- **Service**: `ShopOrderService.getBorrowReturnDynamicList()`
|
||||
- **ApplicationService**: `OrderApplicationService.getBorrowReturnDynamicList()`
|
||||
- **Controller**: `ShopOrderController.getBorrowReturnDynamicList()`
|
||||
|
||||
### 分页处理
|
||||
|
||||
使用 MyBatis-Plus 的 `Page` 插件实现分页查询,支持:
|
||||
- 页码控制(pageNum)
|
||||
- 每页大小控制(pageSize)
|
||||
- 总记录数统计
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **状态筛选**: `status` 参数仅对 `dynamicType=1`(归还记录)有效,对借出记录无效
|
||||
2. **空值处理**: 借出记录的审批相关字段(approvalId、auditName、auditRemark、images)均为 null
|
||||
3. **时间排序**: 查询结果按动态发生时间(dynamic_time)倒序排列
|
||||
4. **数据权限**: 接口遵循现有的数据权限控制逻辑
|
||||
5. **性能考虑**: 大量数据时建议使用分页,并合理设置筛选条件
|
||||
|
||||
## 相关文件
|
||||
|
||||
### 新增文件
|
||||
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/dto/BorrowReturnDynamicDTO.java`
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/query/SearchBorrowReturnDynamicQuery.java`
|
||||
|
||||
### 修改文件
|
||||
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderMapper.java`
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderService.java`
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/db/ShopOrderServiceImpl.java`
|
||||
- `agileboot-domain/src/main/java/com/agileboot/domain/shop/order/OrderApplicationService.java`
|
||||
- `agileboot-admin/src/main/java/com/agileboot/admin/controller/shop/ShopOrderController.java`
|
||||
|
||||
## 更新日志
|
||||
|
||||
| 日期 | 版本 | 更新内容 |
|
||||
|------|------|----------|
|
||||
| 2025-11-28 | v1.0 | 新增借还动态接口,支持借出和归还分别独立查询 |
|
||||
|
||||
## 联系信息
|
||||
|
||||
如有问题或建议,请联系开发团队。
|
||||
|
|
@ -204,3 +204,84 @@ export const getBorrowReturnRecordListApi = (params?: BorrowReturnRecordQuery) =
|
|||
params
|
||||
});
|
||||
};
|
||||
|
||||
// 借还动态接口相关定义
|
||||
|
||||
export interface BorrowReturnDynamicQuery extends BasePageQuery {
|
||||
/** 商品ID */
|
||||
goodsId?: number;
|
||||
/** 格口ID,精确筛选 */
|
||||
cellId?: number;
|
||||
/**
|
||||
* 状态筛选(仅对归还记录有效)
|
||||
* @remarks
|
||||
* 0-未退还 | 1-待审批 | 2-已通过 | 3-已驳回 | 4-开柜中
|
||||
*/
|
||||
status?: number;
|
||||
/**
|
||||
* 动态类型筛选
|
||||
* @remarks
|
||||
* 0-借出记录 | 1-归还记录
|
||||
*/
|
||||
dynamicType?: number;
|
||||
}
|
||||
|
||||
export interface BorrowReturnDynamicDTO {
|
||||
/** 订单商品ID */
|
||||
orderGoodsId: number;
|
||||
/** 动态类型(0-借出 1-归还) */
|
||||
dynamicType: number;
|
||||
/** 动态类型描述 */
|
||||
dynamicTypeStr: string;
|
||||
/** 订单ID */
|
||||
orderId: number;
|
||||
/** 订单创建时间/借出时间 */
|
||||
orderTime?: Date;
|
||||
/** 商品ID */
|
||||
goodsId: number;
|
||||
/** 商品名称 */
|
||||
goodsName: string;
|
||||
/** 商品单价 */
|
||||
goodsPrice: number;
|
||||
/** 数量 */
|
||||
quantity: number;
|
||||
/** 商品封面图 */
|
||||
coverImg?: string;
|
||||
/** 支付方式 */
|
||||
paymentMethod?: string;
|
||||
/** 订单姓名 */
|
||||
orderName?: string;
|
||||
/** 订单手机号 */
|
||||
orderMobile?: string;
|
||||
/** 格口ID */
|
||||
cellId?: number;
|
||||
/** 格口号 */
|
||||
cellNo?: number;
|
||||
/** 柜机ID */
|
||||
cabinetId?: number;
|
||||
/** 柜机名称 */
|
||||
cabinetName?: string;
|
||||
/** 归还/审批时间(归还记录时有效) */
|
||||
operateTime?: Date;
|
||||
/** 审批ID(归还记录时有效) */
|
||||
approvalId?: number;
|
||||
/** 审批状态(归还记录时有效) */
|
||||
status?: number;
|
||||
/** 状态描述 */
|
||||
statusStr?: string;
|
||||
/** 审批人(归还记录时有效) */
|
||||
auditName?: string;
|
||||
/** 审核说明(归还记录时有效) */
|
||||
auditRemark?: string;
|
||||
/** 归还图片(归还记录时有效) */
|
||||
images?: string;
|
||||
/** 审核图片(归还记录时有效) */
|
||||
auditImages?: string;
|
||||
}
|
||||
|
||||
/** 获取借还动态分页列表 */
|
||||
export const getBorrowReturnDynamicListApi = (params?: BorrowReturnDynamicQuery) => {
|
||||
return http.request<ResponseData<PageDTO<BorrowReturnDynamicDTO>>>("get", "/shop/order/borrow-return-dynamic", {
|
||||
params
|
||||
});
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
import { Goods, Shop, Document, Money } from '@element-plus/icons-vue';
|
||||
import { getStats, TodayLatestOrderGoodsDTO, TopGoodsDTO } from '@/api/shop/stats';
|
||||
import { getShopListApi, ShopDTO, getModeText } from '@/api/shop/shop';
|
||||
import { getBorrowReturnDynamicListApi, BorrowReturnDynamicDTO } from '@/api/shop/order';
|
||||
import { markRaw, onMounted, ref } from 'vue';
|
||||
import { useWxStore } from '@/store/modules/wx';
|
||||
import { ElDialog, ElForm, ElFormItem, ElInput, ElMessage } from 'element-plus';
|
||||
|
|
@ -43,6 +44,10 @@ const topGoods = ref<TopGoodsDTO[]>([]);
|
|||
const todayLatestOrderGoods = ref<TodayLatestOrderGoodsDTO[]>([]);
|
||||
const maxOccurrenceCount = ref(0);
|
||||
|
||||
// 借还动态数据
|
||||
const borrowReturnDynamicList = ref<BorrowReturnDynamicDTO[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const showDialog = ref(false);
|
||||
const form = ref({ name: '', idCard: '' });
|
||||
|
||||
|
|
@ -121,6 +126,66 @@ const fetchShopList = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
// 获取借还动态数据
|
||||
const fetchBorrowReturnDynamic = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await getBorrowReturnDynamicListApi({
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
});
|
||||
if (data && data.rows) {
|
||||
borrowReturnDynamicList.value = data.rows;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取借还动态失败:', error);
|
||||
ElMessage.error('获取借还动态失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const formatTime = (time: Date | string) => {
|
||||
if (!time) return '';
|
||||
const date = new Date(time);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
// 获取动态类型图标
|
||||
const getDynamicTypeIcon = (dynamicType: number) => {
|
||||
return dynamicType === 0 ? 'el-icon-shopping-bag-1' : 'el-icon-refresh';
|
||||
};
|
||||
|
||||
// 获取动态类型颜色
|
||||
const getDynamicTypeColor = (dynamicType: number) => {
|
||||
return dynamicType === 0 ? '#409EFF' : '#67C23A';
|
||||
};
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status?: number) => {
|
||||
if (status === undefined) return '#909399';
|
||||
switch (status) {
|
||||
case 0: return '#909399'; // 未退还
|
||||
case 1: return '#E6A23C'; // 待审批
|
||||
case 2: return '#67C23A'; // 已通过
|
||||
case 3: return '#F56C6C'; // 已驳回
|
||||
case 4: return '#409EFF'; // 开柜中
|
||||
default: return '#909399';
|
||||
}
|
||||
};
|
||||
|
||||
// 处理图片字符串,将逗号分隔的图片URL转换为数组
|
||||
const parseImages = (images?: string): string[] => {
|
||||
if (!images) return [];
|
||||
return images.split(',').map(img => img.trim()).filter(img => img.length > 0);
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchShopList();
|
||||
|
||||
|
|
@ -151,6 +216,8 @@ onMounted(async () => {
|
|||
console.error('获取统计数据失败:', error);
|
||||
}
|
||||
|
||||
// 获取借还动态数据
|
||||
await fetchBorrowReturnDynamic();
|
||||
|
||||
if (wxStore.qyUser && !wxStore.qyUser.ab98UserId) {
|
||||
showDialog.value = true;
|
||||
|
|
@ -170,11 +237,8 @@ onMounted(async () => {
|
|||
</div>
|
||||
<div class="shop-content">
|
||||
<div class="shop-info">
|
||||
<el-image
|
||||
:src="currentShop.coverImg || '/src/assets/login/login-bg.png'"
|
||||
fit="cover"
|
||||
class="shop-image"
|
||||
/>
|
||||
<el-image :src="currentShop.coverImg || '/src/assets/login/login-bg.png'" fit="cover"
|
||||
class="shop-image" />
|
||||
<div class="shop-details">
|
||||
<div class="shop-name-container">
|
||||
<h3 class="shop-name">{{ currentShop.shopName }}</h3>
|
||||
|
|
@ -196,11 +260,7 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
<div class="shop-qrcode">
|
||||
<el-image
|
||||
:src="qrcode"
|
||||
fit="contain"
|
||||
class="qrcode-image"
|
||||
/>
|
||||
<el-image :src="qrcode" fit="contain" class="qrcode-image" />
|
||||
<p class="qrcode-text">扫码进入小程序</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -303,7 +363,7 @@ onMounted(async () => {
|
|||
</el-row>
|
||||
</el-col>
|
||||
<!-- 热门商品 -->
|
||||
<el-col :span="7">
|
||||
<!-- <el-col :span="7">
|
||||
<div class="section-container goods-container">
|
||||
<div class="section-title">
|
||||
<div class="title-bar"></div>
|
||||
|
|
@ -326,6 +386,114 @@ onMounted(async () => {
|
|||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</el-col> -->
|
||||
<!-- 借还动态 -->
|
||||
<el-col :span="7">
|
||||
<div class="section-container dynamic-container">
|
||||
<div class="section-title">
|
||||
<div class="title-bar"></div>
|
||||
<div class="title-text">借还动态</div>
|
||||
</div>
|
||||
<div class="dynamic-content">
|
||||
<el-timeline v-if="borrowReturnDynamicList.length > 0" class="dynamic-timeline">
|
||||
<el-timeline-item v-for="item in borrowReturnDynamicList"
|
||||
:key="`${item.orderGoodsId}-${item.dynamicType}`"
|
||||
:timestamp="formatTime(item.dynamicType === 0 ? item.orderTime : item.operateTime)" placement="top"
|
||||
:color="getDynamicTypeColor(item.dynamicType)">
|
||||
<el-card shadow="hover" class="dynamic-card">
|
||||
<div class="dynamic-card-content">
|
||||
<div class="dynamic-header">
|
||||
<el-icon :color="getDynamicTypeColor(item.dynamicType)" class="dynamic-icon">
|
||||
<component :is="getDynamicTypeIcon(item.dynamicType)" />
|
||||
</el-icon>
|
||||
<span class="dynamic-type" :style="{ color: getDynamicTypeColor(item.dynamicType) }">
|
||||
{{ item.dynamicTypeStr }}
|
||||
</span>
|
||||
<el-tag v-if="item.dynamicType === 1 && item.status !== undefined"
|
||||
:color="getStatusColor(item.status)" size="small" class="status-tag">
|
||||
{{ item.statusStr }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="dynamic-info">
|
||||
<!-- 商品图片和基本信息 -->
|
||||
<div class="goods-info">
|
||||
<el-image v-if="item.coverImg" :src="item.coverImg" fit="cover" class="goods-image"
|
||||
:preview-src-list="[item.coverImg]" preview-teleported>
|
||||
<template #error>
|
||||
<div class="image-error">图片加载失败</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="goods-details">
|
||||
<span class="goods-name">{{ item.goodsName }}</span>
|
||||
<span class="goods-price">¥{{ item.goodsPrice }}</span>
|
||||
<div class="user-info">
|
||||
<span class="user-name">{{ item.orderName }}</span>
|
||||
<span class="user-mobile">{{ item.orderMobile }}</span>
|
||||
</div>
|
||||
<div class="location-info">
|
||||
<span class="cabinet-name">{{ item.cabinetName }}</span>
|
||||
<span class="cell-no">格口{{ item.cellNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.dynamicType === 1" class="approval-info">
|
||||
<!-- 图片区域 - 归还图片和审核图片放在同一行 -->
|
||||
<div class="images-row" v-if="item.images || item.auditImages">
|
||||
<!-- 归还图片 -->
|
||||
<div v-if="item.images" class="images-section">
|
||||
<div class="images-label">归还图片:</div>
|
||||
<div class="images-list">
|
||||
<el-image v-for="(img, index) in parseImages(item.images)" :key="index" :src="img"
|
||||
fit="cover" class="return-image" :preview-src-list="parseImages(item.images)"
|
||||
preview-teleported>
|
||||
<template #error>
|
||||
<div class="image-error">图片加载失败</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审核图片 -->
|
||||
<div v-if="item.auditImages" class="images-section">
|
||||
<div class="images-label">审核图片:</div>
|
||||
<div class="images-list">
|
||||
<el-image v-for="(img, index) in parseImages(item.auditImages)" :key="index" :src="img"
|
||||
fit="cover" class="audit-image" :preview-src-list="parseImages(item.auditImages)"
|
||||
preview-teleported>
|
||||
<template #error>
|
||||
<div class="image-error">图片加载失败</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.auditName" class="audit-info">
|
||||
<span class="audit-label">审批人:</span>
|
||||
<span class="audit-name">{{ item.auditName }}</span>
|
||||
</div>
|
||||
<div v-if="item.auditRemark && item.auditRemark != '自动审批'" class="remark-info">
|
||||
<span class="remark-label">说明:</span>
|
||||
<span class="audit-remark">{{ item.auditRemark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<div v-else-if="loading" class="dynamic-empty">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
|
||||
<div v-else class="dynamic-empty">
|
||||
<el-empty description="暂无借还动态" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
|
@ -622,5 +790,223 @@ onMounted(async () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 借还动态样式
|
||||
.dynamic-container {
|
||||
margin-bottom: 0;
|
||||
height: 88vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.dynamic-content {
|
||||
.dynamic-timeline {
|
||||
padding: 0;
|
||||
|
||||
:deep(.el-timeline-item) {
|
||||
.el-timeline-item__timestamp {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.el-timeline-item__node {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dynamic-card {
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
|
||||
.dynamic-card-content {
|
||||
.dynamic-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
|
||||
.dynamic-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.dynamic-type {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
margin-left: auto;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dynamic-info {
|
||||
.goods-info {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.goods-image {
|
||||
width: 120px;
|
||||
height: 90px;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
|
||||
.goods-details {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
min-height: 60px;
|
||||
|
||||
.goods-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: var(--el-text-color-primary);
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-price {
|
||||
font-size: 14px;
|
||||
color: var(--el-color-primary);
|
||||
font-weight: bold;
|
||||
align-self: flex-start;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 4px;
|
||||
|
||||
.user-name,
|
||||
.user-mobile {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.location-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.cabinet-name,
|
||||
.cell-no {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-info,
|
||||
.location-info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.approval-info {
|
||||
background-color: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin-top: 8px;
|
||||
|
||||
.images-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.images-section {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.images-label {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: var(--el-text-color-secondary);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.images-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
|
||||
.return-image,
|
||||
.audit-image {
|
||||
width: 110px;
|
||||
height: 70px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.audit-info,
|
||||
.remark-info {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
.audit-label,
|
||||
.remark-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.audit-name,
|
||||
.audit-remark {
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.remark-info {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image-error {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.dynamic-empty {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
|
||||
:deep(.el-empty) {
|
||||
.el-empty__description {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue