🎈 perf: 处理一些细节问题

This commit is contained in:
砂糖
2025-08-27 16:25:52 +08:00
parent cd822d4296
commit 278b0c8258
12 changed files with 484 additions and 271 deletions

View File

@@ -363,3 +363,7 @@ div.pagination-container {
height: var(--btn-height); height: var(--btn-height);
line-height: var(--btn-height); line-height: var(--btn-height);
} }
.el-tabs__content {
overflow: visible !important;
}

View File

@@ -0,0 +1,278 @@
<template>
<div class="klp-list-container">
<!-- 列表加载状态 -->
<div v-loading="loading" class="list-loading-wrapper">
<!-- 列表项渲染 -->
<div
v-for="(item, index) in listData"
:key="item[listKey] || index"
class="klp-list-item"
:class="{ 'active': isSelected(item) }"
@click.stop="handleItemClick(item)"
>
<!-- 列表标题区域 - 文字溢出省略+Tooltip -->
<div class="klp-list-title">
<span class="title-label">{{ titleLabel }}</span>
<el-tooltip
:content="item[titleField]"
placement="top"
:disabled="!isContentOverflow(item)"
effect="light"
>
<span class="title-value">{{ item[titleField] }}</span>
</el-tooltip>
</div>
<!-- 右侧操作按钮组 -->
<div class="klp-list-actions">
<slot name="actions" :item="item" :isSelected="isSelected(item)"></slot>
</div>
</div>
<!-- 空状态提示 -->
<div v-if="listData.length === 0 && !loading" class="klp-list-empty">
<el-empty description="暂无数据" />
</div>
</div>
</div>
</template>
<script>
export default {
name: "klp-list",
components: {},
props: {
/** 列表数据源(必传) */
listData: {
type: Array,
required: true,
default: () => []
},
/** 列表项唯一标识字段必传如orderId、id */
listKey: {
type: String,
required: true
},
/** 列表标题前置标签(如"订单编号:" */
titleLabel: {
type: String,
required: true,
default: "标题"
},
/** 列表项标题对应的字段名如orderCode、name */
titleField: {
type: String,
required: true,
default: "title"
},
/** 列表加载状态 */
loading: {
type: Boolean,
required: false,
default: false
},
/** 标题最大宽度(像素),控制文字溢出 */
titleMaxWidth: {
type: Number,
required: false,
default: 200
}
},
data() {
return {
// 内部管理选中状态:存储当前选中项的唯一键值(单选中)
selectedKey: null,
// 文字溢出检测临时元素(避免重复创建)
overflowCheckElements: {}
};
},
methods: {
/**
* 判断当前列表项是否被选中
* @param {Object} item - 列表项数据
* @returns {Boolean} 选中状态
*/
isSelected(item) {
const itemKey = item[this.listKey];
return this.selectedKey === itemKey;
},
/**
* 列表项点击事件:切换选中状态(单选中逻辑)
* @param {Object} item - 点击的列表项数据
*/
handleItemClick(item) {
const itemKey = item[this.listKey];
// 单选中逻辑:点击已选中项取消选中,点击未选中项切换选中(取消其他项)
this.selectedKey = this.selectedKey === itemKey ? null : itemKey;
// 向父组件触发事件传递当前选中项null表示无选中
const selectedItem = this.selectedKey ? item : null;
this.$emit("item-click", selectedItem, item);
},
/**
* 清空选中状态支持父组件通过ref调用
*/
clearSelection() {
this.selectedKey = null;
// 触发清空选中事件
this.$emit("selection-cleared");
},
/**
* 主动设置选中项支持父组件通过ref调用
* @param {String/Number} targetKey - 要选中项的唯一键值
*/
setSelection(targetKey) {
const isExist = this.listData.some(item => item[this.listKey] === targetKey);
if (isExist) {
this.selectedKey = targetKey;
// 触发选中事件
const selectedItem = this.listData.find(item => item[this.listKey] === targetKey);
this.$emit("item-click", selectedItem);
} else {
this.selectedKey = null;
console.warn(`列表中不存在key为${targetKey}的项`);
}
},
/**
* 判断文字是否溢出控制Tooltip显示/隐藏)
* @param {Object} item - 列表项数据
* @returns {Boolean} 是否溢出
*/
isContentOverflow(item) {
const itemKey = item[this.listKey];
const content = item[this.titleField] || "";
// 内容为空时不显示Tooltip
if (!content) return false;
// 创建临时元素测量文字实际宽度(复用元素避免性能问题)
if (!this.overflowCheckElements[itemKey]) {
const tempEl = document.createElement("span");
// 复制title-value的样式确保测量准确
tempEl.style.cssText = `
visibility: hidden;
position: absolute;
white-space: nowrap;
font-size: 14px;
font-weight: 500;
color: #303133;
`;
tempEl.textContent = content;
document.body.appendChild(tempEl);
this.overflowCheckElements[itemKey] = tempEl;
}
// 比较文字实际宽度与设定的最大宽度
const tempEl = this.overflowCheckElements[itemKey];
return tempEl.offsetWidth > this.titleMaxWidth;
}
},
watch: {
/** 列表数据变化时,重置选中状态和溢出检测元素 */
listData: {
deep: true,
handler() {
this.clearSelection();
// 清理临时测量元素
Object.values(this.overflowCheckElements).forEach(el => {
document.body.removeChild(el);
});
this.overflowCheckElements = {};
}
},
/** 标题最大宽度变化时,强制重绘以重新计算溢出 */
titleMaxWidth() {
this.$forceUpdate();
}
},
/** 组件销毁时清理临时元素,避免内存泄漏 */
beforeDestroy() {
Object.values(this.overflowCheckElements).forEach(el => {
document.body.removeChild(el);
});
}
};
</script>
<style scoped>
/* 列表容器基础样式 */
.klp-list-container {
max-height: calc(100vh - 220px);
overflow-y: auto;
padding-right: 8px;
margin-top: 10px;
}
/* 加载状态容器(避免加载时容器塌陷) */
.list-loading-wrapper {
min-height: 100px;
}
/* 列表项样式 */
.klp-list-item {
padding: 6px 8px;
margin-bottom: 6px;
border-bottom: 1px solid #e6e6e6;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
}
/* 列表项选中状态(左侧高亮边框+背景色) */
.klp-list-item.active {
border-left: 3px solid #409eff;
background-color: #f5f7fa;
}
/* 列表标题区域(占满中间空间,让操作按钮靠右) */
.klp-list-title {
display: flex;
align-items: center;
flex: 1;
}
/* 标题前置标签样式 */
.klp-list-title .title-label {
color: #606266;
margin-right: 6px;
font-size: 13px;
white-space: nowrap; /* 标签不换行 */
}
/* 标题内容样式(溢出省略) */
.klp-list-title .title-value {
color: #303133;
font-weight: 500;
font-size: 14px;
white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
max-width: v-bind(titleMaxWidth + "px"); /* 绑定父组件传入的最大宽度 */
}
/* 操作按钮组(按钮间距控制) */
.klp-list-actions {
display: flex;
align-items: center;
gap: 4px;
}
/* 空状态样式(居中显示) */
.klp-list-empty {
padding: 40px 0;
text-align: center;
}
</style>

