mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2025-08-23 08:04:50 +00:00
代码同步
This commit is contained in:
3959
package-lock.json
generated
3959
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@logicflow/core": "^2.0.16",
|
"@logicflow/core": "^2.0.16",
|
||||||
|
"@logicflow/engine": "^0.1.1",
|
||||||
"@logicflow/extension": "^2.0.21",
|
"@logicflow/extension": "^2.0.21",
|
||||||
|
"@logicflow/vue-node-registry": "^1.0.18",
|
||||||
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
"@vueup/vue-quill": "^1.2.0",
|
"@vueup/vue-quill": "^1.2.0",
|
||||||
"element-plus": "^2.9.1",
|
"element-plus": "^2.9.1",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
|
@@ -77,12 +77,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {ref, reactive, onMounted} from 'vue';
|
import {ref, reactive, onMounted} from 'vue';
|
||||||
import html2canvas from "html2canvas";
|
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import updateLogs from "../data/updateLog.json"
|
import updateLogs from "../data/updateLog.json"
|
||||||
import {useFilesStore} from "@/ts/useStore";
|
import {useFilesStore} from "@/ts/useStore";
|
||||||
import {ElMessageBox} from "element-plus";
|
import {ElMessageBox} from "element-plus";
|
||||||
import {useGlobalMessage} from "@/ts/useGlobalMessage";
|
import {useGlobalMessage} from "@/ts/useGlobalMessage";
|
||||||
|
// import { useScreenshot } from '@/ts/useScreenshot';
|
||||||
|
import { getCurrentInstance } from 'vue';
|
||||||
|
|
||||||
const filesStore = useFilesStore();
|
const filesStore = useFilesStore();
|
||||||
const { showMessage } = useGlobalMessage();
|
const { showMessage } = useGlobalMessage();
|
||||||
@@ -210,162 +211,46 @@ const applyWatermarkSettings = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 计算视觉总高度
|
// 获取 App 根实例,便于跨组件获取 flowEditorRef
|
||||||
function calculateVisualHeight(selector) {
|
const appInstance = getCurrentInstance();
|
||||||
// 1. 获取所有目标元素
|
|
||||||
const elements = Array.from(document.querySelectorAll(selector));
|
|
||||||
|
|
||||||
// 2. 获取元素位置信息并排序
|
// const { captureFlow, dataUrl } = useScreenshot();
|
||||||
const rects = elements.map(el => {
|
|
||||||
const rect = el.getBoundingClientRect();
|
|
||||||
return {
|
|
||||||
el,
|
|
||||||
top: rect.top,
|
|
||||||
bottom: rect.bottom,
|
|
||||||
height: rect.height
|
|
||||||
};
|
|
||||||
}).sort((a, b) => a.top - b.top); // 按垂直位置排序
|
|
||||||
|
|
||||||
// 3. 动态分组同行元素
|
|
||||||
const rows = [];
|
|
||||||
rects.forEach(rect => {
|
|
||||||
let placed = false;
|
|
||||||
|
|
||||||
// 尝试将元素加入已有行
|
|
||||||
for (const row of rows) {
|
|
||||||
if (
|
|
||||||
rect.top < row.bottom && // 元素顶部在行底部上方
|
|
||||||
rect.bottom > row.top // 元素底部在行顶部下方
|
|
||||||
) {
|
|
||||||
row.elements.push(rect);
|
|
||||||
row.bottom = Math.max(row.bottom, rect.bottom); // 扩展行底部
|
|
||||||
row.maxHeight = Math.max(row.maxHeight, rect.height);
|
|
||||||
placed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未加入则创建新行
|
|
||||||
if (!placed) {
|
|
||||||
rows.push({
|
|
||||||
elements: [rect],
|
|
||||||
top: rect.top,
|
|
||||||
bottom: rect.bottom,
|
|
||||||
maxHeight: rect.height
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. 累加每行最大高度
|
|
||||||
return rows.reduce((sum, row) => sum + row.maxHeight, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ignoreElements = (element) => {
|
|
||||||
return element.classList.contains('ql-toolbar') || element.classList.contains('el-tabs__header');
|
|
||||||
};
|
|
||||||
|
|
||||||
const prepareCapture = async () => {
|
const prepareCapture = async () => {
|
||||||
state.previewVisible = true;
|
// 获取 FlowEditor 实例
|
||||||
|
// 这里假设 App.vue 已将 flowEditorRef 作为全局 property 或 provide
|
||||||
// 创建临时样式
|
// 或者你可以通过 window.__VUE_DEVTOOLS_GLOBAL_HOOK__.$vm0.$refs.flowEditorRef 方式调试
|
||||||
const style = document.createElement('style');
|
let flowEditor = null;
|
||||||
style.textContent = `
|
|
||||||
.ql-container.ql-snow {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
#main-container {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 100%;
|
|
||||||
}`;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
|
|
||||||
// 获取目标元素
|
|
||||||
const element = document.querySelector('#main-container');
|
|
||||||
if (!element) {
|
|
||||||
console.error('Element not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存原始 overflow 样式
|
|
||||||
const originalOverflow = element.style.overflow;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 临时隐藏 overflow 样式
|
// 通过 DOM 查找
|
||||||
element.style.overflow = 'visible';
|
const flowEditorDom = document.querySelector('#main-container .flow-editor');
|
||||||
|
if (!flowEditorDom) {
|
||||||
// 计算需要忽略的元素高度
|
showMessage('error', '未找到流程图编辑器');
|
||||||
let totalHeight = calculateVisualHeight('[data-html2canvas-ignore]') + calculateVisualHeight('.ql-toolbar');
|
return;
|
||||||
console.log('所有携带指定属性的元素高度之和:', totalHeight);
|
|
||||||
|
|
||||||
console.log('主元素宽度', element.scrollWidth);
|
|
||||||
console.log('主元素高度', element.scrollHeight);
|
|
||||||
|
|
||||||
// 1. 生成原始截图
|
|
||||||
const canvas = await html2canvas(element, {
|
|
||||||
ignoreElements: ignoreElements,
|
|
||||||
scrollX: 0,
|
|
||||||
scrollY: 0,
|
|
||||||
width: element.scrollWidth,
|
|
||||||
height: element.scrollHeight - totalHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. 创建新Canvas添加水印
|
|
||||||
const watermarkedCanvas = document.createElement('canvas');
|
|
||||||
const ctx = watermarkedCanvas.getContext('2d');
|
|
||||||
|
|
||||||
// 设置新Canvas尺寸
|
|
||||||
watermarkedCanvas.width = canvas.width;
|
|
||||||
watermarkedCanvas.height = canvas.height;
|
|
||||||
|
|
||||||
// 绘制原始截图
|
|
||||||
ctx.drawImage(canvas, 0, 0);
|
|
||||||
|
|
||||||
// 添加水印
|
|
||||||
ctx.font = `${watermark.fontSize}px Arial`;
|
|
||||||
ctx.fillStyle = watermark.color;
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'middle';
|
|
||||||
|
|
||||||
// 计算每个水印的位置间隔
|
|
||||||
const colSpace = watermarkedCanvas.width / (watermark.cols + 1);
|
|
||||||
const rowSpace = watermarkedCanvas.height / (watermark.rows + 1);
|
|
||||||
|
|
||||||
// 保存原始画布状态
|
|
||||||
ctx.save();
|
|
||||||
|
|
||||||
// 循环绘制多个水印
|
|
||||||
for (let row = 1; row <= watermark.rows; row++) {
|
|
||||||
for (let col = 1; col <= watermark.cols; col++) {
|
|
||||||
ctx.save(); // 保存当前状态
|
|
||||||
const x = col * colSpace;
|
|
||||||
const y = row * rowSpace;
|
|
||||||
|
|
||||||
// 移动到目标位置并旋转
|
|
||||||
ctx.translate(x, y);
|
|
||||||
ctx.rotate((watermark.angle * Math.PI) / 180);
|
|
||||||
|
|
||||||
// 绘制水印文字
|
|
||||||
ctx.fillText(watermark.text, 0, 0);
|
|
||||||
ctx.restore(); // 恢复状态
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// 通过 ref 获取 vueflow 根元素
|
||||||
ctx.restore(); // 恢复原始状态
|
const vueflowRoot = flowEditorDom.querySelector('.vue-flow');
|
||||||
|
if (!vueflowRoot || !(vueflowRoot instanceof HTMLElement)) {
|
||||||
// 3. 存储带水印的图片
|
showMessage('error', '未找到 VueFlow 画布');
|
||||||
state.previewImage = watermarkedCanvas.toDataURL();
|
return;
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Capture failed', error);
|
state.previewVisible = true;
|
||||||
} finally {
|
// 截图
|
||||||
// 恢复原始 overflow 样式
|
const img = await captureFlow(vueflowRoot as HTMLElement, {
|
||||||
element.style.overflow = originalOverflow;
|
type: 'png',
|
||||||
|
shouldDownload: false,
|
||||||
// 移除临时样式
|
watermark: {
|
||||||
document.head.removeChild(style);
|
text: watermark.text,
|
||||||
|
fontSize: watermark.fontSize,
|
||||||
|
color: watermark.color,
|
||||||
|
angle: watermark.angle,
|
||||||
|
rows: watermark.rows,
|
||||||
|
cols: watermark.cols,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
state.previewImage = img;
|
||||||
|
} catch (e) {
|
||||||
|
showMessage('error', '截图失败: ' + (e?.message || e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -373,9 +258,9 @@ const downloadImage = () => {
|
|||||||
if (state.previewImage) {
|
if (state.previewImage) {
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = state.previewImage;
|
link.href = state.previewImage;
|
||||||
link.download = 'screenshot.png'; // 设置下载的文件名
|
link.download = 'screenshot.png';
|
||||||
link.click();
|
link.click();
|
||||||
state.previewVisible = false; // 关闭预览弹窗
|
state.previewVisible = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user