TestTemplate.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <template>
  2. <div>
  3. <!-- 搜索区域 -->
  4. <el-form inline>
  5. <el-form-item>
  6. <el-input v-model="searchInfo" placeholder="搜索关键字" clearable style="width: 300px;" />
  7. <!-- 文件夹筛选下拉菜单 -->
  8. <el-dropdown @command="handleFolderCommand" style="margin-right: 10px;">
  9. <el-button icon="search" title="文件夹筛选">
  10. {{ selectedFolder || '文件夹筛选' }}<el-icon class="el-icon--right"><arrow-down /></el-icon>
  11. </el-button>
  12. <template #dropdown>
  13. <el-dropdown-menu>
  14. <el-dropdown-item command="">全部文件夹</el-dropdown-item>
  15. <el-dropdown-item v-for="(folder, index) in folderList" :key="index" :command="folder">
  16. {{ folder }}
  17. </el-dropdown-item>
  18. </el-dropdown-menu>
  19. </template>
  20. </el-dropdown>
  21. <el-button type="primary" icon="search" @click="onSubmit" title="搜索">查询</el-button>
  22. <el-button type="primary" title="选择图片,文生图" @click="texttoimg" :loading="isLoading">文生图</el-button>
  23. <el-button type="primary" title="选择图片,图生图" @click="imgtoimg" :loading="isLoading">图生图</el-button>
  24. </el-form-item>
  25. </el-form>
  26. <!-- 文生图弹窗 -->
  27. <el-dialog v-model="txtimgDialogVisible" title="文生图" width="660px" top="10%">
  28. <el-form label-width="100px">
  29. <!-- <el-form-item label="提示词" prop="chinese_description">
  30. <el-input
  31. v-model="form.chinese_description"
  32. type="textarea"
  33. :rows="3"
  34. placeholder="请输入文生图描述"
  35. />
  36. </el-form-item> -->
  37. <el-form-item label="文生图模型">
  38. <div style="display: flex; align-items: center; gap: 20px; flex-wrap: wrap;">
  39. <el-radio-group v-model="txtimgselectedModel" @change="handleModelChange">
  40. <el-radio
  41. v-for="item in txttoimg_modelList"
  42. :key="item.id"
  43. :label="item.txttoimg">
  44. {{ item.txttoimg }}
  45. <span v-if="item.id === usedIds.wenshengtu" style="color: #67C23A; margin-left: 5px;">(默认使用)</span>
  46. </el-radio>
  47. </el-radio-group>
  48. <el-button type="success" @click="setActive('wenshengtu')" :disabled="!selectedIds.wenshengtu || selectedIds.wenshengtu === usedIds.wenshengtu">
  49. 设为默认使用模型
  50. </el-button>
  51. </div>
  52. </el-form-item>
  53. </el-form>
  54. <template #footer>
  55. <el-button @click="txtimgDialogVisible = false">取消</el-button>
  56. <el-button type="primary" @click="generateTxtImg" :loading="isGenerating">生成文生图</el-button>
  57. </template>
  58. </el-dialog>
  59. <!-- 表格展示 -->
  60. <el-table
  61. ref="multipleTable"
  62. style="width: 100%; height: 62vh;"
  63. :row-style="{ height: '20px' }"
  64. :header-cell-style="{ padding: '0px' }"
  65. :cell-style="{ padding: '0px' }"
  66. :header-row-style="{ height: '20px' }"
  67. border tooltip-effect="dark"
  68. :data="tableData1"
  69. row-key="ID"
  70. highlight-current-row
  71. :cell-class-name="tableDataCellClass"
  72. @selection-change="SelectionChange"
  73. @row-dblclick="onRowDblClick"
  74. :show-overflow-tooltip="true"
  75. >
  76. <el-table-column type="selection" width="55" />
  77. <el-table-column label="ID" prop="id" width="70" />
  78. <el-table-column label="第一段" prop="chinese_description" width="300" />
  79. <el-table-column label="第二段" prop="english_description" width="300" />
  80. <el-table-column label="图片名称" prop="img_name" width="300" />
  81. <el-table-column label="原图" width="120">
  82. <template #default="{ row }">
  83. <el-image
  84. :src="formatImageUrl(row.old_image_url)"
  85. :preview-src-list="[formatImageUrl(row.old_image_url)]"
  86. style="width: 90px; height: 70px;"
  87. fit="contain"
  88. preview-teleported
  89. />
  90. </template>
  91. </el-table-column>
  92. <el-table-column
  93. v-if="tableData1.some(row => row.model === 'black-forest-labs/FLUX.1-kontext-pro')"
  94. label="FLUX.1"
  95. width="120">
  96. <template #default="{ row }">
  97. <el-image
  98. v-if="row.model === 'black-forest-labs/FLUX.1-kontext-pro'"
  99. :src="formatImageUrl(row.new_image_url)"
  100. :preview-src-list="[formatImageUrl(row.new_image_url)]"
  101. style="width: 90px; height: 70px;"
  102. fit="contain"
  103. preview-teleported
  104. />
  105. <span v-else>-</span>
  106. </template>
  107. </el-table-column>
  108. <el-table-column
  109. v-if="tableData1.some(row => row.model === 'dall-e-3')"
  110. label="dall-e-3"
  111. width="120">
  112. <template #default="{ row }">
  113. <el-image
  114. v-if="row.model === 'dall-e-3'"
  115. :src="formatImageUrl(row.new_image_url)"
  116. :preview-src-list="[formatImageUrl(row.new_image_url)]"
  117. style="width: 90px; height: 70px;"
  118. fit="contain"
  119. preview-teleported
  120. />
  121. <span v-else>-</span>
  122. </template>
  123. </el-table-column>
  124. <el-table-column
  125. v-if="tableData1.some(row => row.model === 'gpt-image-1')"
  126. label="gpt-image-1"
  127. width="120">
  128. <template #default="{ row }">
  129. <el-image
  130. v-if="row.model === 'gpt-image-1'"
  131. :src="formatImageUrl(row.new_image_url)"
  132. :preview-src-list="[formatImageUrl(row.new_image_url)]"
  133. style="width: 90px; height: 70px;"
  134. fit="contain"
  135. preview-teleported
  136. />
  137. <span v-else>-</span>
  138. </template>
  139. </el-table-column>
  140. <el-table-column
  141. v-if="tableData1.some(row => row.model === 'MID_JOURNEY')"
  142. label="MID_JOURNEY"
  143. width="130">
  144. <template #default="{ row }">
  145. <el-image
  146. v-if="row.model === 'MID_JOURNEY'"
  147. :src="`https://chatapi.onechats.ai/mj/image/${row.taskId}`"
  148. :preview-src-list="[`https://chatapi.onechats.ai/mj/image/${row.taskId}`]"
  149. style="width: 90px; height: 70px;"
  150. fit="contain"
  151. preview-teleported
  152. />
  153. <span v-else>-</span>
  154. </template>
  155. </el-table-column>
  156. <el-table-column label="图生图预览" width="120">
  157. <template #default="{ row }">
  158. <el-image
  159. :src="formatImageUrl(row.imgtoimg_url)"
  160. :preview-src-list="[formatImageUrl(row.imgtoimg_url)]"
  161. style="width: 90px; height: 70px;"
  162. fit="contain"
  163. preview-teleported
  164. />
  165. </template>
  166. </el-table-column>
  167. </el-table>
  168. <!-- 分页 -->
  169. <div class="gva-pagination">
  170. <el-pagination
  171. @size-change="handleSizeChange"
  172. @current-change="handleCurrentChange"
  173. :current-page="page"
  174. :page-sizes="[10, 30, 50, 100]"
  175. :page-size="pageSize"
  176. layout="total, sizes, prev, pager, next, jumper"
  177. :total="total"
  178. />
  179. </div>
  180. <el-dialog
  181. v-model="editDialogVisible"
  182. title="查看信息"
  183. style="width: 70%; height: 80%;top: -6%;"
  184. :modal="true">
  185. <el-scrollbar style="max-height: 60vh; overflow-y: auto;">
  186. <el-form :model="editFormData">
  187. <el-form-item label="ID" label-width="80px">
  188. <el-input v-model="editFormData.id" disabled />
  189. </el-form-item>
  190. <el-form-item label="文生图路径">
  191. <el-input v-model="editFormData.new_image_url" disabled />
  192. </el-form-item>
  193. <el-form-item label="图生图路径">
  194. <el-input v-model="editFormData.imgtoimg_url" disabled />
  195. </el-form-item>
  196. <el-form-item label="第一段">
  197. <el-input
  198. type="textarea"
  199. v-model="editFormData.chinese_description"
  200. :rows="6"
  201. />
  202. </el-form-item>
  203. <el-form-item label="第二段">
  204. <el-input
  205. type="textarea"
  206. v-model="editFormData.english_description"
  207. :rows="6"
  208. />
  209. </el-form-item>
  210. <el-form-item label="图片名称">
  211. <el-input v-model="editFormData.img_name" />
  212. </el-form-item>
  213. </el-form>
  214. </el-scrollbar>
  215. <template #footer>
  216. <el-button @click="editDialogVisible = false">取消</el-button>
  217. <el-button type="primary" @click="submitEdit">确定</el-button>
  218. </template>
  219. </el-dialog>
  220. <!-- 图片预览 -->
  221. <el-dialog v-model="dialogVisible" title="图片预览" width="70%" top="5vh" destroy-on-close>
  222. <div style="text-align: center;">
  223. <el-image :src="currentImageUrl" style="max-width: 100%; max-height: 70vh;" fit="contain" />
  224. </div>
  225. </el-dialog>
  226. </div>
  227. </template>
  228. <script setup>
  229. import { ref, reactive, toRaw, onMounted } from 'vue'
  230. import { ElMessage } from 'element-plus'
  231. import { getTable, imageToText, Template_ids,txttoimg_moxing,GetHttpUrl,txttoimg_update, getSide } from '@/api/mes/job'
  232. import { useUserStore } from '@/pinia/modules/user'
  233. import { ArrowDown } from '@element-plus/icons-vue'
  234. //获取登录用户信息
  235. const userStore = useUserStore()
  236. const _username = ref('')
  237. _username.value = userStore.userInfo.userName + '/' + userStore.userInfo.nickName
  238. console.log('获取用户信息',_username.value)
  239. console.log('获取用户名称',userStore.userInfo.nickName)
  240. //获取服务器地址
  241. const baseUrl = ref('')
  242. const port = ref('')
  243. const full_url = ref('')
  244. // 从接口获取服务器地址
  245. const fetchServerUrl = async () => {
  246. try {
  247. const res = await GetHttpUrl()
  248. if (res.code === 0 && res.data && res.data.full_url) {
  249. full_url.value = res.data.full_url
  250. baseUrl.value = res.data.baseUrl
  251. port.value = res.data.port
  252. console.log('获取服务器地址',full_url.value)
  253. console.log('IP地址',baseUrl.value)
  254. console.log('端口',port.value)
  255. }
  256. } catch (error) {
  257. console.error('获取服务器地址失败:', error)
  258. }
  259. }
  260. fetchServerUrl();
  261. const searchInfo = ref('')
  262. const tableData1 = ref([])
  263. const total = ref(0)
  264. const page = ref(1)
  265. const pageSize = ref(50)
  266. const dialogVisible = ref(false)
  267. const currentImageUrl = ref('')
  268. const width = ref('')
  269. const height = ref('')
  270. const isLoading = ref(false)
  271. const txttotxt_selectedOption = ref('gemini-2.0-flash')
  272. const selectedOption = ref('dall-e-3')
  273. const num = ref(1)
  274. const _parh = ref([])
  275. const folderList = ref([])
  276. const selectedFolder = ref('')
  277. const editDialogVisible = ref(false)
  278. const editFormData = reactive({
  279. id: '',
  280. chinese_description: '',
  281. english_description: '',
  282. new_image_url: '',
  283. imgtoimg_url: '',
  284. img_name: ''
  285. })
  286. // 定义文生图表单对象
  287. const form = reactive({
  288. chinese_description: ''
  289. })
  290. const formatImageUrl = (path) => {
  291. if (!path) return ''
  292. return `${full_url.value}/${path.replace(/^public\//, '')}`
  293. }
  294. const txttoimg_modelList = ref([]); // 存储所有模型数据
  295. const txtimgselectedModel = ref(''); // 当前选中的模型名称
  296. const usedId = ref(null); // 当前使用的模型ID(兼容旧代码)
  297. const selectedId = ref(null); // 用户选择的模型ID(兼容旧代码)
  298. const txtimgDialogVisible = ref(false); // 文生图弹窗可见性
  299. const isGenerating = ref(false); // 文生图生成中状态
  300. const usedIds = ref({}); // 当前使用的模型ID集合
  301. const selectedIds = ref({}); // 用户选择的模型ID集合
  302. // 获取文生图模型列表
  303. const fetchTxtImgModels = async () => {
  304. try {
  305. const response = await txttoimg_moxing();
  306. txttoimg_modelList.value = response.data.models.wenshengtu;
  307. // 获取当前使用的模型ID
  308. usedIds.value = response.data.used_ids;
  309. const currentUsedId = usedIds.value.wenshengtu;
  310. // 设置默认选中的模型
  311. const defaultModel = txttoimg_modelList.value.find(item => item.id === currentUsedId);
  312. if (defaultModel) {
  313. txtimgselectedModel.value = defaultModel.txttoimg;
  314. usedId.value = defaultModel.id;
  315. selectedId.value = defaultModel.id;
  316. selectedIds.value.wenshengtu = defaultModel.id;
  317. selectedOption.value = defaultModel.txttoimg;
  318. }
  319. console.log('文生图模型列表:', txttoimg_modelList.value);
  320. console.log('当前使用的模型ID:', currentUsedId);
  321. console.log('默认选中的模型:', defaultModel);
  322. } catch (error) {
  323. console.error('获取文生图模型列表失败:', error);
  324. ElMessage.error('获取模型列表失败');
  325. }
  326. }
  327. fetchTxtImgModels();
  328. const getTableData = async () => {
  329. const res = await getTable({
  330. search: searchInfo.value,
  331. limit: pageSize.value,
  332. page: page.value,
  333. folder: selectedFolder.value
  334. })
  335. tableData1.value = res.data.list
  336. total.value = res.data.total
  337. // 更新文件夹列表
  338. if (res.data.folder) {
  339. folderList.value = res.data.folder
  340. }
  341. // const res_Template = await Template_ids({path:row['old_image_url']});
  342. // height.value = res_Template.data.height
  343. // width.value = res_Template.data.width
  344. }
  345. // 当选择模型变化时
  346. const handleModelChange = (modelName) => {
  347. const model = txttoimg_modelList.value.find(item => item.txttoimg === modelName);
  348. if (model) {
  349. selectedId.value = model.id;
  350. selectedIds.value.wenshengtu = model.id;
  351. selectedOption.value = model.txttoimg;
  352. console.log('选择的模型ID:', model.id);
  353. }
  354. }
  355. // 设置默认模型
  356. const setActive = async (type) => {
  357. const id = selectedIds.value[type]
  358. if (id) {
  359. try {
  360. // 调用API设置默认模型
  361. const res = await txttoimg_update({ id: id,type:"wenshengtu"})
  362. if (res.code === 0) {
  363. usedIds.value[type] = id
  364. ElMessage.success('默认模型设置成功')
  365. } else {
  366. ElMessage.error(res.msg || '设置默认模型失败')
  367. }
  368. } catch (error) {
  369. console.error('设置默认模型失败', error)
  370. ElMessage.error('设置默认模型失败')
  371. }
  372. }
  373. }
  374. // 处理文件夹选择
  375. const handleFolderCommand = (command) => {
  376. selectedFolder.value = command
  377. onSubmit()
  378. }
  379. const onSubmit = () => {
  380. page.value = 1
  381. getTableData()
  382. }
  383. const handleCurrentChange = (val) => {
  384. page.value = val
  385. getTableData()
  386. }
  387. const handleSizeChange = (val) => {
  388. pageSize.value = val
  389. page.value = 1
  390. getTableData()
  391. }
  392. const SelectionChange = (rows) => {
  393. _parh.value = rows.map(item => item.old_image_url)
  394. }
  395. // 文生图生成函数
  396. const generateTxtImg = () => {
  397. txtimgDialogVisible.value = false
  398. processImages(toRaw(_parh.value), '文生图', form.chinese_description)
  399. }
  400. const texttoimg = async () => {
  401. if (_parh.value.length === 0) {
  402. ElMessage.warning('请先选择要处理的图片')
  403. return
  404. }
  405. // 获取模型列表
  406. await fetchTxtImgModels()
  407. // 显示弹窗
  408. txtimgDialogVisible.value = true
  409. }
  410. const imgtoimg = () => {
  411. processImages(toRaw(_parh.value), '图生图')
  412. }
  413. // 是否执行几何图(默认不执行)
  414. const executeKeywords = ref(false)
  415. const processImages = async (files, type, prompt = '') => {
  416. if (!Array.isArray(files) || files.length === 0) {
  417. ElMessage.warning('请选择要处理的图片')
  418. return
  419. }
  420. const pathGroups = {}
  421. _parh.value.forEach(path => {
  422. const dir = path.replace(/\/[^\/]*$/, '')
  423. if (!pathGroups[dir]) pathGroups[dir] = []
  424. pathGroups[dir].push(path)
  425. })
  426. isLoading.value = true
  427. const failList = []
  428. for (const [dir, images] of Object.entries(pathGroups)) {
  429. const payload = {
  430. old_image_file: dir,
  431. type,
  432. selectedOption: selectedOption.value,
  433. txttotxt_selectedOption: txttotxt_selectedOption.value,
  434. batch: images,
  435. num: num.value,
  436. width: width.value,
  437. height: height.value,
  438. executeKeywords:executeKeywords.value,
  439. }
  440. // console.log(payload);
  441. // await new Promise(resolve => setTimeout(resolve, 2000));
  442. // isLoading.value = false;
  443. // return;
  444. try {
  445. await imageToText(payload)
  446. ElMessage.success(`处理目录:${dir},共 ${images.length} 张 * ${num.value} 次`)
  447. } catch (e) {
  448. console.warn('处理失败:', dir, e)
  449. images.forEach((_, i) => failList.push(`目录 ${dir} 第 ${i + 1} 张`))
  450. }
  451. }
  452. if (failList.length > 0) {
  453. ElMessage.warning('部分图片处理失败,请查看控制台')
  454. }
  455. await new Promise(resolve => setTimeout(resolve, 3000))
  456. isLoading.value = false
  457. }
  458. const previewImage = (url) => {
  459. currentImageUrl.value = url
  460. dialogVisible.value = true
  461. }
  462. const onRowDblClick = (row) => {
  463. editFormData.id = row.id
  464. editFormData.chinese_description = row.chinese_description
  465. editFormData.english_description = row.english_description
  466. editFormData.img_name = row.img_name
  467. editFormData.new_image_url = row.new_image_url
  468. editFormData.imgtoimg_url = row.imgtoimg_url
  469. editDialogVisible.value = true
  470. }
  471. const submitEdit = async () => {
  472. try {
  473. const { id, chinese_description, english_description, img_name } = editFormData;
  474. const payload = {
  475. id,
  476. chinese_description,
  477. english_description,
  478. img_name
  479. };
  480. await getSide(payload);
  481. ElMessage.success('修改成功');
  482. editDialogVisible.value = false;
  483. getTableData();
  484. } catch (error) {
  485. console.error('修改失败:', error);
  486. ElMessage.error('修改失败,请稍后重试');
  487. }
  488. };
  489. onMounted(() => {
  490. getTableData()
  491. })
  492. </script>
  493. <style scoped>
  494. :deep(.el-table td .cell) {
  495. line-height: 22px !important;
  496. }
  497. :deep(.el-dialog__body) {
  498. padding: 10px 20px;
  499. }
  500. .gva-pagination {
  501. margin-top: 10px;
  502. text-align: right;
  503. }
  504. :deep(.el-table__body tr.current-row) > td {
  505. background: #ff80ff !important;
  506. }
  507. </style>