View File

@@ -4,10 +4,36 @@
<el-col :span="6"> <el-col :span="6">
<div> <div>
<el-row> <el-row>
<el-input v-model="queryParams.orderCode" placeholder="请输入单据编号" clearable <el-input
@keyup.enter.native="handleQuery" /> v-model="queryParams.orderCode"
placeholder="请输入单据编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-row> </el-row>
<el-tree v-loading="orderListLoading" :data="treeData" :props="defaultProps" @node-click="handleNodeClick" />
<!-- 使用klp-list组件替换el-tree -->
<klp-list
v-loading="orderListLoading"
:list-data="orderList"
list-key="orderId"
title-label="订单编号"
title-field="orderCode"
:title-max-width="180"
@item-click="handleItemClick"
>
<!-- 操作按钮插槽 -->
<!-- <template #actions="{ item, isSelected }">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click.stop="handleViewDetail(item)"
title="查看详情"
></el-button>
</template> -->
</klp-list>
<pagination <pagination
:page.sync="queryParams.pageNum" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" :limit.sync="queryParams.pageSize"
@@ -47,14 +73,15 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="应收明细" name="receivable"> <el-tab-pane label="应收明细" name="receivable">
<el-table v-loading="rightLoading" :data="currentOrder.receivables" empty-text="暂无数据"> <el-table v-loading="rightLoading" :data="currentOrder.receivables" empty-text="暂无数据">
<el-table-column label="应收ID" align="center" prop="receivableId" v-if="false" />
<el-table-column label="客户" align="center" prop="customerName" /> <el-table-column label="客户" align="center" prop="customerName" />
<el-table-column label="订单ID" align="center" prop="orderId" /> <el-table-column label="订单ID" align="center" prop="orderId" />
<el-table-column label="到期日" align="center" prop="dueDate" width="180"> <el-table-column label="到期日" align="center" prop="dueDate" width="180">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.dueDate, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.dueDate, '{y}-{m}-{d}') }}</span>
<el-tag v-if="new Date(scope.row.dueDate) < new Date()" type="danger">过期</el-tag> <el-tag v-if="new Date(scope.row.dueDate) < new Date()" type="danger">过期</el-tag>
<el-tag v-else-if="new Date(scope.row.dueDate) > new Date()" type="success">还剩{{ parseInt((new Date(scope.row.dueDate) - new Date()) / (1000 * 60 * 60 * 24)) }}</el-tag> <el-tag v-else-if="new Date(scope.row.dueDate) > new Date()" type="success">
还剩{{ parseInt((new Date(scope.row.dueDate) - new Date()) / (1000 * 60 * 60 * 24)) }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="应收金额" align="center" prop="amount" /> <el-table-column label="应收金额" align="center" prop="amount" />
@@ -66,14 +93,15 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="应付明细" name="payable"> <el-tab-pane label="应付明细" name="payable">
<el-table v-loading="rightLoading" :data="currentOrder.payables" empty-text="暂无数据"> <el-table v-loading="rightLoading" :data="currentOrder.payables" empty-text="暂无数据">
<el-table-column label="应付ID" align="center" prop="payableId" v-if="false" />
<el-table-column label="供应商" align="center" prop="supplierName" /> <el-table-column label="供应商" align="center" prop="supplierName" />
<el-table-column label="订单ID" align="center" prop="orderId" /> <el-table-column label="订单ID" align="center" prop="orderId" />
<el-table-column label="到期日" align="center" prop="dueDate" width="180"> <el-table-column label="到期日" align="center" prop="dueDate" width="180">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.dueDate, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.dueDate, '{y}-{m}-{d}') }}</span>
<el-tag v-if="new Date(scope.row.dueDate) < new Date()" type="danger">过期</el-tag> <el-tag v-if="new Date(scope.row.dueDate) < new Date()" type="danger">过期</el-tag>
<el-tag v-else-if="new Date(scope.row.dueDate) > new Date()" type="success">还剩{{ parseInt((new Date(scope.row.dueDate) - new Date()) / (1000 * 60 * 60 * 24)) }}</el-tag> <el-tag v-else-if="new Date(scope.row.dueDate) > new Date()" type="success">
还剩{{ parseInt((new Date(scope.row.dueDate) - new Date()) / (1000 * 60 * 60 * 24)) }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="应付金额" align="center" prop="amount" /> <el-table-column label="应付金额" align="center" prop="amount" />
@@ -97,7 +125,6 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
@@ -108,11 +135,13 @@ import { listPayable } from "@/api/finance/payable";
import { listFinancialDocument } from "@/api/finance/financialDocument"; import { listFinancialDocument } from "@/api/finance/financialDocument";
import OrderDetailList from '@/views/wms/order/panels/detail.vue' import OrderDetailList from '@/views/wms/order/panels/detail.vue'
import klpList from "@/components/KLPUI/KLPList/index.vue"; // 引入klp-list组件
export default { export default {
name: "Order", name: "Order",
components: { components: {
OrderDetailList OrderDetailList,
klpList // 注册klp-list组件
}, },
data() { data() {
return { return {
@@ -124,12 +153,8 @@ export default {
orderCode: undefined orderCode: undefined
}, },
orderListLoading: false, orderListLoading: false,
treeData: [], orderList: [], // 用于klp-list的列表数据
total: 0, total: 0,
defaultProps: {
children: 'children',
label: 'label'
},
rightLoading: false, rightLoading: false,
} }
}, },
@@ -144,37 +169,60 @@ export default {
getList() { getList() {
this.orderListLoading = true; this.orderListLoading = true;
listOrder(this.queryParams).then(response => { listOrder(this.queryParams).then(response => {
this.treeData = response.rows.map(item => ({ this.orderList = response.rows; // 直接使用原始数据
label: item.orderCode,
value: item
}));
this.total = response.total; this.total = response.total;
}).finally(() => { }).finally(() => {
this.orderListLoading = false; this.orderListLoading = false;
}); });
}, },
handleNodeClick(data) { // 处理列表项点击事件
handleItemClick(selectedItem) {
if (!selectedItem) {
this.currentOrder = null;
return;
}
if (this.rightLoading) { if (this.rightLoading) {
this.$message.warning('请等待当前订单加载完成'); this.$message.warning('请等待当前订单加载完成');
return; return;
} }
this.currentOrder = data.value;
this.fetchData(data.value.orderId); this.currentOrder = selectedItem;
this.fetchData(selectedItem.orderId);
},
// 查看详情按钮点击事件
handleViewDetail(item) {
this.currentOrder = item;
this.fetchData(item.orderId);
}, },
async fetchData(orderId) { async fetchData(orderId) {
this.rightLoading = true; this.rightLoading = true;
// 需要逐个获取订单明细、应收明细、应付明细、凭证管理 try {
const orderDetail = (await listOrderDetail({ orderId, pageSize: 1000 })).rows; // 并行请求提高性能
const receivableDetail = (await listReceivable({ orderId, pageSize: 1000 })).rows; const [
const payableDetail = (await listPayable({ orderId, pageSize: 1000 })).rows; orderDetailRes,
const documentDetail = (await listFinancialDocument({ orderId, pageSize: 1000 })).rows; receivableRes,
this.rightLoading = false; payableRes,
documentRes
] = await Promise.all([
listOrderDetail({ orderId, pageSize: 1000 }),
listReceivable({ orderId, pageSize: 1000 }),
listPayable({ orderId, pageSize: 1000 }),
listFinancialDocument({ orderId, pageSize: 1000 })
]);
this.currentOrder = { this.currentOrder = {
...this.currentOrder, ...this.currentOrder,
details: orderDetail, details: orderDetailRes.rows,
receivables: receivableDetail, receivables: receivableRes.rows,
payables: payableDetail, payables: payableRes.rows,
documents: documentDetail documents: documentRes.rows
};
} catch (error) {
this.$message.error('数据加载失败,请重试');
console.error(error);
} finally {
this.rightLoading = false;
} }
} }
} }

