邃芒慧影、口播(PC) https://neuver.metavatar.cc/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

2527 line
70 KiB

  1. <template>
  2. <div class="page flex justify-around">
  3. <!-- 遮罩层 -->
  4. <div class="mask" v-show="showChooseModel"></div>
  5. <!-- 左侧栏 -->
  6. <div class="left">
  7. <!-- 字幕框 -->
  8. <div v-show="videoType == 2" class="subtitlesBoxTop">
  9. <!-- 开关 -->
  10. <div class="tab4Item">
  11. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  12. <p>{{ $t("createVideo.subtitlesSwitch") }}</p>
  13. <el-switch
  14. v-model="Subtitles.SubtitlesSwitch"
  15. style="--el-switch-on-color: #000"
  16. />
  17. </div>
  18. </div>
  19. <!-- 字体系列 -->
  20. <div class="tab4Item">
  21. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  22. <p>
  23. {{ lanChange == "zh-cn" ? `字体` : `Font Family` }}
  24. </p>
  25. <el-select
  26. v-model="Subtitles.SubtitlesFontFamily"
  27. style="width: 100px"
  28. placeholder=" "
  29. size="default"
  30. >
  31. <el-option
  32. v-for="item in Subtitles.fontFamilyList"
  33. :key="item.value"
  34. :label="item.label"
  35. :value="item.value"
  36. />
  37. </el-select>
  38. </div>
  39. </div>
  40. <!-- y轴 -->
  41. <div class="tab4Item">
  42. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  43. <p>
  44. {{ lanChange == "zh-cn" ? `纵坐标` : `Y-axis` }}
  45. </p>
  46. <el-input-number
  47. :precision="0"
  48. v-model="Subtitles.SubtitlesTop"
  49. size="large"
  50. controls-position="right"
  51. />
  52. </div>
  53. </div>
  54. <!-- 字号大小 -->
  55. <div class="tab4Item">
  56. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  57. <p>
  58. {{ lanChange == "zh-cn" ? `字号大小` : `Font Size` }}
  59. </p>
  60. <el-select
  61. v-model="Subtitles.SubtitlesSize"
  62. style="width: 100px"
  63. placeholder=" "
  64. size="default"
  65. >
  66. <el-option
  67. v-for="item in Subtitles.fontSizeList"
  68. :key="item"
  69. :label="item"
  70. :value="item"
  71. :style="{
  72. height: item + 'px',
  73. lineHeight: item + 'px',
  74. }"
  75. >
  76. <span :style="{ fontSize: item + 'px', padding: '5px' }">{{
  77. item
  78. }}</span>
  79. </el-option>
  80. </el-select>
  81. </div>
  82. </div>
  83. <!-- 字色 -->
  84. <div class="tab4Item">
  85. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  86. <p>
  87. {{ lanChange == "zh-cn" ? `字体颜色` : `Font Color` }}
  88. </p>
  89. <div
  90. @click="handleSubtitlesColorShow"
  91. class="myColorBox"
  92. :style="{ backgroundColor: Subtitles.SubtitlesColor }"
  93. >
  94. <div v-show="SubtitlesColorShow" class="selectColor">
  95. <div
  96. v-for="item in Subtitles.colorList"
  97. :style="{ backgroundColor: item }"
  98. class="selectColor-item"
  99. @click="Subtitles.SubtitlesColor = item"
  100. ></div>
  101. </div>
  102. </div>
  103. </div>
  104. </div>
  105. </div>
  106. <div v-show="videoType == 1" class="subtitlesBoxLeft">
  107. <!-- 开关 -->
  108. <div class="tab4Item">
  109. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  110. <p>{{ $t("createVideo.subtitlesSwitch") }}</p>
  111. <el-switch
  112. v-model="Subtitles.SubtitlesSwitch"
  113. style="--el-switch-on-color: #000"
  114. />
  115. </div>
  116. </div>
  117. <!-- 字体系列 -->
  118. <div class="tab4Item">
  119. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  120. <p>
  121. {{ lanChange == "zh-cn" ? `字体` : `Font Family` }}
  122. </p>
  123. <el-select
  124. v-model="Subtitles.SubtitlesFontFamily"
  125. style="width: 100px"
  126. placeholder=" "
  127. size="default"
  128. >
  129. <el-option
  130. v-for="item in Subtitles.fontFamilyList"
  131. :key="item.value"
  132. :label="item.label"
  133. :value="item.value"
  134. />
  135. </el-select>
  136. </div>
  137. </div>
  138. <!-- y轴 -->
  139. <div class="tab4Item">
  140. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  141. <p>
  142. {{ lanChange == "zh-cn" ? `纵坐标` : `Y-axis` }}
  143. </p>
  144. <el-input-number
  145. :precision="0"
  146. v-model="Subtitles.SubtitlesTop"
  147. size="large"
  148. controls-position="right"
  149. />
  150. </div>
  151. </div>
  152. <!-- 字号大小 -->
  153. <div class="tab4Item">
  154. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  155. <p>
  156. {{ lanChange == "zh-cn" ? `字号大小` : `Font Size` }}
  157. </p>
  158. <el-select
  159. v-model="Subtitles.SubtitlesSize"
  160. style="width: 100px"
  161. placeholder=" "
  162. size="default"
  163. >
  164. <el-option
  165. v-for="item in Subtitles.fontSizeList"
  166. :key="item"
  167. :label="item"
  168. :value="item"
  169. :style="{
  170. height: item + 'px',
  171. lineHeight: item + 'px',
  172. }"
  173. >
  174. <span :style="{ fontSize: item + 'px', padding: '5px' }">{{
  175. item
  176. }}</span>
  177. </el-option>
  178. </el-select>
  179. </div>
  180. </div>
  181. <!-- 字色 -->
  182. <div class="tab4Item">
  183. <div v-if="materialList.BGI.material" class="SubtitlesSwitch">
  184. <p>
  185. {{ lanChange == "zh-cn" ? `字体颜色` : `Font Color` }}
  186. </p>
  187. <div
  188. @click="handleSubtitlesColorShow"
  189. class="myColorBox"
  190. :style="{ backgroundColor: Subtitles.SubtitlesColor }"
  191. >
  192. <div v-show="SubtitlesColorShow" class="selectColor">
  193. <div
  194. v-for="item in Subtitles.colorList"
  195. :style="{ backgroundColor: item }"
  196. class="selectColor-item"
  197. @click="Subtitles.SubtitlesColor = item"
  198. ></div>
  199. </div>
  200. </div>
  201. </div>
  202. </div>
  203. </div>
  204. <!-- 选择模版弹出框 -->
  205. <div class="chooseModelBox" :class="showChooseModel ? '' : 'noModel'">
  206. <!-- 伸缩按钮 -->
  207. <div
  208. v-if="showChooseModel && videoType"
  209. class="shrinkBtn"
  210. @click="showChooseModel = false"
  211. >
  212. &gt;
  213. </div>
  214. <div
  215. v-if="!showChooseModel"
  216. class="shrinkBtn"
  217. @click="showChooseModel = true"
  218. >
  219. &lt;
  220. </div>
  221. <!-- tab栏 性别-->
  222. <p>{{ $t("createVideo.sex") }}</p>
  223. <div class="tab" v-show="showChooseModel">
  224. <div
  225. class="tabItem"
  226. :class="modelTabItemActive == -1 ? 'tabItemActive' : ''"
  227. @click="changeTabSex(-1)"
  228. >
  229. {{ $t("createVideo.all") }}
  230. </div>
  231. <div
  232. class="tabItem"
  233. :class="modelTabItemActive == 1 ? 'tabItemActive' : ''"
  234. @click="changeTabSex(1)"
  235. >
  236. {{ $t("createVideo.male") }}
  237. </div>
  238. <div
  239. class="tabItem"
  240. :class="modelTabItemActive == 2 ? 'tabItemActive' : ''"
  241. @click="changeTabSex(2)"
  242. >
  243. {{ $t("createVideo.female") }}
  244. </div>
  245. </div>
  246. <!-- 横屏竖屏 -->
  247. <p>{{ $t("createVideo.videoRatio") }}</p>
  248. <div class="tab" v-show="showChooseModel">
  249. <div
  250. class="tabItem"
  251. :class="modelTabItemActive2 == -1 ? 'tabItemActive' : ''"
  252. @click="changeTabVideoType(-1)"
  253. >
  254. {{ $t("createVideo.all") }}
  255. </div>
  256. <div
  257. class="tabItem"
  258. :class="modelTabItemActive2 == 2 ? 'tabItemActive' : ''"
  259. @click="changeTabVideoType(2)"
  260. >
  261. {{ $t("createVideo.landscape") }}
  262. </div>
  263. <div
  264. class="tabItem"
  265. :class="modelTabItemActive2 == 1 ? 'tabItemActive' : ''"
  266. @click="changeTabVideoType(1)"
  267. >
  268. {{ $t("createVideo.portrait") }}
  269. </div>
  270. </div>
  271. <!-- 风格模版 -->
  272. <!-- <p>{{ $t("createVideo.videoRatio") }}</p>
  273. <div class="tab" v-show="showChooseModel">
  274. <div
  275. class="tabItem"
  276. :class="modelTabItemActive3 == -1 ? 'tabItemActive' : ''"
  277. @click="changeModelStyle(-1)"
  278. >
  279. {{ $t("createVideo.all") }}
  280. </div>
  281. </div> -->
  282. <!-- 模版列表 -->
  283. <div
  284. class="modeListBox"
  285. v-show="showChooseModel"
  286. v-infinite-scroll="loadMoreModel"
  287. :infinite-scroll-disabled="overLoadModel"
  288. >
  289. <div
  290. :class="modelId == item.id ? 'modeListBoxItmeActive' : ''"
  291. class="modeListBoxItme"
  292. v-for="item in modeList"
  293. :key="item.id"
  294. @click="clickModelItem(item)"
  295. >
  296. <img class="boxCentreImg" :src="item.coverImg" alt="" />
  297. <div v-if="item.customized === 1" class="subscript">私人定制</div>
  298. </div>
  299. </div>
  300. </div>
  301. <!-- 展示区 -->
  302. <div class="showModelImgBox">
  303. <!-- <img :src="modelImg" alt="" /> -->
  304. <!-- 编辑图片位置组件 -->
  305. <div
  306. :class="videoType == 1 ? 'EditPositionBox1' : 'EditPositionBox2'"
  307. class=""
  308. >
  309. <EditPosition
  310. ref="EditPositionDom"
  311. id="editPosition"
  312. :materialList="materialList"
  313. :videoType="videoType"
  314. :proportion="proportion"
  315. :showImgInfo="showImgInfo"
  316. :Subtitles="Subtitles"
  317. >
  318. </EditPosition>
  319. </div>
  320. <!-- 控制台 -->
  321. <div v-if="showImgInfo.data" class="showImgInfoBox">
  322. <!-- <div class="infoItem">
  323. </div> -->
  324. <div class="ItemName">{{ $t("createVideo.abscissa") }}</div>
  325. <div class="ItemValue">
  326. <el-input-number
  327. :precision="0"
  328. v-model="showImgInfo.data.x"
  329. size="large"
  330. controls-position="right"
  331. />
  332. </div>
  333. <div class="ItemName">{{ $t("createVideo.ordinate") }}</div>
  334. <div class="ItemValue">
  335. <el-input-number
  336. :precision="0"
  337. v-model="showImgInfo.data.y"
  338. size="large"
  339. controls-position="right"
  340. />
  341. </div>
  342. <div class="ItemName">{{ $t("createVideo.hierarchy") }}</div>
  343. <div class="ItemValue">
  344. <el-input-number
  345. v-model="showImgInfo.data.z"
  346. :min="1"
  347. :max="10"
  348. size="large"
  349. controls-position="right"
  350. />
  351. </div>
  352. <div class="ItemName">{{ $t("createVideo.ratio") }}</div>
  353. <div class="ItemValue">
  354. <el-input-number
  355. :precision="2"
  356. v-model="showImgInfo.data.w"
  357. :min="0.4"
  358. :max="1.3"
  359. size="large"
  360. controls-position="right"
  361. />
  362. </div>
  363. </div>
  364. <!-- 保存视频和生成视频 -->
  365. <div class="btnBox" v-show="!showChooseModel">
  366. <div
  367. @click="saveOrUpdate('isSave')"
  368. class="customBtn"
  369. style="margin-right: 15px"
  370. >
  371. <MyLoadingIcon :isLoading="saveLoading"></MyLoadingIcon>
  372. <div v-if="!saveLoading">
  373. {{ $t("createVideo.saveVideo") }}
  374. </div>
  375. </div>
  376. <div @click="createVideo" class="customBtn">
  377. <MyLoadingIcon :isLoading="createLoading"></MyLoadingIcon>
  378. <div v-if="!createLoading">
  379. {{ $t("createVideo.generateVideo") }}
  380. </div>
  381. </div>
  382. </div>
  383. </div>
  384. </div>
  385. <!-- 右侧栏 -->
  386. <div class="right">
  387. <!-- tab栏 -->
  388. <div class="tab">
  389. <div
  390. id="tab-0"
  391. class="tabItem"
  392. :class="tabItemActive == 1 ? 'tabItemActive' : ''"
  393. @click="tabItemActive = 1"
  394. >
  395. {{ $t("createVideo.copywriting") }}
  396. </div>
  397. <div
  398. class="tabItem"
  399. :class="tabItemActive == 2 ? 'tabItemActive' : ''"
  400. @click="tabItemActive = 2"
  401. >
  402. {{ $t("createVideo.BackgroundImage") }}
  403. </div>
  404. <div
  405. class="tabItem"
  406. :class="tabItemActive == 3 ? 'tabItemActive' : ''"
  407. @click="tabItemActive = 3"
  408. >
  409. {{ $t("createVideo.material") }}
  410. </div>
  411. <div
  412. class="tabItem"
  413. :class="tabItemActive == 4 ? 'tabItemActive' : ''"
  414. @click="tabItemActive = 4"
  415. >
  416. <img class="gpt" src="../../assets/img/gpt.png" alt="">
  417. {{ $t("createVideo.aiScript") }}
  418. </div>
  419. <!-- <div
  420. class="tabItem"
  421. :class="tabItemActive == 5 ? 'tabItemActive' : ''"
  422. @click="tabItemActive = 5"
  423. >
  424. {{ $t("createVideo.AItext") }}
  425. </div> -->
  426. </div>
  427. <!-- 文案 只能使用v-if,不能使用v-show-->
  428. <div v-if="tabItemActive == 1" class="tabContent tab1">
  429. <!-- 标题 -->
  430. <div class="title">
  431. <el-input
  432. style="width: 90%"
  433. maxlength="15"
  434. v-model="title"
  435. :placeholder="$t('createVideo.typeTitle')"
  436. />
  437. <el-icon class="icon-edit"><i-ep-edit /></el-icon>
  438. </div>
  439. <!-- 文本域 -->
  440. <div class="text">
  441. <!-- 文本域 -->
  442. <el-input
  443. maxlength="1000"
  444. id="textInputId"
  445. v-model="textareaContent"
  446. :rows="15"
  447. type="textarea"
  448. :placeholder="$t('createVideo.aiText')"
  449. />
  450. <span
  451. style="margin-right: 10px; font-size: 20px"
  452. class="pointer"
  453. @click="insertIcon"
  454. >
  455. <el-tooltip
  456. class="box-item"
  457. effect="dark"
  458. :content="$t('createVideo.tooltip2')"
  459. placement="bottom"
  460. >
  461. 🕗
  462. </el-tooltip>
  463. </span>
  464. <i style="float: right; font-size: 14px; padding-top: 5px"
  465. >{{ $t("createVideo.entered") }} {{ textareaContent.length }} /
  466. 1000</i
  467. >
  468. </div>
  469. <p>{{ $t("createVideo.languages") }}</p>
  470. <!-- 语言 -->
  471. <el-select
  472. @change="chooseType"
  473. v-model="language"
  474. :placeholder="$t('createVideo.select')"
  475. style="width: 100%"
  476. filterable
  477. >
  478. <el-option
  479. v-for="item in languageOptions"
  480. :key="item.value"
  481. :label="lanChange == 'zh-cn' ? item.chineseName : item.name"
  482. :value="item.id"
  483. >
  484. <img
  485. :src="item.img"
  486. alt=""
  487. style="width: 20px; margin-right: 10px; vertical-align: text-top"
  488. />
  489. <span style="line-height: 20px">{{
  490. lanChange == "zh-cn" ? item.chineseName : item.name
  491. }}</span>
  492. <span v-if="item.customized === 1" style="color:red">(私人订制)</span>
  493. </el-option>
  494. </el-select>
  495. <p>{{ $t("createVideo.voices") }}</p>
  496. <!-- 声音 -->
  497. <el-select
  498. @change="chooseStyle"
  499. :disabled="language ? false : true"
  500. v-model="sound"
  501. :placeholder="$t('createVideo.select')"
  502. style="width: 100%"
  503. >
  504. <el-option
  505. v-for="item in soundOptions"
  506. :key="item.value"
  507. :value="item.id"
  508. :label="(lanChange == 'zh-cn'
  509. ? item.displayName + `&nbsp; (${item.sex == 1 ? '男' : '女'})`
  510. : item.localName +
  511. `&nbsp; (${item.sex == 1 ? 'male' : 'female'})`) +
  512. returnAge(item.ageType)
  513. "
  514. > {{ (lanChange == 'zh-cn'
  515. ? item.displayName + `&nbsp; (${item.sex == 1 ? '男' : '女'})`
  516. : item.localName +
  517. `&nbsp; (${item.sex == 1 ? 'male' : 'female'})`) +
  518. returnAge(item.ageType)
  519. }}
  520. </el-option>
  521. </el-select>
  522. <p>{{ $t("createVideo.styles") }}</p>
  523. <!-- 风格 -->
  524. <el-select
  525. :disabled="soundStyleOptions ? false : true"
  526. v-model="soundStyle"
  527. :placeholder="$t('createVideo.select')"
  528. style="width: 100%"
  529. >
  530. <el-option
  531. v-for="item in soundStyleOptions"
  532. :key="item.name"
  533. :label="lanChange == 'zh-cn' ? item.name : item.engName"
  534. :value="item.name"
  535. >
  536. <div style="padding: 0 20px">
  537. {{ lanChange == "zh-cn" ? item.name : item.engName }}
  538. <span
  539. style="float: right; cursor: pointer"
  540. @click.stop="ListenVoices(item.url)"
  541. >🔊</span
  542. >
  543. </div>
  544. </el-option>
  545. </el-select>
  546. </div>
  547. <!-- 背景 -->
  548. <div v-if="tabItemActive == 2" class="tabContent tab2">
  549. <div class="tab">
  550. <div
  551. class="tabItem"
  552. :class="bjTabItemActive == 1 ? 'tabItemActive' : ''"
  553. @click="changeBjTabItemActive(1)"
  554. >
  555. {{ $t("createVideo.systemBGI") }}
  556. </div>
  557. <div
  558. class="tabItem"
  559. :class="bjTabItemActive == 2 ? 'tabItemActive' : ''"
  560. @click="changeBjTabItemActive(2)"
  561. >
  562. {{ $t("createVideo.personBGI") }}
  563. </div>
  564. </div>
  565. <!-- 系统背景图片展示 -->
  566. <div
  567. v-if="bjTabItemActive == 1"
  568. class="bjSystemBox"
  569. v-infinite-scroll="loadMoreSystemBGI"
  570. :infinite-scroll-disabled="overLoadSystemBG"
  571. >
  572. <div
  573. class="bjSystemBoxItem"
  574. v-for="item in systemBGIList"
  575. :key="item.id"
  576. :class="bjImgActive == item.id ? 'ImgBorderActive' : ''"
  577. @click="selectBjImg(item)"
  578. >
  579. <img class="boxCentreImg" :src="item.material" alt="" />
  580. </div>
  581. </div>
  582. <!-- 个人背景图片展示 -->
  583. <div
  584. v-if="bjTabItemActive == 2"
  585. class="bjSystemBox"
  586. v-infinite-scroll="loadMorePersonBGI"
  587. :infinite-scroll-disabled="overLoadpersonBG"
  588. >
  589. <!-- 上传个人背景 -->
  590. <div class="bjSystemBoxItem">
  591. <img
  592. class="boxCentreImg"
  593. src="../../assets/img/uploadImg.png"
  594. alt=""
  595. />
  596. <cutImg
  597. @reGetpersonBGIList="reGetpersonBGIList"
  598. :type="4"
  599. :videoType="videoType"
  600. style="width: 100%; height: 100%"
  601. ></cutImg>
  602. </div>
  603. <div
  604. class="bjSystemBoxItem"
  605. v-for="item in personBGIList"
  606. :key="item.id"
  607. :class="bjImgActive == item.id ? 'ImgBorderActive' : ''"
  608. @click="selectBjImg(item)"
  609. >
  610. <div
  611. v-show="bjImgActive == item.id"
  612. class="delIcon"
  613. @click="delImg(item.id)"
  614. >
  615. <img src="../../assets/img/delete.png" alt="" />
  616. </div>
  617. <img class="boxCentreImg" :src="item.material" alt="" />
  618. </div>
  619. </div>
  620. </div>
  621. <!-- 素材 -->
  622. <div v-if="tabItemActive == 3" class="tabContent tab3">
  623. <div class="tab">
  624. <div
  625. class="tabItem"
  626. :class="scTabItemActive == 1 ? 'tabItemActive' : ''"
  627. @click="changeScTabItemActive(1)"
  628. >
  629. {{ $t("createVideo.systemMaterial") }}
  630. </div>
  631. <div
  632. class="tabItem"
  633. :class="scTabItemActive == 2 ? 'tabItemActive' : ''"
  634. @click="changeScTabItemActive(2)"
  635. >
  636. {{ $t("createVideo.personMaterial") }}
  637. </div>
  638. </div>
  639. <!-- 系统素材图片展示 -->
  640. <div
  641. v-if="scTabItemActive == 1"
  642. class="bjSystemBox"
  643. v-infinite-scroll="loadMoreSystemSCI"
  644. :infinite-scroll-disabled="overLoadSystemSC"
  645. >
  646. <div
  647. class="bjSystemBoxItem"
  648. v-for="item in systemSCIList"
  649. :key="item.id"
  650. :class="scImgActive == item.id ? 'ImgBorderActive' : ''"
  651. @click="selectScImg(item)"
  652. >
  653. <img class="boxCentreImg" :src="item.material" alt="" />
  654. </div>
  655. </div>
  656. <!-- 个人素材图片展示 -->
  657. <div
  658. v-if="scTabItemActive == 2"
  659. class="bjSystemBox"
  660. v-infinite-scroll="loadMorePersonSCI"
  661. :infinite-scroll-disabled="overLoadpersonSC"
  662. >
  663. <div class="bjSystemBoxItem">
  664. <img
  665. class="boxCentreImg"
  666. src="../../assets/img/uploadImg.png"
  667. alt=""
  668. />
  669. <cutImg
  670. @reGetpersonSCIList="reGetpersonSCIList"
  671. :type="5"
  672. :videoType="videoType"
  673. style="width: 100%; height: 100%"
  674. ></cutImg>
  675. </div>
  676. <div
  677. class="bjSystemBoxItem"
  678. v-for="item in personSCIList"
  679. :key="item.id"
  680. :class="scImgActive == item.id ? 'ImgBorderActive' : ''"
  681. @click="selectScImg(item)"
  682. >
  683. <div
  684. v-show="scImgActive == item.id"
  685. class="delIcon"
  686. @click="delImg(item.id)"
  687. >
  688. <img src="../../assets/img/delete.png" alt="" />
  689. </div>
  690. <img class="boxCentreImg" :src="item.material" alt="" />
  691. </div>
  692. </div>
  693. </div>
  694. <!-- 智能文案 -->
  695. <div v-if="tabItemActive == 4" class="tabContent tab4">
  696. <div class="card4">
  697. <!-- 语言 -->
  698. <div class="text">
  699. <!-- 文本域 -->
  700. <el-input
  701. maxlength="200"
  702. id="textInputId"
  703. v-model="problemContent"
  704. :rows="5"
  705. type="textarea"
  706. :placeholder="$t('createVideo.inputRequirement')"
  707. />
  708. </div>
  709. <el-button
  710. type="primary"
  711. :disabled="sendLoading"
  712. @click="sendRequirement"
  713. color="#000"
  714. >
  715. <my-Loading-Icon2 v-if="sendLoading"></my-Loading-Icon2>
  716. <span v-if="!sendLoading">
  717. {{ $t("createVideo.GeneratedText") }}
  718. </span>
  719. </el-button>
  720. <p>{{ $t("createVideo.SmartText") }}</p>
  721. <!-- 语言 -->
  722. <div class="text" style="cursor: text">
  723. <!-- 文本域 -->
  724. <el-input
  725. maxlength="500"
  726. id="textInputId"
  727. v-model="answerContent"
  728. :rows="15"
  729. type="textarea"
  730. disabled=""
  731. />
  732. <i
  733. >{{ $t("createVideo.entered") }} {{ answerContent.length }} /
  734. 500</i
  735. >
  736. </div>
  737. <el-button color="#000" @click="copyAnswerContent">
  738. {{ $t("createVideo.copy") }}
  739. </el-button>
  740. </div>
  741. </div>
  742. </div>
  743. <!-- 试听的mp3 -->
  744. <div style="opacity: 0; width: 0">
  745. <audio autoplay :src="previewAudioUrl" id="audio"></audio>
  746. </div>
  747. </div>
  748. </template>
  749. <!-- videoType==1为竖,==2为横 -->
  750. <script setup>
  751. //#region 导入
  752. import { getCurrentInstance } from "vue";
  753. import {
  754. modeListApi,
  755. voiceTotalApi,
  756. chooseTypeApi,
  757. previewAudioApi,
  758. systemBGIListApi,
  759. personBGIListApi,
  760. systemSCIListApi,
  761. personSCIListApi,
  762. delImgApi,
  763. personPatchApi,
  764. saveOrUpdateApi,
  765. createVideoApi,
  766. getModelDetailByIdApi,
  767. getLanguageApi,
  768. checkVideoApi
  769. } from "@/apis/createVideo";
  770. import { useI18n } from "vue-i18n";
  771. import Recorder from "js-audio-recorder"; //mp3插件
  772. import imageCompression from "browser-image-compression"; //压缩图片插件
  773. import { useRoute, useRouter } from "vue-router";
  774. import cutImg from "@/components/cutImg.vue";
  775. //#endregion -----------------------------------
  776. const route = useRoute();
  777. const router = useRouter();
  778. const { locale } = useI18n();
  779. const lanChange = ref(locale);
  780. const AccessToken = localStorage.getItem("AccessToken"); // 判断有没有登录
  781. // ElMessage.error(
  782. // lanChange.value == "zh-cn"
  783. // ? `您还没有输入文案`
  784. // : `You have not entered the copy yet`
  785. // );
  786. //#region 选择模版弹出框
  787. const videoType = ref(null); //判断横屏2还是竖屏1,初始为没有,控制按钮显隐
  788. const showChooseModel = ref(true); // 控制弹窗显示隐藏
  789. const modeList = ref([]); //模版列表
  790. const modelImg = ref(null); //模版回显图
  791. const modelTabItemActive = ref(-1); //模版tab栏 性别
  792. const modelTabItemActive2 = ref(-1); //模版tab栏 比例
  793. const modelPageNum = ref(1);
  794. const modelPageSize = ref(20);
  795. const overLoadModel = ref(true); //禁用模版加载更多
  796. const modelId = ref(null); // 选中模版的id
  797. // 获取模版列表
  798. async function getModeList(sex, pageNum, pageSize, videoType) {
  799. try {
  800. const res = await modeListApi(sex, pageNum, pageSize, videoType);
  801. // 排序 定制分身在前
  802. res.data.list.sort(function(a, b){
  803. return (b.customized) - (a.customized)
  804. })
  805. modeList.value = res.data.list;
  806. overLoadModel.value = false;
  807. } catch (error) {
  808. // ElMessage.success(msg);
  809. }
  810. }
  811. // 点击模版
  812. function clickModelItem(item) {
  813. modelImg.value = item.coverImg;
  814. showChooseModel.value = false;
  815. videoType.value = item.videoType;
  816. modelId.value = item.id;
  817. }
  818. // tab栏切换 性别
  819. // tab栏切换 横竖比例 videoType==1为竖,==2为横,==-1为全部
  820. const tabSex = ref(-1);
  821. const tabVideoType = ref(-1);
  822. function changeTabSex(index) {
  823. modeList.value = [];
  824. modelPageNum.value = 1;
  825. modelTabItemActive.value = index;
  826. tabSex.value = false;
  827. tabSex.value = index;
  828. getModeList(
  829. index,
  830. modelPageNum.value,
  831. modelPageSize.value,
  832. tabVideoType.value
  833. );
  834. }
  835. function changeTabVideoType(index) {
  836. modeList.value = [];
  837. modelPageNum.value = 1;
  838. modelTabItemActive2.value = index;
  839. overLoadModel.value = false;
  840. tabVideoType.value = index;
  841. getModeList(
  842. tabSex.value,
  843. modelPageNum.value,
  844. modelPageSize.value,
  845. tabVideoType.value
  846. );
  847. }
  848. // 模版风格tab
  849. const modelStyleTabList = ref([]);
  850. const modelTabItemActive3 = ref(-1); //模版风格tab栏 比例
  851. function changeModelStyle(index) {
  852. modelTabItemActive3.value = index;
  853. }
  854. // watch(tabSex, (newVal, oldVal) => {
  855. // if (newVal == 0) {
  856. // getModeList("", modelPageNum.value, modelPageSize.value);
  857. // } else {
  858. // }
  859. // });
  860. // function changeTabSex(index) {
  861. // modeList.value = [];
  862. // modelPageNum.value = 1;
  863. // modelTabItemActive.value = index;
  864. // overLoadModel.value = false;
  865. // if (index == 0) {
  866. // getModeList("", modelPageNum.value, modelPageSize.value);
  867. // } else {
  868. // getModeList(index, modelPageNum.value, modelPageSize.value);
  869. // }
  870. // }
  871. // 触底刷新
  872. async function loadMoreModel(params) {
  873. modelPageNum.value += 1;
  874. const res = await modeListApi(
  875. tabSex.value,
  876. modelPageNum.value,
  877. modelPageSize.value,
  878. tabVideoType.value
  879. );
  880. modeList.value.push(...res.data.list);
  881. if (res.data.list.length == 0) {
  882. overLoadModel.value = true;
  883. modelPageNum.value = modelPageNum.value - 1;
  884. }
  885. }
  886. //#endregion ----------------------------
  887. //#region 右侧tab栏切换
  888. const tabItemActive = ref(1);
  889. //#endregion ----------------------------
  890. //#region 文案及其音频
  891. const title =
  892. lanChange.value == "zh-cn" ? ref("未定义标题") : ref("Undefined Title");
  893. const textareaContent = ref("");
  894. function insertIcon() {
  895. const textarea = document.getElementById("textInputId");
  896. const icon = "🕗";
  897. // 获取光标位置
  898. const startPos = textarea.selectionStart;
  899. const endPos = textarea.selectionEnd;
  900. // 在光标位置插入图标
  901. textareaContent.value =
  902. textareaContent.value.substring(0, startPos) +
  903. icon +
  904. textareaContent.value.substring(endPos, textareaContent.value.length);
  905. // 设置光标位置为插入图标后的位置
  906. const newCursorPos = startPos + icon.length;
  907. textarea.setSelectionRange(newCursorPos, newCursorPos);
  908. textarea.focus();
  909. }
  910. const language = ref(""); //选择的语言
  911. const sound = ref(""); // 选择的声音
  912. const soundStyle = ref(""); //选择的风格
  913. // 输入文字判断语种
  914. const getLanguageTimeOut = ref(false); //定时器
  915. watch(
  916. () => textareaContent.value,
  917. () => {
  918. clearTimeout(getLanguageTimeOut.value);
  919. getLanguageTimeOut.value = setTimeout(async () => {
  920. try {
  921. const data = {
  922. paperwork: textareaContent.value,
  923. };
  924. const res = await getLanguageApi(data);
  925. if (res.code == 200) {
  926. language.value = res.data.id;
  927. }
  928. } catch (error) {}
  929. }, 1500);
  930. }
  931. );
  932. watch(
  933. () => language.value,
  934. () => {
  935. chooseType();
  936. }
  937. );
  938. // 语言下拉框内容
  939. const languageOptions = ref([]);
  940. async function voiceTotal() {
  941. const res = await voiceTotalApi();
  942. res.data.sort(function(a, b){
  943. return (b.customized) - (a.customized)
  944. })
  945. languageOptions.value = res.data;
  946. }
  947. // 声音下拉框内容
  948. const soundOptions = ref([]);
  949. async function chooseType() {
  950. const res = await chooseTypeApi(language.value);
  951. soundOptions.value = res.data;
  952. sound.value = null;
  953. }
  954. // 风格下拉框内容
  955. const soundStyleOptions = ref(null);
  956. async function chooseStyle() {
  957. const item = soundOptions.value.find((element) => element.id == sound.value);
  958. soundStyle.value = null;
  959. if (item) {
  960. soundStyleOptions.value = item.style;
  961. }
  962. }
  963. // 试听
  964. const previewAudioUrl = ref(null);
  965. async function getPreviewAudio() {
  966. if (!textareaContent.value) {
  967. ElMessage.error(
  968. lanChange.value == "zh-cn"
  969. ? `您还没有输入文案`
  970. : `You have not entered the copy yet`
  971. );
  972. return;
  973. }
  974. if (!sound.value) {
  975. ElMessage.error(
  976. lanChange.value == "zh-cn"
  977. ? `您还没有选择声音`
  978. : `You have not selected the sound`
  979. );
  980. return;
  981. }
  982. try {
  983. const res = await previewAudioApi(
  984. textareaContent.value,
  985. sound.value,
  986. soundStyle.value
  987. );
  988. if (!res.data.data) {
  989. ElMessage.error(
  990. lanChange.value == "zh-cn"
  991. ? `暂时无法试听,请稍后重试`
  992. : `Unable to listen, please try again later`
  993. );
  994. }
  995. previewAudioUrl.value = res.data.data;
  996. document.getElementById("audio").play();
  997. } catch (error) {
  998. ElMessage.error(error);
  999. }
  1000. }
  1001. // 根据ageType返回年龄
  1002. function returnAge(ageType) {
  1003. if (!ageType) {
  1004. return "";
  1005. } else if (ageType == 1) {
  1006. if (lanChange.value == "zh-cn") {
  1007. return " 儿童";
  1008. } else {
  1009. return "Child";
  1010. }
  1011. } else if (ageType == 2) {
  1012. if (lanChange.value == "zh-cn") {
  1013. return " 少年";
  1014. } else {
  1015. return "Teen";
  1016. }
  1017. } else if (ageType == 3) {
  1018. if (lanChange.value == "zh-cn") {
  1019. return " 年轻";
  1020. } else {
  1021. return "YoungAdult";
  1022. }
  1023. } else if (ageType == 4) {
  1024. if (lanChange.value == "zh-cn") {
  1025. return " 中年";
  1026. } else {
  1027. return "OlderAdult";
  1028. }
  1029. } else if (ageType == 5) {
  1030. if (lanChange.value == "zh-cn") {
  1031. return " 老年";
  1032. } else {
  1033. return "Senior";
  1034. }
  1035. }
  1036. }
  1037. // 新的试听
  1038. function ListenVoices(url) {
  1039. previewAudioUrl.value = url;
  1040. let audioA = document.getElementById("audio");
  1041. audioA.addEventListener("loadedmetadata", async () => {
  1042. audioA.muted = false; // 将 muted 属性设置为 false
  1043. await audioA.play();
  1044. });
  1045. }
  1046. //#endregion ----------------------------
  1047. //#region 背景
  1048. const bjTabItemActive = ref(1); //系统或个人tab高亮
  1049. const systemBGIList = ref([]); //系统背景列表
  1050. const systemBGPageNum = ref(1); //系统背景页数
  1051. const overLoadSystemBG = ref(true); //禁止系统背景触底刷新
  1052. // tab栏切换
  1053. function changeBjTabItemActive(type) {
  1054. bjTabItemActive.value = type;
  1055. if (type == 2 && !AccessToken) {
  1056. ElMessage.error(
  1057. lanChange.value == "zh-cn"
  1058. ? `登录过期,请重新登录!`
  1059. : `Login expired, please login again`
  1060. );
  1061. router.push("login");
  1062. }
  1063. }
  1064. async function getSystemBGIList() {
  1065. const res = await systemBGIListApi(
  1066. systemBGPageNum.value,
  1067. 20,
  1068. videoType.value
  1069. );
  1070. systemBGIList.value = res.data.list;
  1071. overLoadSystemBG.value = false;
  1072. }
  1073. // 加载更多系统背景图
  1074. async function loadMoreSystemBGI() {
  1075. systemBGPageNum.value += 1;
  1076. const res = await systemBGIListApi(
  1077. systemBGPageNum.value,
  1078. 20,
  1079. videoType.value
  1080. );
  1081. systemBGIList.value.push(...res.data.list);
  1082. if (res.data.list.length == 0) {
  1083. overLoadSystemBG.value = true;
  1084. systemBGPageNum.value = modelPageNum.value - 1;
  1085. }
  1086. }
  1087. // 个人背景
  1088. const personBGIList = ref([]); //个人背景列表
  1089. const personBGPageNum = ref(1); //个人背景页数
  1090. const overLoadpersonBG = ref(true); //禁止个人背景触底刷新
  1091. async function getpersonBGIList() {
  1092. const res = await personBGIListApi(
  1093. personBGPageNum.value,
  1094. 20,
  1095. videoType.value
  1096. );
  1097. personBGIList.value = res.data.list;
  1098. overLoadpersonBG.value = false;
  1099. }
  1100. // 加载更多个人背景图
  1101. async function loadMorePersonBGI() {
  1102. personBGPageNum.value += 1;
  1103. const res = await personBGIListApi(
  1104. personBGPageNum.value,
  1105. 20,
  1106. videoType.value
  1107. );
  1108. personBGIList.value.push(...res.data.list);
  1109. if (res.data.list.length == 0) {
  1110. overLoadpersonBG.value = true;
  1111. personBGPageNum.value = modelPageNum.value - 1;
  1112. }
  1113. }
  1114. // 当横屏竖屏改变时,重新获取背景列表,且还原页数和禁止刷新状态
  1115. watch(
  1116. () => videoType,
  1117. async (newValue, oldValue) => {
  1118. systemBGPageNum.value = 1;
  1119. personBGPageNum.value = 1;
  1120. overLoadSystemBG.value = true;
  1121. overLoadpersonBG.value = true;
  1122. await getSystemBGIList();
  1123. if (AccessToken) {
  1124. await getpersonBGIList();
  1125. }
  1126. },
  1127. { deep: true }
  1128. );
  1129. // 重新获取个人仓库背景图片
  1130. async function reGetpersonBGIList() {
  1131. personBGPageNum.value = 1;
  1132. overLoadpersonBG.value = true;
  1133. await getpersonBGIList();
  1134. }
  1135. // 点击列表中的背景图
  1136. const bjImgActive = ref(null);
  1137. function selectBjImg(img) {
  1138. bjImgActive.value = img.id;
  1139. materialList.BGI.material = img.material;
  1140. materialList.BGI.id = img.id;
  1141. }
  1142. //#endregion ----------------------------
  1143. //#region 素材
  1144. const scTabItemActive = ref(1); //系统或个人tab高亮
  1145. const systemSCIList = ref([]); //系统素材列表
  1146. const systemSCPageNum = ref(1); //系统素材页数
  1147. const overLoadSystemSC = ref(true); //禁止系统素材触底刷新
  1148. // tab栏切换
  1149. function changeScTabItemActive(type) {
  1150. scTabItemActive.value = type;
  1151. if (type == 2 && !AccessToken) {
  1152. ElMessage.error(
  1153. lanChange.value == "zh-cn"
  1154. ? `登录过期,请重新登录!`
  1155. : `Login expired, please login again`
  1156. );
  1157. router.push("login");
  1158. }
  1159. }
  1160. async function getSystemSCIList() {
  1161. const res = await systemSCIListApi(
  1162. systemSCPageNum.value,
  1163. 20,
  1164. videoType.value
  1165. );
  1166. systemSCIList.value = res.data.list;
  1167. overLoadSystemSC.value = false;
  1168. }
  1169. // 加载更多系统素材图
  1170. async function loadMoreSystemSCI() {
  1171. systemSCPageNum.value += 1;
  1172. const res = await systemSCIListApi(
  1173. systemSCPageNum.value,
  1174. 20,
  1175. videoType.value
  1176. );
  1177. systemSCIList.value.push(...res.data.list);
  1178. if (res.data.list.length == 0) {
  1179. overLoadSystemSC.value = true;
  1180. systemSCPageNum.value = modelPageNum.value - 1;
  1181. }
  1182. }
  1183. // 个人素材
  1184. const personSCIList = ref([]); //个人素材列表
  1185. const personSCPageNum = ref(1); //个人素材页数
  1186. const overLoadpersonSC = ref(true); //禁止个人素材触底刷新
  1187. async function getpersonSCIList() {
  1188. const res = await personSCIListApi(
  1189. personSCPageNum.value,
  1190. 20,
  1191. videoType.value
  1192. );
  1193. personSCIList.value = res.data.list;
  1194. overLoadpersonSC.value = false;
  1195. }
  1196. // 加载更多个人素材图
  1197. async function loadMorePersonSCI() {
  1198. personSCPageNum.value += 1;
  1199. const res = await personSCIListApi(
  1200. personSCPageNum.value,
  1201. 20,
  1202. videoType.value
  1203. );
  1204. personSCIList.value.push(...res.data.list);
  1205. if (res.data.list.length == 0) {
  1206. overLoadpersonSC.value = true;
  1207. personSCPageNum.value = modelPageNum.value - 1;
  1208. }
  1209. }
  1210. // 重新获取个人仓库背景图片
  1211. async function reGetpersonSCIList() {
  1212. personSCPageNum.value = 1;
  1213. overLoadpersonSC.value = true;
  1214. await getpersonSCIList();
  1215. }
  1216. // 点击列表中的素材图
  1217. const scImgActive = ref(null);
  1218. function selectScImg(img) {
  1219. scImgActive.value = img.id;
  1220. materialList.scList.push({ ...img, x: 0, y: 0, z: 0, w: 1, h: 1 });
  1221. }
  1222. // 删除上传的背景或素材
  1223. function delImg(id) {
  1224. const msg =
  1225. lanChange.value == "zh-cn"
  1226. ? "确定删除图片吗?"
  1227. : "Are you sure to delete the picture?";
  1228. const notice = lanChange.value == "zh-cn" ? "提示" : "Notice";
  1229. const confirm = lanChange.value == "zh-cn" ? "确定" : "Confirm";
  1230. const cancel = lanChange.value == "zh-cn" ? "取消" : "Cancel";
  1231. ElMessageBox.confirm(msg, notice, {
  1232. confirmButtonText: confirm,
  1233. cancelButtonText: cancel,
  1234. }).then(async () => {
  1235. try {
  1236. const res = await delImgApi(id);
  1237. reGetpersonSCIList();
  1238. reGetpersonBGIList();
  1239. ElMessage.success(
  1240. lanChange.value == "zh-cn" ? "删除成功!" : "Deleted successfully!"
  1241. );
  1242. } catch (error) {}
  1243. });
  1244. }
  1245. //#endregion ----------------------------
  1246. //#region 字幕
  1247. const SubtitlesColorShow = ref(false); // 字色调色板开关
  1248. const Subtitles = reactive({
  1249. SubtitlesSwitch: false, //开关
  1250. SubtitlesFontFamily: "仿宋", //字体系列
  1251. SubtitlesTop: 400, //Y轴
  1252. SubtitlesSize: 30, //字号大小
  1253. SubtitlesWeight: 500, //字粗
  1254. SubtitlesColor: "#FFFFFF", //字色
  1255. SubtitlesWidth: 500, //宽度
  1256. colorList: [
  1257. "#FFFFFF",
  1258. "#FF8080",
  1259. "#000000",
  1260. "#FF80FF",
  1261. "#FF0000",
  1262. "#00FF80",
  1263. "#FF00FF",
  1264. "#0080FF",
  1265. "#00FF00",
  1266. "#0000FF",
  1267. "#000080",
  1268. "#FFFF00",
  1269. "#FF0080",
  1270. "#FF8000",
  1271. "#80FF00",
  1272. "#00FFFF",
  1273. ],
  1274. fontFamilyList: [
  1275. { label: "仿宋", value: "仿宋" },
  1276. { label: "微软雅黑", value: "微软雅黑" },
  1277. { label: "新宋体", value: "新宋体" },
  1278. { label: "黑体", value: "黑体" },
  1279. { label: "楷体", value: "楷体" },
  1280. { label: "等线", value: "等线" },
  1281. { label: "Corbel", value: "Corbel" },
  1282. { label: "Terminal", value: "Terminal" },
  1283. { label: "Times New Roman", value: "Times New Roman" },
  1284. { label: "Verdana", value: "Verdana" },
  1285. ],
  1286. fontSizeList: [12, 16, 20, 30, 44, 60, 80, 100],
  1287. fontWeightList: [100, 500, 900],
  1288. });
  1289. const handleSubtitlesColorShow = (e) => {
  1290. e.stopPropagation(); // 阻止事件冒泡
  1291. SubtitlesColorShow.value = true;
  1292. // SubtitlesBgcShow.value = false;
  1293. };
  1294. const handleClickOutside = () => {
  1295. if (SubtitlesColorShow.value == true) {
  1296. }
  1297. SubtitlesColorShow.value = false;
  1298. };
  1299. onMounted(() => {
  1300. document.addEventListener("click", handleClickOutside);
  1301. });
  1302. function rgbaToBgra(hexColor) {
  1303. // 去除颜色前缀
  1304. hexColor = hexColor.replace("#", "");
  1305. // 分割颜色分量
  1306. const red = hexColor.substring(0, 2);
  1307. const green = hexColor.substring(2, 4);
  1308. const blue = hexColor.substring(4, 6);
  1309. // 组合为新的颜色字符串
  1310. const convertedColor = `&H${blue}${green}${red}`;
  1311. return convertedColor;
  1312. }
  1313. function BgraToRgba(hexColor) {
  1314. // 去除颜色前缀
  1315. hexColor = hexColor.replace("&H", "");
  1316. // 分割颜色分量
  1317. const blue = hexColor.substring(0, 2);
  1318. const green = hexColor.substring(2, 4);
  1319. const red = hexColor.substring(4, 6);
  1320. // 组合为新的颜色字符串
  1321. const convertedColor = `#${red}${green}${blue}`;
  1322. return convertedColor;
  1323. }
  1324. //#endregion ----------------------------
  1325. //#region 编辑区域
  1326. const proportion = ref(1); //比例
  1327. const elementWidth = ref(null);
  1328. onMounted(() => {
  1329. elementWidth.value = document.getElementById("editPosition").clientWidth;
  1330. });
  1331. const EditPositionDom = ref(null);
  1332. watch(
  1333. () => videoType.value,
  1334. async (newValue, oldValue) => {
  1335. // 根据横竖屏来确定比例 1竖2横
  1336. await nextTick();
  1337. elementWidth.value = document.getElementById("editPosition").clientWidth;
  1338. if (videoType.value == 1) {
  1339. proportion.value = Number(1080 / elementWidth.value).toFixed(2);
  1340. } else if (videoType.value == 2) {
  1341. proportion.value = Number(1920 / elementWidth.value).toFixed(2);
  1342. }
  1343. console.log("设置比例");
  1344. Subtitles.SubtitlesWidth = elementWidth.value * 0.7 * proportion.value;
  1345. console.log(proportion.value, "比例");
  1346. },
  1347. { deep: true }
  1348. );
  1349. const materialList = reactive({
  1350. BGI: {},
  1351. scList: [],
  1352. virtual: {},
  1353. });
  1354. // 根据模版id获取信息
  1355. async function getPersonPatch() {
  1356. try {
  1357. const res = await personPatchApi(modelId.value);
  1358. materialList.BGI.material = res.data.backgroundMaterial;
  1359. materialList.BGI.id = res.data.backgroundId + "";
  1360. materialList.virtual.material = res.data.material;
  1361. materialList.virtual.id = res.data.id;
  1362. // if (!materialList.virtual.w) {
  1363. materialList.virtual = {
  1364. ...materialList.virtual,
  1365. x: 50,
  1366. y: 10,
  1367. z: 0,
  1368. w: 0.55,
  1369. h: 1,
  1370. };
  1371. // }
  1372. } catch (error) {}
  1373. }
  1374. watch(
  1375. () => modelId.value,
  1376. async (newValue, oldValue) => {
  1377. await getPersonPatch();
  1378. },
  1379. { deep: true }
  1380. );
  1381. //#endregion ----------------------------
  1382. //#region 编辑区域控制台
  1383. const showImgInfo = ref({
  1384. id: null,
  1385. data: null,
  1386. }); //显示素材or数字人的id
  1387. // const showImgInfoData = ref(null);
  1388. watch(
  1389. () => showImgInfo,
  1390. (newValue, oldValue) => {
  1391. if (materialList.virtual.id == showImgInfo.value.id) {
  1392. showImgInfo.value.data = materialList.virtual;
  1393. } else {
  1394. showImgInfo.value.data = materialList.scList.find(
  1395. (item) => item.id == showImgInfo.value.id
  1396. );
  1397. }
  1398. },
  1399. { deep: true }
  1400. );
  1401. //#endregion ----------------------------
  1402. //#region 保存视频和生成草稿
  1403. const workId = ref(null); //保存作品完成后会有一个id,用于生成视频
  1404. const saveLoading = ref(false);
  1405. const createLoading = ref(false);
  1406. async function saveOrUpdate(isCreate) {
  1407. // 单纯的保存视频
  1408. if (isCreate == "isSave") {
  1409. if (saveLoading.value == true) {
  1410. ElMessage.error(lanChange.value == "zh-cn" ? `正在保存` : `Be saving`);
  1411. return;
  1412. }
  1413. if (createLoading.value == true) {
  1414. ElMessage.error(
  1415. lanChange.value == "zh-cn" ? `正在生成` : `Be generating`
  1416. );
  1417. return;
  1418. }
  1419. try {
  1420. saveLoading.value = true;
  1421. // 处理数字人
  1422. const shuziren = {
  1423. id: materialList.virtual.id,
  1424. type: 1,
  1425. material: materialList.virtual.material,
  1426. x: materialList.virtual.x,
  1427. y: materialList.virtual.y,
  1428. z: materialList.virtual.z,
  1429. w: materialList.virtual.w,
  1430. h: materialList.virtual.h,
  1431. };
  1432. // 处理素材
  1433. const scList = materialList.scList.map((item) => {
  1434. return {
  1435. id: item.id,
  1436. type: 5,
  1437. material: item.material,
  1438. x: item.x,
  1439. y: item.y,
  1440. z: item.z,
  1441. w: item.w,
  1442. h: item.h,
  1443. };
  1444. });
  1445. // 处理上传数据
  1446. const data = {
  1447. id: workId.value,
  1448. videoType: videoType.value, //横屏竖屏
  1449. personMouldId: materialList.virtual.id, //数字人id
  1450. speakTypeStr: soundStyle.value, //声音风格
  1451. voiceMouldId: sound.value, //声音id
  1452. // voiceMouldIds: [soundStyle.value ? soundStyle.value : sound.value], //声音id
  1453. title: title.value, //标题
  1454. paperwork: textareaContent.value, //语音内容
  1455. backgroundId: materialList.BGI.id + "", //背景id
  1456. personJson: JSON.stringify(shuziren), // 数字人
  1457. materialAllJson: JSON.stringify([...scList]), // 素材
  1458. subtitleEnabled: Subtitles.SubtitlesSwitch ? 1 : 0, //字幕开关
  1459. subtitleParams: JSON.stringify({
  1460. Fontname: Subtitles.SubtitlesFontFamily, //字体名字
  1461. Fontsize: Subtitles.SubtitlesSize * proportion.value, //字体大小
  1462. Marginv: Subtitles.SubtitlesTop, //上边距
  1463. PrimaryColour: rgbaToBgra(Subtitles.SubtitlesColor), //字色
  1464. Alignment: 6,
  1465. }),
  1466. };
  1467. const res = await saveOrUpdateApi(data);
  1468. workId.value = res.data.id;
  1469. saveLoading.value = false;
  1470. ElMessage.success(
  1471. lanChange.value == "zh-cn"
  1472. ? `保存草稿成功!`
  1473. : `Save draft successfully!`
  1474. );
  1475. router.push("/myCreating");
  1476. } catch (error) {
  1477. ElMessage.error(error);
  1478. saveLoading.value = false;
  1479. }
  1480. }
  1481. // 生成视频时触发的保存
  1482. else if (isCreate == "isCreate") {
  1483. try {
  1484. if (!textareaContent.value) {
  1485. ElMessage.error(
  1486. lanChange.value == "zh-cn"
  1487. ? `您还没有输入文案`
  1488. : `You have not entered the copy yet`
  1489. );
  1490. saveLoading.value = false;
  1491. return;
  1492. }
  1493. if (!sound.value && !soundStyle.value) {
  1494. ElMessage.error(
  1495. lanChange.value == "zh-cn"
  1496. ? `您还没有选择声音`
  1497. : `You have not selected a sound`
  1498. );
  1499. saveLoading.value = false;
  1500. return;
  1501. }
  1502. // 处理数字人
  1503. const shuziren = {
  1504. id: materialList.virtual.id,
  1505. type: 1,
  1506. material: materialList.virtual.material,
  1507. x: materialList.virtual.x,
  1508. y: materialList.virtual.y,
  1509. z: materialList.virtual.z,
  1510. w: materialList.virtual.w,
  1511. h: materialList.virtual.h,
  1512. };
  1513. // 处理素材
  1514. const scList = materialList.scList.map((item) => {
  1515. return {
  1516. id: item.id,
  1517. type: 5,
  1518. material: item.material,
  1519. x: item.x,
  1520. y: item.y,
  1521. z: item.z,
  1522. w: item.w,
  1523. h: item.h,
  1524. };
  1525. });
  1526. // 处理上传数据
  1527. const data = {
  1528. id: workId.value,
  1529. videoType: videoType.value, //横屏竖屏
  1530. personMouldId: materialList.virtual.id, //数字人id
  1531. speakTypeStr: soundStyle.value, //声音风格
  1532. voiceMouldId: sound.value, //声音id
  1533. title: title.value, //标题
  1534. paperwork: textareaContent.value, //语音内容
  1535. backgroundId: materialList.BGI.id + "", //背景id
  1536. personJson: JSON.stringify(shuziren), // 数字人
  1537. materialAllJson: JSON.stringify([...scList]), // 素材
  1538. subtitleEnabled: Subtitles.SubtitlesSwitch ? 1 : 0, //字幕开关
  1539. subtitleParams: JSON.stringify({
  1540. Fontname: Subtitles.SubtitlesFontFamily, //字体名字
  1541. Fontsize: Subtitles.SubtitlesSize * proportion.value, //字体大小
  1542. Marginv: Subtitles.SubtitlesTop, //上边距
  1543. PrimaryColour: rgbaToBgra(Subtitles.SubtitlesColor), //字色
  1544. Alignment: 6,
  1545. }),
  1546. };
  1547. const res = await saveOrUpdateApi(data);
  1548. workId.value = res.data.id;
  1549. saveLoading.value = false;
  1550. } catch (error) {
  1551. ElMessage.error(error);
  1552. saveLoading.value = false;
  1553. }
  1554. }
  1555. }
  1556. async function createVideo() {
  1557. if (saveLoading.value == true) {
  1558. ElMessage.error(lanChange.value == "zh-cn" ? `正在保存` : `Be saving`);
  1559. return;
  1560. }
  1561. if (createLoading.value == true) {
  1562. ElMessage.error(lanChange.value == "zh-cn" ? `正在生成` : `Be generating`);
  1563. return;
  1564. }
  1565. try {
  1566. createLoading.value = true;
  1567. if (!textareaContent.value) {
  1568. ElMessage.error(
  1569. lanChange.value == "zh-cn"
  1570. ? `您还没有输入文案`
  1571. : `You have not entered the copy yet`
  1572. );
  1573. createLoading.value = false;
  1574. return;
  1575. }
  1576. if (!sound.value && !soundStyle.value) {
  1577. ElMessage.error(
  1578. lanChange.value == "zh-cn"
  1579. ? `您还没有选择声音`
  1580. : `You have not selected a sound`
  1581. );
  1582. createLoading.value = false;
  1583. return;
  1584. }
  1585. // 获取预估时长
  1586. const res4 = await previewAudioApi(
  1587. textareaContent.value,
  1588. sound.value,
  1589. soundStyle.value
  1590. );
  1591. if (res4.code != 200) {
  1592. ElMessage.error(
  1593. lanChange.value == "zh-cn"
  1594. ? `视频时长预估失败,无法生成视频,您可以换个声音试试`
  1595. : `Video duration estimation failed, can not generate video, you can try to change the sound`
  1596. );
  1597. createLoading.value = false;
  1598. saveLoading.value = false;
  1599. return;
  1600. }
  1601. if (res4.data.time > 300) {
  1602. ElMessage.error(
  1603. lanChange.value == "zh-cn"
  1604. ? `预计生成的视频时长约为${formatTime(
  1605. res4.data.time
  1606. )},免费用户生成视频时长为300秒,无法生成视频`
  1607. : `The video generated is expected to be about ${formatTime(
  1608. res4.data.time
  1609. )} long, and the free user-generated video is 300 seconds long and cannot be generated`
  1610. );
  1611. createLoading.value = false;
  1612. saveLoading.value = false;
  1613. return;
  1614. }
  1615. ElMessage.success(
  1616. lanChange.value == "zh-cn"
  1617. ? `预计生成的视频时长约为${formatTime(res4.data.time)}`
  1618. : `The video generated is expected to be approximately${formatTime(
  1619. res4.data.time
  1620. )} in length`
  1621. );
  1622. await saveOrUpdate("isCreate");
  1623. let checkVideo = await checkVideoApi(workId.value)
  1624. if (checkVideo.code !== 200) {
  1625. ElMessage.success(checkVideo.message);
  1626. return
  1627. }
  1628. const res2 = await createVideoApi(workId.value);
  1629. // 去充值
  1630. if (res2.code === 64001) {
  1631. // router.push("/myStore");
  1632. router.push({
  1633. path: "/myStore",
  1634. query: {
  1635. tag: 'createVideo'
  1636. },
  1637. });
  1638. return
  1639. }
  1640. ElMessage.success(
  1641. lanChange.value == "zh-cn"
  1642. ? `视频生成中,请稍后!`
  1643. : `Video generation, please wait!`
  1644. );
  1645. createLoading.value = false;
  1646. router.push("/myCreating");
  1647. } catch (error) {
  1648. ElMessage.error(error);
  1649. createLoading.value = false;
  1650. }
  1651. }
  1652. // 预估时长时把秒转为分
  1653. function formatTime(seconds) {
  1654. const minutes = Math.floor(Math.floor(seconds) / 60);
  1655. const remainingSeconds = Math.floor(seconds) % 60;
  1656. const formattedMinutes = String(minutes).padStart(2, "0");
  1657. const formattedSeconds = String(remainingSeconds).padStart(2, "0");
  1658. return `${formattedMinutes}:${formattedSeconds}`;
  1659. }
  1660. //#endregion ----------------------------
  1661. //#region 草稿回显
  1662. const subtitleParamsToObj = ref(null);
  1663. async function getModelDetailById() {
  1664. const res = await getModelDetailByIdApi(route.query.id);
  1665. videoType.value = res.data.videoType;
  1666. language.value = res.data.voiceInfo ? res.data.voiceInfo.languageId : null;
  1667. if (language.value) {
  1668. await chooseType(language.value);
  1669. }
  1670. workId.value = res.data.id;
  1671. title.value = res.data.title; //标题
  1672. textareaContent.value = res.data.paperwork; //语音内容
  1673. materialList.BGI.id = res.data.backgroundId + ""; //背景id
  1674. // sound.value = res.data.voiceMouldId;
  1675. // soundStyle.value = JSON.parse(res.data.voiceMouldSm).speakTypeStr;
  1676. sound.value = res.data.voiceMouldId; //声音id
  1677. chooseStyle();
  1678. soundStyle.value = res.data.speakTypeStr; //选择风格
  1679. materialList.BGI.material = JSON.parse(res.data.backgroundSm).material;
  1680. materialList.virtual = JSON.parse(res.data.personJson);
  1681. materialList.virtual.id = res.data.personMouldId; //数字人id
  1682. materialList.scList = res.data.materialAllJson
  1683. ? JSON.parse(res.data.materialAllJson)
  1684. : [];
  1685. showChooseModel.value = false;
  1686. // 字幕回显 需要延时,不然proportion缩放比例不准确
  1687. setTimeout(() => {
  1688. Subtitles.SubtitlesSwitch = res.data.subtitleEnabled == 1 ? true : false;
  1689. subtitleParamsToObj.value = JSON.parse(res.data.subtitleParams);
  1690. Subtitles.SubtitlesFontFamily = subtitleParamsToObj.value.Fontname;
  1691. Subtitles.SubtitlesSize =
  1692. subtitleParamsToObj.value.Fontsize / proportion.value;
  1693. Subtitles.SubtitlesTop = subtitleParamsToObj.value.Marginv;
  1694. Subtitles.SubtitlesColor = BgraToRgba(
  1695. subtitleParamsToObj.value.PrimaryColour
  1696. );
  1697. }, 100);
  1698. }
  1699. //#endregion ----------------------------
  1700. //#region 公共方法
  1701. function NumToFixed(num1, num2) {
  1702. return Number(num1).toFixed(num2);
  1703. }
  1704. //#endregion ----------------------------
  1705. //#region AI文案
  1706. // 创建websocket
  1707. let mySocket = null; //websocket对象
  1708. const sendLoading = ref(false); //发送状态
  1709. function createWS() {
  1710. mySocket = new WebSocket("wss://gptsocket.malls.iformall.com:8010");
  1711. mySocket.onopen = () => {
  1712. console.log("WebSocket 连接成功1");
  1713. sendRequirement();
  1714. };
  1715. mySocket.onmessage = (event) => {
  1716. // console.log("接收到 WebSocket 消息2:", event.data);
  1717. if (!event.data) {
  1718. return;
  1719. }
  1720. if (event.data == "___talk_end___") {
  1721. sendLoading.value = false;
  1722. mySocket.close();
  1723. return;
  1724. }
  1725. answerContent.value = answerContent.value + event.data;
  1726. };
  1727. mySocket.onclose = () => {
  1728. console.log(
  1729. "WebSocket 连接关闭3,关闭代码:",
  1730. event.code,
  1731. "关闭原因:",
  1732. event.reason
  1733. );
  1734. console.log(mySocket.readyState, "链接状态3");
  1735. };
  1736. mySocket.onerror = (error) => {
  1737. ElMessage.error(
  1738. lanChange.value == "zh-cn"
  1739. ? `GPT服务器连接失败,请稍后重试!`
  1740. : `GPT Server connection failed, please try again later`
  1741. );
  1742. sendLoading.value = false;
  1743. mySocket.close();
  1744. console.error("WebSocket 错误4:", error);
  1745. };
  1746. }
  1747. const problemContent = ref(""); //问题输入框内容
  1748. const answerContent = ref(""); //回答输入框内容
  1749. // 点击发送需求
  1750. async function sendRequirement() {
  1751. // loading
  1752. sendLoading.value = true;
  1753. // 无需求时
  1754. if (!problemContent.value) {
  1755. sendLoading.value = false;
  1756. mySocket.close();
  1757. return;
  1758. }
  1759. const sendMessage = [
  1760. {
  1761. role: "user",
  1762. content: problemContent.value,
  1763. },
  1764. ];
  1765. // 如果处于链接状态
  1766. if (mySocket.readyState != WebSocket.OPEN) {
  1767. createWS();
  1768. } else {
  1769. answerContent.value = "";
  1770. mySocket.send(JSON.stringify(sendMessage));
  1771. }
  1772. }
  1773. // 复制到文案
  1774. function copyAnswerContent() {
  1775. textareaContent.value = answerContent.value;
  1776. document.getElementById("tab-0").click();
  1777. }
  1778. //#endregion ----------------------------
  1779. //#region
  1780. //#endregion ----------------------------
  1781. // $t在script中的使用
  1782. const {
  1783. appContext: {
  1784. config: { globalProperties },
  1785. },
  1786. } = getCurrentInstance();
  1787. const string = "createVideo.typeScript";
  1788. const t = globalProperties.$t(string);
  1789. onMounted(async () => {
  1790. voiceTotal(); //获取声音列表
  1791. // getSystemSCIList();
  1792. // getpersonBGIList();
  1793. await getModeList(tabSex.value, 1, 20, tabVideoType.value); //获取模版列表
  1794. if (AccessToken) {
  1795. getpersonSCIList();
  1796. }
  1797. getSystemSCIList();
  1798. if (route.query.id) {
  1799. getModelDetailById();
  1800. }
  1801. createWS();
  1802. });
  1803. </script>
  1804. <style lang="scss" scoped>
  1805. .page {
  1806. height: calc(100vh - 80px);
  1807. // height: calc(100vh - 80px);
  1808. background-color: #fff;
  1809. border-radius: 30px 0 0 0;
  1810. overflow: hidden;
  1811. font-size: 18px;
  1812. }
  1813. .mask {
  1814. position: absolute;
  1815. width: 100%;
  1816. height: 100%;
  1817. z-index: 4;
  1818. background-color: rgba(0, 0, 0, 0.5);
  1819. }
  1820. .left {
  1821. position: relative;
  1822. width: 68%;
  1823. height: 100%;
  1824. padding: 20px;
  1825. // 字幕盒子
  1826. .subtitlesBoxTop {
  1827. z-index: 3;
  1828. position: absolute;
  1829. top: 50px;
  1830. left: 225px;
  1831. width: 700px;
  1832. // height: 50px;
  1833. background-color: #e9ecf1;
  1834. border-radius: 20px;
  1835. padding: 10px;
  1836. box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 15px;
  1837. display: flex;
  1838. justify-content: flex-start;
  1839. align-content: flex-start;
  1840. flex-wrap: wrap;
  1841. .tab4Item {
  1842. display: flex;
  1843. justify-content: space-between;
  1844. width: 130px;
  1845. background-color: rgba(71, 105, 218, 0.1);
  1846. text-align: center;
  1847. padding: 10px;
  1848. height: 80px;
  1849. border-radius: 10px;
  1850. margin: auto;
  1851. // margin-bottom: 10px;
  1852. p {
  1853. padding-bottom: 5px;
  1854. }
  1855. }
  1856. .SubtitlesSwitch {
  1857. width: 100%;
  1858. .el-input-number--large {
  1859. width: 100px;
  1860. }
  1861. .el-input-number__increase {
  1862. width: none;
  1863. }
  1864. }
  1865. .myColorBox {
  1866. position: relative;
  1867. width: 25px;
  1868. height: 25px;
  1869. margin: auto;
  1870. border: #000 solid 2px;
  1871. cursor: pointer;
  1872. .selectColor {
  1873. position: absolute;
  1874. top: 150%;
  1875. left: 50%;
  1876. transform: translateX(-50%);
  1877. display: flex;
  1878. justify-content: flex-start;
  1879. flex-wrap: wrap;
  1880. width: 142px;
  1881. background-color: #ffffff;
  1882. z-index: 99;
  1883. box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
  1884. &::before {
  1885. content: "";
  1886. display: block;
  1887. position: absolute;
  1888. top: -10%;
  1889. left: 50%;
  1890. transform: translateX(-50%);
  1891. width: 0;
  1892. height: 0;
  1893. border-left: 15px solid transparent;
  1894. border-right: 15px solid transparent;
  1895. border-bottom: 15px solid white;
  1896. }
  1897. &-item {
  1898. width: 25px;
  1899. height: 25px;
  1900. margin: 5px 5px;
  1901. border: #000 solid 2px;
  1902. border-radius: 5px;
  1903. cursor: pointer;
  1904. }
  1905. }
  1906. }
  1907. }
  1908. .subtitlesBoxLeft {
  1909. z-index: 3;
  1910. position: absolute;
  1911. top: 150px;
  1912. left: 150px;
  1913. width: 150px;
  1914. // height: 50px;
  1915. background-color: #e9ecf1;
  1916. border-radius: 20px;
  1917. padding: 10px;
  1918. box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 15px;
  1919. display: flex;
  1920. justify-content: flex-start;
  1921. align-content: flex-start;
  1922. flex-wrap: wrap;
  1923. .tab4Item {
  1924. display: flex;
  1925. justify-content: space-between;
  1926. width: 130px;
  1927. background-color: rgba(71, 105, 218, 0.1);
  1928. text-align: center;
  1929. padding: 10px;
  1930. height: 80px;
  1931. border-radius: 10px;
  1932. margin: auto;
  1933. margin-bottom: 10px;
  1934. p {
  1935. padding-bottom: 5px;
  1936. }
  1937. }
  1938. .SubtitlesSwitch {
  1939. width: 100%;
  1940. .el-input-number--large {
  1941. width: 100px;
  1942. }
  1943. .el-input-number__increase {
  1944. width: none;
  1945. }
  1946. }
  1947. .myColorBox {
  1948. position: relative;
  1949. width: 25px;
  1950. height: 25px;
  1951. margin: auto;
  1952. border: #000 solid 2px;
  1953. cursor: pointer;
  1954. .selectColor {
  1955. position: absolute;
  1956. top: 150%;
  1957. left: 50%;
  1958. transform: translateX(-50%);
  1959. display: flex;
  1960. justify-content: flex-start;
  1961. flex-wrap: wrap;
  1962. width: 142px;
  1963. background-color: #ffffff;
  1964. z-index: 99;
  1965. box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
  1966. &::before {
  1967. content: "";
  1968. display: block;
  1969. position: absolute;
  1970. top: -10%;
  1971. left: 50%;
  1972. transform: translateX(-50%);
  1973. width: 0;
  1974. height: 0;
  1975. border-left: 15px solid transparent;
  1976. border-right: 15px solid transparent;
  1977. border-bottom: 15px solid white;
  1978. }
  1979. &-item {
  1980. width: 25px;
  1981. height: 25px;
  1982. margin: 5px 5px;
  1983. border: #000 solid 2px;
  1984. border-radius: 5px;
  1985. cursor: pointer;
  1986. }
  1987. }
  1988. }
  1989. }
  1990. // 选择模版弹出框
  1991. .chooseModelBox {
  1992. position: absolute;
  1993. top: 0px;
  1994. left: 0px;
  1995. width: 50%;
  1996. height: 100%;
  1997. padding: 30px 30px 10px 30px;
  1998. transition: all 0.3s;
  1999. // background-color: #f1f2f6;
  2000. background-color: #454545;
  2001. z-index: 9;
  2002. // 伸缩按钮
  2003. .shrinkBtn {
  2004. position: absolute;
  2005. top: 350px;
  2006. right: -40px;
  2007. width: 40px;
  2008. height: 100px;
  2009. line-height: 80px;
  2010. padding: 10px;
  2011. font-size: 30px;
  2012. background-color: #454545;
  2013. color: #fff;
  2014. &::before {
  2015. position: absolute;
  2016. top: -40px;
  2017. left: -0px;
  2018. content: "";
  2019. width: 0;
  2020. height: 0px;
  2021. border-top: 20px solid transparent;
  2022. border-right: 20px solid transparent;
  2023. border-bottom: 20px solid #454545;
  2024. border-left: 20px solid #454545;
  2025. }
  2026. &::after {
  2027. position: absolute;
  2028. bottom: -40px;
  2029. left: -0px;
  2030. content: "";
  2031. width: 0;
  2032. height: 0px;
  2033. border-top: 20px solid #454545;
  2034. border-right: 20px solid transparent;
  2035. border-bottom: 20px solid transparent;
  2036. border-left: 20px solid #454545;
  2037. }
  2038. }
  2039. // tab栏
  2040. p {
  2041. color: #fff;
  2042. font-size: 16px;
  2043. padding: 0 20px;
  2044. }
  2045. .tab {
  2046. margin: auto;
  2047. margin-top: 10px;
  2048. display: flex;
  2049. justify-content: space-between;
  2050. width: 100%;
  2051. height: 40px;
  2052. // background-color: #fff;
  2053. margin-bottom: 10px;
  2054. border-radius: 25px;
  2055. color: #b9b9b9;
  2056. .tabItem {
  2057. // flex: 1;
  2058. width: 30%;
  2059. font-size: 14px;
  2060. text-align: center;
  2061. line-height: 40px;
  2062. border-radius: 15px;
  2063. transition: all 0.3s;
  2064. cursor: pointer;
  2065. // margin: 0 10px;
  2066. // padding: 0 5px;
  2067. background-color: #000;
  2068. user-select: none;
  2069. position: relative;
  2070. }
  2071. .tabItemActive {
  2072. background-color: #fff;
  2073. color: #000;
  2074. }
  2075. }
  2076. // 模版列表
  2077. .modeListBox {
  2078. display: flex;
  2079. flex-wrap: wrap;
  2080. justify-content: flex-start;
  2081. width: 100%;
  2082. max-height: calc(100% - 180px);
  2083. padding: 20px;
  2084. border-radius: 20px;
  2085. // background-color: #fff;
  2086. overflow: hidden;
  2087. overflow-y: scroll;
  2088. .modeListBoxItme {
  2089. position: relative;
  2090. padding: 20px;
  2091. width: 42%;
  2092. height: 190px;
  2093. margin: 0 20px 20px 0;
  2094. border-radius: 15px;
  2095. // padding: 20px;
  2096. // background: rgba(0, 0, 0, 0.1);
  2097. background-color: #fff;
  2098. z-index: 2;
  2099. cursor: pointer;
  2100. img {
  2101. max-width: 100%;
  2102. max-height: 100%;
  2103. // max-width: 200px;
  2104. // max-height: 200px;
  2105. }
  2106. .subscript{
  2107. border-radius: 4px 4px 4px 0px;
  2108. color: #fff;
  2109. padding: 2px 6px;
  2110. font-size: 10px;
  2111. line-height: 16px;
  2112. background: #EA3447;
  2113. position: absolute;
  2114. left: 0;
  2115. top: 0;
  2116. }
  2117. }
  2118. .modeListBoxItmeActive {
  2119. border: 3px solid #7264f5;
  2120. }
  2121. }
  2122. }
  2123. .noModel {
  2124. width: 0;
  2125. padding: 0;
  2126. // opacity: 0;
  2127. }
  2128. // 展示区
  2129. .showModelImgBox {
  2130. position: relative;
  2131. width: 100%;
  2132. height: 100%;
  2133. padding: 50px;
  2134. z-index: 1;
  2135. img {
  2136. position: absolute;
  2137. top: 50%;
  2138. left: 50%;
  2139. transform: translate(-50%, -50%);
  2140. max-width: 800px;
  2141. max-height: 800px;
  2142. }
  2143. .EditPositionBox1 {
  2144. position: absolute;
  2145. top: 50%;
  2146. left: 50%;
  2147. transform: translate(-50%, -50%);
  2148. height: 768px;
  2149. width: 432px;
  2150. border-radius: 20px;
  2151. overflow: hidden;
  2152. }
  2153. .EditPositionBox2 {
  2154. position: absolute;
  2155. top: 50%;
  2156. left: 50%;
  2157. transform: translate(-50%, -50%);
  2158. height: 432px;
  2159. width: 768px;
  2160. border-radius: 20px;
  2161. overflow: hidden;
  2162. }
  2163. .showImgInfoBox {
  2164. position: absolute;
  2165. top: 200px;
  2166. right: 0px;
  2167. width: 120px;
  2168. background-color: #e9ecf1;
  2169. border-radius: 20px;
  2170. padding: 10px;
  2171. box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 15px;
  2172. .ItemName {
  2173. width: 100%;
  2174. height: 40px;
  2175. text-align: center;
  2176. line-height: 40px;
  2177. background-color: #fff;
  2178. border-radius: 10px;
  2179. }
  2180. .ItemValue {
  2181. margin-bottom: 10px;
  2182. width: 100%;
  2183. height: 40px;
  2184. text-align: center;
  2185. line-height: 40px;
  2186. background-color: #f5f7fa;
  2187. border-radius: 10px;
  2188. .el-input-number--large {
  2189. width: 100px;
  2190. }
  2191. .el-input-number__increase {
  2192. width: none;
  2193. }
  2194. }
  2195. }
  2196. .btnBox {
  2197. position: absolute;
  2198. display: flex;
  2199. bottom: 10px;
  2200. right: 10px;
  2201. width: 250px;
  2202. .customBtn {
  2203. display: flex;
  2204. justify-content: space-between;
  2205. align-items: center;
  2206. width: 45%;
  2207. height: 50px;
  2208. user-select: none;
  2209. > div {
  2210. width: 100%;
  2211. text-align: center;
  2212. cursor: pointer;
  2213. }
  2214. }
  2215. }
  2216. }
  2217. }
  2218. .right {
  2219. width: 32%;
  2220. height: 100%;
  2221. padding: 30px 30px 20px 30px;
  2222. background-color: #f1f2f6;
  2223. border-radius: 30px 0 0 0;
  2224. position: relative;
  2225. .tab {
  2226. display: flex;
  2227. width: 100%;
  2228. height: 50px;
  2229. background-color: #fff;
  2230. margin-bottom: 10px;
  2231. border-radius: 25px;
  2232. .tabItem {
  2233. flex: 1;
  2234. text-align: center;
  2235. line-height: 50px;
  2236. border-radius: 25px;
  2237. transition: all 0.3s;
  2238. cursor: pointer;
  2239. user-select: none;
  2240. position: relative;
  2241. .gpt{
  2242. position: absolute;
  2243. right: -10px;
  2244. top: -10px;
  2245. transform: rotate(45deg);
  2246. width: 30px;
  2247. }
  2248. }
  2249. .tabItemActive {
  2250. background-color: #000;
  2251. color: #fff;
  2252. }
  2253. }
  2254. .tabContent {
  2255. padding: 20px;
  2256. width: 100%;
  2257. height: calc(100% - 60px);
  2258. background-color: #fff;
  2259. border-radius: 30px;
  2260. .tab {
  2261. background-color: #f1f2f6;
  2262. }
  2263. }
  2264. .tab1 {
  2265. .title {
  2266. :deep(.el-textarea) {
  2267. padding: 10px;
  2268. overflow: hidden;
  2269. }
  2270. :deep(.el-textarea__inner) {
  2271. border-radius: 15px;
  2272. }
  2273. :deep(.el-input) {
  2274. padding-left: 10px;
  2275. margin-bottom: 10px;
  2276. }
  2277. :deep(.el-input__wrapper) {
  2278. border: none;
  2279. border-bottom: 2px solid #000;
  2280. border-radius: 0;
  2281. box-shadow: none;
  2282. }
  2283. .el-icon.icon-edit {
  2284. width: 35px;
  2285. height: 35px;
  2286. font-size: 35px;
  2287. color: #8c939d;
  2288. text-align: center;
  2289. vertical-align: middle;
  2290. }
  2291. }
  2292. .text {
  2293. padding: 10px;
  2294. border: 1px solid #c4c8d0;
  2295. border-radius: 15px;
  2296. .el-button {
  2297. float: right;
  2298. margin-top: 10px;
  2299. }
  2300. :deep(.el-textarea__inner) {
  2301. resize: none;
  2302. box-shadow: none;
  2303. }
  2304. }
  2305. p {
  2306. margin: 7px 0;
  2307. }
  2308. }
  2309. .tab2,
  2310. .tab3 {
  2311. .bjSystemBox {
  2312. display: flex;
  2313. flex-wrap: wrap;
  2314. max-height: calc(100% - 60px);
  2315. overflow-y: scroll;
  2316. // background-color: pink;
  2317. .bjSystemBoxItem {
  2318. position: relative;
  2319. margin-right: 3%;
  2320. margin-bottom: 10px;
  2321. width: 30%;
  2322. height: 200px;
  2323. background-color: #e5e5e5;
  2324. border-radius: 10px;
  2325. cursor: pointer;
  2326. border: rgba(0, 0, 0, 0) 2px solid;
  2327. > img {
  2328. padding: 10px;
  2329. max-width: 100%;
  2330. max-height: 100%;
  2331. }
  2332. .delIcon {
  2333. position: absolute;
  2334. right: 5px;
  2335. top: 5px;
  2336. width: 20px;
  2337. height: 20px;
  2338. border-radius: 10px;
  2339. z-index: 999;
  2340. cursor: pointer;
  2341. img {
  2342. max-width: 100%;
  2343. max-height: 100%;
  2344. }
  2345. }
  2346. }
  2347. .ImgBorderActive {
  2348. border: #7264f5 2px solid;
  2349. }
  2350. }
  2351. }
  2352. .tab4 {
  2353. // display: flex;
  2354. // justify-content: flex-start;
  2355. // align-content: flex-start;
  2356. // flex-wrap: wrap;
  2357. .card4 {
  2358. .text {
  2359. padding: 10 px;
  2360. border: 1px solid #c0c4cc;
  2361. .el-button {
  2362. float: right;
  2363. margin-top: 10px;
  2364. }
  2365. :deep(.el-textarea__inner) {
  2366. cursor: text;
  2367. }
  2368. :deep(.el-textarea__inner) {
  2369. resize: none;
  2370. box-shadow: none;
  2371. }
  2372. i {
  2373. font-size: 14px;
  2374. float: right;
  2375. }
  2376. }
  2377. p {
  2378. margin: 7px 0;
  2379. }
  2380. .card4Btn {
  2381. margin-top: 10px;
  2382. border-radius: 5px;
  2383. height: 50px;
  2384. width: 100%;
  2385. color: #fff;
  2386. background-color: #000;
  2387. text-align: center;
  2388. line-height: 50px;
  2389. cursor: pointer;
  2390. }
  2391. .el-button {
  2392. width: 100%;
  2393. height: 50px;
  2394. margin-top: 10px;
  2395. }
  2396. }
  2397. :deep(.el-tabs__item.is-active) {
  2398. color: #909393;
  2399. background-color: #000;
  2400. }
  2401. :deep(.el-tabs__nav) {
  2402. width: 100%;
  2403. }
  2404. :deep(.el-tabs__item) {
  2405. width: 26%;
  2406. }
  2407. :deep(.el-tabs__item:hover) {
  2408. color: #909393;
  2409. }
  2410. :deep(.el-tabs__content) {
  2411. height: calc(100% - 60px);
  2412. padding: 0;
  2413. }
  2414. .tab4Item {
  2415. display: flex;
  2416. justify-content: space-between;
  2417. width: 30%;
  2418. background-color: rgba(71, 105, 218, 0.1);
  2419. text-align: center;
  2420. padding: 10px;
  2421. height: 80px;
  2422. border-radius: 10px;
  2423. margin: auto;
  2424. margin-bottom: 10px;
  2425. p {
  2426. padding-bottom: 5px;
  2427. }
  2428. }
  2429. .SubtitlesSwitch {
  2430. width: 100%;
  2431. .el-input-number--large {
  2432. width: 100px;
  2433. }
  2434. .el-input-number__increase {
  2435. width: none;
  2436. }
  2437. }
  2438. .myColorBox {
  2439. position: relative;
  2440. width: 25px;
  2441. height: 25px;
  2442. margin: auto;
  2443. border: #000 solid 2px;
  2444. cursor: pointer;
  2445. .selectColor {
  2446. position: absolute;
  2447. top: 150%;
  2448. left: 50%;
  2449. transform: translateX(-50%);
  2450. display: flex;
  2451. justify-content: flex-start;
  2452. flex-wrap: wrap;
  2453. width: 142px;
  2454. background-color: #ffffff;
  2455. z-index: 99;
  2456. box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 5px;
  2457. &::before {
  2458. content: "";
  2459. display: block;
  2460. position: absolute;
  2461. top: -10%;
  2462. left: 50%;
  2463. transform: translateX(-50%);
  2464. width: 0;
  2465. height: 0;
  2466. border-left: 15px solid transparent;
  2467. border-right: 15px solid transparent;
  2468. border-bottom: 15px solid white;
  2469. }
  2470. &-item {
  2471. width: 25px;
  2472. height: 25px;
  2473. margin: 5px 5px;
  2474. border: #000 solid 2px;
  2475. border-radius: 5px;
  2476. cursor: pointer;
  2477. }
  2478. }
  2479. }
  2480. }
  2481. }
  2482. </style>