Просмотр исходного кода

feat(photography): 实现详情页模板自定义功能

- 添加详情页模板自定义tab组件(暂注释状态)
- 实现商品文字数据结构转换逻辑
- 优化模板数据传递机制,使用sessionStorage避免URL长度限制
- 完善营销编辑组件的数据初始化流程
- 重构模板编辑页面的数据缓存和处理逻辑
- 实现商品文案数据的完整传递和保存功能
panqiuyao 1 день назад
Родитель
Сommit
b2685a0968

+ 26 - 1
frontend/src/views/Photography/detail.vue

@@ -75,6 +75,16 @@
           </div>
         </div>
 
+<!--
+        <div class="service-tab disabled" title="功能开发中">
+          <div class="tab-content">
+            <div class="tab-img flex">
+              <img src="@/assets/images/detail/xqmb.svg" alt="详情页模板自定义" class="tab-icon" />
+            </div>
+            <span class="tab-name">详情页模板自定义</span>
+          </div>
+        </div>
+-->
 
         <div class="service-tab external-tool" title="白底图批量导出" @click="handleWhiteBgExportClick"
           v-log="{ describe: { action: '点击白底图批量导出', service: '白底图批量导出' } }">
@@ -726,10 +736,19 @@ const confirmGoodsSelection = async () => {
       })
     }
 
+    // 将商品文字的 keys 转换为完整的对象结构
+    const templateExcelHeaders = data?.template_excel_headers || []
+    const defaultGoodsTextFallback = []
+    const fullTemplateExcelHeaders = templateExcelHeaders.map(key => {
+      const defaultItem = defaultGoodsTextFallback.find(item => item.key === key)
+      return defaultItem || { key, value: '' }
+    })
+
     // 使用sessionStorage传递数据,避免URL长度限制
     const templateData = {
       customer_template_images: goodsImages,
-      template_image_order: data?.template_image_order || '',
+      template_image_order: data?.template_image_order ||  [],
+      template_excel_headers: data?.template_excel_headers || [], // 传递完整的商品文字数据
       timestamp: Date.now() // 添加时间戳用于标识数据
     }
     sessionStorage.setItem('addTpl_template_data', JSON.stringify(templateData))
