Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -181,6 +181,59 @@ public class CrmOrderItemVo {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 删除标志
|
||||
*/
|
||||
private Long delFlag;
|
||||
|
||||
/**
|
||||
* 合同号(联表查询直接映射)
|
||||
*/
|
||||
@ExcelProperty(value = "合同号")
|
||||
private String contractCode;
|
||||
|
||||
/**
|
||||
* 供方(联表查询直接映射)
|
||||
*/
|
||||
@ExcelProperty(value = "供方")
|
||||
private String supplier;
|
||||
|
||||
/**
|
||||
* 需方(联表查询直接映射)
|
||||
*/
|
||||
@ExcelProperty(value = "需方")
|
||||
private String customer;
|
||||
|
||||
/**
|
||||
* 签订时间(联表查询直接映射)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@ExcelProperty(value = "签订时间")
|
||||
private Date signTime;
|
||||
|
||||
/**
|
||||
* 交货日期(联表查询直接映射)
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@ExcelProperty(value = "交货日期")
|
||||
private Date deliveryDate;
|
||||
|
||||
/**
|
||||
* 订单信息
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.klp.crm.mapper;
|
||||
|
||||
import com.klp.crm.domain.CrmOrderItem;
|
||||
import com.klp.crm.domain.bo.CrmOrderItemBo;
|
||||
import com.klp.crm.domain.vo.CrmOrderItemVo;
|
||||
import com.klp.common.core.mapper.BaseMapperPlus;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
@@ -22,4 +24,14 @@ public interface CrmOrderItemMapper extends BaseMapperPlus<CrmOrderItemMapper, C
|
||||
* @return 订单明细列表
|
||||
*/
|
||||
List<CrmOrderItem> selectOrderItemsByOrderIds(@Param("orderIds") List<Long> orderIds);
|
||||
|
||||
/**
|
||||
* 联表查询订单明细(支持排序和分页)
|
||||
* 排序规则:deliveryDate DESC -> orderId ASC -> createTime DESC
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param bo 查询条件
|
||||
* @return 分页结果
|
||||
*/
|
||||
Page<CrmOrderItemVo> selectVoListWithOrder(Page<CrmOrderItemVo> page, @Param("bo") CrmOrderItemBo bo);
|
||||
}
|
||||
|
||||
@@ -69,51 +69,19 @@ public class CrmOrderItemServiceImpl implements ICrmOrderItemService {
|
||||
|
||||
/**
|
||||
* 查询正式订单明细列表
|
||||
* 实现逻辑:查出全部匹配记录 → Java内存排序(交货日期倒序→订单ID升序→创建时间倒序)→ 手动分页
|
||||
* 排序跨页生效,同一合同明细连续排列
|
||||
* 实现逻辑:SQL联表查询 → 数据库层排序(交货日期倒序→订单ID升序→创建时间倒序)→ 物理分页
|
||||
* 排序跨页生效,同一合同明细连续排列,避免内存排序大数据量问题
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<CrmOrderItemVo> queryPageList(CrmOrderItemBo bo, PageQuery pageQuery) {
|
||||
List<Long> orderIdScope = resolveOrderIdScope(bo);
|
||||
if (orderIdScope != null && orderIdScope.isEmpty()) {
|
||||
Page<CrmOrderItemVo> emptyPage = new Page<>(ObjectUtil.defaultIfNull(pageQuery.getPageNum(), 1),
|
||||
ObjectUtil.defaultIfNull(pageQuery.getPageSize(), 10), 0);
|
||||
emptyPage.setRecords(Collections.emptyList());
|
||||
return TableDataInfo.build(emptyPage);
|
||||
}
|
||||
LambdaQueryWrapper<CrmOrderItem> lqw = buildQueryWrapper(bo);
|
||||
if (orderIdScope != null) {
|
||||
lqw.in(CrmOrderItem::getOrderId, orderIdScope);
|
||||
}
|
||||
// 1. 查出全部匹配记录
|
||||
List<CrmOrderItemVo> allItems = baseMapper.selectVoList(lqw);
|
||||
// 2. 填充订单信息(含 deliveryDate)
|
||||
fillOrderInfoOnItems(allItems);
|
||||
// 3. 三级排序:deliveryDate DESC → orderId ASC → createTime DESC
|
||||
allItems.sort((a, b) -> {
|
||||
// 同一合同组内:按创建时间倒序
|
||||
if (Objects.equals(a.getOrderId(), b.getOrderId())) {
|
||||
return compareDate(a.getCreateTime(), b.getCreateTime(), true);
|
||||
}
|
||||
// 不同合同组:按交货日期倒序
|
||||
Date dateA = a.getOrderInfo() != null ? a.getOrderInfo().getDeliveryDate() : null;
|
||||
Date dateB = b.getOrderInfo() != null ? b.getOrderInfo().getDeliveryDate() : null;
|
||||
int cmp = compareDate(dateA, dateB, true);
|
||||
if (cmp != 0) return cmp;
|
||||
// 交货日期相同:按订单ID升序,保证同一合同组连续
|
||||
return Long.compare(a.getOrderId(), b.getOrderId());
|
||||
});
|
||||
// 4. 手动分页
|
||||
int total = allItems.size();
|
||||
int pageNum = ObjectUtil.defaultIfNull(pageQuery.getPageNum(), 1);
|
||||
int pageSize = ObjectUtil.defaultIfNull(pageQuery.getPageSize(), 10);
|
||||
int from = (pageNum - 1) * pageSize;
|
||||
int to = Math.min(from + pageSize, total);
|
||||
List<CrmOrderItemVo> pageItems = from >= total ? Collections.emptyList() : allItems.subList(from, to);
|
||||
|
||||
Page<CrmOrderItemVo> page = new Page<>(pageNum, pageSize, total);
|
||||
page.setRecords(pageItems);
|
||||
return TableDataInfo.build(page);
|
||||
// 使用MyBatis-Plus分页插件,SQL层完成联表查询、排序和分页
|
||||
Page<CrmOrderItemVo> page = new Page<>(
|
||||
ObjectUtil.defaultIfNull(pageQuery.getPageNum(), 1),
|
||||
ObjectUtil.defaultIfNull(pageQuery.getPageSize(), 10)
|
||||
);
|
||||
// 联表查询:在SQL层完成排序,避免内存排序
|
||||
Page<CrmOrderItemVo> resultPage = baseMapper.selectVoListWithOrder(page, bo);
|
||||
return TableDataInfo.build(resultPage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.crm.mapper.CrmOrderItemMapper">
|
||||
|
||||
<resultMap type="com.klp.crm.domain.CrmOrderItem" id="CrmOrderItemResult">
|
||||
@@ -36,6 +36,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="delFlag" column="del_flag"/>
|
||||
</resultMap>
|
||||
<!-- 联表查询结果映射 -->
|
||||
<resultMap type="com.klp.crm.domain.vo.CrmOrderItemVo" id="CrmOrderItemVoResult">
|
||||
<result property="itemId" column="item_id"/>
|
||||
<result property="orderId" column="order_id"/>
|
||||
<result property="productType" column="product_type"/>
|
||||
<result property="rawMaterialSpec" column="raw_material_spec"/>
|
||||
<result property="productNum" column="product_num"/>
|
||||
<result property="specialRequire" column="special_require"/>
|
||||
<result property="itemAmount" column="item_amount"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="finishedProductSpec" column="finished_product_spec"/>
|
||||
<result property="material" column="material"/>
|
||||
<result property="grade" column="grade"/>
|
||||
<result property="weight" column="weight"/>
|
||||
<result property="widthTolerance" column="width_tolerance"/>
|
||||
<result property="thicknessTolerance" column="thickness_tolerance"/>
|
||||
<result property="contractPrice" column="contract_price"/>
|
||||
<result property="customizer" column="customizer"/>
|
||||
<result property="shipper" column="shipper"/>
|
||||
<result property="productionBatch" column="production_batch"/>
|
||||
<result property="surfaceTreatment" column="surface_treatment"/>
|
||||
<result property="surfaceQuality" column="surface_quality"/>
|
||||
<result property="edgeCuttingReq" column="edge_cutting_req"/>
|
||||
<result property="packagingReq" column="packaging_req"/>
|
||||
<result property="width" column="width"/>
|
||||
<result property="thickness" column="thickness"/>
|
||||
<result property="purpose" column="purpose"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<result property="delFlag" column="del_flag"/>
|
||||
<!-- 合同信息字段 -->
|
||||
<result property="contractCode" column="contract_code"/>
|
||||
<result property="supplier" column="supplier"/>
|
||||
<result property="customer" column="customer"/>
|
||||
<result property="signTime" column="sign_time"/>
|
||||
<result property="deliveryDate" column="delivery_date"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 根据订单ID列表查询订单明细 -->
|
||||
<select id="selectOrderItemsByOrderIds" resultMap="CrmOrderItemResult">
|
||||
SELECT
|
||||
@@ -78,5 +118,132 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ORDER BY item_id ASC
|
||||
</select>
|
||||
|
||||
<!-- 联表查询订单明细(支持排序和分页) -->
|
||||
<select id="selectVoListWithOrder" resultMap="CrmOrderItemVoResult">
|
||||
SELECT
|
||||
i.item_id,
|
||||
i.order_id,
|
||||
i.product_type,
|
||||
i.raw_material_spec,
|
||||
i.product_num,
|
||||
i.special_require,
|
||||
i.item_amount,
|
||||
i.remark,
|
||||
i.finished_product_spec,
|
||||
i.material,
|
||||
i.grade,
|
||||
i.weight,
|
||||
i.width_tolerance,
|
||||
i.thickness_tolerance,
|
||||
i.contract_price,
|
||||
i.customizer,
|
||||
i.shipper,
|
||||
i.production_batch,
|
||||
i.surface_treatment,
|
||||
i.surface_quality,
|
||||
i.edge_cutting_req,
|
||||
i.packaging_req,
|
||||
i.width,
|
||||
i.thickness,
|
||||
i.purpose,
|
||||
i.create_by,
|
||||
i.create_time,
|
||||
i.update_by,
|
||||
i.update_time,
|
||||
i.del_flag,
|
||||
o.contract_code,
|
||||
o.supplier,
|
||||
o.customer,
|
||||
o.sign_time,
|
||||
o.delivery_date
|
||||
FROM crm_order_item i
|
||||
LEFT JOIN crm_order o ON i.order_id = o.order_id AND o.del_flag = 0
|
||||
<where>
|
||||
i.del_flag = 0
|
||||
<if test="bo.itemId != null">
|
||||
AND i.item_id = #{bo.itemId}
|
||||
</if>
|
||||
<if test="bo.orderId != null">
|
||||
AND i.order_id = #{bo.orderId}
|
||||
</if>
|
||||
<if test="bo.productType != null and bo.productType != ''">
|
||||
AND i.product_type = #{bo.productType}
|
||||
</if>
|
||||
<if test="bo.rawMaterialSpec != null and bo.rawMaterialSpec != ''">
|
||||
AND i.raw_material_spec = #{bo.rawMaterialSpec}
|
||||
</if>
|
||||
<if test="bo.productNum != null">
|
||||
AND i.product_num = #{bo.productNum}
|
||||
</if>
|
||||
<if test="bo.specialRequire != null and bo.specialRequire != ''">
|
||||
AND i.special_require = #{bo.specialRequire}
|
||||
</if>
|
||||
<if test="bo.finishedProductSpec != null and bo.finishedProductSpec != ''">
|
||||
AND i.finished_product_spec = #{bo.finishedProductSpec}
|
||||
</if>
|
||||
<if test="bo.material != null and bo.material != ''">
|
||||
AND i.material LIKE CONCAT('%', #{bo.material}, '%')
|
||||
</if>
|
||||
<if test="bo.grade != null and bo.grade != ''">
|
||||
AND i.grade = #{bo.grade}
|
||||
</if>
|
||||
<if test="bo.weight != null">
|
||||
AND i.weight = #{bo.weight}
|
||||
</if>
|
||||
<if test="bo.contractPrice != null">
|
||||
AND i.contract_price = #{bo.contractPrice}
|
||||
</if>
|
||||
<if test="bo.customizer != null and bo.customizer != ''">
|
||||
AND i.customizer = #{bo.customizer}
|
||||
</if>
|
||||
<if test="bo.shipper != null and bo.shipper != ''">
|
||||
AND i.shipper = #{bo.shipper}
|
||||
</if>
|
||||
<if test="bo.productionBatch != null and bo.productionBatch != ''">
|
||||
AND i.production_batch = #{bo.productionBatch}
|
||||
</if>
|
||||
<if test="bo.surfaceTreatment != null and bo.surfaceTreatment != ''">
|
||||
AND i.surface_treatment = #{bo.surfaceTreatment}
|
||||
</if>
|
||||
<if test="bo.surfaceQuality != null and bo.surfaceQuality != ''">
|
||||
AND i.surface_quality = #{bo.surfaceQuality}
|
||||
</if>
|
||||
<if test="bo.edgeCuttingReq != null and bo.edgeCuttingReq != ''">
|
||||
AND i.edge_cutting_req = #{bo.edgeCuttingReq}
|
||||
</if>
|
||||
<if test="bo.packagingReq != null and bo.packagingReq != ''">
|
||||
AND i.packaging_req = #{bo.packagingReq}
|
||||
</if>
|
||||
<if test="bo.width != null and bo.width != ''">
|
||||
AND i.width = #{bo.width}
|
||||
</if>
|
||||
<if test="bo.thickness != null and bo.thickness != ''">
|
||||
AND i.thickness = #{bo.thickness}
|
||||
</if>
|
||||
<if test="bo.purpose != null and bo.purpose != ''">
|
||||
AND i.purpose = #{bo.purpose}
|
||||
</if>
|
||||
<!-- 合同表筛选条件 -->
|
||||
<if test="bo.contractCode != null and bo.contractCode != ''">
|
||||
AND o.contract_code LIKE CONCAT('%', #{bo.contractCode}, '%')
|
||||
</if>
|
||||
<if test="bo.customer != null and bo.customer != ''">
|
||||
AND o.customer LIKE CONCAT('%', #{bo.customer}, '%')
|
||||
</if>
|
||||
<if test="bo.signDateStart != null">
|
||||
AND o.sign_time >= #{bo.signDateStart}
|
||||
</if>
|
||||
<if test="bo.signDateEnd != null">
|
||||
AND o.sign_time < DATE_ADD(#{bo.signDateEnd}, INTERVAL 1 DAY)
|
||||
</if>
|
||||
<if test="bo.deliveryDateStart != null">
|
||||
AND o.delivery_date >= #{bo.deliveryDateStart}
|
||||
</if>
|
||||
<if test="bo.deliveryDateEnd != null">
|
||||
AND o.delivery_date < DATE_ADD(#{bo.deliveryDateEnd}, INTERVAL 1 DAY)
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY o.delivery_date DESC, i.order_id ASC, i.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -314,7 +314,6 @@
|
||||
|
||||
<script>
|
||||
import { listOrderItem, updateOrderItem } from '@/api/crm/orderItem'
|
||||
import { listOrder } from '@/api/crm/order'
|
||||
import { listCategory } from '@/api/wms/category'
|
||||
|
||||
export default {
|
||||
@@ -340,8 +339,6 @@ export default {
|
||||
deliveryDateStart: undefined,
|
||||
deliveryDateEnd: undefined
|
||||
},
|
||||
// 缓存合同信息
|
||||
contractMap: {},
|
||||
// 存储原始数据,用于判断是否有修改
|
||||
originalData: {},
|
||||
// 表面处理选项
|
||||
@@ -372,49 +369,8 @@ export default {
|
||||
getList() {
|
||||
this.loading = true
|
||||
listOrderItem(this.queryParams).then(res => {
|
||||
const items = res.rows || []
|
||||
this.orderItemList = res.rows || []
|
||||
this.total = res.total || 0
|
||||
// 获取所有相关的合同ID
|
||||
const orderIds = [...new Set(items.map(item => item.orderId).filter(id => id))]
|
||||
if (orderIds.length > 0) {
|
||||
// 批量获取合同信息
|
||||
this.loadContractInfo(orderIds, items)
|
||||
} else {
|
||||
this.orderItemList = items
|
||||
this.loading = false
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 加载合同信息
|
||||
loadContractInfo(orderIds, items) {
|
||||
// 使用listOrder获取合同信息,通过params传递orderIds
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
orderIds: orderIds.join(',')
|
||||
}
|
||||
listOrder(params).then(res => {
|
||||
const contracts = res.rows || []
|
||||
// 构建合同ID到合同信息的映射
|
||||
contracts.forEach(contract => {
|
||||
this.contractMap[contract.orderId] = contract
|
||||
})
|
||||
// 直接合并数据(后端已按 deliveryDate DESC + createTime DESC 排序)
|
||||
this.orderItemList = items.map(item => {
|
||||
const contract = this.contractMap[item.orderId] || {}
|
||||
return {
|
||||
...item,
|
||||
contractCode: contract.contractCode || '',
|
||||
supplier: contract.supplier || '',
|
||||
customer: contract.customer || '',
|
||||
signTime: contract.signTime || '',
|
||||
deliveryDate: contract.deliveryDate || ''
|
||||
}
|
||||
})
|
||||
// 保存原始数据副本
|
||||
this.originalData = {}
|
||||
this.orderItemList.forEach(row => {
|
||||
@@ -422,22 +378,11 @@ export default {
|
||||
})
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.orderItemList = items
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 分组行样式:同一合同组使用交替背景色
|
||||
groupRowClassName({ row, rowIndex }) {
|
||||
// 记录上一个orderId,切换时翻转颜色
|
||||
if (rowIndex === 0 || (this._lastOrderId !== undefined && this._lastOrderId !== row.orderId)) {
|
||||
this._groupColorIndex = (this._groupColorIndex || 0) + 1
|
||||
}
|
||||
this._lastOrderId = row.orderId
|
||||
return this._groupColorIndex % 2 === 1 ? 'group-row-a' : 'group-row-b'
|
||||
},
|
||||
|
||||
// 判断数据是否有变化
|
||||
hasChanged(row) {
|
||||
const id = row.itemId || row.detailId
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
@click="handleRefreshDetailList">刷新</el-button>
|
||||
<coil-selector v-loading="buttonLoading" ref="coilSelector" :filters="{ dataType: 1, status: 0 }" multiple
|
||||
@confirm="handleCoilChange" v-if="canAddCoils"></coil-selector>
|
||||
<import-coil v-if="canAddCoils" :transferId="currentOrderId" @success="handleImportSuccess" />
|
||||
<el-checkbox v-model="batchEdit" style="margin-right: 10px;">批量操作</el-checkbox>
|
||||
</div>
|
||||
<transfer-item-table ref="transferItemTable" :data="transferOrderItems" :batchEdit="batchEdit"
|
||||
@@ -147,12 +148,14 @@ import { listTransferOrder, getTransferOrder, delTransferOrder, addTransferOrder
|
||||
import { listTransferOrderItem, batchAddTransferOrderItem } from "@/api/wms/transferOrderItem";
|
||||
import CoilSelector from "@/components/CoilSelector";
|
||||
import TransferItemTable from "@/views/wms/move/components/tranferItemTable.vue";
|
||||
import ImportCoil from "@/views/wms/move/components/ImportCoil.vue";
|
||||
|
||||
export default {
|
||||
name: "TransferOrder",
|
||||
components: {
|
||||
CoilSelector,
|
||||
TransferItemTable,
|
||||
ImportCoil,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -472,6 +475,10 @@ export default {
|
||||
this.buttonLoading = false;
|
||||
})
|
||||
},
|
||||
/** 导入钢卷成功回调 */
|
||||
handleImportSuccess() {
|
||||
this.handleView({ orderId: this.currentOrderId });
|
||||
},
|
||||
// 取消按钮
|
||||
cancel() {
|
||||
this.open = false;
|
||||
|
||||
723
klp-ui/src/views/wms/move/components/ImportCoil.vue
Normal file
723
klp-ui/src/views/wms/move/components/ImportCoil.vue
Normal file
@@ -0,0 +1,723 @@
|
||||
<template>
|
||||
<div class="import-coil-container">
|
||||
<!-- 操作栏 -->
|
||||
<div class="action-bar">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
class="upload-excel"
|
||||
action=""
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:on-change="handleFileChange"
|
||||
accept=".xlsx,.xls"
|
||||
:disabled="isProcessing"
|
||||
>
|
||||
<el-button type="primary" icon="el-icon-upload2" size="small">选择Excel文件</el-button>
|
||||
</el-upload>
|
||||
|
||||
<el-button
|
||||
type="default"
|
||||
icon="el-icon-download"
|
||||
size="small"
|
||||
@click="downloadTemplate"
|
||||
>
|
||||
下载模板
|
||||
</el-button>
|
||||
|
||||
<span v-if="fileName" class="file-name">{{ fileName }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 导入预览弹窗 -->
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="900px"
|
||||
top="5vh"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
append-to-body
|
||||
custom-class="import-coil-dialog"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<!-- 匹配摘要统计卡片 -->
|
||||
<div v-if="step === 'REVIEW' || step === 'IMPORTING' || step === 'FINISHED'" class="summary-cards">
|
||||
<el-row :gutter="15">
|
||||
<el-col :span="6">
|
||||
<div class="stat-card success">
|
||||
<div class="stat-icon"><i class="el-icon-check"></i></div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ matchedRows.length }}</div>
|
||||
<div class="stat-label">已匹配</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card warning">
|
||||
<div class="stat-icon"><i class="el-icon-warning-outline"></i></div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ conflictRows.length }}</div>
|
||||
<div class="stat-label">冲突</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card danger">
|
||||
<div class="stat-icon"><i class="el-icon-close"></i></div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ unmatchedRows.length }}</div>
|
||||
<div class="stat-label">未匹配</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-card primary">
|
||||
<div class="stat-icon"><i class="el-icon-suitcase"></i></div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ pendingImportCount }}</div>
|
||||
<div class="stat-label">待导入</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 数据预览(解析后、匹配前) -->
|
||||
<div v-if="step === 'PARSED'" class="preview-section">
|
||||
<el-alert
|
||||
title="数据预览"
|
||||
type="info"
|
||||
:description="'共解析出 ' + parsedData.length + ' 条数据,点击「开始匹配」进行钢卷匹配'"
|
||||
show-icon
|
||||
:closable="false"
|
||||
style="margin-bottom: 15px;"
|
||||
/>
|
||||
<el-table :data="parsedData" border size="small" max-height="400" stripe>
|
||||
<el-table-column prop="rowNum" label="行号" width="60" align="center" />
|
||||
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="150" />
|
||||
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="150" />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 标签页内容(匹配结果) -->
|
||||
<div v-if="step === 'REVIEW' || step === 'IMPORTING' || step === 'FINISHED'" class="tabs-section">
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<!-- 已匹配 -->
|
||||
<el-tab-pane :label="'已匹配 (' + matchedRows.length + ')'" name="matched">
|
||||
<div v-if="matchedRows.length === 0" class="empty-tip">
|
||||
<i class="el-icon-success" style="color: #67c23a; font-size: 48px;"></i>
|
||||
<p>没有已匹配的钢卷</p>
|
||||
</div>
|
||||
<el-table v-else :data="matchedRows" border size="small" max-height="350" stripe>
|
||||
<el-table-column prop="rowNum" label="行号" width="60" align="center" />
|
||||
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="120" />
|
||||
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="120" />
|
||||
<el-table-column label="匹配钢卷" min-width="120">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag size="small" type="success">{{ row.matchedCoil.currentCoilNo }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="净重" width="100" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
{{ row.matchedCoil.netWeight }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库区" min-width="120">
|
||||
<template slot-scope="{ row }">
|
||||
{{ row.matchedCoil.warehouseName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template slot-scope="{ $index }">
|
||||
<el-button type="text" size="mini" @click="removeMatchedRow($index)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 冲突 -->
|
||||
<el-tab-pane :label="'冲突 (' + conflictRows.length + ')'" name="conflict">
|
||||
<div v-if="conflictRows.length === 0" class="empty-tip">
|
||||
<i class="el-icon-warning" style="color: #e6a23c; font-size: 48px;"></i>
|
||||
<p>没有冲突的钢卷</p>
|
||||
</div>
|
||||
<div v-else class="conflict-list">
|
||||
<el-alert
|
||||
title="以下数据匹配到多条钢卷,请为每行选择正确的钢卷"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
style="margin-bottom: 10px;"
|
||||
/>
|
||||
<el-table :data="conflictRows" border size="small" max-height="350" stripe>
|
||||
<el-table-column prop="rowNum" label="行号" width="60" align="center" />
|
||||
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="120" />
|
||||
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="120" />
|
||||
<el-table-column label="选择钢卷" min-width="280">
|
||||
<template slot-scope="{ row }">
|
||||
<el-select
|
||||
v-model="row.selectedCoilId"
|
||||
placeholder="请选择正确的钢卷"
|
||||
size="small"
|
||||
style="width: 100%;"
|
||||
@change="handleConflictSelect(row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="coil in row.candidates"
|
||||
:key="coil.coilId"
|
||||
:label="coil.currentCoilNo + ' | 净重:' + coil.netWeight + ' | ' + (coil.warehouseName || '')"
|
||||
:value="coil.coilId"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.selectedCoilId" size="small" type="success">已选择</el-tag>
|
||||
<el-tag v-else size="small" type="danger">待选择</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 未匹配 -->
|
||||
<el-tab-pane :label="'未匹配 (' + unmatchedRows.length + ')'" name="unmatched">
|
||||
<div v-if="unmatchedRows.length === 0" class="empty-tip">
|
||||
<i class="el-icon-circle-check" style="color: #909399; font-size: 48px;"></i>
|
||||
<p>没有未匹配的钢卷</p>
|
||||
</div>
|
||||
<el-table v-else :data="unmatchedRows" border size="small" max-height="350" stripe>
|
||||
<el-table-column prop="rowNum" label="行号" width="60" align="center" />
|
||||
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="150" />
|
||||
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="150" />
|
||||
<el-table-column prop="reason" label="原因" min-width="200">
|
||||
<template slot-scope="{ row }">
|
||||
<span style="color: #f56c6c;">{{ row.reason }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 导入进度 -->
|
||||
<div v-if="step === 'IMPORTING'" class="progress-section">
|
||||
<el-progress :percentage="importProgress" status="success" :stroke-width="18" />
|
||||
<p class="progress-text">正在导入 {{ importedCount }} / {{ pendingImportCount }} 条钢卷...</p>
|
||||
</div>
|
||||
|
||||
<!-- 导入完成 -->
|
||||
<div v-if="step === 'FINISHED'" class="finished-section">
|
||||
<div class="success-icon">
|
||||
<i class="el-icon-circle-check"></i>
|
||||
</div>
|
||||
<h3>导入完成</h3>
|
||||
<p>成功导入 {{ importedCount }} 条钢卷到调拨单</p>
|
||||
</div>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<!-- 解析后:开始匹配 -->
|
||||
<template v-if="step === 'PARSED'">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="matchLoading" @click="handleMatch">
|
||||
开始匹配
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 匹配后:确认导入 -->
|
||||
<template v-if="step === 'REVIEW'">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="importLoading"
|
||||
:disabled="pendingImportCount === 0"
|
||||
@click="handleImport"
|
||||
>
|
||||
确认导入 ({{ pendingImportCount }}卷)
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 导入中:禁用关闭 -->
|
||||
<template v-if="step === 'IMPORTING'">
|
||||
<el-button disabled>导入中...</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 导入完成:关闭 -->
|
||||
<template v-if="step === 'FINISHED'">
|
||||
<el-button type="primary" @click="handleFinish">完成</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as XLSX from 'xlsx';
|
||||
import { listMaterialCoil } from '@/api/wms/coil';
|
||||
import { batchAddTransferOrderItem } from '@/api/wms/transferOrderItem';
|
||||
|
||||
// Excel表头定义
|
||||
const REQUIRED_HEADERS = ['入场卷号', '当前卷号'];
|
||||
|
||||
export default {
|
||||
name: 'ImportCoil',
|
||||
props: {
|
||||
transferId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
step: 'IDLE', // IDLE | PARSED | MATCHING | REVIEW | IMPORTING | FINISHED
|
||||
dialogVisible: false,
|
||||
activeTab: 'matched',
|
||||
fileName: '',
|
||||
parsedData: [],
|
||||
matchedRows: [],
|
||||
conflictRows: [],
|
||||
unmatchedRows: [],
|
||||
matchLoading: false,
|
||||
importLoading: false,
|
||||
importProgress: 0,
|
||||
importedCount: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isProcessing() {
|
||||
return this.step === 'MATCHING' || this.step === 'IMPORTING';
|
||||
},
|
||||
dialogTitle() {
|
||||
switch (this.step) {
|
||||
case 'PARSED':
|
||||
return '导入钢卷 - 数据预览 (' + this.parsedData.length + '条)';
|
||||
case 'REVIEW':
|
||||
case 'IMPORTING':
|
||||
case 'FINISHED':
|
||||
return '导入钢卷 - 匹配结果 (' + this.parsedData.length + '条)';
|
||||
default:
|
||||
return '导入钢卷';
|
||||
}
|
||||
},
|
||||
// 待导入数量 = 已匹配 + 冲突中已选择的
|
||||
pendingImportCount() {
|
||||
let count = this.matchedRows.length;
|
||||
this.conflictRows.forEach(r => {
|
||||
if (r.selectedCoilId) count++;
|
||||
});
|
||||
return count;
|
||||
},
|
||||
// 所有待导入的coilId
|
||||
pendingCoilIds() {
|
||||
const ids = this.matchedRows.map(r => r.matchedCoil.coilId);
|
||||
this.conflictRows.forEach(r => {
|
||||
if (r.selectedCoilId) {
|
||||
ids.push(r.selectedCoilId);
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 下载Excel模板 */
|
||||
downloadTemplate() {
|
||||
const wsData = [REQUIRED_HEADERS, ['(示例)G2024001', '(示例)G2024001']];
|
||||
const ws = XLSX.utils.aoa_to_sheet(wsData);
|
||||
ws['!cols'] = [{ wch: 20 }, { wch: 20 }];
|
||||
const wb = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(wb, ws, '钢卷导入');
|
||||
XLSX.writeFile(wb, '调拨单钢卷导入模板.xlsx');
|
||||
},
|
||||
|
||||
/** 选择文件 */
|
||||
handleFileChange(file) {
|
||||
if (this.isProcessing) {
|
||||
this.$message.warning('当前有操作正在进行中');
|
||||
return;
|
||||
}
|
||||
this.fileName = file.name;
|
||||
this.readExcel(file.raw);
|
||||
},
|
||||
|
||||
/** 读取Excel */
|
||||
readExcel(file) {
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = new Uint8Array(e.target.result);
|
||||
const workbook = XLSX.read(data, { type: 'array' });
|
||||
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });
|
||||
|
||||
// 校验表头
|
||||
const headers = jsonData[0] || [];
|
||||
const headerErrors = this.validateHeaders(headers);
|
||||
if (headerErrors.length > 0) {
|
||||
this.$message.error(headerErrors.join(';'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析数据行
|
||||
const rows = jsonData.slice(1).filter(row => row.some(cell => cell !== undefined && cell !== null && String(cell).trim() !== ''));
|
||||
if (rows.length === 0) {
|
||||
this.$message.warning('Excel中没有有效数据');
|
||||
return;
|
||||
}
|
||||
|
||||
this.parsedData = rows.map((row, index) => ({
|
||||
rowNum: index + 2,
|
||||
enterCoilNo: row[0] ? String(row[0]).trim() : '',
|
||||
currentCoilNo: row[1] ? String(row[1]).trim() : '',
|
||||
})).filter(item => item.enterCoilNo || item.currentCoilNo);
|
||||
|
||||
// 重置匹配结果
|
||||
this.matchedRows = [];
|
||||
this.conflictRows = [];
|
||||
this.unmatchedRows = [];
|
||||
this.step = 'PARSED';
|
||||
this.activeTab = 'matched';
|
||||
this.dialogVisible = true;
|
||||
this.$message.success('成功解析 ' + this.parsedData.length + ' 条数据');
|
||||
} catch (error) {
|
||||
this.$message.error('解析Excel失败:' + error.message);
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
},
|
||||
|
||||
/** 校验表头 */
|
||||
validateHeaders(headers) {
|
||||
const errors = [];
|
||||
if (headers.length < REQUIRED_HEADERS.length) {
|
||||
errors.push('表头列数不匹配,需要' + REQUIRED_HEADERS.length + '列,实际' + headers.length + '列');
|
||||
return errors;
|
||||
}
|
||||
REQUIRED_HEADERS.forEach((h, i) => {
|
||||
if (headers[i] !== h) {
|
||||
errors.push('第' + (i + 1) + '列表头应为"' + h + '",实际为"' + headers[i] + '"');
|
||||
}
|
||||
});
|
||||
return errors;
|
||||
},
|
||||
|
||||
/** 匹配钢卷 */
|
||||
async handleMatch() {
|
||||
if (this.parsedData.length === 0) return;
|
||||
|
||||
this.matchLoading = true;
|
||||
this.step = 'MATCHING';
|
||||
this.matchedRows = [];
|
||||
this.conflictRows = [];
|
||||
this.unmatchedRows = [];
|
||||
|
||||
try {
|
||||
// 逐行匹配
|
||||
for (const row of this.parsedData) {
|
||||
await this.matchOneRow(row);
|
||||
}
|
||||
this.step = 'REVIEW';
|
||||
// 自动切换到第一个有数据的tab
|
||||
if (this.matchedRows.length > 0) {
|
||||
this.activeTab = 'matched';
|
||||
} else if (this.conflictRows.length > 0) {
|
||||
this.activeTab = 'conflict';
|
||||
} else if (this.unmatchedRows.length > 0) {
|
||||
this.activeTab = 'unmatched';
|
||||
}
|
||||
this.$message.success('匹配完成,请查看结果');
|
||||
} catch (error) {
|
||||
this.$message.error('匹配过程出错:' + error.message);
|
||||
this.step = 'PARSED';
|
||||
} finally {
|
||||
this.matchLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/** 匹配单行 */
|
||||
async matchOneRow(row) {
|
||||
try {
|
||||
const params = { dataType: 1, status: 0, pageNum: 1, pageSize: 50 };
|
||||
if (row.enterCoilNo) params.enterCoilNo = row.enterCoilNo;
|
||||
if (row.currentCoilNo) params.currentCoilNo = row.currentCoilNo;
|
||||
|
||||
const res = await listMaterialCoil(params);
|
||||
const list = res.rows || [];
|
||||
|
||||
if (list.length === 1) {
|
||||
// 场景1:精确匹配1条
|
||||
this.matchedRows.push({ ...row, matchedCoil: list[0] });
|
||||
} else if (list.length > 1) {
|
||||
// 场景2:匹配到多条,需用户选择
|
||||
this.conflictRows.push({ ...row, candidates: list, selectedCoilId: null });
|
||||
} else {
|
||||
// 场景3:未匹配
|
||||
this.unmatchedRows.push({ ...row, reason: '未找到匹配的钢卷' });
|
||||
}
|
||||
} catch (error) {
|
||||
this.unmatchedRows.push({ ...row, reason: '查询失败:' + error.message });
|
||||
}
|
||||
},
|
||||
|
||||
/** 冲突行选择钢卷 */
|
||||
handleConflictSelect(row) {
|
||||
const selected = row.candidates.find(c => c.coilId === row.selectedCoilId);
|
||||
if (selected) {
|
||||
this.$set(row, 'selectedLabel', selected.currentCoilNo);
|
||||
}
|
||||
},
|
||||
|
||||
/** 移除已匹配行 */
|
||||
removeMatchedRow(index) {
|
||||
this.matchedRows.splice(index, 1);
|
||||
},
|
||||
|
||||
/** 确认导入 */
|
||||
async handleImport() {
|
||||
const coilIds = this.pendingCoilIds;
|
||||
if (coilIds.length === 0) {
|
||||
this.$message.warning('没有可导入的钢卷');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查冲突行是否都已选择
|
||||
const unresolvedConflicts = this.conflictRows.filter(r => !r.selectedCoilId);
|
||||
if (unresolvedConflicts.length > 0) {
|
||||
this.$message.warning('还有 ' + unresolvedConflicts.length + ' 条冲突未选择,未选择的将被跳过');
|
||||
}
|
||||
|
||||
this.step = 'IMPORTING';
|
||||
this.importLoading = true;
|
||||
this.importProgress = 0;
|
||||
this.importedCount = 0;
|
||||
|
||||
try {
|
||||
await batchAddTransferOrderItem({
|
||||
transferId: this.transferId,
|
||||
coilIds: coilIds
|
||||
});
|
||||
|
||||
this.importedCount = coilIds.length;
|
||||
this.importProgress = 100;
|
||||
this.step = 'FINISHED';
|
||||
this.$message.success('成功导入 ' + coilIds.length + ' 条钢卷');
|
||||
} catch (error) {
|
||||
this.$message.error('导入失败:' + error.message);
|
||||
this.step = 'REVIEW';
|
||||
} finally {
|
||||
this.importLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/** 完成导入 */
|
||||
handleFinish() {
|
||||
this.dialogVisible = false;
|
||||
this.$emit('success', this.importedCount);
|
||||
this.resetData();
|
||||
},
|
||||
|
||||
/** 弹窗关闭 */
|
||||
handleDialogClose() {
|
||||
if (this.step === 'IMPORTING') {
|
||||
this.$message.warning('导入进行中,请勿关闭');
|
||||
this.dialogVisible = true;
|
||||
return;
|
||||
}
|
||||
this.resetData();
|
||||
},
|
||||
|
||||
/** 重置数据 */
|
||||
resetData() {
|
||||
this.step = 'IDLE';
|
||||
this.fileName = '';
|
||||
this.parsedData = [];
|
||||
this.matchedRows = [];
|
||||
this.conflictRows = [];
|
||||
this.unmatchedRows = [];
|
||||
this.importProgress = 0;
|
||||
this.importedCount = 0;
|
||||
this.activeTab = 'matched';
|
||||
this.$refs.upload && this.$refs.upload.clearFiles();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.import-coil-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.summary-cards {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
background: #f5f7fa;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.stat-card.success {
|
||||
border-left-color: #67c23a;
|
||||
background: #f0f9eb;
|
||||
}
|
||||
|
||||
.stat-card.warning {
|
||||
border-left-color: #e6a23c;
|
||||
background: #fdf6ec;
|
||||
}
|
||||
|
||||
.stat-card.danger {
|
||||
border-left-color: #f56c6c;
|
||||
background: #fef0f0;
|
||||
}
|
||||
|
||||
.stat-card.primary {
|
||||
border-left-color: #409eff;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.stat-card.success .stat-icon {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.stat-card.warning .stat-icon {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.stat-card.danger .stat-icon {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.stat-card.primary .stat-icon {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 预览区域 */
|
||||
.preview-section {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 标签页区域 */
|
||||
.tabs-section {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.empty-tip p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 冲突列表 */
|
||||
.conflict-list {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* 进度区域 */
|
||||
.progress-section {
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
margin-top: 15px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 完成区域 */
|
||||
.finished-section {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
font-size: 80px;
|
||||
color: #67c23a;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.finished-section h3 {
|
||||
font-size: 20px;
|
||||
color: #303133;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.finished-section p {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 底部按钮 */
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 弹窗自定义样式 */
|
||||
.import-coil-dialog >>> .el-dialog__body {
|
||||
max-height: calc(90vh - 180px);
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.import-coil-dialog >>> .el-dialog__header {
|
||||
padding: 20px 20px 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.import-coil-dialog >>> .el-dialog__footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
</style>
|
||||
@@ -87,4 +87,14 @@ public class WmsReceivableVo {
|
||||
|
||||
// 客户编号
|
||||
private String customerCode;
|
||||
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
private String orderCode;
|
||||
|
||||
/**
|
||||
* 订单名称
|
||||
*/
|
||||
private String orderName;
|
||||
}
|
||||
|
||||
@@ -37,9 +37,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
r.update_time,
|
||||
r.update_by,
|
||||
c.company_name as customerName,
|
||||
c.customer_code as customerCode
|
||||
c.customer_code as customerCode,
|
||||
o.order_code as orderCode,
|
||||
o.contract_name as orderName
|
||||
from wms_receivable r
|
||||
left join crm_customer c on r.customer_id = c.customer_id and c.del_flag = 0
|
||||
left join crm_order o on r.order_id = o.order_id and o.del_flag = 0
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user