text_to_video.html 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  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/index')}">
  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. <!-- 提示词示例 -->
  340. <div class="prompt-examples">
  341. <label for="prompt">提示词</label>
  342. <textarea id="prompt" class="prompt-textarea" placeholder="请输入您想要生成的视频描述,越详细越好..."></textarea>
  343. </div>
  344. <!-- 生成参数 -->
  345. <div class="form-group">
  346. <label>生成参数</label>
  347. <div class="params-grid">
  348. <div class="param-item">
  349. <label for="style">风格</label>
  350. <select id="style">
  351. <option value="">默认</option>
  352. <option value="anime">动漫</option>
  353. <option value="realistic">写实</option>
  354. <option value="cartoon">卡通</option>
  355. <option value="fantasy">奇幻</option>
  356. </select>
  357. </div>
  358. <div class="param-item">
  359. <label for="duration">时长</label>
  360. <select id="duration">
  361. <option value="4">4秒</option>
  362. <option value="8">8秒</option>
  363. <option value="12">12秒</option>
  364. </select>
  365. </div>
  366. <div class="param-item">
  367. <label for="videoSize">尺寸</label>
  368. <select id="videoSize">
  369. <option value="1280x720">1280x720</option>
  370. <option value="720x1280">720x1280</option>
  371. </select>
  372. </div>
  373. </div>
  374. </div>
  375. <div class="button-group">
  376. <button class="clear-btn">
  377. <i class="fas fa-trash"></i>
  378. 清空
  379. </button>
  380. <button class="optimize-btn">
  381. <i class="fas fa-magic"></i>
  382. 一键优化
  383. </button>
  384. <button class="btn btn-primary" id="generateBtn">
  385. <i class="fas fa-arrow-up"></i>
  386. 生成视频
  387. </button>
  388. </div>
  389. </div>
  390. <!-- 右侧预览区域 -->
  391. <div class="preview-section">
  392. <h3 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">视频预览</h3>
  393. <!-- 预览容器 -->
  394. <div id="videoResult" class="preview-container">
  395. <div class="preview-placeholder">
  396. <p>点击"生成视频"按钮开始创建您的视频</p>
  397. <p style="font-size: 12px; margin-top: 10px; color: #999;">生成时间可能需要几十秒到数分钟不等</p>
  398. </div>
  399. </div>
  400. <!-- 模板信息 -->
  401. <div class="template-info">
  402. <h4>模板信息</h4>
  403. <div class="template-form">
  404. <input type="text" id="templateName" placeholder="请输入模板名称">
  405. </div>
  406. </div>
  407. <!-- 保存按钮 -->
  408. <div class="button-group">
  409. <button class="btn btn-primary" id="saveBtn" disabled>保存为视频模板</button>
  410. <button class="btn btn-secondary" id="downloadBtn" disabled>下载视频</button>
  411. </div>
  412. </div>
  413. </div>
  414. </div>
  415. <script>
  416. // 自定义提示框函数
  417. function showAlert(message, type = 'info', duration = 3000) {
  418. // 检查是否已存在提示框容器,如果不存在则创建
  419. let alertElement = document.getElementById('customAlert');
  420. if (!alertElement) {
  421. alertElement = document.createElement('div');
  422. alertElement.id = 'customAlert';
  423. alertElement.className = 'custom-alert';
  424. document.body.appendChild(alertElement);
  425. }
  426. alertElement.textContent = message;
  427. alertElement.className = `custom-alert ${type}`;
  428. // 显示提示框
  429. alertElement.classList.add('show');
  430. // 自动隐藏
  431. setTimeout(() => {
  432. alertElement.classList.remove('show');
  433. }, duration);
  434. }
  435. // 生成视频函数
  436. function generateVideo() {
  437. const promptText = document.getElementById('prompt').value.trim();
  438. const videoStyle = document.getElementById('style').value;
  439. const videoDuration = document.getElementById('duration').value;
  440. const videoSize = document.getElementById('videoSize').value;
  441. const videoResultDiv = document.getElementById('videoResult');
  442. // 检查是否有提示词
  443. if (!promptText) {
  444. alert('请先输入视频描述');
  445. return;
  446. }
  447. // 显示加载状态
  448. videoResultDiv.innerHTML = `
  449. <div class="video-loading">
  450. <i class="fas fa-spinner fa-spin"></i>
  451. <p>正在生成视频,请稍候...</p>
  452. <p style="font-size: 12px; margin-top: 10px; color: #999;">生成时间可能需要几十秒到数分钟不等</p>
  453. </div>
  454. `;
  455. // 拼接风格、时长和尺寸到提示词末尾
  456. let fullPrompt = promptText;
  457. if (videoStyle) {
  458. fullPrompt += '风格:' + videoStyle;
  459. }
  460. if (videoDuration) {
  461. fullPrompt += '时长:' + videoDuration + '秒';
  462. }
  463. if (videoSize) {
  464. fullPrompt += '尺寸比例:' + videoSize;
  465. }
  466. // 发送请求到文生视频接口
  467. $.ajax({
  468. url: '/index.php/api/work_order/GetTxtToVideo',
  469. type: 'POST',
  470. dataType: 'json',
  471. data: {
  472. status_val: '文生视频',
  473. prompt: fullPrompt,
  474. model: 'sora-2',
  475. style: videoStyle,
  476. duration: videoDuration,
  477. size: videoSize
  478. },
  479. success: function(response) {
  480. // 处理成功响应
  481. if (response.code === 0 && response.data && response.data.web_url) {
  482. // 视频生成完成
  483. let videoUrl = response.data.web_url;
  484. // 确保web_url格式正确,添加根路径前缀
  485. if (videoUrl && !videoUrl.startsWith('/')) {
  486. videoUrl = '/' + videoUrl;
  487. }
  488. // 显示生成的视频
  489. videoResultDiv.innerHTML = `
  490. <div class="video-container">
  491. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  492. 您的浏览器不支持视频播放
  493. </video>
  494. </div>
  495. `;
  496. // 更新视频信息到全局变量
  497. videoInfo = {
  498. style: videoStyle,
  499. duration: videoDuration,
  500. size: videoSize,
  501. web_url: videoUrl,
  502. video_id: response.data.video_id || response.data.id || ''
  503. };
  504. // 启用下载按钮和保存模板按钮
  505. document.getElementById('downloadBtn').disabled = false;
  506. document.getElementById('saveBtn').disabled = false;
  507. // 添加视频放大模态框(如果还不存在)
  508. if (!document.getElementById('videoModal')) {
  509. const modal = document.createElement('div');
  510. modal.id = 'videoModal';
  511. modal.style.cssText = `
  512. display: none;
  513. position: fixed;
  514. z-index: 9999;
  515. left: 0;
  516. top: 0;
  517. width: 100%;
  518. height: 100%;
  519. overflow: auto;
  520. background-color: rgba(0, 0, 0, 0.9);
  521. justify-content: center;
  522. align-items: center;
  523. `;
  524. modal.style.display = 'flex';
  525. modal.style.display = 'none';
  526. const modalContent = document.createElement('video');
  527. modalContent.id = 'modalVideo';
  528. modalContent.style.cssText = `
  529. max-width: 90%;
  530. max-height: 90%;
  531. margin: auto;
  532. `;
  533. modalContent.setAttribute('controls', 'controls');
  534. const closeBtn = document.createElement('span');
  535. closeBtn.innerHTML = '&times;';
  536. closeBtn.style.cssText = `
  537. position: absolute;
  538. top: 20px;
  539. right: 35px;
  540. color: #f1f1f1;
  541. font-size: 40px;
  542. font-weight: bold;
  543. transition: 0.3s;
  544. cursor: pointer;
  545. `;
  546. closeBtn.onclick = function() {
  547. modal.style.display = 'none';
  548. };
  549. modal.appendChild(closeBtn);
  550. modal.appendChild(modalContent);
  551. document.body.appendChild(modal);
  552. // 点击模态框背景关闭
  553. modal.onclick = function(event) {
  554. if (event.target.id === 'videoModal') {
  555. this.style.display = 'none';
  556. }
  557. };
  558. }
  559. // 为生成的视频添加点击事件
  560. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  561. generatedVideo.onclick = function() {
  562. const modal = document.getElementById('videoModal');
  563. const modalVideo = document.getElementById('modalVideo');
  564. modalVideo.src = this.src;
  565. modal.style.display = 'flex';
  566. };
  567. } else if (response.code === 202 && response.data && response.data.video_id) {
  568. // 视频正在生成中,显示状态信息并开始轮询
  569. const videoId = response.data.video_id;
  570. const status = response.data.status;
  571. const progress = response.data.progress || 0;
  572. // 显示接口返回的message信息
  573. const message = response.msg || response.data.message || '';
  574. // <p style="font-size: 12px; margin-top: 10px; color: #999;">视频ID: ${videoId}</p>
  575. videoResultDiv.innerHTML = `
  576. <div class="video-processing">
  577. <i class="fas fa-hourglass-half fa-spin"></i>
  578. <p>正在生成视频,请稍候...</p>
  579. ${message ? `<p style="font-size: 12px; margin-top: 5px; color: #666;">${message}</p>` : ''}
  580. <p style="font-size: 12px; margin-top: 5px; color: #999;">当前状态: ${status === 'queued' ? '排队中' : '处理中'}</p>
  581. <div class="progress-bar-container" style="width: 200px; height: 8px; background-color: #eee; border-radius: 4px; margin: 10px auto;">
  582. <div class="progress-bar" style="width: ${progress}%; height: 100%; background-color: #4CAF50; border-radius: 4px; transition: width 0.3s;"></div>
  583. </div>
  584. <p style="font-size: 12px; margin-top: 5px; color: #999;">系统正在自动检查视频生成状态...</p>
  585. </div>
  586. `;
  587. // 开始轮询检查视频状态
  588. startVideoPolling(videoId, videoStyle, videoDuration, videoSize);
  589. } else {
  590. // 显示错误信息
  591. videoResultDiv.innerHTML = `
  592. <div class="video-error">
  593. <i class="fas fa-exclamation-triangle"></i>
  594. <p>视频生成失败:${response.msg || '未知错误'}</p>
  595. </div>
  596. `;
  597. }
  598. },
  599. error: function(xhr, status, error) {
  600. // 处理网络错误
  601. videoResultDiv.innerHTML = `
  602. <div class="video-error">
  603. <i class="fas fa-exclamation-triangle"></i>
  604. <p>视频生成失败:网络错误</p>
  605. </div>
  606. `;
  607. }
  608. });
  609. }
  610. // 页面加载完成后绑定事件
  611. $(document).ready(function() {
  612. // 绑定生成视频按钮点击事件
  613. $('#generateBtn').click(generateVideo);
  614. // 绑定保存模板按钮点击事件
  615. $('#saveBtn').click(saveTemplate);
  616. // 绑定下载视频按钮点击事件
  617. $('#downloadBtn').click(downloadVideo);
  618. // 绑定清空按钮点击事件
  619. $('.clear-btn').click(clearPrompt);
  620. // 绑定一键优化按钮点击事件
  621. $('.optimize-btn').click(optimizePrompt);
  622. // 键盘ESC键关闭模态框事件
  623. document.addEventListener('keydown', function(event) {
  624. if (event.key === 'Escape') {
  625. const modal = document.getElementById('videoModal');
  626. if (modal && modal.style.display === 'flex') {
  627. modal.style.display = 'none';
  628. }
  629. }
  630. });
  631. });
  632. // 一键优化提示词功能
  633. function optimizePrompt() {
  634. const textarea = document.getElementById('prompt');
  635. const optimizeBtn = document.querySelector('.optimize-btn');
  636. if (textarea && textarea.value.trim() !== '') {
  637. // 显示加载状态
  638. const originalBtnText = optimizeBtn.innerHTML;
  639. optimizeBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 优化中...';
  640. optimizeBtn.disabled = true;
  641. // 构造请求参数
  642. const params = {
  643. status_val: '文生文',
  644. prompt: textarea.value,
  645. model: 'gemini-2.0-flash'
  646. };
  647. // 发送AJAX请求
  648. $.ajax({
  649. url: '/index.php/api/work_order/GetTxtToTxt',
  650. type: 'POST',
  651. data: params,
  652. dataType: 'json',
  653. success: function(data) {
  654. console.log(data);
  655. // 恢复按钮状态
  656. optimizeBtn.innerHTML = originalBtnText;
  657. optimizeBtn.disabled = false;
  658. // 优化成功,更新文本框内容
  659. textarea.value = data.content;
  660. textarea.scrollTop = textarea.scrollHeight;
  661. updateCharCount();
  662. // 显示优化成功提示
  663. const alert = document.createElement('div');
  664. alert.className = 'alert alert-success';
  665. alert.innerHTML = '提示词已优化!';
  666. alert.style.position = 'fixed';
  667. alert.style.top = '20px';
  668. alert.style.right = '20px';
  669. alert.style.zIndex = '9999';
  670. document.body.appendChild(alert);
  671. // 3秒后移除提示
  672. setTimeout(() => {
  673. alert.remove();
  674. }, 3000);
  675. }
  676. });
  677. } else {
  678. alert('请先输入提示词!');
  679. }
  680. }
  681. // 清空提示词功能
  682. function clearPrompt() {
  683. const textarea = document.getElementById('prompt');
  684. if (textarea) {
  685. textarea.value = '';
  686. textarea.focus();
  687. }
  688. }
  689. // 轮询检查视频状态函数
  690. function startVideoPolling(videoId, videoStyle, videoDuration, videoSize) {
  691. let pollingCount = 0;
  692. const maxPollingCount = 60; // 最大轮询次数 (30次 * 5秒 = 2.5分钟)
  693. const pollingInterval = 6000; // 轮询间隔 (5秒)
  694. // 轮询检查视频状态
  695. const pollingTimer = setInterval(function() {
  696. $.ajax({
  697. url: '/index.php/api/work_order/Getvideo_id',
  698. type: 'GET',
  699. dataType: 'json',
  700. data: {
  701. video_id: videoId
  702. },
  703. success: function(response) {
  704. if (response.code === 0 && response.data && response.data.web_url) {
  705. // 视频生成完成
  706. clearInterval(pollingTimer);
  707. const videoResultDiv = document.getElementById('videoResult');
  708. let videoUrl = response.data.web_url;
  709. // 确保web_url格式正确,添加根路径前缀
  710. if (videoUrl && !videoUrl.startsWith('/')) {
  711. videoUrl = '/' + videoUrl;
  712. }
  713. // 显示生成的视频
  714. videoResultDiv.innerHTML = `
  715. <div class="video-container">
  716. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  717. 您的浏览器不支持视频播放
  718. </video>
  719. </div>
  720. `;
  721. // 更新视频信息到全局变量
  722. videoInfo = {
  723. style: videoStyle,
  724. duration: videoDuration,
  725. size: videoSize,
  726. web_url: videoUrl,
  727. video_id: response.data.video_id || videoId
  728. };
  729. // 启用下载按钮和保存模板按钮
  730. document.getElementById('downloadBtn').disabled = false;
  731. document.getElementById('saveBtn').disabled = false;
  732. // 添加视频放大模态框(如果还不存在)
  733. if (!document.getElementById('videoModal')) {
  734. const modal = document.createElement('div');
  735. modal.id = 'videoModal';
  736. modal.style.cssText = `
  737. display: none;
  738. position: fixed;
  739. z-index: 9999;
  740. left: 0;
  741. top: 0;
  742. width: 100%;
  743. height: 100%;
  744. overflow: auto;
  745. background-color: rgba(0, 0, 0, 0.9);
  746. justify-content: center;
  747. align-items: center;
  748. `;
  749. modal.style.display = 'flex';
  750. modal.style.display = 'none';
  751. const modalContent = document.createElement('video');
  752. modalContent.id = 'modalVideo';
  753. modalContent.style.cssText = `
  754. max-width: 90%;
  755. max-height: 90%;
  756. margin: auto;
  757. `;
  758. modalContent.setAttribute('controls', 'controls');
  759. const closeBtn = document.createElement('span');
  760. closeBtn.innerHTML = '&times;';
  761. closeBtn.style.cssText = `
  762. position: absolute;
  763. top: 20px;
  764. right: 35px;
  765. color: #f1f1f1;
  766. font-size: 40px;
  767. font-weight: bold;
  768. transition: 0.3s;
  769. cursor: pointer;
  770. `;
  771. closeBtn.onclick = function() {
  772. modal.style.display = 'none';
  773. };
  774. modal.appendChild(closeBtn);
  775. modal.appendChild(modalContent);
  776. document.body.appendChild(modal);
  777. // 点击模态框背景关闭
  778. modal.onclick = function(event) {
  779. if (event.target.id === 'videoModal') {
  780. this.style.display = 'none';
  781. }
  782. };
  783. }
  784. // 为生成的视频添加点击事件
  785. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  786. generatedVideo.onclick = function() {
  787. const modal = document.getElementById('videoModal');
  788. const modalVideo = document.getElementById('modalVideo');
  789. modalVideo.src = this.src;
  790. modal.style.display = 'flex';
  791. };
  792. } else if (response.code === 202 && response.data) {
  793. // 视频仍在生成中,更新状态信息
  794. const status = response.data.status;
  795. const progress = response.data.progress || 0;
  796. const progressBar = videoResultDiv.querySelector('.progress-bar');
  797. const statusText = videoResultDiv.querySelector('.video-processing p:nth-child(3)');
  798. const progressText = videoResultDiv.querySelector('.video-processing p:nth-child(4)');
  799. if (progressBar) {
  800. progressBar.style.width = `${progress}%`;
  801. }
  802. if (statusText) {
  803. statusText.textContent = `当前状态: ${status === 'queued' ? '排队中' : '处理中'}`;
  804. }
  805. // if (progressText) {
  806. // progressText.textContent = `进度: ${progress}%`;
  807. // }
  808. } else {
  809. // 视频生成失败
  810. clearInterval(pollingTimer);
  811. const videoResultDiv = document.getElementById('videoResult');
  812. videoResultDiv.innerHTML = `
  813. <div class="video-error">
  814. <i class="fas fa-exclamation-triangle"></i>
  815. <p>视频生成失败:${response.msg || '未知错误'}</p>
  816. </div>
  817. `;
  818. }
  819. },
  820. error: function(xhr, status, error) {
  821. // 网络错误
  822. clearInterval(pollingTimer);
  823. const videoResultDiv = document.getElementById('videoResult');
  824. videoResultDiv.innerHTML = `
  825. <div class="video-error">
  826. <i class="fas fa-exclamation-triangle"></i>
  827. <p>检查视频状态失败:网络错误</p>
  828. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后手动刷新页面查看结果</p>
  829. <div style="margin-top: 10px;">
  830. <button onclick="startVideoPolling('${videoId}', '${videoStyle}', '${videoDuration}', '${videoSize}')" style="padding: 5px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">
  831. 重新检查视频状态
  832. </button>
  833. </div>
  834. </div>
  835. `;
  836. }
  837. });
  838. // 检查是否达到最大轮询次数
  839. pollingCount++;
  840. if (pollingCount >= maxPollingCount) {
  841. clearInterval(pollingTimer);
  842. const videoResultDiv = document.getElementById('videoResult');
  843. const manualCheckDiv = document.createElement('div');
  844. manualCheckDiv.style.cssText = 'text-align: center; margin-top: 10px;';
  845. manualCheckDiv.innerHTML = `
  846. <button onclick="checkVideoStatus('${videoId}')" style="padding: 5px 15px; background-color: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">
  847. 手动检查视频状态
  848. </button>
  849. <p style="font-size: 12px; margin-top: 5px; color: #999;">或稍后刷新页面查看结果</p>
  850. `;
  851. videoResultDiv.appendChild(manualCheckDiv);
  852. }
  853. }, pollingInterval);
  854. }
  855. // 手动检查视频状态函数
  856. function checkVideoStatus(videoId) {
  857. $.ajax({
  858. url: '/index.php/api/work_order/Getvideo_id',
  859. type: 'GET',
  860. dataType: 'json',
  861. data: {
  862. video_id: videoId
  863. },
  864. success: function(response) {
  865. if (response.code === 0 && response.data && response.data.web_url) {
  866. // 视频生成完成,直接显示结果
  867. const videoResultDiv = document.getElementById('videoResult');
  868. let videoUrl = response.data.web_url;
  869. // 确保web_url格式正确,添加根路径前缀
  870. if (videoUrl && !videoUrl.startsWith('/')) {
  871. videoUrl = '/' + videoUrl;
  872. }
  873. // 显示生成的视频
  874. videoResultDiv.innerHTML = `
  875. <div class="video-container">
  876. <video src="${videoUrl}" alt="生成的视频" class="generated-video" id="previewVideo" style="max-width: 100%; height: auto; max-height: 600px; cursor: pointer;" controls>
  877. 您的浏览器不支持视频播放
  878. </video>
  879. </div>
  880. `;
  881. // 更新视频信息到全局变量
  882. videoInfo = {
  883. video_id: response.data.video_id || videoId,
  884. web_url: videoUrl
  885. };
  886. // 启用下载按钮和保存模板按钮
  887. document.getElementById('downloadBtn').disabled = false;
  888. document.getElementById('saveBtn').disabled = false;
  889. // 添加视频放大模态框(如果还不存在)
  890. if (!document.getElementById('videoModal')) {
  891. const modal = document.createElement('div');
  892. modal.id = 'videoModal';
  893. modal.style.cssText = `
  894. display: none;
  895. position: fixed;
  896. z-index: 9999;
  897. left: 0;
  898. top: 0;
  899. width: 100%;
  900. height: 100%;
  901. overflow: auto;
  902. background-color: rgba(0, 0, 0, 0.9);
  903. justify-content: center;
  904. align-items: center;
  905. `;
  906. modal.style.display = 'flex';
  907. modal.style.display = 'none';
  908. const modalContent = document.createElement('video');
  909. modalContent.id = 'modalVideo';
  910. modalContent.style.cssText = `
  911. max-width: 90%;
  912. max-height: 90%;
  913. margin: auto;
  914. `;
  915. modalContent.setAttribute('controls', 'controls');
  916. const closeBtn = document.createElement('span');
  917. closeBtn.innerHTML = '&times;';
  918. closeBtn.style.cssText = `
  919. position: absolute;
  920. top: 20px;
  921. right: 35px;
  922. color: #f1f1f1;
  923. font-size: 40px;
  924. font-weight: bold;
  925. transition: 0.3s;
  926. cursor: pointer;
  927. `;
  928. closeBtn.onclick = function() {
  929. modal.style.display = 'none';
  930. };
  931. modal.appendChild(closeBtn);
  932. modal.appendChild(modalContent);
  933. document.body.appendChild(modal);
  934. // 点击模态框背景关闭
  935. modal.onclick = function(event) {
  936. if (event.target.id === 'videoModal') {
  937. this.style.display = 'none';
  938. }
  939. };
  940. }
  941. // 为生成的视频添加点击事件
  942. const generatedVideo = videoResultDiv.querySelector('.generated-video');
  943. generatedVideo.onclick = function() {
  944. const modal = document.getElementById('videoModal');
  945. const modalVideo = document.getElementById('modalVideo');
  946. modalVideo.src = this.src;
  947. modal.style.display = 'flex';
  948. };
  949. } else {
  950. // 视频仍在生成中或失败
  951. const videoResultDiv = document.getElementById('videoResult');
  952. if (response.code === 202) {
  953. const progress = response.data.progress || 0;
  954. const message = response.msg || response.data.message || '';
  955. videoResultDiv.innerHTML = `
  956. <div class="video-processing">
  957. <i class="fas fa-hourglass-half fa-spin"></i>
  958. <p>${message || '视频正在生成中...'}</p>
  959. <p style="font-size: 12px; margin-top: 10px; color: #999;">视频ID: ${videoId}</p>
  960. <p style="font-size: 12px; margin-top: 5px; color: #999;">当前状态: ${response.data.status === 'queued' ? '排队中' : '处理中'}</p>
  961. <div class="progress-container" style="width: 100%; background-color: #eee; margin-top: 15px;">
  962. <div class="progress-bar" style="width: ${progress}%; height: 20px; background-color: #4CAF50;"></div>
  963. </div>
  964. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后再次手动检查</p>
  965. </div>
  966. `;
  967. } else {
  968. videoResultDiv.innerHTML = `
  969. <div class="video-error">
  970. <i class="fas fa-exclamation-triangle"></i>
  971. <p>视频生成失败:${response.msg || '未知错误'}</p>
  972. </div>
  973. `;
  974. }
  975. }
  976. },
  977. error: function(xhr, status, error) {
  978. // 网络错误
  979. const videoResultDiv = document.getElementById('videoResult');
  980. videoResultDiv.innerHTML = `
  981. <div class="video-error">
  982. <i class="fas fa-exclamation-triangle"></i>
  983. <p>检查视频状态失败:网络错误</p>
  984. <p style="font-size: 12px; margin-top: 5px; color: #999;">请稍后再次尝试</p>
  985. </div>
  986. `;
  987. }
  988. });
  989. }
  990. // 保存模板函数
  991. function saveTemplate() {
  992. // 获取模板名称
  993. const templateName = document.getElementById('templateName').value.trim();
  994. const textarea = document.getElementById('prompt');
  995. // 验证模板名称
  996. if (!templateName) {
  997. showAlert('模板名称不能为空');
  998. return;
  999. }
  1000. // 检查是否有视频信息
  1001. if (!videoInfo || !videoInfo.web_url) {
  1002. showAlert('请先生成视频');
  1003. return;
  1004. }
  1005. // 构建请求参数
  1006. const params = {
  1007. chinese_description: textarea.value.trim(),
  1008. template_image_url: videoInfo.web_url,
  1009. template_name: templateName,
  1010. size: videoInfo.size,
  1011. style: videoInfo.style,
  1012. type: "文生视频",
  1013. sys_id:"管理员",
  1014. seconds: videoInfo.duration,
  1015. video_id: videoInfo.video_id
  1016. };
  1017. // 发送请求保存模板
  1018. $.ajax({
  1019. url: '/index.php/api/work_order/Add_Product_Template',
  1020. type: 'POST',
  1021. dataType: 'json',
  1022. data: params,
  1023. success: function(response) {
  1024. if (response.code === 0) {
  1025. showAlert('模板保存成功');
  1026. // 清空模板名称输入框
  1027. document.getElementById('templateName').value = '';
  1028. } else {
  1029. showAlert('模板保存失败: ' + response.msg, 'error');
  1030. }
  1031. },
  1032. error: function(xhr, status, error) {
  1033. showAlert('保存模板时发生错误: ' + error, 'error');
  1034. }
  1035. });
  1036. }
  1037. // 下载视频函数
  1038. function downloadVideo() {
  1039. const previewVideo = document.getElementById('previewVideo');
  1040. if (previewVideo && previewVideo.src) {
  1041. // 创建临时链接下载视频
  1042. const link = document.createElement('a');
  1043. link.href = previewVideo.src;
  1044. // 生成更有意义的文件名
  1045. const timestamp = new Date().getTime();
  1046. const promptText = document.getElementById('prompt').value.trim();
  1047. // 从提示词中提取前20个字符作为文件名的一部分
  1048. const promptPart = promptText.substring(0, 20).replace(/[^\w\u4e00-\u9fa5]/g, '') || 'video';
  1049. // 组合文件名:提示词部分_时间戳.mp4
  1050. const fileName = `${promptPart}_${timestamp}.mp4`;
  1051. link.download = fileName;
  1052. link.click();
  1053. }
  1054. }
  1055. </script>
  1056. </body>
  1057. </html>