@@ -1106,12 +1125,18 @@ const editTemplate = async (template: any) => {
   const templateImageOrder = template.template_image_order || ''
   const templateName = template.template_name || ''
 
+  // 将商品文字的 keys 转换为完整的对象结构
+  const templateExcelHeaders = template.customer_template_excel_headers || []
+  const defaultGoodsTextFallback = []
+  const fullTemplateExcelHeaders = template.template_excel_headers || ''
+
   // 使用sessionStorage传递数据,避免URL长度限制
   const templateData = {
     customer_template_json: customerTemplateJson,
     template_name: templateName,
     customer_template_images: customerTemplateImages,
     template_image_order: templateImageOrder,
+    template_excel_headers: fullTemplateExcelHeaders, // 传递完整的商品文字数据
     timestamp: Date.now() // 添加时间戳用于标识数据
   }
   sessionStorage.setItem('editTpl_template_data', JSON.stringify(templateData))

+ 65 - 59
frontend/src/views/Tpl/Edit/index.vue

@@ -31,7 +31,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from 'vue';
+import { computed, ref, onMounted } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { ElMessage } from 'element-plus';
 import headerBar from "@/components/header-bar/index.vue";
@@ -52,18 +52,51 @@ const templateName = ref('')
 const showNameDialog = ref(false) // 由"保存"触发,而不是进入页面就弹
 const pendingPayload = ref<any | null>(null)
 
-// 编辑模式:从sessionStorage中还原模板数据和名称
-if (isEdit) {
-  const templateDataStr = sessionStorage.getItem('editTpl_template_data')
-  if (templateDataStr) {
+// 编辑模式:数据将在onMounted中的initTemplateData函数中处理
+
+// 默认商品文案(硬编码的默认值)
+const defaultGoodsTextFallback = []
+
+// 从缓存中获取商品文案,如果没有则使用默认值
+const defaultGoodsText = computed(() => {
+  console.log(templateDataCache.value)
+  if (templateDataCache.value?.template_excel_headers && Array.isArray(templateDataCache.value.template_excel_headers)) {
+    // 缓存中已经是完整的商品文案对象结构,直接使用
+    return templateDataCache.value.template_excel_headers
+  }
+  // 如果没有缓存数据,使用默认值
+  return defaultGoodsTextFallback
+})
+
+// 从sessionStorage中解析数据,缓存到组件中
+const templateDataCache = ref(null)
+
+// 在组件挂载时读取sessionStorage数据
+const initTemplateData = () => {
+  // 新增模式数据
+  const addTplDataStr = sessionStorage.getItem('addTpl_template_data')
+  if (addTplDataStr) {
     try {
-      const templateData = JSON.parse(templateDataStr)
-      // 还原画布 JSON
+      const templateData = JSON.parse(addTplDataStr)
+      templateDataCache.value = templateData
+      // 清理sessionStorage,避免数据残留
+      sessionStorage.removeItem('addTpl_template_data')
+    } catch (e) {
+      console.error('解析 addTpl_template_data 失败:', e)
+    }
+  }
+
+  // 编辑模式数据
+  const editTplDataStr = sessionStorage.getItem('editTpl_template_data')
+  if (editTplDataStr) {
+    try {
+      const templateData = JSON.parse(editTplDataStr)
+      templateDataCache.value = templateData
+
+      // 编辑模式:设置画布数据和模板名称
       if (templateData.customer_template_json && Array.isArray(templateData.customer_template_json)) {
         data.value = templateData.customer_template_json
       }
-
-      // 名称直接取 template_name,取不到则为空
       templateName.value = (templateData.template_name || '') as string
 
       // 清理sessionStorage,避免数据残留
@@ -74,65 +107,33 @@ if (isEdit) {
   }
 }
 
-// 默认商品文案
-const defaultGoodsText = [
-  {
-    key: '设计理念',
-    value: '经典凹出兼具动感同时带来轻盈\n步调轻软,松弛自在蔓延\n立体质感让朝气肆意绽放'
-  },
-  {
-    key: '标题',
-    value: '休闲运动'
-  },
-  {
-    key: '帮面',
-    value: '网布+合成革'
-  },
-  {
-    key: '鞋底',
-    value: '橡胶底'
-  },
-  {
-    key: '颜色',
-    value: '黑色'
-  }
-]
-
-// 从sessionStorage中解析 goods_images,如果没有就使用默认示例
+// 从缓存中解析 goods_images,如果没有就使用默认示例
 const defaultGoodsImages = []
 
 const goodsImages = computed(() => {
-  // 新增模式:从sessionStorage获取数据
-  const templateDataStr = sessionStorage.getItem('addTpl_template_data')
-  if (templateDataStr) {
-    try {
-      const templateData = JSON.parse(templateDataStr)
-      if (templateData.customer_template_images && Array.isArray(templateData.customer_template_images)) {
-        // 清理sessionStorage,避免数据残留
-        sessionStorage.removeItem('addTpl_template_data')
-        return templateData.customer_template_images
-      }
-    } catch (e) {
-      console.error('解析 addTpl_template_data 失败:', e)
-    }
+  if (templateDataCache.value?.customer_template_images && Array.isArray(templateDataCache.value.customer_template_images)) {
+    return templateDataCache.value.customer_template_images
   }
-
   // 编辑模式或其他情况返回默认值
   return defaultGoodsImages
 })
 
-// 新增模式:从sessionStorage中获取template_image_order
+// 从缓存中获取template_image_order
 const templateImageOrder = computed(() => {
-  const templateDataStr = sessionStorage.getItem('addTpl_template_data')
-  if (templateDataStr) {
-    try {
-      const templateData = JSON.parse(templateDataStr)
-      return templateData.template_image_order || ''
-    } catch (e) {
-      console.error('解析 addTpl_template_data 失败:', e)
+  return templateDataCache.value?.template_image_order || ''
+})
+
+// 编辑模式:从缓存中获取模板数据
+const editTemplateData = computed(() => {
+  if (isEdit && templateDataCache.value) {
+    return {
+      customer_template_json: templateDataCache.value.customer_template_json || [],
+      template_name: templateDataCache.value.template_name || '',
+      customer_template_images: templateDataCache.value.customer_template_images || [],
+      template_image_order: templateDataCache.value.template_image_order || ''
     }
   }
-  return ''
+  return null
 })
 
 
@@ -159,8 +160,8 @@ const doSave = async (payload: any) => {
     }
     requestData.customer_template_images = goodsImages.value
     requestData.template_image_order = templateImageOrder.value || undefined
-    // 直接从 goodsText 中获取所有商品文字的 key
-    requestData.template_excel_headers = defaultGoodsText
+    // 保存商品文字的 keys
+    requestData.template_excel_headers = defaultGoodsText.value.map(item => item.key)
     await saveCustomerTemplate(requestData)
     ElMessage.success(isEdit ? '模板保存成功' : '模板创建成功')
     router.back()
@@ -197,6 +198,11 @@ const save = (payload: any) => {
 
   doSave(payload)
 }
+
+// 组件挂载时初始化模板数据
+onMounted(() => {
+  initTemplateData()
+})
 </script>
 
 <style lang="scss" scoped>

+ 21 - 4
frontend/src/views/components/marketingEdit/index.vue

@@ -100,6 +100,20 @@ export default {
     }
   },
   watch: {
+    data: {
+      handler(newData, oldData) {
+        // 当data从空数组变为有数据时(编辑模式加载数据),需要重新初始化
+        // 避免在组件初始挂载时重复初始化
+        if (oldData && oldData.length === 0 && newData && newData.length > 0 && this.fcanvas) {
+          this.saveCanvasSnapshot()
+          this.destroyCanvasInstance()
+          this.$nextTick(() => {
+            this.init()
+          })
+        }
+      },
+      deep: true
+    },
     index(newValue, oldValue) {
       if (this.isEmpty) return
       const redirectIndex = this.findEditableCanvas(newValue)
@@ -124,10 +138,13 @@ export default {
     }
   },
   mounted() {
-    if(this.$route?.name === 'editTpl'){
-      this.loadTemplate()
-    }
-    this.init()
+    // 延迟初始化,等待父组件设置数据
+    this.$nextTick(() => {
+      if(this.$route?.name === 'editTpl'){
+        this.loadTemplate()
+      }
+      this.init()
+    })
   },
   activated(){
   },