View File

@@ -39,14 +39,22 @@
@click="handleExport" @click="handleExport"
>导出</el-button> >导出</el-button>
</el-col> </el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table v-loading="loading" :data="bomItemList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="bomItemList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="属性名称" align="center" prop="attrKey" /> <el-table-column label="属性名称" align="center" prop="attrKey">
<template slot-scope="scope">
<span>{{ scope.row.attrKey }}</span>
<span v-if="scope.row.remark">
<el-tooltip :content="scope.row.remark" placement="top">
<i class="el-icon-info"></i>
</el-tooltip>
</span>
</template>
</el-table-column>
<el-table-column label="属性值" align="center" prop="attrValue" /> <el-table-column label="属性值" align="center" prop="attrValue" />
<el-table-column label="备注" align="center" prop="remark" /> <!-- <el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button

View File

@@ -65,8 +65,8 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" <!-- <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" /> @pagination="getList" /> -->
<!-- 添加或修改订单明细对话框 --> <!-- 添加或修改订单明细对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
@@ -106,7 +106,7 @@
<el-select placeholder="请选择产品规范" @change="handleChangeSpec" style="width: 100%;"> <el-select placeholder="请选择产品规范" @change="handleChangeSpec" style="width: 100%;">
<el-option v-for="item in productSpecList" :key="item.groupId" :label="item.groupName" :value="item.groupId" /> <el-option v-for="item in productSpecList" :key="item.groupId" :label="item.groupName" :value="item.groupId" />
<template #empty> <template #empty>
<el-button type="primary" @click="handleAddSpec">新增产品规范</el-button> <el-button @click="handleAddSpec" style="width: 100%;">新增产品规范</el-button>
</template> </template>
</el-select> </el-select>
</div> </div>
@@ -158,7 +158,7 @@ export default {
EOrderStatus, EOrderStatus,
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 100,
orderId: this.orderId, orderId: this.orderId,
productId: undefined, productId: undefined,
quantity: undefined, quantity: undefined,

View File

@@ -2,7 +2,7 @@
<div class="app-container"> <div class="app-container">
<!-- 左右布局容器 --> <!-- 左右布局容器 -->
<el-row :gutter="20"> <el-row :gutter="20">
<!-- 左侧自定义简洁订单列表区8 --> <!-- 左侧使用klp-list组件6 -->
<el-col :span="6" style="display: table-cell;"> <el-col :span="6" style="display: table-cell;">
<!-- 搜索表单 - 精简搜索项 --> <!-- 搜索表单 - 精简搜索项 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px" <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"
@@ -19,54 +19,33 @@
</el-row> </el-row>
</el-form> </el-form>
<!-- 功能按钮区 --> <!-- klp-list组件 -->
<!-- <el-row :gutter="10" class="mb-4"> <klp-list
<el-col :span="6"> :list-data="orderList"
:model-value="selectedIds"
</el-col> title-field="orderCode"
<el-col :span="6"> list-key="orderId"
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" title-label="订单编号"
class="w-full">批量删除</el-button> :loading="loading"
</el-col> @item-click="handleRowClick"
</el-row> --> >
<!-- 自定义操作按钮 -->
<!-- 自定义简洁列表 --> <template #actions="{ item }">
<div class="custom-list-container" v-loading="loading">
<!-- 列表项 -->
<div v-for="(item, index) in orderList" :key="item.orderId" class="order-item"
:class="{ 'active': selectedOrderId === item.orderId }" @click="handleRowClick(item)">
<!-- 复选框 -->
<!-- <el-checkbox v-model="selectedIds[item.orderId]" @change.stop="handleCheckboxChange(item, $event)"
class="checkbox"></el-checkbox> -->
<!-- 订单编号 -->
<div class="order-code">
<span class="label">订单编号</span>
<span class="value">{{ item.orderCode }}</span>
</div>
<!-- 操作按钮组 -->
<div class="order-actions">
<!-- 预订单确认按钮 --> <!-- 预订单确认按钮 -->
<el-button size="mini" plain title="预订单确认" type="success" icon="el-icon-check" v-if="isPre" <el-button size="mini" plain title="预订单确认" type="success" icon="el-icon-check" v-if="isPre"
@click.stop="handleStartProduction(item)"></el-button> @click.stop="handleStartProduction(item)"></el-button>
<!-- 删除按钮 --> <!-- 删除按钮 -->
<el-button size="mini" type="text" style="color: red" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button> <el-button size="mini" type="text" style="color: red" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button>
</div> </template>
</div> </klp-list>
<!-- 空状态 -->
<div v-if="orderList.length === 0 && !loading" class="empty-state">
<el-empty description="暂无订单数据" />
</div>
</div>
<!-- 分页组件 --> <!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" class="mt-4" /> :limit.sync="queryParams.pageSize" @pagination="getList" class="mt-4" />
</el-col> </el-col>
<!-- 右侧详情区占16 --> <!-- 右侧详情区占18 -->
<el-col :span="18"> <el-col :span="18">
<div> <div>
<!-- 未选中行时显示提示 --> <!-- 未选中行时显示提示 -->
@@ -81,56 +60,45 @@
<!-- 订单信息Tab --> <!-- 订单信息Tab -->
<el-tab-pane label="订单信息" name="orderInfo"> <el-tab-pane label="订单信息" name="orderInfo">
<el-form ref="detailForm" :model="form" :rules="rules" label-width="80px" size="small" class="mt-4"> <el-form ref="detailForm" :model="form" :rules="rules" label-width="80px" size="small" class="mt-4">
<!-- <el-row :gutter="20"> -->
<!-- <el-col :span="12"> -->
<el-form-item label="订单ID" prop="orderId"> <el-form-item label="订单ID" prop="orderId">
<el-input style="width: 60%;" v-model="form.orderId" placeholder="无" disabled> <el-input style="width: 60%;" v-model="form.orderId" placeholder="无" disabled>
<el-button style="padding: -1;" slot="append" size="mini" type="text" icon="el-icon-document-copy" <el-button style="padding: -1;" slot="append" size="mini" type="text" icon="el-icon-document-copy"
@click.stop="copyOrderId(form.orderId)"></el-button> @click.stop="copyOrderId(form.orderId)"></el-button>
</el-input> </el-input>
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<el-form-item label="订单编号" prop="orderCode"> <el-form-item label="订单编号" prop="orderCode">
<el-input style="width: 60%;" v-model="form.orderCode" placeholder="无" disabled /> <el-input style="width: 60%;" v-model="form.orderCode" placeholder="无" disabled />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<el-form-item label="客户名称" prop="customerId"> <el-form-item label="客户名称" prop="customerId">
<customer-select style="width: 60%;" v-model="form.customerId" /> <customer-select style="width: 60%;" v-model="form.customerId" />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<el-form-item label="销售经理" prop="salesManager"> <el-form-item label="销售经理" prop="salesManager">
<el-input style="width: 60%;" v-model="form.salesManager" placeholder="无" /> <el-input style="width: 60%;" v-model="form.salesManager" placeholder="无" />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<el-form-item label="含税金额" prop="taxAmount"> <el-form-item label="含税金额" prop="taxAmount">
<el-input-number style="width: 60%;" :controls="false" v-model="form.taxAmount" placeholder="0.00" precision="2" <el-input-number style="width: 60%;" :controls="false" v-model="form.taxAmount" placeholder="0.00" precision="2"
:min="0" /> :min="0" />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12"> -->
<el-form-item label="无税金额" prop="noTaxAmount"> <el-form-item label="无税金额" prop="noTaxAmount">
<el-input-number style="width: 60%;" :controls="false" v-model="form.noTaxAmount" placeholder="0.00" precision="2" <el-input-number style="width: 60%;" :controls="false" v-model="form.noTaxAmount" placeholder="0.00" precision="2"
:min="0" /> :min="0" />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="12" v-if="!isPre"> --> <el-form-item label="订单状态" prop="orderStatus" v-if="!isPre">
<el-form-item label="订单状态" prop="orderStatus">
<el-select style="width: 60%;" v-model="form.orderStatus" @change="handleOrderStatusChange" size="mini"> <el-select style="width: 60%;" v-model="form.orderStatus" @change="handleOrderStatusChange" size="mini">
<el-option v-for="item in dict.type.order_status" :key="item.value" :label="item.label" <el-option v-for="item in dict.type.order_status" :key="item.value" :label="item.label"
:value="parseInt(item.value)" /> :value="parseInt(item.value)" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- <el-col :span="24"> -->
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input style="width: 60%;" v-model="form.remark" placeholder="无" type="textarea" rows="4" /> <el-input style="width: 60%;" v-model="form.remark" placeholder="无" type="textarea" rows="4" />
</el-form-item> </el-form-item>
<!-- </el-col> -->
<!-- </el-row> -->
<!-- 更新按钮 --> <!-- 更新按钮 -->
<el-form-item class="text-right"> <el-form-item class="text-right">
@@ -203,10 +171,11 @@ import { getOrder, delOrder, addOrder, updateOrder, listByStatus } from "@/api/w
import OrderDetailPanel from './detail.vue'; import OrderDetailPanel from './detail.vue';
import { EOrderStatus } from "@/utils/enums"; import { EOrderStatus } from "@/utils/enums";
import CustomerSelect from '@/components/KLPService/CustomerSelect/index.vue'; import CustomerSelect from '@/components/KLPService/CustomerSelect/index.vue';
import klpList from "@/components/KLPUI/KLPList/index.vue";
export default { export default {
name: "Order", name: "Order",
components: { OrderDetailPanel, CustomerSelect }, components: { OrderDetailPanel, CustomerSelect, klpList },
dicts: ['order_status'], dicts: ['order_status'],
props: { props: {
isPre: { isPre: {
@@ -227,10 +196,8 @@ export default {
buttonLoading: false, buttonLoading: false,
// 列表加载遮罩 // 列表加载遮罩
loading: true, loading: true,
// 选中的订单ID集合(用于批量操作) // 选中的订单ID集合
selectedIds: {}, selectedIds: {},
// 非多个禁用(批量删除用)
multiple: true,
// 显示搜索条件 // 显示搜索条件
showSearch: true, showSearch: true,
// 总条数 // 总条数
@@ -304,7 +271,6 @@ export default {
// 重置选中状态 // 重置选中状态
this.selectedIds = {}; this.selectedIds = {};
this.checkMultipleStatus();
// 如果之前选中的订单不在列表中了,清空选中状态 // 如果之前选中的订单不在列表中了,清空选中状态
if (this.selectedOrderId && !this.orderList.some(item => item.orderId === this.selectedOrderId)) { if (this.selectedOrderId && !this.orderList.some(item => item.orderId === this.selectedOrderId)) {
@@ -337,18 +303,6 @@ export default {
}); });
}, },
/** 处理复选框变化 */
handleCheckboxChange(item, checked) {
this.selectedIds[item.orderId] = checked;
this.checkMultipleStatus();
},
/** 检查批量操作状态 */
checkMultipleStatus() {
const selectedCount = Object.values(this.selectedIds).filter(checked => checked).length;
this.multiple = selectedCount === 0;
},
/** 订单状态变更 */ /** 订单状态变更 */
handleOrderStatusChange() { handleOrderStatusChange() {
updateOrder(this.form).then(response => { updateOrder(this.form).then(response => {
@@ -463,30 +417,13 @@ export default {
/** 删除订单 */ /** 删除订单 */
handleDelete(row) { handleDelete(row) {
// 获取要删除订单ID this.$modal.confirm(`是否确认删除订单"${row.orderCode}"`).then(() => {
let orderIds;
if (row) {
// 单条删除
orderIds = [row.orderId];
} else {
// 批量删除
orderIds = Object.entries(this.selectedIds)
.filter(([id, checked]) => checked)
.map(([id]) => id);
}
if (!orderIds.length) {
this.$modal.msgWarning("请选择要删除的订单");
return;
}
this.$modal.confirm(`是否确认删除选中的 ${orderIds.length} 个订单?`).then(() => {
this.loading = true; this.loading = true;
return delOrder(orderIds); return delOrder([row.orderId]);
}).then(() => { }).then(() => {
this.$modal.msgSuccess("删除成功"); this.$modal.msgSuccess("删除成功");
// 如果删除的是当前选中的订单,清空详情 // 如果删除的是当前选中的订单,清空详情
if (row && this.selectedOrderId === row.orderId) { if (this.selectedOrderId === row.orderId) {
this.selectedOrderId = null; this.selectedOrderId = null;
} }
this.getList(); this.getList();
@@ -525,78 +462,6 @@ export default {
padding: 16px; padding: 16px;
} }
/* 自定义列表容器 */
.custom-list-container {
max-height: calc(100vh - 220px);
overflow-y: auto;
padding-right: 8px;
margin-top: 10px;
}
/* 列表项样式 */
.order-item {
padding: 12px 16px;
margin-bottom: 10px;
border-bottom: 1px solid #e6e6e6;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 选中状态 */
.order-item.active {
border-left: 3px solid #409eff;
background-color: #f5f7fa;
}
/* 订单编号样式 */
.order-code {
display: flex;
align-items: center;
}
.order-code .label {
color: #606266;
margin-right: 6px;
font-size: 13px;
}
.order-code .value {
color: #303133;
font-weight: 500;
font-size: 14px;
}
/* 操作按钮组 */
.order-actions {
display: flex;
align-items: center;
gap: 4px;
}
/* 复选框样式调整 */
.checkbox {
margin-left: 8px;
}
/* 空状态样式 */
.empty-state {
padding: 40px 0;
text-align: center;
}
/* 右侧详情容器 */
.detail-container {
height: 100%;
background: #fff;
border-radius: 4px;
padding: 16px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
min-height: calc(100vh - 32px);
}
/* 空提示样式 */ /* 空提示样式 */
.empty-tip { .empty-tip {
height: 400px; height: 400px;

View File

@@ -1,10 +1,6 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="采购订单编号" prop="detailCode">
<el-input v-model="queryParams.detailCode" placeholder="请输入采购订单编号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="原材料" prop="rawMaterialId"> <el-form-item label="原材料" prop="rawMaterialId">
<RawMaterialSelect v-model="queryParams.rawMaterialId" placeholder="请选择原材料" can-add /> <RawMaterialSelect v-model="queryParams.rawMaterialId" placeholder="请选择原材料" can-add />
</el-form-item> </el-form-item>
@@ -40,10 +36,10 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <!-- <el-col :span="1.5">
<el-button type="info" plain icon="el-icon-plus" size="mini" :disabled="!hasArrivalItems" <el-button type="info" plain icon="el-icon-plus" size="mini" :disabled="!hasArrivalItems"
@click="handleCreateStockIn">创建入库单</el-button> @click="handleCreateStockIn">创建入库单</el-button>
</el-col> </el-col> -->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>

View File

@@ -12,15 +12,15 @@
> >
<!-- 下拉选项循环制造规范列表 --> <!-- 下拉选项循环制造规范列表 -->
<el-option <el-option
v-for="item in mSpecList" v-for="item in pSpecList"
:key="item.specId" :key="item.groupId"
:label="item.specName" :label="item.groupName"
:value="item.specId" :value="item.groupId"
/> />
<!-- 无数据提示 --> <!-- 无数据提示 -->
<el-option <el-option
v-if="mSpecList.length === 0 && !loading" v-if="pSpecList.length === 0 && !loading"
value="" value=""
disabled disabled
> >
@@ -30,7 +30,7 @@
</template> </template>
<script> <script>
import { listManufacturingSpec } from '@/api/work/manufacturingSpec' import { listProductSpecGroup } from '@/api/work/productSpecGroup'
import { debounce } from '@/utils' // 防抖:避免频繁触发接口请求 import { debounce } from '@/utils' // 防抖:避免频繁触发接口请求
export default { export default {
@@ -44,7 +44,7 @@ export default {
// 可选:允许外部自定义搜索参数的字段名(增强组件复用性) // 可选:允许外部自定义搜索参数的字段名(增强组件复用性)
searchKey: { searchKey: {
type: String, type: String,
default: 'specName' // 默认为“规范名称”搜索对应接口的mSpecName参数 default: 'groupName' // 默认为“规范名称”搜索对应接口的mSpecName参数
}, },
// 可选:每页加载数量(外部可配置) // 可选:每页加载数量(外部可配置)
pageSize: { pageSize: {
@@ -54,7 +54,7 @@ export default {
}, },
data() { data() {
return { return {
mSpecList: [], // 制造规范列表数据 pSpecList: [], // 制造规范列表数据
loading: false, // 远程请求加载状态 loading: false, // 远程请求加载状态
queryParams: { queryParams: {
// 搜索参数默认用specName可通过searchKey自定义 // 搜索参数默认用specName可通过searchKey自定义
@@ -109,14 +109,14 @@ export default {
// 5. 获取制造规范列表(远程接口请求) // 5. 获取制造规范列表(远程接口请求)
getList() { getList() {
this.loading = true // 开始加载:显示加载动画 this.loading = true // 开始加载:显示加载动画
listManufacturingSpec(this.queryParams) listProductSpecGroup(this.queryParams)
.then(res => { .then(res => {
// 接口成功赋值列表数据兼容res.rows或res.data.rows避免接口返回格式差异 // 接口成功赋值列表数据兼容res.rows或res.data.rows避免接口返回格式差异
this.mSpecList = res.rows || res.data?.rows || [] this.pSpecList = res.rows || res.data?.rows || []
}) })
.catch(() => { .catch(() => {
// 接口失败:清空列表,避免旧数据残留 // 接口失败:清空列表,避免旧数据残留
this.mSpecList = [] this.pSpecList = []
this.$message.error('获取制造规范列表失败,请重试') // 错误提示(优化用户体验) this.$message.error('获取制造规范列表失败,请重试') // 错误提示(优化用户体验)
}) })
.finally(() => { .finally(() => {
@@ -125,9 +125,9 @@ export default {
}, },
// 6. 选择变化时触发(返回完整订单项,方便父组件使用) // 6. 选择变化时触发(返回完整订单项,方便父组件使用)
handleSelectChange(mSpecId) { handleSelectChange(pSpecId) {
// 根据选中的ID找到对应的完整制造规范对象 // 根据选中的ID找到对应的完整制造规范对象
const selectedItem = this.mSpecList.find(item => item.specId === mSpecId) const selectedItem = this.pSpecList.find(item => item.groupId === pSpecId)
// 触发change事件返回完整项父组件可通过@change接收 // 触发change事件返回完整项父组件可通过@change接收
this.$emit('change', selectedItem) this.$emit('change', selectedItem)
// 可选触发input事件后额外触发change事件符合常规组件使用习惯 // 可选触发input事件后额外触发change事件符合常规组件使用习惯

View File

@@ -53,7 +53,9 @@ export default {
return; return;
} }
// 处理数据,兼容多种字段名,保证任务名唯一 // 处理数据,兼容多种字段名,保证任务名唯一
const taskData = this.tasks.map((item, idx) => {
const taskData = this.tasks
.map((item, idx) => {
const name = (item.taskName || item.planName || item.remark || item.productName || item.name || `任务${idx+1}`) + (item.productName ? `-${item.productName}` : ''); const name = (item.taskName || item.planName || item.remark || item.productName || item.name || `任务${idx+1}`) + (item.productName ? `-${item.productName}` : '');
const start = item.startDate || item.start_time || item.start || item.start_date; const start = item.startDate || item.start_time || item.start || item.start_date;
const end = item.endDate || item.end_time || item.end || item.end_date; const end = item.endDate || item.end_time || item.end || item.end_date;
@@ -72,7 +74,10 @@ export default {
startDate: start, startDate: start,
endDate: end endDate: end
}; };
}); })
// 确保三个字段都存在startDate,endDate,name
.filter(item => item.startDate && item.endDate && item.name);
// 先全部用 getColor 分配基础色 // 先全部用 getColor 分配基础色
taskData.forEach((item, idx) => { taskData.forEach((item, idx) => {
item._color = getColor(item.lineId, item.orderId, idx); item._color = getColor(item.lineId, item.orderId, idx);

View File

@@ -44,11 +44,18 @@
<el-table v-loading="loading" :data="productSpecList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="productSpecList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="specId" v-if="false"/> <el-table-column label="规范名称" align="center" prop="specKey">
<el-table-column label="所属产品规范组ID" align="center" prop="groupId" /> <template slot-scope="scope">
<el-table-column label="规范名称" align="center" prop="specKey" /> <span>{{ scope.row.specKey }}</span>
<span v-if="scope.row.remark">
<el-tooltip :content="scope.row.remark" placement="top">
<i class="el-icon-info"></i>
</el-tooltip>
</span>
</template>
</el-table-column>
<el-table-column label="规范值" align="center" prop="specValue" /> <el-table-column label="规范值" align="center" prop="specValue" />
<el-table-column label="备注" align="center" prop="remark" /> <!-- <el-table-column label="备注" align="center" prop="remark" /> -->
<el-table-column label="操作" v-if="!readonly" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" v-if="!readonly" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button

View File

@@ -135,8 +135,10 @@ export default {
this.$refs.detailForm.validate(valid => { this.$refs.detailForm.validate(valid => {
if (!valid) return; if (!valid) return;
if (this.detailForm.dateRange && this.detailForm.dateRange.length === 2) { if (this.detailForm.dateRange && this.detailForm.dateRange.length === 2) {
this.detailForm.startDate = this.detailForm.dateRange[0].replace(' ', "T") + '.000Z'; // const startDate = this.parseTime(this.detailForm.dateRange[0], '{y}-{m}-{d}T{h}:{i}:{s}') + '.000Z';
this.detailForm.endDate = this.detailForm.dateRange[1].replace(' ', "T") + '.000Z'; // const endDate = this.parseTime(this.detailForm.dateRange[1], '{y}-{m}-{d}T{h}:{i}:{s}') + '.000Z';
this.detailForm.startDate = this.detailForm.dateRange[0];
this.detailForm.endDate = this.detailForm.dateRange[1];
} else { } else {
this.detailForm.startDate = ''; this.detailForm.startDate = '';
this.detailForm.endDate = ''; this.detailForm.endDate = '';

View File

@@ -35,7 +35,7 @@
<el-select placeholder="请选择产品规范" @change="handleChangeSpec" style="width: 100%;"> <el-select placeholder="请选择产品规范" @change="handleChangeSpec" style="width: 100%;">
<el-option v-for="item in productSpecList.filter(item => item.productId != this.form.productId)" :key="item.groupId" :label="item.groupName" :value="item.groupId" /> <el-option v-for="item in productSpecList.filter(item => item.productId != this.form.productId)" :key="item.groupId" :label="item.groupName" :value="item.groupId" />
<template #empty> <template #empty>
<el-button type="primary" @click="handleAddSpec">新增产品规范</el-button> <el-button @click="handleAddSpec" style="width: 100%;">新增产品规范</el-button>
</template> </template>
</el-select> </el-select>
</div> </div>