Compare commits

...

2 Commits

Author SHA1 Message Date
zxy
d40c3ceff7 Merge remote-tracking branch 'origin/main' 2026-03-27 10:31:46 +08:00
zxy
f711f88abc feat(storage): 添加入库单打印功能 2026-03-26 16:21:42 +08:00
2 changed files with 410 additions and 4 deletions

View File

@ -104,10 +104,27 @@
</ContentWrap>
<!-- 列表 -->
<el-card class="hl-card-info">
<template #header>
<div class="hl-card-info-icona"></div><span class="hl-card-info-text">订单明细</span>
<el-button style="margin-left: 20px" @click="isPrint" type="primary" size="large">打印</el-button>
<!-- <el-button-->
<!-- style="margin-left: 20px"-->
<!-- type="success"-->
<!-- plain-->
<!-- @click="handleExport"-->
<!-- :loading="exportLoading"-->
<!-- >-->
<!-- <Icon icon="ep:download" class="mr-5px" /> 导出EXCEL-->
<!-- </el-button>-->
</template>
<ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true" class="hl-table">
<!-- <el-table-column label="主键id" align="center" prop="id" /> -->
<el-table-column type="index" width="100" fixed label="序号" align="center" />
<el-table
v-loading="loading" :data="list" class="hl-table"
ref="multipleTableRef"
@selection-change="handleSelectionChange" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="70" fixed="left" />
<el-table-column type="index" width="70" fixed label="序号" align="center" />
<el-table-column label="入库单号" align="center" prop="stockNo" min-width="210" fixed />
<!-- <template #default="scope">-->
<!-- <el-button text type="primary" @click="openDetail('review',scope.row.id)">-->
@ -126,7 +143,7 @@
{{ warehouseList.find((tag) => tag.id === scope.row.whId)?.whName }}
</template>
</el-table-column>
<el-table-column label="即入即出" align="center" prop="inOutFlag" min-width="90" >
<el-table-column label="即入即出" align="center" prop="inOutFlag" min-width="100" >
<template #default="scope">
<el-tag :type="scope.row.inOutFlag ? 'success' : 'info'">
{{ scope.row.inOutFlag ? '是' : '否' }}
@ -203,6 +220,8 @@
/>
</ContentWrap>
</el-card>
<PrintRef ref="printRef" />
</el-card>
</template>
<script setup lang="ts">
@ -214,6 +233,9 @@ import * as UserApi from '@/api/system/user'
import routeParamsCache from '@/utils/routeParamsCache'
import axios from "axios";
import {getAccessToken, getTenantId} from "@/utils/auth";
import {ElButton, ElTable} from "element-plus";
import PrintRef from "./print.vue"
import * as PurchaseOrderApi from "@/api/heli/purchaseorder";
defineOptions({ name: 'Storage' })
const message = useMessage() //
@ -275,6 +297,29 @@ const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const multipleTableRef: any = ref<InstanceType<typeof ElTable>>()
const multipleSelection: any = ref([])
const handleSelectionChange = (val: PurchaseOrderApi.PurchaseOrderVO[]) => {
// isSelected false
list.value.forEach(row => {
row.isSelected = false;
});
// isSelected true
val.forEach(selectedRow => {
//
const targetRow = list.value.find(row => row.id === selectedRow.id);
if (targetRow) {
targetRow.isSelected = true;
}
});
multipleSelection.value = val
}
const tableRowClassName = ({ row }) => {
return row.isSelected ? 'selected-row' : '';
}
/** 导出按钮操作 */
const handleExport = async () => {
if (!queryParams.stockNo){
@ -337,6 +382,43 @@ const openDetail = (active: string, id?: number) => {
router.push({ path: '/inventory/storagedetail', query: { type: active, id: id } })
}
const printRef = ref<InstanceType<typeof PrintRef>>()
const isPrint = async () => {
function formatDate(val) {
if (!val) return ''
const date = new Date(val)
return date.toLocaleDateString()
}
try {
const list = multipleSelection.value|| []; //
if(list.length==0){
message.warning("请选择入库单信息");
return;
}
//
//createTime brief projectSubCode blueprintNo boomName compositionName matSpec purchaseAmount unitPrice estimatedPrice requireTime
const data = list?.map((item,idx) => ({
编号: idx + 1,
入库单号: item.stockNo,
入库类型: getIntDictOptions(DICT_TYPE.HELI_STORAGE_IN_TYPE).find(d => d.value === item.stockInType)?.label || item.stockInType,
上游单号: item.headerNo,
入库仓库: warehouseList.value.find(w => w.id === item.whId)?.whName || item.whId,
即入即出: item.inOutFlag ? '是' : '否',
单据状态: getIntDictOptions(DICT_TYPE.HELI_STORAGE_IN_STATUS).find(d => d.value === item.status)?.label || item.status,
创建时间: formatDate(item.createTime),
提交时间: formatDate(item.keeperTime),
})) || [];
console.log(data)
// openPrintDialog(newVar)
printRef.value?.open(data,"")
} finally {
}
}
/** 初始化 **/
const route = useRoute()
const routeValue = ref('')
@ -376,3 +458,9 @@ onActivated(() => {
getList()
})
</script>
<style>
.selected-row {
background-color: #ffe6cc !important;
}
</style>

View File

@ -0,0 +1,318 @@
<template>
<el-dialog v-model="dialogVisible" title="" width="80%" append-to-body>
<div class="print-wrap">
<div class="header header-grid">
<div class="cell company-title">
<span class="company">杭州合立模具有限公司</span>
<span class="title">入库管理单</span>
</div>
<div class="cell audit"> 审核<span class="sign-space"></span> </div>
<div class="cell handler"> 委外加工经手人<span class="sign-space"></span> </div>
<div class="cell receiver"> 收货人<span class="sign-space"></span> </div>
<div class="cell order-no" v-if="purchaseNo"> 订单编号<span class="sign-space">{{ purchaseNo }}</span> </div>
</div>
<table class="sheet" border="1" cellspacing="0" cellpadding="0">
<!-- 固定列宽总计 100% -->
<colgroup>
<col style="width: 4%" />
<col style="width: 9%" />
<col style="width: 7.5%" />
<col style="width: 10%" />
<col style="width: 5.5%" />
<col style="width: 6%" />
<col style="width: 6%" />
<col style="width: 6%" />
<col style="width: 6%" />
</colgroup>
<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-for="row in rows" :key="row['编号']">
<td>{{ row['编号'] }}</td>
<td class="text-left">{{ row['入库单号'] }}</td>
<td class="text-left">{{ row['入库类型'] }}</td>
<td class="text-left">{{ row['上游单号'] }}</td>
<td class="text-left">{{ row['入库仓库'] }}</td>
<td>{{ row['即入即出'] }}</td>
<td>{{ row['单据状态'] }}</td>
<td>{{ fmtDate(row['创建时间']) }}</td>
<td>{{ fmtDate(row['提交时间']) }}</td>
</tr>
</tbody>
</table>
<div class="footer-sign sign-row">
<div class="sign-item">
<span class="label">合立经手人</span>
<span class="name">{{ duEmpName }}</span>
</div>
<div class="sign-item"> 审核人<span class="sign-space"></span> </div>
<div class="sign-item"> 委外加工经手人<span class="sign-space"></span> </div>
</div>
</div>
<template #footer>
<el-button @click="dialogVisible = false">关闭</el-button>
<el-button type="primary" @click="onPrint">打印</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRoute } from 'vue-router'
import dayjs from 'dayjs'
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore();
const userName = userStore.getUser.nickname//nickname
console.log(userStore,`userStore`);
const dialogVisible = ref(false)
const rows = ref<Record<string, any>[]>([])
const duEmpName = computed(() => userName)
const fmtDate = (v: any) => (v ? dayjs(v).format('YYYY-MM-DD') : '')
const purchaseNo = ref('')
const { query } = useRoute()
import * as PurchaseOrderNoApi from '@/api/heli/purchaseorderno'
/**
*
* @param data 打印数据
* @param str 采购订单号=订单编号 零件采购收货时订单编号为空 零件采购订单管理 不为空
*/
const open = (data: Record<string, any>[],str: string) => {
rows.value = Array.isArray(data) ? data : []
dialogVisible.value = true;
purchaseNo.value = str;
console.log(query.id);
}
defineExpose({ open })
// onPrint()
const onPrint = () => {
const printNode = document.querySelector('.print-wrap') as HTMLElement
if (!printNode) return
const iframe = document.createElement('iframe')
iframe.setAttribute('style', 'width:0;height:0;position:absolute;left:-9999px;top:-9999px;')
document.body.appendChild(iframe)
const doc = iframe.contentWindow!.document
doc.open()
doc.title = ''
doc.write(`
<style>
/* 针式打印纸规格241mm×140mm撕边后222mm */
@page { size: 241mm 140mm; margin: 5mm; }
body {
font-family: "宋体","Arial",sans-serif;//使使ArialArial使sans-serif
color:#000;
margin: 5px;
font-size: 12px;
box-sizing: border-box;
}
/* 打印区域适配撕边后的有效宽度 */
.print-wrap {
width: 100%;
height: 118mm; /* 130mm - 12mm上下边距 */
}
.header.header-grid {
display: flex;
justify-content: flex-start;
align-items: center;
column-gap: 12px;
margin-bottom: 8px;
}
.header.header-grid .company-title {
font-size: 12px;
text-align: left;
display: flex;
align-items: center;
gap: 8px;
}
.header.header-grid .audit,
.header.header-grid .handler,
.header.header-grid .receiver,
.header.header-grid .order-no{
font-size: 12px;
text-align: right;
display: flex;
align-items: center;
justify-content: flex-end;
}
.order-no{
margin-left: 12px;
}
/* 合计行样式 */
.sum-row {
border-top: none;
font-weight: bold;
}
.sum-label {
text-align: right;
font-weight: bold;
}
.sum-value {
font-weight: bold;
}
.sign-space {
display: inline-block;
width: 50px;
height: 18px;
margin-left: 4px;
}
.sheet { width:100%; border-collapse:collapse; font-size:12px; table-layout: fixed; }
.sheet th, .sheet td { border:1.5pt solid #333; padding:4px 0px; text-align:center; line-height:1; vertical-align: middle; }
.sheet thead th { background:#f5f7fa; font-weight:600; }
.sheet .text-left { text-align:left; padding-left:6px; }
.sheet .spec { word-break: break-word; white-space: normal; }
.sheet .num { text-align:right; padding-right:6px; }
.sheet tfoot .sum-label, .sheet tfoot .sum-value { font-weight:700; }
.sheet tfoot .sum-row td { border-top: none; }
.sign-row { display:flex; justify-content:flex-start; margin-top:12px; font-size:12px;gap:30px }
.sign-item { display: flex; align-items: center; }
.sign-item .label { margin-right: 4px; }
</style>
`)
doc.write(printNode.outerHTML)
doc.close()
iframe.contentWindow!.focus()
iframe.contentWindow!.print()
setTimeout(() => document.body.removeChild(iframe), 500)
if(query.id){// id=true,=false
PurchaseOrderNoApi.updateIsPrint(query.id);
}
}
</script>
<style scoped lang="less">
.print-wrap {
/* 预览时也按目标纸张尺寸展示,便于校对 */
width: 100%;
min-height: 140mm;
background: #fff;
}
.header.header-grid {
display: flex;
justify-content: flex-start;
align-items: center;
column-gap: 12px;
}
.header.header-grid .company-title {
display: flex;
align-items: center;
gap: 8px;
}
.header.header-grid .company {
font-weight: bold;
}
.header.header-grid .title {
font-weight: bold;
}
.header.header-grid .audit,
.header.header-grid .handler,
.header.header-grid .receiver
.header.header-grid .order-no{
display: flex;
align-items: center;
justify-content: flex-end;
}
/* 签字空间样式 */
.sign-space {
display: inline-block;
width: 70px;
height: 18px;
margin-left: 4px;
}
.sheet {
margin-top: 8px;
table-layout: fixed;
border-collapse: collapse !important;
border-spacing: 0 !important;
font-size: 12px;
width: 100%;
th,
td {
padding: 6px 4px;
text-align: center;
line-height: 1.3;
vertical-align: middle;
border:1pt solid #000 !important;
}
thead th {
background: #f5f7fa;
font-weight: 600;
}
.text-left {
text-align: left;
padding-left: 6px;
}
.spec {
word-break: break-word;
white-space: normal;
}
.num {
text-align: right;
padding-right: 6px;
}
tfoot .sum-row td {
border-top: none;
}
}
.footer-sign.sign-row {
display: flex;
justify-content: flex-start;
margin-top: 12px;
gap: 30px;
}
.sign-item {
display: flex;
align-items: center;
}
.sign-item .label {
margin-right: 4px;
}
.sign-item .name {
// font-weight: bold;
}
</style>