feat: Add texture management features and update UI components
This commit is contained in:
@@ -34,11 +34,11 @@
|
||||
<div class="brush-section">
|
||||
<div class="section-header">
|
||||
<span>{{ category }}</span>
|
||||
<div class="section-actions" v-if="category === '材质'">
|
||||
<!-- <div class="section-actions" v-if="category === '纹理'">
|
||||
<button class="action-btn" @click="showLibrary = !showLibrary">
|
||||
<i class="icon-library">📚</i> 材质库
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<!-- 针对每个属性,根据其类型渲染合适的控件 -->
|
||||
@@ -163,7 +163,7 @@
|
||||
</template>
|
||||
|
||||
<!-- 文件选择器(用于材质) -->
|
||||
<template v-else-if="prop.type === 'file'">
|
||||
<!-- <template v-else-if="prop.type === 'file'">
|
||||
<div class="file-property">
|
||||
<div class="file-header">
|
||||
<span>{{ prop.name }}</span>
|
||||
@@ -191,7 +191,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template> -->
|
||||
|
||||
<!-- 材质网格选择器 -->
|
||||
<template v-else-if="prop.type === 'texture-grid'">
|
||||
@@ -200,6 +200,7 @@
|
||||
<span>{{ prop.name }}</span>
|
||||
</div>
|
||||
<div class="texture-grid">
|
||||
<!-- 预设纹理 -->
|
||||
<div
|
||||
v-for="texture in prop.options"
|
||||
:key="texture.value"
|
||||
@@ -212,8 +213,17 @@
|
||||
:alt="texture.label"
|
||||
class="texture-thumbnail"
|
||||
/>
|
||||
<span class="texture-label">{{ texture.label }}</span>
|
||||
<!-- <span class="texture-label">{{ texture.label }}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="uploaded-textures-section">
|
||||
<div class="uploaded-textures-divider">
|
||||
<span>{{ $t("上传的纹理") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="texture-grid">
|
||||
<!-- 上传的纹理缓存区域 -->
|
||||
|
||||
<!-- 自定义纹理上传按钮 -->
|
||||
<div
|
||||
class="texture-item upload-item"
|
||||
@@ -222,8 +232,30 @@
|
||||
<div class="upload-icon">
|
||||
<span>+</span>
|
||||
</div>
|
||||
<span class="texture-label">上传纹理</span>
|
||||
<span class="texture-label">{{ $t("上传纹理") }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="textureId in brushStore.state.uploadedTextures"
|
||||
:key="textureId"
|
||||
class="texture-item upload-item"
|
||||
:class="{ active: prop.value === textureId }"
|
||||
@click="handleTextureSelect(textureId)"
|
||||
>
|
||||
<img
|
||||
:src="getUploadedTexturePreview(textureId)"
|
||||
:alt="getUploadedTextureName(textureId)"
|
||||
class="texture-thumbnail"
|
||||
/>
|
||||
<!-- 删除按钮 -->
|
||||
<div
|
||||
class="texture-remove-btn"
|
||||
@click.stop="removeUploadedTexture(textureId)"
|
||||
:title="$t('删除纹理')"
|
||||
>
|
||||
<span>×</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏的文件输入 -->
|
||||
<input
|
||||
ref="textureFileInput"
|
||||
@@ -261,7 +293,7 @@
|
||||
</template>
|
||||
|
||||
<!-- 材质库弹窗 -->
|
||||
<div
|
||||
<!-- <div
|
||||
v-if="showLibrary"
|
||||
class="texture-library-overlay"
|
||||
@click.self="showLibrary = false"
|
||||
@@ -312,7 +344,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 笔刷预设 -->
|
||||
<div class="brush-section">
|
||||
@@ -373,6 +405,9 @@ const toolManager = inject("toolManager");
|
||||
// 命令管理器
|
||||
const commandManager = inject("commandManager");
|
||||
|
||||
// 注入纹理预设管理器
|
||||
const texturePresetManager = inject("texturePresetManager");
|
||||
|
||||
// 计算属性:按分类获取当前笔刷可配置属性
|
||||
const propertiesByCategory = computed(() => {
|
||||
return BrushStore.getPropertiesByCategory();
|
||||
@@ -460,7 +495,11 @@ function handleTextureSelect(textureId) {
|
||||
|
||||
// 触发纹理上传文件选择
|
||||
function triggerTextureUpload() {
|
||||
handleFileSelect("texturePath");
|
||||
if (textureFileInput.value.length) {
|
||||
textureFileInput.value[0].click();
|
||||
return;
|
||||
}
|
||||
textureFileInput.value.click();
|
||||
}
|
||||
|
||||
// 处理纹理文件上传
|
||||
@@ -482,8 +521,7 @@ async function handleTextureUpload(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取纹理预设管理器和笔刷管理器
|
||||
const texturePresetManager = toolManager?.texturePresetManager;
|
||||
// 获取笔刷管理器
|
||||
const brushManager = toolManager?.brushManager;
|
||||
|
||||
if (!texturePresetManager || !brushManager) {
|
||||
@@ -501,7 +539,14 @@ async function handleTextureUpload(event) {
|
||||
brushManager: brushManager,
|
||||
});
|
||||
|
||||
await commandManager.execute(command);
|
||||
const result = await commandManager.execute(command);
|
||||
|
||||
// 上传成功后,将纹理ID添加到缓存列表
|
||||
if (result && result.textureId) {
|
||||
brushStore.cacheUploadedTexture(result.textureId);
|
||||
// 自动选择新上传的纹理
|
||||
handleTextureSelect(result.textureId);
|
||||
}
|
||||
|
||||
// 清空文件输入,允许重复上传同一文件
|
||||
event.target.value = "";
|
||||
@@ -511,6 +556,42 @@ async function handleTextureUpload(event) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取上传纹理的预览图
|
||||
function getUploadedTexturePreview(textureId) {
|
||||
const texture = texturePresetManager?.getTextureById?.(textureId);
|
||||
return texture?.preview || texture?.path || "/placeholder-texture.png";
|
||||
}
|
||||
|
||||
// 获取上传纹理的名称
|
||||
function getUploadedTextureName(textureId) {
|
||||
const texture = texturePresetManager?.getTextureById?.(textureId);
|
||||
return texture?.name || "未知纹理";
|
||||
}
|
||||
|
||||
// 删除上传的纹理
|
||||
function removeUploadedTexture(textureId) {
|
||||
try {
|
||||
// 从缓存中移除
|
||||
brushStore.removeCachedTexture(textureId);
|
||||
|
||||
// 从纹理预设管理器中删除
|
||||
if (texturePresetManager?.removeCustomTexture) {
|
||||
texturePresetManager.removeCustomTexture(textureId);
|
||||
}
|
||||
|
||||
// 如果当前选中的是被删除的纹理,清空选择
|
||||
const currentProperty = propertiesByCategory.value?.["纹理"]?.find(
|
||||
(p) => p.id === "textureSelector"
|
||||
);
|
||||
if (currentProperty?.value === textureId) {
|
||||
handlePropertyChange("textureSelector", null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("删除纹理失败:", error);
|
||||
alert(`删除失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化属性值显示
|
||||
function formatPropertyValue(prop) {
|
||||
if (prop.type === "slider") {
|
||||
@@ -560,30 +641,6 @@ function setBrushTypeWithCommand(type) {
|
||||
commandManager.execute(command, { nonUndoable: true }); // 不需要撤销
|
||||
}
|
||||
|
||||
// 处理文件选择
|
||||
function handleFileSelect(propId) {
|
||||
// 创建一个文件输入元素
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "image/*";
|
||||
|
||||
input.onchange = (event) => {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
const file = event.target.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
// 更新属性值
|
||||
handlePropertyChange(propId, e.target.result);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
input.click();
|
||||
}
|
||||
|
||||
// 获取笔刷预览样式
|
||||
function getBrushPreviewStyle(brush) {
|
||||
const baseStyle = {
|
||||
@@ -1360,7 +1417,7 @@ const brushStore = BrushStore;
|
||||
|
||||
.texture-thumbnail {
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
@@ -1611,10 +1668,97 @@ const brushStore = BrushStore;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 移除内部动画,只保留整体动画 */
|
||||
.brush-type-grid,
|
||||
.property-list,
|
||||
.presets-container {
|
||||
animation: none;
|
||||
/* 上传纹理缓存区域样式 */
|
||||
.uploaded-textures-section {
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.uploaded-textures-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.uploaded-textures-divider::before,
|
||||
.uploaded-textures-divider::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
rgba(0, 0, 0, 0.08),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.uploaded-textures-divider span {
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
background-color: #fff;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.uploaded-textures-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(75px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.uploaded-texture-item {
|
||||
position: relative;
|
||||
border: 1px solid rgba(108, 117, 125, 0.2);
|
||||
background-color: rgba(248, 249, 250, 0.8);
|
||||
}
|
||||
|
||||
.uploaded-texture-item:hover {
|
||||
border-color: rgba(66, 133, 244, 0.3);
|
||||
background-color: rgba(66, 133, 244, 0.05);
|
||||
}
|
||||
|
||||
.uploaded-texture-item.active {
|
||||
background-color: rgba(66, 133, 244, 0.15);
|
||||
border-color: rgba(66, 133, 244, 0.4);
|
||||
}
|
||||
|
||||
.texture-remove-btn {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: rgba(244, 67, 54, 0.9);
|
||||
border: 1px solid #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 10;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.texture-remove-btn span {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.uploaded-texture-item:hover .texture-remove-btn {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.texture-remove-btn:hover {
|
||||
background-color: rgba(244, 67, 54, 1);
|
||||
transform: scale(1.2) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user