text_to_video.html 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  1. <!doctype html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>生成模板 - 文生视频</title>
  8. <!-- 引入Font Awesome图标 -->
  9. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  10. <!-- 引入自定义CSS -->
  11. <link rel="shortcut icon" href="__CDN__/assets/img/favicon.ico"/>
  12. <style>
  13. /* 自定义提示框样式 */
  14. .custom-alert {
  15. position: fixed;
  16. top: 10%;
  17. left: 50%;
  18. transform: translate(-50%, -50%);
  19. padding: 15px 25px;
  20. border-radius: 8px;
  21. color: white;
  22. font-size: 14px;
  23. font-weight: 500;
  24. z-index: 10000;
  25. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  26. opacity: 0;
  27. visibility: hidden;
  28. transition: all 0.3s ease;
  29. max-width: 80%;
  30. text-align: center;
  31. }
  32. .custom-alert.show {
  33. opacity: 1;
  34. visibility: visible;
  35. }
  36. .custom-alert.error {
  37. background-color: #e74c3c;
  38. }
  39. .custom-alert.success {
  40. background-color: #28a745;
  41. }
  42. .custom-alert.warning {
  43. background-color: #ffc107;
  44. color: #333;
  45. }
  46. .custom-alert.info {
  47. background-color: #17a2b8;
  48. }
  49. * {
  50. margin: 0;
  51. padding: 0;
  52. box-sizing: border-box;
  53. }
  54. /* 标题样式 */
  55. .page-title {
  56. font-size: 24px;
  57. font-weight: bold;
  58. margin-bottom: 25px;
  59. color: #333;
  60. }
  61. /* 内容区域 */
  62. .content-area {
  63. display: grid;
  64. grid-template-columns: 1fr 1fr;
  65. gap: 30px;
  66. }
  67. /* 左侧输入区域 */
  68. .input-section {
  69. background-color: #fff;
  70. border-radius: 8px;
  71. padding: 20px;
  72. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  73. }
  74. /* 右侧预览区域 */
  75. .preview-section {
  76. background-color: #fff;
  77. border-radius: 8px;
  78. padding: 20px;
  79. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  80. }
  81. /* 表单组样式 */
  82. .form-group {
  83. margin-bottom: 20px;
  84. }
  85. .form-group label {
  86. display: block;
  87. font-size: 14px;
  88. font-weight: bold;
  89. color: #333;
  90. margin-bottom: 8px;
  91. }
  92. /* 文本输入区域 */
  93. .prompt-textarea {
  94. width: 100%;
  95. height: 150px;
  96. padding: 12px;
  97. border: 1px solid #ddd;
  98. border-radius: 8px;
  99. resize: vertical;
  100. font-size: 14px;
  101. line-height: 1.5;
  102. }
  103. /* 参数设置区域 */
  104. .params-grid {
  105. display: grid;
  106. grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  107. gap: 15px;
  108. }
  109. .param-item {
  110. margin-bottom: 15px;
  111. }
  112. .param-item label {
  113. display: block;
  114. font-size: 13px;
  115. font-weight: bold;
  116. color: #333;
  117. margin-bottom: 5px;
  118. }
  119. .param-item select, .param-item input[type="number"] {
  120. width: 100%;
  121. padding: 8px 12px;
  122. border: 1px solid #ddd;
  123. border-radius: 4px;
  124. font-size: 14px;
  125. }
  126. /* 提示词示例 */
  127. .prompt-examples {
  128. background-color: #f8f9fa;
  129. border-radius: 8px;
  130. padding: 15px;
  131. margin-bottom: 20px;
  132. }
  133. .prompt-examples h4 {
  134. font-size: 14px;
  135. font-weight: bold;
  136. margin-bottom: 10px;
  137. color: #333;
  138. }
  139. .example-list {
  140. display: flex;
  141. flex-wrap: wrap;
  142. gap: 8px;
  143. }
  144. .example-tag {
  145. font-size: 12px;
  146. background-color: #e9ecef;
  147. color: #666;
  148. padding: 4px 10px;
  149. border-radius: 4px;
  150. cursor: pointer;
  151. transition: background-color 0.2s;
  152. }
  153. .example-tag:hover {
  154. background-color: #dee2e6;
  155. }
  156. /* 按钮样式 */
  157. .button-group {
  158. display: flex;
  159. gap: 10px;
  160. }
  161. .btn {
  162. padding: 12px 24px;
  163. border: none;
  164. border-radius: 8px;
  165. font-size: 14px;
  166. cursor: pointer;
  167. transition: background-color 0.2s;
  168. }
  169. .btn-primary {
  170. background-color: #007bff;
  171. color: #fff;
  172. }
  173. .btn-primary:hover {
  174. background-color: #0056b3;
  175. }
  176. .btn-secondary {
  177. background-color: #6c757d;
  178. color: #fff;
  179. }
  180. .btn-secondary:hover {
  181. background-color: #545b62;
  182. }
  183. /* 预览区域 */
  184. .preview-container {
  185. width: 100%;
  186. min-height: 300px;
  187. background-color: #f8f9fa;
  188. border-radius: 8px;
  189. display: flex;
  190. align-items: center;
  191. justify-content: center;
  192. margin-bottom: 20px;
  193. overflow: hidden;
  194. }
  195. .preview-image {
  196. max-width: 100%;
  197. max-height: 100%;
  198. object-fit: contain;
  199. }
  200. .preview-placeholder {
  201. font-size: 14px;
  202. color: #666;
  203. text-align: center;
  204. }
  205. /* 模板信息 */
  206. .template-info {
  207. margin-bottom: 20px;
  208. }
  209. .template-info h4 {
  210. font-size: 14px;
  211. font-weight: bold;
  212. margin-bottom: 10px;
  213. color: #333;
  214. }
  215. .template-form input {
  216. width: 100%;
  217. padding: 10px 12px;
  218. border: 1px solid #ddd;
  219. border-radius: 8px;
  220. font-size: 14px;
  221. margin-bottom: 10px;
  222. }
  223. /* 响应式设计 */
  224. @media (max-width: 768px) {
  225. .content-area {
  226. grid-template-columns: 1fr;
  227. }
  228. .clear-btn, .optimize-btn, .arrow-btn {
  229. flex: 1;
  230. }
  231. }
  232. .optimize-btn {
  233. background-color: #ffffff;
  234. color: #4b5563;
  235. padding: 10px 18px;
  236. border: 1px solid #d1d5db;
  237. border-radius: 8px;
  238. font-size: 14px;
  239. font-weight: 500;
  240. cursor: pointer;
  241. transition: all 0.3s ease;
  242. display: flex;
  243. align-items: center;
  244. gap: 6px;
  245. }
  246. .optimize-btn:hover {
  247. background-color: #f9fafb;
  248. border-color: #9ca3af;
  249. transform: translateY(-1px);
  250. }
  251. .clear-btn {
  252. background-color: #ffffff;
  253. color: #dc3545;
  254. border: 1px solid #f87171;
  255. border-radius: 8px;
  256. font-size: 14px;
  257. font-weight: 500;
  258. cursor: pointer;
  259. transition: all 0.3s ease;
  260. display: flex;
  261. align-items: center;
  262. gap: 6px;
  263. }
  264. .clear-btn:hover {
  265. background-color: #fee2e2;
  266. border-color: #ef4444;
  267. transform: translateY(-1px);
  268. }
  269. /* 操作按钮 */
  270. .prompt-actions {
  271. gap: 8px;
  272. }
  273. .clear-btn, .optimize-btn {
  274. padding: 8px 12px;
  275. font-size: 13px;
  276. }
  277. /* 菜单样式 */
  278. .menu-container {
  279. background-color: #f5f7fa;
  280. border-radius: 8px;
  281. padding: 10px;
  282. margin-bottom: 20px;
  283. display: flex;
  284. gap: 10px;
  285. }
  286. .menu-item {
  287. flex: 1;
  288. }
  289. .menu-item a {
  290. display: block;
  291. padding: 12px;
  292. background-color: white;
  293. border: 1px solid #e1e8ed;
  294. border-radius: 6px;
  295. text-align: center;
  296. text-decoration: none;
  297. color: #657786;
  298. font-size: 14px;
  299. font-weight: 500;
  300. transition: all 0.3s ease;
  301. }
  302. .menu-item a:hover {
  303. background-color: #e8f0fe;
  304. border-color: #1da1f2;
  305. color: #1da1f2;
  306. }
  307. .menu-item.active a {
  308. background-color: #1da1f2;
  309. border-color: #1da1f2;
  310. color: white;
  311. }
  312. .menu-item i {
  313. margin-right: 6px;
  314. font-size: 16px;
  315. }
  316. </style>
  317. </head>
  318. <body>
  319. <div class="container">
  320. <!-- 菜单功能区 -->
  321. <div class="menu-container">
  322. <div class="menu-item">
  323. <a href="{:url('user/text_to_image')}">
  324. <i class="fas fa-image"></i>
  325. 文生图
  326. </a>
  327. </div>
  328. <div class="menu-item active">
  329. <a href="{:url('user/text_to_video')}">
  330. <i class="fas fa-video"></i>
  331. 文生视频
  332. </a>
  333. </div>
  334. </div>
  335. <div class="content-area">
  336. <!-- 左侧输入区域 -->
  337. <div class="input-section">
  338. <h3 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">文生视频生成参数设置</h3>
  339. <input type="text" value="{$user.id}" id="user_id" disabled style="display: none">
  340. <!-- 提示词示例 -->
  341. <div class="prompt-examples">
  342. <label for="prompt">提示词</label>
  343. <textarea id="prompt" class="prompt-textarea" placeholder="请输入您想要生成的视频描述,越详细越好..."></textarea>
  344. </div>
  345. <!-- 生成参数 -->
  346. <div class="form-group">
  347. <label>生成参数</label>
  348. <div class="params-grid">
  349. <div class="param-item">
  350. <label for="style">风格</label>
  351. <select id="style">
  352. <option value="">默认</option>
  353. <option value="anime">动漫</option>
  354. <option value="realistic">写实</option>
  355. <option value="cartoon">卡通</option>
  356. <option value="fantasy">奇幻</option>
  357. </select>
  358. </div>
  359. <div class="param-item">
  360. <label for="duration">时长</label>
  361. <select id="duration">
  362. <option value="4">4秒</option>
  363. <option value="8">8秒</option>
  364. <option value="12">12秒</option>
  365. </select>
  366. </div>
  367. <div class="param-item">
  368. <label for="videoSize">尺寸</label>
  369. <select id="videoSize">
  370. <option value="1280x720">1280x720</option>
  371. <option value="720x1280">720x1280</option>
  372. </select>
  373. </div>
  374. </div>
  375. </div>
  376. <div class="button-group">
  377. <button class="clear-btn">
  378. <i class="fas fa-trash"></i>
  379. 清空
  380. </button>
  381. <button class="optimize-btn">
  382. <i class="fas fa-magic"></i>
  383. 一键优化
  384. </button>
  385. <button class="btn btn-primary" id="generateBtn">
  386. <i class="fas fa-arrow-up"></i>
  387. 生成视频
  388. </button>
  389. </div>
  390. </div>
  391. <!-- 右侧预览区域 -->
  392. <div class="preview-section">
  393. <h3 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">视频预览</h3>
  394. <!-- 预览容器 -->
  395. <div id="videoResult" class="preview-container">
  396. <div class="preview-placeholder">
  397. <p>点击"生成视频"按钮开始创建您的视频</p>
  398. <p style="font-size: 12px; margin-top: 10px; color: #999;">生成时间可能需要几十秒到数分钟不等</p>
  399. </div>
  400. </div>
  401. <!-- 模板信息 -->
  402. <div class="template-info">
  403. <h4>模板信息</h4>
  404. <div class="template-form">
  405. <input type="text" id="templateName" placeholder="请输入模板名称">
  406. </div>
  407. </div>
  408. <!-- 保存按钮 -->
  409. <div class="button-group">
  410. <button class="btn btn-primary" id="saveBtn" disabled>保存为视频模板</button>
  411. <button class="btn btn-secondary" id="downloadBtn" disabled>下载视频</button>
  412. </div>
  413. </div>
  414. </div>
  415. </div>
  416. <script>
  417. // 自定义提示框函数
  418. function showAlert(message, type = 'info', duration = 3000) {
  419. // 检查是否已存在提示框容器,如果不存在则创建
  420. let alertElement = document.getElementById('customAlert');
  421. if (!alertElement) {
  422. alertElement = document.createElement('div');
  423. alertElement.id = 'customAlert';
  424. alertElement.className = 'custom-alert';
  425. document.body.appendChild(alertElement);
  426. }
  427. alertElement.textContent = message;
  428. alertElement.className = `custom-alert ${type}`;
  429. // 显示提示框
  430. alertElement.classList.add('show');
  431. // 自动隐藏
  432. setTimeout(() => {
  433. alertElement.classList.remove('show');
  434. }, duration);
  435. }
  436. // 生成视频函数
  437. function generateVideo() {
  438. const promptText = document.getElementById('prompt').value.trim();
  439. const videoStyle = document.getElementById('style').value;
  440. const videoDuration = document.getElementById('duration').value;
  441. const videoSize = document.getElementById('videoSize').value;
  442. const videoResultDiv = document.getElementById('videoResult');
  443. // 检查是否有提示词
  444. if (!promptText) {
  445. alert('请先输入视频描述');
  446. return;
  447. }
  448. // 显示加载状态
  449. videoResultDiv.innerHTML = `
  450. <div class="video-loading">
  451. <i class="fas fa-spinner fa-spin"></i>
  452. <p>正在生成视频,请稍候...</p>
  453. <p style="font-size: 12px; margin-top: 10px; color: #999;">生成时间可能需要几十秒到数分钟不等</p>
  454. </div>
  455. `;
  456. // 拼接风格、时长和尺寸到提示词末尾
  457. let fullPrompt = promptText;
  458. if (videoStyle) {
  459. fullPrompt += '风格:' + videoStyle;
  460. }
  461. if (videoDuration) {
  462. fullPrompt += '时长:' + videoDuration + '秒';
  463. }
  464. if (videoSize) {
  465. fullPrompt += '尺寸比例:' + videoSize;
  466. }
  467. // 发送请求到文生视频接口
  468. $.ajax({
  469. url: '/index.php/api/work_order/GetTxtToVideo',
  470. type: 'POST',
  471. dataType: 'json',
  472. data: {
  473. status_val: '文生视频',
  474. prompt: fullPrompt,
  475. model: 'sora-2',
  476. style: videoStyle,
  477. duration: videoDuration,
  478. size: videoSize
  479. },
  480. success: function(response) {
  481. // 处理成功响应
  482. if (response.code === 0 && response.data && response.data.web_url) {
  483. // 视频生成完成
  484. let videoUrl = response.data.web_url;
  485. // 确保web_url格式正确,添加根路径前缀
  486. if (videoUrl && !videoUrl.startsWith('/')) {
  487. videoUrl = '/' + videoUrl;
  488. }
  489. // 显示生成的视频
  490. videoResultDiv.innerHTML = `
  491. <div class="video-container">
  492. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  493. 您的浏览器不支持视频播放
  494. </video>
  495. </div>
  496. `;
  497. // 更新视频信息到全局变量
  498. videoInfo = {
  499. style: videoStyle,
  500. duration: videoDuration,
  501. size: videoSize,
  502. web_url: videoUrl,
  503. video_id: response.data.video_id || response.data.id || ''
  504. };
  505. // 启用下载按钮和保存模板按钮
  506. document.getElementById('downloadBtn').disabled = false;
  507. document.getElementById('saveBtn').disabled = false;
  508. // 添加视频放大模态框(如果还不存在)
  509. if (!document.getElementById('videoModal')) {
  510. const modal = document.createElement('div');
  511. modal.id = 'videoModal';
  512. modal.style.cssText = `
  513. display: none;
  514. position: fixed;
  515. z-index: 9999;
  516. left: 0;
  517. top: 0;
  518. width: 100%;
  519. height: 100%;
  520. overflow: auto;
  521. background-color: rgba(0, 0, 0, 0.9);
  522. justify-content: center;
  523. align-items: center;
  524. `;
  525. modal.style.display = 'flex';
  526. modal.style.display = 'none';
  527. const modalContent = document.createElement('video');
  528. modalContent.id = 'modalVideo';
  529. modalContent.style.cssText = `
  530. max-width: 90%;
  531. max-height: 90%;
  532. margin: auto;
  533. `;
  534. modalContent.setAttribute('controls', 'controls');
  535. const closeBtn = document.createElement('span');
  536. closeBtn.innerHTML = '&times;';
  537. closeBtn.style.cssText = `
  538. position: absolute;
  539. top: 20px;
  540. right: 35px;
  541. color: #f1f1f1;
  542. font-size: 40px;
  543. font-weight: bold;
  544. transition: 0.3s;
  545. cursor: pointer;
  546. `;
  547. closeBtn.onclick = function() {
  548. modal.style.display = 'none';
  549. };
  550. modal.appendChild(closeBtn);
  551. modal.appendChild(modalContent);
  552. document.body.appendChild(modal);
  553. // 点击模态框背景关闭
  554. modal.onclick = function(event) {
  555. if (event.target.id === 'videoModal') {
  556. this.style.display = 'none';
  557. }
  558. };
  559. }
  560. // 为生成的视频添加点击事件
  561. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  562. generatedVideo.onclick = function() {
  563. const modal = document.getElementById('videoModal');
  564. const modalVideo = document.getElementById('modalVideo');
  565. modalVideo.src = this.src;
  566. modal.style.display = 'flex';
  567. };
  568. } else if (response.code === 202 && response.data && response.data.video_id) {
  569. // 视频正在生成中,显示状态信息并开始轮询
  570. const videoId = response.data.video_id;
  571. const status = response.data.status;
  572. const progress = response.data.progress || 0;
  573. // 显示接口返回的message信息
  574. const message = response.msg || response.data.message || '';
  575. // <p style="font-size: 12px; margin-top: 10px; color: #999;">视频ID: ${videoId}</p>
  576. videoResultDiv.innerHTML = `
  577. <div class="video-processing">
  578. <i class="fas fa-hourglass-half fa-spin"></i>
  579. <p>正在生成视频,请稍候...</p>
  580. ${message ? `<p style="font-size: 12px; margin-top: 5px; color: #666;">${message}</p>` : ''}
  581. <p style="font-size: 12px; margin-top: 5px; color: #999;">当前状态: ${status === 'queued' ? '排队中' : '处理中'}</p>
  582. <div class="progress-bar-container" style="width: 200px; height: 8px; background-color: #eee; border-radius: 4px; margin: 10px auto;">
  583. <div class="progress-bar" style="width: ${progress}%; height: 100%; background-color: #4CAF50; border-radius: 4px; transition: width 0.3s;"></div>
  584. </div>
  585. <p style="font-size: 12px; margin-top: 5px; color: #999;">系统正在自动检查视频生成状态...</p>
  586. </div>
  587. `;
  588. // 开始轮询检查视频状态
  589. startVideoPolling(videoId, videoStyle, videoDuration, videoSize);
  590. } else {
  591. // 显示错误信息
  592. videoResultDiv.innerHTML = `
  593. <div class="video-error">
  594. <i class="fas fa-exclamation-triangle"></i>
  595. <p>视频生成失败:${response.msg || '未知错误'}</p>
  596. </div>
  597. `;
  598. }
  599. },
  600. error: function(xhr, status, error) {
  601. // 处理网络错误
  602. videoResultDiv.innerHTML = `
  603. <div class="video-error">
  604. <i class="fas fa-exclamation-triangle"></i>
  605. <p>视频生成失败:网络错误</p>
  606. </div>
  607. `;
  608. }
  609. });
  610. }
  611. // 页面加载完成后绑定事件
  612. $(document).ready(function() {
  613. // 绑定生成视频按钮点击事件
  614. $('#generateBtn').click(generateVideo);
  615. // 绑定保存模板按钮点击事件
  616. $('#saveBtn').click(saveTemplate);
  617. // 绑定下载视频按钮点击事件
  618. $('#downloadBtn').click(downloadVideo);
  619. // 绑定清空按钮点击事件
  620. $('.clear-btn').click(clearPrompt);
  621. // 绑定一键优化按钮点击事件
  622. $('.optimize-btn').click(optimizePrompt);
  623. // 键盘ESC键关闭模态框事件
  624. document.addEventListener('keydown', function(event) {
  625. if (event.key === 'Escape') {
  626. const modal = document.getElementById('videoModal');
  627. if (modal && modal.style.display === 'flex') {
  628. modal.style.display = 'none';
  629. }
  630. }
  631. });
  632. });
  633. // 一键优化提示词功能
  634. function optimizePrompt() {
  635. const textarea = document.getElementById('prompt');
  636. const optimizeBtn = document.querySelector('.optimize-btn');
  637. if (textarea && textarea.value.trim() !== '') {
  638. // 显示加载状态
  639. const originalBtnText = optimizeBtn.innerHTML;
  640. optimizeBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 优化中...';
  641. optimizeBtn.disabled = true;
  642. // 构造请求参数
  643. const params = {
  644. status_val: '文生文',
  645. prompt: textarea.value,
  646. model: 'gemini-2.0-flash'
  647. };
  648. // 发送AJAX请求
  649. $.ajax({
  650. url: '/index.php/api/work_order/GetTxtToTxt',
  651. type: 'POST',
  652. data: params,
  653. dataType: 'json',
  654. success: function(data) {
  655. console.log(data);
  656. // 恢复按钮状态
  657. optimizeBtn.innerHTML = originalBtnText;
  658. optimizeBtn.disabled = false;
  659. // 优化成功,更新文本框内容
  660. textarea.value = data.content;
  661. textarea.scrollTop = textarea.scrollHeight;
  662. updateCharCount();
  663. // 显示优化成功提示
  664. const alert = document.createElement('div');
  665. alert.className = 'alert alert-success';
  666. alert.innerHTML = '提示词已优化!';
  667. alert.style.position = 'fixed';
  668. alert.style.top = '20px';
  669. alert.style.right = '20px';
  670. alert.style.zIndex = '9999';
  671. document.body.appendChild(alert);
  672. // 3秒后移除提示
  673. setTimeout(() => {
  674. alert.remove();
  675. }, 3000);
  676. }
  677. });
  678. } else {
  679. alert('请先输入提示词!');
  680. }
  681. }
  682. // 清空提示词功能
  683. function clearPrompt() {
  684. const textarea = document.getElementById('prompt');
  685. if (textarea) {
  686. textarea.value = '';
  687. textarea.focus();
  688. }
  689. }
  690. // 轮询检查视频状态函数
  691. function startVideoPolling(videoId, videoStyle, videoDuration, videoSize) {
  692. let pollingCount = 0;
  693. const maxPollingCount = 60; // 最大轮询次数 (30次 * 5秒 = 2.5分钟)
  694. const pollingInterval = 6000; // 轮询间隔 (5秒)
  695. // 轮询检查视频状态
  696. const pollingTimer = setInterval(function() {
  697. $.ajax({
  698. url: '/index.php/api/work_order/Getvideo_id',
  699. type: 'GET',
  700. dataType: 'json',
  701. data: {
  702. video_id: videoId
  703. },
  704. success: function(response) {
  705. if (response.code === 0 && response.data && response.data.web_url) {
  706. // 视频生成完成
  707. clearInterval(pollingTimer);
  708. const videoResultDiv = document.getElementById('videoResult');
  709. let videoUrl = response.data.web_url;
  710. // 确保web_url格式正确,添加根路径前缀
  711. if (videoUrl && !videoUrl.startsWith('/')) {
  712. videoUrl = '/' + videoUrl;
  713. }
  714. // 显示生成的视频
  715. videoResultDiv.innerHTML = `
  716. <div class="video-container">
  717. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  718. 您的浏览器不支持视频播放
  719. </video>
  720. </div>
  721. `;
  722. // 更新视频信息到全局变量
  723. videoInfo = {
  724. style: videoStyle,
  725. duration: videoDuration,
  726. size: videoSize,
  727. web_url: videoUrl,
  728. video_id: response.data.video_id || videoId
  729. };
  730. // 启用下载按钮和保存模板按钮
  731. document.getElementById('downloadBtn').disabled = false;
  732. document.getElementById('saveBtn').disabled = false;
  733. // 添加视频放大模态框(如果还不存在)
  734. if (!document.getElementById('videoModal')) {
  735. const modal = document.createElement('div');
  736. modal.id = 'videoModal';
  737. modal.style.cssText = `
  738. display: none;
  739. position: fixed;
  740. z-index: 9999;
  741. left: 0;
  742. top: 0;
  743. width: 100%;
  744. height: 100%;
  745. overflow: auto;
  746. background-color: rgba(0, 0, 0, 0.9);
  747. justify-content: center;
  748. align-items: center;
  749. `;
  750. modal.style.display = 'flex';
  751. modal.style.display = 'none';
  752. const modalContent = document.createElement('video');
  753. modalContent.id = 'modalVideo';
  754. modalContent.style.cssText = `
  755. max-width: 90%;
  756. max-height: 90%;
  757. margin: auto;
  758. `;
  759. modalContent.setAttribute('controls', 'controls');
  760. const closeBtn = document.createElement('span');
  761. closeBtn.innerHTML = '&times;';
  762. closeBtn.style.cssText = `
  763. position: absolute;
  764. top: 20px;
  765. right: 35px;
  766. color: #f1f1f1;
  767. font-size: 40px;
  768. font-weight: bold;
  769. transition: 0.3s;
  770. cursor: pointer;
  771. `;
  772. closeBtn.onclick = function() {
  773. modal.style.display = 'none';
  774. };
  775. modal.appendChild(closeBtn);
  776. modal.appendChild(modalContent);
  777. document.body.appendChild(modal);
  778. // 点击模态框背景关闭
  779. modal.onclick = function(event) {
  780. if (event.target.id === 'videoModal') {
  781. this.style.display = 'none';
  782. }
  783. };
  784. }
  785. // 为生成的视频添加点击事件
  786. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  787. generatedVideo.onclick = function() {
  788. const modal = document.getElementById('videoModal');
  789. const modalVideo = document.getElementById('modalVideo');
  790. modalVideo.src = this.src;
  791. modal.style.display = 'flex';
  792. };
  793. } else if (response.code === 202 && response.data) {
  794. // 视频仍在生成中,更新状态信息
  795. const status = response.data.status;
  796. const progress = response.data.progress || 0;
  797. const progressBar = videoResultDiv.querySelector('.progress-bar');
  798. const statusText = videoResultDiv.querySelector('.video-processing p:nth-child(3)');
  799. const progressText = videoResultDiv.querySelector('.video-processing p:nth-child(4)');
  800. if (progressBar) {
  801. progressBar.style.width = `${progress}%`;
  802. }
  803. if (statusText) {
  804. statusText.textContent = `当前状态: ${status === 'queued' ? '排队中' : '处理中'}`;
  805. }
  806. // if (progressText) {
  807. // progressText.textContent = `进度: ${progress}%`;
  808. // }
  809. } else {
  810. // 视频生成失败
  811. clearInterval(pollingTimer);
  812. const videoResultDiv = document.getElementById('videoResult');
  813. videoResultDiv.innerHTML = `
  814. <div class="video-error">
  815. <i class="fas fa-exclamation-triangle"></i>
  816. <p>视频生成失败:${response.msg || '未知错误'}</p>
  817. </div>
  818. `;
  819. }
  820. },
  821. error: function(xhr, status, error) {
  822. // 网络错误
  823. clearInterval(pollingTimer);
  824. const videoResultDiv = document.getElementById('videoResult');
  825. videoResultDiv.innerHTML = `
  826. <div class="video-error">
  827. <i class="fas fa-exclamation-triangle"></i>
  828. <p>检查视频状态失败:网络错误</p>
  829. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后手动刷新页面查看结果</p>
  830. <div style="margin-top: 10px;">
  831. <button onclick="startVideoPolling('${videoId}', '${videoStyle}', '${videoDuration}', '${videoSize}')" style="padding: 5px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">
  832. 重新检查视频状态
  833. </button>
  834. </div>
  835. </div>
  836. `;
  837. }
  838. });
  839. // 检查是否达到最大轮询次数
  840. pollingCount++;
  841. if (pollingCount >= maxPollingCount) {
  842. clearInterval(pollingTimer);
  843. const videoResultDiv = document.getElementById('videoResult');
  844. const manualCheckDiv = document.createElement('div');
  845. manualCheckDiv.style.cssText = 'text-align: center; margin-top: 10px;';
  846. manualCheckDiv.innerHTML = `
  847. <button onclick="checkVideoStatus('${videoId}')" style="padding: 5px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">
  848. 手动检查视频状态
  849. </button>
  850. <p style="font-size: 12px; margin-top: 5px; color: #999;">或稍后刷新页面查看结果</p>
  851. `;
  852. videoResultDiv.appendChild(manualCheckDiv);
  853. }
  854. }, pollingInterval);
  855. }
  856. // 手动检查视频状态函数
  857. function checkVideoStatus(videoId) {
  858. $.ajax({
  859. url: '/index.php/api/work_order/Getvideo_id',
  860. type: 'GET',
  861. dataType: 'json',
  862. data: {
  863. video_id: videoId
  864. },
  865. success: function(response) {
  866. if (response.code === 0 && response.data && response.data.web_url) {
  867. // 视频生成完成,直接显示结果
  868. const videoResultDiv = document.getElementById('videoResult');
  869. let videoUrl = response.data.web_url;
  870. // 确保web_url格式正确,添加根路径前缀
  871. if (videoUrl && !videoUrl.startsWith('/')) {
  872. videoUrl = '/' + videoUrl;
  873. }
  874. // 显示生成的视频
  875. videoResultDiv.innerHTML = `
  876. <div class="video-container">
  877. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  878. 您的浏览器不支持视频播放
  879. </video>
  880. </div>
  881. `;
  882. // 更新视频信息到全局变量
  883. videoInfo = {
  884. video_id: response.data.video_id || videoId,
  885. web_url: videoUrl
  886. };
  887. // 启用下载按钮和保存模板按钮
  888. document.getElementById('downloadBtn').disabled = false;
  889. document.getElementById('saveBtn').disabled = false;
  890. // 添加视频放大模态框(如果还不存在)
  891. if (!document.getElementById('videoModal')) {
  892. const modal = document.createElement('div');
  893. modal.id = 'videoModal';
  894. modal.style.cssText = `
  895. display: none;
  896. position: fixed;
  897. z-index: 9999;
  898. left: 0;
  899. top: 0;
  900. width: 100%;
  901. height: 100%;
  902. overflow: auto;
  903. background-color: rgba(0, 0, 0, 0.9);
  904. justify-content: center;
  905. align-items: center;
  906. `;
  907. modal.style.display = 'flex';
  908. modal.style.display = 'none';
  909. const modalContent = document.createElement('video');
  910. modalContent.id = 'modalVideo';
  911. modalContent.style.cssText = `
  912. max-width: 90%;
  913. max-height: 90%;
  914. margin: auto;
  915. `;
  916. modalContent.setAttribute('controls', 'controls');
  917. const closeBtn = document.createElement('span');
  918. closeBtn.innerHTML = '&times;';
  919. closeBtn.style.cssText = `
  920. position: absolute;
  921. top: 20px;
  922. right: 35px;
  923. color: #f1f1f1;
  924. font-size: 40px;
  925. font-weight: bold;
  926. transition: 0.3s;
  927. cursor: pointer;
  928. `;
  929. closeBtn.onclick = function() {
  930. modal.style.display = 'none';
  931. };
  932. modal.appendChild(closeBtn);
  933. modal.appendChild(modalContent);
  934. document.body.appendChild(modal);
  935. // 点击模态框背景关闭
  936. modal.onclick = function(event) {
  937. if (event.target.id === 'videoModal') {
  938. this.style.display = 'none';
  939. }
  940. };
  941. }
  942. // 为生成的视频添加点击事件
  943. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  944. generatedVideo.onclick = function() {
  945. const modal = document.getElementById('videoModal');
  946. const modalVideo = document.getElementById('modalVideo');
  947. modalVideo.src = this.src;
  948. modal.style.display = 'flex';
  949. };
  950. } else {
  951. // 视频仍在生成中或失败
  952. const videoResultDiv = document.getElementById('videoResult');
  953. if (response.code === 202) {
  954. const progress = response.data.progress || 0;
  955. const message = response.msg || response.data.message || '';
  956. videoResultDiv.innerHTML = `
  957. <div class="video-processing">
  958. <i class="fas fa-hourglass-half fa-spin"></i>
  959. <p>${message || '视频正在生成中...'}</p>
  960. <p style="font-size: 12px; margin-top: 10px; color: #999;">视频ID: ${videoId}</p>
  961. <p style="font-size: 12px; margin-top: 5px; color: #999;">当前状态: ${response.data.status === 'queued' ? '排队中' : '处理中'}</p>
  962. <div class="progress-container" style="width: 100%; background-color: #eee; margin-top: 15px;">
  963. <div class="progress-bar" style="width: ${progress}%; height: 20px; background-color: #4CAF50;"></div>
  964. </div>
  965. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后再次手动检查</p>
  966. </div>
  967. `;
  968. } else {
  969. videoResultDiv.innerHTML = `
  970. <div class="video-error">
  971. <i class="fas fa-exclamation-triangle"></i>
  972. <p>视频生成失败:${response.msg || '未知错误'}</p>
  973. </div>
  974. `;
  975. }
  976. }
  977. },
  978. error: function(xhr, status, error) {
  979. // 网络错误
  980. const videoResultDiv = document.getElementById('videoResult');
  981. videoResultDiv.innerHTML = `
  982. <div class="video-error">
  983. <i class="fas fa-exclamation-triangle"></i>
  984. <p>检查视频状态失败:网络错误</p>
  985. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后再次尝试</p>
  986. </div>
  987. `;
  988. }
  989. });
  990. }
  991. // 保存模板函数
  992. function saveTemplate() {
  993. // 获取模板名称
  994. const templateName = document.getElementById('templateName').value.trim();
  995. const textarea = document.getElementById('prompt');
  996. const user_id = document.getElementById('user_id');
  997. // 验证模板名称
  998. if (!templateName) {
  999. showAlert('模板名称不能为空');
  1000. return;
  1001. }
  1002. // 检查是否有视频信息
  1003. if (!videoInfo || !videoInfo.web_url) {
  1004. showAlert('请先生成视频');
  1005. return;
  1006. }
  1007. // 构建请求参数
  1008. const params = {
  1009. chinese_description: textarea.value.trim(),
  1010. template_image_url: videoInfo.web_url,
  1011. template_name: templateName,
  1012. size: videoInfo.size,
  1013. style: videoInfo.style,
  1014. type: "文生视频",
  1015. user_id:user_id,
  1016. seconds: videoInfo.duration,
  1017. video_id: videoInfo.video_id
  1018. };
  1019. // 发送请求保存模板
  1020. $.ajax({
  1021. url: '/index.php/api/work_order/Add_Product_Template',
  1022. type: 'POST',
  1023. dataType: 'json',
  1024. data: params,
  1025. success: function(response) {
  1026. if (response.code === 0) {
  1027. showAlert('模板保存成功');
  1028. // 清空模板名称输入框
  1029. document.getElementById('templateName').value = '';
  1030. } else {
  1031. showAlert('模板保存失败: ' + response.msg, 'error');
  1032. }
  1033. },
  1034. error: function(xhr, status, error) {
  1035. showAlert('保存模板时发生错误: ' + error, 'error');
  1036. }
  1037. });
  1038. }
  1039. // 下载视频函数
  1040. function downloadVideo() {
  1041. const previewVideo = document.getElementById('previewVideo');
  1042. if (previewVideo && previewVideo.src) {
  1043. // 创建临时链接下载视频
  1044. const link = document.createElement('a');
  1045. link.href = previewVideo.src;
  1046. // 生成更有意义的文件名
  1047. const timestamp = new Date().getTime();
  1048. const promptText = document.getElementById('prompt').value.trim();
  1049. // 从提示词中提取前20个字符作为文件名的一部分
  1050. const promptPart = promptText.substring(0, 20).replace(/[^\w\u4e00-\u9fa5]/g, '') || 'video';
  1051. // 组合文件名:提示词部分_时间戳.mp4
  1052. const fileName = `${promptPart}_${timestamp}.mp4`;
  1053. link.download = fileName;
  1054. link.click();
  1055. }
  1056. }
  1057. </script>
  1058. </body>
  1059. </html>