整合前端
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="mini" style="margin-bottom: 10px" :disabled="selected.length === 0"
|
||||
@click="handleExport">导出选中</el-button> -->
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" width="60" prop="sequence" />
|
||||
<el-table-column label="工序名称" prop="processName" />
|
||||
<el-table-column label="工序详情" prop="processDetail" show-overflow-tooltip />
|
||||
<el-table-column label="作业责任人" prop="workResponsiblePerson" />
|
||||
<el-table-column label="工序开始时间" prop="processStartTime" />
|
||||
<el-table-column label="工序结束时间" prop="processEndTime" />
|
||||
<el-table-column label="质检员" prop="inspector" />
|
||||
<el-table-column label="不合格内容" prop="unqualifiedContent" />
|
||||
<el-table-column label="备注" prop="remark" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DetailTable',
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 999
|
||||
},
|
||||
selected: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange (val) {
|
||||
this.selected = val;
|
||||
},
|
||||
handleExport () {
|
||||
this.$emit('export-detail', this.selected);
|
||||
},
|
||||
handlePagination (val) {
|
||||
this.$emit('pagination', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
304
ruoyi-ui/src/views/oa/workshop/craft/components/MasterTable.vue
Normal file
304
ruoyi-ui/src/views/oa/workshop/craft/components/MasterTable.vue
Normal file
@@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="设备名称" align="center" prop="equipmentName" />
|
||||
<el-table-column label="设备数量" align="center" prop="equipmentQuantity" />
|
||||
<el-table-column label="制造负责人" align="center" prop="manufacturingLeader" />
|
||||
<el-table-column label="作业负责人" align="center" prop="operationLeader" />
|
||||
<el-table-column label="计划交货日期" align="center" prop="plannedDeliveryDate" />
|
||||
<el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-search"
|
||||
@click="handleShowDetail(scope.row)">查看详情</el-button>
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-printer"
|
||||
@click="handlePrint(scope.row)">打印</el-button>
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog title="工艺卡详情" :visible.sync="detail" width="95%" append-to-body v-loading="formLoading">
|
||||
<!-- 主表详情 -->
|
||||
<el-form ref="detailForm" :model="detailData" label-width="100px" style="margin-bottom: 20px;">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="equipmentName" label="设备名称">
|
||||
<el-input v-model="detailData.equipmentName" placeholder="请输入设备名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="equipmentQuantity" label="设备数量">
|
||||
<el-input v-model="detailData.equipmentQuantity" placeholder="请输入设备数量" type="number" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="manufacturingLeader" label="制造负责人">
|
||||
<el-select filterable allow-create v-model="detailData.manufacturingLeader">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="operationLeader" label="作业负责人">
|
||||
<el-select filterable allow-create v-model="detailData.operationLeader">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="plannedDeliveryDate" label="计划交货日期">
|
||||
<el-date-picker v-model="detailData.plannedDeliveryDate" type="date" placeholder="请选择计划交货日期"
|
||||
style="width: 100%;" value-format="yyyy-MM-DD HH:mm:ss" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input v-model="detailData.remark" placeholder="请输入备注" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<el-divider>工艺卡明细</el-divider>
|
||||
<div style="margin-bottom: 10px; display: flex; align-items: center;">
|
||||
<el-button v-loading="buttonLoading" @click="addDetailRow" type="primary" size="mini">添加明细</el-button>
|
||||
<el-alert title="工艺卡明细的变更会立刻生效,无需手动保存"></el-alert>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="detailData.oaProcessCardDetailList" border>
|
||||
<el-table-column label="序号" width="80" prop="sequence" />
|
||||
<el-table-column label="工序名称">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.processName" placeholder="请输入工序名称"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序详情">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.processDetail" placeholder="请输入工序详情"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="作业责任人">
|
||||
<template slot-scope="scope">
|
||||
<el-select filterable allow-create v-model="scope.row.workResponsiblePerson"
|
||||
@change="handleDetailChange(scope.row)" placeholder="请选择或输入作业责任人">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序开始时间">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.processStartTime" type="datetime" placeholder="请选择开始时间"
|
||||
style="width: 100%;" @change="handleDetailChange(scope.row)" value-format="yyyy-MM-dd HH:mm:ss" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序结束时间">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.processEndTime" type="datetime" placeholder="请选择结束时间"
|
||||
style="width: 100%;" @change="handleDetailChange(scope.row)" value-format="yyyy-MM-dd HH:mm:ss" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检员">
|
||||
<template slot-scope="scope">
|
||||
<el-select filterable allow-create placeholder="请选择或输入质检员" v-model="scope.row.inspector"
|
||||
@change="handleDetailChange(scope.row)">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不合格内容">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.unqualifiedContent" placeholder="请输入不合格内容"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.remark" placeholder="请输入备注"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-loading="buttonLoading" type="danger" size="mini"
|
||||
@click="removeDetailRow(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detail = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave" v-loading="buttonLoading">保存修改</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { delProcessCard, updateProcessCard } from "@/api/oa/workshop/processCard";
|
||||
import { addProcessCardDetail, delProcessCardDetail, listProcessCardDetail, updateProcessCardDetail } from "@/api/oa/workshop/processCardDetail";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
|
||||
export default {
|
||||
name: 'MasterTable',
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showSearch: true,
|
||||
detail: false,
|
||||
detailData: {
|
||||
oaProcessCardDetailList: []
|
||||
},
|
||||
userList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 999,
|
||||
signTime: undefined
|
||||
},
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
equipmentName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
|
||||
equipmentQuantity: [{ required: true, message: '请输入设备数量', trigger: 'blur' }]
|
||||
},
|
||||
buttonLoading: false,
|
||||
formLoading: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getAllUser()
|
||||
},
|
||||
methods: {
|
||||
getAllUser () {
|
||||
listUser({ pageNum: 1, pageSize: 999 }).then(res => {
|
||||
this.userList = res.rows
|
||||
})
|
||||
},
|
||||
handleShowDetail (row) {
|
||||
// 调用list接口查询详情
|
||||
this.formLoading = true;
|
||||
this.detail = true;
|
||||
listProcessCardDetail({ cardId: row.cardId, pageSize: 999 }).then(response => {
|
||||
this.detailData = { ...row, oaProcessCardDetailList: response.rows || [] };
|
||||
}).finally(() => {
|
||||
this.formLoading = false;
|
||||
});
|
||||
},
|
||||
// 添加明细行
|
||||
addDetailRow () {
|
||||
this.buttonLoading = true;
|
||||
addProcessCardDetail({
|
||||
sequence: this.detailData.oaProcessCardDetailList.length + 1,
|
||||
orderId: this.detailData.orderId,
|
||||
processName: '',
|
||||
processDetail: '',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
}).then(response => {
|
||||
listProcessCardDetail({ orderId: this.detailData.orderId, pageSize: 999 }).then(response => {
|
||||
this.detailData.oaProcessCardDetailList = response.rows || [];
|
||||
this.$message.success("添加成功");
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
// 删除明细行
|
||||
removeDetailRow (row) {
|
||||
this.buttonLoading = true;
|
||||
delProcessCardDetail(row.detailId).then(response => {
|
||||
listProcessCardDetail({ orderId: this.detailData.orderId, pageSize: 999 }).then(response => {
|
||||
this.detailData.oaProcessCardDetailList = response.rows || [];
|
||||
this.$message.success("删除成功");
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
// 明细行数据改变时触发
|
||||
handleDetailChange (row) {
|
||||
updateProcessCardDetail(row)
|
||||
},
|
||||
// 保存修改
|
||||
handleSave () {
|
||||
this.$refs.detailForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
updateProcessCard(this.detailData).then(response => {
|
||||
this.$message.success("更新成功");
|
||||
this.detail = false;
|
||||
// 刷新父组件数据
|
||||
this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDelete (row) {
|
||||
// 删除添加二次确认
|
||||
this.$modal.confirm('确认删除该工艺卡吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.buttonLoading = true;
|
||||
delProcessCard(row.cardId).then(response => {
|
||||
this.$message.success("删除成功");
|
||||
this.detail = false;
|
||||
// 刷新父组件数据
|
||||
this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
// 打印工艺卡
|
||||
handlePrint (row) {
|
||||
this.buttonLoading = true;
|
||||
// 获取工艺卡明细
|
||||
listProcessCardDetail({ cardId: row.cardId, pageSize: 999 }).then(response => {
|
||||
// 通过事件将数据传递给父组件
|
||||
this.$emit('print', {
|
||||
waybill: row,
|
||||
waybillDetails: response.rows || []
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
620
ruoyi-ui/src/views/oa/workshop/craft/components/Printer.vue
Normal file
620
ruoyi-ui/src/views/oa/workshop/craft/components/Printer.vue
Normal file
@@ -0,0 +1,620 @@
|
||||
<!-- 收货单组件 -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="waybill-container" ref="waybillRef">
|
||||
<!-- 头部信息 -->
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">设备名称:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.equipmentName" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">设备数量:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.equipmentQuantity" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">交货日期:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.plannedDeliveryDate" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">制造负责人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.manufacturingLeader" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">作业负责人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.operationLeader" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">创建人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.createBy" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 100%">
|
||||
<span class="label">备注:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.remark"
|
||||
style="width: calc(100% - 80px)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<table class="waybill-table" v-loading="loading" loading-text="加载中...">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>工序名称</th>
|
||||
<th>工序详情</th>
|
||||
<th>作业责任人</th>
|
||||
<th>工序开始时间</th>
|
||||
<th>工序结束时间</th>
|
||||
<th>质检员</th>
|
||||
<th>不合格内容</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 无明细提示 -->
|
||||
<tr v-if="localWaybillDetails.length === 0">
|
||||
<td colspan="9" class="no-data">
|
||||
<div class="no-data-content">
|
||||
<el-empty description="暂无工艺卡明细" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 明细数据 -->
|
||||
<tr v-for="(item, index) in localWaybillDetails" :key="index">
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.sequence" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.processName" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.processDetail" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.workResponsiblePerson" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.processStartTime" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.processEndTime" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.inspector" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.unqualifiedContent" />
|
||||
</td>
|
||||
<td>
|
||||
<div class="table-cell-with-action">
|
||||
<input type="text" class="table-input transparent-input" v-model="item.remark" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 签名栏 -->
|
||||
<div class="waybill-footer">
|
||||
<div class="footer-item inline">
|
||||
<span class="label">制单:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" v-model="localWaybill.createBy" />
|
||||
</div>
|
||||
<div class="footer-item inline">
|
||||
<span class="label">审核:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" />
|
||||
</div>
|
||||
<div class="footer-item inline">
|
||||
<span class="label">确认:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作按钮 -->
|
||||
<div class="waybill-actions">
|
||||
<el-button type="primary" @click="saveAsImage">保存为图片</el-button>
|
||||
<el-button type="success" @click="printWaybill">打印</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import domtoimage from 'dom-to-image';
|
||||
import printJS from 'print-js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 组件接收完整的工艺卡内容, 渲染工艺卡,这个面板内包括一个保存为图片和一个打印按钮
|
||||
waybill: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
},
|
||||
waybillDetails: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 本地可编辑的工艺卡数据
|
||||
localWaybill: {
|
||||
equipmentName: '',
|
||||
equipmentQuantity: '',
|
||||
manufacturingLeader: '',
|
||||
operationLeader: '',
|
||||
plannedDeliveryDate: '',
|
||||
remark: '',
|
||||
createTime: '',
|
||||
createBy: ''
|
||||
},
|
||||
// 本地可编辑的工艺卡明细
|
||||
localWaybillDetails: [],
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听props变化,更新本地数据
|
||||
waybill: {
|
||||
handler (newVal) {
|
||||
if (newVal) {
|
||||
this.localWaybill = {
|
||||
equipmentName: newVal.equipmentName || '',
|
||||
equipmentQuantity: newVal.equipmentQuantity || '',
|
||||
manufacturingLeader: newVal.manufacturingLeader || '',
|
||||
operationLeader: newVal.operationLeader || '',
|
||||
plannedDeliveryDate: newVal.plannedDeliveryDate ? this.formatDate(newVal.plannedDeliveryDate) : '',
|
||||
remark: newVal.remark || '',
|
||||
createTime: newVal.createTime ? this.formatDate(newVal.createTime) : '',
|
||||
createBy: newVal.createBy || ''
|
||||
};
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
waybillDetails: {
|
||||
handler (newVal) {
|
||||
if (newVal && Array.isArray(newVal)) {
|
||||
this.localWaybillDetails = newVal.map(item => ({
|
||||
...item,
|
||||
// 格式化日期字段
|
||||
processStartTime: item.processStartTime ? this.formatDate(item.processStartTime) : '',
|
||||
processEndTime: item.processEndTime ? this.formatDate(item.processEndTime) : ''
|
||||
}));
|
||||
} else {
|
||||
this.localWaybillDetails = [];
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 格式化日期
|
||||
formatDate (dateStr) {
|
||||
if (dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// 保存为图片
|
||||
saveAsImage () {
|
||||
const node = this.$refs.waybillRef;
|
||||
// 确保容器在保存图片时能完整显示所有内容
|
||||
const originalWidth = node.style.width;
|
||||
const originalOverflow = node.style.overflow;
|
||||
|
||||
// 临时调整容器样式,确保所有内容可见
|
||||
node.style.width = 'auto';
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// 获取实际内容宽度
|
||||
const contentWidth = node.scrollWidth;
|
||||
const contentHeight = node.scrollHeight;
|
||||
|
||||
domtoimage.toPng(node, {
|
||||
width: contentWidth,
|
||||
height: contentHeight,
|
||||
style: {
|
||||
width: `${contentWidth}px`,
|
||||
height: `${contentHeight}px`,
|
||||
overflow: 'visible'
|
||||
}
|
||||
})
|
||||
.then(dataUrl => {
|
||||
const link = document.createElement('a');
|
||||
link.download = `工艺卡_${this.waybill.equipmentName || this.waybill.cardId || Date.now()}.png`;
|
||||
link.href = dataUrl;
|
||||
link.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('保存图片失败:', error);
|
||||
this.$message.error('保存图片失败');
|
||||
})
|
||||
.finally(() => {
|
||||
// 恢复原始样式
|
||||
node.style.width = originalWidth;
|
||||
node.style.overflow = originalOverflow;
|
||||
});
|
||||
},
|
||||
// 打印工艺卡
|
||||
printWaybill () {
|
||||
const node = this.$refs.waybillRef;
|
||||
// 确保容器在打印时能完整显示所有内容
|
||||
const originalWidth = node.style.width;
|
||||
const originalOverflow = node.style.overflow;
|
||||
|
||||
// 临时调整容器样式,确保所有内容可见
|
||||
node.style.width = 'auto';
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// 获取实际内容宽度
|
||||
const contentWidth = node.scrollWidth;
|
||||
|
||||
printJS({
|
||||
printable: node,
|
||||
maxWidth: contentWidth,
|
||||
type: 'html',
|
||||
scanStyles: true,
|
||||
style: `
|
||||
@page { size: A4 landscape; margin: 1cm; }
|
||||
.waybill-container { width: 100%; max-width: none; overflow: visible; }
|
||||
.waybill-table { width: 100%; table-layout: auto; }
|
||||
`,
|
||||
targetStyles: ['*']
|
||||
});
|
||||
|
||||
// 恢复原始样式
|
||||
setTimeout(() => {
|
||||
node.style.width = originalWidth;
|
||||
node.style.overflow = originalOverflow;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.waybill-container {
|
||||
width: 870px;
|
||||
max-width: none;
|
||||
min-width: 870px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
font-family: SimSun, serif;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.waybill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.header-left,
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.date-label {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
/* 可编辑输入框样式 */
|
||||
.editable-input {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-family: SimSun, serif;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
border-bottom: 1px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.editable-input:focus {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.date-input {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* 透明输入框样式 */
|
||||
.transparent-input {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.transparent-input:focus {
|
||||
border-bottom-color: #409eff;
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.waybill-table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
font-size: 12px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.waybill-table th,
|
||||
.waybill-table td {
|
||||
border: 1px solid #000;
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 表格列宽设置 */
|
||||
.waybill-table th:nth-child(1),
|
||||
.waybill-table td:nth-child(1) {
|
||||
width: 40px;
|
||||
/* 卷ID */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(2),
|
||||
.waybill-table td:nth-child(2) {
|
||||
width: 80px;
|
||||
/* 卷号 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(3),
|
||||
.waybill-table td:nth-child(3) {
|
||||
width: 200px;
|
||||
/* 动作状态 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(4),
|
||||
.waybill-table td:nth-child(4) {
|
||||
width: 80px;
|
||||
/* 项目类型 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(5),
|
||||
.waybill-table td:nth-child(5) {
|
||||
width: 80px;
|
||||
/* 项目ID */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(6),
|
||||
.waybill-table td:nth-child(6) {
|
||||
width: 80px;
|
||||
/* 动作类型 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(7),
|
||||
.waybill-table td:nth-child(7) {
|
||||
width: 80px;
|
||||
/* 操作类型 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(8),
|
||||
.waybill-table td:nth-child(8) {
|
||||
width: 80px;
|
||||
/* 仓库ID */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(9),
|
||||
.waybill-table td:nth-child(9) {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.waybill-table th {
|
||||
background-color: #f5f7fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.waybill-table tr:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/* 表格输入框样式 */
|
||||
.table-input {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.table-input:focus {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
/* 无数据样式 */
|
||||
.no-data {
|
||||
height: 200px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.no-data-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 表格单元格操作区 */
|
||||
.table-cell-with-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 备注样式 */
|
||||
.waybill-remarks {
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.waybill-remarks p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
/* 底部签名样式 */
|
||||
.waybill-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.waybill-pickup-location {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.waybill-pickup-location label {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.footer-item.inline {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer-item .label {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.signature-input {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.full-input {
|
||||
/* 占满本行的剩余空间,父容器不是flex */
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.waybill-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* 添加明细按钮 */
|
||||
.add-detail-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 打印样式 */
|
||||
@media print {
|
||||
.waybill-container {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.waybill-header {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.waybill-footer {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.waybill-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.waybill-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.waybill-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.waybill-table th,
|
||||
.waybill-table td {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.waybill-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.footer-item.inline {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: auto;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
608
ruoyi-ui/src/views/oa/workshop/craft/index.vue
Normal file
608
ruoyi-ui/src/views/oa/workshop/craft/index.vue
Normal file
@@ -0,0 +1,608 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧项目列表 -->
|
||||
<el-col :span="6">
|
||||
<ProjectTree v-model="currentProject" @select="handleProjectSelect" :list="projectList">
|
||||
<template slot-scope="scope">
|
||||
<!-- 如果scope.data.processCardCount > 0改为蓝色文本#2bf -->
|
||||
<span
|
||||
:style="{ 'color': scope.data.processCardCount > 0 ? '#2bf' : '', display: 'flex', alignItems: 'center' }">
|
||||
<span style="margin-right: 1px;">[{{ scope.data.processCardCount || 0
|
||||
}}]</span>
|
||||
<span
|
||||
style="text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; width: calc(100% - 20px);">{{
|
||||
scope.data.projectName }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</ProjectTree>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧发货明细 -->
|
||||
<el-col :span="18">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ currentProject.projectName || '请选择项目' }}</span>
|
||||
<span v-if="currentProject.projectCode" style="margin-left: 5px; color: #2bf">[{{ currentProject.projectCode
|
||||
}}]</span>
|
||||
<span v-if="currentProject.projectNum" style="margin-left: 5px;">({{ currentProject.projectNum
|
||||
}})</span>
|
||||
<div style="float: right;">
|
||||
<el-button v-if="currentProject.projectId" type="text" @click="getList">刷新</el-button>
|
||||
<el-button v-if="currentProject.projectId" type="text" @click="handleAdd">新增单据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!currentProject.projectId" description="请从左侧选择项目" :image-size="200">
|
||||
<el-button type="primary" @click="handleSelectFirstProject" v-if="projectList.length > 0">
|
||||
选择第一个项目
|
||||
</el-button>
|
||||
</el-empty>
|
||||
|
||||
<el-tabs v-else v-model="activeTab" @tab-click="getList">
|
||||
<el-tab-pane label="单据维度" name="master">
|
||||
<MasterTable :list="masterList" :loading="loading" @refresh-data="getList" @print="handlePrint" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="明细维度" name="detail">
|
||||
<DetailTable :list="detailList" :loading="loading" :total="total" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增工艺卡弹窗 -->
|
||||
<el-drawer :title="title" :visible.sync="open" append-to-body direction="btt" size="90%">
|
||||
<el-form ref="form" :model="form" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="equipmentName" label="设备名称">
|
||||
<el-input placeholder="请输入设备名称" v-model="form.equipmentName" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="equipmentQuantity" label="设备数量">
|
||||
<el-input placeholder="请输入设备数量" v-model="form.equipmentQuantity" type="number" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="manufacturingLeader" label="制造负责人">
|
||||
<el-select filterable allow-create placeholder="请选择或输入制造负责人" v-model="form.manufacturingLeader">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="operationLeader" label="作业负责人">
|
||||
<el-select filterable allow-create placeholder="请选择或输入作业负责人" v-model="form.operationLeader">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="plannedDeliveryDate" label="计划交货日期">
|
||||
<el-date-picker v-model="form.plannedDeliveryDate" type="date" placeholder="请选择计划交货日期"
|
||||
style="width: 100%;" value-format="yyyy-MM-DD HH:mm:ss" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input type="textarea" placeholder="请输入备注" v-model="form.remark" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider>工艺卡明细</el-divider>
|
||||
|
||||
<div>
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button @click="addRow" type="primary">添加明细</el-button>
|
||||
</div>
|
||||
<el-table :data="form.oaProcessCardDetailList" style="width: 100%" border stripe v-loading="loading">
|
||||
<el-table-column label="序号" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.sequence" placeholder="请输入序号"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序名称">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.processName" placeholder="请输入工序名称"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序详情">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.processDetail" placeholder="请输入工序详情"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="作业责任人">
|
||||
<template slot-scope="scope">
|
||||
<el-select filterable allow-create placeholder="请选择或输入作业责任人" v-model="scope.row.workResponsiblePerson">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序开始时间">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.processStartTime" type="datetime" placeholder="请选择开始时间"
|
||||
style="width: 100%;" value-format="yyyy-MM-dd HH:mm:ss" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工序结束时间">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.processEndTime" type="datetime" placeholder="请选择结束时间"
|
||||
style="width: 100%;" value-format="yyyy-MM-dd HH:mm:ss" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质检员">
|
||||
<template slot-scope="scope">
|
||||
<el-select filterable allow-create placeholder="请选择或输入质检员" v-model="scope.row.inspector">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不合格内容">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.unqualifiedContent" placeholder="请输入不合格内容"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.remark" placeholder="请输入备注"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" @click="removeRow(scope.$index)">-</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-form>
|
||||
<div style="display: flex; justify-content: flex-end;">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">创 建 工 艺 卡</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 打印组件 -->
|
||||
<el-dialog title="打印工艺卡" :visible.sync="printVisible" width="95%" append-to-body>
|
||||
<Printer :waybill="printData" :waybillDetails="printDetails" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listUser } from "@/api/system/user";
|
||||
import ProjectTree from '@/components/fad-service/ProjectList';
|
||||
import DetailTable from './components/DetailTable';
|
||||
import MasterTable from './components/MasterTable';
|
||||
import Printer from './components/Printer';
|
||||
|
||||
import { insertProcessCard, listProcessCard, listProjectWithCount } from "@/api/oa/workshop/processCard";
|
||||
import { listProcessCardDetailByProjectId } from "@/api/oa/workshop/processCardDetail";
|
||||
|
||||
export default {
|
||||
name: "ProjectOutWarehouse",
|
||||
components: {
|
||||
ProjectTree,
|
||||
MasterTable,
|
||||
DetailTable,
|
||||
Printer
|
||||
},
|
||||
mounted () {
|
||||
this.getAllUser()
|
||||
this.getProjectList()
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 用户列表
|
||||
userList: [],
|
||||
// 当前选中的项目
|
||||
currentProject: {},
|
||||
// 项目列表
|
||||
projectList: [],
|
||||
// 发货列表
|
||||
outWareHouseList: [],
|
||||
// 新增弹窗
|
||||
open: false,
|
||||
// 打印弹窗
|
||||
printVisible: false,
|
||||
// 打印数据
|
||||
printData: {},
|
||||
// 打印明细
|
||||
printDetails: [],
|
||||
// 标题
|
||||
title: "",
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 库存数据
|
||||
oaWarehouseList: [],
|
||||
// 选中的行
|
||||
selectedRows: [],
|
||||
// 当前激活的tab
|
||||
activeTab: 'master',
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
projectId: undefined,
|
||||
type: 0
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
projectId: '',
|
||||
oaProcessCardDetailList: []
|
||||
},
|
||||
detailList: [],
|
||||
masterList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getAllUser () {
|
||||
listUser({ pageNum: 1, pageSize: 999 }).then(res => {
|
||||
this.userList = res.rows
|
||||
})
|
||||
},
|
||||
// 获取项目列表(带工艺卡和发货单据数量)
|
||||
getProjectList () {
|
||||
listProjectWithCount({ pageNum: 1, pageSize: 999 }).then(res => {
|
||||
this.projectList = res.data;
|
||||
})
|
||||
},
|
||||
// 项目选择
|
||||
handleProjectSelect (data) {
|
||||
this.currentProject = data;
|
||||
this.queryParams.projectId = data.projectId;
|
||||
this.getList();
|
||||
},
|
||||
// 获取发货列表
|
||||
getList () {
|
||||
if (!this.currentProject.projectId) return;
|
||||
this.loading = true;
|
||||
if (this.activeTab === 'master') {
|
||||
listProcessCard(this.queryParams).then(res => {
|
||||
this.masterList = res.rows;
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
listProcessCardDetailByProjectId(this.queryParams.projectId).then(res => {
|
||||
this.detailList = res.data;
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
// 添加新行
|
||||
addRow () {
|
||||
this.form.oaProcessCardDetailList.push({
|
||||
sequence: this.form.oaProcessCardDetailList.length + 1,
|
||||
processName: '',
|
||||
processDetail: '',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
processStartTime: undefined,
|
||||
processEndTime: undefined,
|
||||
remark: ''
|
||||
});
|
||||
},
|
||||
// 删除选中行
|
||||
removeRow (index) {
|
||||
this.form.oaProcessCardDetailList.splice(index, 1);
|
||||
// 更新序号
|
||||
this.form.oaProcessCardDetailList.forEach((item, idx) => {
|
||||
item.sequence = idx + 1;
|
||||
});
|
||||
},
|
||||
|
||||
// 新增
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加工艺卡";
|
||||
this.form.projectId = this.currentProject.projectId;
|
||||
},
|
||||
// 取消
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 重置表单
|
||||
reset () {
|
||||
this.form = {
|
||||
projectId: this.currentProject.projectId,
|
||||
oaProcessCardDetailList: [
|
||||
{
|
||||
sequence: 1,
|
||||
processName: '图纸设计',
|
||||
processDetail: '图纸资料是否齐全',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 2,
|
||||
processName: '钣金安装',
|
||||
processDetail: '框架的开孔/尺寸是否正确',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 3,
|
||||
processName: '钣金安装',
|
||||
processDetail: '安装板尺寸/数量是否正确',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 4,
|
||||
processName: '钣金安装',
|
||||
processDetail: '组装配件是否齐全',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 5,
|
||||
processName: '钣金安装',
|
||||
processDetail: '涂装是否正确无损伤',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 6,
|
||||
processName: '钣金安装',
|
||||
processDetail: '钣金安装是否可靠美观',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 7,
|
||||
processName: '元件安装',
|
||||
processDetail: '元件数量/型号安装是否正确',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 8,
|
||||
processName: '元件安装',
|
||||
processDetail: '元件外观是否完好',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 9,
|
||||
processName: '元件安装',
|
||||
processDetail: '元件固定是否可靠',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 10,
|
||||
processName: '元件安装',
|
||||
processDetail: '元件说明书是否齐全',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 11,
|
||||
processName: '元件安装',
|
||||
processDetail: '元件标记是否齐全',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 12,
|
||||
processName: '母排制作',
|
||||
processDetail: '母排型号规格是否正确',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 13,
|
||||
processName: '母排制作',
|
||||
processDetail: '母排支架是否结实可靠',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 14,
|
||||
processName: '母排制作',
|
||||
processDetail: '母排制作安装是否符合标准',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 15,
|
||||
processName: '母排制作',
|
||||
processDetail: '母排连接是否结实',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 16,
|
||||
processName: '配线电缆',
|
||||
processDetail: '线/电缆型号是否正确',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 17,
|
||||
processName: '配线电缆',
|
||||
processDetail: '配线是否符合标准,互相不干扰',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 18,
|
||||
processName: '配线电缆',
|
||||
processDetail: '线/缆是否固定可靠',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 19,
|
||||
processName: '配线电缆',
|
||||
processDetail: '末端处理是否良好',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
},
|
||||
{
|
||||
sequence: 20,
|
||||
processName: '配线电缆',
|
||||
processDetail: '终端连接是否紧固',
|
||||
workResponsiblePerson: '',
|
||||
inspector: '',
|
||||
unqualifiedContent: '',
|
||||
remark: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
// 提交表单
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
this.form.oaProcessCardDetailList.forEach((item, index) => {
|
||||
item.sequence = index + 1;
|
||||
});
|
||||
insertProcessCard(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择第一个项目
|
||||
handleSelectFirstProject () {
|
||||
if (this.projectList.length > 0) {
|
||||
console.log(this.projectList[0], '选择第一个项目');
|
||||
this.handleProjectSelect(this.projectList[0]);
|
||||
}
|
||||
},
|
||||
// 处理打印
|
||||
handlePrint (data) {
|
||||
this.printData = data.waybill;
|
||||
this.printDetails = data.waybillDetails;
|
||||
this.printVisible = true;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 修改滚动条样式 */
|
||||
:deep(.el-card__body) {
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
}
|
||||
|
||||
:deep(.el-card__body::-webkit-scrollbar) {
|
||||
display: none;
|
||||
/* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
/* 修改表格滚动条样式 */
|
||||
:deep(.el-table__body-wrapper) {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 添加box-card滚动条样式 */
|
||||
.box-card {
|
||||
height: calc(100vh - 120px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.table-input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
445
ruoyi-ui/src/views/oa/workshop/dashboard/index.vue
Normal file
445
ruoyi-ui/src/views/oa/workshop/dashboard/index.vue
Normal file
@@ -0,0 +1,445 @@
|
||||
<template>
|
||||
<div class="workshop-report-container">
|
||||
<!-- 筛选区域 -->
|
||||
<div class="filter-bar">
|
||||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||||
<el-form-item label="时间范围:">
|
||||
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
|
||||
end-placeholder="结束日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
|
||||
@change="handleDateChange"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 数据概览卡片 -->
|
||||
<el-row :gutter="20" class="summary-card-row">
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">涉及项目数</div>
|
||||
<div class="card-value">{{ summary.involvedProjectCount || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">工艺卡总数</div>
|
||||
<div class="card-value">{{ summary.processCardCount || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">工艺步骤总数</div>
|
||||
<div class="card-value">{{ summary.processCardDetailCount || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">总工时</div>
|
||||
<div class="card-value">{{ summary.totalWorkHours || 0 }}h</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">平均工时</div>
|
||||
<div class="card-value">{{ summary.avgWorkHours || 0 }}h</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card class="summary-card">
|
||||
<div class="card-title">设备种类数</div>
|
||||
<div class="card-value">{{ summary.equipmentKinds || 0 }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表统计区域 -->
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<!-- 按项目统计工艺卡数量 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="chart-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>按项目统计工艺卡数量</span>
|
||||
</div>
|
||||
<div id="projectProcessCardChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 按作业责任人统计工艺步骤数量 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="chart-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>按作业责任人统计工艺步骤数量</span>
|
||||
</div>
|
||||
<div id="responsPersonStepChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 按作业负责人统计工艺卡数量 -->
|
||||
<el-col :span="8">
|
||||
<el-card class="chart-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>按作业负责人统计工艺卡数量</span>
|
||||
</div>
|
||||
<div id="responsPersonCardChart" class="chart-container"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 工艺卡列表 -->
|
||||
<el-card class="table-card" style="margin-top: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>工艺卡列表</span>
|
||||
</div>
|
||||
<el-table :data="processCardList" border stripe style="width: 100%;" v-loading="loading">
|
||||
<el-table-column prop="projectName" label="项目名称" min-width="200"></el-table-column>
|
||||
<el-table-column prop="projectNum" label="项目编号"></el-table-column>
|
||||
<el-table-column prop="equipmentName" label="设备名称"></el-table-column>
|
||||
<el-table-column prop="equipmentQuantity" label="设备数量"></el-table-column>
|
||||
<el-table-column prop="manufacturingLeader" label="生产负责人"></el-table-column>
|
||||
<el-table-column prop="operationLeader" label="作业负责人"></el-table-column>
|
||||
<el-table-column prop="plannedDeliveryDate" label="计划交付日期">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.plannedDeliveryDate ? scope.row.plannedDeliveryDate.split(' ')[0] : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 工艺卡详情列表 -->
|
||||
<el-card class="table-card" style="margin-top: 20px;">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>工艺卡详情列表</span>
|
||||
</div>
|
||||
<el-table :data="processCardDetailList" border stripe style="width: 100%;" v-loading="loading">
|
||||
<el-table-column prop="processName" label="工序名称"></el-table-column>
|
||||
<el-table-column prop="processDetail" label="工序详情" min-width="200"></el-table-column>
|
||||
<el-table-column prop="workResponsiblePerson" label="作业责任人">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.workResponsiblePerson || '未指定' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sequence" label="工序序号"></el-table-column>
|
||||
<el-table-column prop="processStartTime" label="工序开始时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.processStartTime ? scope.row.processStartTime.split(' ')[0] : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="processEndTime" label="工序结束时间">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.processEndTime ? scope.row.processEndTime.split(' ')[0] : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="inspector" label="检验员">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.inspector || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listProcessCard } from "@/api/oa/workshop/processCard";
|
||||
import { listProcessCardDetail } from "@/api/oa/workshop/processCardDetail";
|
||||
import { getSummary } from "@/api/oa/workshop/workshopReport";
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: "WorkshopReport",
|
||||
data () {
|
||||
return {
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
startTime: "",
|
||||
endTime: ""
|
||||
},
|
||||
// 时间范围选择器绑定值
|
||||
dateRange: [],
|
||||
// 加载状态
|
||||
loading: false,
|
||||
// 汇总数据
|
||||
summary: {},
|
||||
// 工艺卡列表
|
||||
processCardList: [],
|
||||
// 工艺卡详情列表
|
||||
processCardDetailList: [],
|
||||
// echarts实例
|
||||
projectChart: null,
|
||||
personStepChart: null,
|
||||
personCardChart: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
queryParams: {
|
||||
handler (newVal, oldVal) {
|
||||
if (newVal.startTime && newVal.endTime) {
|
||||
this.fetchAllData();
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化当月时间范围
|
||||
this.initDate();
|
||||
// 初始化图表
|
||||
this.initCharts();
|
||||
},
|
||||
beforeDestroy () {
|
||||
// 销毁echarts实例
|
||||
if (this.projectChart) this.projectChart.dispose();
|
||||
if (this.personStepChart) this.personStepChart.dispose();
|
||||
if (this.personCardChart) this.personCardChart.dispose();
|
||||
},
|
||||
methods: {
|
||||
// 初始化当月时间范围
|
||||
initDate () {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
// 当月第一天
|
||||
const firstDay = new Date(year, month, 1);
|
||||
// 当月最后一天
|
||||
const lastDay = new Date(year, month + 1, 0);
|
||||
|
||||
// 格式化日期为YYYY-MM-DD
|
||||
const formatDate = (date) => {
|
||||
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const start = formatDate(firstDay);
|
||||
const end = formatDate(lastDay);
|
||||
|
||||
this.dateRange = [start, end];
|
||||
this.queryParams.startTime = start;
|
||||
this.queryParams.endTime = end;
|
||||
},
|
||||
// 时间范围改变处理
|
||||
handleDateChange (val) {
|
||||
if (val && val.length === 2) {
|
||||
this.queryParams.startTime = val[0];
|
||||
this.queryParams.endTime = val[1];
|
||||
}
|
||||
},
|
||||
// 获取所有数据
|
||||
fetchAllData () {
|
||||
this.loading = true;
|
||||
// 并发请求所有接口
|
||||
Promise.all([
|
||||
this.getSummary(),
|
||||
this.getProcessCard(),
|
||||
this.getProcessCardDetail()
|
||||
]).then(() => {
|
||||
// 更新图表数据
|
||||
this.updateCharts();
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
this.$message.error('数据加载失败');
|
||||
});
|
||||
},
|
||||
// 获取汇总数据
|
||||
getSummary () {
|
||||
return new Promise((resolve, reject) => {
|
||||
getSummary(this.queryParams)
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.summary = res.data;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取工艺卡列表
|
||||
getProcessCard () {
|
||||
return new Promise((resolve, reject) => {
|
||||
listProcessCard(this.queryParams)
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.rows) {
|
||||
this.processCardList = res.rows;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
// 获取工艺卡详情列表
|
||||
getProcessCardDetail () {
|
||||
return new Promise((resolve, reject) => {
|
||||
listProcessCardDetail(this.queryParams)
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.rows) {
|
||||
this.processCardDetailList = res.rows;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
// 初始化图表
|
||||
initCharts () {
|
||||
// 按项目统计工艺卡图表
|
||||
this.projectChart = echarts.init(document.getElementById('projectProcessCardChart'));
|
||||
// 按作业责任人统计工艺步骤图表
|
||||
this.personStepChart = echarts.init(document.getElementById('responsPersonStepChart'));
|
||||
// 按作业负责人统计工艺卡图表
|
||||
this.personCardChart = echarts.init(document.getElementById('responsPersonCardChart'));
|
||||
|
||||
// 初始化默认配置
|
||||
this.projectChart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: { type: 'category', data: [], axisLabel: { rotate: 30 } },
|
||||
yAxis: { type: 'value', name: '工艺卡数量' },
|
||||
series: [{ type: 'bar', data: [], name: '工艺卡数' }]
|
||||
});
|
||||
|
||||
this.personStepChart.setOption({
|
||||
tooltip: { trigger: 'item' },
|
||||
series: [{ type: 'pie', radius: ['40%', '70%'], data: [] }]
|
||||
});
|
||||
|
||||
this.personCardChart.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
xAxis: { type: 'category', data: [], axisLabel: { rotate: 30 } },
|
||||
yAxis: { type: 'value', name: '工艺卡数量' },
|
||||
series: [{ type: 'bar', data: [], name: '工艺卡数' }]
|
||||
});
|
||||
|
||||
// 监听窗口大小变化,自适应图表
|
||||
window.addEventListener('resize', () => {
|
||||
this.projectChart.resize();
|
||||
this.personStepChart.resize();
|
||||
this.personCardChart.resize();
|
||||
});
|
||||
},
|
||||
// 更新图表数据
|
||||
updateCharts () {
|
||||
// 1. 按项目统计工艺卡数量
|
||||
const projectCardData = this.statProjectCard();
|
||||
this.projectChart.setOption({
|
||||
xAxis: { data: projectCardData.labels },
|
||||
series: [{ data: projectCardData.values }]
|
||||
});
|
||||
|
||||
// 2. 按作业责任人统计工艺步骤数量
|
||||
const personStepData = this.statPersonStep();
|
||||
this.personStepChart.setOption({
|
||||
series: [{ data: personStepData }]
|
||||
});
|
||||
|
||||
// 3. 按作业负责人统计工艺卡数量
|
||||
const personCardData = this.statPersonCard();
|
||||
this.personCardChart.setOption({
|
||||
xAxis: { data: personCardData.labels },
|
||||
series: [{ data: personCardData.values }]
|
||||
});
|
||||
},
|
||||
// 统计:按项目统计工艺卡数量
|
||||
statProjectCard () {
|
||||
const result = {};
|
||||
this.processCardList.forEach(item => {
|
||||
const projectName = item.projectName || '未知项目';
|
||||
result[projectName] = (result[projectName] || 0) + 1;
|
||||
});
|
||||
return {
|
||||
labels: Object.keys(result),
|
||||
values: Object.values(result)
|
||||
};
|
||||
},
|
||||
// 统计:按作业责任人统计工艺步骤数量
|
||||
statPersonStep () {
|
||||
const result = {};
|
||||
this.processCardDetailList.forEach(item => {
|
||||
const person = item.workResponsiblePerson || '未指定责任人';
|
||||
result[person] = (result[person] || 0) + 1;
|
||||
});
|
||||
return Object.entries(result).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
},
|
||||
// 统计:按作业负责人统计工艺卡数量
|
||||
statPersonCard () {
|
||||
const result = {};
|
||||
this.processCardList.forEach(item => {
|
||||
const person = item.operationLeader || '未指定负责人';
|
||||
result[person] = (result[person] || 0) + 1;
|
||||
});
|
||||
return {
|
||||
labels: Object.keys(result),
|
||||
values: Object.values(result)
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.workshop-report-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.summary-card-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
text-align: center;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.summary-card .card-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.summary-card .card-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #1989fa;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* 适配小屏幕 */
|
||||
@media (max-width: 1200px) {
|
||||
.el-col-8 {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <el-button type="primary" size="mini" style="margin-bottom: 10px" :disabled="selected.length === 0"
|
||||
@click="handleExport">导出选中</el-button> -->
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="设备名称" prop="equipmentName" />
|
||||
<el-table-column label="型号规格" prop="modelSpec" />
|
||||
<el-table-column label="数量" width="60" prop="quantity" />
|
||||
<el-table-column label="单位" width="60" prop="unit" />
|
||||
<el-table-column label="包装类型" width="100" prop="packagingType" />
|
||||
<el-table-column label="净重(kg)" width="80" prop="netWeight" />
|
||||
<el-table-column label="毛重(kg)" width="80" prop="grossWeight" />
|
||||
<el-table-column label="体积(m³)" width="80" prop="volume" />
|
||||
<el-table-column label="箱尺寸(mm,长*宽*高)" width="150" prop="boxSize" />
|
||||
<el-table-column label="备注" prop="detailRemark" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DetailTable',
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 999
|
||||
},
|
||||
selected: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange (val) {
|
||||
this.selected = val;
|
||||
},
|
||||
handleExport () {
|
||||
this.$emit('export-detail', this.selected);
|
||||
},
|
||||
handlePagination (val) {
|
||||
this.$emit('pagination', val);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="项目" align="center" prop="projectName" show-overflow-tooltip />
|
||||
<el-table-column label="供应商" align="center" prop="supplierFullname" show-overflow-tooltip />
|
||||
<el-table-column label="送货单号" align="center" prop="deliveryOrderNo" />
|
||||
<el-table-column label="送货地址" align="center" prop="deliveryAddress" />
|
||||
<el-table-column label="送达目的地" align="center" prop="deliveryDestination" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.status" placeholder="请选择状态" @change="handleStatusChange(scope.row)">
|
||||
<el-option label="已完成" :value="0" />
|
||||
<el-option label="未完成" :value="1" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-upload" @click="handleUpload(scope.row)">上传({{
|
||||
scope.row.accessory ? scope.row.accessory.split(',').length : 0 }})</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-search"
|
||||
@click="handleShowDetail(scope.row)">查看详情</el-button>
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-printer"
|
||||
@click="handlePrint(scope.row)">打印</el-button>
|
||||
<el-button v-loading="buttonLoading" size="mini" type="text" icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)">删除</el-button>
|
||||
<!-- <el-button size="mini" type="text" icon="el-icon-download"
|
||||
@click="$emit('export-detail', scope.row)">导出</el-button> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog title="出库物料详情" :visible.sync="detail" width="95%" append-to-body v-loading="formLoading">
|
||||
<!-- 主表详情 -->
|
||||
<el-form ref="detailForm" :model="detailData" label-width="100px" style="margin-bottom: 20px;">
|
||||
<el-form-item prop="deliveryOrderNo" label="编号">
|
||||
<el-input v-model="detailData.deliveryOrderNo" placeholder="请输入送货单号" />
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="contractNo" label="合同号">
|
||||
<el-input v-model="detailData.contractNo" placeholder="请输入合同号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="userContractNo" label="用户合同号">
|
||||
<el-input v-model="detailData.userContractNo" placeholder="请输入用户合同号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="procurementNo" label="采购订单号">
|
||||
<el-input v-model="detailData.procurementNo" placeholder="请输入采购订单号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="deliveryDestination" label="送货目的地">
|
||||
<el-input v-model="detailData.deliveryDestination" placeholder="请输入送货目的地" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="deliveryAddress" label="送货地址">
|
||||
<el-input v-model="detailData.deliveryAddress" placeholder="请输入送货地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="receiver" label="接收人">
|
||||
<el-input v-model="detailData.receiver" placeholder="请输入接收人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="receiverPhone" label="接收人手机号">
|
||||
<el-input v-model="detailData.receiverPhone" placeholder="请输入接收人手机号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierFullname" label="供应商名称">
|
||||
<el-input v-model="detailData.supplierFullname" placeholder="请输入供应商名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierContact" label="供应商联系人">
|
||||
<el-input v-model="detailData.supplierContact" placeholder="请输入供应商联系人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierPhone" label="供应商手机号">
|
||||
<el-input v-model="detailData.supplierPhone" placeholder="请输入供应商手机号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="receivingCompany" label="收货单位">
|
||||
<el-input v-model="detailData.receivingCompany" placeholder="请输入收货单位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="actualReceiver" label="实际接收人">
|
||||
<el-input v-model="detailData.actualReceiver" placeholder="请输入实际接收人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="constructionCompany" label="施工单位">
|
||||
<el-input v-model="detailData.constructionCompany" placeholder="请输入施工单位" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="deliveryRemark" label="送货备注">
|
||||
<el-input v-model="detailData.deliveryRemark" placeholder="请输入送货备注" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 明细列表 -->
|
||||
<el-divider>物料明细</el-divider>
|
||||
<div style="margin-bottom: 10px; display: flex; align-items: center;">
|
||||
<el-button v-loading="buttonLoading" @click="addDetailRow" type="primary" size="mini">添加明细</el-button>
|
||||
<el-alert title="物料明细的变更会立刻生效,无需手动保存"></el-alert>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="detailData.detailList" border>
|
||||
<el-table-column label="序号" width="50" type="index" />
|
||||
<el-table-column label="分箱编号/总箱件数">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.boxNoTotal" placeholder="请输入分箱编号/总箱件数"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备工位号">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.equipmentStationNo" placeholder="请输入设备工位号"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备名称">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.equipmentName" placeholder="请输入设备名称"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="型号规格">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.modelSpec" placeholder="请输入设备规格"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.quantity" placeholder="请输入数量"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.unit" placeholder="请输入单位"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="包装类型" width="100">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.packagingType" placeholder="请输入包装类型"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="净重(kg)" width="100">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.netWeight" placeholder="请输入净重"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="毛重(kg)" width="100">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.grossWeight" placeholder="请输入毛重"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="体积(m³)" width="100">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.volume" placeholder="请输入体积"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="箱尺寸(mm)" width="150">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.boxSize" placeholder="请输入箱尺寸"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.detailRemark" placeholder="请输入备注"
|
||||
@change="handleDetailChange(scope.row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-loading="buttonLoading" type="danger" size="mini"
|
||||
@click="removeDetailRow(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detail = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave" v-loading="buttonLoading">保存修改</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="附件上传" :visible.sync="fileOpen">
|
||||
<file-upload v-model="detailData.accessory"></file-upload>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="fileOpen = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleFileSave" v-loading="buttonLoading">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { delDeliveryOrder, updateDeliveryOrder } from "@/api/oa/workshop/deliveryOrder";
|
||||
import { addDeliveryOrderDetail, delDeliveryOrderDetail, listDeliveryOrderDetail, updateDeliveryOrderDetail } from "@/api/oa/workshop/deliveryOrderDetail";
|
||||
|
||||
export default {
|
||||
name: 'MasterTable',
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showSearch: true,
|
||||
detail: false,
|
||||
detailData: {
|
||||
detailList: []
|
||||
},
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 999,
|
||||
signTime: undefined
|
||||
},
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
deliveryOrderNo: [{ required: true, message: '请输入送货单号', trigger: 'blur' }],
|
||||
deliveryDestination: [{ required: true, message: '请输入送货目的地', trigger: 'blur' }],
|
||||
deliveryAddress: [{ required: true, message: '请输入送货地址', trigger: 'blur' }],
|
||||
supplierFullname: [{ required: true, message: '请输入供应商名称', trigger: 'blur' }]
|
||||
},
|
||||
buttonLoading: false,
|
||||
formLoading: false,
|
||||
fileOpen: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleShowDetail (row) {
|
||||
// 调用list接口查询详情
|
||||
this.formLoading = true;
|
||||
this.detail = true;
|
||||
listDeliveryOrderDetail({ orderId: row.orderId, pageSize: 999 }).then(response => {
|
||||
this.detailData = { ...row, detailList: response.rows || [] };
|
||||
}).finally(() => {
|
||||
this.formLoading = false;
|
||||
});
|
||||
},
|
||||
// 添加明细行
|
||||
addDetailRow () {
|
||||
this.buttonLoading = true;
|
||||
addDeliveryOrderDetail({
|
||||
sequence: this.detailData.detailList.length + 1,
|
||||
orderId: this.detailData.orderId,
|
||||
boxNoTotal: '',
|
||||
equipmentStationNo: '',
|
||||
equipmentName: '',
|
||||
modelSpec: '',
|
||||
quantity: 1,
|
||||
unit: '',
|
||||
packagingType: '',
|
||||
netWeight: 0,
|
||||
grossWeight: 0,
|
||||
volume: 0,
|
||||
boxSize: '',
|
||||
detailRemark: ''
|
||||
}).then(response => {
|
||||
listDeliveryOrderDetail({ orderId: this.detailData.orderId, pageSize: 999 }).then(response => {
|
||||
this.detailData.detailList = response.rows || [];
|
||||
this.$message.success("添加成功");
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
// 删除明细行
|
||||
removeDetailRow (row) {
|
||||
this.buttonLoading = true;
|
||||
delDeliveryOrderDetail(row.detailId).then(response => {
|
||||
listDeliveryOrderDetail({ orderId: this.detailData.orderId, pageSize: 999 }).then(response => {
|
||||
this.detailData.detailList = response.rows || [];
|
||||
this.$message.success("删除成功");
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
// 明细行数据改变时触发
|
||||
handleDetailChange (row) {
|
||||
updateDeliveryOrderDetail(row)
|
||||
},
|
||||
// 保存修改
|
||||
handleSave () {
|
||||
this.$refs.detailForm.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
updateDeliveryOrder(this.detailData).then(response => {
|
||||
this.$message.success("更新成功");
|
||||
this.detail = false;
|
||||
this.fileOpen = false;
|
||||
// 刷新父组件数据
|
||||
this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleStatusChange (row) {
|
||||
this.buttonLoading = true;
|
||||
updateDeliveryOrder(row).then(response => {
|
||||
this.$message.success("更新成功");
|
||||
// 刷新父组件数据
|
||||
// this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
handleUpload (row) {
|
||||
this.detailData = row;
|
||||
this.fileOpen = true;
|
||||
},
|
||||
handleFileSave () {
|
||||
updateDeliveryOrder(this.detailData).then(response => {
|
||||
this.$message.success("更新成功");
|
||||
this.detail = false;
|
||||
this.fileOpen = false;
|
||||
// 刷新父组件数据
|
||||
this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
handleDelete (row) {
|
||||
this.$modal.confirm('确认删除该送货单吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.buttonLoading = true;
|
||||
delDeliveryOrder(row.orderId).then(response => {
|
||||
this.$message.success("删除成功");
|
||||
this.detail = false;
|
||||
// 刷新父组件数据
|
||||
this.$emit('refresh-data');
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
// 处理打印
|
||||
handlePrint (row) {
|
||||
this.buttonLoading = true;
|
||||
// 获取发货单据明细
|
||||
listDeliveryOrderDetail({ orderId: row.orderId, pageSize: 999 }).then(response => {
|
||||
// 通过事件将数据传递给父组件
|
||||
this.$emit('print', {
|
||||
waybill: row,
|
||||
waybillDetails: response.rows || []
|
||||
});
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
731
ruoyi-ui/src/views/oa/workshop/delivery/components/Printer.vue
Normal file
731
ruoyi-ui/src/views/oa/workshop/delivery/components/Printer.vue
Normal file
@@ -0,0 +1,731 @@
|
||||
<!-- 收货单组件 -->
|
||||
<template>
|
||||
<div>
|
||||
<div class="waybill-container" ref="waybillRef">
|
||||
<!-- 头部信息 -->
|
||||
<div class="waybill-title">
|
||||
<h2>发货单据</h2>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">编号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.deliveryOrderNo" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">合同号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.contractNo" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">用户合同号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.userContractNo" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">采购订单号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.procurementNo" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">送货目的地:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.deliveryDestination" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">送货地址:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.deliveryAddress" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">接收人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.receiver" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">接收人手机号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.receiverPhone" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">供应商名称:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.supplierFullname" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">供应商联系人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.supplierContact" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">供应商手机号:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.supplierPhone" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">收货单位:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.receivingCompany" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 33%">
|
||||
<span class="label">实际接收人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.actualReceiver" />
|
||||
</div>
|
||||
<div class="header-center" style="width: 33%">
|
||||
<span class="label">施工单位:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.constructionCompany" />
|
||||
</div>
|
||||
<div class="header-right" style="width: 33%">
|
||||
<span class="label">创建人:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.createBy" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="waybill-header">
|
||||
<div class="header-left" style="width: 100%">
|
||||
<span class="label">送货备注:</span>
|
||||
<input type="text" class="editable-input transparent-input" v-model="localWaybill.deliveryRemark"
|
||||
style="width: calc(100% - 80px)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<table class="waybill-table" v-loading="loading" loading-text="加载中...">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>分箱编号</th>
|
||||
<th>工位号</th>
|
||||
<th>设备名称</th>
|
||||
<th>型号规格</th>
|
||||
<th>数量</th>
|
||||
<th>单位</th>
|
||||
<th>包装类型</th>
|
||||
<th>净重(kg)</th>
|
||||
<th>毛重(kg)</th>
|
||||
<th>体积(m³)</th>
|
||||
<th>箱尺寸(mm,长*宽*高)</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 无明细提示 -->
|
||||
<tr v-if="localWaybillDetails.length === 0">
|
||||
<td colspan="13" class="no-data">
|
||||
<div class="no-data-content">
|
||||
<el-empty description="暂无发货单据明细" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 明细数据 -->
|
||||
<tr v-for="(item, index) in localWaybillDetails" :key="index">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.boxNoTotal" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.equipmentStationNo" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.equipmentName" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.modelSpec" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.quantity" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.unit" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.packagingType" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.netWeight" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.grossWeight" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.volume" />
|
||||
</td>
|
||||
<td><input type="text" class="table-input transparent-input" v-model="item.boxSize" />
|
||||
</td>
|
||||
<td>
|
||||
<div class="table-cell-with-action">
|
||||
<input type="text" class="table-input transparent-input" v-model="item.detailRemark" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 签名栏 -->
|
||||
<div class="waybill-footer">
|
||||
<div class="footer-item inline">
|
||||
<span class="label">制单:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" v-model="localWaybill.createBy" />
|
||||
</div>
|
||||
<div class="footer-item inline">
|
||||
<span class="label">审核:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" />
|
||||
</div>
|
||||
<div class="footer-item inline">
|
||||
<span class="label">确认:</span>
|
||||
<input type="text" class="editable-input signature-input transparent-input" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 操作按钮 -->
|
||||
<div class="waybill-actions">
|
||||
<el-button type="primary" @click="saveAsImage">保存为图片</el-button>
|
||||
<el-button type="success" @click="printWaybill">打印</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import domtoimage from 'dom-to-image';
|
||||
import printJS from 'print-js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
// 组件接收完整的发货单据内容, 渲染发货单据,这个面板内包括一个保存为图片和一个打印按钮
|
||||
waybill: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
},
|
||||
waybillDetails: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 本地可编辑的发货单据数据
|
||||
localWaybill: {
|
||||
deliveryOrderNo: '',
|
||||
contractNo: '',
|
||||
userContractNo: '',
|
||||
procurementNo: '',
|
||||
deliveryDestination: '',
|
||||
deliveryAddress: '',
|
||||
receiver: '',
|
||||
receiverPhone: '',
|
||||
supplierFullname: '',
|
||||
supplierContact: '',
|
||||
supplierPhone: '',
|
||||
receivingCompany: '',
|
||||
actualReceiver: '',
|
||||
constructionCompany: '',
|
||||
deliveryRemark: '',
|
||||
createTime: '',
|
||||
createBy: ''
|
||||
},
|
||||
// 本地可编辑的发货单据明细
|
||||
localWaybillDetails: [],
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听props变化,更新本地数据
|
||||
waybill: {
|
||||
handler (newVal) {
|
||||
if (newVal) {
|
||||
this.localWaybill = {
|
||||
deliveryOrderNo: newVal.deliveryOrderNo || '',
|
||||
contractNo: newVal.contractNo || '',
|
||||
userContractNo: newVal.userContractNo || '',
|
||||
procurementNo: newVal.procurementNo || '',
|
||||
deliveryDestination: newVal.deliveryDestination || '',
|
||||
deliveryAddress: newVal.deliveryAddress || '',
|
||||
receiver: newVal.receiver || '',
|
||||
receiverPhone: newVal.receiverPhone || '',
|
||||
supplierFullname: newVal.supplierFullname || '',
|
||||
supplierContact: newVal.supplierContact || '',
|
||||
supplierPhone: newVal.supplierPhone || '',
|
||||
receivingCompany: newVal.receivingCompany || '',
|
||||
actualReceiver: newVal.actualReceiver || '',
|
||||
constructionCompany: newVal.constructionCompany || '',
|
||||
deliveryRemark: newVal.deliveryRemark || '',
|
||||
createTime: newVal.createTime ? this.formatDate(newVal.createTime) : '',
|
||||
createBy: newVal.createBy || ''
|
||||
};
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
waybillDetails: {
|
||||
handler (newVal) {
|
||||
if (newVal && Array.isArray(newVal)) {
|
||||
this.localWaybillDetails = newVal;
|
||||
} else {
|
||||
this.localWaybillDetails = [];
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 格式化日期
|
||||
formatDate (dateStr) {
|
||||
if (dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// 保存为图片
|
||||
saveAsImage () {
|
||||
const node = this.$refs.waybillRef;
|
||||
// 确保容器在保存图片时能完整显示所有内容
|
||||
const originalWidth = node.style.width;
|
||||
const originalOverflow = node.style.overflow;
|
||||
|
||||
// 临时调整容器样式,确保所有内容可见
|
||||
node.style.width = 'auto';
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// 获取实际内容宽度
|
||||
const contentWidth = node.scrollWidth;
|
||||
const contentHeight = node.scrollHeight;
|
||||
|
||||
domtoimage.toPng(node, {
|
||||
width: contentWidth,
|
||||
height: contentHeight,
|
||||
style: {
|
||||
width: `${contentWidth}px`,
|
||||
height: `${contentHeight}px`,
|
||||
overflow: 'visible'
|
||||
}
|
||||
})
|
||||
.then(dataUrl => {
|
||||
const link = document.createElement('a');
|
||||
link.download = `发货单据_${this.waybill.deliveryOrderNo || this.waybill.orderId || Date.now()}.png`;
|
||||
link.href = dataUrl;
|
||||
link.click();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('保存图片失败:', error);
|
||||
this.$message.error('保存图片失败');
|
||||
})
|
||||
.finally(() => {
|
||||
// 恢复原始样式
|
||||
node.style.width = originalWidth;
|
||||
node.style.overflow = originalOverflow;
|
||||
});
|
||||
},
|
||||
// 打印发货单据
|
||||
printWaybill () {
|
||||
const node = this.$refs.waybillRef;
|
||||
// 确保容器在打印时能完整显示所有内容
|
||||
const originalWidth = node.style.width;
|
||||
const originalOverflow = node.style.overflow;
|
||||
|
||||
// 临时调整容器样式,确保所有内容可见
|
||||
node.style.width = 'auto';
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// 获取实际内容宽度
|
||||
const contentWidth = node.scrollWidth;
|
||||
|
||||
printJS({
|
||||
printable: node,
|
||||
maxWidth: contentWidth,
|
||||
type: 'html',
|
||||
scanStyles: true,
|
||||
style: `
|
||||
@page { size: A4 landscape; margin: 1cm; }
|
||||
.waybill-container { width: 100%; max-width: none; overflow: visible; }
|
||||
.waybill-table { width: 100%; table-layout: auto; }
|
||||
`,
|
||||
targetStyles: ['*']
|
||||
});
|
||||
|
||||
// 恢复原始样式
|
||||
setTimeout(() => {
|
||||
node.style.width = originalWidth;
|
||||
node.style.overflow = originalOverflow;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.waybill-container {
|
||||
width: 1000px;
|
||||
max-width: none;
|
||||
min-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
font-family: SimSun, serif;
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.waybill-title {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.waybill-title h2 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.waybill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.header-left,
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.date-label {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
/* 可编辑输入框样式 */
|
||||
.editable-input {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-family: SimSun, serif;
|
||||
outline: none;
|
||||
transition: all 0.2s;
|
||||
border-bottom: 1px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.editable-input:focus {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.date-input {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* 透明输入框样式 */
|
||||
.transparent-input {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.transparent-input:focus {
|
||||
border-bottom-color: #409eff;
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.waybill-table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
font-size: 12px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.waybill-table th,
|
||||
.waybill-table td {
|
||||
border: 1px solid #000;
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 表格列宽设置 */
|
||||
.waybill-table th:nth-child(1),
|
||||
.waybill-table td:nth-child(1) {
|
||||
width: 40px;
|
||||
/* 序号 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(2),
|
||||
.waybill-table td:nth-child(2) {
|
||||
width: 80px;
|
||||
/* 分箱编号/总箱件数 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(3),
|
||||
.waybill-table td:nth-child(3) {
|
||||
width: 40px;
|
||||
/* 设备工位号 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(4),
|
||||
.waybill-table td:nth-child(4) {
|
||||
width: 100px;
|
||||
/* 设备名称 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(5),
|
||||
.waybill-table td:nth-child(5) {
|
||||
width: 100px;
|
||||
/* 型号规格 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(6),
|
||||
.waybill-table td:nth-child(6) {
|
||||
width: 40px;
|
||||
/* 数量 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(7),
|
||||
.waybill-table td:nth-child(7) {
|
||||
width: 40px;
|
||||
/* 单位 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(8),
|
||||
.waybill-table td:nth-child(8) {
|
||||
width: 80px;
|
||||
/* 包装类型 */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(9),
|
||||
.waybill-table td:nth-child(9) {
|
||||
width: 60px;
|
||||
/* 净重(kg) */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(10),
|
||||
.waybill-table td:nth-child(10) {
|
||||
width: 60px;
|
||||
/* 毛重(kg) */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(11),
|
||||
.waybill-table td:nth-child(11) {
|
||||
width: 60px;
|
||||
/* 体积(m³) */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(12),
|
||||
.waybill-table td:nth-child(12) {
|
||||
width: 120px;
|
||||
/* 箱尺寸(mm,长*宽*高) */
|
||||
}
|
||||
|
||||
.waybill-table th:nth-child(13),
|
||||
.waybill-table td:nth-child(13) {
|
||||
width: 100px;
|
||||
/* 备注 */
|
||||
}
|
||||
|
||||
.waybill-table th {
|
||||
background-color: #f5f7fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.waybill-table tr:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/* 表格输入框样式 */
|
||||
.table-input {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.table-input:focus {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
/* 无数据样式 */
|
||||
.no-data {
|
||||
height: 200px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.no-data-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 表格单元格操作区 */
|
||||
.table-cell-with-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 备注样式 */
|
||||
.waybill-remarks {
|
||||
margin-bottom: 30px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.waybill-remarks p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
/* 底部签名样式 */
|
||||
.waybill-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.waybill-pickup-location {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.waybill-pickup-location label {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.footer-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.footer-item.inline {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer-item .label {
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.signature-input {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.full-input {
|
||||
/* 占满本行的剩余空间,父容器不是flex */
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.waybill-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* 添加明细按钮 */
|
||||
.add-detail-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 打印样式 */
|
||||
@media print {
|
||||
.waybill-container {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.waybill-header {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.waybill-footer {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.waybill-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.waybill-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.waybill-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.waybill-table th,
|
||||
.waybill-table td {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.waybill-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.footer-item.inline {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: auto;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
483
ruoyi-ui/src/views/oa/workshop/delivery/index.vue
Normal file
483
ruoyi-ui/src/views/oa/workshop/delivery/index.vue
Normal file
@@ -0,0 +1,483 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧项目列表 -->
|
||||
<el-col :span="6">
|
||||
<ProjectTree v-model="currentProject" @select="handleProjectSelect" :list="projectList">
|
||||
<template slot-scope="scope">
|
||||
<!-- 如果scope.data.processCardCount > 0改为蓝色文本#2bf -->
|
||||
<span
|
||||
:style="{ 'color': scope.data.processCardCount > 0 ? '#2bf' : '', display: 'flex', alignItems: 'center' }">
|
||||
<span style="margin-right: 1px;">[{{ scope.data.processCardCount || 0
|
||||
}}]</span>
|
||||
<span
|
||||
style="text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; width: calc(100% - 20px);">{{
|
||||
scope.data.projectName }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</ProjectTree>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧发货明细 -->
|
||||
<el-col :span="18">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ currentProject.projectName || '请选择项目' }}</span>
|
||||
<span v-if="currentProject.projectCode" style="margin-left: 5px; color: #2bf">[{{ currentProject.projectCode
|
||||
}}]</span>
|
||||
<span v-if="currentProject.projectNum" style="margin-left: 5px;">({{ currentProject.projectNum
|
||||
}})</span>
|
||||
<div style="float: right;">
|
||||
<el-button v-if="currentProject.projectId" type="text" @click="getList">刷新</el-button>
|
||||
<el-button v-if="currentProject.projectId" type="text" @click="handleAdd">新增单据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!currentProject.projectId" description="请从左侧选择项目" :image-size="200">
|
||||
<el-button type="primary" @click="handleSelectFirstProject" v-if="projectList.length > 0">
|
||||
选择第一个项目
|
||||
</el-button>
|
||||
</el-empty>
|
||||
|
||||
<el-tabs v-else v-model="activeTab" @tab-click="getList">
|
||||
<el-tab-pane label="单据维度" name="master">
|
||||
<MasterTable :list="masterList" :loading="loading" @refresh-data="getList" @print="handlePrint" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="明细维度" name="detail">
|
||||
<DetailTable :list="detailList" :loading="loading" :total="total" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新增发货弹窗 -->
|
||||
<el-drawer :title="title" :visible.sync="open" append-to-body direction="btt" size="90%">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form-item prop="deliveryOrderNo" label="编号">
|
||||
<el-input placeholder="请输入送货单号" v-model="form.deliveryOrderNo" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="contractNo" label="合同号">
|
||||
<el-input placeholder="请输入合同号" v-model="form.contractNo" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="userContractNo" label="用户合同号">
|
||||
<el-input placeholder="请输入用户合同号" v-model="form.userContractNo" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="procurementNo" label="采购订单号">
|
||||
<el-input placeholder="请输入采购订单号" v-model="form.procurementNo" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="deliveryDestination" label="送货目的地">
|
||||
<el-input placeholder="请输入送货目的地" v-model="form.deliveryDestination" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="deliveryAddress" label="送货地址">
|
||||
<el-input placeholder="请输入送货地址" v-model="form.deliveryAddress" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="receiver" label="接收人">
|
||||
<el-input placeholder="请输入接收人" v-model="form.receiver" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="receiverPhone" label="接收人手机号">
|
||||
<el-input placeholder="请输入接收人手机号" v-model="form.receiverPhone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider>明细</el-divider>
|
||||
|
||||
<div>
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button @click="addRow" type="primary">添加</el-button>
|
||||
</div>
|
||||
<el-table :data="form.detailList" style="width: 100%" border stripe v-loading="loading">
|
||||
<el-table-column label="序号" width="50" type="index" />
|
||||
<el-table-column label="分箱编号/总箱件数">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.boxNoTotal" placeholder="请输入分箱编号/总箱件数"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备工位号">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.equipmentStationNo" placeholder="请输入设备工位号"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备名称">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.equipmentName" placeholder="请输入设备名称"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="型号规格">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.modelSpec" placeholder="请输入设备规格"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" width="60">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.quantity" placeholder="请输入数量"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="60">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.unit" placeholder="请输入单位"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="包装类型" width="100">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.packagingType" placeholder="请输入包装类型"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="净重(kg)" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.netWeight" placeholder="请输入净重"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="毛重(kg)" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.grossWeight" placeholder="请输入毛重"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="体积(m³)" width="80">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.volume" placeholder="请输入体积"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="箱尺寸(mm,长*宽*高)" width="150">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.boxSize" placeholder="请输入箱尺寸"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注">
|
||||
<template slot-scope="scope">
|
||||
<input class="table-input" v-model="scope.row.detailRemark" placeholder="请输入备注"></input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="60">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" @click="removeRow(scope.$index)">-</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-row style="margin-top: 10px;">
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierFullname" label="供应商名称">
|
||||
<el-input placeholder="请输入供应商名称" v-model="form.supplierFullname" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierContact" label="供应商联系人">
|
||||
<el-input placeholder="请输入供应商联系人" v-model="form.supplierContact" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="supplierPhone" label="供应商手机号">
|
||||
<el-input placeholder="请输入供应商手机号" v-model="form.supplierPhone" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="receivingCompany" label="收货单位">
|
||||
<el-input placeholder="请输入收货单位" v-model="form.receivingCompany" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="actualReceiver" label="实际接收人">
|
||||
<el-input placeholder="请输入实际接收人" v-model="form.actualReceiver" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item prop="constructionCompany" label="施工单位">
|
||||
<el-input placeholder="请输入施工单位" v-model="form.constructionCompany" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item prop="deliveryRemark" label="送货备注">
|
||||
<el-input type="textarea" placeholder="请输入送货备注" v-model="form.deliveryRemark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="display: flex; justify-content: flex-end;">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">创 建 单 据</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 打印组件 -->
|
||||
<el-dialog title="打印发货单据" :visible.sync="printVisible" width="95%" append-to-body>
|
||||
<Printer :waybill="printData" :waybillDetails="printDetails" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProjectTree from '@/components/fad-service/ProjectList';
|
||||
import DetailTable from './components/DetailTable';
|
||||
import MasterTable from './components/MasterTable';
|
||||
import Printer from './components/Printer.vue';
|
||||
|
||||
import { insertDeliveryOrder, listDeliveryOrder } from "@/api/oa/workshop/deliveryOrder";
|
||||
import { listDeliveryOrderDetailByProjectId } from "@/api/oa/workshop/deliveryOrderDetail";
|
||||
import { listProjectWithCount } from "@/api/oa/workshop/processCard";
|
||||
|
||||
|
||||
export default {
|
||||
name: "ProjectOutWarehouse",
|
||||
components: {
|
||||
ProjectTree,
|
||||
MasterTable,
|
||||
DetailTable,
|
||||
Printer
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 当前选中的项目
|
||||
currentProject: {},
|
||||
// 项目列表
|
||||
projectList: [],
|
||||
// 发货列表
|
||||
outWareHouseList: [],
|
||||
// 新增弹窗
|
||||
open: false,
|
||||
// 标题
|
||||
title: "",
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 库存数据
|
||||
oaWarehouseList: [],
|
||||
// 选中的行
|
||||
selectedRows: [],
|
||||
// 当前激活的tab
|
||||
activeTab: 'master',
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
projectId: undefined,
|
||||
type: 0
|
||||
},
|
||||
// 表单参数
|
||||
form: {
|
||||
projectId: '',
|
||||
warehouseList: []
|
||||
},
|
||||
detailList: [],
|
||||
masterList: [],
|
||||
// 打印相关
|
||||
printVisible: false,
|
||||
printData: {},
|
||||
printDetails: [],
|
||||
rules: {
|
||||
deliveryOrderNo: [
|
||||
{ required: true, message: '请输入送货单号', trigger: 'blur' }
|
||||
],
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 项目选择
|
||||
handleProjectSelect (data) {
|
||||
this.currentProject = data;
|
||||
this.queryParams.projectId = data.projectId;
|
||||
this.getList();
|
||||
},
|
||||
// 获取项目列表
|
||||
getProjectList () {
|
||||
listProjectWithCount().then(res => {
|
||||
this.projectList = res.data;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 生成编号
|
||||
// 年-月-日-时-分-秒-毫秒
|
||||
generateDeliveryOrderNo () {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hour = String(now.getHours()).padStart(2, '0');
|
||||
const minute = String(now.getMinutes()).padStart(2, '0');
|
||||
const second = String(now.getSeconds()).padStart(2, '0');
|
||||
const millisecond = String(now.getMilliseconds()).padStart(3, '0');
|
||||
return `${year}-${month}-${day}-${hour}-${minute}-${second}-${millisecond}`;
|
||||
},
|
||||
// 获取发货列表
|
||||
getList () {
|
||||
if (!this.currentProject.projectId) return;
|
||||
this.loading = true;
|
||||
if (this.activeTab === 'master') {
|
||||
listDeliveryOrder(this.queryParams).then(res => {
|
||||
this.masterList = res.rows;
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
listDeliveryOrderDetailByProjectId(this.queryParams.projectId).then(res => {
|
||||
this.detailList = res.data;
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
// 添加新行
|
||||
addRow () {
|
||||
this.form.detailList.push({
|
||||
boxNoTotal: '',
|
||||
equipmentName: '',
|
||||
equipmentStationNo: '',
|
||||
modelSpec: '',
|
||||
quantity: 0,
|
||||
unit: '',
|
||||
packagingType: '',
|
||||
netWeight: 0,
|
||||
grossWeight: 0,
|
||||
volume: 0,
|
||||
boxSize: '',
|
||||
detailRemark: '',
|
||||
});
|
||||
},
|
||||
// 删除选中行
|
||||
removeRow (index) {
|
||||
this.form.detailList.splice(index, 1);
|
||||
},
|
||||
|
||||
// 新增
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加发货单据";
|
||||
this.form.projectId = this.currentProject.projectId;
|
||||
this.form.deliveryOrderNo = this.generateDeliveryOrderNo();
|
||||
},
|
||||
// 取消
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 重置表单
|
||||
reset () {
|
||||
this.form = {
|
||||
projectId: undefined,
|
||||
deliveryOrderNo: '',
|
||||
detailList: []
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
// 提交表单
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
this.form.detailList.forEach((item, index) => {
|
||||
item.sequence = index + 1;
|
||||
});
|
||||
insertDeliveryOrder(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择第一个项目
|
||||
handleSelectFirstProject () {
|
||||
if (this.projectList.length > 0) {
|
||||
console.log(this.projectList[0], '选择第一个项目');
|
||||
this.handleProjectSelect(this.projectList[0]);
|
||||
}
|
||||
},
|
||||
// 处理打印
|
||||
handlePrint (data) {
|
||||
this.printData = data.waybill;
|
||||
this.printDetails = data.waybillDetails;
|
||||
this.printVisible = true;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.project-info {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 修改滚动条样式 */
|
||||
:deep(.el-card__body) {
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
}
|
||||
|
||||
:deep(.el-card__body::-webkit-scrollbar) {
|
||||
display: none;
|
||||
/* Chrome, Safari, Opera */
|
||||
}
|
||||
|
||||
/* 修改表格滚动条样式 */
|
||||
:deep(.el-table__body-wrapper) {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
:deep(.el-table__body-wrapper::-webkit-scrollbar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 添加box-card滚动条样式 */
|
||||
.box-card {
|
||||
height: calc(100vh - 120px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.table-input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user