Преглед изворни кода

refactor(photography): 提取货号选择弹窗为独立组件

- 将货号选择弹窗逻辑从 detail.vue 移至 GoodsSelectDialog 组件
- 移除 detail.vue 中的内联弹窗模板和相关状态管理代码
- 更新组件通信方式,使用事件传递处理弹窗确认和取消操作
- 保留原有的货号选择、图片预览和模板生成功能
- 添加分页功能和选中货号信息显示
- 在 editRow.vue 中增加动作名称修改警告提示
panqiuyao пре 18 часа
родитељ
комит
bcdb2e0b7d

+ 518 - 0
frontend/src/views/Photography/components/GoodsSelectDialog.vue

@@ -0,0 +1,518 @@
+<template>
+  <el-dialog
+    :model-value="visible"
+    title="请选择一个货号作为自定义商品的模板"
+    width="60%"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    custom-class="goods-select-dialog"
+    @update:model-value="handleDialogChange"
+  >
+
+    <template #title>
+      <span>请选择一个货号作为自定义商品的模板</span>
+      <div class="fs-12 c-666">
+        *请注意,选择货号后,该模板会自动使用此货号的相关配置!<span v-if="selectedGoodsArtNo">选中货号 {{ selectedGoodsArtNo }} 包含 {{ getSelectedGoodsImageCount }} 张图片({{ getSelectedGoodsImageName.join(',') }})</span>
+      </div>
+    </template>
+    <div class="goods-select-content">
+
+
+      <el-radio-group :model-value="selectedGoodsArtNo" class="goods-radio-group">
+        <div
+          v-for="item in goodsList"
+          :key="item.goods_art_no"
+          class="goods-item"
+          :class="{ 'selected': selectedGoodsArtNo === item.goods_art_no }"
+          @click="item.syncConfig && handleGoodsSelection(item.goods_art_no)"
+        >
+          <div class="goods-item-header">
+            <div class="goods-item-left flex left">
+              <el-radio
+                :label="item.goods_art_no"
+                class="goods-radio"
+                :disabled="!item.syncConfig"
+              ></el-radio>
+              <span class="goods-art-no">{{ item.goods_art_no }}</span>
+
+              <div class="goods-item-meta">
+                <span class="action-time flex left">
+                  <img src="@/assets/images/processImage.vue/riq.png" />
+                  {{ getTime(item.action_time) }}
+                </span>
+                <span class="image-count mar-left-10 flex left">
+                  <img src="@/assets/images/processImage.vue/tup.png" />
+                  {{ item.items?.length || 0 }}张图片
+                </span>
+                <span v-if="!item.syncConfig" class="mar-left-10 c-FF4C00">无法读取到该商品拍摄配置,无法选择</span>
+              </div>
+            </div>
+          </div>
+          <div class="goods-item-images">
+            <div
+              v-for="(image, index) in item.items"
+              :key="image.action_id || image.action_name"
+              class="goods-item_image"
+            >
+              <span class="tag" v-if="!image.PhotoRecord?.image_path">{{
+                image.action_name
+              }}</span>
+              <el-image
+                v-if="image.PhotoRecord?.image_path"
+                :src="getFilePath(image.PhotoRecord.image_path)"
+                :preview-src-list="getPreviewImageList(item)"
+                :initial-index="getPreviewIndex(item, index)"
+                class="preview-image"
+                fit="contain"
+                :preview-teleported="true"
+                lazy
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <span class="tag">{{ image.action_name }}</span>
+                  </div>
+                </template>
+              </el-image>
+              <div class="image-placeholder">
+                <span class="tag">{{ image.action_name }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </el-radio-group>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <div class="flex between" style="width: 100%">
+          <div class="fs-12 c-666">
+           </div>
+          <div style="display:flex; align-items:center; ">
+            <div class="pagination-container" style="padding: 0 10px;">
+              <el-pagination
+                background
+                layout="prev, pager, next"
+                :page-size="pageSize"
+                :current-page="currentPage"
+                :page-count="totalPages"
+                @current-change="onCurrentChange"
+              />
+            </div>
+            <el-button @click="handleCancel">取消</el-button>
+            <el-button
+              type="primary"
+              :loading="goodsGenerateLoading"
+              :disabled="goodsGenerateLoading"
+              @click="handleConfirm"
+            >
+              {{ goodsGenerateLoading ? '正在生成自定义商品模版' : '确定' }}
+            </el-button>
+          </div>
+        </div>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { computed, defineProps, defineEmits, ref, watch } from 'vue'
+import tokenInfo from '@/stores/modules/token'
+import client from '@/stores/modules/client'
+import icpList from '@/utils/ipc'
+import usePhotography from '../mixin/usePhotography'
+import { ElMessage } from 'element-plus'
+
+interface Props {
+  visible: boolean
+}
+
+interface Emits {
+  (e: 'update:visible', value: boolean): void
+  (e: 'confirm', selectedGoodsArtNo: string): void
+  (e: 'cancel'): void
+}
+
+const props = defineProps<Props>()
+const emit = defineEmits<Emits>()
+
+const clientStore = client()
+
+// 使用 usePhotography 中的状态与方法,复用已有逻辑
+const {
+  goodsList,
+  getPhotoRecords,
+  getTime,
+  getFilePath,
+  loading,
+  currentPage,
+  pageSize,
+  totalPages,
+} = usePhotography()
+const selectedGoodsArtNo = ref<string>('')
+const goodsGenerateLoading = ref(false)
+
+// getPhotoRecords 使用 usePhotography 中的实现(通过解构得到)
+
+// 根据选中的货号调用后端生成商品模板
+const generateGoodsTemplate = (goodsArtNo: string) => {
+  return new Promise<any>((resolve, reject) => {
+    try {
+      console.log('调用 get_goods_image_json, goods_art_no:', goodsArtNo)
+
+      // 按后端要求带上 token(参考其他生成类接口)
+      const tokenStore = tokenInfo()
+      const token = (tokenStore as any)?.getToken || ''
+
+      // 先清理监听,避免重复回调
+      clientStore.ipc.removeAllListeners(icpList.generate.getGoodsImageJson)
+
+      clientStore.ipc.send(icpList.generate.getGoodsImageJson, {
+        goods_art_no: goodsArtNo,
+        token,
+      })
+
+      clientStore.ipc.on(icpList.generate.getGoodsImageJson, (_event, result) => {
+        clientStore.ipc.removeAllListeners(icpList.generate.getGoodsImageJson)
+        console.log('get_goods_image_json result:', result)
+        if (result && result.code === 0) {
+          resolve(result.data)
+        } else {
+          const msg = result?.msg || '商品模板生成失败'
+          ElMessage.error(msg)
+          reject(new Error(msg))
+        }
+      })
+    } catch (error: any) {
+      ElMessage.error(error?.message || '商品模板生成异常')
+      reject(error)
+    }
+  })
+}
+
+// 监听弹框显示状态,当显示时获取数据
+watch(() => props.visible, (newVisible) => {
+  if (newVisible) {
+    // 重置状态
+    selectedGoodsArtNo.value = ''
+    goodsGenerateLoading.value = false
+    getPhotoRecords()
+  }
+})
+
+// 计算属性:获取选中货号的图片数量
+const getSelectedGoodsImageCount = computed(() => {
+  if (!selectedGoodsArtNo.value) return 0
+
+  const selectedItem = goodsList.value.find(
+    (item: any) => item.goods_art_no === selectedGoodsArtNo.value
+  )
+
+  return selectedItem?.items?.length || 0
+})
+
+// 计算属性:获取选中货号的步骤
+const getSelectedGoodsImageName = computed(() => {
+  if (!selectedGoodsArtNo.value) return []
+
+  const selectedItem = goodsList.value.find(
+    (item: any) => item.goods_art_no === selectedGoodsArtNo.value
+  )
+
+  return selectedItem?.items?.map((item: any) => item.action_name) || []
+})
+
+// 获取预览图片列表(只包含有图片路径的,保持原始顺序)
+const getPreviewImageList = (item: any) => {
+  if (!item || !item.items) return []
+  return item.items
+    .filter((img: any) => img.PhotoRecord?.image_path)
+    .map((img: any) => getFilePath(img.PhotoRecord.image_path))
+}
+
+// 获取当前图片在预览列表中的索引
+const getPreviewIndex = (item: any, currentIndex: number) => {
+  if (!item || !item.items) return 0
+  // 计算当前图片在过滤后的预览列表中的索引
+  let previewIndex = 0
+  for (let i = 0; i <= currentIndex; i++) {
+    if (item.items[i]?.PhotoRecord?.image_path) {
+      if (i === currentIndex) break
+      previewIndex++
+    }
+  }
+  return previewIndex
+}
+
+const handleDialogChange = (value: boolean) => {
+  emit('update:visible', value)
+}
+
+// 当页码改变时,按照 processImage.vue 的风格发起分页请求
+const onCurrentChange = (page: number) => {
+  // 切换分页时清除当前已选货号,避免跨页误选
+  selectedGoodsArtNo.value = ''
+  getPhotoRecords({ page })
+}
+
+const handleGoodsSelection = (goodsArtNo: string) => {
+  const item = goodsList.value.find((g: any) => g.goods_art_no === goodsArtNo)
+  if (!item) return
+  if (!item.syncConfig) {
+    ElMessage.warning('该货号配置不完整,无法选择')
+    return
+  }
+  selectedGoodsArtNo.value = goodsArtNo
+}
+
+const handleConfirm = async () => {
+  if (!selectedGoodsArtNo.value) {
+    ElMessage.warning('请选择一个货号')
+    return
+  }
+
+  console.log('选中的货号:', selectedGoodsArtNo.value)
+  goodsGenerateLoading.value = true
+
+  try {
+    const data = await generateGoodsTemplate(selectedGoodsArtNo.value)
+    console.log('商品模版数据', data)
+
+    // 组装 goods_images 数组:[{ key: 模板顺序项, value: 对应图片地址 }, ...]
+    const goodsImages: Array<{ key: string; value: string }> = []
+    const orderArr = (data?.template_image_order || '')
+      .split(',')
+      .map((s: string) => s.trim())
+      .filter((s: string) => s)
+    const imagesArr = Array.isArray(data?.customer_template_images)
+      ? data.customer_template_images
+      : []
+
+    const len = Math.min(orderArr.length, imagesArr.length)
+    for (let i = 0; i < len; i++) {
+      goodsImages.push({
+        key: orderArr[i],
+        value: imagesArr[i],
+      })
+    }
+
+    // 商品文字字段直接由后端返回并保存在 sessionStorage 中(无需本地映射)
+
+    // 使用sessionStorage传递数据,避免URL长度限制
+    const templateData = {
+      customer_template_images: goodsImages,
+      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))
+
+    // 关闭弹框并通知父组件
+    emit('update:visible', false)
+    emit('confirm', selectedGoodsArtNo.value)
+  } finally {
+    goodsGenerateLoading.value = false
+  }
+}
+
+const handleCancel = () => {
+  emit('update:visible', false)
+  emit('cancel')
+}
+</script>
+
+<style lang="scss">
+// 货号选择弹窗样式(非 scoped,因为弹窗挂载在 body 上)
+.goods-select-dialog {
+  max-width: 60vw;
+
+  .el-dialog__body {
+    padding: 20px;
+    max-height: 70vh;
+    overflow-y: auto;
+    height: 60vh;
+  }
+
+  .goods-select-content {
+    width: 100%;
+    .goods-radio-group {
+      width: 100%;
+      display: flex;
+      flex-direction: column;
+      gap: 20px;
+    }
+
+    .goods-item {
+      background: #FFFFFF;
+      box-shadow: 0px 2px 4px 0px rgba(23, 33, 71, 0.1);
+      border-radius: 10px;
+      border: 1px solid #D9DEE6;
+      margin-bottom: 20px;
+      cursor: pointer;
+      transition: all 0.3s;
+      width: 100%;
+
+      .goods-radio {
+        width: 30px;
+        margin: 0;
+        padding: 0;
+        position: relative;
+        margin-top: -10px;
+
+        .el-radio__input {
+          position: absolute;
+          top: 14px;
+          left: 18px;
+          z-index: 10;
+          transform: scale(1);
+        }
+
+        .el-radio__inner {
+          border-width: 2px;
+        }
+
+        &.is-checked {
+          .el-radio__inner {
+            background-color: #409eff;
+            border-color: #409eff;
+          }
+        }
+
+        .el-radio__label {
+          width: 100%;
+          padding-left: 40px;
+          display: none;
+        }
+      }
+
+      .goods-item-header {
+        display: flex;
+        justify-content: flex-start;
+        align-items: center;
+        height: 40px;
+        background: linear-gradient(90deg, #F4ECFF 0%, #DFEDFF 100%);
+        border-radius: 10px 10px 0px 0px;
+
+        .goods-item-left {
+          display: flex;
+          align-items: center;
+          gap: 16px;
+
+          .goods-art-no {
+            font-size: 16px;
+            font-weight: 500;
+            color: #333;
+          }
+
+          .goods-item-meta {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            font-size: 12px;
+            color: #666;
+
+            img {
+              height: 14px;
+              margin-right: 2px;
+            }
+
+            .action-time {
+              color: #666;
+            }
+
+            .image-count {
+              color: #666;
+            }
+          }
+        }
+      }
+
+      .goods-item-images {
+        display: grid;
+        grid-template-columns: repeat(5, 1fr);
+        gap: 10px;
+        padding: 15px;
+        border-top: 1px solid #f0f0f0;
+        overflow-x: auto;
+        width: 100%;
+        @media (min-width: 1200px) {
+          grid-template-columns: repeat(5, 1fr);
+        }
+
+        @media (max-width: 768px) {
+          grid-template-columns: repeat(3, 1fr);
+        }
+      }
+
+      .goods-item_image {
+        position: relative;
+        width: 100%;
+        aspect-ratio: 1;
+        background: #F7F7F7;
+        border-radius: 10px;
+        overflow: hidden;
+        cursor: pointer;
+        border: 1px solid #D9DEE6;
+        transition: all 0.3s;
+
+        .tag {
+          color: #bbb;
+          position: absolute;
+          left: 0;
+          right: 0;
+          top: 50%;
+          margin-top: -10px;
+          line-height: 20px;
+          text-align: center;
+          font-size: 12px;
+          z-index: 1;
+          pointer-events: none;
+        }
+
+        .preview-image {
+          width: 100%;
+          height: 100%;
+
+          .el-image__inner {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+          }
+        }
+
+        .image-placeholder {
+          width: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background: #F7F7F7;
+          position: absolute;
+          bottom: 0;
+          height: 30px;
+          line-height: 30px;
+        }
+
+        .image-slot {
+          width: 100%;
+          height: 100%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background: #F7F7F7;
+        }
+
+        &:hover {
+          border-color: #409eff;
+          transform: scale(1.02);
+          box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
+        }
+      }
+    }
+  }
+
+  .dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+  }
+}
+/* empty block removed */
+</style>

