feat(aps): 新增APS排产管理模块完整功能
本次提交完成APS(高级计划与排程)模块的全量开发: 1. 新增CRM订单相关API接口,包含列表、详情、明细查询 2. 新增产需单相关CRUD API与页面,支持排产单管理与订单绑定 3. 新增按日期查询排产单、订单下钻详情页面 4. 为排产单实体类添加日期格式化注解,修复参数绑定问题 5. 统一封装APS模块主题样式,提供通用混入与变量 6. 实现产需单与销售订单的绑定解绑、明细自动生成功能
This commit is contained in:
316
klp-ui/src/views/wms/post/aps/schedule.vue
Normal file
316
klp-ui/src/views/wms/post/aps/schedule.vue
Normal file
@@ -0,0 +1,316 @@
|
||||
<template>
|
||||
<div class="aps-sch-page">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="aps-sch-toolbar">
|
||||
<span class="aps-sch-label">生产日期:</span>
|
||||
<el-date-picker
|
||||
v-model="queryDate"
|
||||
type="date"
|
||||
placeholder="选择生产日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
size="small"
|
||||
style="width:160px"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
<el-button size="small" class="aps-btn-red" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||
<div class="aps-sch-summary" v-if="summaryText">
|
||||
<span>{{ summaryText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 排产明细卡片 -->
|
||||
<div class="detail-card aps-sch-card">
|
||||
<div class="detail-card-header">
|
||||
<span>排产明细</span>
|
||||
<span v-if="detailList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||
共 {{ scheduleList.length }} 个产需单,{{ detailList.length }} 条明细
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-card-body" style="padding:0;" v-loading="schLoading">
|
||||
<el-table
|
||||
v-if="detailList.length > 0"
|
||||
:data="detailList"
|
||||
border
|
||||
size="small"
|
||||
class="aps-table"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" fixed="left">
|
||||
<template slot-scope="{ row }">
|
||||
<span class="sch-link-text">{{ row.scheduleNo }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" prop="spec" min-width="120" />
|
||||
<el-table-column label="材质" prop="material" width="90" align="center" />
|
||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" />
|
||||
<el-table-column label="品名" prop="productType" min-width="100" />
|
||||
<el-table-column label="订货单位" prop="customerName" min-width="140" />
|
||||
<el-table-column label="业务员" prop="businessUser" width="80" align="center" />
|
||||
<el-table-column label="交货期(天)" prop="deliveryCycle" width="90" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="140" />
|
||||
</el-table>
|
||||
<div v-else-if="!schLoading" style="padding:40px;text-align:center;color:#909399;">
|
||||
{{ hasQueried ? '该日期暂无排产数据' : '请选择日期查询排产数据' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下钻弹窗 -->
|
||||
<el-dialog
|
||||
title="来源订单信息"
|
||||
:visible.sync="drillDialogVisible"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
<div v-if="drillOrder" class="detail-card" style="border:none;box-shadow:none;">
|
||||
<div class="detail-card-body">
|
||||
<div class="form-grid-2">
|
||||
<div class="form-field"><label>订单编号</label><div class="field-value">{{ drillOrder.orderCode }}</div></div>
|
||||
<div class="form-field"><label>销售员</label><div class="field-value">{{ drillOrder.salesman }}</div></div>
|
||||
<div class="form-field"><label>客户公司</label><div class="field-value">{{ drillOrder.companyName }}</div></div>
|
||||
<div class="form-field"><label>联系人</label><div class="field-value">{{ drillOrder.contactPerson }}</div></div>
|
||||
<div class="form-field"><label>联系电话</label><div class="field-value">{{ drillOrder.contactWay }}</div></div>
|
||||
<div class="form-field"><label>交货日期</label><div class="field-value">{{ drillOrder.deliveryDate }}</div></div>
|
||||
<div class="form-field"><label>合同号</label><div class="field-value">{{ drillOrder.contractCode }}</div></div>
|
||||
<div class="form-field" style="grid-column:1/3;"><label>备注</label><div class="field-value">{{ drillOrder.remark }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-empty description="未找到订单信息" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listRequirement } from '@/api/aps/requirement'
|
||||
import { getCrmOrderInfo } from '@/api/aps/schedule'
|
||||
|
||||
export default {
|
||||
name: 'ApsSchedule',
|
||||
data() {
|
||||
const today = new Date()
|
||||
const y = today.getFullYear()
|
||||
const m = String(today.getMonth() + 1).padStart(2, '0')
|
||||
const d = String(today.getDate()).padStart(2, '0')
|
||||
return {
|
||||
queryDate: `${y}-${m}-${d}`,
|
||||
schLoading: false,
|
||||
hasQueried: false,
|
||||
scheduleList: [],
|
||||
detailList: [],
|
||||
summaryText: '',
|
||||
drillDialogVisible: false,
|
||||
drillOrder: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.handleQuery()
|
||||
},
|
||||
methods: {
|
||||
handleDateChange() {
|
||||
this.handleQuery()
|
||||
},
|
||||
handleQuery() {
|
||||
if (!this.queryDate) {
|
||||
this.$message.warning('请选择生产日期')
|
||||
return
|
||||
}
|
||||
this.schLoading = true
|
||||
this.hasQueried = true
|
||||
this.detailList = []
|
||||
this.scheduleList = []
|
||||
|
||||
// 后端 list 接口已通过 fillDetailList 填充 detailList
|
||||
listRequirement({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => {
|
||||
const list = res.rows || []
|
||||
this.scheduleList = list
|
||||
|
||||
// 扁平化所有 detailList
|
||||
const merged = []
|
||||
list.forEach(sch => {
|
||||
const details = sch.detailList || []
|
||||
details.forEach(d => {
|
||||
merged.push({
|
||||
...d,
|
||||
scheduleNo: sch.scheduleNo,
|
||||
customerName: sch.customerName,
|
||||
businessUser: sch.businessUser,
|
||||
deliveryCycle: sch.deliveryCycle,
|
||||
_scheduleId: sch.scheduleId
|
||||
})
|
||||
})
|
||||
})
|
||||
this.detailList = merged
|
||||
|
||||
const totalWeight = merged.reduce((sum, d) => sum + (parseFloat(d.scheduleWeight) || 0), 0)
|
||||
this.summaryText = `共 ${list.length} 个产需单,${merged.length} 条明细,排产总吨数 ${totalWeight.toFixed(3)} 吨`
|
||||
}).catch(() => {
|
||||
this.detailList = []
|
||||
this.summaryText = ''
|
||||
}).finally(() => {
|
||||
this.schLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleRowClick(row) {
|
||||
// 通过 _scheduleId 找到产需单,然后找到关联的订单
|
||||
const sch = this.scheduleList.find(s => s.scheduleId === row._scheduleId)
|
||||
if (!sch || !sch.orderList || sch.orderList.length === 0) {
|
||||
this.$message.warning('未找到关联订单')
|
||||
return
|
||||
}
|
||||
// 取第一个关联订单展示
|
||||
const order = sch.orderList[0]
|
||||
getCrmOrderInfo(order.orderId).then(res => {
|
||||
this.drillOrder = res.data
|
||||
this.drillDialogVisible = true
|
||||
}).catch(() => {
|
||||
this.$message.warning('未找到来源订单')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './scss/aps-theme.scss';
|
||||
|
||||
.aps-sch-page {
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
background: $aps-bg;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
// 工具栏
|
||||
.aps-sch-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
background: $aps-white;
|
||||
border: 1px solid $aps-border;
|
||||
border-radius: $aps-radius;
|
||||
box-shadow: $aps-shadow-sm;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aps-sch-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: $aps-text;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.aps-sch-summary {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
color: $aps-text-muted;
|
||||
background: $aps-silver-1;
|
||||
padding: 4px 12px;
|
||||
border-radius: $aps-radius;
|
||||
border: 1px solid $aps-border;
|
||||
}
|
||||
|
||||
// 排产卡片
|
||||
.aps-sch-card {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.detail-card-body {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 表格
|
||||
.aps-table {
|
||||
width: 100%;
|
||||
|
||||
::v-deep th {
|
||||
background: $aps-silver-1 !important;
|
||||
color: $aps-text !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table__body tr:hover > td {
|
||||
background-color: $aps-red-1 !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::v-deep td {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.sch-link-text {
|
||||
color: $aps-red-2;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: $aps-red-3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// 复用卡片/网格变量
|
||||
.aps-btn-red {
|
||||
@include aps-btn-red;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
background: $aps-white;
|
||||
border: 1px solid $aps-border;
|
||||
border-radius: $aps-radius;
|
||||
box-shadow: $aps-shadow-sm;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-card-header {
|
||||
background: linear-gradient(to right, $aps-red-2, $aps-red-3);
|
||||
color: white;
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.detail-card-body {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.form-grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10px 16px;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.form-field label {
|
||||
font-size: 11px;
|
||||
color: $aps-text-muted;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-field .field-value {
|
||||
font-size: 13px;
|
||||
color: $aps-text;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid $aps-silver-mid;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user