zck 2 weeks ago
parent
commit
a3ed4c6b62
1 changed files with 371 additions and 59 deletions
  1. 371 59
      src/view/TemplateManagement/TemplateDesign.vue

+ 371 - 59
src/view/TemplateManagement/TemplateDesign.vue

@@ -2,10 +2,29 @@
   <div class="template-design-container">
     <!-- 左侧工具栏 -->
     <div class="toolbar">
+      <!-- 主菜单 -->
+      <div class="main-menu">
+        <el-menu
+          :default-active="currentView"
+          class="main-menu"
+          mode="vertical"
+          @select="handleMenuSelect"
+        >
+          <el-menu-item index="design">
+            <el-icon><Document /></el-icon>
+            <span>设计模板</span>
+          </el-menu-item>
+          <el-menu-item index="library">
+            <el-icon><Picture /></el-icon>
+            <span>模板库</span>
+          </el-menu-item>
+        </el-menu>
+      </div>
+      
       <!-- 标签页 + 可滚动内容(保存按钮固定在底部) -->
-      <div class="toolbar-tabs">
+      <div class="toolbar-tabs" v-if="currentView === 'design'">
         <el-tabs v-model="activeTab" type="card" @tab-click="handleTabClick">
-        <el-tab-pane label="模版设计" name="design">
+          <el-tab-pane label="模版设计" name="design">
           <el-upload
             class="custom-upload"
             drag
@@ -314,75 +333,121 @@
         </el-tabs>
       </div>
 
-      <el-divider class="save-template-divider" />
+      <el-divider class="save-template-divider" v-if="currentView === 'design'" />
       <el-button
         type="primary"
         class="save-template-btn"
         @click="saveTemplate"
+        v-if="currentView === 'design'"
       >
         生成模版
       </el-button>
     </div>
     
-    <!-- 中间画布区域 -->
-    <div class="canvas-area" ref="canvasAreaRef">
-      <div class="canvas-wrapper">
-        <div 
-          ref="canvasRef"
-          class="canvas"
-          :style="{
-            width: canvasWidth + 'px',
-            height: canvasHeight + 'px'
-          }"
-          @mousedown="handleCanvasMouseDown"
-          @mousemove="handleCanvasMouseMove"
-          @mouseup="handleCanvasMouseUp"
-          @mouseleave="handleCanvasMouseUp"
-          @wheel="handleCanvasWheel"
-        >
-          <div
-            v-for="(layer, index) in layers"
-            :key="layer.id"
-            class="layer"
-            :class="{ 
-              'selected': selectedLayerId === layer.id,
-              'text-layer': layer.type === 'text'
+    <!-- 右侧内容区域 -->
+    <div class="content-area">
+      <!-- 中间画布区域 -->
+      <div class="canvas-area" ref="canvasAreaRef" v-if="currentView === 'design'">
+        <div class="canvas-wrapper">
+          <div 
+            ref="canvasRef"
+            class="canvas"
+            :style="{
+              width: canvasWidth + 'px',
+              height: canvasHeight + 'px'
             }"
-            :style="getLayerStyle(layer)"
-            @mousedown.stop="handleLayerMouseDown($event, layer)"
-            @dblclick.stop="handleLayerDblClick($event, layer)"
+            @mousedown="handleCanvasMouseDown"
+            @mousemove="handleCanvasMouseMove"
+            @mouseup="handleCanvasMouseUp"
+            @mouseleave="handleCanvasMouseUp"
+            @wheel="handleCanvasWheel"
           >
