邃芒慧影、口播(PC) https://neuver.metavatar.cc/
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

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