canvas语言适配

This commit is contained in:
X1627315083
2025-07-20 18:19:34 +08:00
parent 4b694236ee
commit c44aadc9e3
12 changed files with 207 additions and 64 deletions

View File

@@ -281,7 +281,7 @@ onMounted(() => {
" -->
<div class="canvas-settings" v-if="!props.enabledRedGreenMode">
<div class="setting-group">
<span class="setting-label">{{ $t("宽度") }}</span>
<span class="setting-label">{{ $t("Canvas.width") }}</span>
<a-input-number
:value="canvasWidth"
class="setting-input"
@@ -297,7 +297,7 @@ onMounted(() => {
/>
</div>
<div class="setting-group">
<span class="setting-label">{{ $t("高度") }}</span>
<span class="setting-label">{{ $t("Canvas.height") }}</span>
<a-input-number
:value="canvasHeight"
class="setting-input"
@@ -313,7 +313,7 @@ onMounted(() => {
/>
</div>
<div class="setting-group">
<span class="setting-label">{{ $t("颜色") }}</span>
<span class="setting-label">{{ $t("Canvas.color") }}</span>
<div class="color-picker-wrapper">
<input
type="color"

View File

@@ -1,6 +1,8 @@
<script setup>
import { ref, inject, onMounted } from "vue";
import { Skeleton } from "ant-design-vue";
import { useI18n } from 'vue-i18n'
const {t} = useI18n()
const loading = ref(true);
const shortcuts = ref([]);
@@ -44,17 +46,17 @@ function convertShortcuts(managerShortcuts) {
// 基本的Action到显示名称的映射
const actionDisplayMap = {
undo: "撤销",
redo: "重做",
delete: "删除选中元素",
undo: t('Canvas.Undo'),
redo: t('Canvas.Redo'),
delete: t('Canvas.DeleteSelectedElement'),
selectAll: "全选",
copy: "复制",
paste: "粘贴",
cut: "剪切",
copy: t('Canvas.Copy'),
paste: t('Canvas.Pase'),
cut: t('Canvas.Cut'),
save: "保存",
selectTool: "选择工具",
increaseBrushSize: "增加笔触大小",
decreaseBrushSize: "减小笔触大小",
increaseBrushSize: t('Canvas.DecreaseBrush'),
decreaseBrushSize: t('Canvas.IncreaseBrush'),
toggleTempTool: "临时切换工具",
// newLayer: "新建图层",
// groupLayers: "组合图层",
@@ -64,15 +66,15 @@ function convertShortcuts(managerShortcuts) {
// 工具ID到显示名称的映射
const toolDisplayMap = {
select: "选择模式",
draw: "绘画模式",
eraser: "橡皮擦模式",
// eyedropper: "吸色工具",
pan: "移动画布",
lasso: "套索工具",
// area_custom: "自由选区工具",
// wave: "波浪工具",
liquify: "液化工具",
select: t('Canvas.SelectionMode'),
draw: t('Canvas.PaintingMode'),
eraser: t('Canvas.EraserMode'),
// eyedropper: t('Canvas.Cut'),
pan: '移动画布',
lasso: t('Canvas.LassoTool'),
// area_custom: t('Canvas.Cut'),
// wave: t('Canvas.Cut'),
liquify: t('Canvas.LiquifyTool'),
};
// 处理每个快捷键
@@ -95,9 +97,9 @@ function convertShortcuts(managerShortcuts) {
// 添加一些组件特定的快捷键
result.push({
action: "缩放画布",
windows: "鼠标滚轮",
mac: "鼠标滚轮 或 触控板缩放手势",
action: t('Canvas.ZoomCanvas'),
windows: t('Canvas.MouseWheel'),
mac: t('Canvas.MacZoomCanvas'),
touch: "双指捏合",
});
@@ -268,11 +270,11 @@ function getShortcutsByCategory(category) {
<template>
<div class="keyboard-shortcut-help">
<h2>键盘快捷键 & 操作指南</h2>
<h2>{{ $t('Canvas.KeyboardShortcutsOperationGuide') }}</h2>
<Skeleton active :loading="loading">
<div class="platform-info">
检测到的平台:
{{$t('Canvas.TheDetectedPlatform')}}:
<span v-if="platform.isMac">MacOS</span>
<span v-else-if="platform.isWindows">Windows</span>
<span v-else-if="platform.isIPad">iPad</span>
@@ -283,12 +285,12 @@ function getShortcutsByCategory(category) {
</div>
<div class="shortcuts-category">
<h3>基本操作</h3>
<h3> {{$t('Canvas.BasicOperations')}}</h3>
<table class="shortcuts-table">
<thead>
<tr>
<th>操作</th>
<th>快捷键/手势</th>
<th>{{$t('Canvas.BasicOperations')}}</th>
<th>{{$t('Canvas.ShortcutGesture')}}</th>
</tr>
</thead>
<tbody>
@@ -301,12 +303,12 @@ function getShortcutsByCategory(category) {
</div>
<div class="shortcuts-category">
<h3>视图操作</h3>
<h3>{{ $t('Canvas.viewOperations') }}</h3>
<table class="shortcuts-table">
<thead>
<tr>
<th>操作</th>
<th>快捷键/手势</th>
<th>{{ $t('Canvas.Operation') }}</th>
<th>{{ $t('Canvas.ShortcutGesture') }}</th>
</tr>
</thead>
<tbody>
@@ -319,12 +321,12 @@ function getShortcutsByCategory(category) {
</div>
<div class="shortcuts-category">
<h3>工具切换</h3>
<h3>{{ $t('Canvas.toolSwitching') }}</h3>
<table class="shortcuts-table">
<thead>
<tr>
<th>操作</th>
<th>快捷键/手势</th>
<th>{{ $t('Canvas.Operation') }}</th>
<th>{{ $t('Canvas.ShortcutGesture') }}</th>
</tr>
</thead>
<tbody>
@@ -337,12 +339,12 @@ function getShortcutsByCategory(category) {
</div>
<div class="shortcuts-category">
<h3>笔刷调整</h3>
<h3>{{ $t('Canvas.toolSwitching') }}</h3>
<table class="shortcuts-table">
<thead>
<tr>
<th>操作</th>
<th>快捷键/手势</th>
<th>{{ $t('Canvas.Operation') }}</th>
<th>{{ $t('Canvas.ShortcutGesture') }}</th>
</tr>
</thead>
<tbody>

View File

@@ -3,7 +3,8 @@ import { ref, nextTick, computed, inject } from "vue";
import { Checkbox } from "ant-design-vue";
import { VueDraggable } from "vue-draggable-plus";
import { isGroupLayer } from "../../utils/layerHelper";
import { useI18n } from 'vue-i18n'
const {t} = useI18n()
// 设置组件名称,用于递归渲染
defineOptions({
name: "LayerItem",
@@ -393,7 +394,7 @@ function findParentLayerId() {
v-if="layer.thumbnailUrl"
:src="layer.thumbnailUrl"
class="layer-thumbnail"
:alt="$t('图层预览')"
:alt="$t('Canvas.preview')"
/>
</div>
@@ -423,7 +424,7 @@ function findParentLayerId() {
<div
class="visibility-btn"
@click.stop="handleToggleVisibility"
:title="$t('显示/隐藏图层')"
:title="$t('Canvas.showHiddenLayer')"
>
<SvgIcon v-if="layer.visible" name="CEye" :size="16"></SvgIcon>
<SvgIcon v-else name="CUnEye" :size="16"></SvgIcon>
@@ -447,7 +448,7 @@ function findParentLayerId() {
<div
class="delete-btn"
:class="{ disabled: !canDelete }"
:title="$t('删除图层')"
:title="$t('Canvas.deleteLayer')"
@click.stop="handleDelete"
>
<SvgIcon name="CDelete" size="14"></SvgIcon>
@@ -460,7 +461,7 @@ function findParentLayerId() {
class="group-expand-icon"
@click.stop="toggleGroupExpanded"
@dblclick.stop=""
:title="isGroupExpanded ? $t('收起组') : $t('展开组')"
:title="isGroupExpanded ? $t('Canvas.CollapseUp') : $t('Canvas.CollapseDown')"
>
<SvgIcon
name="CRight"

View File

@@ -5,6 +5,8 @@ import ContextMenu from "./ContextMenu.vue";
import LayerItem from "./LayerItem.vue";
import LayersList from "./LayersList.vue"; // 引入 LayersList 组件
import { createCrossLevelMoveCommand } from "../../commands/CrossLevelMoveCommands";
import { useI18n } from 'vue-i18n'
const {t} = useI18n()
// // 导入命令类
// import {
// ReorderLayersCommand,
@@ -1523,7 +1525,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
style="width: 0; height: 0; opacity: 0"
/> -->
<h3>
{{ $t("图层") }}
{{ $t("Canvas.layer") }}
{{ selectedLayerIds.length > 0 ? `(${selectedLayerIds.length})` : "" }}
</h3>
<div class="layer-actions-group">
@@ -1533,7 +1535,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
class="group-btn action-btn"
:class="{ disabled: !canGroupLayers }"
@click="groupSelectedLayers"
:title="$t('创建组')"
:title="$t('Canvas.createGroup')"
>
<SvgIcon name="CCreateGroup" size="20"></SvgIcon>
</div>
@@ -1542,7 +1544,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
class="ungroup-btn action-btn"
:class="{ disabled: !canUngroupLayers }"
@click="ungroupSelectedLayer"
:title="$t('解组')"
:title="$t('Canvas.slutionGroup')"
>
<SvgIcon name="CCancelGroup" size="20"></SvgIcon>
</div>
@@ -1550,7 +1552,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
<div
class="delete-selected-btn action-btn"
@click="deleteSelectedLayers"
:title="$t('删除选中图层')"
:title="$t('Canvas.deleteLayer')"
>
<SvgIcon name="CDelete" size="16"></SvgIcon>
</div>
@@ -1558,7 +1560,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
<div
class="clear-selection-btn action-btn"
@click="clearSelection"
:title="$t('清除选择')"
:title="$t('Canvas.clearSelection')"
>
<SvgIcon name="CCloseNo" size="14"></SvgIcon>
</div>
@@ -1570,7 +1572,7 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
v-if="isChildLayerActive"
class="add-layer-btn action-btn"
@click="addTopLayer"
:title="$t('添加顶级图层')"
:title="$t('Canvas.AddPinnedLayer')"
>
<SvgIcon name="CPlusTop" size="16"></SvgIcon>
</div>
@@ -1587,8 +1589,8 @@ async function moveGroupToGroup(draggedLayer, fromParentId, toParentId, newIndex
<!-- 多选提示条 -->
<div v-if="isMultiSelectMode" class="multi-select-info">
<span>已选择 {{ selectedLayerIds.length }} 个图层</span>
<small>提示按住 Ctrl/Cmd 多选Shift 范围选择长按进入多选模式</small>
<span>{{ $t('Canvas.Hint') }}{{ selectedLayerIds.length }}</span>
<small>{{ $t('Canvas.Hint') }}</small>
</div>
<!-- 图层列表组件 -->

View File

@@ -36,6 +36,8 @@ import { ToolManager } from "./managers/toolManager.js";
// import { fabric } from "fabric-with-all";
import { uploadImageAndCreateLayer, loadImageUrlToLayer, loadImage } from "./utils/imageHelper.js";
// import MinimapPanel from "./components/MinimapPanel.vue";
import { useI18n } from 'vue-i18n'
const {t} = useI18n()
const KeyboardShortcutHelp = defineAsyncComponent(
() => import("./components/KeyboardShortcutHelp.vue")
);
@@ -514,7 +516,7 @@ function updateCanvasColor() {
}
async function addLayer() {
await layerManager.createLayer("空图层");
await layerManager.createLayer(t('Canvas.EmptyLayer'));
}
async function addTopLayer() {
await layerManager.createLayer("空图层", LayerType.EMPTY, {
@@ -980,9 +982,9 @@ defineExpose({
/>
<div class="zoom-info">
缩放: {{ currentZoom }}%
<button class="reset-zoom" @click="resetZoom">重置视图</button>
<button class="help-btn" @click="toggleShortcutHelp" title="查看快捷键和触控操作">
{{ t('Canvas.Scale') }}: {{ currentZoom }}%
<button class="reset-zoom" @click="resetZoom">{{ $t('Canvas.ResetLayer') }}</button>
<button class="help-btn" @click="toggleShortcutHelp" :title="$t('Canvas.Help')">
?
</button>
</div>

View File

@@ -62,7 +62,8 @@ import { fabric } from "fabric-with-all";
import { getOriginObjectInfo } from "../utils/layerUtils";
import { restoreFabricObject } from "../utils/objectHelper";
import { UpdateGroupMaskPositionCommand } from "../commands/UpdateGroupMaskPositionCommand";
import { useI18n } from 'vue-i18n'
const {t} = useI18n()
/**
* 图层管理器 - 负责管理画布上的所有图层
* 包含图层的创建、删除、修改、排序等操作
@@ -579,7 +580,7 @@ export class LayerManager {
this.createFixedLayer();
// 创建一个空白图层(默认位于背景图层和固定图层之上)
await this.createLayer("图层 1");
await this.createLayer(t('Canvas.Layer1'));
} else {
// 检查是否已有背景层
const hasBackgroundLayer = this.layers.value.some((layer) => layer.isBackground);
@@ -601,7 +602,7 @@ export class LayerManager {
);
if (!hasNormalLayer) {
await this.createLayer("图层 1");
await this.createLayer(t('Canvas.Layer1'));
}
}

View File

@@ -546,7 +546,7 @@ setup(props:any,{emit}) {
productimg.selectGenerate.listType = listType
productimg.selectGenerate.isIndex = index
if(!list[index]?.id &&( (list[index].resultType == 'PoseTransfer' && !list[index].gifUrl) || (list[index].resultType == 'PoseTransfer' && !list[index].url))){
if(( (list[index].resultType == 'PoseTransfer' && !list[index].gifUrl) || (list[index].resultType !== 'PoseTransfer' && !list[index].url))){
productimg.productimgIsProductimg = true
productimg.productimgRemProductimg = true
setPrductimg([list[index].taskId])
@@ -626,14 +626,15 @@ setup(props:any,{emit}) {
// productimg.likeDesignCollectionList[index].childList[childIndex] = productimg.generateCourse
// }
}
let emitData = {
}
let emitData = {
status:productimg.openType,
generateCourse:[{
...productimg.generateCourse,
}]
}
emit('upDataDesignLikeList',emitData)
}
productimg.openType = ''
scaleImage.value = false

View File

@@ -1069,6 +1069,7 @@ export default defineComponent({
}
const generateLoad = async (data:any)=>{
// return
console.log(data)
clearInterval(prductimgTime.ToProductImage)
clearInterval(prductimgTime.PoseTransfer)
clearInterval(prductimgTime.Relight)

View File

@@ -69,6 +69,7 @@ export default defineComponent({
dataDom.editCanvas.addImageToLayer(url,{layerId:dataDom.editCanvas.layers[0].id,imageMode:'contains',undoable:false})
}
const canvasInit = (value)=>{
if(!props.imgUrl)return
canvasLoadAddImg(props.imgUrl,value.layers.value[0].id)
}
const getCanvasData = ()=>{
@@ -89,10 +90,40 @@ export default defineComponent({
}
const exportElement = ()=>{
canvasEditor.value.exportImage({isContainBg:true,isContainFixed:false,isCropByBg:true}).then((rv)=>{
dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:false,isCropByBg:true}).then((rv)=>{
downloadBase64Image(rv,'canvas')
})
}
function downloadBase64Image(base64Data, filename) {
// 1. 提取MIME类型自动处理各种Base64前缀
const mimeMatch = base64Data.match(/^data:(.+?);base64,/);
if (!mimeMatch) {
console.error('Invalid Base64 data');
return;
}
// 2. 获取扩展名
const mimeType = mimeMatch[1];
const extension = mimeType.split('/')[1] || 'png';
// 3. 转换Base64为Blob
const byteString = atob(base64Data.split(',')[1]);
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
const blob = new Blob([ab], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${filename}.${extension}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
}
const changeCanvas = ()=>{
emit('canvasChangeGetJSON',{canvasJSON:dataDom.editCanvas.getJSON(),submitDate:5000})
}