-            <!-- 图片图层 -->
-            <template v-if="layer.type !== 'text'">
-              <img :src="layer.url" :alt="layer.name" draggable="false" />
-            </template>
-            
-            <!-- 文字图层 -->
-            <template v-else>
-              <div 
-                class="text-content"
-                :style="getTextStyle(layer)"
-                contenteditable="false"
-              >
-                {{ layer.text }}
+            <div
+              v-for="(layer, index) in layers"
+              :key="layer.id"
+              class="layer"
+              :class="{ 
+                'selected': selectedLayerId === layer.id,
+                'text-layer': layer.type === 'text'
+              }"
+              :style="getLayerStyle(layer)"
+              @mousedown.stop="handleLayerMouseDown($event, layer)"
+              @dblclick.stop="handleLayerDblClick($event, layer)"
+            >
+              <!-- 图片图层 -->
+              <template v-if="layer.type !== 'text'">
+                <img :src="layer.url" :alt="layer.name" draggable="false" />
+              </template>
+              
+              <!-- 文字图层 -->
+              <template v-else>
+                <div 
+                  class="text-content"
+                  :style="getTextStyle(layer)"
+                  contenteditable="false"
+                >
+                  {{ layer.text }}
+                </div>
+              </template>
+              
+              <!-- 选中状态的调整手柄 -->
+              <template v-if="selectedLayerId === layer.id">
+                <div class="resize-handle nw" @mousedown.stop="startResize($event, 'nw')"></div>
+                <div class="resize-handle ne" @mousedown.stop="startResize($event, 'ne')"></div>
+                <div class="resize-handle sw" @mousedown.stop="startResize($event, 'sw')"></div>
+                <div class="resize-handle se" @mousedown.stop="startResize($event, 'se')"></div>
+                <div class="rotate-handle" @mousedown.stop="startRotate($event)"></div>
+              </template>
+            </div>
+          </div>
+        </div>
+      </div>
+      
+      <!-- 模板库视图 -->
+      <div class="template-library-view" v-if="currentView === 'library'">
+        <div class="library-header">
+          <h3>模板库</h3>
+          <el-input
+            v-model="templateSearch"
+            size="small"
+            clearable
+            :prefix-icon="Search"
+            placeholder="搜索模板"
+            @input="handleTemplateSearch"
+            style="width: 300px;"
+          />
+        </div>
+        
+        <el-divider />
+        
+        <div class="template-list">
+          <el-skeleton v-if="templatesLoading" :rows="8" animated />
+          <div v-else-if="templates.length === 0" class="empty-tip">
+            暂无模板
+          </div>
+          <div v-else class="template-grid">
+            <div
+              v-for="template in templates"
+              :key="template.id"
+              class="template-item"
+              @click="useTemplate(template)"
+            >
+              <div class="template-preview">
+                <img :src="template.template_image_url" :alt="template.template_name" />
+              </div>
+              <div class="template-info">
+                <span class="template-name">{{ template.template_name }}</span>
+                <el-button size="small" type="primary" @click.stop="useTemplate(template)">
+                  使用模板
+                </el-button>
               </div>
-            </template>
-            
-            <!-- 选中状态的调整手柄 -->
-            <template v-if="selectedLayerId === layer.id">
-              <div class="resize-handle nw" @mousedown.stop="startResize($event, 'nw')"></div>
-              <div class="resize-handle ne" @mousedown.stop="startResize($event, 'ne')"></div>
-              <div class="resize-handle sw" @mousedown.stop="startResize($event, 'sw')"></div>
-              <div class="resize-handle se" @mousedown.stop="startResize($event, 'se')"></div>
-              <div class="rotate-handle" @mousedown.stop="startRotate($event)"></div>
-            </template>
+            </div>
           </div>
         </div>
       </div>
-    </div>
-    
-    <!-- 右侧图层面板 -->
-    <div class="layer-panel">
+      
+      <!-- 右侧图层面板 -->
+      <div class="layer-panel" v-if="currentView === 'design'">
       <h3>图层</h3>
       <div class="layer-actions">
         <el-button size="small" @click="moveLayerUp" :disabled="!canMoveUp">
@@ -445,6 +510,7 @@
         </div>
       </div>
     </div>
+    </div>
   </div>
 </template>
 
@@ -452,18 +518,24 @@
 import { ref, computed, reactive, onMounted, onUnmounted } from 'vue'
 import { ElMessage } from 'element-plus'
 import { Pointer, Rank, ArrowUp, ArrowDown, Delete, View, Hide, Lock, Unlock, Plus, Document, Picture, Refresh, Upload, Search, ArrowLeft } from '@element-plus/icons-vue'
-import { Material_List, Template_Material_Add } from '@/api/mes/job'
+import { Material_List, Template_Material_Add, product_template } from '@/api/mes/job'
 
 const canvasRef = ref(null)
 const canvasAreaRef = ref(null)
 const layerListRef = ref(null)
 const currentTool = ref('select')
 const activeTab = ref('design') // 'design' 或 'material'
+const currentView = ref('design') // 'design' 或 'library'
 const canvasWidth = ref(600)
 const canvasHeight = ref(450)
 const canvasRatio = ref('4:3')
 const zoomLevel = ref(100)
 
+// 模板库状态
+const templates = ref([])
+const templatesLoading = ref(false)
+const templateSearch = ref('')
+
 const layers = ref([])
 const selectedLayerId = ref(null)
 const maintainAspectRatio = ref(false) // 默认不锁定宽高比例,宽高可自由调整
@@ -799,6 +871,48 @@ const handleTabClick = (tab) => {
   }
 }
 
