Browse Source

2026-01-23

1、收货入库任务通知、拣货出库任务通知页面在【下达】时,要校验下方明细必须有数据,且所有明细的仓库、关联单号、关联单行号必填
2、领料申请管理页面在【提交】【下达】时都要校验下方材料明细的仓库必填
3、调整文件预览
master
fengyuan_yang 1 month ago
parent
commit
d6c71dc292
  1. 414
      src/utils/filePreview.js
  2. 24
      src/views/modules/orderIssure/soIssueNotify/searchIssureNotify.vue
  3. 21
      src/views/modules/qc/inboundNotification.vue
  4. 21
      src/views/modules/qc/outboundNotification.vue

414
src/utils/filePreview.js

@ -1,15 +1,16 @@
/**
* 文件预览工具类
* 支持图片视频PDFTXTExcelWordPPT等格式的预览
* 支持图片视频音频PDFTXTExcelWordPPT等格式的预览
*/
import XLSX from 'xlsx'
// 文件类型分类
const FILE_TYPES = {
image: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
video: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm'],
image: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'],
video: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv', 'ogg'],
audio: ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma'],
pdf: ['pdf'],
txt: ['txt', 'log', 'json', 'xml', 'csv'],
txt: ['txt', 'log', 'json', 'xml', 'csv', 'html', 'htm', 'css', 'js'],
excel: ['xls', 'xlsx'],
word: ['doc', 'docx'],
ppt: ['ppt', 'pptx']
@ -40,12 +41,14 @@ export function getFileType(fileName) {
export function getMimeType(suffix) {
const mimeMap = {
// 图片
jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', bmp: 'image/bmp', webp: 'image/webp',
jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', gif: 'image/gif', bmp: 'image/bmp', webp: 'image/webp', svg: 'image/svg+xml', ico: 'image/x-icon',
// 视频
mp4: 'video/mp4', avi: 'video/x-msvideo', mov: 'video/quicktime', wmv: 'video/x-ms-wmv', flv: 'video/x-flv', webm: 'video/webm',
mp4: 'video/mp4', avi: 'video/x-msvideo', mov: 'video/quicktime', wmv: 'video/x-ms-wmv', flv: 'video/x-flv', webm: 'video/webm', mkv: 'video/x-matroska', ogg: 'video/ogg',
// 音频
mp3: 'audio/mpeg', wav: 'audio/wav', flac: 'audio/flac', aac: 'audio/aac', m4a: 'audio/mp4', wma: 'audio/x-ms-wma',
// 文档
pdf: 'application/pdf',
txt: 'text/plain', log: 'text/plain', json: 'application/json', xml: 'application/xml', csv: 'text/csv',
txt: 'text/plain', log: 'text/plain', json: 'application/json', xml: 'application/xml', csv: 'text/csv', html: 'text/html', htm: 'text/html', css: 'text/css', js: 'application/javascript',
// Office
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
@ -71,6 +74,345 @@ export function blobToArrayBuffer(blob) {
})
}
/**
* 预览图片文件
* @param {Blob} blobData - 文件数据
* @param {String} fileName - 文件名
*/
export function previewImage(blobData, fileName) {
try {
const fileURL = URL.createObjectURL(blobData)
const previewWindow = window.open('', '_blank')
if (!previewWindow) {
// 如果弹窗被阻止,尝试直接打开
window.open(fileURL, '_blank')
return true
}
previewWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片预览 - ${fileName}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: #1a1a1a;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 12px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.header h2 { font-size: 16px; font-weight: 500; }
.toolbar { display: flex; gap: 10px; }
.toolbar button {
background: rgba(255,255,255,0.2);
border: none;
color: white;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.toolbar button:hover { background: rgba(255,255,255,0.3); }
.image-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
overflow: auto;
}
.image-container img {
max-width: 100%;
max-height: calc(100vh - 80px);
object-fit: contain;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
transition: transform 0.3s;
}
</style>
</head>
<body>
<div class="header">
<h2>📷 ${fileName}</h2>
<div class="toolbar">
<button onclick="zoomIn()">放大 +</button>
<button onclick="zoomOut()">缩小 -</button>
<button onclick="resetZoom()">重置</button>
<button onclick="downloadImage()">下载</button>
</div>
</div>
<div class="image-container">
<img id="previewImg" src="${fileURL}" alt="${fileName}">
</div>
<script>
var scale = 1;
var img = document.getElementById('previewImg');
function zoomIn() { scale = Math.min(scale + 0.25, 5); img.style.transform = 'scale(' + scale + ')'; }
function zoomOut() { scale = Math.max(scale - 0.25, 0.25); img.style.transform = 'scale(' + scale + ')'; }
function resetZoom() { scale = 1; img.style.transform = 'scale(1)'; }
function downloadImage() {
var a = document.createElement('a');
a.href = '${fileURL}';
a.download = '${fileName}';
a.click();
}
<\/script>
</body>
</html>
`)
previewWindow.document.close()
return true
} catch (error) {
console.error('图片预览失败:', error)
return false
}
}
/**
* 预览视频文件
* @param {Blob} blobData - 文件数据
* @param {String} fileName - 文件名
* @param {String} mimeType - MIME类型
*/
export function previewVideo(blobData, fileName, mimeType) {
try {
const fileURL = URL.createObjectURL(blobData)
const previewWindow = window.open('', '_blank')
if (!previewWindow) {
window.open(fileURL, '_blank')
return true
}
previewWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>视频播放 - ${fileName}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: #1a1a1a;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.header {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
color: white;
padding: 12px 20px;
}
.header h2 { font-size: 16px; font-weight: 500; }
.video-container {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
video {
max-width: 100%;
max-height: calc(100vh - 80px);
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<div class="header">
<h2>🎬 ${fileName}</h2>
</div>
<div class="video-container">
<video controls autoplay>
<source src="${fileURL}" type="${mimeType}">
您的浏览器不支持视频播放
</video>
</div>
</body>
</html>
`)
previewWindow.document.close()
return true
} catch (error) {
console.error('视频预览失败:', error)
return false
}
}
/**
* 预览音频文件
* @param {Blob} blobData - 文件数据
* @param {String} fileName - 文件名
* @param {String} mimeType - MIME类型
*/
export function previewAudio(blobData, fileName, mimeType) {
try {
const fileURL = URL.createObjectURL(blobData)
const previewWindow = window.open('', '_blank')
if (!previewWindow) {
window.open(fileURL, '_blank')
return true
}
previewWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>音频播放 - ${fileName}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.audio-card {
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 40px;
text-align: center;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
}
.icon { font-size: 64px; margin-bottom: 20px; }
.title { color: white; font-size: 18px; margin-bottom: 30px; word-break: break-all; max-width: 400px; }
audio { width: 350px; }
</style>
</head>
<body>
<div class="audio-card">
<div class="icon">🎵</div>
<div class="title">${fileName}</div>
<audio controls autoplay>
<source src="${fileURL}" type="${mimeType}">
您的浏览器不支持音频播放
</audio>
</div>
</body>
</html>
`)
previewWindow.document.close()
return true
} catch (error) {
console.error('音频预览失败:', error)
return false
}
}
/**
* 预览PDF文件
* @param {Blob} blobData - 文件数据
* @param {String} fileName - 文件名
*/
export function previewPDF(blobData, fileName) {
try {
const fileURL = URL.createObjectURL(blobData)
const previewWindow = window.open('', '_blank')
if (!previewWindow) {
window.open(fileURL, '_blank')
return true
}
previewWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>PDF预览 - ${fileName}</title>
<style>
* { margin: 0; padding: 0; }
body { height: 100vh; }
iframe { width: 100%; height: 100%; border: none; }
</style>
</head>
<body>
<iframe src="${fileURL}"></iframe>
</body>
</html>
`)
previewWindow.document.close()
return true
} catch (error) {
console.error('PDF预览失败:', error)
return false
}
}
/**
* 预览文本文件
* @param {Blob} blobData - 文件数据
* @param {String} fileName - 文件名
*/
export async function previewText(blobData, fileName) {
try {
const text = await blobData.text()
const previewWindow = window.open('', '_blank')
if (!previewWindow) {
throw new Error('无法打开预览窗口')
}
// 转义HTML特殊字符
const escapedText = text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
previewWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文本预览 - ${fileName}</title>
<style>
body {
font-family: 'Consolas', 'Monaco', monospace;
padding: 20px;
background: #1e1e1e;
color: #d4d4d4;
line-height: 1.6;
}
.header {
background: #333;
margin: -20px -20px 20px -20px;
padding: 12px 20px;
color: #fff;
}
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="header">📄 ${fileName}</div>
<pre>${escapedText}</pre>
</body>
</html>
`)
previewWindow.document.close()
return true
} catch (error) {
console.error('文本预览失败:', error)
return false
}
}
/**
* 预览Excel文件
* @param {ArrayBuffer} arrayBuffer - 文件数据ArrayBuffer格式
@ -208,6 +550,46 @@ export async function previewFile(data, fileName) {
}
switch (fileType) {
case 'image':
// 图片预览 - 在新窗口中显示带工具栏的预览
if (previewImage(blobData, fileName)) {
return { success: true, type: 'image' }
} else {
throw new Error('图片预览失败')
}
case 'video':
// 视频预览 - 在新窗口中播放
if (previewVideo(blobData, fileName, mimeType)) {
return { success: true, type: 'video' }
} else {
throw new Error('视频预览失败')
}
case 'audio':
// 音频预览 - 在新窗口中播放
if (previewAudio(blobData, fileName, mimeType)) {
return { success: true, type: 'audio' }
} else {
throw new Error('音频预览失败')
}
case 'pdf':
// PDF预览
if (previewPDF(blobData, fileName)) {
return { success: true, type: 'pdf' }
} else {
throw new Error('PDF预览失败')
}
case 'txt':
// 文本文件预览
if (await previewText(blobData, fileName)) {
return { success: true, type: 'txt' }
} else {
throw new Error('文本预览失败')
}
case 'excel':
// Excel文件需要ArrayBuffer格式
arrayBufferData = await blobToArrayBuffer(blobData)
@ -220,21 +602,12 @@ export async function previewFile(data, fileName) {
case 'word':
// Word文件下载后打开
previewWord(blobData, fileName)
return { success: true, type: 'word', message: '文件已下载,请使用本地软件打开' }
return { success: true, type: 'word', message: 'Word文件已下载,请使用本地软件打开' }
case 'ppt':
// PPT文件下载后打开
previewPPT(blobData, fileName)
return { success: true, type: 'ppt', message: '文件已下载,请使用本地软件打开' }
case 'image':
case 'video':
case 'pdf':
case 'txt':
// 这些类型浏览器原生支持,创建Blob URL后打开
const fileURL = URL.createObjectURL(blobData)
window.open(fileURL, '_blank')
return { success: true, type: fileType }
return { success: true, type: 'ppt', message: 'PPT文件已下载,请使用本地软件打开' }
default:
// 未知类型,下载处理
@ -256,6 +629,11 @@ export default {
getFileSuffix,
getFileType,
getMimeType,
previewImage,
previewVideo,
previewAudio,
previewPDF,
previewText,
previewExcel,
previewWord,
previewPPT,

24
src/views/modules/orderIssure/soIssueNotify/searchIssureNotify.vue

@ -3770,6 +3770,17 @@
return false
}
//
for (let i = 0; i < this.dataList3.length; i++) {
const material = this.dataList3[i]
if (!material.warehouseId && !material.warehouseName) {
this.$alert(`${i + 1}行材料明细的仓库不能为空!`, '提示', {
confirmButtonText: '确定'
})
return false
}
}
// ""
let inData = {
site: row.site,
@ -3808,12 +3819,23 @@
console.log('材料明细数据:', this.dataList3);
if(this.dataList2.length==0 || this.dataList3.length==0){
this.$alert('没有明细记录无法下达!!', '提示', {
this.$alert('没有明细记录无法下达!', '提示', {
confirmButtonText: '确定'
})
return false
}
//
for (let i = 0; i < this.dataList3.length; i++) {
const material = this.dataList3[i]
if (!material.warehouseId && !material.warehouseName) {
this.$alert(`${i + 1}行材料明细的仓库不能为空!`, '提示', {
confirmButtonText: '确定'
})
return false
}
}
xiadaNotify(row).then(({data})=>{
if (data.code === 0) {
//

21
src/views/modules/qc/inboundNotification.vue

@ -1546,6 +1546,27 @@
this.$message.warning('该类型单据不能编辑!')
return
}
//
if (!this.detailList || this.detailList.length === 0) {
this.$message.warning('入库明细不能为空,请先添加明细!')
return
}
//
for (let i = 0; i < this.detailList.length; i++) {
const detail = this.detailList[i]
if (!detail.inWarehouse) {
this.$message.warning(`${i + 1}行明细的入库仓库不能为空!`)
return
}
if (!detail.relatedOrderNo) {
this.$message.warning(`${i + 1}行明细的关联单号不能为空!`)
return
}
if (!detail.relatedOrderLineNo) {
this.$message.warning(`${i + 1}行明细的关联单行号不能为空!`)
return
}
}
issueInboundNotification(row).then(({data}) => {
if (data && data.code === 0) {
this.getDataList()

21
src/views/modules/qc/outboundNotification.vue

@ -1578,6 +1578,27 @@
this.$message.warning('销售出库单据不能编辑!')
return
}
//
if (!this.detailList || this.detailList.length === 0) {
this.$message.warning('出库明细不能为空,请先添加明细!')
return
}
//
for (let i = 0; i < this.detailList.length; i++) {
const detail = this.detailList[i]
if (!detail.outWarehouse) {
this.$message.warning(`${i + 1}行明细的出库仓库不能为空!`)
return
}
if (!detail.relatedOrderNo) {
this.$message.warning(`${i + 1}行明细的关联单号不能为空!`)
return
}
if (!detail.relatedOrderLineNo) {
this.$message.warning(`${i + 1}行明细的关联单行号不能为空!`)
return
}
}
issueOutboundNotification(row).then(({data}) => {
if (data && data.code === 0) {
this.getDataList()

Loading…
Cancel
Save