邃芒慧影、口播(PC) https://neuver.metavatar.cc/
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

2505 rader
69 KiB

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