Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite

This commit is contained in:
X1627315083
2026-01-16 10:34:17 +08:00
7 changed files with 636 additions and 584 deletions

View File

@@ -7,6 +7,7 @@ import {
insertObjectAtZIndex, insertObjectAtZIndex,
removeCanvasObjectByObject, removeCanvasObjectByObject,
createPatternTransform, createPatternTransform,
imageAddGapToCanvas,
} from "../utils/helper"; } from "../utils/helper";
import { restoreFabricObject } from "../utils/objectHelper"; import { restoreFabricObject } from "../utils/objectHelper";
@@ -308,16 +309,8 @@ export class FillRepeatGapChangeCommand extends Command {
await image.decode(); await image.decode();
object.fill_.width = image.width; object.fill_.width = image.width;
object.fill_.height = image.height; object.fill_.height = image.height;
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = image.width + object.fill_.gapX;
tcanvas.height = image.height + object.fill_.gapY;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(image, 0, 0);
const fill = object.get("fill"); const fill = object.get("fill");
fill.source = tcanvas; fill.source = imageAddGapToCanvas(image, object.fill_.gapX, object.fill_.gapY);
object.set("fill", new fabric.Pattern(fill)); object.set("fill", new fabric.Pattern(fill));
this.canvas.renderAll(); this.canvas.renderAll();
return true; return true;

View File

@@ -433,7 +433,7 @@
}; };
changeFill(obj, pattern); changeFill(obj, pattern);
}; };
// 改变填充便宜 // 改变填充偏移
const inputFillOffset = (value, obj) => { const inputFillOffset = (value, obj) => {
if (!obj.oldPattern) obj.oldPattern = obj.get("fill"); if (!obj.oldPattern) obj.oldPattern = obj.get("fill");
const pattern = new fabric.Pattern({ const pattern = new fabric.Pattern({

View File

@@ -34,6 +34,7 @@ import {
createPatternTransform, createPatternTransform,
getTransformScaleAngle, getTransformScaleAngle,
base64ToCanvas, base64ToCanvas,
imageAddGapToCanvas,
} from "../utils/helper"; } from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands"; import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
import { isFunction } from "lodash-es"; import { isFunction } from "lodash-es";
@@ -1630,38 +1631,57 @@ export class CanvasManager {
resolve(tcanvas); resolve(tcanvas);
}, { crossOrigin: "anonymous" }); }, { crossOrigin: "anonymous" });
}) })
let scaleX = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5; let scaleX_ = fixedLayerObj.width / image.width * (item.scale?.[0] || 1) / 5;
let scaleY = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5; let scaleY_ = fixedLayerObj.height / image.height * (item.scale?.[1] || 1) / 5;
let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX : scaleY; let scale = fixedLayerObj.width > fixedLayerObj.height ? scaleX_ : scaleY_;
let left = (item.location?.[0] || 0) - image.width * scale / 2 let offsetX = (item.location?.[0] || 0) - image.width * scale / 2
let top = (item.location?.[1] || 0) - image.height * scale / 2 let offsetY = (item.location?.[1] || 0) - image.height * scale / 2
let top = fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2
let left = fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2
let scaleX = fixedLayerObj.scaleX
let scaleY = fixedLayerObj.scaleY
let opacity = 1
let angle = 0
let gapX = 0
let gapY = 0
let fillSource = image
if(item.object){
top += item.object.top * fixedLayerObj.scaleY
left += item.object.left * fixedLayerObj.scaleX
scaleX *= item.object.scaleX
scaleY *= item.object.scaleY
opacity = item.object.opacity
angle = item.object.angle
gapX = item.object.gapX
gapY = item.object.gapY
fillSource = imageAddGapToCanvas(image, gapX, gapY);
}
let rect = new fabric.Rect({ let rect = new fabric.Rect({
id: id, id: id,
layerId: id, layerId: id,
layerName: name, layerName: name,
width: fixedLayerObj.width, width: fixedLayerObj.width,
height: fixedLayerObj.height, height: fixedLayerObj.height,
top: fixedLayerObj.top - fixedLayerObj.height * fixedLayerObj.scaleY / 2, top: top,
left: fixedLayerObj.left - fixedLayerObj.width * fixedLayerObj.scaleX / 2, left: left,
scaleX: fixedLayerObj.scaleX, scaleX: scaleX,
scaleY: fixedLayerObj.scaleY, scaleY: scaleY,
globalCompositeOperation: BlendMode.MULTIPLY, globalCompositeOperation: BlendMode.MULTIPLY,
fill: new fabric.Pattern({ fill: new fabric.Pattern({
source: image, source: fillSource,
repeat: "repeat", repeat: "repeat",
patternTransform: createPatternTransform(scale, item.angle || 0), patternTransform: createPatternTransform(scale, item.angle || 0),
offsetX: left, // 水平偏移 offsetX: offsetX, // 水平偏移
offsetY: top, // 垂直偏移 offsetY: offsetY, // 垂直偏移
}), }),
fill_ : { fill_ : {
source: item.path, source: item.path,
gapX: 0, gapX: gapX,
gapY: 0, gapY: gapY,
width: image.width, width: image.width,
height: image.height, height: image.height,
}, },
isPrintTrims: true, isPrintTrims: true,
// ...(item.object || {}),
}); });
this.canvas.add(rect); this.canvas.add(rect);
let layer = createLayer({ let layer = createLayer({

View File

@@ -1094,3 +1094,21 @@ export function traceImageContour(canvas) {
return contour; return contour;
} }
/**
* 图片添加gap转换
* @param {HTMLCanvasElement} image - img元素
* @param {Number} gapX - 水平gap
* @param {Number} gapY - 垂直gap
* @returns {HTMLCanvasElement} 转换后的canvas元素
*/
export function imageAddGapToCanvas(image, gapX, gapY) {
// 创建透明 Canvas
const tcanvas = document.createElement('canvas');
tcanvas.width = image.width + gapX;
tcanvas.height = image.height + gapY;
const ctx = tcanvas.getContext('2d');
ctx.clearRect(0, 0, tcanvas.width, tcanvas.height);
ctx.drawImage(image, 0, 0);
return tcanvas;
}

View File

@@ -139,6 +139,8 @@
<pingpu <pingpu
:list="list" :list="list"
ref="pingpuRef" ref="pingpuRef"
:width="600"
:height="900"
@change-canvas="updateCanvas" @change-canvas="updateCanvas"
/> />
</div> </div>
@@ -197,12 +199,12 @@
name: "Print2", name: "Print2",
priority: 1, priority: 1,
object: { object: {
top: 150, top: 450,
left: 250, left: 300,
scaleX: 0.5, scaleX: 0.5,
scaleY: 0.5, scaleY: 0.5,
opacity: 1, opacity: 1,
angle: 45, angle: 0,
flipX: false, flipX: false,
flipY: false, flipY: false,
blendMode: "multiply", blendMode: "multiply",

View File

@@ -33,6 +33,8 @@
const emit = defineEmits(["change-canvas", "init-canvas"]); const emit = defineEmits(["change-canvas", "init-canvas"]);
const props = defineProps({ const props = defineProps({
list: { type: Array, default: () => [] }, list: { type: Array, default: () => [] },
width: { type: Number, required: true },
height: { type: Number, required: true },
}); });
const el = ref(null); const el = ref(null);
const canvasRef = ref(null); const canvasRef = ref(null);
@@ -106,13 +108,13 @@
token, token,
action: ACTIONS.UPDATE, action: ACTIONS.UPDATE,
key: KEYS.O_TOP, key: KEYS.O_TOP,
value: object.top, value: (props.height / canvas.height) * object.top,
}); });
list.push({ list.push({
token, token,
action: ACTIONS.UPDATE, action: ACTIONS.UPDATE,
key: KEYS.O_LEFT, key: KEYS.O_LEFT,
value: object.left, value: (props.width / canvas.width) * object.left,
}); });
if (action === "rotate") { if (action === "rotate") {
list.push({ list.push({
@@ -188,6 +190,8 @@
height: cheight, height: cheight,
fill: pattern, fill: pattern,
...item.object, ...item.object,
top: item.object.top / (props.height / canvas.height),
left: item.object.left / (props.width / canvas.width),
onDelete: (v) => onDeleteItem(v), onDelete: (v) => onDeleteItem(v),
}); });
canvas.add(rect); canvas.add(rect);
@@ -237,10 +241,10 @@
let value = item.value; let value = item.value;
switch (item.key) { switch (item.key) {
case KEYS.O_TOP: case KEYS.O_TOP:
object.set("top", value); object.set("top", value / (props.height / canvas.height));
break; break;
case KEYS.O_LEFT: case KEYS.O_LEFT:
object.set("left", value); object.set("left", value / (props.width / canvas.width));
break; break;
case KEYS.O_OPACITY: case KEYS.O_OPACITY:
object.set("opacity", value); object.set("opacity", value);

View File

@@ -1,18 +1,18 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import CanvasEditor from "./CanvasEditor/index.vue"; import CanvasEditor from "./CanvasEditor/index.vue";
import RedGreenModeExample from "./RedGreenModeExample.vue"; import RedGreenModeExample from "./RedGreenModeExample.vue";
import ExistsImageList from "@/component/Canvas/ExistsImageList/index.vue"; import ExistsImageList from "@/component/Canvas/ExistsImageList/index.vue";
import ToolButton from "@/component/Canvas/ExistsImageList/ToolButton.vue"; import ToolButton from "@/component/Canvas/ExistsImageList/ToolButton.vue";
// 当前显示的组件 // 当前显示的组件
const canvasEditor = ref(); const canvasEditor = ref();
const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample
const clothingImageUrl = "/src/assets/images/canvas/xiangao.png"; const clothingImageUrl = "/src/assets/images/canvas/xiangao.png";
const clothingImageUrlInit = "/src/assets/images/canvas/xiangaofenge.png"; const clothingImageUrlInit = "/src/assets/images/canvas/xiangaofenge.png";
const imageData = [ const imageData = [
{ {
name: "风景照片", name: "风景照片",
type: "风景", type: "风景",
@@ -48,27 +48,27 @@ const imageData = [
{ url: "/src/assets/work/IMG_0008.PNG", name: "肖像2" }, { url: "/src/assets/work/IMG_0008.PNG", name: "肖像2" },
], ],
}, },
]; ];
const handleImageSelect = (selectedImage) => { const handleImageSelect = (selectedImage) => {
console.log("选中的图片:", selectedImage); console.log("选中的图片:", selectedImage);
console.log(selectedImage) console.log(selectedImage);
// selectedImage 包含url, name, categoryName, categoryType, originalItem // selectedImage 包含url, name, categoryName, categoryType, originalItem
}; };
// 切换视图 // 切换视图
function switchView(view) { function switchView(view) {
currentView.value = view; currentView.value = view;
} }
// 定义编辑器配置 // 定义编辑器配置
const editorConfig = { const editorConfig = {
width: 800, width: 800,
height: 600, height: 600,
backgroundColor: "#ffffff", // 画布背景色 backgroundColor: "#ffffff", // 画布背景色
}; };
const exportImage = async () => { const exportImage = async () => {
if (canvasEditor.value) { if (canvasEditor.value) {
const base64 = await canvasEditor.value.exportImage({ const base64 = await canvasEditor.value.exportImage({
isContainFixed: true, // 是否导出底图 isContainFixed: true, // 是否导出底图
@@ -85,12 +85,12 @@ const exportImage = async () => {
link.click(); // 触发下载 link.click(); // 触发下载
document.body.removeChild(link); // 下载后移除链接元素 document.body.removeChild(link); // 下载后移除链接元素
} }
}; };
// 导出颜色图层 // 导出颜色图层
const exportColorLayer = async () => { const exportColorLayer = async () => {
if (canvasEditor.value) { if (canvasEditor.value) {
const colorLayer = await canvasEditor.value.exportColorLayer(); const colorLayer = await canvasEditor.value.exportColorLayer();
console.log("导出颜色图层:",colorLayer); console.log("导出颜色图层:", colorLayer);
// 模拟下载图片 // 模拟下载图片
const link = document.createElement("a"); const link = document.createElement("a");
link.href = colorLayer.base64; link.href = colorLayer.base64;
@@ -99,26 +99,26 @@ const exportColorLayer = async () => {
link.click(); // 触发下载 link.click(); // 触发下载
document.body.removeChild(link); // 下载后移除链接元素 document.body.removeChild(link); // 下载后移除链接元素
} }
}; };
// 导出所有信息 // 导出所有信息
const exportExtraInfo = async () => { const exportExtraInfo = async () => {
if (canvasEditor.value) { if (canvasEditor.value) {
const extraInfo = await canvasEditor.value.exportExtraInfo(); const extraInfo = await canvasEditor.value.exportExtraInfo();
console.log("==========导出信息:", extraInfo); console.log("==========导出信息:", extraInfo);
} }
}; };
// 更新其他图层颜色 // 更新其他图层颜色
const updateOtherLayersColor = async () => { const updateOtherLayersColor = async () => {
const obj = { const obj = {
color: {rgba: {r:255,g:255,b:0,a:1}}, color: { rgba: { r: 255, g: 255, b: 0, a: 1 } },
} };
await canvasEditor?.value?.updateOtherLayers?.(obj); await canvasEditor?.value?.updateOtherLayers?.(obj);
}; };
// 更新其他图层印花 // 更新其他图层印花
const updateOtherLayersPrint = async () => { const updateOtherLayersPrint = async () => {
// document.querySelector(".app-container").style.width = "50vw" // document.querySelector(".app-container").style.width = "50vw"
const obj = { const obj = {
printObject: { printObject: {
prints: [ prints: [
@@ -131,18 +131,18 @@ const updateOtherLayersPrint = async () => {
scale: [0.3, 0.4], scale: [0.3, 0.4],
angle: 0, angle: 0,
}, },
] ],
}, },
} };
await canvasEditor?.value?.updateOtherLayers?.(obj); await canvasEditor?.value?.updateOtherLayers?.(obj);
}; };
const changeCanvas = (command) => { const changeCanvas = (command) => {
console.log(command); console.log(command);
}; };
const changeImageUrl = "/src/assets/work/1.PNG"; const changeImageUrl = "/src/assets/work/1.PNG";
const loadImageUrlToLayer = async () => { const loadImageUrlToLayer = async () => {
try { try {
const layerToLayer1 = canvasEditor?.value?.layers?.[0]?.id; const layerToLayer1 = canvasEditor?.value?.layers?.[0]?.id;
const layerId = await canvasEditor?.value?.addImageToLayer?.( const layerId = await canvasEditor?.value?.addImageToLayer?.(
@@ -156,23 +156,23 @@ const loadImageUrlToLayer = async () => {
} catch (error) { } catch (error) {
console.error("加载图片到图层失败:", error); console.error("加载图片到图层失败:", error);
} }
}; };
// 自定义工具方法 // 自定义工具方法
function exportAsPNG() { function exportAsPNG() {
console.log("导出PNG"); console.log("导出PNG");
// 实现导出PNG逻辑 // 实现导出PNG逻辑
exportImage(); exportImage();
} }
function saveCanvas() { function saveCanvas() {
console.log("保存项目"); console.log("保存项目");
// 实现保存画布逻辑 // 实现保存画布逻辑
const json = canvasEditor.value.getJSON(); const json = canvasEditor.value.getJSON();
localStorage.setItem("canvasProject", json); localStorage.setItem("canvasProject", json);
} }
function canvasProject() { function canvasProject() {
console.log("读取项目"); console.log("读取项目");
// 实现读取画布逻辑 // 实现读取画布逻辑
const json = localStorage.getItem("canvasProject"); const json = localStorage.getItem("canvasProject");
@@ -182,8 +182,8 @@ function canvasProject() {
} else { } else {
console.warn("没有找到保存的画布项目"); console.warn("没有找到保存的画布项目");
} }
} }
const exportJSON = () => { const exportJSON = () => {
console.log("导出JSON"); console.log("导出JSON");
// 实现导出JSON逻辑 // 实现导出JSON逻辑
const json = canvasEditor.value.getJSON(); const json = canvasEditor.value.getJSON();
@@ -195,42 +195,41 @@ const exportJSON = () => {
a.download = "canvas_project.json"; a.download = "canvas_project.json";
a.click(); a.click();
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}; };
// 复制JSON // 复制JSON
const copyJSON = () => { const copyJSON = () => {
console.log("复制JSON"); console.log("复制JSON");
// 实现复制JSON逻辑 // 实现复制JSON逻辑
const json = canvasEditor.value.getJSON(); const json = canvasEditor.value.getJSON();
// 复制JSON到剪贴板 // 复制JSON到剪贴板
navigator.clipboard.writeText(json); navigator.clipboard.writeText(json);
}; };
const getLayers = ()=>{ const getLayers = () => {
console.log("==========layers",canvasEditor.value?.layers) console.log("==========layers", canvasEditor.value?.layers);
} };
// 处理自定义工具点击 // 处理自定义工具点击
const handleCustomToolClick = (tool) => { const handleCustomToolClick = (tool) => {
tool.action(); tool.action();
}; };
const changeFixedImage = () => { const changeFixedImage = () => {
canvasEditor.value.changeFixedImage(changeImageUrl, { canvasEditor.value.changeFixedImage(changeImageUrl, {
imageMode: "contains", // 设置底图包含在画布内 imageMode: "contains", // 设置底图包含在画布内
}); });
}; };
const canvasInit = () => { const canvasInit = () => {
console.log("画布初始化完成"); console.log("画布初始化完成");
// 可以在这里执行一些初始化逻辑 // 可以在这里执行一些初始化逻辑
// canvasEditor.value.changeFixedImage(clothingImageUrlInit, { // canvasEditor.value.changeFixedImage(clothingImageUrlInit, {
// imageMode: "contains", // 设置底图包含在画布内 // imageMode: "contains", // 设置底图包含在画布内
// }); // });
}; };
const frontBackChange =(value)=>{ const frontBackChange = (value) => {};
}
// 自定义工具配置相关 // 自定义工具配置相关
const customToolsList = ref([ const customToolsList = ref([
{ {
id: "exportColorLayer", id: "exportColorLayer",
title: "导出颜色图层", title: "导出颜色图层",
@@ -291,14 +290,14 @@ const customToolsList = ref([
{ {
id: "redGreenExample", id: "redGreenExample",
title: "红绿图模式", title: "红绿图模式",
action: () => switchView('redGreenExample'), action: () => switchView("redGreenExample"),
label: "红", label: "红",
class: "export-btn", class: "export-btn",
}, },
{ {
id: "canvasEditor", id: "canvasEditor",
title: "普通模式", title: "普通模式",
action: () => switchView('canvasEditor'), action: () => switchView("canvasEditor"),
label: "普", label: "普",
class: "export-btn", class: "export-btn",
}, },
@@ -330,41 +329,54 @@ const customToolsList = ref([
label: "查L", label: "查L",
class: "export-btn", class: "export-btn",
}, },
]); ]);
const otherData = { const otherData = {
color: {rgba: {r:255,g:0,b:0,a:1}}, color: { rgba: { r: 255, g: 0, b: 0, a: 1 } },
printObject: { printObject: {
prints: [ prints: [
{
ifSingle: false,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [250, 780],
scale: [1.2, 1.6],
angle: 0,
object: {
top: 600,
left: 800,
scaleX: 0.5,
scaleY: 0.5,
opacity: 1,
angle: 45,
flipX: false,
flipY: false,
blendMode: "multiply",
gapX: 0,
gapY: 0,
},
},
// { // {
// ifSingle: false, // ifSingle: true,
// level2Type: "Pattern", // level2Type: "Pattern",
// designType: "Library", // designType: "Library",
// path: "/src/assets/images/canvas/yinhua1.jpg", // path: "/src/assets/images/canvas/yinhua1.jpg",
// location: [250, 780], // location: [550, 650],
// scale: [0.3, 0.4], // scale: [0.15, 0.2],
// angle: 0, // angle: 0,
// }, // },
{ // {
ifSingle: true, // ifSingle: true,
level2Type: "Pattern", // level2Type: "Pattern",
designType: "Library", // designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg", // path: "/src/assets/images/canvas/yinhua1.jpg",
location: [550, 650], // location: [700, 400],
scale: [0.15, 0.2], // scale: [0.1, 0.133],
angle: 0, // angle: 0,
// },
],
}, },
{ };
ifSingle: true,
level2Type: "Pattern",
designType: "Library",
path: "/src/assets/images/canvas/yinhua1.jpg",
location: [700, 400],
scale: [0.1, 0.133],
angle: 0,
}
]
},
}
</script> </script>
<template> <template>
@@ -405,7 +417,10 @@ const otherData = {
showFixedLayer showFixedLayer
> >
<template #existsImageList> <template #existsImageList>
<ExistsImageList :list="imageData" @select="handleImageSelect" /> <ExistsImageList
:list="imageData"
@select="handleImageSelect"
/>
</template> </template>
<!-- 使用插槽添加自定义工具栏按钮 --> <!-- 使用插槽添加自定义工具栏按钮 -->
@@ -428,13 +443,13 @@ const otherData = {
</template> </template>
<style scoped lang="less"> <style scoped lang="less">
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
box-sizing: border-box; box-sizing: border-box;
} }
.canvas-wrapper-btns { .canvas-wrapper-btns {
position: fixed; position: fixed;
top: 10px; top: 10px;
left: 50%; left: 50%;
@@ -442,27 +457,27 @@ const otherData = {
z-index: 1000; z-index: 1000;
display: flex; display: flex;
gap: 10px; gap: 10px;
} }
html, html,
body { body {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.app-container { .app-container {
height: 100%; height: 100%;
// height: 100vh; // height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.app-container-example { .app-container-example {
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
.app-wrapper-btns { .app-wrapper-btns {
position: relative; position: relative;
width: 200px; width: 200px;
height: 100vh; height: 100vh;
@@ -475,9 +490,9 @@ body {
&.hide { &.hide {
width: 20px; width: 20px;
} }
} }
.view-switcher { .view-switcher {
display: flex; display: flex;
gap: 8px; gap: 8px;
@@ -501,9 +516,9 @@ body {
color: white; color: white;
} }
} }
} }
.app-header { .app-header {
position: fixed; position: fixed;
top: 10px; top: 10px;
right: 10px; right: 10px;
@@ -513,23 +528,23 @@ body {
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
} }
.app-content { .app-content {
position: relative; position: relative;
flex: 1; flex: 1;
height: 100vh; height: 100vh;
} }
/* 自定义工具按钮样式 */ /* 自定义工具按钮样式 */
.tool-separator { .tool-separator {
width: 100%; width: 100%;
height: 1px; height: 1px;
background-color: #e0e0e0; background-color: #e0e0e0;
margin: 8px 0; margin: 8px 0;
} }
.custom-tool-btn { .custom-tool-btn {
position: relative; position: relative;
width: 3.5rem; width: 3.5rem;
height: 3.5rem; height: 3.5rem;
@@ -543,22 +558,22 @@ body {
font-size: 1.6rem; font-size: 1.6rem;
color: #333; color: #333;
transition: all 0.2s ease; transition: all 0.2s ease;
} }
.custom-tool-btn:hover { .custom-tool-btn:hover {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
.custom-tool-btn:hover .tool-tooltip { .custom-tool-btn:hover .tool-tooltip {
display: block; display: block;
} }
.tool-tooltip { .tool-tooltip {
display: none; display: none;
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
writing-mode: vertical-rl; /* 竖直排列 */ writing-mode: vertical-rl; /* 竖直排列 */
text-orientation: upright; /* 保持文字正常显示 */// left: 100%; text-orientation: upright; /* 保持文字正常显示 */ // left: 100%;
left: 50%; left: 50%;
top: -0.8rem; top: -0.8rem;
transform: translate(-50%, -100%); transform: translate(-50%, -100%);
@@ -569,9 +584,9 @@ body {
white-space: nowrap; white-space: nowrap;
font-size: 1.2rem; font-size: 1.2rem;
z-index: 10; z-index: 10;
} }
.tool-tooltip:before { .tool-tooltip:before {
content: ""; content: "";
position: absolute; position: absolute;
left: 50%; left: 50%;
@@ -580,10 +595,10 @@ body {
border-width: 0.5rem; border-width: 0.5rem;
border-style: solid; border-style: solid;
border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent; border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent;
} }
/* 深色模式适配 */ /* 深色模式适配 */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.app-header { .app-header {
background: rgba(45, 45, 45, 0.95); background: rgba(45, 45, 45, 0.95);
@@ -603,5 +618,5 @@ body {
} }
} }
} }
} }
</style> </style>