zck 1 maand geleden
bovenliggende
commit
bbb632cf90

+ 144 - 1
src/api/mes/job.js

@@ -1228,4 +1228,147 @@ export const GetTxtToTxt = (params) => {
     method: 'get',
     params
   })
-}
+}
+
+
+
+
+//商铺列表
+export const merchantList = (params) => {
+  return service({
+    url: '/mes_server/merchant/merchantList',
+    method: 'get',
+    params
+  })
+}
+
+//商户详情
+export const merchantDetail = (params) => {
+  return service({
+    url: '/mes_server/merchant/merchantDetail',
+    method: 'get',
+    params
+  })
+}
+
+//商户添加
+export const merchantAdd = (data) => {
+  return service({
+    url: '/mes_server/merchant/merchantAdd',
+    method: 'post',
+    data
+  })
+}
+
+//商户修改
+export const merchantEdit = (data) => {
+  return service({
+    url: '/mes_server/merchant/merchantEdit',
+    method: 'post',
+    data
+  })
+}
+
+//商户删除
+export const merchantDelete = (params) => {
+  return service({
+    url: '/mes_server/merchant/merchantDelete',
+    method: 'get',
+    params
+  })
+}
+
+//单条修改商户状态
+export const merchantChangeStatus = (params) => {
+  return service({
+    url: '/mes_server/merchant/merchantChangeStatus',
+    method: 'get',
+    params
+  })
+}
+
+//批量更新商户状态
+export const merchantBatchChangeStatus = (params) => {
+  return service({
+    url: '/mes_server/merchant/merchantBatchChangeStatus',
+    method: 'get',
+    params
+  })
+}
+
+//操作日志数据列表
+export const LogList = (params) => {
+  return service({
+    url: '/mes_server/merchant/LogList',
+    method: 'get',
+    params
+  })
+}
+
+//左侧商户菜单
+export const merchantGetab = (params) => {
+  return service({
+    url: '/mes_server/product/merchantGetab',
+    method: 'get',
+    params
+  })
+}
+
+
+
+//产品列表
+export const productList = (params) => {
+  return service({
+    url: '/mes_server/product/productList',
+    method: 'get',
+    params
+  })
+}
+
+//产品数据详情
+export const productDetail = (params) => {
+  return service({
+    url: '/mes_server/product/productDetail',
+    method: 'get',
+    params
+  })
+}
+
+//模版
+export const product_template = (params) => {
+  return service({
+    url: '/mes_server/work_order/product_template',
+    method: 'get',
+    params
+  })
+}
+
+//获取单条产品数据信息
+export const GetProductFind = (params) => {
+  return service({
+    url: '/mes_server/product/GetProductFind',
+    method: 'get',
+    params
+  })
+}
+
+
+//新增产品
+export const productAdd = (data) => {
+  return service({
+    url: '/mes_server/product/productAdd',
+    method: 'post',
+    data
+  })
+}
+
+//获取商户ID
+export const getMerchantId = (params) => {
+  return service({
+    url: '/mes_server/product/getMerchantId',
+    method: 'get',
+    params
+  })
+}
+
+

+ 7 - 7
src/view/login/index.vue

@@ -265,13 +265,13 @@ GetAddr();
 
 
 
-userStore.LoginIn2({
-	   username: "CF01#",
-	   password: "123456",
-	   captcha: "443188",
-	   captchaId: "0FZfnUtAOrdXvTETkJRc",
-	   openCaptcha: false,
-	 });
+// userStore.LoginIn2({
+// 	   username: "CF01#",
+// 	   password: "123456",
+// 	   captcha: "443188",
+// 	   captchaId: "0FZfnUtAOrdXvTETkJRc",
+// 	   openCaptcha: false,
+// 	 });
 </script>
 
 

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