+ 2 - 0
frontend/src/views/Photography/components/editRow.vue

@@ -3,9 +3,11 @@
     <div class="config-type">参数值编辑:
       <!-- <el-checkbox v-model="isDefault">开启运动调试</el-checkbox>-->
     </div>
+    <span class="fs-12  mar-left-10 mar-top-10" v-if="id"  style="position: relative; top:5px">动作名称修改后,可能会导致自定义模板生成失效!</span>
     <el-form class="editForm" :model="editRowData"  v-loading="captureLoading" label-width="100px" >
       <el-form-item label="动作名称">
         <el-input v-model="editRowData.action_name" :disabled="editRowData.is_system" style="width: 170px;"/>
+
       </el-form-item>
       <el-form-item label="是否拍照" v-if="!editRowData.is_system">
         <el-radio-group v-model="editRowData.take_picture">

+ 19 - 432
frontend/src/views/Photography/detail.vue

@@ -470,63 +470,11 @@
     @confirm="handleScenePromptConfirm" @cancel="scenePromptDialogVisible = false" />
 
   <!-- 货号选择弹窗 -->
-  <el-dialog v-model="goodsSelectDialogVisible" title="请选择一个货号作为自定义商品的模板" width="60%" :close-on-click-modal="false"
-    :close-on-press-escape="false" custom-class="goods-select-dialog">
-    <div class="goods-select-content">
-      <el-radio-group v-model="selectedGoodsArtNo" class="goods-radio-group">
-        <div v-for="item in filteredGoodsList" :key="item.goods_art_no" class="goods-item"
-          :class="{ 'selected': selectedGoodsArtNo === item.goods_art_no }"
-          @click="selectedGoodsArtNo = item.goods_art_no">
-
-          <div class="goods-item-header">
-            <div class="goods-item-left flex left">
-              <el-radio :label="item.goods_art_no" class="goods-radio" @click.stop> </el-radio>
-              <span class="goods-art-no">{{ item.goods_art_no }}</span>
-
-              <div class="goods-item-meta ">
-                <span class="action-time flex left">
-
-                  <img src="@/assets/images/processImage.vue/riq.png" />
-                  {{ getTime(item.action_time) }}</span>
-                <span class="image-count mar-left-10 flex left">
-                  <img src="@/assets/images/processImage.vue/tup.png" />
-                  {{ item.items?.length || 0 }}张图片</span>
-              </div>
-
-            </div>
-          </div>
-          <div class="goods-item-images">
-            <div v-for="(image, index) in item.items" :key="image.action_id || image.action_name"
-              class="goods-item_image">
-              <span class="tag" v-if="!image.PhotoRecord?.image_path">{{ image.action_name }}</span>
-              <el-image v-if="image.PhotoRecord?.image_path" :src="getFilePath(image.PhotoRecord.image_path)"
-                :preview-src-list="getPreviewImageList(item)" :initial-index="getPreviewIndex(item, index)"
-                class="preview-image" fit="contain" :preview-teleported="true" lazy>
-                <template #error>
-                  <div class="image-slot">
-                    <span class="tag">{{ image.action_name }}</span>
-                  </div>
-                </template>
-              </el-image>
-              <div v-else class="image-placeholder">
-                <span class="tag">{{ image.action_name }}</span>
-              </div>
-            </div>
-          </div>
-
-        </div>
-      </el-radio-group>
-    </div>
-    <template #footer>
-      <div class="dialog-footer">
-        <el-button @click="cancelGoodsSelection">取消</el-button>
-        <el-button type="primary" :loading="goodsGenerateLoading" :disabled="goodsGenerateLoading"
-          @click="confirmGoodsSelection">
-          {{ goodsGenerateLoading ? '正在生成自定义商品模版' : '确定' }}
-        </el-button>
-      </div>
-    </template>
-  </el-dialog>
+  <GoodsSelectDialog
+    v-model:visible="goodsSelectDialogVisible"
+    @confirm="handleGoodsSelectionConfirm"
+    @cancel="handleGoodsSelectionCancel"
+  />
 
 </template>
 
