zck 1 mesiac pred
rodič
commit
33b985a156

+ 0 - 441
src/view/performance/QualityAssessment/Category.vue

@@ -1,441 +0,0 @@
-<template>
-  <div class="food-category">
-    <el-card shadow="hover">
-      <template #header>
-        <div class="card-header">
-          <span>食品数据管理</span>
-        </div>
-      </template>
-      
-      <!-- 搜索和筛选区域 -->
-      <div class="search-filter">
-        <el-form :model="searchForm" label-width="80px" inline>
-          <el-form-item label="食品名称">
-            <el-input 
-              v-model="searchForm.productName" 
-              placeholder="请输入食品名称" 
-              clearable
-              @keyup.enter="getProductList"
-            />
-          </el-form-item>
-          <el-form-item label="食品类型">
-            <el-select 
-              v-model="searchForm.foodType" 
-              placeholder="请选择食品类型" 
-              clearable
-            >
-              <el-option label="蔬菜" value="vegetable" />
-              <el-option label="水果" value="fruit" />
-              <el-option label="肉类" value="meat" />
-              <el-option label="海鲜" value="seafood" />
-              <el-option label="加工食品" value="processed" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="生成状态">
-            <el-select 
-              v-model="searchForm.status" 
-              placeholder="请选择状态" 
-              clearable
-            >
-              <el-option label="已生成" value="success" />
-              <el-option label="生成中" value="generating" />
-              <el-option label="生成失败" value="failed" />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="getProductList">搜索</el-button>
-            <el-button @click="resetSearch">重置</el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-      
-      <!-- 产品列表 -->
-      <div class="food-list">
-        <el-table 
-          :data="productList" 
-          style="width: 100%" 
-          stripe
-          @row-click="handleRowClick"
-        >
-          <el-table-column prop="id" label="食品ID" width="80" />
-          <el-table-column prop="productName" label="食品名称" width="200" />
-          <el-table-column prop="foodType" label="食品类型" width="120">
-            <template #default="scope">
-              <el-tag 
-                :type="scope.row.foodType === 'vegetable' ? 'success' : 
-                      scope.row.foodType === 'fruit' ? 'warning' : 
-                      scope.row.foodType === 'meat' ? 'danger' : 
-                      scope.row.foodType === 'seafood' ? 'primary' : 'info'"
-              >
-                {{ scope.row.foodType === 'vegetable' ? '蔬菜' : 
-                   scope.row.foodType === 'fruit' ? '水果' : 
-                   scope.row.foodType === 'meat' ? '肉类' : 
-                   scope.row.foodType === 'seafood' ? '海鲜' : '加工食品' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column prop="size" label="图片尺寸" width="120" />
-          <el-table-column prop="style" label="设计风格" width="120" />
-          <el-table-column prop="status" label="生成状态" width="120">
-            <template #default="scope">
-              <el-tag 
-                :type="scope.row.status === 'success' ? 'success' : 
-                      scope.row.status === 'generating' ? 'warning' : 'danger'"
-              >
-                {{ scope.row.status === 'success' ? '已生成' : 
-                   scope.row.status === 'generating' ? '生成中' : '生成失败' }}
-              </el-tag>
-            </template>
-          </el-table-column>
-          <el-table-column prop="createTime" label="创建时间" width="180" />
-          <el-table-column label="操作" width="200">
-            <template #default="scope">
-              <el-button type="primary" size="small" @click="viewProduct(scope.row)">查看详情</el-button>
-              <el-button type="danger" size="small" @click="deleteProduct(scope.row.id)">删除</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-        
-        <!-- 分页 -->
-        <div class="pagination">
-          <el-pagination
-            @size-change="handleSizeChange"
-            @current-change="handleCurrentChange"
-            :current-page="pagination.currentPage"
-            :page-sizes="[10, 20, 50, 100]"
-            :page-size="pagination.pageSize"
-            layout="total, sizes, prev, pager, next, jumper"
-            :total="pagination.total"
-          />
-        </div>
-      </div>
-    </el-card>
-    
-    <!-- 产品详情对话框 -->
-    <el-dialog
-      v-model="detailDialogVisible"
-      title="食品详情"
-      width="800px"
-      :before-close="handleDetailClose"
-    >
-      <div class="food-detail" v-if="selectedProduct">
-        <div class="detail-section">
-          <h3>基本信息</h3>
-          <el-descriptions :column="2" border>
-            <el-descriptions-item label="食品ID">{{ selectedProduct.id }}</el-descriptions-item>
-            <el-descriptions-item label="食品名称">{{ selectedProduct.productName }}</el-descriptions-item>
-            <el-descriptions-item label="食品类型">
-              <el-tag 
-                :type="selectedProduct.foodType === 'vegetable' ? 'success' : 
-                      selectedProduct.foodType === 'fruit' ? 'warning' : 
-                      selectedProduct.foodType === 'meat' ? 'danger' : 
-                      selectedProduct.foodType === 'seafood' ? 'primary' : 'info'"
-              >
-                {{ selectedProduct.foodType === 'vegetable' ? '蔬菜' : 
-                   selectedProduct.foodType === 'fruit' ? '水果' : 
-                   selectedProduct.foodType === 'meat' ? '肉类' : 
-                   selectedProduct.foodType === 'seafood' ? '海鲜' : '加工食品' }}
-              </el-tag>
-            </el-descriptions-item>
-            <el-descriptions-item label="产地">{{ selectedProduct.origin }}</el-descriptions-item>
-            <el-descriptions-item label="营养价值">{{ selectedProduct.nutrition }}</el-descriptions-item>
-            <el-descriptions-item label="图片尺寸">{{ selectedProduct.size }}</el-descriptions-item>
-            <el-descriptions-item label="设计风格">{{ selectedProduct.style }}</el-descriptions-item>
-            <el-descriptions-item label="生成状态">
-              <el-tag 
-                :type="selectedProduct.status === 'success' ? 'success' : 
-                      selectedProduct.status === 'generating' ? 'warning' : 'danger'"
-              >
-                {{ selectedProduct.status === 'success' ? '已生成' : 
-                   selectedProduct.status === 'generating' ? '生成中' : '生成失败' }}
-              </el-tag>
-            </el-descriptions-item>
-            <el-descriptions-item label="创建时间" :span="2">{{ selectedProduct.createTime }}</el-descriptions-item>
-          </el-descriptions>
-        </div>
-        
-        <div class="detail-section">
-          <h3>提示词信息</h3>
-          <el-input
-            v-model="selectedProduct.prompt"
-            type="textarea"
-            :rows="4"
-            readonly
-            style="background-color: #f5f7fa"
-          />
-        </div>
-        
-        <div class="detail-section">
-          <h3>生成结果</h3>
-          <div class="generated-image">
-            <el-image
-              v-if="selectedProduct.generatedImage"
-              :src="selectedProduct.generatedImage"
-              fit="contain"
-              style="max-width: 100%; max-height: 400px"
-            >
-              <template #error>
-                <div class="image-error">图片加载失败</div>
-              </template>
-            </el-image>
-            <el-empty v-else description="暂无生成图片" />
-          </div>
-        </div>
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import {ref,reactive,onMounted} from 'vue'
-import {ElMessage,ElMessageBox,ElEmpty,ElImage} from 'element-plus'
-import {Loading} from '@element-plus/icons-vue'
-import {useUserStore} from '@/pinia/modules/user'
-// 假设存在获取产品列表的API,需要根据实际情况修改
-// import {getProductListApi, deleteProductApi} from '@/api/mes/product'
-
-// 定义组件名称
-
-defineOptions({name: 'Category'})
-
-// 搜索表单
-const searchForm = reactive({
-  productName: '',
-  foodType: '',
-  status: ''
-})
-
-// 食品列表数据
-const productList = ref([])
-
-// 分页信息
-const pagination = reactive({
-  currentPage: 1,
-  pageSize: 10,
-  total: 0
-})
-
-// 加载状态
-const loading = ref(false)
-
-// 详情对话框
-const detailDialogVisible = ref(false)
-const selectedProduct = ref(null)
-
-// 模拟食品数据
-const mockProducts = [
-  {
-    id: 1,
-    productName: '有机胡萝卜',
-    foodType: 'vegetable',
-    origin: '山东寿光',
-    nutrition: '富含胡萝卜素、维生素A',
-    size: '1024x1024',
-    style: 'realistic',
-    status: 'success',
-    createTime: '2023-10-20 10:30:00',
-    prompt: '创建一张有机胡萝卜的宣传图片,展示新鲜的有机胡萝卜,背景使用自然土壤色调,突出健康、有机的主题。',
-    generatedImage: 'https://picsum.photos/seed/carrot1/1024/1024'
-  },
-  {
-    id: 2,
-    productName: '挪威三文鱼',
-    foodType: 'seafood',
-    origin: '挪威',
-    nutrition: '富含Omega-3脂肪酸、蛋白质',
-    size: '1024x1024',
-    style: 'realistic',
-    status: 'success',
-    createTime: '2023-10-21 11:45:00',
-    prompt: '设计一张挪威三文鱼的宣传海报,展示新鲜的三文鱼片,背景使用海洋元素,突出其新鲜和营养价值。',
-    generatedImage: 'https://picsum.photos/seed/salmon1/1024/1024'
-  },
-  {
-    id: 3,
-    productName: '原味鱼干',
-    foodType: 'processed',
-    origin: '福建厦门',
-    nutrition: '富含蛋白质、钙、磷',
-    size: '1024x1024',
-    style: 'realistic',
-    status: 'success',
-    createTime: '2023-10-23 09:00:00',
-    prompt: '创建一张原味鱼干的宣传图片,展示自然晾晒的鱼干,背景使用海边场景,突出其新鲜和传统工艺。',
-    generatedImage: 'https://images.unsplash.com/photo-1610486295123-078299c50e79?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1024&q=80'
-  }
-]
-
-// 获取食品列表
-const getProductList = async () => {
-  loading.value = true
-  try {
-    // 实际项目中这里应该调用API获取数据
-    // const res = await getProductListApi({
-    //   page: pagination.currentPage,
-    //   pageSize: pagination.pageSize,
-    //   productName: searchForm.productName,
-    //   foodType: searchForm.foodType,
-    //   status: searchForm.status
-    // })
-    
-    // 模拟API调用
-    await new Promise(resolve => setTimeout(resolve, 500))
-    
-    // 模拟筛选数据
-    let filteredProducts = [...mockProducts]
-    if (searchForm.productName) {
-      filteredProducts = filteredProducts.filter(p => p.productName.includes(searchForm.productName))
-    }
-    if (searchForm.foodType) {
-      filteredProducts = filteredProducts.filter(p => p.foodType === searchForm.foodType)
-    }
-    if (searchForm.status) {
-      filteredProducts = filteredProducts.filter(p => p.status === searchForm.status)
-    }
-    
-    productList.value = filteredProducts
-    pagination.total = filteredProducts.length
-    
-    ElMessage({ message: '获取食品列表成功', type: 'success' })
-  } catch (error) {
-    console.error('获取食品列表失败:', error)
-    ElMessage({ message: '获取食品列表失败', type: 'error' })
-  } finally {
-    loading.value = false
-  }
-}
-
-// 重置搜索
-const resetSearch = () => {
-  searchForm.productName = ''
-  searchForm.foodType = ''
-  searchForm.status = ''
-  pagination.currentPage = 1
-  getProductList()
-}
-
-// 分页处理
-const handleSizeChange = (size) => {
-  pagination.pageSize = size
-  pagination.currentPage = 1
-  getProductList()
-}
-
-const handleCurrentChange = (current) => {
-  pagination.currentPage = current
-  getProductList()
-}
-
-// 查看产品详情
-const viewProduct = (product) => {
-  selectedProduct.value = {...product}
-  detailDialogVisible.value = true
-}
-
-// 处理行点击
-const handleRowClick = (row) => {
-  viewProduct(row)
-}
-
-// 删除产品
-const deleteProduct = (id) => {
-  ElMessageBox.confirm('确定要删除该产品吗?', '提示', {
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    type: 'warning'
-  }).then(async () => {
-    try {
-      // 实际项目中这里应该调用API删除数据
-      // await deleteProductApi(id)
-      
-      // 模拟API调用
-      await new Promise(resolve => setTimeout(resolve, 300))
-      
-      // 从列表中移除
-      const index = productList.value.findIndex(p => p.id === id)
-      if (index > -1) {
-        productList.value.splice(index, 1)
-        pagination.total--
-      }
-      
-      ElMessage({ message: '删除成功', type: 'success' })
-    } catch (error) {
-      console.error('删除失败:', error)
-      ElMessage({ message: '删除失败', type: 'error' })
-    }
-  }).catch(() => {
-    // 取消删除
-  })
-}
-
-// 处理详情对话框关闭
-const handleDetailClose = () => {
-  detailDialogVisible.value = false
-  selectedProduct.value = null
-}
-
-// 页面挂载时获取产品列表
-onMounted(() => {
-  getProductList()
-})
-</script>
-
-<style scoped>
-.product-category {
-  padding: 20px;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.search-filter {
-  margin-bottom: 20px;
-}
-
-.product-list {
-  margin-top: 20px;
-}
-
-.pagination {
-  margin-top: 20px;
-  text-align: right;
-}
-
-.product-detail {
-  padding: 10px 0;
-}
-
-.detail-section {
-  margin-bottom: 25px;
-}
-
-.detail-section h3 {
-  margin-bottom: 15px;
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-  border-bottom: 1px solid #ebeef5;
-  padding-bottom: 5px;
-}
-
-.generated-image {
-  margin-top: 10px;
-  padding: 20px;
-  background-color: #fafafa;
-  border-radius: 8px;
-  text-align: center;
-}
-
-.image-error {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 100%;
-  height: 200px;
-  background-color: #f5f7fa;
-  color: #909399;
-}
-</style>

+ 0 - 970
src/view/performance/QualityAssessment/Newdialogue.vue

@@ -1,970 +0,0 @@
-<template>
-  <div class="chat-interface">
-    <!-- 生成类型选项卡 -->
-    <div class="generation-tabs">
-      <el-tabs v-model="activeTab" type="card">
-        <el-tab-pane label="视频生成" name="video">
-          <template #label>
-            <div class="tab-label">
-              <el-icon class="tab-icon"><VideoPlay /></el-icon>
-              <span>视频生成</span>
-            </div>
-          </template>
-        </el-tab-pane>
-        <el-tab-pane label="图片生成" name="image">
-          <template #label>
-            <div class="tab-label">
-              <el-icon class="tab-icon"><Picture /></el-icon>
-              <span>图片生成</span>
-            </div>
-          </template>
-        </el-tab-pane>
-      </el-tabs>
-    </div>
-
-    <!-- 输入区域容器 -->
-    <div class="input-container">
-      <!-- 上传区域:上传按钮和已上传图片水平排列 -->
-    <div class="upload-area">
-      <!-- 上传按钮 -->
-      <el-button 
-        type="default" 
-        class="add-btn"
-        @click="triggerUpload"
-        title="上传"
-      >
-        <el-icon><Plus /></el-icon>
-        <span>上传</span>
-      </el-button>
-
-      <!-- 已上传图片预览 -->
-      <div class="uploaded-images-container">
-        <div 
-          v-for="(img, index) in uploadedImages" 
-          :key="index"
-          class="message-image"
-        >
-          <!-- 使用自定义图片预览功能 -->
-          <el-image 
-            :src="img.url" 
-            fit="cover"
-            @click="handleImageClick(img)"
-          />
-          <el-button 
-            type="text" 
-            size="small" 
-            class="delete-img-btn"
-            @click.stop="removeImage(index)"
-          >
-            <el-icon><Delete /></el-icon>
-          </el-button>
-        </div>
-      </div>
-      
-      <!-- 隐藏的文件输入框 -->
-      <input
-        ref="fileInput"
-        type="file"
-        accept="image/*"
-        style="display: none"
-        @change="handleFileChange"
-      />
-    </div>
-
-      <!-- 输入区域:输入框和发送按钮 -->
-      <div class="input-wrapper">
-        <!-- 文本输入框 -->
-        <el-input 
-          v-model="prompt" 
-          type="textarea" 
-          :rows="3" 
-          :placeholder="getPlaceholder"
-          @paste="handlePaste"
-          class="chat-input"
-        />
-        
-        <!-- 右侧提交按钮 -->
-        <el-button type="primary" class="send-btn" @click="handleSubmit" :loading="uploading">
-          提交
-        </el-button>
-      </div>
-    </div>
-  </div>
-
-   <el-form inline>
-      <el-form-item>
-        <el-input v-model="searchInfo" placeholder="搜索关键字" clearable style="width: 300px;" />
-        
-        <!-- 文件夹筛选下拉菜单 -->
-        <el-dropdown @command="handleFolderCommand" style="margin-right: 10px;">
-          <el-button  icon="search" title="文件夹筛选">
-            {{ selectedFolder || '文件夹筛选' }}<el-icon class="el-icon--right"><arrow-down /></el-icon>
-          </el-button>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item command="">全部文件夹</el-dropdown-item>
-              <el-dropdown-item v-for="(folder, index) in folderList" :key="index" :command="folder">
-                {{ folder }}
-              </el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-        
-        <el-button type="primary" icon="search" @click="onSubmit" title="搜索">查询</el-button>
-      </el-form-item>
-    </el-form>
-
-  <!-- 表格展示 -->
-    <el-table
-      ref="multipleTable"
-      style="width: 100%; height: 62vh;"
-      :row-style="{ height: '20px' }"
-      :header-cell-style="{ padding: '0px' }"
-      :cell-style="{ padding: '0px' }"
-      :header-row-style="{ height: '20px' }"
-      border tooltip-effect="dark"
-      :data="tableData1"
-      row-key="ID"
-      highlight-current-row
-      :cell-class-name="tableDataCellClass"
-      @selection-change="SelectionChange"
-      @row-dblclick="onRowDblClick"
-      :show-overflow-tooltip="true"
-    >
-      <el-table-column type="selection" width="55" />
-      <el-table-column label="ID" prop="id" width="70" />
-      <el-table-column label="第一段" prop="chinese_description" width="300" />
-      <el-table-column label="第二段" prop="english_description" width="300" />
-      <el-table-column label="图片名称" prop="img_name" width="300" />
-      <el-table-column label="原图" width="120">
-        <template #default="{ row }">
-          <el-image
-            :src="formatImageUrl(row.old_image_url)"
-            :preview-src-list="[formatImageUrl(row.old_image_url)]"
-            style="width: 90px; height: 70px;"
-            fit="contain"
-            preview-teleported
-          />
-        </template>
-      </el-table-column>
-	  
-	  
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'black-forest-labs/FLUX.1-kontext-pro')" 
-	    label="FLUX.1" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'black-forest-labs/FLUX.1-kontext-pro'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'dall-e-3')" 
-	    label="dall-e-3" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'dall-e-3'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column 
-	    v-if="tableData1.some(row => row.model === 'gpt-image-1')" 
-	    label="gpt-image-1" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'gpt-image-1'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'MID_JOURNEY')" 
-	    label="MID_JOURNEY" 
-	    width="130">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'MID_JOURNEY'"
-	        :src="`https://chatapi.onechats.ai/mj/image/${row.taskId}`"  
-	        :preview-src-list="[`https://chatapi.onechats.ai/mj/image/${row.taskId}`]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-      
-      <el-table-column label="图生图预览" width="120">
-        <template #default="{ row }">
-          <el-image
-            :src="formatImageUrl(row.imgtoimg_url)"
-            :preview-src-list="[formatImageUrl(row.imgtoimg_url)]"
-            style="width: 90px; height: 70px;"
-            fit="contain"
-            preview-teleported
-          />
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 分页 -->
-    <div class="gva-pagination">
-      <el-pagination
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-        :current-page="page"
-        :page-sizes="[10, 30, 50, 100]"
-        :page-size="pageSize"
-        layout="total, sizes, prev, pager, next, jumper"
-        :total="total"
-      />
-    </div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted } from 'vue'
-import { Delete, ZoomIn, CirclePlus, VideoPlay, Picture } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-import { useUserStore } from '@/pinia/modules/user'
-import { isImageMime } from '@/utils/image'
-import service from '@/utils/request'
-import {imageToText,getTable, Template_ids,txttoimg_moxing,GetHttpUrl,txttoimg_update, getSide} from '@/api/mes/job'
-
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-//获取服务器地址
-const baseUrl = ref('')
-const port = ref('')
-const full_url = ref('')
-// 从接口获取服务器地址
-const fetchServerUrl = async () => {
-  try {
-    const res = await GetHttpUrl()
-    if (res.code === 0 && res.data && res.data.full_url) {
-      full_url.value = res.data.full_url
-      baseUrl.value = res.data.baseUrl
-      port.value = res.data.port
-	  console.log('获取服务器地址',full_url.value)
-	  console.log('IP地址',baseUrl.value)
-	  console.log('端口',port.value)
-    }
-  } catch (error) {
-    console.error('获取服务器地址失败:', error)
-  }
-}
-fetchServerUrl();
-
-const tableData1 = ref([])
-const selectedFolder = ref('')
-const folderList = ref([])
-
-const getTableData = async () => {
-    const res = await getTable({
-      search: searchInfo.value,
-      limit: pageSize.value,
-      page: page.value,
-      folder: selectedFolder.value
-    })
-    tableData1.value = res.data.list
-    total.value = res.data.total
-    
-    // 更新文件夹列表
-    if (res.data.folder) {
-      folderList.value = res.data.folder
-    }
-
-    // const res_Template = await Template_ids({path:row['old_image_url']});
-    // height.value = res_Template.data.height
-    // width.value = res_Template.data.width
-}
-getTableData()
-
-// 处理文件夹选择
-const handleFolderCommand = (command) => {
-  selectedFolder.value = command
-  onSubmit()
-}
-
-// 消息内容
-const prompt = ref('')
-// 已上传图片列表
-const uploadedImages = ref([])
-// 文件输入框引用
-const fileInput = ref(null)
-// 上传状态
-const uploading = ref(false)
-// 激活的标签页
-const activeTab = ref('image')
-// 生成类型
-const generationType = computed(() => activeTab.value === 'video' ? '视频生成' : '图片生成')
-// API路径
-const path = ref(import.meta.env.VITE_BASE_API)
-
-// 触发文件上传
-const triggerUpload = () => {
-  fileInput.value?.click()
-}
-
-// 处理文件选择
-const handleFileChange = (e) => {
-  const file = e.target.files[0]
-  if (file) {
-    processImageFile(file)
-  }
-  // 清空文件输入
-  if (fileInput.value) {
-    fileInput.value.value = ''
-  }
-}
-
-// 处理粘贴事件
-const handlePaste = (e) => {
-  console.log('粘贴事件触发')
-  const items = e.clipboardData.items
-  console.log('剪贴板项目:', items)
-  
-  for (let i = 0; i < items.length; i++) {
-    console.log('项目', i, '类型:', items[i].type)
-    if (items[i].type.indexOf('image') !== -1) {
-      const file = items[i].getAsFile()
-      console.log('获取到图片文件:', file)
-      processImageFile(file)
-      break
-    }
-  }
-}
-
-// 处理图片文件
-const processImageFile = (file) => {
-  // 验证文件类型
-  if (!isImageMime(file.type)) {
-    ElMessage.error('请选择JPG、PNG或WebP格式的图片')
-    return
-  }
-  
-  // 验证文件大小(限制5MB)
-  const maxSize = 5 * 1024 * 1024
-  if (file.size > maxSize) {
-    ElMessage.error('图片大小不能超过5MB')
-    return
-  }
-  
-  // 读取文件并添加到上传列表
-  const reader = new FileReader()
-  reader.onload = (e) => {
-    const imageUrl = e.target.result
-    // 检查图片是否已存在
-    const isDuplicate = uploadedImages.value.some(img => img.url === imageUrl)
-    if (!isDuplicate) {
-      uploadedImages.value.push({
-        url: imageUrl,
-        file: file
-      })
-    }
-  }
-  reader.readAsDataURL(file)
-}
-
-// 删除图片(从已上传列表)
-const removeImage = (index) => {
-  uploadedImages.value.splice(index, 1)
-}
-
-// 处理图片点击(放大查看)
-const handleImageClick = (img) => {
-  // 创建自定义图片预览
-  const previewContainer = document.createElement('div')
-  previewContainer.className = 'custom-image-preview'
-  previewContainer.style.cssText = `
-    position: fixed;
-    top: 0;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    background: rgba(0, 0, 0, 0.9);
-    z-index: 9999;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  `
-  
-  // 添加图片
-  const previewImage = document.createElement('img')
-  previewImage.src = img.url
-  previewImage.style.cssText = `
-    max-width: 90%;
-    max-height: 90%;
-    object-fit: contain;
-  `
-  
-  // 添加关闭按钮
-  const closeButton = document.createElement('button')
-  closeButton.innerHTML = '&times;'
-  closeButton.style.cssText = `
-    position: absolute;
-    top: 20px;
-    right: 20px;
-    font-size: 30px;
-    color: white;
-    background: transparent;
-    border: none;
-    cursor: pointer;
-    padding: 0;
-    width: 30px;
-    height: 30px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-  `
-  
-  // 组装预览组件
-  previewContainer.appendChild(previewImage)
-  previewContainer.appendChild(closeButton)
-  document.body.appendChild(previewContainer)
-  
-  // 关闭预览的处理
-  const closePreview = () => {
-    document.body.removeChild(previewContainer)
-  }
-  
-  closeButton.addEventListener('click', closePreview)
-  previewContainer.addEventListener('click', (e) => {
-    if (e.target === previewContainer) {
-      closePreview()
-    }
-  })
-  
-  // ESC键关闭
-  const handleEscKey = (e) => {
-    if (e.key === 'Escape') {
-      closePreview()
-      document.removeEventListener('keydown', handleEscKey)
-    }
-  }
-  document.addEventListener('keydown', handleEscKey)
-}
-
-
-// 提交处理
-const handleSubmit = async () => {
-  if (!prompt.value.trim() && uploadedImages.value.length === 0) {
-    ElMessage.warning('请输入消息或上传图片')
-    return
-  }
-  
-  try {
-    uploading.value = true
-    
-    // 准备提交数据
-    let status_val = ''
-    
-    // 根据标签页和上传内容确定生成类型
-    if (activeTab.value === 'image') {
-      // 图片生成
-      status_val = uploadedImages.value.length > 0 ? '图生图' : '文生图'
-    } else {
-      // 视频生成
-      status_val = uploadedImages.value.length > 0 ? '图生视频' : '文生视频'
-    }
-    
-    const payload = {
-      status_val: status_val,
-      batch: uploadedImages.value.map(item => item.file), // 图片数组
-      prompt: prompt.value
-    }
-    
-    // 打印调试区域
-    console.log(payload);
-    await new Promise(resolve => setTimeout(resolve, 2000));
-    uploading.value = false;
-    return;
-
-    const res = await imageToText(payload)
-    
-    // 处理接口返回结果
-    if (res.code === 0) {
-      ElMessage.success('提交成功')
-      console.log('接口返回结果:', res)
-      
-      // 清空输入和图片列表
-      prompt.value = ''
-      uploadedImages.value = []
-    } else {
-      ElMessage.error(res.msg || '提交失败')
-    }
-  } catch (error) {
-    console.error('提交失败:', error)
-    ElMessage.error('提交失败,请稍后重试')
-  } finally {
-    uploading.value = false
-  }
-}
-
-// 计算预览列表
-const previewList = computed(() => {
-  return uploadedImages.value.map(item => item.url)
-})
-
-// 计算已选图片数量
-const selectedImagesCount = computed(() => {
-  return uploadedImages.value.length
-})
-
-// 根据生成类型获取占位符
-const getPlaceholder = computed(() => {
-  if (generationType.value === '视频生成') {
-    return '输入视频生成的提示词'
-  } else {
-    return '输入图片生成的提示词'
-  }
-})
-
-// 将DataURL转换为File对象
-const dataURLtoFile = (dataurl, filename) => {
-  const arr = dataurl.split(',')
-  const mime = arr[0].match(/:(.*?);/)[1]
-  const bstr = atob(arr[1])
-  let n = bstr.length
-  const u8arr = new Uint8Array(n)
-  while (n--) {
-    u8arr[n] = bstr.charCodeAt(n)
-  }
-  return new File([u8arr], filename, { type: mime })
-}
-
-
-</script>
-
-<style scoped>
-/* 生成类型选项卡 */
-.generation-tabs {
-  margin-bottom: 10px;
-  width: 100%;
-}
-
-.generation-tabs :deep(.el-tabs__header) {
-  margin: 0;
-}
-
-.generation-tabs :deep(.el-tabs__nav-wrap) {
-  margin: 0;
-}
-
-.generation-tabs :deep(.el-tabs__nav) {
-  background: transparent;
-}
-
-.generation-tabs :deep(.el-tabs__item) {
-  padding: 0 20px;
-  height: 40px;
-  line-height: 40px;
-  font-size: 14px;
-  color: #606266;
-  border: none;
-  border-radius: 0;
-  transition: all 0.3s ease;
-}
-
-.generation-tabs :deep(.el-tabs__item.is-active) {
-  color: #409eff;
-  font-weight: 500;
-  background: transparent;
-}
-
-.generation-tabs :deep(.el-tabs__active-bar) {
-  background-color: #409eff;
-  height: 2px;
-}
-
-.generation-tabs :deep(.tab-label) {
-  display: flex;
-  align-items: center;
-  gap: 6px;
-}
-
-.generation-tabs :deep(.tab-icon) {
-  font-size: 16px;
-}
-
-/* 聊天界面样式 */
-.chat-interface {
-  display: flex;
-  flex-direction: column;
-  width: 70%;
-  margin: 0 auto;
-  padding: 20px;
-}
-
-/* 输入区域容器 */
-.input-container {
-  display: flex;
-  flex-direction: column;
-  border: 1px solid #e4e7ed;
-  border-radius: 12px;
-  overflow: hidden;
-  width: 100%;
-  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
-  background-color: #fff;
-}
-
-/* 上传图片列表容器 */
-.uploaded-images-container {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 8px;
-  padding: 0;
-  min-height: auto;
-  max-height: 150px;
-  overflow-y: auto;
-}
-
-/* 单张图片样式 */
-.message-image {
-  position: relative;
-  width: 60px;
-  height: 60px;
-  border-radius: 4px;
-  overflow: hidden;
-  cursor: pointer;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-  transition: all 0.3s ease;
-}
-
-.message-image:hover {
-  transform: translateY(-1px);
-  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
-}
-
-.message-image :deep(.el-image) {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-  cursor: zoom-in;
-}
-
-/* 删除按钮 */
-.delete-img-btn {
-  position: absolute;
-  top: 2px;
-  right: 2px;
-  background-color: rgba(255, 255, 255, 0.8);
-  border-radius: 50%;
-  width: 20px;
-  height: 20px;
-  padding: 0;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  opacity: 0;
-  transition: opacity 0.3s ease;
-  font-size: 12px;
-  cursor: pointer;
-}
-
-/* 图片选择对话框样式 */
-.image-selector {
-  display: flex;
-  flex-direction: column;
-  gap: 15px;
-  height: 100%;
-  max-height: 600px;
-  overflow-y: auto;
-}
-
-.image-upload-area {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  height: 240px;
-  padding: 15px;
-  border: 1px dashed #dcdfe6;
-  border-radius: 8px;
-  background-color: #fafafa;
-  cursor: pointer;
-  transition: all 0.3s ease;
-}
-
-.upload-box {
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-  justify-content: center;
-  align-items: center;
-  height: 100%;
-}
-
-.upload-icon {
-  font-size: 24px;
-  color: #909399;
-  margin-bottom: 8px;
-}
-
-.upload-text {
-  font-size: 14px;
-  color: #606266;
-}
-
-.upload-hint {
-  font-size: 12px;
-  color: #909399;
-  margin-top: 4px;
-}
-
-.image-upload-area:hover {
-  border-color: #409eff;
-  color: #409eff;
-  background-color: #ecf5ff;
-}
-
-
-.image-upload-area:hover {
-  border-color: #409eff;
-  background-color: #ecf5ff;
-}
-
-.upload-box {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-  color: #909399;
-}
-
-.upload-icon {
-  font-size: 24px;
-  color: #c0c4cc;
-}
-
-.upload-box span {
-  font-size: 14px;
-  font-weight: 500;
-}
-
-/* 移除AI绘图文字相关样式 */
-.image-overlay {
-  display: none;
-}
-
-.image-tags {
-  display: none;
-}
-
-/* 图片预览容器 */
-.image-preview-container {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  padding: 20px;
-  max-height: 70vh;
-  overflow: auto;
-}
-
-.full-size-image {
-  max-width: 100%;
-  max-height: 100%;
-  object-fit: contain;
-  border-radius: 4px;
-}
-
-.message-image:hover .delete-img-btn {
-  opacity: 1;
-}
-
-/* 上传区域:上传按钮和已上传图片水平排列 */
-.upload-area {
-  display: flex;
-  gap: 10px;
-  background-color: #fafafa;
-  border-bottom: 1px solid #e4e7ed;
-  margin-bottom: 0;
-  padding: 16px;
-}
-
-/* 上传按钮 */
-.add-btn {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 60px;
-  height: 60px;
-  border: 2px dashed #dcdfe6;
-  border-radius: 8px;
-  background-color: #fff;
-  color: #909399;
-  font-size: 24px;
-  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-.add-btn:hover {
-  border-color: #409eff;
-  color: #409eff;
-  background-color: #ecf5ff;
-  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
-}
-
-.add-btn span {
-  display: none;
-}
-
-/* 已上传图片容器 */
-.uploaded-images-container {
-  display: flex;
-  gap: 10px;
-  flex-wrap: wrap;
-  align-items: center;
-}
-
-/* 消息图片 */
-.message-image {
-  position: relative;
-  width: 60px;
-  height: 60px;
-  border-radius: 8px;
-  overflow: hidden;
-  cursor: pointer;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
-}
-
-.message-image .el-image {
-  width: 100%;
-  height: 100%;
-  border-radius: 8px;
-}
-
-.input-wrapper {
-  display: flex;
-  gap: 12px;
-  align-items: flex-end;
-  padding: 16px;
-  background-color: #fff;
-}
-
-.add-btn :deep(.el-icon) {
-  font-size: 24px;
-  margin-bottom: 4px;
-}
-
-/* 聊天输入框 */
-.chat-input {
-  flex: 1;
-  border: none;
-  border-radius: 4px;
-  resize: none;
-  outline: none;
-  font-size: 14px;
-  line-height: 1.5;
-}
-
-:deep(.el-textarea) {
-  width: 100%;
-  margin: 0;
-}
-
-:deep(.el-textarea__inner) {
-  border: 1px solid #e4e7ed;
-  border-radius: 8px;
-  resize: none;
-  padding: 12px 16px;
-  font-size: 14px;
-  line-height: 1.5;
-  background-color: #fff;
-  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-  min-height: 80px;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
-}
-
-:deep(.el-textarea__inner:focus) {
-  border-color: #409eff;
-  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
-  outline: none;
-}
-
-/* 发送按钮 */
-.send-btn {
-  border-radius: 8px;
-  padding: 8px 16px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: 14px;
-  background-color: #409eff;
-  border-color: #409eff;
-  transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
-  align-self: flex-end;
-  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
-}
-
-.send-btn:hover {
-  background-color: #66b1ff;
-  border-color: #66b1ff;
-  transform: translateY(-1px);
-  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
-}
-
-/* 响应式设计 */
-@media (max-width: 768px) {
-  /* 输入容器响应式 */
-  .input-container {
-    border-radius: 8px;
-  }
-  
-  /* 上传区域响应式 */
-  .upload-area {
-    padding: 12px;
-    gap: 8px;
-  }
-  
-  .add-btn {
-    width: 50px;
-    height: 50px;
-    font-size: 20px;
-  }
-  
-  .message-image {
-    width: 50px;
-    height: 50px;
-  }
-  
-  /* 输入区域响应式 */
-  .input-wrapper {
-    flex-direction: column;
-    align-items: stretch;
-    padding: 12px;
-    gap: 12px;
-  }
-  
-  .chat-input {
-    min-height: 70px;
-  }
-  
-  .send-btn {
-    width: 44px;
-    height: 44px;
-    font-size: 18px;
-  }
-}
-</style>

+ 0 - 1774
src/view/performance/QualityAssessment/Product.vue

@@ -1,1774 +0,0 @@
-<template>
-  <div>
-    <layout>
-      <layout-header>
-        <!-- 搜索区域 -->
-        <el-form inline>
-          <el-form-item>
-            <el-input v-model="searchInfo" placeholder="搜索关键字" clearable style="width: 300px;" />
-            <el-button type="primary" icon="search" @click="onSubmit" title="搜索">查询</el-button>
-            <el-button type="primary"  @click="onADD" >新增产品</el-button>
-          </el-form-item>
-        </el-form>
-      </layout-header>
-
-      <layout>
-        <!-- 左侧树形结构 -->
-        <template v-if="userStore.userInfo.nickName === '超级管理员'">
-        <layout-sider :resize-directions="['right']" :width="290" style="margin-right: 10px">
-          <div class="JKWTree-tree" style="height: 200px;width:100vh">
-            <h3>商户名称</h3>
-            <el-tree :data="treeData" class="treecolor"   @node-click="handleNodeClick"></el-tree>
-          </div>
-        </layout-sider>
-      </template>
-        <!-- 右侧区域 -->
-        <layout-content>
-          <el-main style="padding: 0;">
-            <div class="gva-table-box">
-              <!-- 表格展示 -->
-              <el-table
-                ref="multipleTable"
-                style="width: 100%; height: 62vh;"
-                :row-style="{ height: '20px' }"
-                :header-cell-style="{ padding: '0px' }"
-                :cell-style="{ padding: '0px' }"
-                :header-row-style="{ height: '20px' }"
-                border tooltip-effect="dark"
-                :data="tableData1"
-                row-key="ID"
-                highlight-current-row
-                :cell-class-name="tableDataCellClass"
-                @selection-change="SelectionChange"
-                @row-dblclick="onRowDblClick"
-                :show-overflow-tooltip="true">
-                <el-table-column type="selection" width="55" />
-                <el-table-column label="ID" prop="id" width="70" />
-                <el-table-column label="产品名称" prop="产品名称" width="300" />
-                <el-table-column label="产品编码" prop="产品编码" width="300" />
-                <el-table-column label="产品图片" width="120">
-                  <template #default="{ row }">
-                    <el-image
-                      :src="formatImageUrl(row.产品图片)"
-                      :preview-src-list="[formatImageUrl(row.产品图片)]"
-                      style="width: 90px; height: 70px;"
-                      fit="contain"
-                      preview-teleported
-                    />
-                  </template>
-                </el-table-column>
-                <el-table-column label="效果图" width="120">
-                  <template #default="{ row }">
-                    <el-image
-                      :src="formatImageUrl(row.产品效果图)"
-                      :preview-src-list="[formatImageUrl(row.产品效果图)]"
-                      style="width: 90px; height: 70px;"
-                      fit="contain"
-                      preview-teleported>
-                      <template #error>
-                        <div style="width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #f5f7fa; color: #909399; font-size: 12px;">
-                          <el-icon><Picture /></el-icon>
-                          <span>暂无效果图</span>
-                        </div>
-                      </template>
-                    </el-image>
-                  </template>
-                </el-table-column>
-                <el-table-column label="创建时间" prop="创建时间" width="300" />
-                <el-table-column label="创建人" prop="创建人" width="300" />
-              </el-table>
-
-              <div class="gva-pagination">
-                <el-pagination
-                  @size-change="handleSizeChange"
-                  @current-change="handleCurrentChange"
-                  :current-page="page"
-                  :page-sizes="[10, 30, 50, 100]"
-                  :page-size="pageSize"
-                  layout="total, sizes, prev, pager, next, jumper"
-                  :total="total"/>
-              </div>
-            </div>
-          </el-main>
-        </layout-content>
-      </layout>
-    </layout>
-
-<el-dialog 
-  v-model="editDialogVisible" 
-  title="图片对比与编辑" 
-  fullscreen
-  :modal="true"
-  @close="handleDialogClose">
-  
-  <div class="image-edit-container" style="display: flex; gap: 20px; height: calc(100vh - 140px);">
-    <!-- 左侧:原图新图 + 输入框 -->
-    <div class="left-column" style="flex: 2; display: flex; flex-direction: column; gap: 20px;">
-      
-      <!-- 图片对比区域 -->
-      <div class="image-comparison-section" style="flex: 1; display: flex; flex-direction: column; gap: 15px;">
-        <div class="image-item">
-    <div style="display: flex; justify-content: space-between; width: 100%;">
-      <div style="display: flex; gap: 12px;">
-        <el-button type="danger" size="large" @click="editDialogVisible = false">
-          <el-icon><Close /></el-icon>关闭
-        </el-button>
-        <el-button type="primary" size="large" @click="downloadImage('original')">
-          <el-icon><Download /></el-icon>下载原图
-        </el-button>
-        <el-button type="primary" size="large" @click="downloadImage('new')">
-        <el-icon><Download /></el-icon>下载新图
-        </el-button>
-      </div>
-
-      <div style="display: flex; gap: 12px;">
-      <el-button type="success" size="large">
-        <el-icon><Download /></el-icon>内容审核
-      </el-button>
-        <el-button type="primary" size="large">
-          <el-icon><Download /></el-icon>确认广告图
-        </el-button>
-      </div>
-    </div>
-    
-    <!-- 原图区域 -->
-    <div class="image-preview" style="position: relative;">
-      <el-image
-        v-if="editFormData.original_image_url"
-        :src="formatImageUrl(editFormData.original_image_url)"
-        :preview-src-list="[formatImageUrl(editFormData.original_image_url)]"
-        style="width: 100%; height: 200px;"
-        fit="contain"
-        preview-teleported
-      />
-      <el-button
-        v-if="editFormData.original_image_url"
-        type="primary"
-        circle
-        size="medium"
-        style="position: absolute; top: 10px; right: 10px; opacity: 0; transition: opacity 0.3s; z-index: 10;"
-        @click.stop="downloadImage('original')"
-        class="image-download-btn"
-      >
-        <el-icon><Download /></el-icon>
-      </el-button>
-      <div v-else class="image-placeholder">
-        <el-icon :size="40"><Picture /></el-icon>
-        <span>暂无原图</span>
-      </div>
-      <div class="image-info">
-        <span>文件名: {{ editFormData.original_name || '未命名' }}</span>
-      </div>
-    </div>
-
-    <!-- 新图区域 -->
-    <div class="image-preview" style="position: relative;">
-      <el-image
-        v-if="editFormData.new_image_url"
-        :src="formatImageUrl(editFormData.new_image_url)"
-        style="width: 100%; height: 200px; cursor: pointer;"
-        fit="contain"
-        @click="updateProductImage(editFormData.new_image_url)"
-      />
-      <el-button
-        v-if="editFormData.new_image_url"
-        type="primary"
-        circle
-        size="medium"
-        style="position: absolute; top: 10px; right: 10px; opacity: 0; transition: opacity 0.3s; z-index: 10;"
-        @click.stop="downloadImage('new')"
-        class="image-download-btn"
-      >
-        <el-icon><Download /></el-icon>
-      </el-button>
-      <div v-else class="image-placeholder">
-        <el-icon :size="40"><Picture /></el-icon>
-        <span>暂无新图</span>
-      </div>
-      <div class="image-info">
-        <span>文件名: {{ editFormData.img_name || '未命名' }}</span>
-      </div>
-    </div>
-  </div>
-        
-
-      
-<!-- 输入框区域 -->
-<div class="edit-section" style="flex: 1; border-top: 1px solid #e4e7ed; padding-top: 20px; display: flex; flex-direction: column;">
-    <el-form :model="editFormData" label-width="80px" style="flex: 1; display: flex; flex-direction: column;">
-        <el-input
-          type="textarea"
-          v-model="editFormData.chinese_description"
-          :rows="12"
-          placeholder="点击右侧模板图片可自动填充描述内容"
-          show-word-limit
-          maxlength="500"
-          style="width: 100%;"
-        />
-    </el-form>
-    
-<!-- 按钮放在表单下方 -->
-<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 15px; padding-top: 15px; border-top: 1px solid #e4e7ed;">
-  <!-- 左侧:尺寸选择区域 -->
-  <div style="display: flex; align-items: center; gap: 10px;">
-    <span style="font-size: 14px; color: #606266;">尺寸比例:</span>
-    <el-select v-model="selectedSize" size="small" style="width: 120px;" @change="handleSizeChange">
-      <el-option label="1:1" value="1:1" />
-      <el-option label="4:3" value="4:3" />
-      <el-option label="3:2" value="3:2" />
-      <el-option label="2:3" value="2:3" />
-      <el-option label="9:16" value="9:16" />
-      <el-option label="16:9" value="16:9" />
-      <el-option label="自由尺寸" value="custom" />
-    </el-select>
-    
-    <!-- 自由尺寸的像素输入 -->
-    <div v-if="selectedSize === 'custom'" style="display: flex; align-items: center; gap: 5px;">
-      <el-input 
-        v-model="customWidth" 
-        size="small" 
-        style="width: 80px;" 
-        placeholder="宽"
-        @input="validateCustomSize"
-      >
-        <template #append>px</template>
-      </el-input>
-      <span style="color: #909399;">×</span>
-      <el-input 
-        v-model="customHeight" 
-        size="small" 
-        style="width: 80px;" 
-        placeholder="高"
-        @input="validateCustomSize"
-      >
-        <template #append>px</template>
-      </el-input>
-    </div>
-    
-    <!-- 显示当前尺寸信息 -->
-    <div v-if="selectedSize !== 'custom'" style="font-size: 12px; color: #909399;">
-      {{ getSizeInfo(selectedSize) }}
-    </div>
-  </div>
-  
-  <!-- 右侧:操作按钮 -->
-  <div style="display: flex; gap: 10px;">
-    <el-button @click="clearInput">清空</el-button>
-    <el-button type="primary" @click="optimizeContent">内容优化</el-button>
-    <el-button type="success" @click="generateImage">生成图片</el-button>
-  </div>
-</div>
-  </div>
-</div>
-    
-    </div>
-    
-    <!-- 中间:产品信息 -->
-    <div class="middle-column" style="width: 430px; border-left: 1px solid #e4e7ed; border-right: 1px solid #e4e7ed; padding: 0 20px;">
-      <div style="height: 100%; display: flex; flex-direction: column;">
-        
-        <!-- 上:留出的空白区域 -->
-        <div style="height: 90px; background-image: url('/public/src/assets/top-bg.png'); background-size: cover; background-position: center; border-top-left-radius: 8px; border-top-right-radius: 8px;"></div>
-        
-        <!-- 上中:图片显示区域 -->
-        <div style="display: flex; flex-direction: column; align-items: center;">
-          <div style="width: 430px; height: 290px; display: flex; justify-content: center; align-items: center; background-color: white; border-radius: 4px;">
-            <el-image
-              v-if="editFormData.new_image_url && showProductImage"
-              :src="formatImageUrl(editFormData.new_image_url)"
-              :preview-src-list="[formatImageUrl(editFormData.new_image_url)]"
-              style="width: 100%; height: 100%; object-fit: fill;"
-              fit="fill"
-              preview-teleported
-            />
-            <div v-else-if="!showProductImage || !editFormData.new_image_url" class="image-placeholder">
-              <el-icon :size="60"><Picture /></el-icon>
-              <span style="margin-top: 10px; display: block;">暂无产品图片</span>
-            </div>
-          </div>
-        </div>
-        
-        <!-- 中:商品信息表单 -->
-        <div class="product-info-container" style="height: 320px;border: 12px solid #f6E0dd; border-radius: 4px; padding: 15px;">
-          <div class="product-info-item">
-            <span class="product-info-label">商品条码:</span>
-            <span class="product-info-value">{{ editFormData.barcode || '-' }}</span>
-          </div>
-          <div class="product-info-item">
-            <span class="product-info-label">宏伟编码:</span>
-            <span class="product-info-value">{{ editFormData.code || '-' }}</span>
-          </div>
-          <div class="product-info-item">
-            <span class="product-info-label">产品名称:</span>
-            <span class="product-info-value">{{ editFormData.product_name || '-' }}</span>
-          </div>
-          <div class="product-info-item">
-            <span class="product-info-label">品牌名称:</span>
-            <span class="product-info-value">{{ editFormData.brand_name || '-' }}</span>
-          </div>
-          <div class="product-info-item">
-            <span class="product-info-label">产品规格:</span>
-            <span class="product-info-value">{{ editFormData.specification || '-' }}</span>
-          </div>
-          <div class="product-info-item">
-            <span class="product-info-label">单 位:</span>
-            <span class="product-info-value">{{ editFormData.unit || '-' }}</span>
-          </div>
-        </div>
-        
-        <!-- 下:空出来的部分 -->
-        <div style="width: 430px; height: 61px;">
-          
-        </div>
-      </div>
-    </div>
-    
-    <!-- 右侧:模板列表 -->
-    <div class="right-column" style="flex: 2; padding-left: 20px;">
-  <div class="right-template" style="height: 100%; display: flex; flex-direction: column;">
-    <div class="template-header" style="margin-bottom: 15px;">
-    </div>
-    
-    <!-- 搜索框 -->
-    <div class="template-search" style="margin-bottom: 15px;">
-      <el-input
-        v-model="searchKeyword"
-        placeholder="输入关键词搜索模板..."
-        clearable
-        @keyup.enter="handleSearch"
-        @clear="handleClearSearch"
-      >
-        <template #prefix>
-          <el-icon><Search /></el-icon>
-        </template>
-        <template #append>
-          <el-button @click="handleSearch" >搜索</el-button>
-        </template>
-      </el-input>
-    </div>
-    
-    <!-- 模板列表 -->
-    <div style="flex: 1; min-height: 0;">
-      <h4>参考模板 ({{ templateList.length }})</h4>
-      <el-scrollbar style="height: 100%;">
-        <div class="template-list">
-          <!-- 搜索无结果提示 -->
-          <div v-if="!searchLoading && searchKeyword && templateList.length === 0" class="empty-search">
-            <el-icon :size="50"><Search /></el-icon>
-            <p>未找到相关模板</p>
-          </div>
-          
-          <!-- 模板项 -->
-          <div 
-            v-for="template in templateList" 
-            :key="template.id"
-            class="template-item"
-            :class="{ 'active': currentTemplateId === template.id }"
-            @click="selectTemplate(template)"
-          >
-            <div class="template-thumbnail" style="position: relative;">
-              <div class="image-container" @click.stop="selectTemplate(template)">
-                <el-image
-                  :src="template.template_image_url"
-                  style="width: 100%; height: 100%;"
-                  fit="cover"
-                >
-                  <template #error>
-                    <div class="thumbnail-error">
-                      <el-icon><Picture /></el-icon>
-                    </div>
-                  </template>
-                </el-image>
-                <div class="zoom-icon" @click.stop="handleImageZoom(template.template_image_url)">
-                  <el-icon :size="20"><ZoomIn /></el-icon>
-                </div>
-              </div>
-              <div class="template-id">模板 {{ template.id }}</div>
-              <el-button
-                type="primary"
-                circle
-                size="small"
-                style="position: absolute; top: 10px; right: 10px; opacity: 0; transition: opacity 0.3s; z-index: 10;"
-                @click.stop="downloadTemplateImage(template.template_image_url)"
-                class="image-download-btn"
-              >
-                <el-icon><Download /></el-icon>
-              </el-button>
-            </div>
-            <div class="template-desc" :title="template.chinese_description">
-              {{ truncateText(template.chinese_description, 25) }}
-            </div>
-          </div>
-          
-          <!-- 初始无数据提示 -->
-          <div v-if="!searchKeyword && !searchLoading && templateList.length === 0" class="empty-templates">
-            <el-icon :size="40"><Picture /></el-icon>
-            <p>暂无模板数据</p>
-          </div>
-        </div>
-      </el-scrollbar>
-    
-    <!-- 图片预览组件 -->
-    <el-image-viewer
-      v-if="previewVisible"
-      :url-list="[previewImageUrl]"
-      @close="previewVisible = false"
-    />
-    </div>
-  </div>
-</div>
-  </div>
-  
-
-</el-dialog>
-
-    <!-- 图片预览 -->
-    <el-dialog v-model="dialogVisible" title="图片预览" width="70%" top="5vh" destroy-on-close>
-      <div style="text-align: center;">
-        <el-image :src="currentImageUrl" style="max-width: 100%; max-height: 70vh;" fit="contain" />
-      </div>
-    </el-dialog>
-
-
-
-    <el-dialog
-    v-model="AdddialogVisible"
-    title="新增产品"
-    width="600px"
-    :close-on-click-modal="false"
-    @close="handleClose"
-  >
-    <el-form
-      ref="productFormRef"
-      :model="productForm"
-      label-width="100px"
-      label-position="right"
-      size="medium"
-    >
-      <!-- 产品名称 -->
-      <el-form-item label="产品名称" prop="product_name">
-        <el-input
-          v-model="productForm.product_name"
-          placeholder="请输入产品名称"
-          clearable
-          show-word-limit
-        />
-      </el-form-item>
-
-      <!-- 产品编码 -->
-      <el-form-item label="产品编码" prop="product_code">
-        <el-input
-          v-model="productForm.product_code"
-          placeholder="请输入产品编码"
-          clearable
-          show-word-limit
-        />
-      </el-form-item>
-
-
-
-      <!-- 商户 -->
-      <el-form-item label="商户" prop="merchant_id">
-        <el-input
-          v-model="productForm.merchant_id"
-          placeholder="请选择商户"
-          style="width: 100%"
-          clearable
-          filterable
-        >
-        </el-input>
-      </el-form-item>
-
-      <!-- 产品图片 -->
-      <el-form-item label="产品图片" prop="product_img">
-        <div class="upload-container">
-          <!-- 图片展示 -->
-          <div v-if="productForm.product_img" class="image-preview">
-            <el-image
-              :src="productForm.product_img"
-              :preview-src-list="[productForm.product_img]"
-              fit="cover"
-              class="preview-image"
-            />
-            <div class="image-actions">
-              <el-button type="text" @click="handleViewImage">查看</el-button>
-              <el-button type="text" @click="handleRemoveImage">删除</el-button>
-            </div>
-          </div>
-          
-          <!-- 上传区域 -->
-          <el-upload
-            v-else
-            class="image-uploader"
-            :action="uploadUrl"
-            :show-file-list="false"
-            :on-success="handleUploadSuccess"
-            :on-error="handleUploadError"
-            :before-upload="beforeUpload"
-            accept="image/*"
-          >
-            <el-button type="primary" plain>
-              <el-icon><Plus /></el-icon>
-              上传图片
-            </el-button>
-            <div class="upload-tip">支持 JPG、PNG 格式,大小不超过 5MB</div>
-          </el-upload>
-        </div>
-        <div class="form-tip">请上传产品图片</div>
-      </el-form-item>
-    </el-form>
-
-    <template #footer>
-      <span class="dialog-footer">
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">
-          确定
-        </el-button>
-      </span>
-    </template>
-  </el-dialog>
-
-
-
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, toRaw, onMounted ,watch } from 'vue'
-import { ElMessage, ElLoading} from 'element-plus'
-import { getTable, imageToText, Template_ids,txttoimg_moxing,GetHttpUrl,txttoimg_update, getSide,merchantGetab,productList,productDetail,
-  product_template,GetTxtToTxt,GetProductFind,productAdd,getMerchantId } from '@/api/mes/job'
-import { useUserStore } from '@/pinia/modules/user'
-import { ArrowDown, ZoomIn } from '@element-plus/icons-vue'
-import { Layout, LayoutHeader, LayoutSider, LayoutContent } from '@arco-design/web-vue'
-const props = defineProps({
-  editFormData: {
-    type: Object,
-    default: () => ({})
-  }
-})
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-//获取服务器地址
-const baseUrl = ref('')
-const port = ref('')
-const full_url = ref('')
-// 从接口获取服务器地址
-const fetchServerUrl = async () => {
-  try {
-    const res = await GetHttpUrl()
-    if (res.code === 0 && res.data && res.data.full_url) {
-      full_url.value = res.data.full_url
-      baseUrl.value = res.data.baseUrl
-      port.value = res.data.port
-    console.log('获取服务器地址',full_url.value)
-    console.log('IP地址',baseUrl.value)
-    console.log('端口',port.value)
-    }
-  } catch (error) {
-    console.error('获取服务器地址失败:', error)
-  }
-}
-fetchServerUrl();
-
-const searchInfo = ref('')
-const tableData1 = ref([])
-const total = ref(0)
-const page = ref(1)
-const pageSize = ref(50)
-const dialogVisible = ref(false)
-const currentImageUrl = ref('')
-const width = ref('')
-const height = ref('')
-const isLoading = ref(false)
-const txttotxt_selectedOption = ref('gemini-2.0-flash')
-const selectedOption = ref('dall-e-3')
-const num = ref(1)
-const _parh = ref([])
-const folderList = ref([])
-const selectedFolder = ref('')
-
-// 左侧树形数据
-const treeData = ref([])
-
-const getTreeData = async () => {
-  try {
-    const data  = await merchantGetab();
-    console.log(data)
-    
-    treeData.value = data.data.map(item => {
-      return {
-        label: item.tab || item.merchant_name,
-        value: item.merchant_code,
-        // 如果需要原始数据可以保留
-        originalData: item
-      }
-    })
-    
-  } catch (error) {
-    console.error(error)
-    treeData.value = [] // 出错时清空数据
-  }
-}
-
-getTreeData();
-
-const defaultProps = {
-  children: 'children',
-  label: 'label'
-}
-
-const editDialogVisible = ref(false)
-const editFormData = reactive({
-  id: '',
-  chinese_description: '',
-  english_description: '',
-  new_image_url: '',
-  imgtoimg_url: '',
-  img_name: ''
-})
-
-// 定义文生图表单对象
-const form = reactive({
-  chinese_description: ''
-})
-
-// 控制产品信息部分图片是否显示
-const showProductImage = ref(false)
-
-const formatImageUrl = (path) => {
-  if (!path) return ''
-  return `${full_url.value}/${path.replace(/^public\//, '')}`
-}
-
-// 点击图片时更新产品信息部分的图片
-const updateProductImage = (imageUrl) => {
-  editFormData.new_image_url = imageUrl
-  showProductImage.value = true
-  ElMessage.success('图片已更新到产品信息区域')
-}
-
-const txttoimg_modelList = ref([]); // 存储所有模型数据
-const txtimgselectedModel = ref(''); // 当前选中的模型名称
-const usedId = ref(null); // 当前使用的模型ID(兼容旧代码)
-const selectedId = ref(null); // 用户选择的模型ID(兼容旧代码)
-const isGenerating = ref(false); // 文生图生成中状态
-const usedIds = ref({}); // 当前使用的模型ID集合
-const selectedIds = ref({}); // 用户选择的模型ID集合
-
-// 获取文生图模型列表
-const fetchTxtImgModels = async () => {
-  try {
-    const response = await txttoimg_moxing();
-    txttoimg_modelList.value = response.data.models.wenshengtu;
-    // 获取当前使用的模型ID
-    usedIds.value = response.data.used_ids;
-    const currentUsedId = usedIds.value.wenshengtu;
-    
-    // 设置默认选中的模型
-    const defaultModel = txttoimg_modelList.value.find(item => item.id === currentUsedId);
-    if (defaultModel) {
-      txtimgselectedModel.value = defaultModel.txttoimg;
-      usedId.value = defaultModel.id;
-      selectedId.value = defaultModel.id; 
-      selectedIds.value.wenshengtu = defaultModel.id;
-      selectedOption.value = defaultModel.txttoimg;
-    }
-    
-    console.log('文生图模型列表:', txttoimg_modelList.value);
-    console.log('当前使用的模型ID:', currentUsedId);
-    console.log('默认选中的模型:', defaultModel);
-  } catch (error) {
-    console.error('获取文生图模型列表失败:', error);
-    ElMessage.error('获取模型列表失败');
-  }
-}
-fetchTxtImgModels();
-
-// const getTableData = async () => {
-//     const res = await getTable({
-//       search: searchInfo.value,
-//       limit: pageSize.value,
-//       page: page.value,
-//       folder: selectedFolder.value
-//     })
-//     tableData1.value = res.data.list
-//     total.value = res.data.total
-    
-//     // 更新文件夹列表
-//     if (res.data.folder) {
-//       folderList.value = res.data.folder
-//     }
-// }
-
-const nodeid = ref('')
-// 处理树节点点击
-const handleNodeClick = async(node) => {
-  console.log('点击的节点:', node.value);
-  nodeid.value = node.value;
-  
-  const res = await productList({
-    search: searchInfo.value,
-    limit: pageSize.value,
-    page: page.value,
-    code: node.value
-  });
-  
-  if (res.code === 0) {
-    // 按照ID进行排序,保持数据顺序
-    const sortedData = res.data.list.sort((b, a) => a.id - b.id);
-    tableData1.value = sortedData;
-    total.value = res.data.total;
-  }
-};
-
-// 当选择模型变化时
-const handleModelChange = (modelName) => {
-  const model = txttoimg_modelList.value.find(item => item.txttoimg === modelName);
-  if (model) {
-    selectedId.value = model.id;
-    selectedIds.value.wenshengtu = model.id;
-    selectedOption.value = model.txttoimg;
-    console.log('选择的模型ID:', model.id);
-  }
-}
-// 设置默认模型
-const setActive = async (type) => {
-  const id = selectedIds.value[type]
-  if (id) {
-    try {
-      // 调用API设置默认模型
-      const res = await txttoimg_update({ id: id,type:"wenshengtu"})
-      if (res.code === 0) {
-        usedIds.value[type] = id
-        ElMessage.success('默认模型设置成功')
-      } else {
-        ElMessage.error(res.msg || '设置默认模型失败')
-      }
-    } catch (error) {
-      console.error('设置默认模型失败', error)
-      ElMessage.error('设置默认模型失败')
-    }
-  }
-}
-
-
-const onRowDblClick = async (row) => {
-  try {
-    // 设置编辑表单ID
-    editFormData.id = row.id
-    editDialogVisible.value = true
-    
-    // 1. 获取产品详情并设置原图
-    const detailResponse = await productDetail({ id: row.id })
-    if (detailResponse.code === 0 && detailResponse.data) {
-      editFormData.original_image_url = detailResponse.data['产品图片']
-      editFormData.new_image_url = detailResponse.data['产品效果图']
-      editFormData.original_name = detailResponse.data['产品名称']
-      editFormData.product_name = detailResponse.data['产品名称']
-      editFormData.product_code = detailResponse.data['产品编码']
-    }
-    
-    // 2. 获取所有模板数据
-    await fetchTemplates()
-    
-  } catch (error) {
-    console.error('打开编辑弹窗失败:', error)
-    ElMessage.error('获取数据失败')
-  }
-}
-
-// 存储所有模板数据
-const templateList = ref([])
-const currentTemplateId = ref(null) // 当前选中的模板ID
-
-// 获取所有模板
-const fetchTemplates = async () => {
-  try {
-    const response = await product_template()
-    if (response.code === 0 && response.data) {
-      templateList.value = response.data.map(item => ({
-        id: item.id,
-        template_image_url: item.template_image_url,
-        chinese_description: item.chinese_description || '',
-        english_description: item.english_description || ''
-      }))
-      
-      // 默认选中第一个模板
-      if (templateList.value.length > 0) {
-        selectTemplate(templateList.value[0])
-      }
-    }
-  } catch (error) {
-    console.error('获取模板失败:', error)
-    templateList.value = []
-  }
-}
-
-// 选择模板
-const selectTemplate = (template) => {
-  currentTemplateId.value = template.id
-  editFormData.template_image_url = template.template_image_url
-  editFormData.template_id = template.id
-  editFormData.chinese_description = template.chinese_description
-}
-
-// 处理图片加载错误
-const handleTemplateImageError = (event) => {
-  event.target.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2YwZjBmMCIvPjx0ZXh0IHg9IjUwIiB5PSI1MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEyIiBmaWxsPSIjY2NjIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBkeT0iLjNlbSI+SW1hZ2UgRXJyb3I8L3RleHQ+PC9zdmc+'
-}
-
-
-
-// 修改后的 selectTemplate 函数(如果需要保留按钮)
-const handleTemplateButton = () => {
-  // 可以留空或给提示
-  ElMessage.info('请直接点击上方的模板图片')
-}
-
-
-
-// 图片预览相关
-const previewVisible = ref(false)
-const previewImageUrl = ref('')
-
-// 处理图片放大
-const handleImageZoom = (url) => {
-  previewImageUrl.value = url
-  previewVisible.value = true
-}
-
-// 下载模板图片
-const downloadTemplateImage = async (imageUrl) => {
-  try {
-    // 创建一个临时的a标签用于下载
-    const link = document.createElement('a')
-    link.href = imageUrl
-    // 设置文件名,从URL中提取或使用默认名
-    const fileName = imageUrl.split('/').pop() || `template_${Date.now()}.jpg`
-    link.download = fileName
-    // 触发下载
-    document.body.appendChild(link)
-    link.click()
-    // 清理
-    document.body.removeChild(link)
-    ElMessage.success('图片下载成功')
-  } catch (error) {
-    console.error('图片下载失败:', error)
-    ElMessage.error('图片下载失败,请稍后重试')
-  }
-}
-
-// 截断文本函数
-const truncateText = (text, length) => {
-  if (!text) return '无描述'
-  if (text.length <= length) return text
-  return text.substring(0, length) + '...'
-}
-
-
-
-
-// 清空输入框内容
-const clearInput = () => {
-  editFormData.chinese_description = ''
-  ElMessage.success('已清空输入框内容')
-}
-
-// 内容优化(使用轮询机制)
-const optimizeContent = async () => {
-  try {
-    if (!editFormData.chinese_description) {
-      ElMessage.warning('请输入要优化的内容')
-      return
-    }
-    
-    // 重置状态
-    pollStatus.value = 'optimizing'
-    pollCount.value = 0
-    
-    // 第一步:调用生成接口触发生成任务
-    const generateResponse = await GetTxtToTxt({ 
-      status_val: '图生文',
-      prompt: editFormData.chinese_description,
-      model: 'gemini-3-pro-preview',
-      id: editFormData.id,
-      path: editFormData.original_image_url,
-    })
-    
-    if (generateResponse.code === 0) {
-      ElMessage.success('内容优化任务已开始,请稍候...')
-      
-      // 清除之前的定时器
-      if (pollInterval.value) {
-        clearInterval(pollInterval.value)
-      }
-      
-      // 开始轮询查询结果
-      pollInterval.value = setInterval(async () => {
-        pollCount.value++
-        
-        try {
-          // 调用查询接口
-          const checkResponse = await GetProductFind({ id: editFormData.id })
-          
-          if (checkResponse.code === 0 && checkResponse.data) {
-            // 检查是否有content字段
-            if (checkResponse.data.content) {
-              // 停止轮询
-              clearInterval(pollInterval.value)
-              pollInterval.value = null
-              pollStatus.value = 'success'
-              
-              // 更新内容到输入框
-              editFormData.chinese_description = checkResponse.data.content
-              ElMessage.success(`内容优化成功!耗时约${pollCount.value * 10}秒`)
-              return
-            }
-          }
-          
-          // 如果轮询超过30次(5分钟),停止轮询
-          if (pollCount.value >= 30) {
-            clearInterval(pollInterval.value)
-            pollInterval.value = null
-            pollStatus.value = 'error'
-            ElMessage.warning('优化超时,请稍后手动检查结果')
-          }
-          
-        } catch (pollError) {
-          console.error('优化轮询查询失败:', pollError)
-          // 轮询失败继续尝试
-        }
-      }, 5000) // 每5秒轮询一次
-      
-    } else {
-      pollStatus.value = 'error'
-      ElMessage.error('优化任务启动失败: ' + (generateResponse.msg || '未知错误'))
-    }
-    
-  } catch (generateError) {
-    pollStatus.value = 'error'
-    console.error('内容优化失败:', generateError)
-    ElMessage.error('内容优化失败: ' + (generateError.message || '未知错误'))
-  }
-}
-
-// 在script中定义
-const pollStatus = ref(null) // 轮询状态:'polling', 'success', 'error'
-const pollCount = ref(0) // 轮询次数
-const pollInterval = ref(null) // 轮询定时器
-
-// 生成图片(简化版,只做轮询)
-const generateImage = async () => {
-  try {
-    if (!editFormData.chinese_description) {
-      ElMessage.warning('请输入描述内容')
-      return
-    }
-    
-    // 重置状态
-    pollStatus.value = 'polling'
-    pollCount.value = 0
-    
-    // 第一步:调用生成接口触发生成任务
-    const generateResponse = await GetTxtToTxt({ 
-      status_val: '文生图',
-      prompt: editFormData.chinese_description,
-      model: 'gemini-3-pro-image-preview',
-      id: editFormData.id,
-      size: selectedSize.value,
-    })
-    
-    if (generateResponse.code === 0) {
-      ElMessage.success('图片生成任务已开始,请稍候...')
-      
-      // 清除之前的定时器
-      if (pollInterval.value) {
-        clearInterval(pollInterval.value)
-      }
-      
-      // 开始轮询查询结果
-      pollInterval.value = setInterval(async () => {
-        pollCount.value++
-        
-        try {
-          // 调用查询接口
-          const checkResponse = await GetProductFind({ id: editFormData.id })
-          
-          if (checkResponse.code === 0 && checkResponse.data) {
-            // 如果获取到数据且有product_new_img字段
-            if (checkResponse.data.product_new_img) {
-              // 停止轮询
-              clearInterval(pollInterval.value)
-              pollStatus.value = 'success'
-              
-              // 更新新图URL
-              editFormData.new_image_url = checkResponse.data.product_new_img
-              editFormData.img_name = `生成图片_${new Date().getTime()}.jpg`
-              editFormData.product_name = checkResponse.data.product_name
-              editFormData.product_code = checkResponse.data.product_code
-              
-              // 如果有原图字段,也更新一下
-              if (checkResponse.data.product_img) {
-                editFormData.original_image_url = checkResponse.data.product_img
-              }
-              
-              ElMessage.success(`图片生成成功!耗时约${pollCount.value * 10}秒`)
-            }
-          }
-          
-          // 如果轮询超过30次(5分钟),停止轮询
-          if (pollCount.value >= 30) {
-            clearInterval(pollInterval.value)
-            pollStatus.value = 'error'
-            ElMessage.warning('生成超时,请稍后手动检查结果')
-          }
-          
-        } catch (pollError) {
-          console.error('轮询查询失败:', pollError)
-          // 轮询失败继续尝试
-        }
-      }, 5000) // 每10秒轮询一次
-      
-    } else {
-      pollStatus.value = 'error'
-      ElMessage.error('生成任务启动失败: ' + (generateResponse.msg || '未知错误'))
-    }
-    
-  } catch (generateError) {
-    pollStatus.value = 'error'
-    console.error('生成图片失败:', generateError)
-    ElMessage.error('生成图片失败: ' + (generateError.message || '未知错误'))
-  }
-}
-
-// 尺寸相关的数据
-const selectedSize = ref('1:1') // 默认选择1:1
-const customWidth = ref('')
-const customHeight = ref('')
-const sizePresets = {
-  '1:1': { width: 1024, height: 1024 },
-  '4:3': { width: 1024, height: 768 },
-  '3:2': { width: 1024, height: 683 },
-  '2:3': { width: 683, height: 1024 },
-  '9:16': { width: 675, height: 1200 },
-  '16:9': { width: 1200, height: 675 }
-}
-
-// 获取尺寸信息
-const getSizeInfo = (size) => {
-  if (size === 'custom') return '自定义尺寸'
-  const preset = sizePresets[size]
-  return `${preset.width}×${preset.height}`
-}
-
-// 尺寸变化处理
-const handleSizeChange = (value) => {
-  if (value !== 'custom') {
-    // 如果是预设尺寸,可以在这里执行相关操作
-    console.log('选择了尺寸:', value, sizePresets[value])
-    // 可以更新生成图片的参数
-  }
-}
-
-// 验证自定义尺寸
-const validateCustomSize = () => {
-  // 确保输入的是数字
-  if (customWidth.value && !/^\d+$/.test(customWidth.value)) {
-    customWidth.value = customWidth.value.replace(/\D/g, '')
-  }
-  if (customHeight.value && !/^\d+$/.test(customHeight.value)) {
-    customHeight.value = customHeight.value.replace(/\D/g, '')
-  }
-  
-  // 限制最大最小值
-  const maxSize = 4096
-  const minSize = 64
-  
-  if (customWidth.value) {
-    let width = parseInt(customWidth.value)
-    if (width > maxSize) customWidth.value = maxSize.toString()
-    if (width < minSize) customWidth.value = minSize.toString()
-  }
-  
-  if (customHeight.value) {
-    let height = parseInt(customHeight.value)
-    if (height > maxSize) customHeight.value = maxSize.toString()
-    if (height < minSize) customHeight.value = minSize.toString()
-  }
-}
-
-// 获取当前选择的尺寸
-const getCurrentSize = () => {
-  if (selectedSize.value === 'custom') {
-    return {
-      type: 'custom',
-      width: customWidth.value ? parseInt(customWidth.value) : 1024,
-      height: customHeight.value ? parseInt(customHeight.value) : 1024
-    }
-  } else {
-    return {
-      type: selectedSize.value,
-      ...sizePresets[selectedSize.value]
-    }
-  }
-}
-
-
-
-// 可选:添加常用尺寸按钮
-const quickSizes = [
-  { label: '512×512', width: 512, height: 512 },
-  { label: '768×768', width: 768, height: 768 },
-  { label: '1024×1024', width: 1024, height: 1024 },
-  { label: '1920×1080', width: 1920, height: 1080 }
-]
-
-const selectQuickSize = (size) => {
-  selectedSize.value = 'custom'
-  customWidth.value = size.width.toString()
-  customHeight.value = size.height.toString()
-}
-
-
-const downloadImage = async (type) => {
-  console.log('开始下载...')
-  
-  let imagePath = ''
-  let originalFileName = ''
-  
-  if (type === 'original') {
-    imagePath = editFormData.original_image_url
-    originalFileName = editFormData.original_name || ''
-    
-    if (!imagePath) {
-      ElMessage.warning('原图不存在')
-      return
-    }
-    
-  } else if (type === 'new') {
-    imagePath = editFormData.new_image_url
-    originalFileName = editFormData.img_name || ''
-    
-    if (!imagePath) {
-      ElMessage.warning('新图不存在')
-      return
-    }
-  }
-  
-  console.log(`下载${type}图信息:`, { imagePath, originalFileName })
-  
-  // 获取完整URL
-  const fullUrl = formatImageUrl(imagePath)
-  console.log('完整下载URL:', fullUrl)
-  
-  // 生成安全的文件名
-  const safeFileName = generateSafeFilename(originalFileName, imagePath, type)
-  console.log('安全文件名:', safeFileName)
-  
-  // 调用下载
-  await downloadWithBlob(fullUrl, safeFileName)
-}
-
-// 生成安全的文件名(处理中文和特殊字符)
-const generateSafeFilename = (originalName, imagePath, type) => {
-  // 如果有原始文件名,使用它(但需要处理中文)
-  if (originalName && originalName.trim()) {
-    // 如果文件名包含中文,转换为拼音或使用安全字符
-    if (/[\u4e00-\u9fa5]/.test(originalName)) {
-      // 方法1:直接使用中文(现代浏览器支持)
-      // 方法2:转换为拼音(需要拼音库)或使用时间戳
-      return encodeURIComponent(originalName) // 对中文进行编码
-    }
-    return originalName
-  }
-  
-  // 从路径提取文件名
-  const pathParts = imagePath.split('/')
-  const lastPart = pathParts[pathParts.length - 1]
-  
-  if (lastPart && lastPart.includes('.')) {
-    return lastPart
-  }
-  
-  // 默认使用时间戳
-  const timestamp = Date.now()
-  const extension = getFileExtension(imagePath)
-  return `${type}_${timestamp}.${extension}`
-}
-
-// 获取文件扩展名
-const getFileExtension = (filepath) => {
-  if (!filepath) return 'jpg'
-  const match = filepath.match(/\.([a-zA-Z0-9]+)(?:\?.*)?$/)
-  return match ? match[1].toLowerCase() : 'jpg'
-}
-
-// 使用Blob下载(推荐,支持中文文件名)
-const downloadWithBlob = async (url, filename) => {
-  try {
-    console.log('开始Blob下载:', { url, filename })
-    
-    // 添加时间戳避免缓存
-    const urlWithTimestamp = addTimestamp(url)
-    
-    const response = await fetch(urlWithTimestamp, {
-      method: 'GET',
-      headers: {
-        'Cache-Control': 'no-cache'
-      }
-    })
-    
-    if (!response.ok) {
-      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
-    }
-    
-    const blob = await response.blob()
-    
-    // 检查blob类型
-    if (blob.size === 0) {
-      throw new Error('文件内容为空')
-    }
-    
-    // 创建blob URL
-    const blobUrl = window.URL.createObjectURL(blob)
-    
-    // 创建下载链接
-    const link = document.createElement('a')
-    link.href = blobUrl
-    
-    // 处理文件名:现代浏览器支持中文文件名
-    // 如果需要更兼容,可以编码文件名
-    link.download = filename
-    link.style.display = 'none'
-    
-    document.body.appendChild(link)
-    link.click()
-    
-    // 清理
-    setTimeout(() => {
-      window.URL.revokeObjectURL(blobUrl)
-      document.body.removeChild(link)
-    }, 100)
-    
-    ElMessage.success('下载成功!')
-    
-  } catch (error) {
-    console.error('Blob下载失败:', error)
-    
-    // 备用方案1:直接下载
-    try {
-      console.log('尝试直接下载...')
-      directDownload(url, filename)
-    } catch (directError) {
-      console.error('直接下载失败:', directError)
-      
-      // 备用方案2:直接打开
-      try {
-        console.log('尝试直接打开...')
-        window.open(url, '_blank')
-        ElMessage.info('已在新的标签页打开图片')
-      } catch (openError) {
-        console.error('所有方法都失败:', openError)
-        ElMessage.error('下载失败,请检查网络或联系管理员')
-      }
-    }
-  }
-}
-
-const searchKeyword = ref('')
-const handleSearch = async () => {
-  try {
-    const response = await product_template({ search: searchKeyword.value })
-    if (response.code === 0 && response.data) {
-      templateList.value = response.data.map(item => ({
-        id: item.id,
-        template_image_url: item.template_image_url,
-        chinese_description: item.chinese_description || '',
-        english_description: item.english_description || ''
-      }))
-      
-      // 默认选中第一个模板
-      if (templateList.value.length > 0) {
-        selectTemplate(templateList.value[0])
-      }
-    }
-  } catch (error) {
-    console.error('获取模板失败:', error)
-    templateList.value = []
-  }
-}
-
-
-
-
-
-
-
-// 弹窗显示控制
-const AdddialogVisible = ref(false)
-const productFormRef = ref()
-const submitLoading = ref(false)
-
-// 表单数据
-const productForm = reactive({
-  product_name: '',
-  product_code: '',
-  create_name: '',
-  merchant_id: '',
-  product_img: ''
-})
-
-
-
-const onADD = async() => {
-  if (!nodeid.value) {
-    ElMessage.error('请先选择商户')
-    return
-  }
-  AdddialogVisible.value = true
-  const res = await getMerchantId({
-    merchant_code: nodeid.value,
-  })
-  if (res.code === 0) {
-    productForm.merchant_id = res.data
-  }
-}
-
-
-const handleSubmit = async()=>{
-  productForm.product_img = '/uploads/merchant/690377511/6903775111138/oldimg/鲜美番茄酱.png'
-  productForm.create_name = userStore.userInfo.nickName
-  try {
-    const response = await productAdd(productForm)
-    if (response.code === 0) {
-      ElMessage.success('新增成功')
-      handleClose()
-    } else {
-      ElMessage.error(response.message || '新增失败')
-    }
-  } catch (error) {
-    console.error('新增失败:', error)
-    ElMessage.error('新增失败,请联系管理员')
-  } 
-}
-
-// 上传相关配置
-// 环境配置
-const uploadUrl = ref(`${full_url.value}/api/Facility/ImgUpload`)
-
-// 确保上传URL与服务器地址同步
-watch(full_url, (newUrl) => {
-  uploadUrl.value = `${newUrl}/api/Facility/ImgUpload`
-})
-const uploadHeaders = { 
-  'Content-Type': 'multipart/form-data',
-  'Authorization': 'Bearer ' + localStorage.getItem('token')
-}
-
-// 打开弹窗方法(外部调用)
-const openDialog = () => {
-  // 清空表单
-  Object.keys(productForm).forEach(key => {
-    productForm[key] = ''
-  })
-  
-  dialogVisible.value = true
-  
-  // 如果有默认值可以在这里设置
-  // productForm.create_name = '默认创建人'
-}
-
-// 关闭弹窗
-const handleClose = () => {
-  productFormRef.value?.resetFields()
-  submitLoading.value = false
-}
-
-// 自定义上传逻辑
-const customUpload = async (options) => {
-  const { file, onProgress, onSuccess, onError } = options
-  try {
-    const formData = new FormData()
-    formData.append('image', file)
-    const response = await axios.post(uploadUrl.value, formData, {
-      headers: uploadHeaders,
-      onUploadProgress: (progressEvent) => {
-        const percent = Math.round(
-          (progressEvent.loaded * 100) / progressEvent.total
-        )
-        onProgress({ percent }, file)
-      },
-      timeout: 60000 // 60秒超时
-    })
-
-  } catch (error) {
-    
-  } finally {
-    // 关闭加载中
-    if (file.loadingInstance) {
-      file.loadingInstance.close()
-    }
-  }
-}
-
-// 图片上传成功处理
-const handleUploadSuccess = (response, file) => {
-  // 根据实际接口返回的数据结构调整
-  if (response.code === 0) {
-    productForm.product_img = response.data.url
-    ElMessage.success('上传成功')
-  } else {
-    ElMessage.error(response.message || '上传失败')
-  }
-}
-
-// 上传失败处理
-const handleUploadError = (error, file) => {
-  console.error('上传失败:', error)
-  ElMessage.error('图片上传失败,请重试')
-}
-
-// 上传前验证
-const beforeUpload = (file) => {
-  const isImage = file.type.startsWith('image/')
-  const isLt5M = file.size / 1024 / 1024 < 5
-
-  if (!isImage) {
-    ElMessage.error('只能上传图片文件')
-    return false
-  }
-  
-  if (!isLt5M) {
-    ElMessage.error('图片大小不能超过 5MB')
-    return false
-  }
-  
-  return true
-}
-
-// 查看图片
-const handleViewImage = () => {
-  // el-image 的预览功能会自动处理
-}
-
-// 删除图片
-const handleRemoveImage = () => {
-  productForm.product_img = ''
-}
-
-// 暴露方法给父组件
-defineExpose({
-  openDialog
-})
-
-
-// onMounted(() => {
-//   getTableData()
-// })
-</script>
-
-<style scoped>
-  /* 保持原有通用样式 */
-  :deep(.el-table td .cell) {
-    line-height: 22px !important;
-  }
-  /* :deep(.el-dialog__body) {
-    padding: 10px 20px;
-  } */
-  .gva-pagination {
-    margin-top: 10px;
-    text-align: right;
-  }
-  :deep(.el-table__body tr.current-row) > td {
-    background: #ff80ff !important;
-  }
-  .JKWTree-tree {
-    background-color: #fff;
-    padding: 10px;
-    border-right: 1px solid #e4e7ed;
-  }
-  .JKWTree-tree h3 {
-    font-size: 15px;
-    font-weight: 700;
-    margin: 10px 0;
-    padding-bottom: 10px;
-    border-bottom: 1px solid #e4e7ed;
-  }
-  
-  /* 新布局CSS */
-  .image-edit-container {
-    display: flex;
-    gap: 20px;
-    height: calc(100vh - 140px);
-  }
-  
-  .left-column {
-    flex: 2;
-    display: flex;
-    flex-direction: column;
-    gap: 20px;
-  }
-  
-  .image-comparison-section {
-    flex: none; /* 改为不伸缩 */
-    height: 400px; /* 固定高度,和原来一样 */
-    display: flex;
-    flex-direction: column;
-    gap: 15px;
-  }
-  
-  .image-item {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    min-height: 0; /* 防止溢出 */
-  }
-  
-  .image-title {
-    font-weight: bold;
-    margin-bottom: 10px;
-    color: #409eff;
-  }
-  
-  .image-preview {
-    flex: 1;
-    overflow: hidden;
-    display: flex;
-    flex-direction: column;
-    min-height: 0; /* 防止溢出 */
-  }
-  
-  .image-preview img {
-    width: 100%;
-    height: 160px; /* 固定高度,确保不占满屏幕 */
-    object-fit: contain;
-    background: #f5f7fa;
-  }
-  
-  .image-placeholder {
-    height: 160px;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    background: #f5f7fa;
-    color: #909399;
-  }
-  
-  .image-info {
-    padding: 8px;
-    background: #f5f7fa;
-    font-size: 12px;
-    color: #606266;
-    border-top: 1px solid #dcdfe6;
-  }
-  
-  .image-actions {
-    display: flex;
-    justify-content: center;
-    padding: 10px 0;
-  }
-  
-  .edit-section {
-    overflow: hidden; /* 防止溢出 */
-  }
-  
-  .edit-form {
-    height: 100%;
-  }
-  
-  .edit-form :deep(.el-form) {
-    height: 100%;
-  }
-  
-  .edit-form :deep(.el-form-item) {
-    height: 100%;
-    margin-bottom: 0;
-  }
-  
-  .edit-form :deep(.el-textarea__inner) {
-    height: 100% !important;
-    resize: none;
-  }
-  
-  .right-column {
-    display: flex;
-    flex-direction: column;
-    min-height: 0; /* 防止溢出 */
-  }
-  
-  .right-template {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    min-height: 0; /* 防止溢出 */
-  }
-  
-  .template-header {
-    margin-bottom: 15px;
-    padding-bottom: 10px;
-    border-bottom: 1px solid #e4e7ed;
-  }
-  
-  .template-header h4 {
-    margin: 0;
-    font-size: 16px;
-  }
-  
-  .template-list-container {
-    flex: 1;
-    min-height: 0;
-    display: flex;
-    flex-direction: column;
-  }
-  
-  .template-list {
-    flex: 1;
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-    gap: 10px;
-    overflow-y: auto;
-    min-height: 0; /* 防止溢出 */
-    padding: 5px;
-  }
-  
-  .template-item {
-    display: flex;
-    flex-direction: column;
-    width: calc(33.33% - 7px); /* 一行显示3个,减去gap */
-    padding: 10px;
-    border: 2px solid #e4e7ed;
-    border-radius: 6px;
-    cursor: pointer;
-    transition: all 0.3s;
-    background: white;
-    flex-shrink: 0; /* 防止被压缩 */
-    box-sizing: border-box;
-  }
-  
-  .template-item:hover {
-    border-color: #409eff;
-    background: #f5faff;
-  }
-  
-  .template-item.active {
-    border-color: #409eff;
-    background: #ecf5ff;
-    border-width: 2px;
-  }
-  
-  .template-thumbnail {
-    position: relative;
-    width: 100%;
-    height: 120px;
-    margin-bottom: 8px;
-    border-radius: 4px;
-    overflow: hidden;
-    background: #f5f7fa;
-  }
-  
-  .template-thumbnail img {
-    width: 100%;
-    height: 100%;
-    object-fit: cover;
-  }
-  
-  .image-container {
-    position: relative;
-    width: 100%;
-    height: 100%;
-    cursor: pointer;
-  }
-  
-  .zoom-icon {
-    position: absolute;
-    bottom: 8px;
-    right: 8px;
-    width: 28px;
-    height: 28px;
-    background: rgba(0, 0, 0, 0.6);
-    color: white;
-    border-radius: 50%;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    cursor: zoom-in;
-    opacity: 0;
-    transition: opacity 0.3s ease;
-  }
-  
-  .image-container:hover .zoom-icon {
-    opacity: 1;
-  }
-  
-  /* 商品信息项样式 */
-  .product-info-item {
-    margin-bottom: 10px;
-    display: flex;
-    align-items: center;
-  }
-  
-  .product-info-label {
-    font-size: 14px;
-    color: #000000;
-    font-weight: bold;
-    width: 90px;
-    text-align: left;
-  }
-  
-  .product-info-value {
-    font-size: 14px;
-    color: #000000;
-    flex: 1;
-    text-align: left;
-    word-break: break-all;
-  }
-  
-  .template-id {
-    position: absolute;
-    top: 4px;
-    left: 4px;
-    background: rgba(0, 0, 0, 0.7);
-    color: white;
-    padding: 2px 6px;
-    border-radius: 3px;
-    font-size: 11px;
-    font-weight: bold;
-  }
-  
-  .image-preview:hover .image-download-btn {
-    opacity: 1;
-  }
-  
-  .template-desc {
-    flex: 1;
-    font-size: 12px;
-    color: #606266;
-    line-height: 1.4;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    display: -webkit-box;
-    -webkit-line-clamp: 2;
-    -webkit-box-orient: vertical;
-  }
-  
-  .empty-templates {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    color: #909399;
-  }
-  
-  .empty-templates .el-icon {
-    margin-bottom: 10px;
-  }
-  </style>

+ 0 - 80
src/view/performance/QualityAssessment/SemiFinishedRework.vue

@@ -1,80 +0,0 @@
-<template>
-  <div>
-    <h2>3D 预览</h2>
-    <div ref="threeCanvas" style="width: 400px; height: 400px;"></div>
-  </div>
-</template>
-
-<script setup>
-import { onMounted, ref } from 'vue'
-import * as THREE from 'three'
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
-
-const threeCanvas = ref(null)
-
-let scene, camera, renderer, controls
-
-onMounted(() => {
-  initScene()
-  createTexturedBox()
-  animate()
-})
-
-const initScene = () => {
-  scene = new THREE.Scene()
-  scene.background = null // 背景透明
-
-  camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000)
-  camera.position.set(0, 0, 5)
-
-  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
-  renderer.setSize(400, 400)
-  threeCanvas.value.appendChild(renderer.domElement)
-
-  controls = new OrbitControls(camera, renderer.domElement)
-  controls.enableDamping = true
-
-  // 添加环境光
-  const ambientLight = new THREE.AmbientLight(0xffffff, 1.0)
-  scene.add(ambientLight)
-
-  // 添加方向光
-  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0)
-  directionalLight.position.set(5, 5, 5)
-  scene.add(directionalLight)
-}
-
-const createTexturedBox = () => {
-  const geometry = new THREE.BoxGeometry(2.2, 3.5, 0.8)
-  const textureLoader = new THREE.TextureLoader()
-
-  // 确保纹理加载完成后再使用
-  textureLoader.load(
-    '/src/assets/zh.jpg',
-    (texture) => {
-      const material = new THREE.MeshStandardMaterial({ map: texture })
-      const materials = new Array(6).fill(material)
-
-      const box = new THREE.Mesh(geometry, materials)
-      scene.add(box)
-    },
-    undefined,
-    (error) => {
-      console.error('纹理加载失败:', error)
-    }
-  )
-}
-
-const animate = () => {
-  requestAnimationFrame(animate)
-  controls.update()
-  renderer.render(scene, camera)
-}
-</script>
-
-<style scoped>
-h2 {
-  font-size: 18px;
-  margin-bottom: 10px;
-}
-</style>

+ 0 - 551
src/view/performance/QualityAssessment/Shop.vue

@@ -1,551 +0,0 @@
-<template>
-  <div>
-    <!-- <warning-bar title="注:右上角头像下拉可切换角色" /> -->
-    <div class="gva-table-box">
-      <div class="gva-btn-list">
-        <el-button
-          type="primary"
-          icon="plus"
-          @click="addUser"
-        >新增商户</el-button>
-      </div>
-      <el-table
-        :data="tableData"
-        row-key="ID"
-      >
-        <!-- <el-table-column
-          align="left"
-          label="头像"
-          min-width="75"
-        >
-          <template #default="scope">
-            <CustomPic
-              style="margin-top:8px"
-              :pic-src="scope.row.headerImg"
-            />
-          </template>
-        </el-table-column> -->
-        <el-table-column
-          align="left"
-          label="ID"
-          min-width="50"
-          prop="id"
-        />
-        <el-table-column
-          align="left"
-          label="商户名"
-          min-width="150"
-          prop="商户名"
-        />
-        <el-table-column
-          align="left"
-          label="商户编码"
-          min-width="150"
-          prop="商户编码"
-        />
-        <el-table-column
-          align="left"
-          label="联系人"
-          min-width="180"
-          prop="联系人"
-        />
-        <el-table-column
-          align="left"
-          label="联系电话"
-          min-width="180"
-          prop="联系电话"
-        />
-        <el-table-column
-          align="left"
-          label="地址"
-          min-width="180"
-          prop="地址"
-        />
-        <el-table-column
-          align="left"
-          label="邮箱"
-          min-width="180"
-          prop="邮箱"
-        />
-        <el-table-column
-          align="left"
-          label="备注"
-          min-width="180"
-          prop="备注"
-        />
-        <el-table-column
-          align="left"
-          label="创建时间"
-          min-width="180"
-          prop="创建时间"
-        />
-        <el-table-column
-          align="left"
-          label="创建人"
-          min-width="180"
-          prop="创建人"
-        />
-        <el-table-column
-          align="left"
-          label="更新时间"
-          min-width="180"
-          prop="更新时间"
-        />
-        <el-table-column
-          align="left"
-          label="状态"
-          min-width="180"
-          prop="状态"
-        />
-        <!-- <el-table-column
-          align="left"
-          label="启用"
-          min-width="150"
-        >
-          <template #default="scope">
-            <el-switch
-              v-model="scope.row.enable"
-              inline-prompt
-              :active-value="1"
-              :inactive-value="2"
-              @change="()=>{switchEnable(scope.row)}"
-            />
-          </template>
-        </el-table-column> -->
-
-        <el-table-column
-          label="操作"
-          min-width="250"
-          fixed="right"
-        >
-          <template #default="scope">
-            <el-popover
-              v-model:visible="scope.row.visible"
-              placement="top"
-              width="160"
-            >
-              <p>确定要删除此用户吗</p>
-              <div style="text-align: right; margin-top: 8px;">
-                <el-button
-                  type="primary"
-                  link
-                  @click="scope.row.visible = false"
-                >取消</el-button>
-                <el-button
-                  type="primary"
-                  @click="deleteUserFunc(scope.row)"
-                >确定</el-button>
-              </div>
-              <template #reference>
-                <el-button
-                  type="primary"
-                  link
-                  icon="delete"
-                >删除</el-button>
-              </template>
-            </el-popover>
-            <el-button
-              type="primary"
-              link
-              icon="edit"
-              @click="openEdit(scope.row)"
-            >编辑</el-button>
-            <el-button
-              type="primary"
-              link
-              icon="magic-stick"
-              @click="resetPasswordFunc(scope.row)"
-            >重置密码</el-button>
-          </template>
-        </el-table-column>
-
-      </el-table>
-      <div class="gva-pagination">
-        <el-pagination
-          :current-page="page"
-          :page-size="pageSize"
-          :page-sizes="[10, 30, 50, 100]"
-          :total="total"
-          layout="total, sizes, prev, pager, next, jumper"
-          @current-change="handleCurrentChange"
-          @size-change="handleSizeChange"
-        />
-      </div>
-    </div>
-    <el-dialog
-      v-model="addUserDialog"
-      title="商户"
-      :show-close="false"
-      :close-on-press-escape="false"
-      :close-on-click-modal="false"
-    >
-      <div style="height:60vh;overflow:auto;padding:0 12px;">
-        <el-form
-          ref="userForm"
-          :rules="rules"
-          :model="userInfo"
-          label-width="80px"
-        >
-          <el-form-item
-            label="商户名"
-            prop="商户名称"
-          >
-            <el-input v-model="userInfo.merchant_name" />
-          </el-form-item>
-
-          <el-form-item
-            label="商户编码"
-            prop="merchant_code"
-          >
-            <el-input v-model="userInfo.merchant_code" />
-          </el-form-item>
-
-          <!-- <el-form-item
-            v-if="dialogFlag === 'add'"
-            label="密码"
-            prop="password"
-          >
-            <el-input v-model="userInfo.password" />
-          </el-form-item> -->
-
-          <el-form-item
-            label="联系人"
-            prop="联系人"
-          >
-            <el-input v-model="userInfo.contact_person" />
-          </el-form-item>
-          <el-form-item
-            label="联系电话"
-            prop="联系号码"
-          >
-            <el-input v-model="userInfo.contact_phone" />
-          </el-form-item>
-
-          <el-form-item
-            label="地址"
-            prop="address"
-          >
-            <el-input v-model="userInfo.address" />
-          </el-form-item>
-
-          <el-form-item
-            label="邮箱"
-            prop="email"
-          >
-            <el-input v-model="userInfo.email" />
-          </el-form-item>
-
-          <el-form-item
-            label="备注"
-            prop="remark"
-          >
-            <el-input v-model="userInfo.remark" />
-          </el-form-item>
-
-        </el-form>
-
-      </div>
-
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="closeAddUserDialog">取 消</el-button>
-          <el-button
-            type="primary"
-            @click="enterAddUserDialog"
-          >确 定</el-button>
-        </div>
-      </template>
-    </el-dialog>
-    <ChooseImg
-      ref="chooseImg"
-      :target="userInfo"
-      :target-key="`headerImg`"
-    />
-  </div>
-</template>
-
-
-<script setup>
-
-import {
-  getUserList,
-  setUserAuthorities,
-  register,
-  deleteUser
-} from '@/api/user'
-
-import { getAuthorityList } from '@/api/authority'
-
-import { merchantList, merchantDetail,merchantAdd,merchantEdit,merchantDelete, merchantChangeStatus, merchantBatchChangeStatus, LogList } from '@/api/mes/job'
-import CustomPic from '@/components/customPic/index.vue'
-import ChooseImg from '@/components/chooseImg/index.vue'
-import WarningBar from '@/components/warningBar/warningBar.vue'
-import { setUserInfo, resetPassword } from '@/api/user.js'
-import { useUserStore } from '@/pinia/modules/user'
-import { nextTick, ref, watch } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-defineOptions({
-  name: 'User',
-})
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-const path = ref(import.meta.env.VITE_BASE_API + '/')
-// 初始化相关
-const setAuthorityOptions = (AuthorityData, optionsData) => {
-  AuthorityData &&
-        AuthorityData.forEach(item => {
-          if (item.children && item.children.length) {
-            const option = {
-              authorityId: item.authorityId,
-              authorityName: item.authorityName,
-              children: []
-            }
-            setAuthorityOptions(item.children, option.children)
-            optionsData.push(option)
-          } else {
-            const option = {
-              authorityId: item.authorityId,
-              authorityName: item.authorityName
-            }
-            optionsData.push(option)
-          }
-        })
-}
-
-const page = ref(1)
-const total = ref(0)
-const pageSize = ref(10)
-const tableData = ref([])
-// 分页
-const handleSizeChange = (val) => {
-  pageSize.value = val
-  getTableData()
-}
-
-const handleCurrentChange = (val) => {
-  page.value = val
-  getTableData()
-}
-
-// 查询
-const getTableData = async() => {
-  const table = await merchantList({ page: page.value, pageSize: pageSize.value })
-  if (table.code === 0) {
-    tableData.value = table.data.data
-    total.value = table.data.count
-    // page.value = table.data.page
-    // pageSize.value = table.data.pageSize
-  }
-}
-
-watch(() => tableData.value, () => {
-  setAuthorityIds()
-})
-
-const initPage = async() => {
-  getTableData()
-  const res = await getAuthorityList({ page: 1, pageSize: 999 })
-  setOptions(res.data.list)
-}
-
-initPage()
-
-const resetPasswordFunc = (row) => {
-  ElMessageBox.confirm(
-    '是否将此用户密码重置为123456?',
-    '警告',
-    {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'warning',
-    }
-  ).then(async() => {
-    const res = await resetPassword({
-      ID: row.ID,
-    })
-    if (res.code === 0) {
-      ElMessage({
-        type: 'success',
-        message: res.msg,
-      })
-    } else {
-      ElMessage({
-        type: 'error',
-        message: res.msg,
-      })
-    }
-  })
-}
-const setAuthorityIds = () => {
-  tableData.value && tableData.value.forEach((user) => {
-    user.authorityIds = user.authorities && user.authorities.map(i => {
-      return i.authorityId
-    })
-  })
-}
-
-const chooseImg = ref(null)
-const openHeaderChange = () => {
-  chooseImg.value.open()
-}
-
-const authOptions = ref([])
-const setOptions = (authData) => {
-  authOptions.value = []
-  setAuthorityOptions(authData, authOptions.value)
-}
-
-const deleteUserFunc = async(row) => {
-  const res = await deleteUser({ id: row.ID })
-  if (res.code === 0) {
-    ElMessage.success('删除成功')
-    row.visible = false
-    await getTableData()
-  }
-}
-
-// 弹窗相关
-const userInfo = ref({})
-
-const rules = ref({
-  userName: [
-    { required: true, message: '请输入用户名', trigger: 'blur' },
-    { min: 3, message: '最低3位字符', trigger: 'blur' }
-  ],
-  password: [
-    { required: true, message: '请输入用户密码', trigger: 'blur' },
-    { min: 0, message: '最低0位字符', trigger: 'blur' }
-  ],
-  nickName: [
-    { required: true, message: '请输入用户昵称', trigger: 'blur' }
-  ],
-  phone: [
-    { pattern: /^1([38][0-9]|4[014-9]|[59][0-35-9]|6[2567]|7[0-8])\d{8}$/, message: '请输入合法手机号', trigger: 'blur' },
-  ],
-  email: [
-    { pattern: /^([0-9A-Za-z\-_.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/g, message: '请输入正确的邮箱', trigger: 'blur' },
-  ],
-  authorityId: [
-    { required: true, message: '请选择用户角色', trigger: 'blur' }
-  ]
-})
-const userForm = ref(null)
-const enterAddUserDialog = async() => {
-  userInfo.value.createName = userStore.userInfo.nickName
-  userInfo.value.createCode = userStore.userInfo.userName
-  userForm.value.validate(async valid => {
-    if (valid) {
-      const req = {
-        ...userInfo.value
-      }
-      if (dialogFlag.value === 'add') {
-        console.log('创建用户', req)
-
-        const res = await merchantAdd(req)
-        if (res.code === 0) {
-          ElMessage({ type: 'success', message: '创建成功' })
-          await getTableData()
-          closeAddUserDialog()
-        }
-      }
-      if (dialogFlag.value === 'edit') {
-        console.log('编辑用户', req)
-        return;
-        const res = await merchantEdit(req)
-        if (res.code === 0) {
-          ElMessage({ type: 'success', message: '编辑成功' })
-          await getTableData()
-          closeAddUserDialog()
-        }
-      }
-    }
-  })
-}
-
-const addUserDialog = ref(false)
-const closeAddUserDialog = () => {
-  userForm.value.resetFields()
-  addUserDialog.value = false
-}
-
-const dialogFlag = ref('add')
-
-const addUser = () => {
-  dialogFlag.value = 'add'
-  addUserDialog.value = true
-}
-
-// const tempAuth = {}
-// const changeAuthority = async(row, flag, removeAuth) => {
-//   if (flag) {
-//     if (!removeAuth) {
-//       tempAuth[row.ID] = [...row.authorityIds]
-//     }
-//     return
-//   }
-//   await nextTick()
-//   const res = await setUserAuthorities({
-//     ID: row.ID,
-//     authorityIds: row.authorityIds
-//   })
-//   if (res.code === 0) {
-//     ElMessage({ type: 'success', message: '角色设置成功' })
-//   } else {
-//     if (!removeAuth) {
-//       row.authorityIds = [...tempAuth[row.ID]]
-//       delete tempAuth[row.ID]
-//     } else {
-//       row.authorityIds = [removeAuth, ...row.authorityIds]
-//     }
-//   }
-// }
-
-const openEdit = async(row) => {
-  dialogFlag.value = 'edit'
-  console.log('编辑用户', row)
-  const res = await merchantDetail({ id: row.id })
-  if (res.code === 0) {
-    // 将中文字段映射到英文变量名
-    userInfo.value = {
-      merchant_name: res.data.商户名称,
-      merchant_code: res.data.商户编码,
-      contact_person: res.data.联系人,
-      contact_phone: res.data.联系号码,
-      address: res.data.地址,
-      email: res.data.邮箱,
-      remark: res.data.备注,
-      id: res.data.id
-    }
-    console.log('编辑用户', userInfo.value)
-  }
-  addUserDialog.value = true
-}
-
-// const switchEnable = async(row) => {
-//   userInfo.value = JSON.parse(JSON.stringify(row))
-//   await nextTick()
-//   const req = {
-//     ...userInfo.value
-//   }
-//   const res = await setUserInfo(req)
-//   if (res.code === 0) {
-//     ElMessage({ type: 'success', message: `${req.enable === 2 ? '禁用' : '启用'}成功` })
-//     await getTableData()
-//     userInfo.value.headerImg = ''
-//     userInfo.value.authorityIds = []
-//   }
-// }
-
-</script>
-
-<style lang="scss">
-  .header-img-box {
-    @apply w-52 h-52 border border-solid border-gray-300 rounded-xl flex justify-center items-center cursor-pointer;
- }
-</style>

+ 0 - 261
src/view/performance/QualityAssessment/Template.vue

@@ -1,261 +0,0 @@
-<template>
-  <div class="page-container">
-    <el-descriptions :column="2" border class="queue-status-box">
-      <!-- 模版选择器 -->
-      <el-descriptions-item label="选择" :span="2">
-        <el-row :gutter="10" align="middle">
-			<el-col :span="6">
-			  <el-select v-model="path_selectedId" placeholder="请选择文件夹" @change="path_loadTemplateDetail">
-			    <el-option
-			      v-for="tpl in path_templateList"
-			      :key="tpl.path"
-			      :label="tpl.path"
-			      :value="tpl.path"
-			    />
-			  </el-select>
-			</el-col>
-			
-          <el-col :span="6">
-            <el-select v-model="selectedId" placeholder="请选择模版" @change="loadTemplateDetail">
-              <el-option
-                v-for="tpl in templateList"
-                :key="tpl.id"
-                :label="'模版 ' + tpl.id + (tpl.ids === 1 ? '(使用中)' : '')"
-                :value="tpl.id"
-              />
-            </el-select>
-          </el-col>
-          <el-col :span="3">
-            <el-button type="primary" @click="createNewTemplate">新增模版</el-button>
-          </el-col>
-          <el-col :span="4">
-            <el-button type="success" @click="onsetActive" :disabled="selectedId === usedId">设为当前使用模版</el-button>
-          </el-col>
-        </el-row>
-      </el-descriptions-item>
-
-      <!-- 图生文模版输入区域 -->
-      <el-descriptions-item label="图生文模版" :span="2">
-        <textarea
-          v-model="textareaContent"
-          placeholder="请输入提示词"
-          rows="10"
-          class="custom-textarea"
-          @keydown.tab.prevent="insertTab"
-        ></textarea>
-      </el-descriptions-item>
-
-      <!-- 文生文模版输入区域 -->
-      <el-descriptions-item label="文生文模版" :span="2">
-        <textarea
-          v-model="english_content"
-          placeholder="请输入提示词"
-          rows="10"
-          class="custom-textarea"
-          @keydown.tab.prevent="insertTab"
-        ></textarea>
-      </el-descriptions-item>
-
-      <!-- 出图尺寸输入区域 -->
-      <el-descriptions-item label="出图尺寸" :span="2">
-        <el-row :gutter="10" align="middle">
-          <el-col :span="4">
-            <el-input
-              v-model="width"
-              placeholder="宽度"
-              size="small"
-              type="number"
-              min="1"
-            />
-          </el-col>
-          <el-col :span="1" style="text-align: center;">×</el-col>
-          <el-col :span="4">
-            <el-input
-              v-model="height"
-              placeholder="高度"
-              size="small"
-              type="number"
-              min="1"
-            />
-          </el-col>
-        </el-row>
-      </el-descriptions-item>
-
-      <!-- 操作按钮 -->
-      <el-descriptions-item label="操作" :span="2">
-        <el-button type="primary" @click="saveTemplate" :loading="mb_isLoading">
-          保存模版
-        </el-button>
-      </el-descriptions-item>
-    </el-descriptions>
-  </div>
-</template>
-
-<script setup>
-import { ref, nextTick, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
-import {
-  TemplateList,
-  Template,
-  updatetemplate,getPreviewFolders,
-  setActiveTemplate
-} from '@/api/mes/job'
-
-// 模版状态数据
-const templateList = ref([])
-const selectedId = ref(null)
-const usedId = ref(null)
-
-const path_templateList = ref([])
-const path_selectedId = ref(null)
-
-// 模版内容
-const textareaContent = ref('')
-const english_content = ref('')
-const height = ref('')
-const width = ref('')
-const mb_isLoading = ref(false)
-
-//进入页面自动加载
-const loadTemplateList = async () => {
-	//文件夹列表
-	const getPreviewFolders_list = await getPreviewFolders()
-	path_templateList.value = getPreviewFolders_list.data.folders || []
-}
-
-//点击选择文件夹路径获取-》模版列表
-const path_loadTemplateDetail = async (path) => {
-	if(path){
-		const res = await TemplateList({path:path})
-		templateList.value = res.data.list || []
-		usedId.value = res.data.usedId
-		selectedId.value = usedId.value
-		await loadTemplateDetail(selectedId.value)
-	}
-}
-
-//获取模版列表后将默认模版显示
-const loadTemplateDetail = async (id) => {
-	if(id){
-		const res = await Template({ id })
-		textareaContent.value = res.data.content || ''
-		english_content.value = res.data.english_content || ''
-		width.value = res.data.width || ''
-		height.value = res.data.height || ''
-	}
-}
-
-// 创建新模版(清空内容)
-const createNewTemplate = () => {
-  selectedId.value = null
-  textareaContent.value = '请输入新增模版内容'
-  english_content.value = '请输入新增模版内容'
-  width.value = '1303'
-  height.value = '1024'
-}
-
-// 保存模版(新增或更新)
-const saveTemplate = async () => {
-	
-  if (!textareaContent.value || !width.value || !height.value) {
-    ElMessage.warning('请填写完整模版内容和尺寸')
-    return
-  }
-
-  mb_isLoading.value = true
-  const res = await updatetemplate({
-    id: selectedId.value,
-	path:path_selectedId.value,
-    textareaContent: textareaContent.value,
-    english_content: english_content.value,
-    width: width.value,
-    height: height.value
-  })
-  mb_isLoading.value = false
-
-  if (res.code === 0) {
-    ElMessage.success('模版保存成功')
-    loadTemplateList()
-  } else {
-    ElMessage.error('保存失败')
-  }
-}
-
-// 设为当前模版
-const onsetActive = async () => {
-  const res = await setActiveTemplate({ id: selectedId.value,path:path_selectedId.value })
-  if (res.code === 0) {
-    ElMessage.success('已设为当前使用模版')
-    await loadTemplateList()
-  } else {
-    ElMessage.error('设置失败')
-  }
-}
-
-// tab 缩进逻辑
-const insertTab = (event) => {
-  const textarea = event.target
-  const start = textarea.selectionStart
-  const end = textarea.selectionEnd
-  const isShift = event.shiftKey
-  const value = textareaContent.value
-  const before = value.substring(0, start)
-  const selected = value.substring(start, end)
-  const after = value.substring(end)
-
-  if (selected.includes('\n')) {
-    const lines = selected.split('\n')
-    const newText = lines
-      .map(line => (isShift
-        ? (line.startsWith('\t') ? line.substring(1) : line.startsWith('    ') ? line.substring(4) : line)
-        : '\t' + line))
-      .join('\n')
-    textareaContent.value = before + newText + after
-    const offset = newText.length - selected.length
-    nextTick(() => {
-      textarea.selectionStart = start
-      textarea.selectionEnd = end + offset
-    })
-  } else {
-    if (isShift && value.substring(start - 1, start) === '\t') {
-      textareaContent.value = before.slice(0, -1) + selected + after
-      nextTick(() => {
-        textarea.selectionStart = textarea.selectionEnd = start - 1
-      })
-    } else {
-      textareaContent.value = before + '\t' + selected + after
-      nextTick(() => {
-        textarea.selectionStart = textarea.selectionEnd = start + 1
-      })
-    }
-  }
-
-  event.preventDefault()
-}
-
-// 初始化加载
-onMounted(loadTemplateList)
-</script>
-
-<style scoped>
-.page-container {
-  padding: 20px;
-  background: #fff;
-}
-
-.custom-textarea {
-  width: 100%;
-  font-family: monospace;
-  font-size: 14px;
-  padding: 10px;
-  line-height: 1.6;
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  resize: vertical;
-  box-sizing: border-box;
-}
-
-:deep(.el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell) {
-  width: 100px;
-}
-</style>

+ 0 - 610
src/view/performance/QualityAssessment/TemplateManage.vue

@@ -1,610 +0,0 @@
-<template>
-  <div class="quality-assessment-template">
-    <el-card shadow="hover">
-      <template #header>
-        <div class="card-header">
-          <span>提示词模板管理</span>
-        </div>
-      </template>
-      
-      <!-- 左右两栏布局容器 -->
-      <div class="content-container">
-        <!-- 左侧表单部分 -->
-        <div class="form-section">
-             <!-- 尺寸选择 -->
-            <el-form-item label="选择尺寸"  label-width="120px"  prop="size">
-              <el-select v-model="formData.size" placeholder="请选择DALL-E 3支持的尺寸">
-                <el-option label="1024x1024 (正方形)" value="1024x1024" />
-                <el-option label="1792x1024 (宽屏)" value="1792x1024" />
-                <el-option label="1024x1792 (竖屏)" value="1024x1792" />
-              </el-select>
-            </el-form-item>
-
-          <el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
-            <!-- 场景分类选择 -->
-            <el-form-item label="场景分类" prop="sceneCategory">
-              <el-select v-model="formData.sceneCategory" placeholder="请选择场景分类" @change="onCategoryChange">
-                <el-option v-for="category in sceneCategories" :key="category.value" :label="category.label" :value="category.value" />
-              </el-select>
-            </el-form-item>
-            
-            <!-- 模板选择 -->
-            <el-form-item label="选择模板" prop="template">
-              <el-select v-model="formData.template" placeholder="请选择模板" @change="onTemplateChange">
-                <el-option 
-                  v-for="template in filteredTemplates" 
-                  :key="template.value" 
-                  :label="template.label" 
-                  :value="template.value"
-                >
-                  <template #default>
-                    <div class="template-option">
-                      <div class="template-title">{{ template.label }}</div>
-                      <div class="template-desc">{{ template.description }}</div>
-                    </div>
-                  </template>
-                </el-option>
-              </el-select>
-            </el-form-item>
-            
-            <!-- 通用产品字段 -->
-            <el-form-item v-if="currentTemplate?.fields?.productName" label="产品名称" prop="productName">
-              <el-input v-model="formData.productName" placeholder="请输入产品名称" />
-            </el-form-item>
-            
-            <!-- 通用主题字段 -->
-            <el-form-item v-if="currentTemplate?.fields?.theme" label="主题" prop="theme">
-              <el-input v-model="formData.theme" placeholder="请输入主题" />
-            </el-form-item>
-            
-            <!-- 活动相关字段 -->
-            <el-form-item v-if="currentTemplate?.fields?.eventName" label="活动名称" prop="eventName">
-              <el-input v-model="formData.eventName" placeholder="请输入活动名称" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.eventSlogan" label="宣传语" prop="eventSlogan">
-              <el-input v-model="formData.eventSlogan" type="textarea" :rows="2" placeholder="请输入宣传语" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.eventContent" label="活动内容" prop="eventContent">
-              <el-input v-model="formData.eventContent" type="textarea" :rows="2" placeholder="请输入活动内容" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.eventDesc" label="活动描述" prop="eventDesc">
-              <el-input v-model="formData.eventDesc" type="textarea" :rows="2" placeholder="请输入活动描述" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.eventTime" label="活动时间" prop="eventTime">
-              <el-input v-model="formData.eventTime" placeholder="请输入活动时间,如:7月30日-8月1日 16:00-22:00" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.eventLocation" label="活动地点" prop="eventLocation">
-              <el-input v-model="formData.eventLocation" placeholder="请输入活动地点,如:上海市静安区南京西路1266号恒隆广场" />
-            </el-form-item>
-            
-            <!-- 食品相关字段 -->
-            <el-form-item v-if="currentTemplate?.fields?.foodType" label="食品类型" prop="foodType">
-              <el-input v-model="formData.foodType" placeholder="请输入食品类型,如:鱼干" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.foodFlavor" label="食品口味" prop="foodFlavor">
-              <el-input v-model="formData.foodFlavor" placeholder="请输入食品口味,如:原味、香辣" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.foodFeatures" label="产品特点" prop="foodFeatures">
-              <el-input v-model="formData.foodFeatures" type="textarea" :rows="2" placeholder="请输入产品特点,如:鲜香可口,营养丰富" />
-            </el-form-item>
-            
-            <!-- 电商相关字段 -->
-            <el-form-item v-if="currentTemplate?.fields?.productPrice" label="产品价格" prop="productPrice">
-              <el-input v-model="formData.productPrice" placeholder="请输入产品价格,如:99元" />
-            </el-form-item>
-            
-            <el-form-item v-if="currentTemplate?.fields?.productBenefits" label="产品优势" prop="productBenefits">
-              <el-input v-model="formData.productBenefits" type="textarea" :rows="3" placeholder="请输入产品优势,如:有机材料、环保包装" />
-            </el-form-item>
-            
-            <!-- 提示词输入 -->
-            <el-form-item label="提示词" prop="prompt">
-              <div class="prompt-hint" v-if="currentTemplate">
-                <el-alert
-                  :title="`${currentTemplate.label}提示:${currentTemplate.hint}`"
-                  type="info"
-                  :closable="false"
-                  show-icon
-                  style="margin-bottom: 10px;"
-                />
-              </div>
-              <el-input
-                v-model="formData.prompt"
-                type="textarea"
-                :rows="10"
-                placeholder="请输入提示词,或使用模板自动生成"
-              />
-            </el-form-item>
-            
-            <!-- 按钮区域 -->
-            <el-form-item>
-              <el-button type="primary" @click="expandText">优化提示词</el-button>
-              <el-button type="success" @click="generateImage">立即生成图片</el-button>
-              <el-button @click="resetForm">重置</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-        
-        <!-- 右侧结果部分 -->
-        <div class="result-section">
-          <h3>生图结果</h3>
-          <!-- 加载状态显示 -->
-          <div v-if="loading" class="image-result">
-            <div style="text-align: center; padding: 50px 0;">
-              <el-icon class="is-loading" style="font-size: 32px; margin-bottom: 10px;">
-                <Loading />
-              </el-icon>
-              <p style="margin-bottom: 20px;">{{ progressText }}</p>
-              <el-progress :percentage="progress" :stroke-width="8" style="max-width: 300px; margin: 0 auto;"></el-progress>
-              <p style="margin-top: 10px;">{{ progress }}%</p>
-            </div>
-          </div>
-          <!-- 图片结果显示 -->
-          <div v-else-if="generatedImage" class="image-result">
-            <el-image
-              :src="generatedImage"
-              fit="contain"
-              style="max-width: 100%; max-height: 500px"
-            >
-              <template #error>
-                <div class="image-error">加载失败</div>
-              </template>
-            </el-image>
-          </div>
-          <!-- 空状态显示 -->
-          <div v-else class="no-result">
-            <el-empty description="点击生成图片按钮查看结果"></el-empty>
-          </div>
-        </div>
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script setup>
-import {ref,reactive,computed,watch} from 'vue'
-import {ElMessage,ElMessageBox,ElEmpty,ElImage} from 'element-plus'
-import {Loading} from '@element-plus/icons-vue'
-import {useUserStore} from '@/pinia/modules/user'
-import {GetTxtToTxt,imageToText} from '@/api/mes/job'
-
-defineOptions({name: 'TemplateManage'})
-
-// 场景分类
-const sceneCategories = [
-  { label: '产品宣传', value: 'productPromotion' },
-  { label: '活动海报', value: 'eventPoster' },
-  { label: '食品行业', value: 'foodIndustry' },
-  { label: '其他场景', value: 'other' }
-]
-
-// 模板内容定义(按场景分类)
-const templates = [
-  // 产品宣传类
-  {
-    value: 'productPromo',
-    label: '通用产品宣传',
-    category: 'productPromotion',
-    description: '适用于各类产品的宣传海报,突出产品特点和优势',
-    hint: '请填写产品名称和主题,系统将自动生成专业的产品宣传提示词',
-    fields: {
-      productName: true,
-      theme: true
-    },
-    generatePrompt: (formData) => {
-      return `创建一张精美的产品宣传图片,产品名称:${formData.productName},主题:${formData.theme || '宣传'}。尺寸${formData.size}。包含产品图片、产品名称、核心特点。设计风格现代简洁,色彩搭配协调,突出产品优势,整体视觉效果吸引人。`
-    }
-  },
-  {
-    value: 'techProductPromo',
-    label: '科技产品宣传',
-    category: 'productPromotion',
-    description: '专为科技类产品设计,突出科技感和创新特点',
-    hint: '适合电子产品、软件、数码设备等科技产品的宣传',
-    fields: {
-      productName: true,
-      theme: true,
-      productBenefits: true
-    },
-    generatePrompt: (formData) => {
-      return `设计一张科技感十足的产品宣传海报,产品名称:${formData.productName},主题:${formData.theme || '创新科技'}。尺寸${formData.size}。突出产品的科技感和创新特点,使用现代简约风格,蓝色调为主,包含产品图片、名称和核心优势:${formData.productBenefits || '高性能、智能化'}。整体设计具有未来感和视觉冲击力。`
-    }
-  },
-  
-  // 活动海报类
-  {
-    value: 'eventPoster',
-    label: '通用活动海报',
-    category: 'eventPoster',
-    description: '适用于各类活动的宣传海报,包含活动关键信息',
-    hint: '请填写活动名称、宣传语、时间地点等信息',
-    fields: {
-      eventName: true,
-      eventSlogan: true,
-      eventTime: true,
-      eventLocation: true,
-      eventContent: true,
-      eventDesc: true
-    },
-    generatePrompt: (formData) => {
-      return `设计一张精美的活动宣传海报,活动名称:${formData.eventName},宣传语:${formData.eventSlogan || '精彩不容错过'}。尺寸${formData.size}。活动时间:${formData.eventTime || '待定'},地点:${formData.eventLocation || '待定'}。活动内容:${formData.eventContent || '丰富多样的活动内容'},活动描述:${formData.eventDesc || '期待您的参与'}。使用鲜明的色彩,吸引人的排版,突出活动主题,整体设计具有视觉冲击力。`
-    }
-  },
-  {
-    value: 'festivalPoster',
-    label: '节日活动海报',
-    category: 'eventPoster',
-    description: '专为节日庆典活动设计的宣传海报',
-    hint: '适合节日派对、庆典活动、主题晚会等',
-    fields: {
-      eventName: true,
-      eventSlogan: true,
-      eventTime: true,
-      eventLocation: true,
-      theme: true
-    },
-    generatePrompt: (formData) => {
-      return `设计一张充满节日氛围的活动宣传海报,活动名称:${formData.eventName},节日主题:${formData.theme || '节日庆典'}。尺寸${formData.size}。宣传语:${formData.eventSlogan || '欢度佳节'}。活动时间:${formData.eventTime || '待定'},地点:${formData.eventLocation || '待定'}。使用节日相关的色彩和元素,营造欢快喜庆的氛围,整体设计具有强烈的视觉吸引力。`
-    }
-  },
-  {
-    value: 'foodGeneral',
-    label: '通用食品宣传',
-    category: 'foodIndustry',
-    description: '适用于各类食品的宣传海报,突出食品的美味和品质',
-    hint: '适合零食、饮料、餐饮等各类食品的宣传',
-    fields: {
-      productName: true,
-      foodType: true,
-      foodFlavor: true,
-      foodFeatures: true,
-      productPrice: true
-    },
-    generatePrompt: (formData) => {
-      return `设计一张诱人的${formData.foodType || '食品'}产品宣传海报,产品名称:${formData.productName},口味:${formData.foodFlavor || '美味'}。尺寸${formData.size}。特点:${formData.foodFeatures || '新鲜美味,品质保证'}。价格:${formData.productPrice || '面议'}。使用高饱和度的色彩,突出食品的质感和美味,包含产品图片、名称、口味、特点和价格。整体设计具有食欲感和视觉吸引力。`
-    }
-  },
-  
-  
-  // 其他场景
-  {
-    value: 'customTemplate',
-    label: '自定义模板',
-    category: 'other',
-    description: '自由发挥,自定义提示词生成图片',
-    hint: '可根据需求自由填写提示词,不受固定模板限制',
-    fields: {},
-    generatePrompt: (formData) => {
-      return formData.prompt || '请输入自定义提示词'
-    }
-  }
-]
-
-// 表单数据
-const formData = reactive({
-  sceneCategory: '',
-  size: '1024x1024',
-  template: '',
-  prompt: '',
-  // 通用字段
-  productName: '',
-  theme: '',
-  // 活动相关字段
-  eventName: '',
-  eventSlogan: '',
-  eventContent: '',
-  eventDesc: '',
-  eventTime: '',
-  eventLocation: '',
-  // 食品相关字段
-  foodType: '',
-  foodFlavor: '',
-  foodFeatures: '',
-  // 电商相关字段
-  productPrice: '',
-  productBenefits: ''
-})
-
-// 监听表单字段变化,自动更新提示词
-watch(() => ({
-  productName: formData.productName,
-  theme: formData.theme,
-  eventName: formData.eventName,
-  eventSlogan: formData.eventSlogan,
-  eventContent: formData.eventContent,
-  eventDesc: formData.eventDesc,
-  eventTime: formData.eventTime,
-  eventLocation: formData.eventLocation,
-  foodType: formData.foodType,
-  foodFlavor: formData.foodFlavor,
-  foodFeatures: formData.foodFeatures,
-  productPrice: formData.productPrice,
-  productBenefits: formData.productBenefits,
-  template: formData.template
-}), () => {
-  // 只有当选择了模板且当前模板不是自定义模板时,才自动更新提示词
-  if (currentTemplate.value && currentTemplate.value.value !== 'customTemplate') {
-    formData.prompt = currentTemplate.value.generatePrompt(formData)
-  }
-}, { deep: true })
-
-// 表单验证规则
-const rules = {
-  sceneCategory: [
-    { required: true, message: '请选择场景分类', trigger: 'change' }
-  ],
-  size: [
-    { required: true, message: '请选择尺寸', trigger: 'change' }
-  ],
-  template: [
-    { required: true, message: '请选择模板', trigger: 'change' }
-  ],
-  prompt: [
-    { required: true, message: '请输入提示词', trigger: 'blur' }
-  ]
-}
-
-// 表单引用
-const formRef = ref(null)
-
-// 生成的图片
-const generatedImage = ref('')
-// 加载状态变量
-const loading = ref(false)
-// 进度状态变量
-const progress = ref(0)
-const progressText = ref('准备生成')
-
-// 根据场景分类过滤模板
-const filteredTemplates = computed(() => {
-  if (!formData.sceneCategory) return templates
-  return templates.filter(template => template.category === formData.sceneCategory)
-})
-
-// 当前选中的模板
-const currentTemplate = computed(() => {
-  return templates.find(template => template.value === formData.template)
-})
-
-// 场景分类变化事件处理
-const onCategoryChange = () => {
-  // 重置模板选择
-  formData.template = ''
-  formData.prompt = ''
-}
-
-// 模板变化事件处理
-const onTemplateChange = () => {
-  // 根据选择的模板生成默认提示词
-  if (currentTemplate.value) {
-    formData.prompt = currentTemplate.value.generatePrompt(formData)
-  }
-}
-
-
-
-// 扩写文本方法
-const expandText = async () => {
-  formRef.value.validate(async (valid) => {
-    if (valid) {
-      // 调用扩写文本接口
-      ElMessage({ message: '正在扩写文本...', type: 'info' })
-      
-      // 准备请求参数
-      const params = {
-        status_val: '文生文',
-        prompt: formData.prompt,
-        size: formData.size,
-        productName: formData.productName,
-        theme: formData.theme,
-        template: formData.template,
-        sceneCategory: formData.sceneCategory
-      }
-      
-      try {
-        // 使用async/await调用API接口
-        const res = await GetTxtToTxt(params)
-        formData.prompt = res.data.content
-        ElMessage({ message: '文本扩写成功', type: 'success' })
-      } catch (error) {
-        console.error('扩写文本失败:', error)
-        ElMessage({ message: '扩写失败,请稍后重试', type: 'error' })
-      }
-    }
-  })
-}
-
-// 生成图片方法
-const generateImage = async () => {
-  formRef.value.validate(async (valid) => {
-    if (valid) {
-      // 解析尺寸
-      const [width, height] = formData.size.split('x').map(Number)
-      
-      // 设置加载状态为true
-      loading.value = true
-      // 初始化进度
-      progress.value = 0
-      progressText.value = '正在准备...'
-      
-      // 模拟进度更新(由于实际API可能不支持实时进度返回,使用定时器模拟)
-      const progressInterval = setInterval(() => {
-        if (progress.value < 90) { // 最多到90%,留10%给最终完成
-          progress.value += Math.floor(Math.random() * 10) + 5 // 随机增加5-15%
-          if (progress.value > 90) progress.value = 90
-          
-          // 更新进度文本
-          if (progress.value < 30) {
-            progressText.value = '正在处理请求...'
-          } else if (progress.value < 60) {
-            progressText.value = '正在生成图片内容...'
-          } else {
-            progressText.value = '正在优化图片细节...'
-          }
-        }
-      }, 500) // 每500毫秒更新一次进度
-      
-      // 准备请求参数
-      const params = {
-        status_val: '文生图',
-        prompt: formData.prompt,
-        width: width,
-        height: height,
-        size: formData.size,
-        productName: formData.productName,
-        theme: formData.theme,
-        template: formData.template,
-        sceneCategory: formData.sceneCategory
-      }
-      
-      try {
-        // 使用async/await调用API接口
-        const res = await GetTxtToTxt(params)
-        
-        // 处理API返回结果
-        if (res && res.data && res.data.code === 200) {
-          // 完成进度
-          progress.value = 100
-          progressText.value = '已完成'
-          
-          if (res.data.data.type === 'image') {
-            generatedImage.value = res.data.data.image_path
-            ElMessage({ message: '图片生成成功', type: 'success' })
-          } else {
-            generatedImage.value = res.data.data.content || res.data.data.image_data || res.data.data
-            ElMessage({ message: '图片生成成功', type: 'success' })
-          }
-        } else {
-          const errorMsg = res.data?.message || '生成失败,返回数据格式不正确'
-          ElMessage({ message: errorMsg, type: 'error' })
-        }
-      } catch (error) {
-        console.error('生成图片失败:', error)
-        ElMessage({ message: '生成失败,请稍后重试', type: 'error' })
-        progress.value = 0
-        progressText.value = '生成失败'
-      } finally {
-        // 无论成功或失败,都关闭加载状态和进度定时器
-        loading.value = false
-        clearInterval(progressInterval)
-      }
-    }
-  })
-}
-
-// 重置表单
-const resetForm = () => {
-  formRef.value.resetFields()
-  generatedImage.value = ''
-}
-</script>
-
-<style scoped>
-.quality-assessment-template {
-  padding: 20px;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-/* 左右两栏布局 */
-.content-container {
-  display: flex;
-  gap: 30px;
-  margin-top: 20px;
-}
-
-.form-section {
-  flex: 1;
-  max-width: 50%;
-}
-
-.result-section {
-  flex: 1;
-  max-width: 50%;
-  min-height: 400px;
-  border-left: 1px solid #ebeef5;
-  padding-left: 30px;
-}
-
-.result-section h3 {
-  margin-top: 0;
-  margin-bottom: 20px;
-  font-size: 18px;
-  font-weight: 600;
-  color: #303133;
-}
-
-/* 风格选择下拉框已替代卡片式选择器,相关样式已清理 */
-
-.image-result {
-  margin-top: 20px;
-  padding: 20px;
-  background-color: #fafafa;
-  border-radius: 8px;
-}
-
-.no-result {
-  height: 400px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background-color: #fafafa;
-  border-radius: 8px;
-}
-
-.image-error {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 100%;
-  height: 200px;
-  background-color: #f5f7fa;
-  color: #909399;
-}
-
-/* 模板选项样式 */
-.template-option {
-  padding: 10px;
-}
-
-.template-title {
-  font-weight: 600;
-  margin-bottom: 5px;
-}
-
-.template-desc {
-  font-size: 12px;
-  color: #909399;
-  line-height: 1.4;
-}
-
-/* 响应式设计 */
-@media (max-width: 768px) {
-  .content-container {
-    flex-direction: column;
-  }
-  
-  .form-section,
-  .result-section {
-    max-width: 100%;
-  }
-  
-  .result-section {
-    border-left: none;
-    border-top: 1px solid #ebeef5;
-    padding-left: 0;
-    padding-top: 30px;
-  }
-}
-</style>

+ 0 - 565
src/view/performance/QualityAssessment/TestTemplate.vue

@@ -1,565 +0,0 @@
-<template>
-  <div>
-    <!-- 搜索区域 -->
-    <el-form inline>
-      <el-form-item>
-        <el-input v-model="searchInfo" placeholder="搜索关键字" clearable style="width: 300px;" />
-        
-        <!-- 文件夹筛选下拉菜单 -->
-        <el-dropdown @command="handleFolderCommand" style="margin-right: 10px;">
-          <el-button  icon="search" title="文件夹筛选">
-            {{ selectedFolder || '文件夹筛选' }}<el-icon class="el-icon--right"><arrow-down /></el-icon>
-          </el-button>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item command="">全部文件夹</el-dropdown-item>
-              <el-dropdown-item v-for="(folder, index) in folderList" :key="index" :command="folder">
-                {{ folder }}
-              </el-dropdown-item>
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-        
-        <el-button type="primary" icon="search" @click="onSubmit" title="搜索">查询</el-button>
-        <el-button type="primary" title="选择图片,文生图" @click="texttoimg" :loading="isLoading">文生图</el-button>
-        <el-button type="primary" title="选择图片,图生图" @click="imgtoimg" :loading="isLoading">图生图</el-button>
-      </el-form-item>
-    </el-form>
-
-    <!-- 文生图弹窗 -->
-    <el-dialog v-model="txtimgDialogVisible" title="文生图" width="660px" top="10%">
-      <el-form label-width="100px">
-         <!-- <el-form-item label="提示词" prop="chinese_description">
-          <el-input 
-            v-model="form.chinese_description" 
-            type="textarea" 
-            :rows="3" 
-            placeholder="请输入文生图描述"
-          />
-        </el-form-item> -->
-        <el-form-item label="文生图模型">
-          <div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap;">
-            <el-radio-group v-model="txtimgselectedModel" @change="handleModelChange">
-              <el-radio 
-                v-for="item in txttoimg_modelList" 
-                :key="item.id"
-                :label="item.txttoimg">
-                {{ item.txttoimg }} 
-                <span v-if="item.id === usedIds.wenshengtu" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
-              </el-radio>
-            </el-radio-group>
-            <el-button type="success"  @click="setActive('wenshengtu')"  :disabled="!selectedIds.wenshengtu || selectedIds.wenshengtu === usedIds.wenshengtu">
-              设为默认使用模型
-            </el-button>
-          </div>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="txtimgDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="generateTxtImg" :loading="isGenerating">生成文生图</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 表格展示 -->
-    <el-table
-      ref="multipleTable"
-      style="width: 100%; height: 62vh;"
-      :row-style="{ height: '20px' }"
-      :header-cell-style="{ padding: '0px' }"
-      :cell-style="{ padding: '0px' }"
-      :header-row-style="{ height: '20px' }"
-      border tooltip-effect="dark"
-      :data="tableData1"
-      row-key="ID"
-      highlight-current-row
-      :cell-class-name="tableDataCellClass"
-      @selection-change="SelectionChange"
-      @row-dblclick="onRowDblClick"
-      :show-overflow-tooltip="true"
-    >
-      <el-table-column type="selection" width="55" />
-      <el-table-column label="ID" prop="id" width="70" />
-      <el-table-column label="第一段" prop="chinese_description" width="300" />
-      <el-table-column label="第二段" prop="english_description" width="300" />
-      <el-table-column label="图片名称" prop="img_name" width="300" />
-      <el-table-column label="原图" width="120">
-        <template #default="{ row }">
-          <el-image
-            :src="formatImageUrl(row.old_image_url)"
-            :preview-src-list="[formatImageUrl(row.old_image_url)]"
-            style="width: 90px; height: 70px;"
-            fit="contain"
-            preview-teleported
-          />
-        </template>
-      </el-table-column>
-	  
-	  
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'black-forest-labs/FLUX.1-kontext-pro')" 
-	    label="FLUX.1" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'black-forest-labs/FLUX.1-kontext-pro'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'dall-e-3')" 
-	    label="dall-e-3" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'dall-e-3'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column 
-	    v-if="tableData1.some(row => row.model === 'gpt-image-1')" 
-	    label="gpt-image-1" 
-	    width="120">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'gpt-image-1'"
-	        :src="formatImageUrl(row.new_image_url)"
-	        :preview-src-list="[formatImageUrl(row.new_image_url)]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-	  <el-table-column
-	    v-if="tableData1.some(row => row.model === 'MID_JOURNEY')" 
-	    label="MID_JOURNEY" 
-	    width="130">
-	    <template #default="{ row }">
-	      <el-image
-	        v-if="row.model === 'MID_JOURNEY'"
-	        :src="`https://chatapi.onechats.ai/mj/image/${row.taskId}`"  
-	        :preview-src-list="[`https://chatapi.onechats.ai/mj/image/${row.taskId}`]"
-	        style="width: 90px; height: 70px;"
-	        fit="contain"
-	        preview-teleported
-	      />
-	      <span v-else>-</span>
-	    </template>
-	  </el-table-column>
-      
-      <el-table-column label="图生图预览" width="120">
-        <template #default="{ row }">
-          <el-image
-            :src="formatImageUrl(row.imgtoimg_url)"
-            :preview-src-list="[formatImageUrl(row.imgtoimg_url)]"
-            style="width: 90px; height: 70px;"
-            fit="contain"
-            preview-teleported
-          />
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <!-- 分页 -->
-    <div class="gva-pagination">
-      <el-pagination
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-        :current-page="page"
-        :page-sizes="[10, 30, 50, 100]"
-        :page-size="pageSize"
-        layout="total, sizes, prev, pager, next, jumper"
-        :total="total"
-      />
-    </div>
-
-    <el-dialog 
-      v-model="editDialogVisible" 
-      title="查看信息" 
-      style="width: 70%; height: 80%;top: -6%;" 
-      :modal="true">
-      <el-scrollbar style="max-height: 60vh; overflow-y: auto;">
-        <el-form :model="editFormData">
-          <el-form-item label="ID" label-width="80px">
-            <el-input v-model="editFormData.id" disabled />
-          </el-form-item>
-    
-          <el-form-item label="文生图路径">
-            <el-input v-model="editFormData.new_image_url" disabled />
-          </el-form-item>
-    
-          <el-form-item label="图生图路径">
-            <el-input v-model="editFormData.imgtoimg_url" disabled />
-          </el-form-item>
-    
-          <el-form-item label="第一段">
-            <el-input
-              type="textarea"
-              v-model="editFormData.chinese_description"
-              :rows="6"
-            />
-          </el-form-item>
-    
-          <el-form-item label="第二段">
-            <el-input
-              type="textarea"
-              v-model="editFormData.english_description"
-              :rows="6"
-            />
-          </el-form-item>
-    
-          <el-form-item label="图片名称">
-            <el-input v-model="editFormData.img_name" />
-          </el-form-item>
-        </el-form>
-      </el-scrollbar>
-    
-      <template #footer>
-        <el-button @click="editDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitEdit">确定</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 图片预览 -->
-    <el-dialog v-model="dialogVisible" title="图片预览" width="70%" top="5vh" destroy-on-close>
-      <div style="text-align: center;">
-        <el-image :src="currentImageUrl" style="max-width: 100%; max-height: 70vh;" fit="contain" />
-      </div>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, toRaw, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
-import { getTable, imageToText, Template_ids,txttoimg_moxing,GetHttpUrl,txttoimg_update, getSide } from '@/api/mes/job'
-import { useUserStore } from '@/pinia/modules/user'
-import { ArrowDown } from '@element-plus/icons-vue'
-
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-//获取服务器地址
-const baseUrl = ref('')
-const port = ref('')
-const full_url = ref('')
-// 从接口获取服务器地址
-const fetchServerUrl = async () => {
-  try {
-    const res = await GetHttpUrl()
-    if (res.code === 0 && res.data && res.data.full_url) {
-      full_url.value = res.data.full_url
-      baseUrl.value = res.data.baseUrl
-      port.value = res.data.port
-	  console.log('获取服务器地址',full_url.value)
-	  console.log('IP地址',baseUrl.value)
-	  console.log('端口',port.value)
-    }
-  } catch (error) {
-    console.error('获取服务器地址失败:', error)
-  }
-}
-fetchServerUrl();
-
-const searchInfo = ref('')
-const tableData1 = ref([])
-const total = ref(0)
-const page = ref(1)
-const pageSize = ref(50)
-const dialogVisible = ref(false)
-const currentImageUrl = ref('')
-const width = ref('')
-const height = ref('')
-const isLoading = ref(false)
-const txttotxt_selectedOption = ref('gemini-2.0-flash')
-const selectedOption = ref('dall-e-3')
-const num = ref(1)
-const _parh = ref([])
-const folderList = ref([])
-const selectedFolder = ref('')
-
-const editDialogVisible = ref(false)
-const editFormData = reactive({
-  id: '',
-  chinese_description: '',
-  english_description: '',
-  new_image_url: '',
-  imgtoimg_url: '',
-  img_name: ''
-})
-
-// 定义文生图表单对象
-const form = reactive({
-  chinese_description: ''
-})
-
-const formatImageUrl = (path) => {
-  if (!path) return ''
-  return `${full_url.value}/${path.replace(/^public\//, '')}`
-}
-
-const txttoimg_modelList = ref([]); // 存储所有模型数据
-const txtimgselectedModel = ref(''); // 当前选中的模型名称
-const usedId = ref(null); // 当前使用的模型ID(兼容旧代码)
-const selectedId = ref(null); // 用户选择的模型ID(兼容旧代码)
-const txtimgDialogVisible = ref(false); // 文生图弹窗可见性
-const isGenerating = ref(false); // 文生图生成中状态
-const usedIds = ref({}); // 当前使用的模型ID集合
-const selectedIds = ref({}); // 用户选择的模型ID集合
-
-// 获取文生图模型列表
-const fetchTxtImgModels = async () => {
-  try {
-    const response = await txttoimg_moxing();
-    txttoimg_modelList.value = response.data.models.wenshengtu;
-    // 获取当前使用的模型ID
-    usedIds.value = response.data.used_ids;
-    const currentUsedId = usedIds.value.wenshengtu;
-    
-    // 设置默认选中的模型
-    const defaultModel = txttoimg_modelList.value.find(item => item.id === currentUsedId);
-    if (defaultModel) {
-      txtimgselectedModel.value = defaultModel.txttoimg;
-      usedId.value = defaultModel.id;
-      selectedId.value = defaultModel.id; 
-      selectedIds.value.wenshengtu = defaultModel.id;
-      selectedOption.value = defaultModel.txttoimg;
-    }
-    
-    console.log('文生图模型列表:', txttoimg_modelList.value);
-    console.log('当前使用的模型ID:', currentUsedId);
-    console.log('默认选中的模型:', defaultModel);
-  } catch (error) {
-    console.error('获取文生图模型列表失败:', error);
-    ElMessage.error('获取模型列表失败');
-  }
-}
-fetchTxtImgModels();
-
-const getTableData = async () => {
-    const res = await getTable({
-      search: searchInfo.value,
-      limit: pageSize.value,
-      page: page.value,
-      folder: selectedFolder.value
-    })
-    tableData1.value = res.data.list
-    total.value = res.data.total
-    
-    // 更新文件夹列表
-    if (res.data.folder) {
-      folderList.value = res.data.folder
-    }
-
-    // const res_Template = await Template_ids({path:row['old_image_url']});
-    // height.value = res_Template.data.height
-    // width.value = res_Template.data.width
-}
-
-
-// 当选择模型变化时
-const handleModelChange = (modelName) => {
-  const model = txttoimg_modelList.value.find(item => item.txttoimg === modelName);
-  if (model) {
-    selectedId.value = model.id;
-    selectedIds.value.wenshengtu = model.id;
-    selectedOption.value = model.txttoimg;
-    console.log('选择的模型ID:', model.id);
-  }
-}
-// 设置默认模型
-const setActive = async (type) => {
-  const id = selectedIds.value[type]
-  if (id) {
-    try {
-      // 调用API设置默认模型
-      const res = await txttoimg_update({ id: id,type:"wenshengtu"})
-      if (res.code === 0) {
-        usedIds.value[type] = id
-        ElMessage.success('默认模型设置成功')
-      } else {
-        ElMessage.error(res.msg || '设置默认模型失败')
-      }
-    } catch (error) {
-      console.error('设置默认模型失败', error)
-      ElMessage.error('设置默认模型失败')
-    }
-  }
-}
-
-
-// 处理文件夹选择
-const handleFolderCommand = (command) => {
-  selectedFolder.value = command
-  onSubmit()
-}
-
-const onSubmit = () => {
-  page.value = 1
-  getTableData()
-}
-
-const handleCurrentChange = (val) => {
-  page.value = val
-  getTableData()
-}
-
-const handleSizeChange = (val) => {
-  pageSize.value = val
-  page.value = 1
-  getTableData()
-}
-
-const SelectionChange = (rows) => {
-  _parh.value = rows.map(item => item.old_image_url)
-}
-
-
-// 文生图生成函数
-const generateTxtImg = () => {
-  txtimgDialogVisible.value = false
-  processImages(toRaw(_parh.value), '文生图', form.chinese_description)
-}
-
-const texttoimg = async () => {
-  if (_parh.value.length === 0) {
-    ElMessage.warning('请先选择要处理的图片')
-    return
-  }
-  // 获取模型列表
-  await fetchTxtImgModels()
-  // 显示弹窗
-  txtimgDialogVisible.value = true
-}
-
-const imgtoimg = () => {
-  processImages(toRaw(_parh.value), '图生图')
-}
-
-// 是否执行几何图(默认不执行)
-const executeKeywords = ref(false)
-
-const processImages = async (files, type, prompt = '') => {
-  if (!Array.isArray(files) || files.length === 0) {
-    ElMessage.warning('请选择要处理的图片')
-    return
-  }
-
-  const pathGroups = {}
-  _parh.value.forEach(path => {
-    const dir = path.replace(/\/[^\/]*$/, '')
-    if (!pathGroups[dir]) pathGroups[dir] = []
-    pathGroups[dir].push(path)
-  })
-
-  isLoading.value = true
-  const failList = []
-
-  for (const [dir, images] of Object.entries(pathGroups)) {
-    const payload = {
-      old_image_file: dir,
-      type,
-      selectedOption: selectedOption.value,
-      txttotxt_selectedOption: txttotxt_selectedOption.value,
-      batch: images,
-      num: num.value,
-      width: width.value,
-      height: height.value,
-      executeKeywords:executeKeywords.value,
-    }
-
-    // console.log(payload);
-    // await new Promise(resolve => setTimeout(resolve, 2000));
-    // isLoading.value = false;
-    // return;
-
-    try {
-      await imageToText(payload)
-      ElMessage.success(`处理目录:${dir},共 ${images.length} 张 * ${num.value} 次`)
-    } catch (e) {
-      console.warn('处理失败:', dir, e)
-      images.forEach((_, i) => failList.push(`目录 ${dir} 第 ${i + 1} 张`))
-    }
-  }
-
-  if (failList.length > 0) {
-    ElMessage.warning('部分图片处理失败,请查看控制台')
-  }
-
-  await new Promise(resolve => setTimeout(resolve, 3000))
-  isLoading.value = false
-}
-
-const previewImage = (url) => {
-  currentImageUrl.value = url
-  dialogVisible.value = true
-}
-
-const onRowDblClick = (row) => {
-  editFormData.id = row.id
-  editFormData.chinese_description = row.chinese_description
-  editFormData.english_description = row.english_description
-  editFormData.img_name = row.img_name
-  editFormData.new_image_url = row.new_image_url
-  editFormData.imgtoimg_url = row.imgtoimg_url
-  editDialogVisible.value = true
-}
-
-const submitEdit = async () => {
-  try {
-    const { id, chinese_description, english_description, img_name } = editFormData;
-
-    const payload = {
-      id,
-      chinese_description,
-      english_description,
-      img_name
-    };
-
-    await getSide(payload);
-    ElMessage.success('修改成功');
-    editDialogVisible.value = false;
-    getTableData();
-  } catch (error) {
-    console.error('修改失败:', error);
-    ElMessage.error('修改失败,请稍后重试');
-  }
-};
-
-onMounted(() => {
-  getTableData()
-})
-</script>
-
-<style scoped>
-:deep(.el-table td .cell) {
-  line-height: 22px !important;
-}
-:deep(.el-dialog__body) {
-  padding: 10px 20px;
-}
-.gva-pagination {
-  margin-top: 10px;
-  text-align: right;
-}
-:deep(.el-table__body tr.current-row) > td {
-  background: #ff80ff !important;
-}
-</style>

+ 0 - 607
src/view/performance/QualityAssessment/cropdefective.vue

@@ -1,607 +0,0 @@
-<template>
-  <div class="container">
-	  
-    <!-- 控制面板 -->
-    <!-- <div class="control-panel"> -->
-      <div class="size-controls">
-        <div class="input-group">
-			<label>叠图路径:
-			  <input type="text" v-model="imageCode" placeholder="" @keyup.enter="loadImage" style="margin-left: 15px;"/>
-			</label>
-			<label>背景图路径:
-			  <input type="text" v-model="bgimageCode" placeholder="" @keyup.enter="loadBackgroundImage" />
-			</label>
-			<label>编辑宽度(px):
-				<input type="number" v-model.number="editSize.width" @input="updateScale('width')" disabled/>
-			</label>
-			<label>编辑高度(px):
-				<input type="number" v-model.number="editSize.height" @input="updateScale('height')" disabled/>
-			</label>
-			<label>设置最终效果宽度(px):
-				<input type="text" v-model.number="canvasSize.width" />
-			</label>
-			<label>设置最终效果高度(px):
-				<input type="text" v-model.number="canvasSize.height" />
-			</label>
-		  <div class="image-info" style="background-color: #181d28;width: 400px;">
-			  <p style="color: white;">最终效果尺寸:{{ editSize.width || '--' }} × {{ editSize.height || '--' }}</p>
-			  <p style="color: white;">缩放倍率:{{ scale ? scale.toFixed(2) : '--' }}</p>
-			</div>
-        </div>
-      <div class="input-group" style="display: flex; gap: 10px;">
-        <!-- 图片显示区域条件渲染 -->
-        <div class="image-info" style="background-color: #181d28; height: 280px;">
-          <h3 style="color: white;margin: 5px;">叠图原始图片</h3>
-          <img v-if="overlayImg" :src="overlayImg" alt="叠图原始图片" @error="onImageError"
-               style="width: 200px; height: 200px; background-color: white;" />
-          <div v-else style="width: 200px; height: 200px; background-color: #ccc; display: flex; align-items: center; justify-content: center;">
-            <span style="color: #666;">未加载图片</span>
-          </div>
-		  <p style="color: white;">叠图原图尺寸:{{ imageSize.width || '--' }} × {{ imageSize.height || '--' }}</p>
-		  <p style="color: white;">叠图图案区域:{{ patternSize.width || '--' }} × {{ patternSize.height || '--' }}</p>
-        </div>
-        
-        <div class="image-info" style="background-color: #181d28; height: 280px;">
-          <h3 style="color: white;margin: 5px;">背景原始图片</h3>
-          <img v-if="backgroundImgSrc" :src="backgroundImgSrc" alt="背景原始图片" @error="hasBackground = false"
-               style="width: 200px; height: 200px; background-color: white;" />
-          <div v-else style="width: 200px; height: 200px; background-color: #ccc; display: flex; align-items: center; justify-content: center;">
-            <span style="color: #666;">未加载图片</span>
-          </div>
-		  <p style="color: white;">背景图尺寸:{{ backgroundSize.width || '--' }} × {{ backgroundSize.height || '--' }}</p>
-		  <p style="color: white;">背景图案区域:{{ backgroundPatternSize.width || '--' }} × {{ backgroundPatternSize.height || '--' }}</p>
-        </div>
-
-      </div>
-	</div>
-<!-- </div> -->
-    
-    <!-- 图片展示区 -->
-    <div class="image-display">
-        <div class="image-column">
-		   <h3>叠图原始图 (去白边预览)</h3>
-		   <div class="image-container"  style="background-color: #181d28;">
-			 <canvas ref="previewCanvas" style="background-color: white;"/>
-		   </div>
-		 </div>
-		 
-		 <div class="image-column">
-		   <h3>背景图(去白边预览)</h3>
-		   <div class="image-container" style="background-color: #181d28;">
-		     <canvas ref="backgroundCanvas" style="background-color: white;" />
-		   </div>
-		 </div>
-		 
-		 <div class="image-column">
-		   <h3>最终效果(叠图+背景图)</h3>
-		   <div class="image-container" style="background-color: #181d28;">
-			 <canvas ref="finalCanvas" style="background-color: white;"/>
-		   </div>
-		 </div>
-    </div>
-    
-    <!-- 操作按钮 -->
-    <div class="action-buttons">
-      <button @click="handleBatch" class="batch-btn">批量处理图片导出</button>
-      <button @click="saveImage" class="save-btn">单个图片下载</button>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, watch, onMounted } from 'vue'
-import JSZip from 'jszip'
-
-/** ----------------------------
- * DOM 元素引用
- * ---------------------------- */
-const imageCode = ref('0699000021156')
-const bgimageCode = ref('e2c02fadbadf474421aae73084dbf91')
-const imageObj = ref(null)
-const previewCanvas = ref(null)
-const finalCanvas = ref(null)
-const backgroundCanvas = ref(null)
-
-/** ----------------------------
- * 叠图图片路径相关信息
- * ---------------------------- */
-const getImagePath = (code) => `/src/assets/600/${code}.png`
-const overlayImg = ref('')
-const errorMessage = ref('')
-const imageSize = reactive({ width: 0, height: 0 })
-const patternSize = reactive({ width: 0, height: 0 })
-const patternBounds = ref({ x: 0, y: 0, width: 0, height: 0 })
-const editSize = reactive({ width: 0, height: 0 })
-
-/** ----------------------------
- * 背景图图片路径相关信息
- * ---------------------------- */
-const getBackgroundPath = (code) => `/src/assets/600/${code}.png`
-const backgroundImgSrc = ref('')
-const backgroundImg = ref(null)
-const hasBackground = ref(false)
-const backgroundSize = reactive({ width: 0, height: 0 })
-const backgroundPatternSize = reactive({ width: 0, height: 0 })
-
-/** ----------------------------
- * 缩放比例及画布大小
- * ---------------------------- */
-const scale = ref(1.0)
-const canvasSize = reactive({ width: 709, height: 896 })
-
-/** ----------------------------
- * 通用工具函数
- * ---------------------------- */
-// 计算适配缩放比例
-const getFittedScale = () => {
-  return Math.min(
-    canvasSize.width / patternSize.width,
-    canvasSize.height / patternSize.height
-  )
-}
-
-// 绘制图案
-const drawPattern = (ctx, img, bounds, scale) => {
-  ctx.save()
-  ctx.scale(scale, scale)
-  ctx.drawImage(img, bounds.x, bounds.y, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height)
-  ctx.restore()
-}
-
-// 处理主图加载错误
-const onImageError = () => {
-  errorMessage.value = `❌ 找不到图片编号:${imageCode.value}`
-  overlayImg.value = ''
-  Object.assign(imageSize, { width: 0, height: 0 })
-  Object.assign(patternSize, { width: 0, height: 0 })
-  Object.assign(editSize, { width: 0, height: 0 })
-}
-
-/** ----------------------------
- * 主图相关处理
- * ---------------------------- */
-// 计算非透明像素边界
-const computePatternBounds = (img) => {
-  const canvas = document.createElement('canvas')
-  canvas.width = img.width
-  canvas.height = img.height
-  const ctx = canvas.getContext('2d')
-  ctx.drawImage(img, 0, 0)
-  const data = ctx.getImageData(0, 0, img.width, img.height).data
-
-  let minX = img.width, minY = img.height, maxX = 0, maxY = 0
-  let found = false
-
-  for (let y = 0; y < img.height; y++) {
-    for (let x = 0; x < img.width; x++) {
-      const i = (y * img.width + x) * 4
-      if (data[i + 3] > 0) {
-        found = true
-        minX = Math.min(minX, x)
-        minY = Math.min(minY, y)
-        maxX = Math.max(maxX, x)
-        maxY = Math.max(maxY, y)
-      }
-    }
-  }
-
-  if (found) {
-    const width = maxX - minX + 1
-    const height = maxY - minY + 1
-    patternSize.width = width
-    patternSize.height = height
-    patternBounds.value = { x: minX, y: minY, width, height }
-  }
-}
-
-// 初始化编辑区域
-const initializeEditSize = () => {
-  if (!patternSize.width || !patternSize.height) return
-  scale.value = getFittedScale()
-  editSize.width = Math.round(patternSize.width * scale.value)
-  editSize.height = Math.round(patternSize.height * scale.value)
-}
-
-// 用户缩放更新
-const updateScale = (type) => {
-  if (!patternSize.width || !patternSize.height) return
-  scale.value = type === 'width' ? editSize.width / patternSize.width : editSize.height / patternSize.height
-  scale.value = Math.min(scale.value, canvasSize.width / patternSize.width, canvasSize.height / patternSize.height)
-  editSize.width = Math.round(patternSize.width * scale.value)
-  editSize.height = Math.round(patternSize.height * scale.value)
-}
-
-// 加载主图像
-const loadImage = () => {
-  errorMessage.value = ''
-  const img = new Image()
-  img.crossOrigin = 'Anonymous'
-  img.src = getImagePath(imageCode.value)
-  img.onload = () => {
-    imageObj.value = img
-    overlayImg.value = img.src
-    imageSize.width = img.width
-    imageSize.height = img.height
-    computePatternBounds(img)
-    initializeEditSize()
-    drawPreviewImage()
-    drawFinalImage()
-  }
-  img.onerror = onImageError
-}
-
-/** ----------------------------
- * 背景图处理
- * ---------------------------- */
-const loadBackgroundImage = () => {
-  if (!bgimageCode.value) {
-    hasBackground.value = false
-    backgroundImgSrc.value = ''
-    backgroundSize.width = 0
-    backgroundSize.height = 0
-    backgroundPatternSize.width = 0
-    backgroundPatternSize.height = 0
-    if (backgroundCanvas.value) {
-      const ctx = backgroundCanvas.value.getContext('2d')
-      ctx.clearRect(0, 0, backgroundCanvas.value.width, backgroundCanvas.value.height)
-      backgroundCanvas.value.width = 0
-      backgroundCanvas.value.height = 0
-    }
-    drawFinalImage()
-    return
-  }
-
-  const img = new Image()
-  img.crossOrigin = 'Anonymous'
-  img.src = getBackgroundPath(bgimageCode.value)
-  backgroundImgSrc.value = img.src
-
-  img.onload = () => {
-    backgroundImg.value = img
-    backgroundSize.width = img.width
-    backgroundSize.height = img.height
-
-    const tempCanvas = document.createElement('canvas')
-    tempCanvas.width = img.width
-    tempCanvas.height = img.height
-    const tempCtx = tempCanvas.getContext('2d')
-    tempCtx.drawImage(img, 0, 0)
-
-    const data = tempCtx.getImageData(0, 0, img.width, img.height).data
-
-    let minX = img.width, minY = img.height, maxX = 0, maxY = 0
-    let found = false
-
-    for (let y = 0; y < img.height; y++) {
-      for (let x = 0; x < img.width; x++) {
-        const i = (y * img.width + x) * 4
-        const [r, g, b, a] = [data[i], data[i+1], data[i+2], data[i+3]]
-        const isWhiteEdge = r >= 245 && g >= 245 && b >= 245 && a > 0
-        if (!isWhiteEdge && a > 0) {
-          found = true
-          minX = Math.min(minX, x)
-          maxX = Math.max(maxX, x)
-          minY = Math.min(minY, y)
-          maxY = Math.max(maxY, y)
-        }
-      }
-    }
-
-    if (found) {
-      const width = maxX - minX + 1
-      const height = maxY - minY + 1
-      backgroundPatternSize.width = width
-      backgroundPatternSize.height = height
-      hasBackground.value = true
-
-      const ctx = backgroundCanvas.value?.getContext('2d')
-      if (ctx && backgroundCanvas.value) {
-        backgroundCanvas.value.width = width
-        backgroundCanvas.value.height = height
-        ctx.clearRect(0, 0, width, height)
-
-        const croppedCanvas = document.createElement('canvas')
-        croppedCanvas.width = width
-        croppedCanvas.height = height
-        const croppedCtx = croppedCanvas.getContext('2d')
-        croppedCtx.drawImage(img, minX, minY, width, height, 0, 0, width, height)
-
-        ctx.drawImage(croppedCanvas, 0, 0)
-      }
-    } else {
-      hasBackground.value = false
-      if (backgroundCanvas.value) {
-        const ctx = backgroundCanvas.value.getContext('2d')
-        ctx.clearRect(0, 0, backgroundCanvas.value.width, backgroundCanvas.value.height)
-        backgroundCanvas.value.width = 0
-        backgroundCanvas.value.height = 0
-      }
-    }
-
-    drawFinalImage()
-  }
-
-  img.onerror = () => {
-    hasBackground.value = false
-    backgroundImgSrc.value = ''
-    backgroundSize.width = 0
-    backgroundSize.height = 0
-    backgroundPatternSize.width = 0
-    backgroundPatternSize.height = 0
-    console.error('❌ 背景图加载失败')
-  }
-}
-
-/** ----------------------------
- * 画布绘制处理
- * ---------------------------- */
-// 预览图绘制
-const drawPreviewImage = () => {
-  const canvas = previewCanvas.value
-  const img = imageObj.value
-  const bounds = patternBounds.value
-  if (!canvas || !img || !bounds.width || !bounds.height) return
-  canvas.width = bounds.width
-  canvas.height = bounds.height
-  const ctx = canvas.getContext('2d')
-  ctx.clearRect(0, 0, bounds.width, bounds.height)
-  ctx.drawImage(img, bounds.x, bounds.y, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height)
-}
-
-// 最终图绘制
-const drawFinalImage = () => {
-  const canvas = finalCanvas.value
-  const img = imageObj.value
-  const bounds = patternBounds.value
-  if (!canvas || !img || !bounds.width || !bounds.height) return
-
-  const outputWidth = Math.round(bounds.width * scale.value)
-  const outputHeight = Math.round(bounds.height * scale.value)
-  canvas.width = outputWidth
-  canvas.height = outputHeight
-  const ctx = canvas.getContext('2d')
-  ctx.clearRect(0, 0, outputWidth, outputHeight)
-
-  // 背景图处理
-  if (hasBackground.value && backgroundCanvas.value) {
-    const bgWidth = backgroundCanvas.value.width
-    const bgHeight = backgroundCanvas.value.height
-    const scaleX = outputWidth / bgWidth
-    const scaleY = outputHeight / bgHeight
-    const bgScale = Math.max(scaleX, scaleY)
-    const scaledBgWidth = bgWidth * bgScale
-    const scaledBgHeight = bgHeight * bgScale
-    const offsetX = (outputWidth - scaledBgWidth) / 2
-    const offsetY = (outputHeight - scaledBgHeight) / 2
-
-    ctx.drawImage(backgroundCanvas.value, 0, 0, bgWidth, bgHeight, offsetX, offsetY, scaledBgWidth, scaledBgHeight)
-  }
-
-  drawPattern(ctx, img, bounds, scale.value)
-}
-
-/** ----------------------------
- * 导出与批量处理
- * ---------------------------- */
-// 单张图像导出
-const saveImage = () => {
-  const link = document.createElement('a')
-  link.download = `pattern-${imageCode.value}.png`
-  link.href = finalCanvas.value.toDataURL('image/png')
-  link.click()
-}
-
-// 批量导出 ZIP 包
-const handleBatch = async () => {
-  try {
-    const res = await fetch('/src/assets/600/list.json')
-    const fileList = await res.json()
-    const now = new Date()
-    const dirName = `${now.getFullYear()}${String(now.getMonth()+1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`
-    const zip = new JSZip()
-    const folder = zip.folder(`output/${dirName}`)
-    const failed = []
-
-    for (const fileName of fileList) {
-      const code = fileName.replace('.png', '')
-      imageCode.value = code
-
-      await new Promise(resolve => {
-        const check = () => {
-          if (imageSize.width > 0) {
-            drawFinalImage()
-            setTimeout(() => {
-              finalCanvas.value.toBlob(blob => {
-                if (blob) folder.file(fileName, blob)
-                else failed.push(fileName)
-                resolve()
-              }, 'image/png')
-            }, 100)
-          } else {
-            setTimeout(check, 100)
-          }
-        }
-        check()
-      })
-
-      await new Promise(r => setTimeout(r, 20))
-    }
-
-    const zipBlob = await zip.generateAsync({ type: 'blob' })
-    const link = document.createElement('a')
-    link.href = URL.createObjectURL(zipBlob)
-    link.download = `export_${dirName}.zip`
-    link.click()
-    URL.revokeObjectURL(link.href)
-
-    console.log(`✅ 批量完成:${fileList.length} 张`)
-    if (failed.length) console.warn(`❌ 失败:${failed.join(', ')}`)
-  } catch (err) {
-    console.error('批量处理失败:', err)
-    alert(`出错:${err.message}`)
-  }
-}
-
-/** ----------------------------
- * 生命周期和监听器
- * ---------------------------- */
-watch(imageCode, val => {
-  if (val) loadImage()
-})
-watch(bgimageCode, () => {
-  if (bgimageCode.value) {
-    loadBackgroundImage()
-  }
-})
-watch([() => editSize.width, () => editSize.height, scale], () => {
-  drawFinalImage()
-})
-
-onMounted(() => {
-  if (imageCode.value) loadImage()
-  if (bgimageCode.value) loadBackgroundImage()
-})
-</script>
-
-<style scoped>
-.container {
-  margin: 0 auto;
-  font-family: Arial, sans-serif;
-}
-
-/* 控制面板样式 */
-.control-panel {
-  background: #f5f7fa;
-  padding: 0px;
-  border-radius: 8px;
-  box-shadow: 0 2px 10px rgba(0,0,0,0.05);
-}
-
-.input-group {
-  margin-bottom: 10px;
-}
-
-.input-group label {
-  display: block;
-  margin-bottom: 5px;
-  font-size: 14px;
-  color: #555;
-}
-
-.input-group input {
-  width: 40%;
-  padding: 8px 12px;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  font-size: 14px;
-}
-
-.load-btn {
-  padding: 8px 15px;
-  background: #409eff;
-  color: white;
-  border: none;
-  border-radius: 4px;
-  cursor: pointer;
-  margin-left: 10px;
-}
-
-.load-btn:hover {
-  background: #66b1ff;
-}
-
-.size-controls {
-  display: grid;
-  grid-template-columns: repeat(2, 1fr);
-  gap: 15px;
-  margin-top: 15px;
-}
-
-.image-info {
-  background: white;
-  padding: 10px;
-  border-radius: 6px;
-}
-
-.image-info p {
-  margin: 5px 0;
-  font-size: 14px;
-  color: #666;
-}
-
-/* 图片展示区 */
-.image-display {
-  display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 20px;
-  margin-bottom: 25px;
-}
-
-.image-column {
-  background: #f5f7fa;
-  /* padding: 15px; */
-  border-radius: 8px;
-}
-
-.image-column h3 {
-  text-align: center;
-  /* margin-bottom: 15px; */
-  margin: 5px;
-  color: #333;
-  font-size: 16px;
-}
-
-.image-container {
-  height: 300px;
-  background: white;
-  border: 1px solid #eee;
-  border-radius: 6px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  overflow: hidden;
-}
-
-.image-container img,
-.image-container canvas {
-  max-width: 100%;
-  max-height: 100%;
-  object-fit: contain;
-}
-
-/* 操作按钮 */
-.action-buttons {
-  display: flex;
-  justify-content: center;
-  gap: 20px;
-  margin-top: 20px;
-}
-
-button {
-  padding: 10px 25px;
-  border: none;
-  border-radius: 6px;
-  font-size: 14px;
-  font-weight: bold;
-  cursor: pointer;
-  transition: all 0.3s;
-}
-
-.batch-btn {
-  background: #409eff;
-  color: white;
-}
-
-.batch-btn:hover {
-  background: #66b1ff;
-}
-
-.save-btn {
-  background: #67c23a;
-  color: white;
-}
-
-.save-btn:hover {
-  background: #85ce61;
-}
-</style>

+ 0 - 2061
src/view/performance/QualityAssessment/excessive.vue

@@ -1,2061 +0,0 @@
-<template>
-  <div class="page-container">
-    <el-descriptions title="" :column="1" border>
-		<el-descriptions-item label="图片上传">
-		  <div class="upload-container">
-			<el-upload
-			  ref="uploadRef"
-			  class="upload-list"
-			  :action="uploadUrl"
-			  :http-request="customUpload"
-			  :headers="uploadHeaders"
-			  list-type="picture-card"
-			  :on-preview="handlePreview"
-			  :on-remove="handleRemove"
-			   :on-change="handleChange"
-			  :before-upload="beforeUpload"
-			  :on-success="handleSuccess"
-			  :on-error="handleError"
-			  :on-exceed="handleExceed"
-			  :limit="30"
-			  :multiple="true"
-			  accept="image/jpeg,image/png">
-			  <el-icon><Plus /></el-icon>
-			  <template #tip>
-				<div class="el-upload__tip">
-				  只能上传 <b>jpg/png</b> 文件,且 <b>不超过30张</b>
-				</div>
-			  </template>
-			  <template #file="{ file }">
-			  </template>
-			</el-upload>
-		  </div>
-		</el-descriptions-item>
-	</el-descriptions>
-	
-	<el-descriptions
-		title=""
-		column="7"
-		border
-		class="queue-status-box">
-		
-		<el-descriptions-item label="总任务数">
-			<el-tag type="info">{{ queuestats['总任务数'] }}</el-tag>
-		</el-descriptions-item>
-
-		<el-descriptions-item label="待处理">
-			<el-tag type="warning">{{ queuestats['待处理'] }}</el-tag>
-		</el-descriptions-item>
-
-		<el-descriptions-item label="处理中">
-			<el-tag type="primary">{{ queuestats['处理中'] }}</el-tag>
-		</el-descriptions-item>
-
-		<el-descriptions-item label="成功">
-			<el-tag type="success">{{ queuestats['成功'] }}</el-tag>
-		</el-descriptions-item>
-
-		<el-descriptions-item label="失败">
-			<el-tag type="danger">{{ queuestats['失败'] }}</el-tag>
-		</el-descriptions-item>
-
-		<el-descriptions-item label="操作">
-			<el-button type="danger" size="small" @click="stopQueueclick" :loading="isLoading" :disabled="isLoading">全部停止处理</el-button>
-			<el-button type="primary" size="small" @click="handleViewQueue" :loading="isLoading">任务列表</el-button>
-		</el-descriptions-item>
-	</el-descriptions>
-		
-	<el-dialog
-	  v-model="queueDialogVisible"
-	  :before-close="queueDialog"
-	  title="队列任务详情"
-	  width="700px"
-	  destroy-on-close
-	>
-	  <div class="dialog-scroll-wrapper">
-	    <template v-if="queueData.tasks_preview && queueData.tasks_preview.length">
-	      <el-card
-	        v-for="(task, index) in queueData.tasks_preview"
-	        :key="task.id"
-	        class="task-card"
-	      >
-	        <div class="task-grid">
-	          <div><strong>任务 ID:</strong>{{ task.id }}</div>
-	          <div><strong>尝试次数:</strong>{{ task.attempts }}</div>
-	
-	          <div>
-	            <strong>状态:</strong>
-	            <el-tag :type="index === 0 ? 'success' : 'warning'">
-	              {{ index === 0 ? '处理中' : '待处理' }}
-	            </el-tag>
-	          </div>
-	
-	          <div class="task-image">
-	            <el-image
-	              style="width: 120px; height: 120px; border: 1px solid #ccc"
-	              :src="formatImageUrl(task.data.sourceDir + '/' + task.data.file_name)"
-	              fit="cover"
-	              :preview-src-list="[formatImageUrl(task.data.sourceDir + '/' + task.data.file_name)]"
-	            />
-	          </div>
-	        </div>
-	      </el-card>
-	    </template>
-	
-	    <template v-else>
-	      <div class="empty-queue">当前无任务在队列中。</div>
-	    </template>
-	  </div>
-	</el-dialog>
-
-	  
-	<br>
-	<el-table :data="tableData" border height="450" v-loading="loading">
-	  <el-table-column prop="id" label="ID" width="50" />
-	  <el-table-column prop="old_image_url" label="原图目录" width="360" />
-	  <el-table-column prop="new_image_url" label="出图目录" width="360" />
-	  <el-table-column prop="old_img_count" label="图片数量" width="90" />
-	  <el-table-column prop="new_image_count" label="总数量" width="80" />
-	  <el-table-column prop="queueLog_model_name" label="执行状态" width="100" />
-	  <el-table-column align="center" label="操作" width="230">
-	  <template #default="{ row, $index }" >
-	    <el-button @click="record_deleteRow(row,$index)" type="primary" size="small" 
-			 style="font-size: 16px;padding: 0px;width: 80px;">
-				查看详情
-		  </el-button>
-		  <el-button @click="packRow(row,$index)" type="primary" size="small"
-		  	 style="font-size: 16px;padding: 0px;width: 100px;">
-		  		打包压缩zip
-		    </el-button>
-	  </template>
-	  </el-table-column>
-	</el-table>
-	
-	
-	<el-dialog v-model="zipview" title="请选择打包尺寸" width="500px" top="20%">
-	  <el-radio-group v-model="zipselectedOption" size="medium">
-	    <el-radio label="1024x1024">1024 x 1024</el-radio>
-	    <!-- <el-radio label="1024x1303">1024 x 1303</el-radio> -->
-	    <el-radio label="图生图">图生图</el-radio>
-	    <el-radio label="高清放大">高清放大</el-radio>
-	  </el-radio-group>
-	  <template #footer>
-	    <div style="text-align: right">
-	      <el-button @click="zipview = false">取消</el-button>
-	      <el-button type="primary" @click="confirmPack">确认打包</el-button>
-	    </div>
-	  </template>
-	</el-dialog>
-		
-		
-	<el-dialog v-model="showDescDialog" title="" width="890px" top="0%">
-	  <div style="display: flex; gap: 10px; max-height: 70vh; overflow: hidden;">
-	    <!-- 左侧图片区域,自适应图片尺寸,可滚动查看完整图片 -->
-		<div style="flex: 1; display: flex; align-items: start; justify-content: center; border: 1px solid #eee; overflow: auto; max-height: 80vh;">
-		  <template v-if="path_image_url">
-		    <el-image
-		        :src="formatImageUrl(path_image_url)"
-		        fit="contain"
-		        style="max-width: 100%; height: auto;"
-		        :preview-src-list="[formatImageUrl(path_image_url)]"
-		        :initial-index="0"
-		      />
-		    </template>
-		  <template v-else>
-			  <el-image
-			      :src="`https://chatapi.onechats.ai/mj/image/${taskId_ids}`"
-			      fit="contain"
-			      style="max-width: 100%; height: auto;"
-			      :preview-src-list="[`https://chatapi.onechats.ai/mj/image/${taskId_ids}`]"
-			      :initial-index="0"
-			    />
-		  </template>
-		</div>
-		
-	    <!-- <div style="flex: 1; display: flex; align-items: start; justify-content: center; border: 1px solid #eee; overflow: auto; max-height: 80vh;">
-	      <el-image
-	        :src="formatImageUrl(path_image_url)"
-	        fit="contain"
-	        style="max-width: 100%; height: auto;"
-	        :preview-src-list="[formatImageUrl(path_image_url)]"
-	        :initial-index="0"
-	      />
-	    </div> -->
-	
-		<!-- 图像描述弹窗内容区域 -->
-		<div style="width: 350px; white-space: pre-wrap; overflow-y: auto; max-height: 63vh; border: 1px solid #f0f0f0; padding: 10px;">
-		  <div v-html="activeDescription"></div>
-		</div>
-
-	  </div>
-	  
-	  <el-descriptions title="" :column="1" border>
-	      <el-descriptions-item v-if="img_id" label="ID">
-	          <span>{{ img_id }}</span>
-	      </el-descriptions-item>
-	  
-	      <el-descriptions-item v-if="img_id" label="模型">
-	          <span>{{ model }}</span>
-	      </el-descriptions-item>
-	  
-
-
-		<template v-if="path_image_url">
-	      <el-descriptions-item label="图片路径" width="50">
-	          <span>{{ path_image_url }}</span>
-	      </el-descriptions-item>
-		</template>
-		<template v-else>
-			<el-descriptions-item label="图片路径" width="50">
-			  <span>https://chatapi.onechats.ai/mj/image/{{taskId_ids}}</span>
-			</el-descriptions-item>
-		</template>
-
-
-	      
-		  
-		  <template v-if="path_image_url">
-		
-		  </template>
-		  <template v-else>
-			  <el-descriptions-item label="模型主图ID" width="50">
-			  <span>{{ taskId_ids }}</span>
-			  <!-- <el-button type="primary" @click="image_seed('1')" style="margin-left: 60px;">获取图一</el-button>
-			  <el-button type="primary" @click="image_seed('2')" style="margin-left: 10px;">获取图二</el-button>
-			  <el-button type="primary" @click="image_seed('3')" style="margin-left: 10px;">获取图三</el-button>
-			  <el-button type="primary" @click="image_seed('4')" style="margin-left: 10px;">获取图四</el-button> -->
-			  </el-descriptions-item>
-		  </template>
-		  
-	  </el-descriptions>
-	</el-dialog>
-	
-	<el-dialog
-	  v-model="TaskqueueVisible"
-	  :before-close="TaskqueueDialog"
-	  title=""
-	  width="95%"
-	  top="4vh"
-	  destroy-on-close
-	>
-		<el-table
-		  :data="table_Taskqueue"
-		  row-key="id"
-		  border
-		  style="width: 100%; height: 60vh;"
-		  v-loading="loading"
-		  highlight-current-row
-		  tooltip-effect="dark"
-		  :row-style="{ height: '40px' }"
-		  :cell-style="{ padding: '4px' }"
-		  :header-cell-style="{ padding: '4px' }"
-		  :header-row-style="{ height: '40px' }"
-		  @selection-change="SelectionChange"
-		  :show-overflow-tooltip="true"
-		>
-		<el-table-column type="selection" width="60" align="center" />
-		<el-table-column prop="id" label="任务ID" width="100" align="center" />
-		<el-table-column prop="task_id" label="子任务ID" width="100" align="center" />
-		<el-table-column prop="log" label="状态" width="160" align="center" />
-		</el-table>
-	</el-dialog>
-	
-	<!-- 模版页面 -->
-	<el-dialog v-model="ListTemplate_Visible"
-		  :before-close="ListTemplateDialog"
-		  title="" width="90%"
-		  style="height: 90%;margin: 2%;padding: 0px;"
-		  top="2vh" destroy-on-close>
-		  <div class="page-container">
-		    <el-descriptions :column="2" border class="queue-status-box">
-		      <!-- 模版选择器 -->
-		      <el-descriptions-item label="选择" :span="2">
-		        <el-row :gutter="10" align="middle">
-		  			
-		          <el-col :span="6">
-		            <el-select v-model="selectedId" placeholder="请选择模版" @change="loadTemplateDetail">
-		              <el-option
-		                v-for="tpl in templateList"
-		                :key="tpl.id"
-		                :label="'模版 ' + tpl.id + (tpl.ids === 1 ? '(使用中)' : '')"
-		                :value="tpl.id"
-		              />
-		            </el-select>
-		          </el-col>
-		          <el-col :span="3">
-		            <el-button type="primary" @click="createNewTemplate">新增模版</el-button>
-		          </el-col>
-		          <el-col :span="4">
-		            <el-button type="success" @click="onsetActive" :disabled="selectedId === usedId">设为当前使用模版</el-button>
-		          </el-col>
-		        </el-row>
-		      </el-descriptions-item>
-		  
-		      <!-- 图生文模版输入区域 -->
-		      <el-descriptions-item label="图生文模版" :span="2">
-		        <textarea
-		          v-model="textareaContent"
-		          placeholder="请输入提示词"
-		          rows="10"
-		          class="custom-textarea"
-		          @keydown.tab.prevent="insertTab"
-		        ></textarea>
-		      </el-descriptions-item>
-		  
-		      <!-- 文生文模版输入区域 -->
-		      <el-descriptions-item label="文生文模版" :span="2">
-		        <textarea
-		          v-model="english_content"
-		          placeholder="请输入提示词"
-		          rows="10"
-		          class="custom-textarea"
-		          @keydown.tab.prevent="insertTab"
-		        ></textarea>
-		      </el-descriptions-item>
-		  
-		      <!-- 出图尺寸输入区域 -->
-		      <el-descriptions-item label="出图尺寸" :span="2">
-		        <el-row :gutter="10" align="middle">
-		          <el-col :span="4">
-		            <el-input
-		              v-model="width"
-		              placeholder="宽度"
-		              size="small"
-		              type="number"
-		              min="1"
-		            />
-		          </el-col>
-		          <el-col :span="1" style="text-align: center;">×</el-col>
-		          <el-col :span="4">
-		            <el-input
-		              v-model="height"
-		              placeholder="高度"
-		              size="small"
-		              type="number"
-		              min="1"
-		            />
-		          </el-col>
-		        </el-row>
-		      </el-descriptions-item>
-		  
-		      <!-- 操作按钮 -->
-		      <el-descriptions-item label="操作" :span="2">
-		        <el-button type="primary" @click="saveTemplate" :loading="mb_isLoading">
-		          保存模版
-		        </el-button>
-				<el-button type="danger" title="" @click="ListTemplateDialog">关 闭</el-button>
-		      </el-descriptions-item>
-		    </el-descriptions>
-		  </div>
-	</el-dialog>
-
-	<!-- 查看详情页面 -->
-	<el-dialog v-model="mlinventoryVisible"
-	  :before-close="mlcloseDialog"
-	  title="" width="100%"
-	  style="height: 100%;margin: 0px;"
-	  top="2vh" destroy-on-close>
-	  <div class="dialog-body">
-		  
-	    <!-- 左侧-->
-	    <div class="dialog-left">
-	      <el-form ref="elSearchFormRef" class="demo-form-inline">
-	        <el-form-item>
-			  <el-button type="danger" title="" @click="details_close">关 闭</el-button>&nbsp;&nbsp;
-			  <el-button type="primary" title="" @click="Refresh_Status" :loading="isLoading">刷新执行状态</el-button>&nbsp;&nbsp;&nbsp;
-			  
-			  <el-button type="primary" title="" @click="ListTemplate_model" :loading="isLoading">查看模版</el-button>&nbsp;&nbsp;&nbsp;
-			  
-			  <span>选择状态:</span>
-	          <el-select
-	            v-model="filterStatus"
-	            placeholder="全部"
-	            @change="handleFilterChange"
-	            style="width: 120px;" >
-	            <el-option label="全部" value="全部" />
-				<el-option label="已出图" value="已出图" />
-	            <el-option label="未出图" value="未出图" />
-				<el-option label="已图生文" value="图生文" />
-				<el-option label="已文生文" value="文生文" />
-				<el-option label="已文生图" value="文生图" />
-				<el-option label="已图生图" value="图生图" />
-				<el-option label="已高清放大" value="高清放大" />
-				<el-option label="未图生文" value="未图生文" />
-				<el-option label="未文生文" value="未文生文" />
-				<el-option label="未文生图" value="未文生图" />
-				<el-option label="未图生图" value="未图生图" />
-	          </el-select>
-	        </el-form-item>
-	      </el-form>
-	
-	      <el-table
-	        :data="tableData_lsit"
-	        row-key="id"
-	        border
-	        style="width: 97%; height: 77vh;"
-	        v-loading="loading"
-	        highlight-current-row
-	        tooltip-effect="dark"
-	        :row-style="{ height: '0px' }"
-	        :cell-style="{ padding: '0px' }"
-	        :header-cell-style="{ padding: '0px' }"
-	        :header-row-style="{ height: '0px' }"
-	        @selection-change="SelectionChange"
-	        :show-overflow-tooltip="true"
-	        @row-click="handleRowClick"
-	      >
-	        <el-table-column type="selection" width="50" align="center" />
-	        <el-table-column prop="id" label="序号" width="59" align="center" />
-	        <el-table-column prop="ids" label="ID" width="70" align="center" />
-	      <el-table-column label="状态" width="83" align="center">
-	        <template #default="{ row }">
-	          <el-tag v-if="row.status === 1" type="success">已出图</el-tag>
-	          <el-tag v-else type="danger">未出图</el-tag>
-	        </template>
-	      </el-table-column>
-	        <el-table-column prop="status_name" label="操作" width="69" align="center" />
-			<el-table-column prop="queue_status" label="执行状态说明" width="140" align="center" />
-	        <!-- <el-table-column prop="same_count" label="出图数量" width="90" align="center" /> -->
-	        <el-table-column label="原图预览" width="120">
-	          <template #default="{ row }">
-	            <img
-	              :src="formatImageUrl(row.path)"
-	              style="width: 90px; height: 70px; object-fit: contain; cursor: pointer;"
-	              @click="showDescription(row, 'old')"
-	            />
-	          </template>
-	        </el-table-column>
-			
-			<el-table-column label="原图尺寸" width="120" align="center">
-			  <template #default="{ row }">
-			    {{ row.width }} x {{ row.height }}
-			  </template>
-			</el-table-column>
-	      </el-table>
-	
-	      <div class="gva-pagination">
-	        <el-pagination
-			style="margin-right: 40px;"
-	          @size-change="handleSizeChange"
-	          @current-change="handleCurrentChange"
-	          :current-page="page"
-	          :page-sizes="[10, 30, 50, 100, 500,1000]"
-	          :page-size="pageSize"
-	          layout="total, sizes, prev, pager, next"
-	          :total="total"
-	        />
-	      </div>
-	    </div>
-	
-	    <!-- 右侧 -->
-	    <div class="dialog-right">
-	      <div class="dialog-right-scroll">
-	        <el-card shadow="hover" body-style="padding: 0px;">
-	          <el-form :model="mlformdata" label-position="top">
-	            <el-descriptions column="1" border>
-					
-					<el-descriptions-item label="操作">
-						<el-button type="primary" :loading="isLoading" title="选择图片,执行图生文" @click="imgtotext" >图生文</el-button>
-						<el-button type="primary" :loading="isLoading" title="选择图片,执行文生文" @click="texttotxt" >文生文</el-button>
-						<el-button type="primary" :loading="isLoading" title="选择图片,执行执行文生图" @click="texttoimg" >文生图</el-button>
-						<el-button type="primary" :loading="isLoading" title="选择图片,执行图生图" @click="imgtoimg" >图生图</el-button>
-						<el-button type="primary" :loading="isLoading" title="选择图片,执行图像高清放大处理" @click="singleimg" >图像高清放大处理</el-button>
-						<el-button type="primary" :loading="isLoading" title="选中图片,执行顺序:图生文->文生文->文生图->图生图->高清放大" @click="handleSelected"  :disabled="isLoading">执选择任务</el-button>
-						<el-button type="primary" :loading="isLoading" title="执行当前所有图片,执行顺序:图生文->文生文->文生图->图生图->高清放大" @click="handleAll" >执行全部任务</el-button>
-					</el-descriptions-item>
-					
-					    <el-descriptions-item label="图生文模型">
-					      <div style="display: flex; align-items: center; gap: 20px;">
-					        <el-radio-group v-model="imgtotxtselectedModel" @change="handleModelChange">
-					          <el-radio 
-					            v-for="item in imgtotxt_modelList" 
-					            :key="item.id"
-					            :label="item.imgtotxt">
-					            {{ item.imgtotxt }} 
-					            <span v-if="item.id === usedIds.tushengwen" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
-					          </el-radio>
-					        </el-radio-group>
-					      </div>
-					      <el-button type="success" @click="setActive('tushengwen')" :disabled="!selectedIds.tushengwen || selectedIds.tushengwen === usedIds.tushengwen">
-					        设为默认使用模型
-					      </el-button>
-					    </el-descriptions-item>
-					
-					    <el-descriptions-item label="文生文模型">
-					      <div style="display: flex; align-items: center; gap: 20px;">
-					        <el-radio-group v-model="txttotxtselectedModel" @change="handleModelChange">
-					          <el-radio 
-					            v-for="item in txttotxt_modelList" 
-					            :key="item.id"
-					            :label="item.txttotxt">
-					            {{ item.txttotxt }} 
-					            <span v-if="item.id === usedIds.wenshengwen" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
-					          </el-radio>
-					        </el-radio-group>
-					      </div>
-					      <el-button type="success" @click="setActive('wenshengwen')" :disabled="!selectedIds.wenshengwen || selectedIds.wenshengwen === usedIds.wenshengwen">
-					        设为默认使用模型
-					      </el-button>
-					    </el-descriptions-item>
-					
-					    <el-descriptions-item label="文生图模型">
-					      <div style="display: flex; align-items: center; gap: 20px;">
-					        <el-radio-group v-model="txtimgselectedModel" @change="handleModelChange">
-					          <el-radio 
-					            v-for="item in txttoimg_modelList" 
-					            :key="item.id"
-					            :label="item.txttoimg">
-					            {{ item.txttoimg }} 
-					            <span v-if="item.id === usedIds.wenshengtu" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
-					          </el-radio>
-					        </el-radio-group>
-					      </div>
-					      <el-button type="success"  @click="setActive('wenshengtu')"  :disabled="!selectedIds.wenshengtu || selectedIds.wenshengtu === usedIds.wenshengtu">
-					        设为默认使用模型
-					      </el-button>
-					    </el-descriptions-item>
-					  
-					  
-					  <el-descriptions-item label="是否执行">
-					     <el-checkbox v-model="executeKeywords">
-					             执行几何图
-					     </el-checkbox>
-					   </el-descriptions-item>
-					  
-					
-					<el-descriptions-item label="出图预览">
-					<div style="display: flex; gap: 20px; align-items: flex-start;">
-					  <!-- 左侧:图片列表区域 -->
-					  <div style="flex: 1;">
-						  
-						  <div class="image-preview-wrap" style="height: 140px;">
-						    <template v-if="taskId_ids">
-						      <div 
-						        class="image-item" 
-								@click="showDescription('new',`https://chatapi.onechats.ai/mj/image/${taskId_ids}`)"
-						      >
-						        <el-image
-						          :src="`https://chatapi.onechats.ai/mj/image/${taskId_ids}`"
-						          fit="contain"
-						          style="max-width: 100%; height: auto;"
-						          :initial-index="0"
-						        />
-						        <span class="image-dimension">2048x2048</span>
-						      </div>
-						    </template>
-						    <template v-else>
-						      <div class="image-grid">
-						        <div
-						          v-for="(img, index) in imageList"
-						          :key="`image-item-${index}`"
-						          class="image-item"
-						          @click="showDescription(img, 'new')"
-						        >
-						          <img :src="formatImageUrl(img.new_image_url)"/>
-						          <span class="image-dimension">1024x1024</span>
-						        </div>
-						      </div>
-						    </template>
-						  </div>
-						  
-						<div class="image-preview-wrap" style="margin-top: 16px; height: 167px;">
-						  <div
-							v-for="(img, index) in imageList"
-							:key="'imgtoimg-' + index"
-							class="image-itemsimg"
-							@click="showDescription(img, 'san')"
-						  >
-							<img :src="formatImageUrl(img.imgtoimg_url)" />图生图</div>
-						</div>
-						
-						<div class="image-preview-wrap" style="margin-top: 16px; height: 140px;">
-						  <div
-							v-for="(img, index) in imageList"
-							:key="'custom-' + index"
-							class="image-items"
-							@click="showDescription(img, 'custom')"
-						  >
-							<img :src="formatImageUrl(img.custom_image_url)" />
-							高清放大
-						  </div>
-						</div>
-					  </div>
-		
-					  <!-- 右侧:文字描述区域 -->
-					  <div
-						style="
-						  width: 700px;
-						  white-space: pre-wrap;
-						  overflow-y: auto;
-						  max-height: 60vh;
-						  border: 1px solid #f0f0f0;
-						  padding: 10px 80px 10px 10px;
-						"
-					  >
-					  <p>第一段(文生图 文字修改区)</p>
-					  <el-input
-					    type="textarea"
-					    v-model="yi_activeDescription"
-					    :rows="6"
-					  />
-					  <p>第二段(文生图 风格修改区)</p>
-					  <el-input
-					    type="textarea"
-					    v-model="er_activeDescription"
-					    :rows="6"
-					  />
-					  <p>图片名称</p>
-					  <el-input
-					    type="textarea"
-					    v-model="name_activeDescription"
-					    :rows="2"
-					  />
-					  <el-button type="primary" @click="submitEdit" style="margin-top: 20px;">修改</el-button>
-					  </div>
-					</div>
-				  </el-descriptions-item>
-					
-	
-	              <el-descriptions-item label="出图尺寸">
-	                <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 12px;">
-	                  <el-input v-model="width" placeholder="宽度" size="small" style="width: 80px;" type="number" min="1" />
-	                  <span style="font-weight: bold;">×</span>
-	                  <el-input v-model="height" placeholder="高度" size="small" style="width: 80px;" type="number" min="1" />
-	                </div>
-	              </el-descriptions-item>
-	
-	         <!--     <el-descriptions-item label="执行次数">
-	                <el-input-number v-model="num" :step="1" />
-	              </el-descriptions-item> -->
-				  
-				  <el-descriptions-item label="任务队列">
-				    <el-table 
-					  :data="table_queue" border
-					  :show-overflow-tooltip="true"
-					  v-loading="loading"
-					  tooltip-effect="dark"
-					  :row-style="{ height: '30px' }"
-					  :cell-style="{ padding: '2px' }"
-					  :header-cell-style="{ padding: '0px' }"
-					  :header-row-style="{ height: '30px' }"
-					  style="width: 100%; height: 21vh;"
-					 >
-				      <el-table-column prop="id" label="ID" width="50" />
-				      <el-table-column prop="status" label="状态" width="100" />
-				      <el-table-column prop="model" label="队列模型" width="170" />
-					  <el-table-column prop="model_name" label="队列模型" width="90" />
-					  <el-table-column prop="image_count" label="执行数量" width="90" />
-					  <el-table-column prop="排队中的数量" label="队列中" width="70" />
-					  <el-table-column prop="处理中数量" label="处理中" width="70" />
-					  <el-table-column prop="已完成数量" label="成功" width="70" />
-					  <el-table-column prop="失败数量" label="失败" width="70" />
-				    </el-table>
-				  </el-descriptions-item>
-				 
-	            </el-descriptions>
-	          </el-form>
-	        </el-card>
-	      </div>
-	    </div>
-	  
-	  
-	  </div>
-	</el-dialog>
-	
-  </div>
-</template>
-
-<script setup>
-import {ref,reactive,toRaw,watch} from 'vue'
-import {ElMessage, ElMessageBox,ElLoading } from 'element-plus'
-import axios from 'axios'
-import {Plus,Delete,CircleCheck,CircleClose} from '@element-plus/icons-vue'
-import {stopQueueProcesses, queueStats, viewQueueStatus,ImgUpload,getPreviewSubDirs,getPreviewimg,getlsit,
-imageToText,txttoimg_moxing,txttoimg_update,
-textToImage,Template_ids,sd_models,getSide,
-TemplateList,
-Template,
-updatetemplate,getPreviewFolders,
-setActiveTemplate,GetHttpUrl,
-packImagess,getUploadPath,get_queue_logs,getTaskProgress
-} from '@/api/mes/job'
-import { useUserStore } from '@/pinia/modules/user';
-
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-//获取服务器地址
-const baseUrl = ref('')
-const port = ref('')
-const full_url = ref('')
-// 从接口获取服务器地址
-const fetchServerUrl = async () => {
-  try {
-    const res = await GetHttpUrl()
-    if (res.code === 0 && res.data && res.data.full_url) {
-      full_url.value = res.data.full_url
-      baseUrl.value = res.data.baseUrl
-      port.value = res.data.port
-	  console.log('获取服务器地址',full_url.value)
-	  console.log('IP地址',baseUrl.value)
-	  console.log('端口',port.value)
-    }
-  } catch (error) {
-    console.error('获取服务器地址失败:', error)
-  }
-}
-fetchServerUrl();
-
-const formatImageUrl = (path) => {
-  if (!path) return ''
-  // 如果path已经是完整URL,直接返回
-  if (path.startsWith('http://') || path.startsWith('https://')) {
-    return path
-  }
-  // 如果path是相对路径,拼接baseUrl
-  return `${full_url.value}/${path.replace(/^public\//, '')}`
-}
-
-
-// --------------------- 图片上传 ---------------------
-const tableData = ref([])
-const getPreviewSubDirslist = async () => {
-	const res = await getPreviewSubDirs()
-	console.log('getPreviewSubDirs',res)
-	const processedData = res.data.map(item => {
-	  return {
-	    ...item,
-	    new_image_url: `${item.new_image_url}${item.name}/`
-	  };
-	});
-	tableData.value = processedData;
-}
-getPreviewSubDirslist()
-
-// 环境配置
-const uploadUrl = ref(`${full_url.value}/api/Facility/ImgUpload`)
-
-// 确保上传URL与服务器地址同步
-watch(full_url, (newUrl) => {
-  uploadUrl.value = `${newUrl}/api/Facility/ImgUpload`
-})
-const uploadHeaders = { 
-  'Content-Type': 'multipart/form-data',
-  'Authorization': 'Bearer ' + localStorage.getItem('token')
-}
-
-// 文件列表
-const fileList = ref([])
-const uploadRef = ref(null)
-
-// 获取本地文件URL
-const getObjectUrl = (file) => {
-  return URL.createObjectURL(file)
-}
-
-// 上传前校验
-const beforeUpload = (file) => {
-  const isImage = file.type === 'image/jpeg' || file.type === 'image/png'
-  const isLt5M = file.size / 1024 / 1024 < 5 // 限制5MB
-  
-  if (!isImage) {
-    ElMessage.error('只能上传 JPG/PNG 格式的图片!')
-    return false
-  }
-  
-  if (!isLt5M) {
-    ElMessage.error('图片大小不能超过 5MB!')
-    return false
-  }
-  
-  // 显示加载中
-  const loading = ElLoading.service({
-    lock: true,
-    text: '正在上传图片...',
-    background: 'rgba(0, 0, 0, 0.7)'
-  })
-  
-  // 将loading实例附加到file对象上,以便后续关闭
-  file.loadingInstance = loading
-  
-  return true
-}
-
-// 自定义上传逻辑
-const customUpload = async (options) => {
-  const { file, onProgress, onSuccess, onError } = options
-  try {
-    const formData = new FormData()
-    formData.append('image', file)
-    const response = await axios.post(uploadUrl.value, formData, {
-      headers: uploadHeaders,
-      onUploadProgress: (progressEvent) => {
-        const percent = Math.round(
-          (progressEvent.loaded * 100) / progressEvent.total
-        )
-        onProgress({ percent }, file)
-      },
-      timeout: 60000 // 60秒超时
-    })
-
-  } catch (error) {
-    
-  } finally {
-    // 关闭加载中
-    if (file.loadingInstance) {
-      file.loadingInstance.close()
-    }
-  }
-}
-
-// 预览图片
-const handlePreview = (file) => {
-  if (file.url) {
-    window.open(file.url, '_blank')
-  } else if (file.raw) {
-    const url = getObjectUrl(file.raw)
-    window.open(url, '_blank')
-    URL.revokeObjectURL(url) // 释放内存
-  }
-}
-
-// 上传成功
-const handleSuccess = (response, file, fileList) => {
-  getPreviewSubDirslist()
-  file.loadingInstance?.close()
-}
-
-// 上传失败
-const handleError = (error, file, fileList) => {
-  console.error('上传错误:', error)
-  ElMessage.error(`${file.name} 上传失败`)
-  file.loadingInstance?.close()
-}
-
-// 超出限制
-const handleExceed = (files, fileList) => {
-  ElMessage.warning(`最多只能上传 30 张图片,您已选择 ${files.length} 张,共 ${files.length + fileList.length} 张`)
-}
-
-
-// --------------------- 查看队列任务 ---------------------
-const queuestats = ref({
-  总任务数: 0,
-  待处理: 0,
-  处理中: 0,
-  成功: 0,
-  失败: 0
-})
-//队列状态
-const queueStats_list = async () => {
-	const res = await queueStats()
-	console.log(res)
-	if (res.code === 0) {
-	    queuestats.value = res.data
-	  }
-}
-queueStats_list()
-
-//停止处理
-const stopQueueLoading = ref(false)
-const stopQueueclick = async () => {
-  if (stopQueueLoading.value) return
-  try {
-    await ElMessageBox.confirm(
-      '确定要停止所有队列任务吗?',
-      '确认操作',
-      {
-        confirmButtonText: '清空队列',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }
-    )
-    stopQueueLoading.value = true
-    const res = await stopQueueProcesses()
-    if (res.code === 0) {
-      ElMessage.success(res.msg || '已成功停止队列任务')
-    }
-  } catch (err) {
-    if (err !== 'cancel') {
-      ElMessage.error('操作失败,请稍后重试')
-    }
-  } finally {
-    stopQueueLoading.value = false
-  }
-}
-
-//任务列表
-const queueDialogVisible = ref(false)
-// 队列数据
-const queueData = ref({
-  tasks_preview: [],
-  count: 0
-})
-const QueueStatusisLoading = ref(false)
-// 任务列表按钮
-const handleViewQueue = async () => {
-	queueStats_list()
-	getPreviewSubDirslist()
-	QueueStatusisLoading.value = true
-  try {
-    const res = await viewQueueStatus()
-    if (res.code === 0) {
-      queueData.value = res
-      queueDialogVisible.value = true
-    } else {
-      ElMessage.error(res.msg || '查询失败')
-    }
-  } catch (err) {
-    ElMessage.error('请求异常')
-  } finally {
-    QueueStatusisLoading.value = false
-  }
-}
-const queueDialog = async () => {
-	queueDialogVisible.value = false
-}
-
-// --------------------- 查看详情 ---------------------
-const fetchPreviewImages = async () => {
-  loading.value = true;
-  const params = {
-	sys_id:userStore.userInfo.nickName,
-    path: _resrow.value.old_image_url,
-    page: page.value,
-    limit: pageSize.value
-  };
-	
-  if (filterStatus.value === '已出图') {
-    params.status = 1
-  } else if (filterStatus.value === '未出图') {
-    params.status = 0
-  }else if(filterStatus.value === '图生文') {
-	  params.status_name = "图生文"
-  }else if(filterStatus.value === '未图生文') {
-	  params.status_name = "未图生文"
-  }else if(filterStatus.value === '文生文') {
-	  params.status_name = "文生文"
-  }else if(filterStatus.value === '未文生文') {
-	  params.status_name = "未文生文"
-  }else if(filterStatus.value === '文生图') {
-	  params.status_name = "文生图"
-  }else if(filterStatus.value === '未文生图') {
-	  params.status_name = "未文生图"
-  }else if(filterStatus.value === '图生图') {
-	  params.status_name = "图生图"
-  }else if(filterStatus.value === '未图生图') {
-	  params.status_name = "未图生图"
-  }else if(filterStatus.value === '高清放大') {
-	  params.status_name = "高清放大"
-  }
-
-  // 获取分页数据
-  const res = await getPreviewimg(params);
-
-  if (res.code === 0) {
-    tableData_lsit.value = res.data;
-    total.value = res.total;
-  }
-  loading.value = false;
-};
-
-// 分页
-const page = ref(1)
-const total = ref(0)
-const pageSize = ref(50)
-// 页码变化
-const handleCurrentChange = (val) => {
-  page.value = val;
-  fetchPreviewImages();
-};
-
-// 每页数量变化
-const handleSizeChange = (val) => {
-  pageSize.value = val;
-  page.value = 1;
-  fetchPreviewImages();
-};
-
-//---------------选择框----------------
-const filterStatus = ref('全部')
-const handleFilterChange = () => {
-  // console.log('当前选择状态:', filterStatus.value);
-  page.value = 1;
-  fetchPreviewImages();
-};
-
-const mlinventoryVisible = ref(false)
-//关闭查看详情页面
-const details_close = async () => {
-	mlinventoryVisible.value = false;
-}
-
-const loading = ref(false)
-const width = ref('')
-const height = ref('')
-const mlformdata = ref({})
-const tableData_lsit = ref([])
-const _resrow = ref('')
-
-const TaskqueueVisible = ref(false)
-const table_queue = ref([])
-const table_Taskqueue = ref([])
-
-//SD模型
-const selectedModel = ref('Realistic_Vision_V5.0-inpainting.safetensors [cf5d9eb09b]')
-const modelList = ref([])
-
-
-
-// 模型列表
-const txttoimg_modelList = ref([]);
-const txtimgselectedModel = ref('');
-const imgtotxt_modelList = ref([]);
-const imgtotxtselectedModel = ref('');
-const txttotxt_modelList = ref([]);
-const txttotxtselectedModel = ref('');
-
-// 当前使用和选择的ID
-const usedIds = ref({
-  wenshengwen: null,
-  tushengwen: null,
-  wenshengtu: null
-});
-
-const selectedIds = ref({
-  wenshengwen: null,
-  tushengwen: null,
-  wenshengtu: null
-});
-
-
-//查看详情按钮
-const record_deleteRow = async (row) => {
-	//打开弹窗
-	mlinventoryVisible.value = true;
-	
-	//获取当前文件夹行信息
-	_resrow.value = row;
-	
-	//清空信息保持数据最新
-	imageList.value = []
-	img_id.value = '';
-	model.value = '';
-	new_image_url.value = '';
-	imgtoimg_url.value = '';
-	path_image_url.value = '';
-	activeDescription.value =  '';
-	yi_activeDescription.value =  '';
-	er_activeDescription.value =  '';
-	name_activeDescription.value =  '';
-	taskId_ids.value =  '';
-	
-	// 获取 SD 模型列表
-	// const sd_models_list = await sd_models();
-	// modelList.value = sd_models_list.data
-		
-	// 任务队列
-	const queue_logs = await get_queue_logs({ old_image_file: row['old_image_url'] });
-	table_queue.value = queue_logs.data;
-	
-	//获取图出图自定义尺寸
-	const TemplateList_res = await TemplateList({path:row['old_image_url']})
-	
-	const res = await Template_ids({path:row['old_image_url']});
-	height.value = res.data.height
-	width.value = res.data.width
-	
-	// 获取模型列表
-	const response = await txttoimg_moxing();
-
-	// 设置模型列表
-	imgtotxt_modelList.value = response.data.models.tushengwen;
-	txttotxt_modelList.value = response.data.models.wenshengwen;
-	txttoimg_modelList.value = response.data.models.wenshengtu;
-
-	// 设置当前使用的ID
-	usedIds.value = response.data.used_ids;
-
-	// 设置默认选中的模型
-	if (txttoimg_modelList.value.length > 0) {
-		const defaultModel = txttoimg_modelList.value.find(item => item.id === usedIds.value.wenshengtu);
-		if (defaultModel) {
-		  txtimgselectedModel.value = defaultModel.txttoimg;
-		  selectedIds.value.wenshengtu = defaultModel.id;
-		}
-	}
-
-	if (imgtotxt_modelList.value.length > 0) {
-		const defaultModel = imgtotxt_modelList.value.find(item => item.id === usedIds.value.tushengwen);
-		if (defaultModel) {
-		  imgtotxtselectedModel.value = defaultModel.imgtotxt;
-		  selectedIds.value.tushengwen = defaultModel.id;
-		}
-	}
-
-	if (txttotxt_modelList.value.length > 0) {
-		const defaultModel = txttotxt_modelList.value.find(item => item.id === usedIds.value.wenshengwen);
-		if (defaultModel) {
-		  txttotxtselectedModel.value = defaultModel.txttotxt;
-		  selectedIds.value.wenshengwen = defaultModel.id;
-		}
-	}
-
-	await fetchPreviewImages();
-}
-
-// 当选择模型变化时
-const handleModelChange = (modelName) => {
-  // 更新文生图模型选择
-    const txttoimg_model = txttoimg_modelList.value.find(item => item.txttoimg === modelName);
-    if (txttoimg_model) {
-      selectedIds.value.wenshengtu = txttoimg_model.id;
-    }
-    
-    // 更新图生文模型选择
-    const imgtotxt_model = imgtotxt_modelList.value.find(item => item.imgtotxt === modelName);
-    if (imgtotxt_model) {
-      selectedIds.value.tushengwen = imgtotxt_model.id;
-    }
-    
-    // 更新文生文模型选择
-    const txttotxt_model = txttotxt_modelList.value.find(item => item.txttotxt === modelName);
-    if (txttotxt_model) {
-      selectedIds.value.wenshengwen = txttotxt_model.id;
-    }
-  
-}
-//设置模型
-const setActive = async (type) => {
-  const id = selectedIds.value[type];
-    if (id) {
-      const result = await txttoimg_update({type:type,id: id});
-      if (result.code === 0) {
-        usedIds.value[type] = id;
-        ElMessage.success('设置成功');
-        
-        // 重新加载模型列表以更新状态
-        const response = await txttoimg_moxing();
-        usedIds.value = response.data.used_ids;
-      } else {
-        ElMessage.error('设置失败');
-      }
-    }
-}
-
-
-const record_queueRow = async (row) => {
-	TaskqueueVisible.value = true;
-	
-	console.log(row['id'])
-	const getTaskProgress_data = await getTaskProgress({ id: row['id'] });
-	table_Taskqueue.value = getTaskProgress_data.data.data;
-}
-
-const TaskqueueDialog = async (row) => {
-	TaskqueueVisible.value = false;
-}
-
-
-const imageList = ref([]);
-const _oldrow = ref('');
-const old_path = ref('');
-const ids = ref('');
-//点击当前行
-const handleRowClick = async (row) => {
-	imgtoimg_url.value = ''
-	activeDescription.value = ''
-	yi_activeDescription.value =  '';
-	er_activeDescription.value =  '';
-	name_activeDescription.value =  '';
-	// fetchPreviewImages();
-	
-	console.log("当前行信息",row)
-	
-	// 文本内容高亮处理
-	const zh = highlightKeywords(row['chinese_description'] || '');
-	const en = highlightKeywords(row['english_description'] || '');
-	const name = highlightKeywords(row['img_name'] || '');
-	
-	activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
-	yi_activeDescription.value =  zh;
-	er_activeDescription.value =  en;
-	name_activeDescription.value =  name;
-	
-	_oldrow.value = row
-	taskId_ids.value = row['taskId']
-	old_path.value = row['path']
-	ids.value = row['ids']
-
-	const res = await getlsit({sys_id:userStore.userInfo.nickName, path: row['path'] });
-	if (res.data === null) {
-		imageList.value = [];
-	} else {
-		imageList.value = res.data.map(item => ({
-		  id: item.id,
-		  taskId: item.taskId,
-		  model: item.model,
-		  chinese_description: item.chinese_description,
-		  english_description: item.english_description,
-		  img_name: item.img_name,
-		  new_image_url: item.new_image_url,
-		  imgtoimg_url:item.imgtoimg_url,
-		  custom_image_url: item.custom_image_url,
-		  size: item.size
-		}));
-		const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
-		table_queue.value = queue_logs.data;
-	}
-};
-
-//关闭按钮
-const mlcloseDialog = () => {
-  mlinventoryVisible.value = false
-}
-
-const submitEdit = async () => {
-    const payload = {
-      id:ids.value,
-      chinese_description:yi_activeDescription.value,
-      english_description:er_activeDescription.value,
-      img_name:name_activeDescription.value
-    };
-    await getSide(payload);
-    ElMessage.success('修改成功');
-	Refresh_Status()
-};
-
-
-const ListTemplate_Visible = ref(false)
-// 模版状态数据
-const templateList = ref([])
-const selectedId = ref(null)
-const usedId = ref(null)
-
-const path_templateList = ref([])
-const path_selectedId = ref(null)
-
-// 模版内容
-const textareaContent = ref('')
-const english_content = ref('')
-// const height = ref('')
-// const width = ref('')
-const mb_isLoading = ref(false)
-
-const ListTemplate_model = async () => {
-	ListTemplate_Visible.value = true
-	console.log(_resrow.value['old_image_url'])
-	const res = await TemplateList({path:_resrow.value['old_image_url']})
-	templateList.value = res.data.list || []
-	usedId.value = res.data.usedId
-	selectedId.value = usedId.value
-	await loadTemplateDetail(selectedId.value)
-}
-//获取模版列表后将默认模版显示
-const loadTemplateDetail = async (id) => {
-	if(id){
-		const res = await Template({ id })
-		textareaContent.value = res.data.content || ''
-		english_content.value = res.data.english_content || ''
-		width.value = res.data.width || ''
-		height.value = res.data.height || ''
-	}
-}
-// 创建新模版(清空内容)
-const createNewTemplate = () => {
-  selectedId.value = null
-  textareaContent.value = '请输入新增模版内容'
-  english_content.value = '请输入新增模版内容'
-  width.value = '1303'
-  height.value = '1024'
-}
-
-// 保存模版(新增或更新)
-const saveTemplate = async () => {
-	
-  if (!textareaContent.value || !width.value || !height.value) {
-    ElMessage.warning('请填写完整模版内容和尺寸')
-    return
-  }
-
-  mb_isLoading.value = true
-  const res = await updatetemplate({
-    id: selectedId.value,
-	path:_resrow.value['old_image_url'],
-    textareaContent: textareaContent.value,
-    english_content: english_content.value,
-    width: width.value,
-    height: height.value
-  })
-  mb_isLoading.value = false
-
-  if (res.code === 0) {
-    ElMessage.success('模版保存成功')
-    loadTemplateList()
-  } else {
-    ElMessage.error('保存失败')
-  }
-}
-
-// 设为当前模版
-const onsetActive = async () => {
-  const res = await setActiveTemplate({ id: selectedId.value,path:path_selectedId.value })
-  if (res.code === 0) {
-    ElMessage.success('已设为当前使用模版')
-    await loadTemplateList()
-  } else {
-    ElMessage.error('设置失败')
-  }
-}
-
-// tab 缩进逻辑
-const insertTab = (event) => {
-  const textarea = event.target
-  const start = textarea.selectionStart
-  const end = textarea.selectionEnd
-  const isShift = event.shiftKey
-  const value = textareaContent.value
-  const before = value.substring(0, start)
-  const selected = value.substring(start, end)
-  const after = value.substring(end)
-
-  if (selected.includes('\n')) {
-    const lines = selected.split('\n')
-    const newText = lines
-      .map(line => (isShift
-        ? (line.startsWith('\t') ? line.substring(1) : line.startsWith('    ') ? line.substring(4) : line)
-        : '\t' + line))
-      .join('\n')
-    textareaContent.value = before + newText + after
-    const offset = newText.length - selected.length
-    nextTick(() => {
-      textarea.selectionStart = start
-      textarea.selectionEnd = end + offset
-    })
-  } else {
-    if (isShift && value.substring(start - 1, start) === '\t') {
-      textareaContent.value = before.slice(0, -1) + selected + after
-      nextTick(() => {
-        textarea.selectionStart = textarea.selectionEnd = start - 1
-      })
-    } else {
-      textareaContent.value = before + '\t' + selected + after
-      nextTick(() => {
-        textarea.selectionStart = textarea.selectionEnd = start + 1
-      })
-    }
-  }
-
-  event.preventDefault()
-}
-//关闭按钮
-const ListTemplateDialog = () => {
-  ListTemplate_Visible.value = false
-}
-
-
-
-/**
- * 点击图片查看显示
-*/
-const yi_activeDescription = ref('');
-const er_activeDescription = ref('');
-const name_activeDescription = ref('');
-const activeDescription = ref('');
-const path_image_url = ref('');
-const taskId_ids = ref('');
-const img_id = ref('');
-const model = ref('');
-const new_image_url = ref('');
-const imgtoimg_url = ref('');
-const showDescDialog = ref(false);
-// 高亮关键词为红色
-const highlightKeywords = (text) => {
-  const keywords = ['几何', 'geometry', 'geometric'];
-  keywords.forEach(word => {
-    const reg = new RegExp(word, 'gi');
-    text = text.replace(reg, match => `<span style="color:red">${match}</span>`);
-  });
-  return text;
-};
-
-// 显示图像描述
-const showDescription = async (img, type,url) => {
-	showDescDialog.value = true;
-	console.log(img)
-	//刷新任务队列-保持实时查看
-	Refresh_Status()
-
-	// 公共字段初始化
-	img_id.value = img.id || '';
-	model.value = img.model || '';
-	path_image_url.value = '';
-
-	// 文本内容高亮处理
-	const zh = highlightKeywords(img.chinese_description || '');
-	const en = highlightKeywords(img.english_description || '');
-	const name = highlightKeywords(img.img_name || '');
-
-	  
-  if (type === 'new') {
-	path_image_url.value = img.new_image_url || '';
-	activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
-	imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
-	taskId_ids.value = taskId_ids.value || '';
-	
-  } else if (type === 'custom') {
-	  
-    path_image_url.value = img.custom_image_url || '图片未生成';
-	activeDescription.value = `第一段:\n${zh}\n\n第二段:\n${en}\n\n第三段:\n${name}`;
-	imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
-	// taskId_ids.value = '';
-	
-  }else if (type === 'san') {
-	  
-    path_image_url.value = img.imgtoimg_url || '图片未生成';
-	imgtoimg_url.value = img.imgtoimg_url || '图片未生成';
-	activeDescription.value =  '';
-	model.value =  '';
-	// taskId_ids.value = '';
-	
-  }else if (type === 'old') {
-	  
-    path_image_url.value = img.path || '暂无说明';
-	imgtoimg_url.value = '';
-	activeDescription.value = '';
-	// taskId_ids.value = '';
-	
-  } else {
-    // console.log('无效的类型');
-  }
-};
-
-const image_seed = async (label) => {
-	console.log(label);
-	console.log(ids.value);
-	console.log(taskId_ids.value);
-	console.log(old_path.value)
-}
-
-
-//复选框
-const _parh = ref('')
-const _queue_status = ref('')
-const SelectionChange = (rows) => {
-	_parh.value = rows.map(item => item.path);
-	_queue_status.value = rows.map(item => item.queue_status);
-};
-
-//查看详情右侧区域
-const isLoading = ref(false)
-//执行次数默认值
-const num = ref(1)
-
-//图生文模型(默认值)
-const imgtotxt_selectedOption = ref('gemini-2.5-flash-lite-preview-06-17')
-//文生文模型(默认值)
-const txttotxt_selectedOption = ref('gemini-2.0-flash')
-//文生图模型(默认值)
-const selectedOption = ref('')
-
-const executeKeywords = ref(false) // 默认未选择
-
-//刷新表格任务状态
-const Refresh_Status = async () => {
-	// 开始 loading
-	loading.value = true;
-	
-	fetchPreviewImages();
-	// 任务队列
-	const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
-	table_queue.value = queue_logs.data;
-	
-	// 延迟2秒再关闭loading
-	await new Promise(resolve => setTimeout(resolve, 3000));
-	isLoading.value = false;
-}
-
-//执行图生文
-const imgtotext = () => {
-	processImages(toRaw(_parh.value),'图生文');
-};
-//执行文生文
-const texttotxt = () => {
-	processImages(toRaw(_parh.value),'文生文');
-};
-//执行文生图
-const texttoimg = async() => {
-    const isProcessing = _queue_status.value.some(
-        status => status.includes("处理中") || status.includes("队列中")
-    );
-    
-    if (isProcessing) {
-		await ElMessageBox.confirm(
-		  '部分数据正在处理中,请稍后再操作!?',
-		  '文生图',
-		  {
-		    confirmButtonText: '确定',
-		    cancelButtonText: '取消',
-		    type: 'warning'
-		  }
-		);
-        return; // 阻止继续执行
-    }
-    
-    // 如果没有任务在执行,才允许调用
-    processImages(toRaw(_parh.value), '文生图');
-};
-//执行图生图
-const imgtoimg = () => {
-	processImages(toRaw(_parh.value),'图生图');
-};
-//执行高清放大
-const singleimg = () => {
-	processImages(toRaw(_parh.value),'高清放大');
-};
-//执行选择任务
-const handleSelected = () => {
-  processImages(toRaw(_parh.value),'');
-};
-//执行全部任务
-const allTableData = ref([]); // 全部数据缓存
-const handleAll = async () => {
-  try {
-    await ElMessageBox.confirm(
-      '确定要执行全部任务吗?',
-      '确认操作',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }
-    );
-
-    // 开始 loading
-    loading.value = true;
-
-    // 发起全量请求
-    const allParams = {
-	  sys_id:userStore.userInfo.nickName,
-      path: _resrow.value.old_image_url,
-      page: 1,
-      limit: 10000
-    };
-   const allRes = await getPreviewimg(allParams);
-   console.log(allRes);
-   
-   if (allRes.code === 0) {
-     // 全部原始数据
-     allTableData.value = allRes.data;
-   
-     // 只保留 status 为 0 且 status_name 为空的项
-     const filteredPaths = toRaw(allTableData.value)
-       .filter(item => item.status === 0 && !item.status_name)
-       .map(item => item.path);
-   
-     processImages(filteredPaths, '');
-   } else {
-     ElMessage.error(allRes.msg || '获取全部图片失败');
-   }
-
-  } catch (err) {
-    if (err !== 'cancel') {
-      ElMessage.error('操作取消');
-    }
-  } finally {
-    // 无论成功/失败/异常,最后关闭 loading
-    loading.value = false;
-  }
-};
-
-
-//操作 统一执行操作按钮处理方法
-const processImages = async (files,type) => {
-	if (!Array.isArray(files) || files.length === 0) {
-		ElMessage.warning('请选择要处理的图片')
-		return
-	}
-	
-	// console.log(imgtotxtselectedModel.value)
-	// console.log(txttotxtselectedModel.value)
-	// console.log(txtimgselectedModel.value)
-	
-	//加载开启
-	isLoading.value = true
-	
-	const failList = []
-	const payload = {
-		old_image_file:_resrow.value['old_image_url'],
-		type:type,
-		imgtotxt_selectedOption:imgtotxtselectedModel.value,
-		txttotxt_selectedOption:txttotxtselectedModel.value,
-		selectedOption:txtimgselectedModel.value,
-		batch:files,
-		num:num.value,
-		width: width.value,
-		height: height.value,
-		executeKeywords:executeKeywords.value,
-		sys_id:userStore.userInfo.nickName
-	}
-	
-	// 打印调试区域
-	// console.log(payload);
-	// await new Promise(resolve => setTimeout(resolve, 2000));
-	// isLoading.value = false;
-	// return;
-	
-	//提交
-	try {
-		const res = await imageToText(payload)
-	} catch (e) {
-		failList.push(`第 ${batchStart + i + 1} 张 第 ${j + 1} 次`)
-	}
-		ElMessage.success(`共处理 ${files.length} 张 * ${num.value} 次,共 ${files.length * num.value} 次`)
-	if (failList.length > 0) {
-		console.warn('失败列表:', failList)
-	}
-	
-	// 延迟2秒再关闭loading
-	await new Promise(resolve => setTimeout(resolve, 3000));
-	isLoading.value = false;
-	
-	//刷新执行状态
-	Refresh_Status()
-	
-	//任务队列刷新
-	const queue_logs = await get_queue_logs({ old_image_file: _resrow.value['old_image_url'] });
-	table_queue.value = queue_logs.data;
-	  
-
-}
-
-// --------------------- 打包zip ---------------------
-const zipview = ref(false)
-const ziprow = ref('')
-const zipselectedOption = ref('图生图') // 默认选中
-//点击打包按钮
-const packRow = async (row) => {
-	zipview.value = true;
-	
-	ziprow.value = row
-}
-
-//确定按钮
-const confirmPack = async () => {
-  isLoading.value = true;
-  // console.log(zipselectedOption.value)
-  // return;
-  try {
-    const res = await getPreviewimg({
-	  sys_id:userStore.userInfo.nickName,
-      path: ziprow.value.old_image_url,
-      page: 1,
-      limit: 10000
-    });
-
-    if (res.code !== 0 || !res.data || res.data.length === 0) {
-      ElMessage.warning('未找到出图记录');
-      return;
-    }
-
-    // 根据选择的选项筛选对应的图片路径
-    const imagePaths = res.data.flatMap(item => {
-      const paths = [];
-      
-      switch(zipselectedOption.value) {
-        case '1024x1024':
-          if (item.new_image_url?.trim()) {
-            paths.push({ type: 'new_image_url', path: item.new_image_url });
-          }
-          break;
-          
-        // case '1024x1303':
-        //   if (item.imgtoimg_url?.trim()) {
-        //     paths.push({ type: 'imgtoimg_url', path: item.imgtoimg_url });
-        //   }
-        //   break;
-          
-        case '高清放大':
-          if (item.custom_image_url?.trim()) {
-            paths.push({ type: 'custom_image_url', path: item.custom_image_url });
-          }
-          break;
-          
-        case '图生图':
-          // 如果图生图需要多个字段,可以在这里添加
-          if (item.imgtoimg_url?.trim()) {
-            paths.push({ type: 'imgtoimg_url', path: item.imgtoimg_url });
-          }
-          // 可以根据需要添加其他字段
-          break;
-          
-        default:
-          // 默认情况下,可以保留原来的逻辑或添加其他处理
-          break;
-      }
-      
-      return paths;
-    });
-
-    if (imagePaths.length === 0) {
-      ElMessage.warning(`没有找到${zipselectedOption.value}尺寸的图片`);
-      return;
-    }
-	// console.log(imagePaths)
-	// return;
-
-    const packRes = await packImagess({ paths: imagePaths });
-    if (packRes.code === 0 && packRes.download_url) {
-      ElMessage.success('打包成功,正在下载...');
-      let url = packRes.download_url;
-      try {
-        const parsedUrl = new URL(url);
-        if (parsedUrl.hostname === '127.0.0.1' || parsedUrl.hostname === 'localhost') {
-          // 使用从接口获取的服务器地址
-          const serverUrl = new URL(full_url.value);
-          parsedUrl.hostname = serverUrl.hostname;
-          parsedUrl.port = serverUrl.port;
-          url = parsedUrl.toString();
-        }
-	   console.log('修改前URL:', packRes.download_url);
-	   console.log('修改后URL:', url);
-	   console.log('使用的服务器地址:', full_url.value);
-      } catch (e) {
-        console.error('URL 解析失败:', e);
-      }
-
-      const link = document.createElement('a');
-      link.href = url;
-      link.setAttribute('download', '');
-      document.body.appendChild(link);
-      link.click();
-      document.body.removeChild(link);
-    } else {
-      ElMessage.error(`打包失败:${packRes.msg || '未知错误'}`);
-    }
-  } catch (err) {
-    ElMessage.error('打包请求异常');
-    console.error(err);
-  } finally {
-    isLoading.value = false;
-    zipview.value = false; // 关闭对话框
-  }
-};
-
-</script>
-<style scoped>
-.page-container {
-  padding: 20px;
-  background: #fff;
-}
-
-.input-container {
-  margin-bottom: 20px;
-}
-
-.gva-table-box {
-  padding-bottom: 20px;
-}
-
-.table-container {
-  margin-top: 20px;
-}
-
-/* 表格行高细节调整 */
-:deep(.el-table td .cell) {
-  line-height: 20px !important;
-}
-
-:deep(.el-tabs__header) {
-  margin-bottom: 0;
-}
-
-/* 选中高亮 */
-:deep(.el-table__body tr.current-row) > td {
-  background: #ff80ff !important;
-}
-
-
-
-/* 新增上传区域样式 */
-.upload-container {
-  margin: 20px 0;
-  padding: 20px;
-  border: 1px dashed #dcdfe6;
-  border-radius: 4px;
-  background-color: #f5f7fa;
-}
-
-.upload-list {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 10px;
-}
-
-.upload-item {
-  position: relative;
-  width: 100px;
-  height: 100px;
-  border-radius: 4px;
-  overflow: hidden;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
-.upload-item-thumbnail {
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-}
-/* 修改上传卡片容器 */
-:deep(.el-upload--picture-card) {
-  --el-upload-picture-card-size: 100px;
-}
-
-:deep(.el-upload-list--picture-card) {
-  --el-upload-list-picture-card-size: 100px;
-}
-
-.upload-item-actions {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 8px;
-  background: rgba(0, 0, 0, 0.6);
-  color: white;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-
-.upload-item-name {
-  flex: 1;
-  font-size: 12px;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  margin-right: 8px;
-}
-
-.upload-progress {
-  font-size: 12px;
-  color: #409eff;
-}
-
-.upload-item-status {
-  margin: 0 8px;
-}
-
-.success-icon {
-  color: #67c23a;
-}
-
-.error-icon {
-  color: #f56c6c;
-}
-
-.upload-item-delete {
-  cursor: pointer;
-  color: #f56c6c;
-}
-
-.upload-item-delete:hover {
-  color: #ff0000;
-}
-
-:deep(.desc-overlay) {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 120px;
-  height: 120px;
-  background: rgba(0, 0, 0, 0.75);
-  color: #fff;
-  font-size: 12px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 8px;
-  box-sizing: border-box;
-  border-radius: 8px;
-  text-align: center;
-  word-break: break-word;
-  line-height: 1.4;
-  z-index: 10;
-}
-
-:deep(.fade-enter-active,
-.fade-leave-active) {
-  transition: opacity 0.3s;
-}
-:deep(.fade-enter-from,
-.fade-leave-to) {
-  opacity: 0;
-}
-
-
-.dialog-scroll-wrapper {
-  max-height: 500px;
-  overflow-y: auto;
-  padding-right: 10px;
-}
-
-.task-card {
-  margin-bottom: 12px;
-}
-
-.task-grid {
-  display: grid;
-  grid-template-columns: 1fr;
-  row-gap: 6px;
-  font-size: 14px;
-  line-height: 1.6;
-}
-
-.task-prompt {
-  word-break: break-word;
-  white-space: pre-wrap;
-}
-
-.task-image {
-  margin-top: 8px;
-}
-.image-preview-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: rgba(0, 0, 0, 0.7);
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  z-index: 9999;
-}
-
-.image-preview {
-  max-width: 90%;
-  max-height: 90%;
-  object-fit: contain;
-  cursor: pointer;
-}
-
-.dialog-body {
-  height: 93vh;
-  display: flex;
-  gap: 0px;
-  overflow: hidden;
-}
-
-/* 左侧表格区域 */
-.dialog-left {
-  flex: 1.4;
-  height: 100%;
-  overflow-y: auto;
-}
-
-/* 右侧详情区域 */
-.dialog-right {
-  flex: 2;
-  height: 100%;
-  overflow: hidden;
-  display: flex;
-  flex-direction: column;
-}
-
-.dialog-right-scroll {
-  flex: 1;
-  overflow-y: auto;
-  padding-right: 5px;
-}
-
-/* 按钮区域自动换行不拥挤 */
-.button-group {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 10px;
-  margin-top: 5px;
-}
-
-/* 图片容器 */
-.image-preview-wrap {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16px;
-}
-.image-item {
-  position: relative;
-  text-align: center;
-  width: 110px;
-}
-.image-item img {
-  width: 120px;
-  height: 120px;
-  border: 1px solid #ccc;
-  object-fit: contain;
-}
-.image-item div {
-  margin-top: 4px;
-  font-size: 12px;
-  color: #666;
-}
-
-.image-items {
-  position: relative;
-  text-align: center;
-  width: 107px;
-}
-.image-items img {
-  width: 95px;
-  height: 120px;
-  border: 1px solid #ccc;
-  object-fit: contain;
-}
-.image-items div {
-  margin-top: 4px;
-  font-size: 12px;
-  color: #666;
-}
-
-.image-itemsimg {
-  position: relative;
-  text-align: center;
-  width: 110px;
-}
-.image-itemsimg img {
-  width: 118px;
-  height: 150px;
-  border: 1px solid #ccc;
-  object-fit: contain;
-}
-.image-itemsimg div {
-  margin-top: 4px;
-  font-size: 12px;
-  color: #666;
-}
-/* 模版样式 */
-.page-container {
-  padding: 20px;
-  background: #fff;
-}
-
-.custom-textarea {
-  width: 100%;
-  font-family: monospace;
-  font-size: 14px;
-  padding: 10px;
-  line-height: 1.6;
-  border: 1px solid #dcdfe6;
-  border-radius: 4px;
-  resize: vertical;
-  box-sizing: border-box;
-}
-
-:deep(.el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell) {
-  width: 100px;
-}
-</style>

+ 0 - 170
src/view/performance/QualityAssessment/patternList.vue

@@ -1,170 +0,0 @@
-<template>
-  <div>
-    <div class="gva-table-box">
-      <el-form :inline="true" style="padding: 20px;">
-        <!-- 店铺下拉 -->
-        <el-form-item label="选择店铺">
-          <el-select v-model="shopValue" placeholder="请选择店铺" clearable style="width: 180px;">
-            <el-option v-for="item in shopOptions" :key="item.value" :label="item.label" :value="item.value" />
-          </el-select>
-        </el-form-item>
-
-        <!-- SKC输入 -->
-        <el-form-item label="SKC编码">
-          <el-input v-model="searchInfo" placeholder="请输入SKC编码" clearable style="width: 180px;" />
-        </el-form-item>
-
-        <!-- 查询按钮 -->
-        <el-form-item>
-          <el-button type="primary" @click="onSubmit">查询</el-button>
-        </el-form-item>
-      </el-form>
-
-      <!-- 表格 -->
-      <el-table
-        ref="multipleTable"
-        style="width: 100%; height: 65vh"
-        :row-style="{ height: '20px' }"
-        :header-cell-style="{ padding: '0px' }"
-        :cell-style="{ padding: '0px' }"
-        :header-row-style="{ height: '20px' }"
-        border
-        tooltip-effect="dark"
-        :data="tableData"
-        row-key="ID"
-        highlight-current-row="true"
-        :cell-class-name="tableDataCellClass"
-        @row-click="Clickonthetable"
-        @row-dblclick="Doubleclickonthetable"
-        @selection-change="cjsSelectionChange"
-        :show-overflow-tooltip="true"
-      >
-        <el-table-column align="center" type="selection" width="30" />
-        <el-table-column align="left" sortable label="订单编号" prop="订单编号" width="105" />
-      </el-table>
-
-      <!-- 分页 -->
-      <div class="gva-pagination">
-        <el-pagination
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-          :current-page="page"
-          :page-sizes="[10, 30, 50, 100]"
-          :page-size="pageSize"
-          layout="total, sizes, prev, pager, next, jumper"
-          :total="total"
-        />
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
-import { useUserStore } from '@/pinia/modules/user'
-import {PatternApi} from '@/api/mes/job'
-defineOptions({ name: '06PackingDocuments' })
-
-// 用户信息
-const userStore = useUserStore()
-const _username = ref(`${userStore.userInfo.userName}/${userStore.userInfo.nickName}`)
-console.log('用户名称:', _username.value)
-
-// 当前日期
-const today = new Date()
-const year = today.getFullYear()
-const month = String(today.getMonth() + 1).padStart(2, '0')
-const day = String(today.getDate()).padStart(2, '0')
-const hours = String(today.getHours()).padStart(2, '0')
-const minutes = String(today.getMinutes()).padStart(2, '0')
-const seconds = String(today.getSeconds()).padStart(2, '0')
-const currentDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
-const currentDates = `${year}-${month}-${day}`
-
-// 下拉框
-const shopValue = ref('')
-const shopOptions = ref([
-  { label: 'hengda xiaodian', value: '634418222212751' },
-  { label: 'LuxePanels', value: 'LuxePanels' }
-])
-
-// 搜索框
-const searchInfo = ref('')
-
-// 表格数据
-const tableData = reactive([])
-
-// 分页
-const total = ref(0)
-const page = ref(1)
-const pageSize = ref(10)
-
-// 查询逻辑
-const onSubmit = async () => {
-  console.log('查询触发:')
-  console.log('店铺:', shopValue.value)
-  console.log('SKC:', searchInfo.value)
-  
-  const res = await PatternApi({ pattern_id: shopValue.value });
-  console.log(res)
-}
-
-// 表格交互
-const Clickonthetable = (row) => {
-  console.log('点击行:', row)
-}
-const Doubleclickonthetable = (row) => {
-  console.log('双击行:', row)
-}
-const cjsSelectionChange = (row) => {
-  console.log('选择变更:', row)
-}
-const tableDataCellClass = () => {
-  return ''
-}
-
-// 分页事件
-const handleCurrentChange = (val) => {
-  page.value = val
-}
-const handleSizeChange = (val) => {
-  pageSize.value = val
-}
-</script>
-
-<style scoped>
-:deep(.el-table__body .warning-row) {
-  background: #FFFF80 !important;
-}
-:deep(.el-table__body tr.current-row)>td {
-  background: #ff80ff !important;
-}
-:deep(.el-table .bg-yellow) {
-  background: yellow;
-}
-:deep(.el-table td .cell) {
-  line-height: 25px !important;
-}
-:deep(.el-tabs__header) {
-  margin-bottom: 0;
-}
-.search {
-  margin-left: 0px !important;
-  margin-right: 10px !important;
-}
-.bt {
-  margin-left: 2px !important;
-  padding: 3px !important;
-  font-size: 12px;
-}
-.el-tabs__header {
-  margin: 0px !important;
-}
-.gva-table-box {
-  padding: 0px !important;
-}
-.mab {
-  margin-bottom: 5px;
-}
-</style>

+ 0 - 583
src/view/performance/QualityAssessment/texttoimage.vue

@@ -1,583 +0,0 @@
-<template>
-  <div class="image-generation-container">
-    <el-card class="mb-4">
-      <template #header>
-          <span>文生图生成</span>
-      </template>
-      <br>
-      <el-form ref="formRef" :model="form" label-width="100px" class="mb-4"
-          style="margin: 0px;padding: 0px !important;">
-        <el-form-item label="提示词" prop="chinese_description">
-          <el-input 
-            v-model="form.chinese_description" 
-            type="textarea" 
-            :rows="3" 
-            placeholder="请输入文生图描述"
-          />
-        </el-form-item>
-        
-        <el-form-item label="模型" prop="model">
-          <div style="display: flex; align-items: center; gap: 20px;">
-            <el-radio-group v-model="txtimgselectedModel" @change="handleModelChange">
-                <el-radio 
-                v-for="item in txttoimg_modelList" 
-                :key="item.id"
-                :label="item.txttoimg">
-                {{ item.txttoimg }} 
-                <span v-if="item.id === usedIds.wenshengtu" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
-                </el-radio>
-            </el-radio-group>
-            <el-button type="success"  @click="setActive('wenshengtu')"  :disabled="!selectedIds.wenshengtu || selectedIds.wenshengtu === usedIds.wenshengtu">
-            设为默认使用模型
-            </el-button>
-          </div>
-        </el-form-item>
-        
-        <el-form-item label="出图尺寸">
-          <div style="display: flex; gap: 10px;">
-            <div style="width: calc(50% - 5px);">
-              <el-input 
-                v-model.number="form.width" 
-                placeholder="宽度" 
-                type="number" 
-                min="1" 
-                style="width: 100%"
-              />
-            </div>
-            <div style="width: calc(50% - 5px);">
-              <el-input 
-                v-model.number="form.height" 
-                placeholder="高度" 
-                type="number" 
-                min="1" 
-                style="width: 100%"
-              />
-            </div>
-          </div>
-        </el-form-item>
-        
-        <el-form-item>
-          <el-button 
-            type="primary" 
-            @click="texttoimg" 
-            :loading="isGenerating"
-          >
-            {{ isGenerating ? '生成中...' : '生成文生图' }}
-          </el-button>
-          <el-button @click="resetForm">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-    
-    <!-- 历史记录 -->
-    <el-card class="mt-1">
-      <el-table :data="historyList"
-                :row-style="{ height: '20px' }"
-                :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
-                :header-cell-style="{ padding: '0px' }"
-                style="width: 100%;height: 40vh">
-        <el-table-column align="center" prop="id" label="ID" width="80" />
-        <el-table-column prop="prompt" label="提示词" width="500">
-          <template #default="scope">
-            <span :title="scope.row.prompt" class="text-ellipsis" style="display: inline-block; width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">{{ scope.row.prompt }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="model" label="模型" width="100" />
-        <el-table-column label="尺寸" width="100">
-          <template #default="scope">
-            {{ scope.row.width }}x{{ scope.row.height }}
-          </template>
-        </el-table-column>
-        <el-table-column prop="sys_rq" label="创建时间" width="180" />
-        <el-table-column label="状态" width="100">
-          <template #default="scope">
-            <el-tag :type="scope.row.web_url ? 'success' : 'warning'">
-              {{ scope.row.web_url ? '已完成' : '生成中' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" width="300">
-            <template #default="scope">
-              <el-button 
-                size="small" 
-                @click="viewHistoryImage(scope.row)"
-                :disabled="!scope.row.web_url"
-              >
-                查看
-              </el-button>
-              <el-button 
-                size="small" 
-                type="primary" 
-                @click="refreshImage(scope.row)"
-                v-if="!scope.row.web_url && scope.row.image_id"
-              >
-                重新获取图片
-              </el-button>
-            </template>
-        </el-table-column>
-      </el-table>
-      <!-- 分页组件 -->
-      <div class="gva-pagination">
-          <el-pagination
-              v-model:current-page="page"
-              v-model:page-size="pageSize"
-              :page-sizes="[10, 30, 50, 100, 500, 1000]"
-              layout="total, sizes, prev, pager, next, jumper"
-              :total="total"
-              @size-change="handleSizeChange"
-              @current-change="handleCurrentChange">
-          </el-pagination>
-        </div>
-    </el-card>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive } from 'vue'
-import { ElMessage } from 'element-plus'
-import { txttoimg_moxing, txttoimg_update,imageToText } from '@/api/mes/job'
-import { useUserStore } from '@/pinia/modules/user';
-
-//获取登录用户信息
-const userStore = useUserStore()
-const _username = ref('')
-_username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
-console.log('获取用户信息',_username.value)
-console.log('获取用户名称',userStore.userInfo.nickName)
-
-// 表单数据
-const form = reactive({
-  chinese_description: '',
-  model: '',
-  width: 1024, // 默认宽度
-  height: 1024 // 默认高度
-})
-
-// 表单引用
-const formRef = ref()
-
-// 页面状态
-const isGenerating = ref(false);
-const dialogVisible = ref(false);
-const dialogImageUrl = ref('');
-const isPreviewLoading = ref(false);
-
-// 历史记录相关
-const historyList = ref([]);
-const page = ref(1);
-const pageSize = ref(10);
-const total = ref(0);
-
-// 模型选择相关变量
-const txtimgselectedModel = ref('');
-const txttoimg_modelList = ref([]);
-const usedIds = ref({});
-const selectedIds = ref({});
-
-// 格式化图片URL
-const formatImageUrl = (path) => {
-  if (!path) return ''
-  
-  // 避免重复拼接http://
-  if (path.startsWith('http://') || path.startsWith('https://')) {
-    return path
-  }
-  
-  const base = 'http://20.0.16.128:9093'
-  // 避免重复拼接斜杠
-  const normalizedPath = path.startsWith('/') ? path.substring(1) : path
-  const normalizedBase = base.endsWith('/') ? base : `${base}/`
-  
-  return `${normalizedBase}${normalizedPath}`
-}
-
-const loadImageHistory = async () => {
-  // 获取模型列表和历史记录
-  const response = await txttoimg_moxing();
-  txttoimg_modelList.value = response.data.models.wenshengtu;
-  usedIds.value = response.data.used_ids;
-  // 设置默认选中的模型
-  if (txttoimg_modelList.value.length > 0) {
-    const defaultModel = txttoimg_modelList.value.find(item => item.id === usedIds.value.wenshengtu);
-    if (defaultModel) {
-      txtimgselectedModel.value = defaultModel.txttoimg;
-      selectedIds.value.wenshengtu = defaultModel.id;
-      form.model = defaultModel.txttoimg;
-    }
-  }
-}
-loadImageHistory()
-
-// 生成图片
-const texttoimg = async () => {
-  if (!form.chinese_description) {
-    ElMessage.warning('请输入提示词')
-    return
-  }
-  
-  // 加载开启
-  isGenerating.value = true
-  
-  try {
-    const payload = {
-        status_val : '文生图',
-        type: '文生图',
-        selectedOption: txtimgselectedModel.value,
-        width: form.width,
-        height: form.height,
-        chinese_description: form.chinese_description,
-        sys_id:userStore.userInfo.nickName
-    }
-
-    // 打印调试信息
-    console.log('生成图片请求参数:', payload)
-    // await new Promise(resolve => setTimeout(resolve, 2000));
-	// isLoading.value = false;
-	// return;
-
-    // 调用图片生成API
-    const res = await imageToText(payload)
-    
-    if (res.code === 200) {
-      ElMessage.success('图片生成任务已提交')
-      // 生成成功后刷新历史记录
-      await loadImageHistory()
-      // 重置表单
-      resetForm()
-    } else {
-      ElMessage.error(res.msg || '图片生成失败')
-    }
-  } catch (error) {
-    console.error('图片生成过程中发生错误:', error)
-    ElMessage.error('图片生成失败,请检查网络连接后重试')
-  } finally {
-    // 关闭加载状态
-    isGenerating.value = false
-  }
-}
-
-// 轮询检查视频生成进度
-let checkInterval = null
-const startCheckingProgress = (videoId) => {
-  if (!videoId) return
-  
-  // 清除之前的轮询
-  if (checkInterval) {
-    clearInterval(checkInterval)
-  }
-  
-  // 最大检查次数,避免无限轮询
-  let checkCount = 0
-  const maxCheckCount = 20 // 最多检查20次(60秒)
-  
-  // 每3秒检查一次
-  checkInterval = setInterval(async () => {
-    checkCount++
-    
-    if (checkCount > maxCheckCount) {
-      ElMessage.info('视频生成可能需要更长时间,请手动刷新查看')
-      clearInterval(checkInterval)
-      return
-    }
-    
-    try {
-      // 使用videoContent接口检查视频状态
-      const statusRes = await videoContent({ video_id: videoId, action: 'status' })
-      
-      if (statusRes.code === 0 && statusRes.data?.status === 'completed' && statusRes.data?.web_url) {
-        // 视频生成完成
-        ElMessage.success('视频生成完成')
-        
-        // 重新加载历史记录
-        await loadVideoHistory()
-        
-        // 查找对应的视频记录
-        const videoItem = historyList.value.find(item => item.video_id === videoId)
-        if (videoItem) {
-          // 自动打开视频播放对话框
-          playHistoryVideo(videoItem)
-        }
-        
-        clearInterval(checkInterval)
-      } else if (statusRes.code !== 0) {
-        console.error('检查视频状态失败', statusRes.msg)
-      }
-    } catch (error) {
-      console.error('检查视频进度失败', error)
-    }
-  }, 3000)
-}
-
-// 重置表单
-const resetForm = () => {
-  form.prompt = ''
-  form.model = 'sora-2'
-  form.seconds = 4
-  form.size = '1280x720'
-  videoUrl.value = ''
-}
-
-// 下载视频
-const downloadVideo = () => {
-  if (!videoUrl.value) return
-  
-  // 创建下载链接
-  const link = document.createElement('a')
-  link.href = videoUrl.value
-  link.download = `video_${Date.now()}.mp4`
-  document.body.appendChild(link)
-  link.click()
-  document.body.removeChild(link)
-  
-  ElMessage.success('视频下载中')
-}
-
-// 下载历史视频
-const downloadHistoryVideo = (row) => {
-  if (!row.web_url) {
-    ElMessage.warning('视频尚未生成完成')
-    return
-  }
-  
-  const videoUrl = formatVideoUrl(row.web_url)
-  const link = document.createElement('a')
-  link.href = videoUrl
-  link.download = `video_${row.video_id || Date.now()}.mp4`
-  document.body.appendChild(link)
-  link.click()
-  document.body.removeChild(link)
-  
-  ElMessage.success('视频下载中')
-}
-
-// 下载对话框中的视频
-const downloadDialogVideo = () => {
-  if (!currentVideoItem.value || !currentVideoItem.value.web_url) return
-  
-  downloadHistoryVideo(currentVideoItem.value)
-}
-
-// 重新生成
-const regenerateVideo = () => {
-  generateVideo()
-}
-
-// 播放历史视频
-const playHistoryVideo = (row) => {
-  if (!row.web_url) {
-    ElMessage.warning('视频尚未生成完成')
-    return
-  }
-  
-  // 设置当前视频项和URL
-  currentVideoItem.value = row
-  dialogVideoUrl.value = formatVideoUrl(row.web_url)
-  
-  console.log('正在播放视频:', dialogVideoUrl.value)
-  
-  // 显示对话框
-  dialogVisible.value = true
-  
-  // 填充表单
-  // form.prompt = row.prompt
-  // form.model = row.model
-  // form.seconds = row.seconds
-  // form.size = row.size
-}
-
-// 重新获取视频
-const refreshVideo = async (row) => {
-  if (!row.video_id) {
-    ElMessage.warning('视频ID不存在,无法重新获取')
-    return
-  }
-    ElMessage.info('正在重新获取视频...')
-    // 调用videoContent接口重新获取视频
-    const res = await videoContent({ video_id: row.video_id })
-    // 无论获取结果如何,都刷新表格数据
-    await loadVideoHistory()
-    if (res.code === 0) {
-      // 如果获取到了视频URL
-      if (res.data && res.data.web_url) {
-        ElMessage.success('视频获取成功')
-        
-        // 查找更新后的视频项
-        const updatedVideo = historyList.value.find(item => item.video_id === row.video_id)
-        if (updatedVideo && updatedVideo.web_url) {
-          // 自动播放获取到的视频
-          playHistoryVideo(updatedVideo)
-        }
-      } else {
-        ElMessage.info('视频仍在生成中,请稍后再试')
-      }
-    }
-}
-
-// 处理模型选择变化
-const handleModelChange = (modelName) => {
-  // 找到当前选中模型的ID
-  const selectedModel = txttoimg_modelList.value.find(item => item.txttoimg === modelName)
-  if (selectedModel) {
-    selectedIds.value.wenshengtu = selectedModel.id
-    form.model = modelName
-  }
-}
-
-// 设置默认模型
-const setActive = async (type) => {
-  const id = selectedIds.value[type]
-  if (id) {
-    try {
-      // 调用API设置默认模型
-      const res = await txttoimg_update({ id: id,type:"wenshengtu"})
-      if (res.code === 0) {
-        usedIds.value[type] = id
-        ElMessage.success('默认模型设置成功')
-      } else {
-        ElMessage.error(res.msg || '设置默认模型失败')
-      }
-    } catch (error) {
-      console.error('设置默认模型失败', error)
-      ElMessage.error('设置默认模型失败')
-    }
-  }
-}
-
-// 搜索处理
-const handleSearch = () => {
-  // 重置页码为1
-  page.value = 1
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 分页大小变化处理
-const handleSizeChange = (size) => {
-  pageSize.value = size
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 当前页码变化处理
-const handleCurrentChange = (current) => {
-  page.value = current
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 删除历史视频
-const deleteHistoryVideo = async (id) => {
-  try {
-    // 这里应该调用实际的删除API
-    // const res = await deleteVideoApi({ id })
-    // if (res.code === 0) {
-      const index = historyList.value.findIndex(item => item.id === id)
-      if (index !== -1) {
-        historyList.value.splice(index, 1)
-        ElMessage.success('删除成功')
-      }
-    // }
-  } catch (error) {
-    console.error('删除视频失败', error)
-    ElMessage.error('删除失败,请重试')
-  }
-}
-</script>
-
-<style scoped lang="scss">
-
-::v-deep(.el-card__body){
-	padding: 0px;
-}
-.video-generation-container {
-  padding: 0px;
-  
-  .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-  
-  .refresh-button {
-    margin-left: auto;
-  }
-}
-
-.el-form {
-  .el-form-item {
-    margin-bottom: 10px;
-    padding: 0px;
-  }
-}
-
-.el-table {
-  .el-button {
-    margin-right: 5px;
-  }
-}
-
-// 对话框视频样式
-.dialog-video-preview {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  min-height: 400px;
-  padding: 20px 0;
-  
-  video {
-    max-height: 600px;
-    max-width: 100%;
-    border-radius: 4px;
-  }
-  
-  .loading-text {
-    font-size: 16px;
-    color: #909399;
-  }
-}
-
-// 最新生成结果样式
-.video-result-preview {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 10px;
-  background-color: #fafafa;
-  border-radius: 4px;
-  
-  .video-result-info {
-    flex: 1;
-    p {
-      margin: 5px 0;
-      font-size: 14px;
-    }
-  }
-  
-  .video-result-actions {
-    display: flex;
-    gap: 10px;
-  }
-}
-
-// 响应式调整
-@media screen and (max-width: 768px) {
-  .dialog-video-preview {
-    min-height: 300px;
-    
-    video {
-      max-height: 400px;
-    }
-  }
-  
-  .video-result-preview {
-    flex-direction: column;
-    align-items: flex-start;
-    gap: 15px;
-    
-    .video-result-actions {
-      width: 100%;
-      justify-content: flex-start;
-    }
-  }
-}
-</style>

+ 0 - 622
src/view/performance/QualityAssessment/video.vue

@@ -1,622 +0,0 @@
-<template>
-  <div class="video-generation-container">
-    <el-card class="mb-4">
-      <template #header>
-          <span>文生视频生成</span>
-      </template>
-      <br>
-      <el-form ref="formRef" :model="form" label-width="100px" class="mb-4"
-          style="margin: 0px;padding: 0px !important;">
-        <el-form-item label="提示词" prop="prompt" required>
-          <el-input 
-            v-model="form.prompt" 
-            type="textarea" 
-            :rows="3" 
-            placeholder="请输入视频描述,例如:一个穿着宇航服的宇航员在月球上行走, 高品质, 电影级"
-          />
-        </el-form-item>
-        
-        <el-form-item label="模型" prop="model">
-          <el-select v-model="form.model" placeholder="请选择模型">
-            <el-option label="sora-2" value="sora-2" />
-          </el-select>
-        </el-form-item>
-        
-        <el-form-item label="时长(秒)" prop="seconds">
-          <el-select 
-            v-model.number="form.seconds" 
-            placeholder="请选择视频时长" 
-            style="width: 100%"
-          >
-            <el-option label="4秒" :value="4" />
-            <el-option label="8秒" :value="8" />
-            <el-option label="12秒" :value="12" />
-          </el-select>
-        </el-form-item>
-        
-        <el-form-item label="分辨率" prop="size">
-          <el-select v-model="form.size" placeholder="请选择分辨率" style="width: 100%">
-            <el-option label="720x1280" value="720x1280" />
-            <el-option label="1280x720" value="1280x720" />
-          </el-select>
-        </el-form-item>
-        
-        <el-form-item>
-           <!-- <el-input 
-              v-model="searchInfo" 
-              placeholder="搜索提示词" 
-              @keyup.enter="handleSearch"
-              clearable
-              @clear="handleSearch"
-              style="width: 230px; margin-right: 10px;margin: 0px;" 
-            />
-            <el-button type="primary" icon="Search" class="search" @click="handleSearch">查询</el-button> -->
-          <el-button 
-            type="primary" 
-            @click="generateVideo" 
-            :loading="isGenerating"
-          >
-            {{ isGenerating ? '生成中...' : '生成视频' }}
-          </el-button>
-          <el-button @click="resetForm">重置</el-button>
-        </el-form-item>
-      </el-form>
-    </el-card>
-    
-    <!-- 视频播放对话框 -->
-    <el-dialog
-      v-model="dialogVisible"
-      title="视频播放"
-      width="80%"
-	  style="margin: 0px;margin-left: 10%;margin-top: 3%;"
-      center
-      destroy-on-close
-    >
-      <div class="dialog-video-preview">
-        <div v-if="!dialogVideoUrl" class="loading-text">加载中...</div>
-        <video 
-          :src="dialogVideoUrl" 
-          controls 
-          width="100%" 
-          height="auto"
-          v-else
-          autoplay
-        ></video>
-      </div>
-      <template #footer>
-        <el-button @click="dialogVisible = false">关闭</el-button>
-      </template>
-    </el-dialog>
-    
-    <!-- 历史记录 -->
-    <el-card class="mt-1">
-      <el-table :data="historyList"
-                :row-style="{ height: '20px' }"
-                :cell-style="{ padding: '0px' }" :header-row-style="{ height: '20px' }"
-                :header-cell-style="{ padding: '0px' }"
-                style="width: 100%;height: 40vh">
-        <el-table-column align="center" prop="id" label="ID" width="80" />
-        <el-table-column prop="prompt" label="提示词" width="500">
-          <template #default="scope">
-            <span :title="scope.row.prompt" class="text-ellipsis" style="display: inline-block; width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">{{ scope.row.prompt }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="model" label="模型" width="100" />
-        <el-table-column prop="seconds" label="时长" width="80" />
-        <el-table-column prop="size" label="分辨率" width="100" />
-        <el-table-column prop="sys_rq" label="创建时间" width="180" />
-        <el-table-column label="状态" width="100">
-          <template #default="scope">
-            <el-tag :type="scope.row.web_url ? 'success' : 'warning'">
-              {{ scope.row.web_url ? '已完成' : '生成中' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" width="300">
-            <template #default="scope">
-              <el-button 
-                size="small" 
-                @click="playHistoryVideo(scope.row)"
-                :disabled="!scope.row.web_url"
-              >
-                播放
-              </el-button>
-              <el-button 
-                size="small" 
-                type="primary" 
-                @click="refreshVideo(scope.row)"
-                v-if="!scope.row.web_url && scope.row.video_id"
-              >
-                重新获取视频
-              </el-button>
-              <!-- <el-button size="small" type="danger" @click="deleteHistoryVideo(scope.row.id)">删除</el-button> -->
-            </template>
-        </el-table-column>
-      </el-table>
-      <!-- 分页组件 -->
-      <div class="gva-pagination">
-          <el-pagination
-              v-model:current-page="page"
-              v-model:page-size="pageSize"
-              :page-sizes="[10, 30, 50, 100, 500, 1000]"
-              layout="total, sizes, prev, pager, next, jumper"
-              :total="total"
-              @size-change="handleSizeChange"
-              @current-change="handleCurrentChange">
-          </el-pagination>
-        </div>
-    </el-card>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive } from 'vue'
-import { ElMessage } from 'element-plus'
-import {Getvideolist,video,videoContent
-} from '@/api/mes/job'
-
-// 表单数据
-const form = reactive({
-  prompt: '',
-  model: 'sora-2',
-  seconds: 4,
-  size: '1280x720'
-})
-
-// 表单引用
-const formRef = ref()
-
-// 状态变量
-const isGenerating = ref(false)
-const videoUrl = ref('')
-const dialogVisible = ref(false)
-const dialogVideoUrl = ref('')
-const currentVideoItem = ref(null)
-const historyList = ref([])
-// 分页相关数据
-const page = ref(1)
-const pageSize = ref(30)
-const total = ref(0)
-// 搜索关键词
-const searchInfo = ref('')
-
-// 格式化视频URL
-const formatVideoUrl = (path) => {
-  if (!path) return ''
-  
-  // 避免重复拼接http://
-  if (path.startsWith('http://') || path.startsWith('https://')) {
-    return path
-  }
-  
-  const base = 'http://20.0.16.128:9093'
-  // 避免重复拼接斜杠
-  const normalizedPath = path.startsWith('/') ? path.substring(1) : path
-  const normalizedBase = base.endsWith('/') ? base : `${base}/`
-  
-  return `${normalizedBase}${normalizedPath}`
-}
-
-// 加载历史视频列表
-const loadVideoHistory = async () => {
-  try {
-    const params = {
-      page: page.value,
-      limit: pageSize.value
-    }
-    // 如果有搜索关键词,添加到请求参数中
-    if (searchInfo.value.trim()) {
-      params.search = searchInfo.value.trim()
-    }
-    
-    const res = await Getvideolist(params)
-    if (res.code === 0) {
-      historyList.value = Array.isArray(res.data.list) ? res.data.list : res.data
-      // 如果后端返回total,使用返回值,否则保持现有值
-      // 设置总数
-      total.value = res.count || 0
-    }
-  } catch (error) {
-    console.error('加载视频历史失败', error)
-    ElMessage.error('加载历史记录失败')
-  }
-}
-
-// 组件挂载时加载历史
-loadVideoHistory()
-
-// 生成视频
-const generateVideo = async () => {
-// console.log(form);
-// return false;
-  // 表单验证
-  if (!form.prompt) {
-    ElMessage.warning('请输入提示词')
-    return
-  }
-  
-  isGenerating.value = true
-  
-  try {
-    // 第一步:调用video接口生成视频ID
-    const videoIdRes = await video(form)
-    
-    if (videoIdRes.code === 0 && videoIdRes.data?.video_id) {
-      const videoId = videoIdRes.data.video_id
-      ElMessage.success('已提交视频生成请求,正在生成中...')
-      
-      // 第二步:使用videoContent接口根据ID生成视频
-      // 这里添加一个延迟,确保视频ID已经在后端系统中就绪
-      await new Promise(resolve => setTimeout(resolve, 1000))
-      
-      // 调用videoContent接口
-      const generateRes = await videoContent({ video_id: videoId })
-      
-      // 无论成功与否,都重新加载历史记录表格
-      await loadVideoHistory()
-      
-      if (generateRes.code === 0) {
-        // 如果视频已生成完成
-        if (generateRes.data && generateRes.data.web_url) {
-          ElMessage.success('视频生成成功')
-          
-          // 查找生成的视频项
-          const newVideoItem = historyList.value.find(item => item.video_id === videoId)
-          if (newVideoItem) {
-            // 自动打开视频播放对话框
-            playHistoryVideo(newVideoItem)
-          }
-        } else {
-          ElMessage.info('视频正在生成中,请稍后刷新查看')
-          
-          // 启动轮询检查进度
-          startCheckingProgress(videoId)
-        }
-      } else {
-        // 提取更详细的错误信息
-        let errorMsg = generateRes.msg || '未知错误'
-        if (generateRes.data && generateRes.data.error_message) {
-          try {
-            // 尝试解析JSON格式的错误信息
-            const errorData = JSON.parse(generateRes.data.error_message)
-            if (errorData.error && errorData.error.message) {
-              errorMsg += ' - ' + errorData.error.message
-            }
-          } catch (e) {
-            // 如果解析失败,直接使用原始错误信息
-            errorMsg += ' - ' + generateRes.data.error_message
-          }
-        }
-        
-        ElMessage.error('视频生成失败:' + errorMsg)
-      }
-    } else {
-      // 即使创建任务失败,也刷新表格数据
-      await loadVideoHistory()
-      
-      // 提取更详细的错误信息
-      let errorMsg = videoIdRes.msg || '未知错误'
-      if (videoIdRes.data && videoIdRes.data.error_message) {
-        try {
-          // 尝试解析JSON格式的错误信息
-          const errorData = JSON.parse(videoIdRes.data.error_message)
-          if (errorData.error && errorData.error.message) {
-            errorMsg += ' - ' + errorData.error.message
-          }
-        } catch (e) {
-          // 如果解析失败,直接使用原始错误信息
-          errorMsg += ' - ' + videoIdRes.data.error_message
-        }
-      }
-      
-      ElMessage.error('创建视频任务失败:' + errorMsg)
-    }
-  } catch (error) {
-    console.error('视频生成过程中发生错误', error)
-    // 发生异常也尝试刷新表格
-    try {
-      await loadVideoHistory()
-    } catch (e) {
-      console.error('刷新表格失败', e)
-    }
-    ElMessage.error('视频生成失败,请检查网络连接后重试')
-  } finally {
-    isGenerating.value = false
-  }
-}
-
-// 轮询检查视频生成进度
-let checkInterval = null
-const startCheckingProgress = (videoId) => {
-  if (!videoId) return
-  
-  // 清除之前的轮询
-  if (checkInterval) {
-    clearInterval(checkInterval)
-  }
-  
-  // 最大检查次数,避免无限轮询
-  let checkCount = 0
-  const maxCheckCount = 20 // 最多检查20次(60秒)
-  
-  // 每3秒检查一次
-  checkInterval = setInterval(async () => {
-    checkCount++
-    
-    if (checkCount > maxCheckCount) {
-      ElMessage.info('视频生成可能需要更长时间,请手动刷新查看')
-      clearInterval(checkInterval)
-      return
-    }
-    
-    try {
-      // 使用videoContent接口检查视频状态
-      const statusRes = await videoContent({ video_id: videoId, action: 'status' })
-      
-      if (statusRes.code === 0 && statusRes.data?.status === 'completed' && statusRes.data?.web_url) {
-        // 视频生成完成
-        ElMessage.success('视频生成完成')
-        
-        // 重新加载历史记录
-        await loadVideoHistory()
-        
-        // 查找对应的视频记录
-        const videoItem = historyList.value.find(item => item.video_id === videoId)
-        if (videoItem) {
-          // 自动打开视频播放对话框
-          playHistoryVideo(videoItem)
-        }
-        
-        clearInterval(checkInterval)
-      } else if (statusRes.code !== 0) {
-        console.error('检查视频状态失败', statusRes.msg)
-      }
-    } catch (error) {
-      console.error('检查视频进度失败', error)
-    }
-  }, 3000)
-}
-
-// 重置表单
-const resetForm = () => {
-  form.prompt = ''
-  form.model = 'sora-2'
-  form.seconds = 4
-  form.size = '1280x720'
-  videoUrl.value = ''
-}
-
-// 下载视频
-const downloadVideo = () => {
-  if (!videoUrl.value) return
-  
-  // 创建下载链接
-  const link = document.createElement('a')
-  link.href = videoUrl.value
-  link.download = `video_${Date.now()}.mp4`
-  document.body.appendChild(link)
-  link.click()
-  document.body.removeChild(link)
-  
-  ElMessage.success('视频下载中')
-}
-
-// 下载历史视频
-const downloadHistoryVideo = (row) => {
-  if (!row.web_url) {
-    ElMessage.warning('视频尚未生成完成')
-    return
-  }
-  
-  const videoUrl = formatVideoUrl(row.web_url)
-  const link = document.createElement('a')
-  link.href = videoUrl
-  link.download = `video_${row.video_id || Date.now()}.mp4`
-  document.body.appendChild(link)
-  link.click()
-  document.body.removeChild(link)
-  
-  ElMessage.success('视频下载中')
-}
-
-// 下载对话框中的视频
-const downloadDialogVideo = () => {
-  if (!currentVideoItem.value || !currentVideoItem.value.web_url) return
-  
-  downloadHistoryVideo(currentVideoItem.value)
-}
-
-// 重新生成
-const regenerateVideo = () => {
-  generateVideo()
-}
-
-// 播放历史视频
-const playHistoryVideo = (row) => {
-  if (!row.web_url) {
-    ElMessage.warning('视频尚未生成完成')
-    return
-  }
-  
-  // 设置当前视频项和URL
-  currentVideoItem.value = row
-  dialogVideoUrl.value = formatVideoUrl(row.web_url)
-  
-  console.log('正在播放视频:', dialogVideoUrl.value)
-  
-  // 显示对话框
-  dialogVisible.value = true
-  
-  // 填充表单
-  // form.prompt = row.prompt
-  // form.model = row.model
-  // form.seconds = row.seconds
-  // form.size = row.size
-}
-
-// 重新获取视频
-const refreshVideo = async (row) => {
-  if (!row.video_id) {
-    ElMessage.warning('视频ID不存在,无法重新获取')
-    return
-  }
-    ElMessage.info('正在重新获取视频...')
-    // 调用videoContent接口重新获取视频
-    const res = await videoContent({ video_id: row.video_id })
-    // 无论获取结果如何,都刷新表格数据
-    await loadVideoHistory()
-    if (res.code === 0) {
-      // 如果获取到了视频URL
-      if (res.data && res.data.web_url) {
-        ElMessage.success('视频获取成功')
-        
-        // 查找更新后的视频项
-        const updatedVideo = historyList.value.find(item => item.video_id === row.video_id)
-        if (updatedVideo && updatedVideo.web_url) {
-          // 自动播放获取到的视频
-          playHistoryVideo(updatedVideo)
-        }
-      } else {
-        ElMessage.info('视频仍在生成中,请稍后再试')
-      }
-    }
-}
-
-// 搜索处理
-const handleSearch = () => {
-  // 重置页码为1
-  page.value = 1
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 分页大小变化处理
-const handleSizeChange = (size) => {
-  pageSize.value = size
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 当前页码变化处理
-const handleCurrentChange = (current) => {
-  page.value = current
-  // 重新加载数据
-  loadVideoHistory()
-}
-
-// 删除历史视频
-const deleteHistoryVideo = async (id) => {
-  try {
-    // 这里应该调用实际的删除API
-    // const res = await deleteVideoApi({ id })
-    // if (res.code === 0) {
-      const index = historyList.value.findIndex(item => item.id === id)
-      if (index !== -1) {
-        historyList.value.splice(index, 1)
-        ElMessage.success('删除成功')
-      }
-    // }
-  } catch (error) {
-    console.error('删除视频失败', error)
-    ElMessage.error('删除失败,请重试')
-  }
-}
-</script>
-
-<style scoped lang="scss">
-
-::v-deep(.el-card__body){
-	padding: 0px;
-}
-.video-generation-container {
-  padding: 0px;
-  
-  .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-  
-  .refresh-button {
-    margin-left: auto;
-  }
-}
-
-.el-form {
-  .el-form-item {
-    margin-bottom: 10px;
-    padding: 0px;
-  }
-}
-
-.el-table {
-  .el-button {
-    margin-right: 5px;
-  }
-}
-
-// 对话框视频样式
-.dialog-video-preview {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  min-height: 400px;
-  padding: 20px 0;
-  
-  video {
-    max-height: 600px;
-    max-width: 100%;
-    border-radius: 4px;
-  }
-  
-  .loading-text {
-    font-size: 16px;
-    color: #909399;
-  }
-}
-
-// 最新生成结果样式
-.video-result-preview {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 10px;
-  background-color: #fafafa;
-  border-radius: 4px;
-  
-  .video-result-info {
-    flex: 1;
-    p {
-      margin: 5px 0;
-      font-size: 14px;
-    }
-  }
-  
-  .video-result-actions {
-    display: flex;
-    gap: 10px;
-  }
-}
-
-// 响应式调整
-@media screen and (max-width: 768px) {
-  .dialog-video-preview {
-    min-height: 300px;
-    
-    video {
-      max-height: 400px;
-    }
-  }
-  
-  .video-result-preview {
-    flex-direction: column;
-    align-items: flex-start;
-    gap: 15px;
-    
-    .video-result-actions {
-      width: 100%;
-      justify-content: flex-start;
-    }
-  }
-}
-</style>

+ 1581 - 0
src/view/zs/laowozhengshudayin.vue

@@ -0,0 +1,1581 @@
+<template>
+  <div>
+    <layout>
+      <layout-header>
+        <div class="top-action-bar no-print">
+          <el-form class="demo-form-inline">
+            <el-form-item>
+              <div class="action-buttons">
+                <!-- 打印按钮 -->
+                <el-button type="primary" @click="printCertificate" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2H5zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/>
+                    <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2V7zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>
+                  </svg>
+                  打印证书
+                </el-button>
+                
+                <!-- PDF导出按钮 -->
+                <el-button type="primary" @click="exportPDF" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M8.5 1.5A.5.5 0 0 1 9 1h1v2.5A1.5 1.5 0 0 1 8.5 5H10a.5.5 0 0 1 0 1H8.5A1.5 1.5 0 0 1 7 5.5V4H5a1.5 1.5 0 0 1-1.5-1.5V1h1a.5.5 0 0 1 .5.5zM6 3.75V1H2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H6z"/>
+                    <path d="M14 14V4.5A1.5 1.5 0 0 0 12.5 3H11a.5.5 0 0 0 0 1h1.5a.5.5 0 0 1 .5.5V14a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5V4.5A1.5 1.5 0 0 1 2.5 3H4a.5.5 0 0 0 0-1H2.5A2.5 2.5 0 0 0 0 4.5v10A2.5 2.5 0 0 0 2.5 17h11a2.5 2.5 0 0 0 2.5-2.5V4.5a.5.5 0 0 0-.5-.5H14z"/>
+                  </svg>
+                  PDF导出
+                </el-button>
+                
+                <!-- 编辑按钮 -->
+                <el-button type="primary" @click="toggleEditMode" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
+                  </svg>
+                  {{ isEditing ? '完成编辑' : '编辑证书' }}
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+
+      <layout>
+        <!-- 左侧树状图区域 -->
+        <layout-sider :resize-directions="['right']" :width="300" v-if="showTree">
+          <div class="tree-section">
+            <div class="tree-header">
+              <h3>单位选择</h3>
+            </div>
+            <div class="tree-wrapper">
+              <el-tree
+                ref="treeRef"
+                :data="treeData"
+                node-key="code"
+                show-checkbox
+                :props="defaultProps"
+                @check="handleCheck"
+                highlight-current
+                :default-expand-all="true"
+                :check-strictly="true"
+              >
+                <template #default="{ node, data }">
+                  <span class="tree-node">
+                    <span>{{ data.name }}</span>
+                    <span v-if="data.GCP" class="tree-gcp">{{ data.GCP }}</span>
+                  </span>
+                </template>
+              </el-tree>
+            </div>
+            <div class="selected-info" v-if="selectedUnit">
+              已选择:{{ selectedUnit.name }} ({{ selectedUnit.GCP }})
+            </div>
+          </div>
+        </layout-sider>
+
+        <!-- 右侧证书区域 -->
+        <layout-content>
+          <div class="certificate-content">
+            <div class="cert-wrapper" ref="certificateWrapper">
+              <!-- 老挝证书样式 -->
+              <div class="lao-certificate">
+                <!-- 证书背景图片 -->
+                <div class="certificate-background">
+                  <img src="http://20.0.16.87:9094/uploads/certificate/lw.png" alt="老挝证书背景" class="bg-image">
+                </div>
+                
+                <!-- 可编辑字段 -->
+                <div class="certificate-fields">
+                  <!-- License No. -->
+                  <div class="field-container license-no-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'code'"
+                      class="field license-number"
+                      @click="startEditing('code')"
+                      :style="{ width: codeWidth }"
+                      ref="codeRef"
+                    >
+                      {{ editableData.code }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.code"
+                      class="field-input code-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="codeInput"
+                      :style="{ width: codeInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- Name -->
+                  <div class="field-container name-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'name'"
+                      class="field lao-name-field"
+                      @click="startEditing('name')"
+                      :style="{ width: nameWidth }"
+                      ref="nameRef"
+                    >
+                      {{ editableData.name }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.name"
+                      class="field-input name-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="nameInput"
+                      :style="{ width: nameInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- Address -->
+                  <div class="field-container address-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'address'"
+                      class="field lao-address-field"
+                      @click="startEditing('address')"
+                      :style="{ width: addressWidth }"
+                      ref="addressRef"
+                    >
+                      {{ editableData.address }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.address"
+                      class="field-input address-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="addressInput"
+                      :style="{ width: addressInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- GCP -->
+                  <div class="field-container gcp-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'GCP'"
+                      class="field lao-gcp-field"
+                      @click="startEditing('GCP')"
+                      :style="{ width: gcpWidth }"
+                      ref="gcpRef"
+                    >
+                      {{ editableData.GCP }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.GCP"
+                      class="field-input gcp-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="gcpInput"
+                      :style="{ width: gcpInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- GLN -->
+                  <div class="field-container gln-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'GLN'"
+                      class="field lao-gln-field"
+                      @click="startEditing('GLN')"
+                      :style="{ width: glnWidth }"
+                      ref="glnRef"
+                    >
+                      {{ editableData.GLN }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.GLN"
+                      class="field-input gln-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="glnInput"
+                      :style="{ width: glnInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- Start Date -->
+                  <div class="field-container startdate-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'startdate'"
+                      class="field lao-date-field"
+                      @click="startEditing('startdate')"
+                      :style="{ width: startdateWidth }"
+                      ref="startdateRef"
+                    >
+                      {{ formatDate(editableData.startdate) }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.startdate"
+                      class="field-input startdate-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="startdateInput"
+                      :style="{ width: startdateInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- End Date -->
+                  <div class="field-container enddate-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'enddate'"
+                      class="field lao-date-field"
+                      @click="startEditing('enddate')"
+                      :style="{ width: enddateWidth }"
+                      ref="enddateRef"
+                    >
+                      {{ formatDate(editableData.enddate) }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.enddate"
+                      class="field-input enddate-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="enddateInput"
+                      :style="{ width: enddateInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- 下方横线上的开始日期 -->
+                  <div class="field-container startdate-bottom-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'startdate'"
+                      class="field lao-date-field"
+                      @click="startEditing('startdate')"
+                      :style="{ width: startdateWidth }"
+                    >
+                      {{ formatDate(editableData.startdate) }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.startdate"
+                      class="field-input startdate-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      :style="{ width: startdateInputWidth }"
+                    />
+                  </div>
+                  
+                  <!-- 下方横线上的结束日期 -->
+                  <div class="field-container enddate-bottom-container">
+                    <span 
+                      v-if="!isEditing || editingField !== 'enddate'"
+                      class="field lao-date-field"
+                      @click="startEditing('enddate')"
+                      :style="{ width: enddateWidth }"
+                    >
+                      {{ formatDate(editableData.enddate) }}
+                    </span>
+                    <input 
+                      v-else
+                      v-model="editableData.enddate"
+                      class="field-input enddate-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      :style="{ width: enddateInputWidth }"
+                    />
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </layout-content>
+      </layout>
+    </layout>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
+import { Layout, LayoutSider, LayoutContent, LayoutHeader } from '@arco-design/web-vue'
+import { ElTree, ElButton, ElForm, ElFormItem } from 'element-plus'
+import { Get_lwPrintFields } from '@/api/mes/job'
+
+// 树状图相关
+const showTree = ref(true)
+const treeRef = ref(null)
+const treeData = ref([])
+const selectedUnit = ref(null) // 存储单个选中的节点
+const defaultProps = {
+  label: 'name'
+}
+
+
+// 证书数据
+const props = defineProps({
+  certificateData: {
+    type: Object,
+    default: () => ({
+      name: 'DAO-HEUANG GROUP',
+      address: 'No. 437, Unit 26, Hongkair Village, Saysetha District, Vientiane Capital, Lao PDR.',
+      GCP: '88720002',
+      GLN: '',
+      startdate: '2026-03-01',
+      enddate: '2027-02-28',
+      code: '260000201'
+    })
+  }
+})
+
+// 响应式数据
+const certificateWrapper = ref(null)
+const isEditing = ref(false)
+const editingField = ref(null)
+
+// 宽度相关
+const codeWidth = ref('150px')
+const nameWidth = ref('400px')
+const addressWidth = ref('400px')
+const gcpWidth = ref('150px')
+const glnWidth = ref('200px')
+const startdateWidth = ref('100px')
+const enddateWidth = ref('100px')
+const codeInputWidth = ref('150px')
+const nameInputWidth = ref('400px')
+const addressInputWidth = ref('400px')
+const gcpInputWidth = ref('150px')
+const glnInputWidth = ref('200px')
+const startdateInputWidth = ref('100px')
+const enddateInputWidth = ref('100px')
+
+// 可编辑数据
+const editableData = ref({
+  name: props.certificateData.name,
+  address: props.certificateData.address,
+  GCP: props.certificateData.GCP,
+  GLN: props.certificateData.GLN,
+  startdate: props.certificateData.startdate,
+  enddate: props.certificateData.enddate,
+  code: props.certificateData.code
+})
+
+// 日期格式化函数
+const formatDate = (dateString) => {
+  if (!dateString) return ''
+  const date = new Date(dateString)
+  const day = String(date.getDate()).padStart(2, '0')
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const year = String(date.getFullYear()).slice(2)
+  return `${day}/${month}/${year}`
+}
+
+// 添加监听器,确保当 props.certificateData 变化时更新显示
+watch(() => props.certificateData, (newData) => {
+  console.log('🔔 父组件数据更新:', newData)
+  // 更新本地响应式数据
+  Object.assign(editableData.value, newData)
+  // 强制更新一次字段宽度
+  nextTick(() => {
+    adjustFieldWidths()
+  })
+}, { deep: true })
+
+// 获取打印数据字段
+const getPrintDataFields = async () => {
+  try {
+    const response = await Get_lwPrintFields({})
+    if (response.code === 0) {
+      // 直接将接口数据转换为树状图数据
+      treeData.value = response.data
+      console.log('获取到的数据:', response.data)
+    } else {
+      console.error('获取打印数据字段失败:', response.msg)
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error)
+  }
+}
+
+// 处理树状图勾选 - 单选逻辑
+const handleCheck = (nodeData, checkedInfo) => {
+  const checkedKeys = treeRef.value?.getCheckedKeys() || []
+  
+  // 实现单选
+  if (checkedKeys.length > 1) {
+    treeRef.value?.setCheckedKeys([nodeData.code])
+  }
+  
+  if (checkedKeys.includes(nodeData.code)) {
+    selectedUnit.value = nodeData
+    console.log('✅ 选中节点:', nodeData)
+    updateCertificateFromUnit(nodeData)
+  } else {
+    selectedUnit.value = null
+  }
+}
+
+// 更新证书数据方法
+const updateCertificateFromUnit = (unitData) => {
+  console.log('从选中单位更新证书数据:', unitData)
+  
+  // 创建一个完整的更新对象,包含所有字段
+  const updatedData = {
+    name: unitData.name || unitData.personName || 'DAO-HEUANG GROUP',
+    address: unitData.address || 'No. 437, Unit 26, Hongkair Village, Saysetha District, Vientiane Capital, Lao PDR.',
+    GCP: unitData.GCP || '88720002',
+    GLN: unitData.GLN,
+    startdate: unitData.startdate || '2026-03-01',
+    enddate: unitData.enddate || '2027-02-28',
+    code: unitData.code || unitData.certificateNo || '260000201'
+  }
+  
+  console.log('📤 要更新的数据:', updatedData)
+  
+  // 直接更新本地响应式数据
+  Object.assign(editableData.value, updatedData)
+  
+  // 发送更新到父组件
+  emit('update:certificate-data', updatedData)
+  
+  // 强制重新计算并显示
+  nextTick(() => {
+    console.log('当前显示的数据:', {
+      名称: editableData.value.name,
+      地址: editableData.value.address,
+      GCP: editableData.value.GCP,
+      GLN: editableData.value.GLN,
+      开始日期: formatDate(editableData.value.startdate),
+      结束日期: formatDate(editableData.value.enddate),
+      编号: editableData.value.code
+    })
+    adjustFieldWidths()
+  })
+}
+
+// 调整字段宽度
+const adjustFieldWidths = () => {
+  const adjustField = (fieldRef, minWidth = 100) => {
+    const element = certificateWrapper.value?.querySelector(`[ref="${fieldRef}"]`);
+    if (element) {
+      const textLength = element.textContent?.length || 0;
+      const newWidth = Math.max(minWidth, textLength * 12);
+      return newWidth + 'px';
+    }
+    return minWidth + 'px';
+  }
+  
+  codeWidth.value = adjustField('codeRef', 150);
+  nameWidth.value = adjustField('nameRef', 400);
+  addressWidth.value = adjustField('addressRef', 750);
+  gcpWidth.value = adjustField('gcpRef', 150);
+  glnWidth.value = adjustField('glnRef', 200);
+  startdateWidth.value = adjustField('startdateRef', 100);
+  enddateWidth.value = adjustField('enddateRef', 100);
+}
+
+const adjustInputWidth = (fieldName) => {
+  const minWidthMap = {
+    code: 150,
+    name: 400,
+    address: 750,
+    GCP: 150,
+    GLN: 200,
+    startdate: 100,
+    enddate: 100
+  }
+  
+  const widthPropMap = {
+    code: 'codeInputWidth',
+    name: 'nameInputWidth',
+    address: 'addressInputWidth',
+    GCP: 'gcpInputWidth',
+    GLN: 'glnInputWidth',
+    startdate: 'startdateInputWidth',
+    enddate: 'enddateInputWidth'
+  }
+  
+  const value = editableData.value[fieldName]
+  const minWidth = minWidthMap[fieldName] || 100
+  const textLength = value ? value.length : 0
+  const newWidth = Math.max(minWidth, textLength * 12 + 20)
+  
+  const propName = widthPropMap[fieldName]
+  if (propName) {
+    if (fieldName === 'code') codeInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'name') nameInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'address') addressInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'GCP') gcpInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'GLN') glnInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'startdate') startdateInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'enddate') enddateInputWidth.value = newWidth + 'px'
+  }
+}
+
+const toggleEditMode = () => {
+  if (isEditing.value) {
+    saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    
+    nextTick(() => {
+      adjustFieldWidths();
+    });
+  } else {
+    isEditing.value = true;
+    editingField.value = null;
+  }
+}
+
+const startEditing = (fieldName) => {
+  if (!isEditing.value) {
+    isEditing.value = true;
+  }
+  editingField.value = fieldName;
+  
+  nextTick(() => {
+    const inputRefMap = {
+      code: 'codeInput',
+      name: 'nameInput',
+      address: 'addressInput',
+      GCP: 'gcpInput',
+      GLN: 'glnInput',
+      startdate: 'startdateInput',
+      enddate: 'enddateInput'
+    };
+    
+    const refName = inputRefMap[fieldName];
+    if (refName) {
+      const element = certificateWrapper.value?.querySelector(`[ref="${refName}"]`);
+      if (element) {
+        element.focus();
+        element.select();
+      }
+    }
+  });
+}
+
+const blurField = () => {
+  editingField.value = null
+}
+
+const saveAllChanges = () => {
+  emit('update:certificate-data', {
+    name: editableData.value.name,
+    address: editableData.value.address,
+    GCP: editableData.value.GCP,
+    GLN: editableData.value.GLN,
+    startdate: editableData.value.startdate,
+    enddate: editableData.value.enddate,
+    code: editableData.value.code
+  });
+  
+  emit('certificate-updated', {
+    data: { ...editableData.value }
+  });
+}
+
+// 打印证书方法
+const printCertificate = async () => {
+  // 如果正在编辑,先保存再打印
+  if (isEditing.value) {
+    await saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    await nextTick();
+  }
+  
+  const originalBodyStyle = document.body.style.cssText;
+  const originalBodyClass = document.body.className;
+  const originalHtmlStyle = document.documentElement.style.cssText;
+  
+  try {
+    // 创建打印结构
+    const iframe = document.createElement('iframe');
+    iframe.style.position = 'fixed';
+    iframe.style.right = '0';
+    iframe.style.bottom = '0';
+    iframe.style.width = '0';
+    iframe.style.height = '0';
+    iframe.style.border = 'none';
+    iframe.style.visibility = 'hidden';
+    document.body.appendChild(iframe);
+    
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'about:blank';
+    });
+    
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+    iframeDoc.open();
+    
+    // 获取当前数据
+    const currentData = editableData.value;
+    
+    // 老挝证书打印配置
+    const printConfig = {
+      fields: {
+        code: {
+          top: 160,
+          right: 100,
+          width: 150
+        },
+        name: {
+          top: 200,
+          left: 150,
+          width: 400
+        },
+        address: {
+          top: 250,
+          left: 150,
+          width: 500
+        },
+        GCP: {
+          top: 300,
+          left: 150,
+          width: 200
+        },
+        GLN: {
+          top: 350,
+          left: 150,
+          width: 250
+        },
+        dates: {
+          top: 450,
+          left: 150,
+          width: 150
+        }
+      }
+    };
+    
+    iframeDoc.write(`
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>老挝证书打印</title>
+  <style>
+    @page {
+      size: A4 landscape;
+      margin: 0;
+    }
+    body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+      -webkit-print-color-adjust: exact;
+      print-color-adjust: exact;
+      overflow: hidden;
+      font-family: 'Source Han Sans CN', Arial, sans-serif;
+    }
+    
+    .cert-print-container {
+      width: 297mm;
+      height: 210mm;
+      position: relative;
+      margin: 0 auto;
+      background: #fff;
+    }
+    
+    .lao-cert-print {
+      width: 100%;
+      height: 100%;
+      position: relative;
+      padding: 40px;
+      box-sizing: border-box;
+    }
+    
+    .lao-header-print {
+      text-align: center;
+      margin-bottom: 50px;
+    }
+    
+    .gs1-logo-print {
+      text-align: center;
+      margin-bottom: 20px;
+    }
+    
+    .lao-title-print {
+      text-align: center;
+      margin-bottom: 30px;
+    }
+    
+    .lao-title-print h1 {
+      font-size: 24px;
+      font-weight: bold;
+      margin: 0 0 10px 0;
+    }
+    
+    .lao-subtitle-print {
+      font-size: 16px;
+      color: #666;
+    }
+    
+    .license-no-print {
+      position: absolute;
+      top: ${printConfig.fields.code.top}px;
+      right: ${printConfig.fields.code.right}px;
+      text-align: right;
+    }
+    
+    .license-label-print {
+      font-size: 14px;
+      color: #666;
+    }
+    
+    .license-number-print {
+      font-size: 18px;
+      font-weight: bold;
+      margin-top: 5px;
+    }
+    
+    .lao-content-print {
+      margin-top: 50px;
+    }
+    
+    .lao-field-section-print {
+      margin-bottom: 25px;
+    }
+    
+    .lao-field-label-print {
+      font-size: 14px;
+      color: #666;
+      margin-bottom: 5px;
+    }
+    
+    .lao-field-value-print {
+      font-size: 16px;
+      font-weight: normal;
+    }
+    
+    .lao-name-field-print {
+      position: absolute;
+      top: 240px;
+      left: 180px;
+      width: 400px;
+      text-align: center;
+    }
+    
+    .lao-address-field-print {
+      position: absolute;
+      top: 290px;
+      left: 270px;
+      width: 750px;
+      text-align: center;
+    }
+    
+    .lao-gcp-field-print {
+      position: absolute;
+      top: 330px;
+      left: 360px;
+      width: 200px;
+      text-align: center;
+    }
+    
+    .lao-gln-field-print {
+      position: absolute;
+      top: 390px;
+      left: 420px;
+      width: 250px;
+      text-align: center;
+    }
+    
+    .lao-date-section-print {
+      position: absolute;
+      top: 550px;
+      left: 565px;
+      width: 100px;
+      text-align: center;
+    }
+    
+    .license-no-print {
+      position: absolute;
+      top: 170px;
+      right: 80px;
+      width: 150px;
+      text-align: right;
+    }
+    
+    .end-date-print {
+      position: absolute;
+      top: 550px;
+      left: 710px;
+      width: 100px;
+      text-align: center;
+    }
+    
+    /* 下方横线上的日期 */
+    .startdate-bottom-print {
+      position: absolute;
+      top: 570px;
+      left: 425px;
+      width: 100px;
+      text-align: center;
+    }
+    
+    .enddate-bottom-print {
+      position: absolute;
+      top: 570px;
+      left: 670px;
+      width: 100px;
+      text-align: center;
+    }
+    
+    .lao-date-field-print {
+      font-weight: bold;
+      margin: 0 10px;
+    }
+    
+    
+    * {
+      box-sizing: border-box;
+    }
+  </style>
+</head>
+<body>
+  <div class="cert-print-container">
+    <div class="lao-cert-print">
+      <!-- 只打印需要的字段,不包含背景和头部 -->
+      
+      <!-- 证书编号 -->
+      <div class="license-no-print">
+        <div class="license-number-print">${currentData.code}</div>
+      </div>
+      
+      <!-- 名称 -->
+      <div class="lao-field-section-print lao-name-field-print">
+        <div class="lao-field-value-print">${currentData.name}</div>
+      </div>
+      
+      <!-- 地址 -->
+      <div class="lao-field-section-print lao-address-field-print">
+        <div class="lao-field-value-print">${currentData.address}</div>
+      </div>
+      
+      <!-- GCP -->
+      <div class="lao-field-section-print lao-gcp-field-print">
+        <div class="lao-field-value-print">${currentData.GCP}</div>
+      </div>
+      
+      <!-- GLN -->
+      <div class="lao-field-section-print lao-gln-field-print">
+        <div class="lao-field-value-print">${currentData.GLN}</div>
+      </div>
+      
+      <!-- 日期 -->
+      <div class="lao-date-section-print">
+        <div class="lao-field-value-print">
+          <span class="lao-date-field-print">${formatDate(currentData.startdate)}</span>
+        </div>
+      </div>
+      
+      <div class="lao-date-section-print end-date-print">
+        <div class="lao-field-value-print">
+          <span class="lao-date-field-print">${formatDate(currentData.enddate)}</span>
+        </div>
+      </div>
+      
+      <!-- 下方横线上的开始日期 -->
+      <div class="lao-field-section-print startdate-bottom-print">
+        <div class="lao-field-value-print">
+          <span class="lao-date-field-print">${formatDate(currentData.startdate)}</span>
+        </div>
+      </div>
+      
+      <!-- 下方横线上的结束日期 -->
+      <div class="lao-field-section-print enddate-bottom-print">
+        <div class="lao-field-value-print">
+          <span class="lao-date-field-print">${formatDate(currentData.enddate)}</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</body>
+</html>
+`);
+    iframeDoc.close();
+    
+    // 等待iframe渲染完成后执行打印
+    await new Promise(resolve => setTimeout(resolve, 500));
+    
+    // 打印调试信息
+    console.log('打印字段位置信息:', {
+      名称: currentData.name,
+      地址: currentData.address,
+      GCP: currentData.GCP,
+      GLN: currentData.GLN,
+      开始日期: formatDate(currentData.startdate),
+      结束日期: formatDate(currentData.enddate),
+      编号: currentData.code
+    });
+    
+    iframe.contentWindow.print();
+    
+    // 打印后移除iframe
+    setTimeout(() => {
+      document.body.removeChild(iframe);
+      // 恢复原页面样式
+      document.body.style.cssText = originalBodyStyle;
+      document.body.className = originalBodyClass;
+      document.documentElement.style.cssText = originalHtmlStyle;
+    }, 1000);
+    
+  } catch (error) {
+    console.error('打印失败:', error);
+    // 异常时恢复样式
+    document.body.style.cssText = originalBodyStyle;
+    document.body.className = originalBodyClass;
+    document.documentElement.style.cssText = originalHtmlStyle;
+  }
+};
+
+// PDF导出方法 - 使用固定尺寸的iframe渲染,确保位置一致
+const exportPDF = async () => {
+  // 如果正在编辑,先保存再导出
+  if (isEditing.value) {
+    await saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    await nextTick();
+  }
+
+  try {
+    // 导入html2canvas和jsPDF
+    const html2canvas = (await import('html2canvas')).default;
+    const { jsPDF } = await import('jspdf');
+
+    // 创建隐藏的iframe用于渲染固定尺寸的证书
+    const iframe = document.createElement('iframe');
+    iframe.style.position = 'fixed';
+    iframe.style.right = '0';
+    iframe.style.bottom = '0';
+    iframe.style.width = '0';
+    iframe.style.height = '0';
+    iframe.style.border = 'none';
+    iframe.style.visibility = 'hidden';
+    document.body.appendChild(iframe);
+
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'about:blank';
+    });
+
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+    iframeDoc.open();
+
+    // 获取当前数据
+    const currentData = editableData.value;
+    const bgImageSrc = 'http://20.0.16.87:9094/uploads/certificate/lw.png';
+
+    // 使用固定像素尺寸的HTML内容
+    iframeDoc.write(`
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>老挝证书PDF导出</title>
+  <style>
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+    }
+    
+    body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+      overflow: hidden;
+    }
+    
+    .cert-container {
+      width: 1123px;
+      height: 794px;
+      position: relative;
+      background: #fff;
+    }
+    
+    .certificate-bg {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 1123px;
+      height: 794px;
+    }
+    
+    .certificate-bg img {
+      width: 100%;
+      height: 100%;
+      object-fit: contain;
+    }
+    
+    .certificate-fields {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 1123px;
+      height: 794px;
+    }
+    
+    .field {
+      position: absolute;
+      font-family: 'Source Han Sans CN', Arial, sans-serif;
+    }
+    
+    .address {
+      white-space: normal;
+      word-wrap: break-word;
+      line-height: 1.4;
+    }
+    
+    /* 证书编号 - 右上角 */
+    .license-no {
+      top: 200px;
+      right: 160px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 名称 */
+    .name {
+      top: 250px;
+      left: 250px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 地址 */
+    .address {
+      top: 300px;
+      left: 340px;
+      font-size: 13px;
+      font-weight: 400;
+      width: 600px;
+      text-align: center;
+    }
+    
+    /* GCP */
+    .gcp {
+      top: 350px;
+      left: 430px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* GLN */
+    .gln {
+      top: 400px;
+      left: 510px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 开始日期 */
+    .startdate {
+      top: 560px;
+      left: 580px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 结束日期 */
+    .enddate {
+      top: 560px;
+      left: 730px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 下方开始日期 */
+    .startdate-bottom {
+      top: 580px;
+      left: 450px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+    
+    /* 下方结束日期 */
+    .enddate-bottom {
+      top: 580px;
+      left: 690px;
+      font-size: 13px;
+      font-weight: 400;
+    }
+  </style>
+</head>
+<body>
+  <div class="cert-container">
+    <!-- 背景图片 -->
+    <div class="certificate-bg">
+      <img src="${bgImageSrc}" crossorigin="anonymous" alt="背景">
+    </div>
+    
+    <!-- 字段 -->
+    <div class="certificate-fields">
+      <div class="field license-no">${currentData.code}</div>
+      <div class="field name">${currentData.name}</div>
+      <div class="field address">${currentData.address}</div>
+      <div class="field gcp">${currentData.GCP}</div>
+      <div class="field gln">${currentData.GLN}</div>
+      <div class="field startdate">${formatDate(currentData.startdate)}</div>
+      <div class="field enddate">${formatDate(currentData.enddate)}</div>
+      <div class="field startdate-bottom">${formatDate(currentData.startdate)}</div>
+      <div class="field enddate-bottom">${formatDate(currentData.enddate)}</div>
+    </div>
+  </div>
+</body>
+</html>
+`);
+    iframeDoc.close();
+
+    // 等待内容渲染和图片加载
+    await new Promise(resolve => setTimeout(resolve, 1000));
+
+    // 获取iframe中的证书容器
+    const certContainer = iframeDoc.querySelector('.cert-container');
+    if (!certContainer) {
+      console.error('证书容器未找到');
+      document.body.removeChild(iframe);
+      return;
+    }
+
+    // 使用html2canvas生成图片
+    const canvas = await html2canvas(certContainer, {
+      scale: 2,
+      useCORS: true,
+      logging: false,
+      backgroundColor: '#ffffff',
+      allowTaint: true,
+      imageTimeout: 15000
+    });
+
+    // 创建PDF实例
+    const pdf = new jsPDF({
+      orientation: 'landscape',
+      unit: 'mm',
+      format: 'a4'
+    });
+
+    // 计算图片在PDF中的尺寸
+    const imgWidth = 297;
+    const imgHeight = canvas.height * imgWidth / canvas.width;
+
+    // 添加图片到PDF
+    pdf.addImage(
+      canvas.toDataURL('image/png'),
+      'PNG',
+      0,
+      0,
+      imgWidth,
+      imgHeight
+    );
+
+    // 保存PDF
+    const fileName = `老挝证书_${currentData.code}_${new Date().getTime()}.pdf`;
+    pdf.save(fileName);
+
+    // 清理
+    document.body.removeChild(iframe);
+
+    console.log('PDF导出成功:', fileName);
+
+  } catch (error) {
+    console.error('PDF导出失败:', error);
+  }
+};
+
+// 定义emit事件
+const emit = defineEmits(['update:certificate-data', 'certificate-updated'])
+
+// 生命周期
+onMounted(() => {
+  adjustFieldWidths()
+  window.addEventListener('resize', adjustFieldWidths)
+  getPrintDataFields()
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', adjustFieldWidths)
+})
+
+// 监听器
+watch(() => editableData.value.code, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('code')
+  })
+})
+
+watch(() => editableData.value.name, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('name')
+  })
+})
+
+watch(() => editableData.value.address, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('address')
+  })
+})
+
+watch(() => editableData.value.GCP, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('GCP')
+  })
+})
+
+watch(() => editableData.value.GLN, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('GLN')
+  })
+})
+
+watch(() => editableData.value.startdate, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('startdate')
+  })
+})
+
+watch(() => editableData.value.enddate, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('enddate')
+  })
+})
+</script>
+
+<style scoped>
+/* 基本样式 */
+.certificate-content {
+  padding: 0;
+  background-color: #f9f9f9;
+  min-height: calc(100vh - 100px);
+  font-family: "SimSun", "思源黑体", "STSong", serif;
+  font-size: 15pt;
+  font-weight: normal;
+}
+
+/* 顶部操作栏 */
+.top-action-bar {
+  padding: 10px 20px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+/* 树状图区域 */
+.tree-section {
+  height: 100%;
+  padding: 10px;
+  background: #fff;
+  border-right: 1px solid #e8e8e8;
+}
+
+.tree-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.tree-header h3 {
+  margin: 0;
+  font-size: 16px;
+  color: #303133;
+}
+
+.tree-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  max-height: calc(100vh - 200px);
+}
+
+.tree-node {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.tree-gcp {
+  font-size: 12px;
+  color: #909399;
+  margin-left: 10px;
+}
+
+.selected-info {
+  margin-top: 15px;
+  padding: 10px;
+  background: #f5f7fa;
+  border-radius: 4px;
+  text-align: center;
+  font-size: 14px;
+  color: #606266;
+}
+
+/* 证书容器 */
+.certificate-content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 0px;
+}
+
+.cert-wrapper {
+  width: 297mm;
+  height: 210mm;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
+  background: #fff;
+  margin: 0 auto;
+  position: relative;
+  padding: 0;
+  overflow: hidden;
+}
+
+/* 老挝证书样式 */
+.lao-certificate {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  padding: 0;
+}
+
+.certificate-background {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1;
+}
+
+.bg-image {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+
+.certificate-fields {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 2;
+  padding: 60px;
+  box-sizing: border-box;
+}
+
+.field-container {
+  position: absolute;
+  text-align: center;
+}
+
+/* 字段定位 - 适应横向证书 */
+.license-no-container {
+  top: 170px;
+  right: 80px;
+  width: 150px;
+}
+
+.name-container {
+  top: 240px;
+  left: 180px;
+  width: 400px;
+}
+
+.address-container {
+  top: 290px;
+  left: 270px;
+  width: 750px;
+}
+
+.gcp-container {
+  top: 330px;
+  left: 360px;
+  width: 200px;
+}
+
+.gln-container {
+  top: 390px;
+  left: 420px;
+  width: 250px;
+}
+
+.startdate-container {
+  top: 550px;
+  left: 565px;
+  width: 100px;
+}
+
+.enddate-container {
+  top: 550px;
+  left: 710px;
+  width: 100px;
+}
+
+/* 下方横线上的日期字段 */
+.startdate-bottom-container {
+  top: 570px;
+  left: 425px;
+  width: 100px;
+}
+
+.enddate-bottom-container {
+  top: 570px;
+  left: 670px;
+  width: 100px;
+}
+
+/* 字段样式 */
+.field {
+  display: inline-block;
+  border: 2px solid transparent;
+  /* border-bottom: 2px solid #000; */
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 13px;
+  font-weight: 400;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.field:hover {
+  background: rgba(0, 102, 204, 0.1);
+  border-bottom-color: #0066cc;
+}
+
+.field-input {
+  display: inline-block;
+  border: 2px solid #0066cc;
+  background: rgba(255, 255, 255, 0.9);
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 16px;
+  font-weight: bold;
+  outline: none;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+}
+
+.field-input:focus {
+  background: rgba(0, 102, 204, 0.1);
+  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
+}
+
+/* 响应式设计 */
+@media (max-width: 1200px) {
+  .cert-wrapper {
+    width: 95%;
+    padding: 30px;
+  }
+  
+  .lao-certificate {
+    padding: 40px;
+  }
+  
+  .lao-title h1 {
+    font-size: 20px;
+  }
+  
+  .field, .field-input {
+    font-size: 14px;
+  }
+}
+
+@media (max-width: 768px) {
+  .cert-wrapper {
+    width: 100%;
+    padding: 20px;
+  }
+  
+  .lao-certificate {
+    padding: 30px;
+    background-image: none;
+    border: 1px solid #e8e8e8;
+  }
+  
+  .lao-header {
+    margin-bottom: 30px;
+  }
+  
+  .gs1-logo img {
+    width: 80px;
+    height: 80px;
+  }
+  
+  .lao-title h1 {
+    font-size: 18px;
+  }
+  
+  .license-no {
+    position: relative;
+    top: 0;
+    right: 0;
+    text-align: center;
+    margin-bottom: 20px;
+  }
+  
+  .lao-qr-section {
+    position: relative;
+    top: 0;
+    right: 0;
+    margin-top: 30px;
+    text-align: center;
+  }
+  
+  .action-buttons {
+    flex-direction: column;
+  }
+  
+  .action-buttons button {
+    width: 100%;
+    justify-content: center;
+  }
+}
+
+/* 打印时隐藏的样式 */
+@media print {
+  .no-print,
+  .field-input,
+  .tree-section,
+  .top-action-bar {
+    display: none !important;
+  }
+  
+  .cert-wrapper {
+    box-shadow: none !important;
+    margin: 0 !important;
+    padding: 0 !important;
+  }
+  
+  .lao-certificate {
+    box-shadow: none !important;
+  }
+  
+  .field {
+    display: inline-block !important;
+  }
+}
+</style>

+ 1490 - 0
src/view/zs/zhengshudayin.vue

@@ -0,0 +1,1490 @@
+<template>
+  <div>
+    <layout>
+      <layout-header>
+        <div class="top-action-bar no-print">
+          <el-form class="demo-form-inline">
+            <el-form-item>
+              <div class="action-buttons">
+                <!-- 打印按钮 -->
+                <el-button type="primary" @click="printCertificate" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M5 1a2 2 0 0 0-2 2v1h10V3a2 2 0 0 0-2-2H5zm6 8H5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1z"/>
+                    <path d="M0 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2h-1v-2a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2H2a2 2 0 0 1-2-2V7zm2.5 1a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1z"/>
+                  </svg>
+                  打印证书
+                </el-button>
+                
+                <!-- 样式切换按钮 -->
+                <el-button type="primary" @click="toggleCertificateStyle" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M3 2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 .5.5v.5h-8v-.5z"/>
+                    <path d="M3 4v9a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4H3zm1 1h8v7H4V5z"/>
+                  </svg>
+                  {{ certificateStyle === 'personnel' ? '切换为单位证书' : '切换为人员证书' }}
+                </el-button>
+                
+                <!-- 编辑按钮 -->
+                <el-button type="primary" @click="toggleEditMode" style="margin: 5px">
+                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="margin-right: 5px">
+                    <path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
+                  </svg>
+                  {{ isEditing ? '完成编辑' : '编辑证书' }}
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-form>
+        </div>
+      </layout-header>
+
+      <layout>
+        <!-- 左侧树状图区域 -->
+        <layout-sider :resize-directions="['right']" :width="300" v-if="showTree">
+          <div class="tree-section">
+            <div class="tree-header">
+              <h3>人员选择</h3>
+            </div>
+            <div class="tree-wrapper">
+              <el-tree
+                ref="treeRef"
+                :data="treeData"
+                node-key="code"
+                show-checkbox
+                :props="defaultProps"
+                @check="handleCheck"
+                highlight-current
+                :default-expand-all="true"
+                :check-strictly="true"
+              >
+                <template #default="{ node, data }">
+                  <span class="tree-node">
+                    <span>{{ data.name }}</span>
+                    <span v-if="data.position" class="tree-position">{{ data.position }}</span>
+                  </span>
+                </template>
+              </el-tree>
+            </div>
+            <div class="selected-info" v-if="selectedPerson">
+              已选择:{{ selectedPerson.name }} ({{ selectedPerson.code }})
+            </div>
+          </div>
+        </layout-sider>
+
+        <!-- 右侧证书区域 -->
+        <layout-content>
+          <div class="certificate-content">
+            <div class="cert-wrapper" ref="certificateWrapper">
+              <!-- 左侧区域:二维码和签名横线 -->
+              <div class="left-section">
+                <div class="qr-code-container">
+                  <div class="qr-code-placeholder">
+                    <!-- 二维码图片 -->
+                    <img 
+                      :src="qrCodeUrl"
+                      alt="证书二维码" 
+                      width="180" 
+                      height="180"
+                    />
+                  </div>
+                </div>
+
+                
+                <!-- 左侧签名区域 -->
+                <div class="left-signature-container">
+                  <span 
+                    v-if="!isEditing || editingField !== 'leftSignature'"
+                    class="left-signature-field"
+                    @click="startEditing('leftSignature')"
+                  >
+                    {{ signatureText }}
+                  </span>
+                  <input 
+                    v-else
+                    v-model="signatureText"
+                    class="left-signature-input"
+                    type="text"
+                    @blur="blurField"
+                    @keyup.enter="blurField"
+                    ref="leftSignatureInput"
+                    :style="{ width: leftSignatureInputWidth }"
+                  />
+                </div>
+              </div>
+              
+              <!-- 右侧区域:证书正文 -->
+              <div class="right-section">
+                <!-- 证书标题 -->
+                <div class="title" :class="{ 'unit-title-style': certificateStyle === 'unit' }">
+                  {{ certificateStyle === 'personnel' ? '证 书' : '杭州市数据标准化技术委员会' }}
+                </div>
+          
+                <!-- 证书内容 -->
+                <div class="content">
+                  <!-- 人员证书样式 -->
+                  <template v-if="certificateStyle === 'personnel'">
+  <!-- 证书编号行(保留原有专属类名) -->
+  <div class="content-line cert-no-line">
+    <span class="cert-no-label">证书编号:</span>
+    <!-- 证书编号字段 -->
+    <span 
+      v-if="!isEditing || editingField !== 'certificateNo'"
+      class="field cert-no"
+      @click="startEditing('certificateNo')"
+      :style="{ width: certNoWidth }"
+      ref="certNoRef"
+    >
+      {{ editableData.certificateNo }}
+    </span>
+    <input 
+      v-else
+      v-model="editableData.certificateNo"
+      class="field-input cert-no-input"
+      type="text"
+      @blur="blurField"
+      @keyup.enter="blurField"
+      ref="certNoInput"
+      :style="{ width: certNoInputWidth }"
+    />
+  </div>
+  
+  <!-- 姓名字段行(拆成独立行,加专属类名 name-line) -->
+  <div class="content-line name-line">
+    兹聘请
+    <span 
+      v-if="!isEditing || editingField !== 'personName'"
+      class="field name"
+      @click="startEditing('personName')"
+      :style="{ width: nameWidth }"
+      ref="nameRef"
+    >
+      {{ editableData.personName }}
+    </span>
+    <input 
+      v-else
+      v-model="editableData.personName"
+      class="field-input name-input"
+      type="text"
+      @blur="blurField"
+      @keyup.enter="blurField"
+      ref="nameInput"
+      :style="{ width: nameInputWidth }"
+    />
+    同志为杭州市数据标准化
+  </div>
+  
+  <!-- 职位字段行(拆成独立行,加专属类名 position-line) -->
+  <div class="content-line position-line">
+    技术委员会(HZ/TC04)
+    <span 
+      v-if="!isEditing || editingField !== 'position'"
+      class="field position"
+      @click="startEditing('position')"
+      :style="{ width: positionWidth }"
+      ref="positionRef"
+    >
+      {{ editableData.position }}
+    </span>
+    <input 
+      v-else
+      v-model="editableData.position"
+      class="field-input position-input"
+      type="text"
+      @blur="blurField"
+      @keyup.enter="blurField"
+      ref="positionInput"
+      :style="{ width: positionInputWidth }"
+    />
+    。
+  </div>
+</template>
+                  
+                  <!-- 单位证书样式 -->
+                  <template v-else>
+    <div class="unit-content">
+      <div class="unit-title">单位委员</div>
+      
+      <div class="content-line unit-line">
+        <span class="cert-no-label">单位名称:</span>
+        <!-- 单位名称字段 -->
+        <span 
+          v-if="!isEditing || editingField !== 'unitName'"
+          class="field unit-name"
+          @click="startEditing('unitName')"
+          :style="{ width: unitNameWidth }"
+          ref="unitNameRef"
+        >
+          {{ editableData.unitName }}
+        </span>
+        <input 
+          v-else
+          v-model="editableData.unitName"
+          class="field-input unit-name-input"
+          type="text"
+          @blur="blurField"
+          @keyup.enter="blurField"
+          ref="unitNameInput"
+          :style="{ width: unitNameInputWidth }"
+        />
+      </div>
+      
+      <div class="content-line cert-no-line unit-cert-no-line">
+        <span class="cert-no-label">证书编号:</span>
+        <!-- 证书编号字段 -->
+        <span 
+          v-if="!isEditing || editingField !== 'certificateNo'"
+          class="field cert-no"
+          @click="startEditing('certificateNo')"
+          :style="{ width: certNoWidth }"
+          ref="certNoRef"
+        >
+          {{ editableData.certificateNo }}
+        </span>
+        <input 
+          v-else
+          v-model="editableData.certificateNo"
+          class="field-input cert-no-input"
+          type="text"
+          @blur="blurField"
+          @keyup.enter="blurField"
+          ref="certNoInput"
+          :style="{ width: certNoInputWidth }"
+        />
+      </div>
+    </div>
+  </template>
+                </div>
+
+                <!-- 落款 -->
+                <div class="signature">
+                  <div class="org-name">杭州市数据标准化技术委员会</div>
+                  <!-- 日期字段 -->
+                  <div class="date" @click="startEditing('issueDate')">
+                    <span v-if="!isEditing || editingField !== 'issueDate'">{{ editableData.issueDate }}</span>
+                    <input 
+                      v-else
+                      v-model="editableData.issueDate"
+                      class="date-input"
+                      type="text"
+                      @blur="blurField"
+                      @keyup.enter="blurField"
+                      ref="dateInput"
+                    />
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </layout-content>
+      </layout>
+    </layout>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
+import { Layout, LayoutSider, LayoutContent, LayoutHeader } from '@arco-design/web-vue'
+import { ElTree, ElButton, ElForm, ElFormItem } from 'element-plus'
+import { Get_PrintFields } from '@/api/mes/job'
+
+// 树状图相关
+const showTree = ref(true)
+const treeRef = ref(null)
+const treeData = ref([])
+const selectedPerson = ref(null) // 改为存储单个选中的节点
+const defaultProps = {
+  label: 'name'
+}
+
+// 计算二维码URL - 一行代码版本
+const qrCodeUrl = computed(() => {
+  const fileName = signatureText.value
+    ? signatureText.value
+        .split(')')[1]              // 取 ) 后面的部分
+        ?.trim()                    // 去空格
+        .replace(/\//g, '-')        // / 替换为 -
+        + '.jfif'                   // 添加后缀
+    : '6910001021017HZ-TC04-01001.jfif' // 默认
+  
+  return `http://20.0.16.87:9094/uploads/qrcode/${encodeURIComponent(fileName)}`
+})
+
+// 证书数据
+const props = defineProps({
+  certificateData: {
+    type: Object,
+    default: () => ({
+      certificateNo: 'HZ-TC04-2025001',
+      personName: '徐青山',
+      position: '主任委员',
+      unitName: 'XXXX科技有限公司',
+      issueDate: '2025年7月30日'
+    })
+  },
+  leftSignature: {
+    type: String,
+    default: 'GDTI:(253)6910001021017HZ/TC04-01001'
+  },
+  defaultStyle: {
+    type: String,
+    default: 'personnel',
+    validator: (value) => ['personnel', 'unit'].includes(value)
+  }
+})
+
+// 响应式数据
+const certificateWrapper = ref(null)
+const signatureText = ref(props.leftSignature)
+const isEditing = ref(false)
+const editingField = ref(null)
+const certificateStyle = ref(props.defaultStyle)
+
+// 宽度相关
+const certNoWidth = ref('250px')
+const nameWidth = ref('120px')
+const positionWidth = ref('180px')
+const unitNameWidth = ref('200px')
+const certNoInputWidth = ref('250px')
+const nameInputWidth = ref('120px')
+const positionInputWidth = ref('180px')
+const unitNameInputWidth = ref('200px')
+const leftSignatureInputWidth = ref('200px')
+
+// 单位数据
+const unitData = reactive({
+  certificateNo: 'HZ-TC04-2025001',
+  unitName: 'XXXX科技有限公司',
+  issueDate: '2025年7月30日',
+  leftSignature: 'GDTI:(253)6910001021017HZ/TC04-01001'
+})
+
+// 可编辑数据
+// 可编辑数据 - 确保正确响应 props 变化
+const editableData = computed(() => {
+  const baseData = {
+    leftSignature: signatureText.value
+  }
+  
+  if (certificateStyle.value === 'personnel') {
+    return {
+      ...baseData,
+      certificateNo: props.certificateData.certificateNo,
+      personName: props.certificateData.personName,
+      position: props.certificateData.position,
+      issueDate: props.certificateData.issueDate
+    }
+  } else {
+    return {
+      ...baseData,
+      certificateNo: unitData.certificateNo,
+      unitName: unitData.unitName,
+      issueDate: unitData.issueDate
+    }
+  }
+})
+
+// 添加监听器,确保当 props.certificateData 变化时更新显示
+watch(() => props.certificateData, (newData) => {
+  console.log('🔔 父组件数据更新:', newData)
+  // 强制更新一次字段宽度
+  nextTick(() => {
+    adjustFieldWidths()
+  })
+}, { deep: true })
+
+// 获取打印数据字段
+const getPrintDataFields = async () => {
+  try {
+    const response = await Get_PrintFields({})
+    if (response.code === 0) {
+      // 直接将接口数据转换为树状图数据
+      treeData.value = response.data
+      console.log('获取到的数据:', response.data)
+    } else {
+      console.error('获取打印数据字段失败:', response.msg)
+    }
+  } catch (error) {
+    console.error('获取数据失败:', error)
+  }
+}
+
+// 处理树状图勾选 - 修改单选逻辑
+const handleCheck = (nodeData, checkedInfo) => {
+  const checkedKeys = treeRef.value?.getCheckedKeys() || []
+  
+  // 实现单选
+  if (checkedKeys.length > 1) {
+    treeRef.value?.setCheckedKeys([nodeData.code])
+  }
+  
+  if (checkedKeys.includes(nodeData.code)) {
+    selectedPerson.value = nodeData
+    console.log('✅ 选中节点:', nodeData)
+    
+    // 自动检测并切换证书样式
+    if (nodeData.position && nodeData.position.includes('单位委员')) {
+      // 如果是单位委员,自动切换到单位证书
+      if (certificateStyle.value !== 'unit') {
+        console.log('🔄 检测到单位委员,自动切换到单位证书')
+        certificateStyle.value = 'unit'
+      }
+      
+      // 更新单位证书数据
+      updateUnitCertificateFromNode(nodeData)
+    } else {
+      // 否则使用人员证书
+      if (certificateStyle.value !== 'personnel') {
+        certificateStyle.value = 'personnel'
+      }
+      updateCertificateFromPerson(nodeData)
+    }
+  } else {
+    selectedPerson.value = null
+  }
+}
+
+// 修复:更新证书数据方法 - 确保更新所有相关字段
+const updateCertificateFromPerson = (personData) => {
+  console.log('从选中人员更新证书数据:', personData)
+  
+  if (certificateStyle.value === 'personnel') {
+    // 1. 更新左侧签名文字
+    if (personData.signature) {
+      signatureText.value = personData.signature
+      console.log('✅ 更新签名文字:', personData.signature)
+    }
+    
+    // 2. 创建一个完整的更新对象,包含所有字段
+    const updatedData = {
+      certificateNo: personData.code || personData.certificateNo || 'HZ-TC04-2025001',
+      personName: personData.name || personData.personName || '徐青山',
+      position: personData.position || '主任委员',
+      issueDate: props.certificateData.issueDate || '2025年7月30日',
+      unitName: props.certificateData.unitName || 'XXXX科技有限公司'
+    }
+    
+    console.log('📤 要更新的数据:', updatedData)
+    
+    // 3. 直接更新响应式数据(不只是发送emit)
+    // 这里需要更新 props.certificateData 对应的响应式数据
+    // 但由于 props 是只读的,我们需要使用 emit 或者使用 ref 来存储
+    
+    // 发送更新到父组件
+    emit('update:certificate-data', updatedData)
+    
+    // 4. 同时更新本地的响应式数据
+    // 创建一个临时变量来强制更新(根据你的数据结构调整)
+    Object.assign(props.certificateData, updatedData)
+    
+    // 5. 强制重新计算并显示
+    nextTick(() => {
+      console.log('当前显示的数据:', {
+        证书编号: editableData.value.certificateNo,
+        姓名: editableData.value.personName,
+        职位: editableData.value.position,
+        左侧签名: signatureText.value
+      })
+      console.log('当前二维码URL:', qrCodeUrl.value)
+      adjustFieldWidths()
+    })
+  }
+}
+
+// 更新单位证书数据
+const updateUnitCertificateFromNode = (nodeData) => {
+  console.log('更新单位证书数据:', nodeData)
+  
+  // 更新单位数据
+  unitData.certificateNo = nodeData.code || 'HZ-TC04-2025001'
+  unitData.unitName = nodeData.name || '未命名单位'
+  unitData.issueDate = props.certificateData.issueDate || '2025年7月30日'
+  
+  // 更新左侧签名
+  if (nodeData.signature) {
+    signatureText.value = nodeData.signature
+  }
+  
+  console.log('单位证书数据已更新:', {
+    证书编号: unitData.certificateNo,
+    单位名称: unitData.unitName,
+    左侧签名: signatureText.value
+  })
+}
+
+// 调整字段宽度
+const adjustFieldWidths = () => {
+  const adjustField = (fieldRef, minWidth = 100) => {
+    const element = certificateWrapper.value?.querySelector(`[ref="${fieldRef}"]`);
+    if (element) {
+      const textLength = element.textContent?.length || 0;
+      const newWidth = Math.max(minWidth, textLength * 16);
+      return newWidth + 'px';
+    }
+    return minWidth + 'px';
+  }
+  
+  certNoWidth.value = adjustField('certNoRef', 250);
+  nameWidth.value = adjustField('nameRef', 120);
+  positionWidth.value = adjustField('positionRef', 180);
+  unitNameWidth.value = adjustField('unitNameRef', 200);
+}
+
+const adjustInputWidth = (fieldName) => {
+  const minWidthMap = {
+    certificateNo: 250,
+    personName: 120,
+    position: 180,
+    unitName: 200,
+    leftSignature: 200
+  }
+  
+  const widthPropMap = {
+    certificateNo: 'certNoInputWidth',
+    personName: 'nameInputWidth',
+    position: 'positionInputWidth',
+    unitName: 'unitNameInputWidth',
+    leftSignature: 'leftSignatureInputWidth'
+  }
+  
+  const value = editableData.value[fieldName]
+  const minWidth = minWidthMap[fieldName] || 100
+  const textLength = value ? value.length : 0
+  const newWidth = Math.max(minWidth, textLength * 16 + 20)
+  
+  const propName = widthPropMap[fieldName]
+  if (propName) {
+    if (fieldName === 'certificateNo') certNoInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'personName') nameInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'position') positionInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'unitName') unitNameInputWidth.value = newWidth + 'px'
+    else if (fieldName === 'leftSignature') leftSignatureInputWidth.value = newWidth + 'px'
+  }
+}
+
+const toggleCertificateStyle = () => {
+  certificateStyle.value = certificateStyle.value === 'personnel' ? 'unit' : 'personnel'
+  isEditing.value = false
+  editingField.value = null
+  nextTick(() => {
+    adjustFieldWidths()
+  })
+}
+
+const toggleEditMode = () => {
+  if (isEditing.value) {
+    saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    
+    nextTick(() => {
+      adjustFieldWidths();
+    });
+  } else {
+    isEditing.value = true;
+    editingField.value = null;
+  }
+}
+
+const startEditing = (fieldName) => {
+  if (!isEditing.value) {
+    isEditing.value = true;
+  }
+  editingField.value = fieldName;
+  
+  nextTick(() => {
+    const inputRefMap = {
+      certificateNo: 'certNoInput',
+      personName: 'nameInput',
+      position: 'positionInput',
+      unitName: 'unitNameInput',
+      issueDate: 'dateInput',
+      leftSignature: 'leftSignatureInput'
+    };
+    
+    const refName = inputRefMap[fieldName];
+    if (refName) {
+      const element = certificateWrapper.value?.querySelector(`[ref="${refName}"]`);
+      if (element) {
+        element.focus();
+        element.select();
+      }
+    }
+  });
+}
+
+const blurField = () => {
+  editingField.value = null
+}
+
+const saveAllChanges = () => {
+  if (certificateStyle.value === 'personnel') {
+    emit('update:certificate-data', {
+      certificateNo: editableData.value.certificateNo,
+      personName: editableData.value.personName,
+      position: editableData.value.position,
+      issueDate: editableData.value.issueDate
+    });
+  } else {
+    unitData.certificateNo = editableData.value.certificateNo;
+    unitData.unitName = editableData.value.unitName;
+    unitData.issueDate = editableData.value.issueDate;
+  }
+  
+  emit('update:left-signature', signatureText.value);
+  
+  emit('certificate-updated', {
+    style: certificateStyle.value,
+    data: { ...editableData.value }
+  });
+}
+
+// 打印证书方法
+const printCertificate = async () => {
+  // 如果正在编辑,先保存再打印
+  if (isEditing.value) {
+    await saveAllChanges();
+    isEditing.value = false;
+    editingField.value = null;
+    await nextTick();
+  }
+  
+  const originalBodyStyle = document.body.style.cssText;
+  const originalBodyClass = document.body.className;
+  const originalHtmlStyle = document.documentElement.style.cssText;
+  
+  try {
+    // 不再克隆DOM,直接创建新的打印结构
+    const iframe = document.createElement('iframe');
+    iframe.style.position = 'fixed';
+    iframe.style.right = '0';
+    iframe.style.bottom = '0';
+    iframe.style.width = '0';
+    iframe.style.height = '0';
+    iframe.style.border = 'none';
+    iframe.style.visibility = 'hidden';
+    document.body.appendChild(iframe);
+    
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'about:blank';
+    });
+    
+    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+    iframeDoc.open();
+    
+    // 获取当前数据
+    const currentData = editableData.value;
+    
+    // ========== 统一位置配置 ==========
+    const isUnitCertificate = certificateStyle.value === 'unit';
+    
+    // 检查单位名称长度
+    const unitName = currentData.unitName || '';
+    const isLongUnitName = unitName.length > 15; // 超过15个字符
+    console.log('单位名称长度:', unitName.length);
+    
+    // 根据名称长度动态调整位置
+    const unitNamePosition = {
+      top: isLongUnitName ? 345 : 365, // 长名称上移20px
+      left: 57,
+      width: 250
+    };
+    
+    // 如果单位名称很长,证书编号也需要相应上移
+    const unitCertNoPosition = {
+      top: isLongUnitName ? 440 : 440, // 长名称上移20px
+      left: 57,
+      width: 400
+    };
+    
+    const leftSignatureTop = isUnitCertificate ? 520 : 540;
+    const leftleftSectionTop = isUnitCertificate ? 185 : 205;
+    const leftleftSectionLeft = isUnitCertificate ? 180 : 200;
+    
+    const printConfig = {
+      // 左侧区域位置(固定)
+      leftSection: {
+        qrCode: {
+          top: leftleftSectionTop,
+          left: 45,
+          width: 180,
+          height: 180
+        },
+        signature: {
+          top: leftSignatureTop,
+          left: 45,
+          fontSize: '15pt',
+          maxWidth: 600
+        }
+      },
+      
+      // 人员证书字段位置(保持不变)
+      personnel: {
+        certNo: {
+          top: 260,
+          left: 73,
+          width: 400
+        },
+        name: {
+          top: 360,
+          left: 41,
+          width: 400
+        },
+        position: {
+          top: 405,
+          left: 64,
+          width: 400
+        }
+      },
+      
+      // 单位证书字段位置(使用动态位置)
+      unit: {
+        unitName: unitNamePosition,
+        certNo: unitCertNoPosition
+      }
+    };
+    
+    // 根据证书类型生成打印内容
+    let rightSectionHtml = '';
+    
+    if (!isUnitCertificate) {
+      // 人员证书:三个字段
+      const pos = printConfig.personnel;
+      rightSectionHtml = `
+        <!-- 证书编号 -->
+        <div class="print-field cert-no-field" style="
+          position: absolute;
+          top: ${pos.certNo.top}px;
+          left: ${pos.certNo.left}%;
+          transform: translateX(-50%);
+          width: ${pos.certNo.width}px;
+          text-align: center;
+          font-family: 'Source Han Sans CN', sans-serif;
+          font-size: 15pt;
+        ">${currentData.certificateNo}</div>
+        
+        <!-- 姓名 -->
+        <div class="print-field name-field" style="
+          position: absolute;
+          top: ${pos.name.top}px;
+          left: ${pos.name.left}%;
+          transform: translateX(-50%);
+          width: ${pos.name.width}px;
+          text-align: center;
+          font-family: 'Source Han Sans CN', sans-serif;
+          font-size: 15pt;
+        ">${currentData.personName}</div>
+        
+        <!-- 职位 -->
+        <div class="print-field position-field" style="
+          position: absolute;
+          top: ${pos.position.top}px;
+          left: ${pos.position.left}%;
+          transform: translateX(-50%);
+          width: ${pos.position.width}px;
+          text-align: center;
+          font-family: 'Source Han Sans CN', sans-serif;
+          font-size: 15pt;
+        ">${currentData.position}</div>
+      `;
+    } else {
+// 单位证书:两个字段
+const pos = printConfig.unit;
+
+// 根据长度设置不同的样式
+const unitNameStyle = isLongUnitName ? `
+  position: absolute;
+  top: ${pos.unitName.top}px;
+  left: ${pos.unitName.left}%;
+  transform: translateX(-50%);
+  width: ${pos.unitName.width}px;
+  text-align: center;
+  font-family: 'Source Han Sans CN', sans-serif;
+  font-size: 15pt;
+  white-space: normal !important;
+  word-wrap: break-word !important;
+  word-break: break-all !important;
+  line-height: 1.4 !important;
+  max-height: none !important;
+  overflow: visible !important;
+  display: block !important;
+` : `
+  position: absolute;
+  top: ${pos.unitName.top}px;
+  left: ${pos.unitName.left}%;
+  transform: translateX(-50%);
+  width: ${pos.unitName.width}px;
+  text-align: center;
+  font-family: 'Source Han Sans CN', sans-serif;
+  font-size: 15pt;
+  white-space: nowrap !important;
+`;
+
+rightSectionHtml = `
+  <!-- 单位名称 -->
+  <div class="print-field unit-name-field" style="${unitNameStyle.trim()}">
+    ${currentData.unitName}
+  </div>
+  
+  <!-- 单位证书编号 -->
+  <div class="print-field unit-cert-no-field" style="
+    position: absolute;
+    top: ${pos.certNo.top}px;
+    left: ${pos.certNo.left}%;
+    transform: translateX(-50%);
+    width: ${pos.certNo.width}px;
+    text-align: center;
+    font-family: 'Source Han Sans CN', sans-serif;
+    font-size: 15pt;
+    white-space: nowrap !important;
+  ">${currentData.certificateNo}</div>
+`;
+    }
+    
+    iframeDoc.write(`
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>证书打印</title>
+  <style>
+    @page {
+      size: A4 landscape;
+      margin: 0;
+    }
+    body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+      -webkit-print-color-adjust: exact;
+      print-color-adjust: exact;
+      overflow: hidden;
+    }
+    
+    .cert-print-container {
+      width: 1200px;
+      height: 650px;
+      position: relative;
+      margin: 0 auto;
+    }
+    
+    .left-print-section {
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 50%;
+      height: 100%;
+    }
+    
+    .right-print-section {
+      position: absolute;
+      right: 0;
+      top: 0;
+      width: 50%;
+      height: 100%;
+    }
+    
+    /* 使用配置的位置 */
+    .qr-code-print {
+      position: absolute;
+      top: ${printConfig.leftSection.qrCode.top}px;
+      left: ${printConfig.leftSection.qrCode.left}%;
+      transform: translateX(-50%);
+      text-align: center;
+    }
+    
+    /* 左侧签名 */
+    .left-signature-print {
+      position: absolute;
+      top: ${printConfig.leftSection.signature.top}px;
+      left: ${printConfig.leftSection.signature.left}%;
+      transform: translateX(-50%);
+      text-align: center;
+      font-family: 'Source Han Sans CN', sans-serif;
+      font-size: ${printConfig.leftSection.signature.fontSize};
+      white-space: nowrap;
+      max-width: ${printConfig.leftSection.signature.maxWidth}px;
+    }
+    
+    /* 打印字段通用样式 - 确保不覆盖内联样式 */
+    .print-field {
+      position: absolute;
+      text-align: center;
+      font-family: 'Source Han Sans CN', sans-serif;
+      font-size: 15pt;
+      /* 不要设置white-space在这里,让内联样式控制 */
+    }
+    
+    /* 移除或注释掉这个类,因为我们现在用内联样式 */
+    /*
+    .long-unit-name {
+      white-space: normal !important;
+      word-wrap: break-word !important;
+      line-height: 1.4 !important;
+      max-height: 60px !important;
+      overflow: hidden !important;
+      display: flex !important;
+      align-items: center !important;
+      justify-content: center !important;
+      text-align: center !important;
+    }
+    */
+    
+    * {
+      box-sizing: border-box;
+    }
+    
+   
+  </style>
+</head>
+<body>
+  <div class="cert-print-container">
+    <div class="left-print-section">
+      <div class="qr-code-print">
+        <img src="${qrCodeUrl.value}" alt="证书二维码" 
+             width="${printConfig.leftSection.qrCode.width}" 
+             height="${printConfig.leftSection.qrCode.height}">
+      </div>
+      <div class="left-signature-print">${signatureText.value}</div>
+    </div>
+    
+    <div class="right-print-section">
+      ${rightSectionHtml}
+    </div>
+  </div>
+</body>
+</html>
+`);
+    iframeDoc.close();
+    
+    // 等待iframe渲染完成后执行打印
+    await new Promise(resolve => setTimeout(resolve, 500));
+    
+    // 打印调试信息
+    console.log('打印字段位置信息:', {
+      证书类型: isUnitCertificate ? '单位证书' : '人员证书',
+      单位名称: currentData.unitName,
+      单位名称长度: unitName.length,
+      是否为长名称: isLongUnitName,
+      单位名称位置: `${printConfig.unit.unitName.top}px`,
+      证书编号位置: `${printConfig.unit.certNo.top}px`,
+      左侧签名位置: `${printConfig.leftSection.signature.top}px`,
+      二维码位置: `${printConfig.leftSection.qrCode.top}px`
+    });
+    
+    iframe.contentWindow.print();
+    
+    // 打印后移除iframe
+    setTimeout(() => {
+      document.body.removeChild(iframe);
+      // 恢复原页面样式
+      document.body.style.cssText = originalBodyStyle;
+      document.body.className = originalBodyClass;
+      document.documentElement.style.cssText = originalHtmlStyle;
+    }, 1000);
+    
+  } catch (error) {
+    console.error('打印失败:', error);
+    // 异常时恢复样式
+    document.body.style.cssText = originalBodyStyle;
+    document.body.className = originalBodyClass;
+    document.documentElement.style.cssText = originalHtmlStyle;
+  }
+};
+
+// 定义emit事件
+const emit = defineEmits(['update:certificate-data', 'update:left-signature', 'certificate-updated'])
+
+// 生命周期
+onMounted(() => {
+  adjustFieldWidths()
+  window.addEventListener('resize', adjustFieldWidths)
+  getPrintDataFields()
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', adjustFieldWidths)
+})
+
+// 监听器
+watch(() => editableData.value.certificateNo, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('certificateNo')
+  })
+})
+
+watch(() => editableData.value.personName, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('personName')
+  })
+})
+
+watch(() => editableData.value.position, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('position')
+  })
+})
+
+watch(() => editableData.value.unitName, () => {
+  nextTick(() => {
+    adjustFieldWidths()
+    adjustInputWidth('unitName')
+  })
+})
+
+watch(() => editableData.value.leftSignature, (newVal) => {
+  nextTick(() => {
+    adjustInputWidth('leftSignature')
+  })
+})
+</script>
+
+<style scoped>
+/* 基本样式 */
+.certificate-content {
+  padding: 0;
+  background-color: #f9f9f9;
+  min-height: calc(100vh - 100px);
+  font-family: "SimSun", "思源黑体", "STSong", serif;
+  font-size: 15pt;  /* 或 20px,根据实际需求 */
+  font-weight: normal;  /* 正常粗细 */
+}
+
+/* 顶部操作栏 */
+.top-action-bar {
+  padding: 10px 20px;
+  background: #fff;
+  border-bottom: 1px solid #e8e8e8;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+  flex-wrap: wrap;
+}
+
+/* 树状图区域 */
+.tree-section {
+  height: 100%;
+  padding: 10px;
+  background: #fff;
+  border-right: 1px solid #e8e8e8;
+}
+
+.tree-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #e4e7ed;
+}
+
+.tree-header h3 {
+  margin: 0;
+  font-size: 16px;
+  color: #303133;
+}
+
+.tree-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  max-height: calc(100vh - 200px);
+}
+
+.tree-node {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.tree-position {
+  font-size: 12px;
+  color: #909399;
+  margin-left: 10px;
+}
+
+.selected-info {
+  margin-top: 15px;
+  padding: 10px;
+  background: #f5f7fa;
+  border-radius: 4px;
+  text-align: center;
+  font-size: 14px;
+  color: #606266;
+}
+
+/* 证书容器 */
+.certificate-content {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 20px;
+}
+
+.cert-wrapper {
+  width: 1200px;
+  height: 650px;
+  display: flex;
+  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
+  background: #fff;
+  margin: 0 auto;
+  position: relative;
+}
+
+/* 左侧区域 */
+.left-section {
+  width: 50%;
+  padding: 40px 30px;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.qr-code-container {
+  text-align: center;
+  margin-top: 60px;
+  margin-bottom: 40px;
+}
+
+.qr-code-placeholder {
+  width: 180px;
+  height: 180px;
+  background: #f0f0f0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin: 0 auto 15px;
+  border: 1px dashed #ccc;
+}
+
+.left-signature-container {
+  width: 100%;
+  text-align: center;
+  margin-top: 40px;
+}
+
+.left-signature-field {
+  display: inline-block;
+  border-bottom: 2px solid #000;
+  text-align: center;
+  box-sizing: border-box;
+  min-width: 200px;
+  height: 30px;
+  vertical-align: bottom;
+  position: relative;
+  margin: 0 5px;
+  padding: 0 10px;
+  font-size: 16px;
+  line-height: 30px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.left-signature-field:hover {
+  background: rgba(40, 167, 69, 0.05);
+  border-bottom-color: #28a745;
+}
+
+.left-signature-input {
+  display: inline-block;
+  border: 2px solid #28a745;
+  background: rgba(40, 167, 69, 0.05);
+  text-align: center;
+  box-sizing: border-box;
+  min-width: 200px;
+  height: 30px;
+  vertical-align: bottom;
+  position: relative;
+  margin: 0 5px;
+  padding: 0 10px;
+  font-size: 16px;
+  line-height: 30px;
+  font-family: "SimSun", "思源黑体", "STSong", serif;
+  font-size: 16px;
+  outline: none;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+}
+
+.left-signature-input:focus {
+  background: rgba(40, 167, 69, 0.1);
+  box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.2);
+}
+
+/* 右侧区域 */
+.right-section {
+  width: 50%;
+  padding: 60px 60px;
+  margin-top: 60px;
+  box-sizing: border-box;
+  position: relative;
+}
+
+.title {
+  text-align: center;
+  font-size: 42px;
+  font-weight: bold;
+  margin: 5px 0 50px 0;
+  letter-spacing: 12px;
+}
+
+.title.unit-title-style {
+  font-size: 36px;
+  letter-spacing: 6px;
+  line-height: 1.4;
+  margin: 5px 0 40px 0;
+  word-break: keep-all;
+  white-space: nowrap;
+}
+
+.content {
+  font-size: 16px;
+  margin-bottom: 60px;
+  color: #333;
+  line-height: 2.2;
+}
+
+.unit-content {
+  text-align: center;
+}
+
+.unit-title {
+  font-size: 28px;
+  font-weight: bold;
+  margin-bottom: 50px;
+  color: #333;
+}
+
+.cert-no-line,
+.unit-line {
+  text-align: center;
+  margin-bottom: 40px;
+  white-space: nowrap;
+}
+
+.cert-no-label {
+  font-size: 16px;
+  margin-right: 10px;
+  display: inline-block;
+  vertical-align: middle;
+}
+
+.field {
+  display: inline-block;
+  border: 2px solid transparent;
+  border-bottom: 2px solid #000;
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 22px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  background: transparent;
+}
+
+.field:hover {
+  background: rgba(0, 102, 204, 0.05);
+  border-bottom-color: #0066cc;
+}
+
+.field.cert-no {
+  min-width: 250px;
+  font-size: 20px;
+  font-weight: normal;
+  letter-spacing: 1px;
+}
+
+.field.name,
+.field.unit-name {
+  min-width: 200px;
+}
+
+.field.position {
+  min-width: 180px;
+}
+
+.field-input {
+  display: inline-block;
+  border: 2px solid #0066cc;
+  background: rgba(0, 102, 204, 0.05);
+  text-align: center;
+  box-sizing: border-box;
+  vertical-align: middle;
+  position: relative;
+  min-height: 30px;
+  line-height: 30px;
+  padding: 0 10px;
+  font-size: 22px;
+  outline: none;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+}
+
+.field-input:focus {
+  background: rgba(0, 102, 204, 0.1);
+  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
+}
+
+.field-input.cert-no-input {
+  font-size: 20px;
+  font-weight: normal;
+  letter-spacing: 1px;
+}
+
+.content-line {
+  margin-bottom: 25px;
+  white-space: nowrap;
+}
+
+.signature {
+  position: absolute;
+  right: 60px;
+  bottom: 30px;
+  text-align: right;
+}
+
+.org-name {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: 20px;
+  margin-bottom: 20px;
+}
+
+.date {
+  font-size: 22px;
+  cursor: pointer;
+  padding: 2px 8px;
+  border-radius: 4px;
+  transition: all 0.3s ease;
+  display: inline-block;
+}
+
+.date:hover {
+  background: rgba(255, 193, 7, 0.05);
+}
+
+.date-input {
+  border: 2px solid #ffc107;
+  background: rgba(255, 193, 7, 0.05);
+  padding: 2px 8px;
+  font-size: 22px;
+  font-family: "SimSun", "思源黑体", "STSong", serif;
+  font-size: 22px;
+  outline: none;
+  border-radius: 4px;
+  text-align: center;
+  min-width: 150px;
+  transition: all 0.3s ease;
+  margin-left: 10px;
+}
+
+.date-input:focus {
+  background: rgba(255, 193, 7, 0.1);
+  box-shadow: 0 0 0 2px rgba(255, 193, 7, 0.2);
+}
+
+/* 响应式设计 */
+@media (max-width: 1200px) {
+  .cert-wrapper {
+    width: 95%;
+    max-width: 1200px;
+    height: auto;
+    min-height: 650px;
+  }
+}
+
+@media (max-width: 768px) {
+  .cert-wrapper {
+    flex-direction: column;
+    width: 100%;
+    height: auto;
+  }
+  
+  .left-section,
+  .right-section {
+    width: 100%;
+    padding: 30px 20px;
+  }
+  
+  .right-section {
+    margin-top: 0;
+  }
+  
+  .title {
+    font-size: 32px;
+    letter-spacing: 8px;
+    margin-bottom: 30px;
+  }
+  
+  .content {
+    font-size: 18px;
+    margin-bottom: 40px;
+  }
+  
+  .unit-title {
+    font-size: 24px;
+    margin-bottom: 30px;
+  }
+  
+  .field,
+  .field-input {
+    font-size: 18px;
+  }
+  
+  .field.cert-no,
+  .field-input.cert-no-input {
+    font-size: 16px;
+  }
+  
+  .org-name {
+    font-size: 20px;
+  }
+  
+  .date,
+  .date-input {
+    font-size: 18px;
+  }
+  
+  .action-buttons {
+    flex-direction: column;
+  }
+  
+  .action-buttons button {
+    width: 100%;
+    justify-content: center;
+  }
+}
+
+/* 打印时隐藏的样式 */
+@media print {
+  .no-print,
+  .field-input,
+  .left-signature-input,
+  .date-input,
+  .tree-section,
+  .top-action-bar {
+    display: none !important;
+  }
+  
+  .cert-wrapper {
+    box-shadow: none !important;
+    margin: 0 !important;
+  }
+  
+  .field,
+  .left-signature-field,
+  .date span {
+    display: inline-block !important;
+  }
+}
+</style>

+ 622 - 0
src/view/zs/zhengshuguanli.vue

@@ -0,0 +1,622 @@
+<template>
+    <div class="certificate-manager">
+      <!-- 模板选择 -->
+      <div class="template-selector">
+        <h3>选择证书模板</h3>
+        <div class="template-list">
+          <div v-for="template in templates" 
+               :key="template.id"
+               class="template-card"
+               @click="loadTemplate(template)">
+            <div class="template-name">{{ template.name }}</div>
+            <div class="template-actions">
+              <button @click.stop="editTemplate(template)">编辑</button>
+              <button @click.stop="deleteTemplate(template.id)">删除</button>
+            </div>
+          </div>
+          <div class="template-card add-new" @click="createNewTemplate">
+            + 添加新模板
+          </div>
+        </div>
+      </div>
+  
+      <!-- 编辑模式 / 填写模式 -->
+      <div v-if="currentTemplate" class="work-area">
+        <div class="mode-switcher">
+          <button @click="mode = 'edit'" :class="{ active: mode === 'edit' }">
+            📐 编辑模式(定位字段)
+          </button>
+          <button @click="mode = 'fill'" :class="{ active: mode === 'fill' }">
+            ✏️ 填写模式(批量生成)
+          </button>
+          <button @click="saveTemplateConfig">💾 保存模板</button>
+        </div>
+  
+        <!-- 编辑模式 -->
+        <div v-if="mode === 'edit'" class="edit-mode">
+          <div class="edit-tools">
+            <h4>字段列表</h4>
+            <button @click="addField">+ 添加字段</button>
+            <div v-for="(field, index) in fields" 
+                 :key="index"
+                 class="field-config"
+                 :class="{ active: selectedFieldId === field.id }"
+                 @click="selectField(field.id)">
+              <input v-model="field.name" placeholder="字段名称" />
+              <input v-model="field.placeholder" placeholder="提示文字" />
+              <input v-model="field.fontSize" type="number" placeholder="字号" />
+              <input v-model="field.color" type="color" />
+              <button @click.stop="removeField(field.id)">删除</button>
+            </div>
+          </div>
+  
+          <div class="pdf-editor-area">
+            <!-- PDF预览 -->
+            <div class="pdf-preview" ref="pdfContainer">
+              <canvas ref="pdfCanvas"></canvas>
+              
+              <!-- 字段定位层 -->
+              <div v-for="field in fields" 
+                   :key="field.id"
+                   class="field-marker"
+                   :style="getFieldStyle(field)"
+                   @mousedown="startDrag($event, field.id)">
+                <div class="field-label">{{ field.name }}</div>
+                <div class="field-preview">{{ field.placeholder }}</div>
+                <div class="resize-handle" @mousedown.stop="startResize($event, field.id)"></div>
+              </div>
+            </div>
+  
+            <!-- 坐标信息 -->
+            <div class="coordinate-info">
+              <div v-if="selectedField">
+                <h4>选中字段:{{ selectedField.name }}</h4>
+                <div>X: {{ selectedField.x }}px</div>
+                <div>Y: {{ selectedField.y }}px</div>
+                <div>字号: {{ selectedField.fontSize }}px</div>
+                <div>颜色: {{ selectedField.color }}</div>
+              </div>
+            </div>
+          </div>
+        </div>
+  
+        <!-- 填写模式 -->
+        <div v-if="mode === 'fill'" class="fill-mode">
+          <div class="batch-upload">
+            <h4>批量填写数据</h4>
+            <div class="upload-options">
+              <button @click="importExcel">导入Excel</button>
+              <button @click="addRow">手动添加</button>
+            </div>
+            
+            <!-- 数据表格 -->
+            <div class="data-table">
+              <table>
+                <thead>
+                  <tr>
+                    <th v-for="field in fields" :key="field.id">{{ field.name }}</th>
+                    <th>操作</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  <tr v-for="(row, index) in batchData" :key="index">
+                    <td v-for="field in fields" :key="field.id">
+                      <input v-model="row[field.name]" :placeholder="field.placeholder" />
+                    </td>
+                    <td>
+                      <button @click="previewRow(row)">预览</button>
+                      <button @click="generateSingle(row)">生成</button>
+                    </td>
+                  </tr>
+                </tbody>
+              </table>
+            </div>
+  
+            <div class="batch-actions">
+              <button @click="previewBatch">预览全部</button>
+              <button @click="generateBatch" :disabled="batchData.length === 0">
+                批量生成所有证书 ({{ batchData.length }}份)
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </template>
+  
+  <script setup>
+  import { ref, reactive, computed, onMounted } from 'vue'
+  import * as pdfjsLib from 'pdfjs-dist'
+  import { PDFDocument, rgb } from 'pdf-lib'
+  
+  // 初始化
+  pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`
+  
+  // 状态管理
+  const mode = ref('edit') // 'edit' or 'fill'
+  const currentTemplate = ref(null)
+  const selectedFieldId = ref(null)
+  
+  // 模板管理
+  const templates = ref([
+    { id: '1', name: '优秀员工证书', pdfFile: '', fields: [] },
+    { id: '2', name: '培训结业证书', pdfFile: '', fields: [] },
+    // 更多模板...
+  ])
+  
+  // 当前编辑的字段
+  const fields = ref([])
+  
+  // 批量数据
+  const batchData = ref([])
+  
+  // 计算属性
+  const selectedField = computed(() => {
+    return fields.value.find(f => f.id === selectedFieldId.value)
+  })
+  
+  // 创建新模板
+  const createNewTemplate = () => {
+    const templateId = Date.now().toString()
+    const newTemplate = {
+      id: templateId,
+      name: `新证书模板_${templateId.slice(-4)}`,
+      pdfFile: '',
+      fields: []
+    }
+    templates.value.push(newTemplate)
+    loadTemplate(newTemplate)
+  }
+  
+  // 加载模板
+  const loadTemplate = async (template) => {
+    currentTemplate.value = template
+    fields.value = [...template.fields]
+    mode.value = 'edit'
+    
+    // 加载PDF文件
+    if (template.pdfFile) {
+      await loadPDF(template.pdfFile)
+    }
+  }
+  
+  // 添加字段
+  const addField = () => {
+    const fieldId = Date.now().toString()
+    const newField = {
+      id: fieldId,
+      name: `字段${fields.value.length + 1}`,
+      placeholder: '请填写内容',
+      x: 100,
+      y: 100,
+      fontSize: 16,
+      color: '#000000',
+      fontFamily: 'SimHei' // 默认字体
+    }
+    fields.value.push(newField)
+    selectField(fieldId)
+  }
+  
+  // 选择字段
+  const selectField = (fieldId) => {
+    selectedFieldId.value = fieldId
+  }
+  
+  // 获取字段样式
+  const getFieldStyle = (field) => {
+    return {
+      left: `${field.x}px`,
+      top: `${field.y}px`,
+      fontSize: `${field.fontSize}px`,
+      color: field.color,
+      fontFamily: field.fontFamily,
+      border: field.id === selectedFieldId.value ? '2px solid #007bff' : '1px dashed #666'
+    }
+  }
+  
+  // 开始拖拽字段
+  const startDrag = (event, fieldId) => {
+    event.preventDefault()
+    selectField(fieldId)
+    
+    const field = fields.value.find(f => f.id === fieldId)
+    const startX = event.clientX
+    const startY = event.clientY
+    const startLeft = field.x
+    const startTop = field.y
+    
+    const onMouseMove = (e) => {
+      const dx = e.clientX - startX
+      const dy = e.clientY - startY
+      field.x = startLeft + dx
+      field.y = startTop + dy
+    }
+    
+    const onMouseUp = () => {
+      document.removeEventListener('mousemove', onMouseMove)
+      document.removeEventListener('mouseup', onMouseUp)
+    }
+    
+    document.addEventListener('mousemove', onMouseMove)
+    document.addEventListener('mouseup', onMouseUp)
+  }
+  
+  // 开始调整大小
+  const startResize = (event, fieldId) => {
+    event.stopPropagation()
+    const field = fields.value.find(f => f.id === fieldId)
+    const startX = event.clientX
+    const startSize = field.fontSize
+    
+    const onMouseMove = (e) => {
+      const dx = e.clientX - startX
+      field.fontSize = Math.max(12, Math.min(72, startSize + dx / 2))
+    }
+    
+    const onMouseUp = () => {
+      document.removeEventListener('mousemove', onMouseMove)
+      document.removeEventListener('mouseup', onMouseUp)
+    }
+    
+    document.addEventListener('mousemove', onMouseMove)
+    document.addEventListener('mouseup', onMouseUp)
+  }
+  
+  // 保存模板配置
+  const saveTemplateConfig = () => {
+    if (!currentTemplate.value) return
+    
+    // 更新当前模板的字段配置
+    const templateIndex = templates.value.findIndex(t => t.id === currentTemplate.value.id)
+    if (templateIndex !== -1) {
+      templates.value[templateIndex].fields = JSON.parse(JSON.stringify(fields.value))
+      
+      // 保存到localStorage或后端
+      localStorage.setItem('certificateTemplates', JSON.stringify(templates.value))
+      alert('模板已保存!')
+    }
+  }
+  
+  // 导入Excel数据(简化版)
+  const importExcel = () => {
+    // 这里可以集成 xlsx 库解析Excel
+    // 简化示例:模拟数据
+    const mockData = [
+      { 姓名: '张三', 职位: '高级工程师', 日期: '2024-01-15' },
+      { 姓名: '李四', 职位: '项目经理', 日期: '2024-01-16' },
+    ]
+    
+    batchData.value = mockData.map(row => {
+      const dataRow = {}
+      fields.value.forEach(field => {
+        dataRow[field.name] = row[field.name] || ''
+      })
+      return dataRow
+    })
+  }
+  
+  // 添加数据行
+  const addRow = () => {
+    const newRow = {}
+    fields.value.forEach(field => {
+      newRow[field.name] = ''
+    })
+    batchData.value.push(newRow)
+  }
+  
+  // 批量生成证书
+  const generateBatch = async () => {
+    try {
+      for (const [index, data] of batchData.value.entries()) {
+        console.log(`生成第 ${index + 1} 个证书`)
+        await generateSingleCertificate(data, index)
+      }
+      
+      alert(`已成功生成 ${batchData.value.length} 份证书`)
+    } catch (error) {
+      console.error('批量生成失败:', error)
+      alert('生成过程中出现错误')
+    }
+  }
+  
+  // 生成单个证书
+  const generateSingleCertificate = async (data, index) => {
+    if (!currentTemplate.value?.pdfFile) return
+    
+    // 1. 加载PDF模板
+    const pdfBytes = await fetch(currentTemplate.value.pdfFile).then(res => res.arrayBuffer())
+    const pdfDoc = await PDFDocument.load(pdfBytes)
+    
+    // 2. 加载中文字体
+    const fontUrl = '/fonts/SimHei.ttf'
+    const fontBytes = await fetch(fontUrl).then(res => res.arrayBuffer())
+    const chineseFont = await pdfDoc.embedFont(fontBytes)
+    
+    // 3. 获取页面
+    const pages = pdfDoc.getPages()
+    const page = pages[0]
+    const { width, height } = page.getSize()
+    
+    // 4. 转换坐标(Canvas坐标转PDF坐标)
+    const canvasWidth = pdfCanvas.value?.width || 800
+    const canvasHeight = pdfCanvas.value?.height || 600
+    
+    fields.value.forEach(field => {
+      const value = data[field.name]
+      if (!value) return
+      
+      // 坐标转换
+      const pdfX = (field.x / canvasWidth) * width
+      const pdfY = height - (field.y / canvasHeight) * height
+      const fontSize = (field.fontSize / canvasHeight) * 72 // 转换为PDF字号
+      
+      // 在PDF上绘制文字
+      page.drawText(value, {
+        x: pdfX,
+        y: pdfY,
+        size: fontSize,
+        font: chineseFont,
+        color: rgb(0, 0, 0),
+      })
+    })
+    
+    // 5. 保存PDF
+    const modifiedPdfBytes = await pdfDoc.save()
+    const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' })
+    const url = URL.createObjectURL(blob)
+    
+    // 6. 自动下载
+    const a = document.createElement('a')
+    a.href = url
+    a.download = `${currentTemplate.value.name}_${index + 1}.pdf`
+    document.body.appendChild(a)
+    a.click()
+    document.body.removeChild(a)
+    URL.revokeObjectURL(url)
+  }
+  
+  // 预览单个证书
+  const previewRow = async (data) => {
+    console.log('预览数据:', data)
+    // 这里可以实现在线预览功能
+  }
+  
+  // 批量预览
+  const previewBatch = () => {
+    console.log('预览全部证书')
+    // 实现批量预览功能
+  }
+  
+  // 编辑模板
+  const editTemplate = (template) => {
+    loadTemplate(template)
+  }
+  
+  // 删除模板
+  const deleteTemplate = (templateId) => {
+    if (confirm('确定删除这个模板吗?')) {
+      templates.value = templates.value.filter(t => t.id !== templateId)
+      localStorage.setItem('certificateTemplates', JSON.stringify(templates.value))
+    }
+  }
+  
+  // 加载PDF
+  const loadPDF = async (fileOrUrl) => {
+    // 实现PDF加载逻辑
+    console.log('加载PDF:', fileOrUrl)
+  }
+  </script>
+  
+  <style scoped>
+  .certificate-manager {
+    padding: 20px;
+    height: 100vh;
+    display: flex;
+    gap: 20px;
+  }
+  
+  .template-selector {
+    width: 280px;
+    border-right: 1px solid #ddd;
+    padding-right: 20px;
+  }
+  
+  .template-list {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+  }
+  
+  .template-card {
+    padding: 15px;
+    border: 1px solid #ddd;
+    border-radius: 8px;
+    cursor: pointer;
+    transition: all 0.3s;
+  }
+  
+  .template-card:hover {
+    border-color: #007bff;
+    background: #f8f9fa;
+  }
+  
+  .template-card.add-new {
+    text-align: center;
+    color: #007bff;
+    border-style: dashed;
+  }
+  
+  .template-name {
+    font-weight: bold;
+    margin-bottom: 5px;
+  }
+  
+  .template-actions {
+    display: flex;
+    gap: 5px;
+    margin-top: 10px;
+  }
+  
+  .work-area {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+  }
+  
+  .mode-switcher {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 20px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #ddd;
+  }
+  
+  .mode-switcher button.active {
+    background: #007bff;
+    color: white;
+  }
+  
+  .edit-mode {
+    flex: 1;
+    display: flex;
+    gap: 20px;
+  }
+  
+  .edit-tools {
+    width: 300px;
+    padding: 20px;
+    border-right: 1px solid #ddd;
+    overflow-y: auto;
+  }
+  
+  .field-config {
+    padding: 10px;
+    margin-bottom: 10px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    cursor: pointer;
+  }
+  
+  .field-config.active {
+    border-color: #007bff;
+    background: #e3f2fd;
+  }
+  
+  .field-config input {
+    width: 100%;
+    margin-bottom: 5px;
+    padding: 5px;
+    border: 1px solid #ccc;
+    border-radius: 3px;
+  }
+  
+  .pdf-editor-area {
+    flex: 1;
+    display: flex;
+    gap: 20px;
+  }
+  
+  .pdf-preview {
+    flex: 1;
+    position: relative;
+    border: 1px solid #ddd;
+    background: #f9f9f9;
+    overflow: auto;
+    min-height: 600px;
+  }
+  
+  .field-marker {
+    position: absolute;
+    padding: 5px;
+    cursor: move;
+    background: rgba(255, 255, 255, 0.8);
+    border-radius: 3px;
+    user-select: none;
+  }
+  
+  .field-label {
+    font-size: 12px;
+    color: #666;
+    margin-bottom: 2px;
+  }
+  
+  .field-preview {
+    white-space: nowrap;
+    min-width: 50px;
+    min-height: 20px;
+  }
+  
+  .resize-handle {
+    position: absolute;
+    right: -5px;
+    bottom: -5px;
+    width: 10px;
+    height: 10px;
+    background: #007bff;
+    border-radius: 50%;
+    cursor: nwse-resize;
+  }
+  
+  .coordinate-info {
+    width: 250px;
+    padding: 20px;
+    background: #f8f9fa;
+    border-radius: 8px;
+  }
+  
+  .fill-mode {
+    padding: 20px;
+  }
+  
+  .batch-upload {
+    background: white;
+    padding: 20px;
+    border-radius: 8px;
+    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+  }
+  
+  .upload-options {
+    display: flex;
+    gap: 10px;
+    margin: 15px 0;
+  }
+  
+  .data-table {
+    margin: 20px 0;
+    max-height: 400px;
+    overflow-y: auto;
+  }
+  
+  .data-table table {
+    width: 100%;
+    border-collapse: collapse;
+  }
+  
+  .data-table th,
+  .data-table td {
+    border: 1px solid #ddd;
+    padding: 10px;
+    text-align: left;
+  }
+  
+  .data-table th {
+    background: #f8f9fa;
+    font-weight: bold;
+  }
+  
+  .data-table input {
+    width: 100%;
+    padding: 5px;
+    border: 1px solid #ccc;
+    border-radius: 3px;
+  }
+  
+  .batch-actions {
+    margin-top: 20px;
+    padding-top: 20px;
+    border-top: 1px solid #ddd;
+    display: flex;
+    gap: 10px;
+    justify-content: flex-end;
+  }
+  </style>