@@ -0,0 +1,1762 @@
+<template>
+  <div>
+    <layout>
+      <layout-header>
+        <!-- 搜索区域 -->
+        <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"  @click="onADD" >新增产品</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>
+      </layout-header>
+
+      <layout>
+        <!-- 左侧树形结构 -->
+        <template v-if="userStore.userInfo.nickName === '超级管理员'">
+        <layout-sider :resize-directions="['right']" :width="190" 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="txtimgDialogVisible" title="文生图" width="660px" top="10%">
+      <el-form label-width="100px">
+        <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-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 class="image-title">原图</div>
+    <div style="display: flex; justify-content: space-between; width: 100%;">
+      <div style="display: flex; gap: 12px;">
+        <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">
+      <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
+      />
+      <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">
+      <el-image
+        v-if="editFormData.new_image_url"
+        :src="formatImageUrl(editFormData.new_image_url)"
+        :preview-src-list="[formatImageUrl(editFormData.new_image_url)]"
+        style="width: 100%; height: 200px;"
+        fit="contain"
+        preview-teleported
+      />
+      <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%; flex: 1;"
+        />
+    </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 size="small" @click="clearInput">清空</el-button>
+    <el-button type="primary" size="small" @click="optimizeContent">内容优化</el-button>
+    <el-button type="success" size="small" @click="generateImage">生成图片</el-button>
+  </div>
+</div>
+  </div>
+</div>
+    
+    </div>
+    
+    <!-- 右侧:模板列表 -->
+    <div class="right-column" style="flex: 1; border-left: 1px solid #e4e7ed; padding-left: 20px;">
+  <div class="right-template" style="height: 100%; display: flex; flex-direction: column;">
+    <div class="template-header" style="margin-bottom: 15px;">
+      <h4>参考模板 ({{ templateList.length }})</h4>
+      <el-tag type="warning" size="small">点击选择</el-tag>
+    </div>
+    
+    <!-- 搜索框 -->
+    <div class="template-search" style="margin-bottom: 15px;">
+      <el-input
+        v-model="searchKeyword"
+        placeholder="输入关键词搜索模板..."
+        clearable
+        size="small"
+        @keyup.enter="handleSearch"
+        @clear="handleClearSearch"
+      >
+        <template #prefix>
+          <el-icon><Search /></el-icon>
+        </template>
+        <template #append>
+          <el-button
+            :icon="Search"
+            size="small"
+            :loading="searchLoading"
+            @click="handleSearch"
+          />搜索
+        </template>
+      </el-input>
+    </div>
+    
+    <!-- 模板列表 -->
+    <div style="flex: 1; min-height: 0;">
+      <el-scrollbar style="height: 100%;">
+        <div class="template-list">
+          <!-- 搜索结果为空提示 -->
+          <div v-if="searchLoading" class="loading-state">
+            <el-icon class="loading-icon" :size="30"><Loading /></el-icon>
+            <p>搜索中...</p>
+          </div>
+          
+          <!-- 搜索无结果提示 -->
+          <div v-if="!searchLoading && searchKeyword && templateList.length === 0" class="empty-search">
+            <el-icon :size="40"><Search /></el-icon>
+            <p>未找到相关模板</p>
+            <el-button
+              type="text"
+              size="small"
+              @click="handleClearSearch"
+              style="margin-top: 10px;"
+            >
+              查看全部模板
+            </el-button>
+          </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">
+              <el-image
+                :src="template.template_image_url"
+                :preview-src-list="[template.template_image_url]"
+                style="width: 100%; height: 100%;"
+                fit="cover"
+                preview-teleported
+                hide-on-click-modal
+                @click.stop="handleTemplatePreview($event, template)"
+              >
+                <template #error>
+                  <div class="thumbnail-error">
+                    <el-icon><Picture /></el-icon>
+                  </div>
+                </template>
+              </el-image>
+              <div class="template-id">模板 {{ template.id }}</div>
+            </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>
+    </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 } 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 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 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 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,
+//     }
+
+//     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 = 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 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;
+    border: 1px solid #dcdfe6;
+    border-radius: 4px;
+    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;
+    gap: 10px;
+    justify-content: center;
+    padding: 10px 0;
+  }
+  
+  .edit-section {
+    flex: 1;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    padding: 15px;
+    min-height: 200px;
+    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 {
+    flex: 1;
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    padding: 15px;
+    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: column;
+    gap: 10px;
+    overflow-y: auto;
+    min-height: 0; /* 防止溢出 */
+  }
+  
+  .template-item {
+    display: flex;
+    align-items: center;
+    padding: 10px;
+    border: 1px solid #e4e7ed;
+    border-radius: 6px;
+    cursor: pointer;
+    transition: all 0.3s;
+    background: white;
+    flex-shrink: 0; /* 防止被压缩 */
+  }
+  
+  .template-item:hover {
+    border-color: #409eff;
+    background: #f5faff;
+  }
+  
+  .template-item.active {
+    border-color: #409eff;
+    background: #ecf5ff;
+    border-width: 2px;
+  }
+  
+  .template-thumbnail {
+    position: relative;
+    width: 160px;
+    height: 140px;
+    flex-shrink: 0;
+    margin-right: 12px;
+    border-radius: 4px;
+    overflow: hidden;
+    background: #f5f7fa;
+  }
+  
+  .template-thumbnail img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
+  
+  .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;
+  }
+  
+  .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>

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

@@ -0,0 +1,551 @@
+<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>