import QRCode from 'qrcode'; // 二维码生成库
import { ElDialog, ElButton, ElMessage ,ElImage,ElMessageBox } from 'element-plus';// 引入ElMessage,解决提示无反应
import { h,createVNode, render } from 'vue';
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
components: {
//查询界面扩展组件
gridHeader: '',
gridBody: '',
gridFooter: '',
//新建、编辑弹出框扩展组件
modelHeader: '',
modelBody: '',
modelFooter: ''
},
tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
buttons: { view: [{
name: '生成二维码',
type: 'success',
value: '生成二维码',
onClick: async function () {
// 1. 校验选中行
const selectedRows = this.$refs.table.getSelected();
if (selectedRows.length === 0) {
ElMessage.warning('请先选择一行数据');
return;
}
if (selectedRows.length > 1) {
ElMessage.warning('仅支持选择一行数据生成二维码');
return;
}
// 2. 获取单据编号
const newMaterialBarCode = selectedRows[0].newMaterialBarCode;
if (!newMaterialBarCode) {
ElMessage.error('选中的数据中未找到单据编号');
return;
}
// 3. 获取单据明细数据(新增:通过单据编号获取QtyTrans、MaterialCode、MaterialName)
// let detailList = [];
// const loading = ElLoading.service({ text: '获取单据明细中...' });
// try {
// const res = await http.post('/api/DocumentDetail/details', { transNo: transNo });
// // 校验返回格式(必须是数组且至少有一条数据)
// if (!res.status || !Array.isArray(res.data) || res.data.length === 0) {
// throw new Error('未查询到单据明细数据');
// }
// detailList = res.data;
// // 校验每条明细的必填字段
// detailList.forEach((item, index) => {
// if (!item.qtyTrans || !item.materialCode || !item.materialName) {
// throw new Error(`第${index + 1}条明细数据不完整`);
// }
// });
// } catch (err) {
// ElMessage.error(err.message || '获取单据明细失败');
// loading.close();
// return;
// } finally {
// loading.close();
// }
// 4. 生成二维码
let qrCodeUrl = '';
try {
qrCodeUrl = await QRCode.toDataURL(newMaterialBarCode, {
width: 200, // 二维码大小适配布局
margin: 1
});
} catch (err) {
ElMessage.error('二维码生成失败,请重试');
console.error('二维码生成错误:', err);
return;
}
// 5. 创建弹窗
const mountNode = document.createElement('div');
document.body.appendChild(mountNode);
// 添加打印专用样式
const addPrintStyle = () => {
const style = document.createElement('style');
style.id = 'qr-print-style';
style.textContent = `
@media print {
body > *:not(.print-container) { display: none !important; }
.print-container {
position: fixed !important;
top: 50px !important;
left: 50px !important;
width: 90% !important;
height: auto !important;
}
/* 打印内容布局 */
.print-content {
display: flex !important;
flex-direction: column !important;
gap: 20px !important;
}
.qr-wrapper {
align-self: flex-start !important; /* 二维码靠左 */
}
.detail-fields {
display: flex !important;
justify-content: space-around !important; /* 字段均匀分布 */
width: 100% !important;
margin-top: 20px !important;
font-size: 16px !important;
}
.field-item {
text-align: center !important;
padding: 10px !important;
border: 1px solid #eee !important;
border-radius: 4px !important;
width: 30% !important; /* 每个字段占1/3宽度 */
}
.field-label {
font-weight: bold !important;
margin-bottom: 5px !important;
display: block !important;
}
}
`;
document.head.appendChild(style);
return style;
};
// 打印函数
const printQrCode = () => {
// 创建打印容器
const printContainer = document.createElement('div');
printContainer.className = 'print-container';
printContainer.style = 'position:fixed; top:-9999px; left:-9999px;';
document.body.appendChild(printContainer);
// 填充打印内容(布局:二维码左上角 + 三个字段均匀分布)
printContainer.innerHTML = `
单据编号:${newMaterialBarCode}
交易数量
${detailList[0].qtyTrans}
物料编码
${detailList[0].materialCode}
物料名称
${detailList[0].materialName}
`;
// 添加打印样式
const printStyle = addPrintStyle();
// 清理函数(防止重复移除)
const cleanUp = () => {
if (printContainer.parentNode === document.body) {
document.body.removeChild(printContainer);
}
if (printStyle && printStyle.parentNode === document.head) {
document.head.removeChild(printStyle);
}
window.removeEventListener('afterprint', cleanUp);
};
// 监听打印完成
window.addEventListener('afterprint', cleanUp, { once: true });
// 触发打印
window.print();
};
// 弹窗组件(预览布局)
const vnode = createVNode(ElDialog, {
title: '单据二维码及明细',
width: '600px',
modelValue: true,
appendToBody: true,
'onUpdate:modelValue': (isVisible) => {
if (!isVisible) {
const printStyle = document.getElementById('qr-print-style');
if (printStyle && printStyle.parentNode === document.head) {
document.head.removeChild(printStyle);
}
render(null, mountNode);
if (mountNode.parentNode === document.body) {
document.body.removeChild(mountNode);
}
}
}
}, {
default: () => [
// 预览区布局(和打印布局一致)
h('div', { style: { padding: '20px' } }, [
// 二维码预览(左上角)
h('div', { style: { marginBottom: '20px' } }, [
h(ElImage, {
src: qrCodeUrl,
alt: '单据二维码',
style: { width: '200px', height: '200px' }
}),
h('p', { style: { marginTop: '10px', fontSize: '16px' } }, `单据编号:${newMaterialBarCode}`)
]),
// 明细字段预览(均匀分布)
// h('div', { style: { display: 'flex', justifyContent: 'space-around', gap: '10px' } }, [
// h('div', { style: { textAlign: 'center', padding: '10px', border: '1px solid #eee', width: '30%' } }, [
// h('div', { style: { fontWeight: 'bold', marginBottom: '5px' } }, '交易数量'),
// h('div', null, detailList[0].qtyTrans)
// ]),
// h('div', { style: { textAlign: 'center', padding: '10px', border: '1px solid #eee', width: '30%' } }, [
// h('div', { style: { fontWeight: 'bold', marginBottom: '5px' } }, '物料编码'),
// h('div', null, detailList[0].materialCode)
// ]),
// h('div', { style: { textAlign: 'center', padding: '10px', border: '1px solid #eee', width: '30%' } }, [
// h('div', { style: { fontWeight: 'bold', marginBottom: '5px' } }, '物料名称'),
// h('div', null, detailList[0].materialName)
// ])
// ])
])
],
footer: () => h('div', null, [
h(ElButton, {
type: 'default',
onClick: () => {
const printStyle = document.getElementById('qr-print-style');
if (printStyle) document.head.removeChild(printStyle);
render(null, mountNode);
if (mountNode.parentNode === document.body) {
document.body.removeChild(mountNode);
}
}
}, '关闭'),
h(ElButton, {
type: 'primary',
onClick: () => {
ElMessageBox.confirm(
'是否打印该内容?',
'打印确认',
{
confirmButtonText: '确认打印',
cancelButtonText: '取消',
type: 'info'
}
).then(async () => {
try {
// 执行打印并等待完成,捕获可能的错误
await printQrCode();
setTimeout(() => {
render(null, mountNode);
if (mountNode.parentNode === document.body) {
document.body.removeChild(mountNode);
}
}, 500);
} catch (printErr) {
// 显示真实错误(而非“已取消”)
ElMessage.error(`打印失败:${printErr.message || '未知错误'}`);
console.error('打印错误:', printErr);
}
}).catch((err) => {
// 仅用户主动取消时才显示“已取消”
if (err === 'cancel' || err.name === 'CanceledError') {
ElMessage.info('已取消打印');
}
});
}
}, '打印')
])
});
vnode.appContext = this.$.appContext;
render(vnode, mountNode);
}
}], box: [], detail: [] }, //扩展的按钮
methods: {
//下面这些方法可以保留也可以删除
onInit() {
},
onInited() {
//框架初始化配置后
//如果要配置明细表,在此方法操作
//this.detailOptions.columns.forEach(column=>{ });
},
searchBefore(param) {
//界面查询前,可以给param.wheres添加查询参数
//返回false,则不会执行查询
return true;
},
searchAfter(result) {
//查询后,result返回的查询数据,可以在显示到表格前处理表格的值
return true;
},
addBefore(formData) {
//新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
return true;
},
updateBefore(formData) {
//编辑保存前formData为对象,包括明细表、删除行的Id
return true;
},
rowClick({ row, column, event }) {
//查询界面点击行事件
this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
},
modelOpenAfter(row) {
//点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
//(1)判断是编辑还是新建操作: this.currentAction=='Add';
//(2)给弹出框设置默认值
//(3)this.editFormFields.字段='xxx';
//如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
//看不懂就把输出看:console.log(this.editFormOptions)
}
}
};
export default extension;