代码会自动:
阻止原图上传(返回false)
给图片添加水印
上传水印图
更新表单
// 图片上传控件 - 上传前 V8 事件
// 直接处理水印后上传,保留多图片
function getCurrentTime() {
const date = new Date();
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');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
const nowTime = getCurrentTime();
var userName = V8.CurrentUser.Name;
var dept = V8.CurrentUser.DeptName;
const watermarkConfig = {
texts: [
{
text: nowTime,
font: '16px "Microsoft YaHei"',
color: 'rgba(0, 0, 0, 1)',
position: 'bottomLeft',
margin: 20,
offsetX: 150,
offsetY: -120,
rotation: 0
},
{
text: '记录人:' + userName,
font: '20px Arial',
color: 'rgba(0, 0, 0, 1)',
position: 'bottomLeft',
margin: 20,
offsetX: 40,
offsetY: -75,
rotation: 0
},
{
text: '记录部门:' + dept,
font: '20px Arial',
color: 'rgba(0, 0, 0, 1)',
position: 'bottomLeft',
margin: 20,
offsetX: 40,
offsetY: -35,
rotation: 0
}
],
tile: false,
tileSpacing: 200,
logoUrl: 'https://x.pdyou.com/public/itdos/img/sy.png',
logoWidth: 330,
logoHeight: 200,
logoOpacity: 0.8,
logoPosition: 'bottomLeft',
logoMargin: 20,
logoOffsetX: 0,
logoOffsetY: 0,
logoRotation: 0,
showMode: 'both'
};
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
}
function getSingleTextPosition(canvas, textConfig) {
const ctx = canvas.getContext('2d');
ctx.font = textConfig.font;
const lines = textConfig.text.split('\n');
const fontSize = parseInt(textConfig.font) || 24;
const lineHeight = fontSize * 1.5;
const maxWidth = Math.max(...lines.map(line => ctx.measureText(line).width));
const totalHeight = lines.length * lineHeight;
let x, y;
const margin = textConfig.margin || 20;
const position = textConfig.position || 'bottomRight';
switch (position) {
case 'topLeft':
x = margin + maxWidth / 2;
y = margin + lineHeight;
break;
case 'topRight':
x = canvas.width - margin - maxWidth / 2;
y = margin + lineHeight;
break;
case 'bottomLeft':
x = margin + maxWidth / 2;
y = canvas.height - margin - totalHeight + lineHeight;
break;
case 'center':
x = canvas.width / 2;
y = canvas.height / 2;
break;
default:
x = canvas.width - margin - maxWidth / 2;
y = canvas.height - margin - totalHeight + lineHeight;
break;
}
x += textConfig.offsetX || 0;
y += textConfig.offsetY || 0;
return { x, y, lines, lineHeight };
}
function drawSingleText(ctx, canvas, textConfig, globalTile, tileSpacing) {
const text = textConfig.text || '';
if (!text) return;
ctx.save();
ctx.font = textConfig.font;
ctx.fillStyle = textConfig.color;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const useTile = textConfig.tile !== undefined ? textConfig.tile : globalTile;
const spacing = textConfig.tileSpacing || tileSpacing || 200;
if (useTile) {
if (textConfig.rotation) {
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(textConfig.rotation * Math.PI / 180);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
}
for (let px = spacing / 2; px < canvas.width; px += spacing) {
for (let py = spacing / 2; py < canvas.height; py += spacing) {
const lines = text.split('\n');
const fontSize = parseInt(textConfig.font) || 24;
const lineHeight = fontSize * 1.5;
lines.forEach((line, index) => {
ctx.fillText(line, px, py + (index - (lines.length - 1) / 2) * lineHeight);
});
}
}
} else {
const { x, y, lines, lineHeight } = getSingleTextPosition(canvas, textConfig);
if (textConfig.rotation) {
ctx.translate(x, y);
ctx.rotate(textConfig.rotation * Math.PI / 180);
ctx.translate(-x, -y);
}
lines.forEach((line, index) => {
ctx.fillText(line, x, y + (index - (lines.length - 1) / 2) * lineHeight);
});
}
ctx.restore();
}
function drawTextWatermark(ctx, canvas, config) {
const texts = config.texts || [];
texts.forEach(textConfig => {
drawSingleText(ctx, canvas, textConfig, config.tile, config.tileSpacing);
});
}
function getLogoPosition(canvas, logoWidth, logoHeight, config) {
let x, y;
const margin = config.logoMargin || 20;
const position = config.logoPosition || 'bottomRight';
switch (position) {
case 'topLeft':
x = margin;
y = margin;
break;
case 'topRight':
x = canvas.width - logoWidth - margin;
y = margin;
break;
case 'bottomLeft':
x = margin;
y = canvas.height - logoHeight - margin;
break;
case 'center':
x = (canvas.width - logoWidth) / 2;
y = (canvas.height - logoHeight) / 2;
break;
default:
x = canvas.width - logoWidth - margin;
y = canvas.height - logoHeight - margin;
break;
}
x += config.logoOffsetX || 0;
y += config.logoOffsetY || 0;
return { x, y };
}
async function drawLogoWatermark(ctx, canvas, config) {
if (!config.logoUrl) return;
try {
const logo = await loadImage(config.logoUrl);
const logoWidth = config.logoWidth || 100;
const logoHeight = config.logoHeight || 50;
ctx.save();
ctx.globalAlpha = config.logoOpacity || 0.5;
if (config.tile) {
const spacing = config.tileSpacing || 200;
for (let px = spacing / 2; px < canvas.width; px += spacing) {
for (let py = spacing / 2; py < canvas.height; py += spacing) {
ctx.drawImage(logo, px - logoWidth / 2, py - logoHeight / 2, logoWidth, logoHeight);
}
}
} else {
const { x, y } = getLogoPosition(canvas, logoWidth, logoHeight, config);
if (config.logoRotation) {
ctx.translate(x + logoWidth / 2, y + logoHeight / 2);
ctx.rotate(config.logoRotation * Math.PI / 180);
ctx.translate(-(x + logoWidth / 2), -(y + logoHeight / 2));
}
ctx.drawImage(logo, x, y, logoWidth, logoHeight);
}
ctx.restore();
} catch (e) {
console.error('Logo 加载失败:', e);
}
}
async function addWatermarkToBase64(base64, config) {
const img = await loadImage(base64);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const showMode = config.showMode || 'both';
if (showMode === 'text') {
drawTextWatermark(ctx, canvas, config);
} else if (showMode === 'logo') {
await drawLogoWatermark(ctx, canvas, config);
} else if (showMode === 'both') {
await drawLogoWatermark(ctx, canvas, config);
drawTextWatermark(ctx, canvas, config);
}
return canvas.toDataURL('image/png');
}
function base64ToBlob(base64Data) {
const byteCharacters = atob(base64Data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
return new Blob([new Uint8Array(byteNumbers)], { type: 'image/jpeg' });
}
function uploadWatermarkedImage(base64, fileName) {
return new Promise((resolve, reject) => {
const base64Data = base64.split(',')[1];
const blob = base64ToBlob(base64Data);
const timestamp = new Date().getTime();
const nameParts = fileName.split('.');
const ext = nameParts.pop();
const baseName = nameParts.join('.');
const watermarkedFileName = `${baseName}_watermarked_${timestamp}.jpg`;
const file = new File([blob], watermarkedFileName, { type: 'image/jpeg' });
const formData = new FormData();
formData.append('file', file);
formData.append('Path', '/img');
formData.append('Limit', false);
formData.append('Preview', false);
V8.Post({
url: '/api/HDFS/Upload',
data: formData,
success: (result) => {
if (result && result.Code === 1) {
resolve(result);
} else {
reject(new Error(result?.Msg || '上传失败'));
}
},
fail: reject
});
});
}
// ========== 主流程 ==========
async function processUpload() {
const originalFile = V8.ThisValue;
const fieldName = 'YongpinTP';
if (!originalFile) return false;
// 检查是否已经是水印图
if (originalFile.name && originalFile.name.includes('watermarked')) {
console.log('已经是水印图,允许上传');
return true;
}
let isMultiple = false;
try {
if (V8.Field?.[fieldName]?.Config?.ImgUpload?.Multiple === true) {
isMultiple = true;
}
} catch (e) {}
let existingImages = [];
if (isMultiple) {
let val = V8.Form[fieldName];
if (typeof val === 'string') {
try { val = JSON.parse(val); } catch (e) { val = []; }
}
if (Array.isArray(val)) {
// 只保留已经是水印图的图片
existingImages = val.filter(item => item.State === 1 && item.Name && item.Name.includes('watermarked'));
}
}
try {
console.log('处理水印:', originalFile.name);
const base64 = await fileToBase64(originalFile);
const watermarkedBase64 = await addWatermarkToBase64(base64, watermarkConfig);
const uploadResult = await uploadWatermarkedImage(watermarkedBase64, originalFile.name);
console.log('水印上传成功');
const data = uploadResult.Data || {};
const fileObject = {
Id: data.Id,
State: 1,
Name: data.Name || originalFile.name,
Size: data.Size || originalFile.size,
CreateTime: data.CreateTime || new Date().toISOString().replace('T', ' ').split('.')[0],
Path: data.Path
};
// 保留已有的水印图,添加新的
if (isMultiple) {
const allImages = [...existingImages, fileObject];
V8.Form[fieldName] = allImages;
console.log('当前图片数量:', allImages.length);
} else {
V8.Form[fieldName] = JSON.stringify(fileObject);
}
const fileServer = V8.SysConfig.FileServer || '';
let realPath = data.Path;
if (!realPath.startsWith('http') && fileServer) {
realPath = fileServer + (realPath.startsWith('/') ? '' : '/') + realPath;
}
V8.Form[`${fieldName}_${data.Id}_RealPath`] = realPath;
if (!isMultiple) {
V8.Form[`${fieldName}_${fieldName}_RealPath`] = realPath;
}
if (V8.FieldSet) {
V8.FieldSet(fieldName, 'Value', V8.Form[fieldName]);
}
V8.Tips('水印添加成功', true);
} catch (e) {
console.error('失败:', e);
V8.Tips('水印添加失败', false);
}
// 返回 false 阻止原图上传
return false;
}
processUpload();
© 版权声明
1.本站提供的资源转载自国内外各大媒体和网络,仅供试玩体验。
2.不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。
3.若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4.您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。
5.如果您喜欢该内容,请支持正版,购买注册,得到更好的正版服务。
6.本站部分内容均由互联网收集整理,仅供大家参考、学习,不存在任何商业目的与商业用途。
7.在本网站提供的内容中,可能包含第三方的链接或广告信息,这些链接和信息不受本网站控制。本网站不对这些链接和信息的准确性,完整性或可靠性负责。访问这些链接的风险由用户自行承担。
8.本站提供的所有资源仅供参考学习使用,版权归原著所有。
9.我们非常重视版权问题,如有侵权请邮件或与站点客服联系处理。敬请谅解!E-mail:375505524@qq.com。
2.不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。
3.若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4.您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。
5.如果您喜欢该内容,请支持正版,购买注册,得到更好的正版服务。
6.本站部分内容均由互联网收集整理,仅供大家参考、学习,不存在任何商业目的与商业用途。
7.在本网站提供的内容中,可能包含第三方的链接或广告信息,这些链接和信息不受本网站控制。本网站不对这些链接和信息的准确性,完整性或可靠性负责。访问这些链接的风险由用户自行承担。
8.本站提供的所有资源仅供参考学习使用,版权归原著所有。
9.我们非常重视版权问题,如有侵权请邮件或与站点客服联系处理。敬请谅解!E-mail:375505524@qq.com。
THE END


















