瀏覽代碼

refactor(frontend): 重构摄影详情页面并优化相关功能

- 更新服务卡片逻辑,修改服务类型名称
- 增加服务内容必填验证
-调整参数设置和生成逻辑- 优化场景提示词对话框样式
- 精简首页组件,移除不必要的功能
panqiuyao 3 月之前
父節點
當前提交
4fff75bc71

+ 7 - 4
frontend/src/components/ScenePromptDialog/index.vue

@@ -6,10 +6,10 @@
       <div class="input-section">
         <div class="input-wrapper">
           <el-input v-model="scenePrompt" type="textarea" :rows="8" placeholder="请输入场景提示词" class="scene-input"
-            resize="none" maxlength="500" show-word-limit />
+            resize="none" maxlength="500"  />
           <!-- AI帮我写按钮 -->
           <div class="ai-help-button-container">
-            <el-button type="primary" size="small" @click="handleAIHelp" :loading="aiLoading" class="ai-help-button">
+            <el-button type="primary"  @click="handleAIHelp" :loading="aiLoading" class="ai-help-button">
               AI帮我写
             </el-button>
           </div>
@@ -152,8 +152,9 @@ watch(dialogVisible, (newValue) => {
 
 .ai-help-button-container {
   position: absolute;
-  bottom: 4px;
-  right: 4px;
+  bottom: 15px;
+  height: 25px;
+  right: 6px;
 }
 
 .ai-help-button {
@@ -193,6 +194,7 @@ watch(dialogVisible, (newValue) => {
   background: linear-gradient(135deg, #409eff 0%, #a855f7 100%);
   border: none;
   border-radius: 6px;
+  height: 36px;
   padding: 12px 32px;
   font-size: 14px;
   font-weight: 500;
@@ -226,6 +228,7 @@ watch(dialogVisible, (newValue) => {
 
   .el-dialog__footer {
     padding: 0 !important;
+    background: #EAECED;
   }
 
   .el-dialog {

+ 30 - 148
frontend/src/views/Home/index.vue

@@ -18,58 +18,12 @@
       <div class="overlay-text">拍摄产品<br>并处理图像</div>
     </div>
 
-         <!-- 右侧图片区域 -->
-     <div class="image-container right-image" @click="goShot" v-log="{ describe: { action: '点击仅处理图像入口' } }">
-       <img src="@/assets/images/home/right.jpg" alt="仅处理图像" class="zoom-on-hover" />
-       <div class="overlay-text" style="line-height: 80px;">仅处理图像</div>
-     </div>
-
-           <!-- 模特生成按钮 -->
-      <el-button type="info" @click="openModelDialog" class="model-generation-btn">
-        打开模特生成弹窗
-      </el-button>
-
-      <!-- 场景提示词按钮 -->
-      <el-button type="success" @click="openScenePromptDialog" class="scene-prompt-btn">
-        场景图生成
-      </el-button>
-
-     <!-- 显示选中的模特信息 -->
-     <div v-if="selectedModels" class="selected-models-info">
-       <h3>已选择的模特:</h3>
-       <div class="models-display">
-         <div v-if="selectedModels.female" class="model-info">
-           <strong>女模特:</strong>{{ selectedModels.female.image_url }}
-         </div>
-         <div v-if="selectedModels.male" class="model-info">
-           <strong>男模特:</strong>{{ selectedModels.male.image_url }}
-         </div>
-       </div>
-     </div>
-     <div class="scene-prompt-info">
-      <div class="scene-prompt-info-item">
-        <div class="scene-prompt-info-item-label">场景提示词:</div>
-        <div class="scene-prompt-info-item-value">{{ scenePrompt }}</div>
-      </div>
-     </div>
-
-     
-
-      <ModelGenerationDialog 
-         v-model="modelDialogVisible"
-         :initial-models="selectedModels"
-         @confirm="handleModelSelection"
-         @cancel="modelDialogVisible = false"
-       />
-
-               <!-- 场景提示词弹窗 -->
-        <ScenePromptDialog 
-          v-model="scenePromptDialogVisible"
-          :initial-prompt="scenePrompt"
-          @confirm="handleScenePromptConfirm"
-          @cancel="scenePromptDialogVisible = false"
-        />
-   </div>
+    <!-- 右侧图片区域 -->
+    <div class="image-container right-image" @click="goShot" v-log="{ describe: { action: '点击仅处理图像入口' } }">
+      <img src="@/assets/images/home/right.jpg" alt="仅处理图像" class="zoom-on-hover" />
+      <div class="overlay-text" style="line-height: 80px;">仅处理图像</div>
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
@@ -77,20 +31,13 @@ import headerBar from "@/components/header-bar/index.vue";
 import { useRouter } from "vue-router";
 import configInfo from '@/stores/modules/config';
 import { ref, onMounted } from 'vue';
-import { ElMessage } from 'element-plus';
 import axios from 'axios';
 import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
-import ModelGenerationDialog from '@/components/ModelGeneration/index.vue';
-import ScenePromptDialog from '@/components/ScenePromptDialog/index.vue';
 
 const configInfoStore = configInfo();
 const router = useRouter();
 const loading = ref(true);
-const selectedModels = ref<{ female: any; male: any } | null>(null);
-const modelDialogVisible = ref(false);
-const scenePromptDialogVisible = ref(false);
-const scenePrompt = ref('');
 
 
 import socket from "@/stores/modules/socket";
@@ -98,69 +45,48 @@ import socket from "@/stores/modules/socket";
 const socketStore = socket();
 
 function socketConnect(){
-    socketStore.connectSocket();
+  socketStore.connectSocket();
 }
 
 const goCheck = () => {
-    configInfoStore.updateAppModel(1);
-    router.push({
-        name: 'PhotographyCheck'
-    });
+  configInfoStore.updateAppModel(1);
+  router.push({
+    name: 'PhotographyCheck'
+  });
 };
 
 const goShot = () => {
-    socketConnect();
-    configInfoStore.updateAppModel(2);
-    router.push({
-        name: 'PhotographyShot'
-    });
-};
-
-// 打开模特生成弹窗
-const openModelDialog = () => {
-    modelDialogVisible.value = true;
-};
-
-// 处理模特选择结果
-const handleModelSelection = (models: { female: any; male: any }) => {
-    selectedModels.value = models;
-    modelDialogVisible.value = false;
-    ElMessage.success('模特选择完成!');
-};
-
-// 打开场景提示词弹窗
-const openScenePromptDialog = () => {
-    scenePromptDialogVisible.value = true;
-};
-
-// 处理场景提示词确认
-const handleScenePromptConfirm = (prompt: string) => {
-    scenePrompt.value = prompt;
-    // 这里可以添加处理场景提示词的逻辑
+  socketConnect();
+  configInfoStore.updateAppModel(2);
+  router.push({
+    name: 'PhotographyShot'
+  });
 };
 
 // 健康检查函数
 const checkHealth = async () => {
-    try {
-        const response = await axios.get('http://127.0.0.1:7074');
-        if (response.status === 200) {
-            loading.value = false; // 健康检查成功,关闭 loading
-        }
-    } catch (error) {
-        setTimeout(() => {
-            checkHealth(); // 延迟检查
-        }, 2000);
-        // 可以在这里处理错误,例如显示错误提示
+  try {
+    const response = await axios.get('http://127.0.0.1:7074');
+    if (response.status === 200) {
+      loading.value = false; // 健康检查成功,关闭 loading
     }
+  } catch (error) {
+    console.error('健康检查失败:', error);
+    setTimeout(() => {
+      checkHealth(); // 延迟检查
+    }, 2000);
+    // 可以在这里处理错误,例如显示错误提示
+  }
 };
 
 const settingClickCount = ref(0);
 // 修改headerBar的点击处理函数
 function handleSettingClick() {
+  console.log('handleSettingClickhandleSettingClick')
   settingClickCount.value++;
 
   if (settingClickCount.value >= 5) {
-      openResourceDirectory()
+    openResourceDirectory()
     settingClickCount.value = 0;
   }
 
@@ -186,7 +112,7 @@ function openResourceDirectory() {
 
 // 在组件挂载时执行健康检查
 onMounted(() => {
-    checkHealth();
+  checkHealth();
 });
 </script>
 
@@ -259,48 +185,4 @@ onMounted(() => {
   min-height: 80px;
   min-width: 250px;
 }
-
-.model-generation-btn {
-  position: absolute;
-  top: 20px;
-  right: 20px;
-  z-index: 10;
-}
-
-.scene-prompt-btn {
-  position: absolute;
-  top: 70px;
-  right: 20px;
-  z-index: 10;
-}
-
-.selected-models-info {
-  position: absolute;
-  top: 20px;
-  left: 20px;
-  background: rgba(255, 255, 255, 0.9);
-  padding: 15px;
-  border-radius: 8px;
-  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
-  max-width: 300px;
-  z-index: 10;
-  
-  h3 {
-    margin: 0 0 10px 0;
-    color: #303133;
-    font-size: 16px;
-  }
-  
-  .models-display {
-    .model-info {
-      margin: 5px 0;
-      color: #606266;
-      font-size: 14px;
-      
-      strong {
-        color: #409eff;
-      }
-    }
-  }
-}
 </style>

+ 93 - 15
frontend/src/views/Photography/detail.vue

@@ -11,37 +11,37 @@
           勾选服务内容(多选):
         </div>
         <div class="service-cards">
-          <div class="service-card" :class="{ active: form.services.includes('scene') }" @click="toggleService('scene')" v-log="{ describe: { action: '点击服务卡片', service: '场景图生成' } }">
+          <div class="service-card" :class="{ active: form.services.includes('is_product_scene') }" @click="toggleService('is_product_scene')" v-log="{ describe: { action: '点击服务卡片', service: '场景图生成' } }">
             <div class="service-checkbox" @click.stop>
-              <el-checkbox size="large" :model-value="form.services.includes('scene')" @change="toggleService('scene')" />
+                              <el-checkbox size="large" :model-value="form.services.includes('is_product_scene')" @change="toggleService('is_product_scene')" />
             </div>
             <div class="service-content">
               <div class="service-image">
                 <img src="@/assets/images/Photography/cj.png" alt="场景图生成" />
-                <div class="service-icon">
+                <div class="service-icon" @click.stop="openScenePromptDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '场景图生成-弹窗' } }">
                   <el-icon><EditPen /></el-icon>
                 </div>
               </div>
               <div class="service-name">场景图生成</div>
             </div>
           </div>
-          <div class="service-card" :class="{ active: form.services.includes('model') }" @click="toggleService('model')" v-log="{ describe: { action: '点击服务卡片', service: '模特图生成' } }">
+          <div class="service-card" :class="{ active: form.services.includes('is_upper_footer') }" @click="toggleService('is_upper_footer')" v-log="{ describe: { action: '点击服务卡片', service: '模特图生成' } }">
             <div class="service-checkbox" @click.stop>
-              <el-checkbox size="large" :model-value="form.services.includes('model')" @change="toggleService('model')" />
+                              <el-checkbox size="large" :model-value="form.services.includes('is_upper_footer')" @change="toggleService('is_upper_footer')" />
             </div>
             <div class="service-content">
               <div class="service-image">
                 <img src="@/assets/images/Photography/mt.png" alt="模特图生成" />
-                <div class="service-icon">
+                <div class="service-icon" @click.stop="openModelDialog" v-log="{ describe: { action: '点击服务卡片图标', service: '模特图生成-弹窗' } }">
                   <el-icon><EditPen /></el-icon>
                 </div>
               </div>
               <div class="service-name">模特图生成</div>
             </div>
           </div>
-          <div class="service-card" :class="{ active: form.services.includes('detail') }" @click="toggleService('detail')" v-log="{ describe: { action: '点击服务卡片', service: '详情页生成' } }">
+          <div class="service-card" :class="{ active: form.services.includes('is_detail') }" @click="toggleService('is_detail')" v-log="{ describe: { action: '点击服务卡片', service: '详情页生成' } }">
             <div class="service-checkbox" @click.stop>
-              <el-checkbox size="large" :model-value="form.services.includes('detail')" @change="toggleService('detail')" />
+                              <el-checkbox size="large" :model-value="form.services.includes('is_detail')" @change="toggleService('is_detail')" />
             </div>
             <div class="service-content">
               <div class="service-image">
@@ -310,6 +310,21 @@
     <img style="width: 100%;" :src="dialogImageUrl" alt="Preview Image" />
   </el-dialog>
 
+  <!-- 模特生成弹窗 -->
+  <ModelGenerationDialog
+    v-model="modelDialogVisible"
+    :initial-models="selectedModels"
+    @confirm="handleModelSelection"
+    @cancel="modelDialogVisible = false"
+  />
+
+  <!-- 场景提示词弹窗 -->
+  <ScenePromptDialog
+    v-model="scenePromptDialogVisible"
+    :initial-prompt="scenePrompt"
+    @confirm="handleScenePromptConfirm"
+    @cancel="scenePromptDialogVisible = false"
+  />
 
 
 </template>
@@ -336,6 +351,8 @@ import { getRouterUrl } from '@/utils/appfun'
 import { useUuidStore } from '@/stores/modules/uuid'
 import socket from "@/stores/modules/socket";
 const socketStore = socket();
+import ModelGenerationDialog from '@/components/ModelGeneration/index.vue'
+import ScenePromptDialog from '@/components/ScenePromptDialog/index.vue'
 
 import { Close, Warning } from '@element-plus/icons-vue'
 import LoadingDialog from '@/views/Photography/components/LoadingDialog.vue'
@@ -407,7 +424,7 @@ const form = reactive({
   dataType: '1', // 1: 选择excel文件 2: 系统对接
   logo_path: '', // 主图LOGO
   excel_path: '', // 商品基础资料EXCEL文件选择
-  services: ['detail'], // 勾选服务内容(多选)默认包含详情页生成
+  services: ['is_detail'], // 勾选服务内容(多选)默认包含详情页生成
 })
 onMounted(() => {
   // 页面访问埋点
@@ -483,6 +500,27 @@ const publishToPlatforms = () => {
   // 具体上架逻辑按后续接口对接
 }
 
+// 模特与场景弹窗
+const modelDialogVisible = ref(false)
+const scenePromptDialogVisible = ref(false)
+const selectedModels = ref<{ female: any; male: any } | null>(null)
+const scenePrompt = ref('')
+
+const openModelDialog = () => {
+  modelDialogVisible.value = true
+}
+const openScenePromptDialog = () => {
+  scenePromptDialogVisible.value = true
+}
+const handleModelSelection = (models: { female: any; male: any }) => {
+  selectedModels.value = models
+  modelDialogVisible.value = false
+  ElMessage.success('模特选择完成!')
+}
+const handleScenePromptConfirm = (prompt: string) => {
+  scenePrompt.value = prompt
+}
+
 
 
 
@@ -540,6 +578,11 @@ const handleSegmentProgressMessage = (data: any) => {
 
 // 开始生成操作
 const generate = async function () {
+
+  if(form.services.length == 0){
+    ElMessage.error('请选择服务内容')
+    return
+  }
   // 埋点:开始生成详情页
   clickLog({
     describe: {
@@ -552,10 +595,23 @@ const generate = async function () {
     }
   }, route);
 
-  if ( form.dataType == '1'  && !form.excel_path) {
-    ElMessage.error('请上传商品基础资料')
+  // 必填验证
+  if (form.services.includes('is_upper_footer') && !selectedModels.value) {
+    ElMessage.error('请选择模特')
     return
   }
+
+  if (form.services.includes('is_product_scene') && !scenePrompt.value) {
+    ElMessage.error('请设置场景提示词')
+    return
+  }
+
+  if(form.services.includes('is_detail')){
+    if ( form.dataType == '1'  && !form.excel_path) {
+      ElMessage.error('请上传商品基础资料')
+      return
+    }
+  }
   const tokenInfoStore = tokenInfo();
   const token = tokenInfoStore.getToken; // 使用 getToken() 获取 token
   let temp_list = []
@@ -565,6 +621,11 @@ const generate = async function () {
       template_local_classes: item.template_local_classes,
     })
   })
+  // 根据选择的服务内容设置参数
+  const isDetail = form.services.includes('is_detail') ? 1 : 0
+  const isProductScene = form.services.includes('is_product_scene') ? 1 : 0
+  const isUpperFooter = form.services.includes('is_upper_footer') ? 1 : 0
+
   const params = {
     goods_art_no: JSON.parse(JSON.stringify(goods_art_nos.value)),
     logo_path: form.logo_path || '',
@@ -573,9 +634,19 @@ const generate = async function () {
     template_image_order: form.selectTemplate.template_image_order,
     temp_list,
     token,
-    uuid: uuidStore.getUuid || ''
+    uuid: uuidStore.getUuid || '',
+          // 新增服务参数
+      online_stores: Object.values(domesticPlatforms.value || {}),
+    is_detail: isDetail,
+    is_product_scene: isProductScene,
+    is_upper_footer: isUpperFooter,
+    upper_footer_params: selectedModels.value ? {
+      man_id: selectedModels.value.male?.id || "",
+      women_id: selectedModels.value.female?.id || ""
+    } :  "",
+    product_scene_prompt: scenePrompt.value || ''
   }
-  console.log(params);
+
   // 开启进度弹窗
   requesting.value =  false
   partErrList.value = []
@@ -943,9 +1014,16 @@ const selectFolder = () => {
 
     .service-name {
       font-size: 14px;
-      color: #333;
       font-weight: 500;
       text-align: center;
+      position: absolute;
+      background: rgba(0,0,0,.5);
+      height: 30px;
+      line-height: 30px;
+      color: #fff;
+      left: 0;
+      right: 0;
+      bottom:0px
     }
   }
 }
@@ -960,7 +1038,7 @@ const selectFolder = () => {
   display: flex;
   gap: 24px;
   align-items: flex-start;
-  .logo-col { flex: 1; min-width: 300px; }
+  .logo-col { flex: 2; min-width: 300px; }
   .template-col { flex: 3; }
 }