@@ -556,6 +504,7 @@ const socketStore = socket();
 const useUserInfoStore = useUserInfo();
 import ModelGenerationDialog from '@/components/ModelGeneration/index.vue'
 import ScenePromptDialog from '@/components/ScenePromptDialog/index.vue'
+import GoodsSelectDialog from './components/GoodsSelectDialog.vue'
 
 import { Close, Warning } from '@element-plus/icons-vue'
 import LoadingDialog from '@/views/Photography/components/LoadingDialog.vue'
@@ -569,13 +518,8 @@ const EXTERNAL_TOOL_EXECUTABLE = '智慧映拍照机辅助工具箱.exe'
 
 import usePhotography from './mixin/usePhotography'
 
-const {
-  loading,
-  goodsList,
-  getPhotoRecords,
-  getTime,
-  getFilePath,
-} = usePhotography()
+// 从 mixin 中导入必要的函数和状态(已不再使用,保留以防其他地方需要)
+const { } = usePhotography()
 
 
 const launchExternalTool = async (pagePath: string, successMessage: string) => {
@@ -611,189 +555,25 @@ const handleProductAlbumClick = () => launchExternalTool('/product_list', '产
 
 // 货号选择弹窗相关状态
 const goodsSelectDialogVisible = ref(false)
-const filteredGoodsList = ref<any[]>([])
-const selectedGoodsArtNo = ref<string>('')
-const goodsGenerateLoading = ref(false)
-
-const addCustomTemplate = async () => {
-  // 调用获取数据
-  getPhotoRecords()
-
-  // 处理数据的函数
-  const processData = () => {
-    // 从 goodsList 中筛选出 goods_art_no 在 goods_art_nos.value 中的对象
-    const filteredGoods = goodsList.value.filter((item: any) =>
-      goods_art_nos.value.includes(item.goods_art_no)
-    )
-
-    console.log('goods_art_nos', goods_art_nos.value)
-    console.log('filteredGoods', filteredGoods)
-
-    if (filteredGoods.length > 0) {
-      // 若有匹配项,则只展示匹配到的货号
-      filteredGoodsList.value = filteredGoods
-      selectedGoodsArtNo.value = filteredGoods[0].goods_art_no
-      goodsSelectDialogVisible.value = true
-    } else if (goodsList.value.length > 0) {
-      // 若没有匹配到货号,则展示全部货号列表
-      filteredGoodsList.value = goodsList.value
-      selectedGoodsArtNo.value = goodsList.value[0].goods_art_no
-      goodsSelectDialogVisible.value = true
-    } else {
-      // 列表本身为空时再提示
-      ElMessage.warning('未找到匹配的货号数据')
-    }
-  }
-
-  // 使用 watch 监听 loading 状态,当从 true 变为 false 时,说明数据加载完成
-  const stopWatcher = watch(
-    () => loading.value,
-    (isLoading, wasLoading) => {
-      // 当 loading 从 true 变为 false 时,说明数据已经加载完成
-      if (wasLoading && !isLoading) {
-        processData()
-        // 停止监听,避免重复执行
-        stopWatcher()
-      }
-    }
-  )
 
-  // 如果当前 loading 已经是 false,可能数据已经存在或加载很快完成
-  // 使用 nextTick 等待一个 tick,如果 loading 仍然是 false,直接处理
-  if (!loading.value) {
-    await nextTick()
-    // 如果 loading 仍然是 false,可能数据已经存在,直接处理
-    if (!loading.value) {
-      processData()
-      stopWatcher() // 停止监听
-    }
-  }
+const addCustomTemplate = () => {
+  goodsSelectDialogVisible.value = true
 }
 
-// 根据选中的货号调用后端生成商品模板
-const generateGoodsTemplate = (goodsArtNo: string) => {
-  return new Promise<void>((resolve, reject) => {
-    try {
-      console.log('调用 get_goods_image_json, goods_art_no:', goodsArtNo)
-
-      // 按后端要求带上 token(参考其他生成类接口)
-      const tokenStore = tokenInfo();
-      const token = (tokenStore as any)?.getToken || '';
-
-      // 先清理监听,避免重复回调
-      clientStore.ipc.removeAllListeners(icpList.generate.getGoodsImageJson)
-
-      clientStore.ipc.send(icpList.generate.getGoodsImageJson, {
-        goods_art_no: goodsArtNo,
-        token,
-      })
-
-      clientStore.ipc.on(icpList.generate.getGoodsImageJson, (event, result) => {
-        clientStore.ipc.removeAllListeners(icpList.generate.getGoodsImageJson)
-        console.log('get_goods_image_json result:', result)
-        if (result && result.code === 0) {
-          resolve(result.data)
-        } else {
-          const msg = result?.msg || '商品模板生成失败'
-          ElMessage.error(msg)
-          reject(new Error(msg))
-        }
-      })
-    } catch (error: any) {
-      ElMessage.error(error?.message || '商品模板生成异常')
-      reject(error)
-    }
+// 处理货号选择确认
+const handleGoodsSelectionConfirm = (selectedGoodsArtNo: string) => {
+  console.log('选中的货号:', selectedGoodsArtNo)
+  // 跳转到新增模板页(组件内部已经处理了数据存储到 sessionStorage)
+  router.push({
+    name: 'addTpl'
   })
 }
 
-// 确认选择货号
-const confirmGoodsSelection = async () => {
-  if (!selectedGoodsArtNo.value) {
-    ElMessage.warning('请选择一个货号')
-    return
-  }
-
-  console.log('选中的货号:', selectedGoodsArtNo.value)
-  goodsGenerateLoading.value = true
-
-  try {
-    const data = await generateGoodsTemplate(selectedGoodsArtNo.value)
-    console.log('商品模版数据', data)
-
-    // 组装 goods_images 数组:[{ key: 模板顺序项, value: 对应图片地址 }, ...]
-    const goodsImages: Array<{ key: string; value: string }> = []
-    const orderArr = (data?.template_image_order || '')
-      .split(',')
-      .map((s: string) => s.trim())
-      .filter((s: string) => s)
-    const imagesArr = Array.isArray(data?.customer_template_images)
-      ? data.customer_template_images
-      : []
-
-    const len = Math.min(orderArr.length, imagesArr.length)
-    for (let i = 0; i < len; i++) {
-      goodsImages.push({
-        key: orderArr[i],
-        value: imagesArr[i],
-      })
-    }
-
-    // 将商品文字的 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_excel_headers: data?.template_excel_headers || [], // 传递完整的商品文字数据
-      timestamp: Date.now() // 添加时间戳用于标识数据
-    }
-    sessionStorage.setItem('addTpl_template_data', JSON.stringify(templateData))
-
-    // 生成商品模板成功后,跳转到新增模板页
-    router.push({
-      name: 'addTpl'
-    })
-  } finally {
-    // 无论成功或失败,都关闭弹窗,保留当前选择
-    goodsSelectDialogVisible.value = false
-    goodsGenerateLoading.value = false
-  }
-}
-
-// 取消选择
-const cancelGoodsSelection = () => {
-  goodsSelectDialogVisible.value = false
-  selectedGoodsArtNo.value = ''
-  filteredGoodsList.value = []
-}
-
-// 获取预览图片列表(只包含有图片路径的,保持原始顺序)
-const getPreviewImageList = (item: any) => {
-  if (!item || !item.items) return []
-  return item.items
-    .filter((img: any) => img.PhotoRecord?.image_path)
-    .map((img: any) => getFilePath(img.PhotoRecord.image_path))
+// 处理货号选择取消
+const handleGoodsSelectionCancel = () => {
+  // 取消选择,不做任何操作
 }
 
-// 获取当前图片在预览列表中的索引
-const getPreviewIndex = (item: any, currentIndex: number) => {
-  if (!item || !item.items) return 0
-  // 计算当前图片在过滤后的预览列表中的索引
-  let previewIndex = 0
-  for (let i = 0; i <= currentIndex; i++) {
-    if (item.items[i]?.PhotoRecord?.image_path) {
-      if (i === currentIndex) break
-      previewIndex++
-    }
-  }
-  return previewIndex
-}
 
 
 
@@ -3153,196 +2933,3 @@ const selectFolder = () => {
 }
 </style>
 
-<style lang="scss">
-// 货号选择弹窗样式(非 scoped,因为弹窗挂载在 body 上)
-.goods-select-dialog {
-  max-width: 60vw;
-
-  .el-dialog__body {
-    padding: 20px;
-    max-height: 70vh;
-    overflow-y: auto;
-    height: 60vh;
-  }
-
-  .goods-select-content {
-    .goods-radio-group {
-      width: 100%;
-      display: flex;
-      flex-direction: column;
-      gap: 20px;
-    }
-
-    .goods-item {
-      background: #FFFFFF;
-      box-shadow: 0px 2px 4px 0px rgba(23, 33, 71, 0.1);
-      border-radius: 10px;
-      border: 1px solid #D9DEE6;
-      margin-bottom: 20px;
-      cursor: pointer;
-      transition: all 0.3s;
-
-
-
-      .goods-radio {
-        width: 30px;
-        margin: 0;
-        padding: 0;
-        position: relative;
-        margin-top: -10px;
-
-        .el-radio__input {
-          position: absolute;
-          top: 14px;
-          left: 18px;
-          z-index: 10;
-          transform: scale(1);
-        }
-
-        .el-radio__inner {
-          border-width: 2px;
-        }
-
-        &.is-checked {
-          .el-radio__inner {
-            background-color: #409eff;
-            border-color: #409eff;
-          }
-        }
-
-        .el-radio__label {
-          width: 100%;
-          padding-left: 40px;
-          display: none;
-        }
-      }
-
-      .goods-item-header {
-        display: flex;
-        justify-content: flex-start;
-        align-items: center;
-        height: 40px;
-        background: linear-gradient(90deg, #F4ECFF 0%, #DFEDFF 100%);
-        border-radius: 10px 10px 0px 0px;
-
-        .goods-item-left {
-          display: flex;
-          align-items: center;
-          gap: 16px;
-
-          .goods-art-no {
-            font-size: 16px;
-            font-weight: 500;
-            color: #333;
-          }
-
-          .goods-item-meta {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            font-size: 12px;
-            color: #666;
-
-            img {
-              height: 14px;
-              margin-right: 2px;
-            }
-
-            .action-time {
-              color: #666;
-            }
-
-            .image-count {
-              color: #666;
-            }
-          }
-        }
-      }
-
-      .goods-item-images {
-        display: grid;
-        grid-template-columns: repeat(5, 1fr);
-        gap: 10px;
-        padding: 15px;
-        border-top: 1px solid #f0f0f0;
-        overflow-x: auto;
-
-        @media (min-width: 1200px) {
-          grid-template-columns: repeat(5, 1fr);
-        }
-
-        @media (max-width: 768px) {
-          grid-template-columns: repeat(3, 1fr);
-        }
-      }
-
-      .goods-item_image {
-        position: relative;
-        width: 100%;
-        aspect-ratio: 1;
-        background: #F7F7F7;
-        border-radius: 10px;
-        overflow: hidden;
-        cursor: pointer;
-        border: 1px solid #D9DEE6;
-        transition: all 0.3s;
-
-        .tag {
-          color: #bbb;
-          position: absolute;
-          left: 0;
-          right: 0;
-          top: 50%;
-          margin-top: -10px;
-          line-height: 20px;
-          text-align: center;
-          font-size: 12px;
-          z-index: 1;
-          pointer-events: none;
-        }
-
-        .preview-image {
-          width: 100%;
-          height: 100%;
-
-          .el-image__inner {
-            width: 100%;
-            height: 100%;
-            object-fit: cover;
-          }
-        }
-
-        .image-placeholder {
-          width: 100%;
-          height: 100%;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          background: #F7F7F7;
-        }
-
-        .image-slot {
-          width: 100%;
-          height: 100%;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          background: #F7F7F7;
-        }
-
-        &:hover {
-          border-color: #409eff;
-          transform: scale(1.02);
-          box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
-        }
-      }
-    }
-  }
-
-  .dialog-footer {
-    display: flex;
-    justify-content: flex-end;
-    gap: 10px;
-  }
-}
-</style>