+// 菜单选择事件处理
+const handleMenuSelect = (key) => {
+  currentView.value = key
+  if (key === 'library') {
+    fetchTemplates()
+  }
+}
+
+// 获取模板库数据
+const fetchTemplates = async () => {
+  try {
+    templatesLoading.value = true
+    // 调用实际的模板库API
+    const response = await product_template()
+    const data = response
+    if (data.code === 0) {
+      templates.value = data.data
+    } else {
+      ElMessage.error('获取模板库失败')
+    }
+  } catch (error) {
+    console.error('获取模板库失败:', error)
+    ElMessage.error('获取模板库失败')
+  } finally {
+    templatesLoading.value = false
+  }
+}
+
+// 搜索模板
+const handleTemplateSearch = () => {
+  // 这里应该根据搜索词过滤模板
+  // 暂时不实现具体逻辑
+}
+
+// 使用模板
+const useTemplate = (template) => {
+  // 这里应该根据模板数据初始化画布
+  ElMessage.success(`使用模板: ${template.template_name}`)
+  // 切换回设计视图
+  currentView.value = 'design'
+}
+
 // 点击素材添加到画布
 const addMaterialToCanvas = (material) => {
   const img = new Image()
@@ -1614,8 +1728,206 @@ const handleCanvasWheel = (e) => {
 .property-item span {
   width: 40px;
   font-size: 11px;
-  color: #666;
-  flex-shrink: 0;
+}
+
+/* 主菜单样式 */
+.main-menu {
+  margin-bottom: 12px;
+  border-bottom: 1px solid #e5e5e5;
+}
+
+.main-menu :deep(.el-menu) {
+  border-bottom: none;
+}
+
+.main-menu :deep(.el-menu-item) {
+  height: 40px;
+  line-height: 40px;
+  font-size: 14px;
+}
+
+.main-menu :deep(.el-menu-item.is-active) {
+  color: #409eff;
+  background-color: #ecf5ff;
+}
+
+/* 模板库样式 */
+.template-library {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+}
+
+.library-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 8px;
+}
+
+.library-header h3 {
+  margin: 0;
+  font-size: 14px;
+  color: #333;
+}
+
+.template-list {
+  flex: 1;
+  min-height: 0;
+  overflow-y: auto;
+}
+
+.template-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 12px;
+  padding: 8px 0;
+}
+
+.template-item {
+  background-color: #f9f9f9;
+  border: 1px solid #e5e5e5;
+  border-radius: 8px;
+  overflow: hidden;
+  transition: all 0.2s;
+  cursor: pointer;
+}
+
+.template-item:hover {
+  border-color: #409eff;
+  transform: translateY(-2px);
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.template-preview {
+  height: 120px;
+  overflow: hidden;
+}
+
+.template-preview img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.template-info {
+  padding: 8px;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.template-name {
+  font-size: 12px;
+  color: #333;
+  font-weight: 500;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.template-info .el-button {
+  width: 100%;
+  font-size: 12px;
+  padding: 4px 0;
+}
+
+/* 右侧内容区域 */
+.content-area {
+  flex: 1;
+  display: flex;
+  background-color: #f5f5f5;
+  overflow: hidden;
+}
+
+.content-area:has(.template-library-view) {
+  flex-direction: column;
+}
+
+/* 模板库视图 */
+.template-library-view {
+  flex: 1;
+  padding: 20px;
+  overflow-y: auto;
+}
+
+.template-library-view .library-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 16px;
+}
+
+.template-library-view .library-header h3 {
+  margin: 0;
+  font-size: 16px;
+  color: #333;
+}
+
+.template-list {
+  width: 100%;
+}
+
+.template-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+  gap: 20px;
+}
+
+.template-item {
+  background-color: #fff;
+  border: 1px solid #e5e5e5;
+  border-radius: 8px;
+  overflow: hidden;
+  transition: all 0.2s;
+  cursor: pointer;
+}
+
+.template-item:hover {
+  border-color: #409eff;
+  transform: translateY(-2px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.template-preview {
+  height: 180px;
+  overflow: hidden;
+}
+
+.template-preview img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.template-info {
+  padding: 12px;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.template-name {
+  font-size: 14px;
+  font-weight: 500;
+  color: #333;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.template-meta {
+  display: flex;
+  gap: 8px;
+  font-size: 12px;
+  color: #999;
+}
+
+.template-style, .template-type {
+  background-color: #f5f5f5;
+  padding: 2px 8px;
+  border-radius: 12px;
 }
 
 .property-item .el-input-number {