邃芒慧影、口播(PC) https://neuver.metavatar.cc/
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 

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