index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <template>
  2. <div style="position: relative;">
  3. <el-dialog title="交互视频设置" :visible.sync="dialogVisibleVideo" :append-to-body="true" width="95%"
  4. :before-close="handleClose" class="dialog_diy">
  5. <div v-if="dialogVisibleVideo">
  6. <div class="btnBox">
  7. <el-button type="primary" size="default" @click="addImg($event)" v-if="!this.json.video">
  8. 添加视频
  9. <input type="file" accept="video/mp4, video/quicktime, video/x-msvideo" style="display: none"
  10. @change="addVideo($event)" />
  11. </el-button>
  12. <el-button type="primary" size="default" @click="reset" v-else>重置</el-button>
  13. </div>
  14. <div class="videoBox">
  15. <video-player class="video-player vjs-custom-skin" ref="videoPlayer" :playsinline="true"
  16. :options="playerO" v-if="this.json.video"></video-player>
  17. <div class="content" v-else>请上传视频</div>
  18. </div>
  19. <div class="settingBox" v-if="this.json.video">
  20. <div class="settingBtn">
  21. <span class="title">交互设置</span>
  22. <el-button type="primary" size="small" @click="addSetting">添加</el-button>
  23. </div>
  24. <div class="settingContent">
  25. <div class="setting_b" v-for="(item, index) in json.setting" :key="index">
  26. <div class="time_box">
  27. <span>触发时间:</span>
  28. <el-input-number v-model="item.time" :controls="false" :min="1" placeholder="视频第几秒"
  29. @change="changeTime(item.time, index)" style="width: 100px;"></el-input-number><span style="margin-left: 5px;">秒</span>
  30. </div>
  31. <div class="setting_fool">
  32. <span>工具设置:</span>
  33. <el-button type="primary" size="mini" @click="setting(index)">{{ item.tool.tool ? "已设置" :
  34. "插入工具" }}</el-button><el-button type="text" size="mini" @click="deleteSetting(index)" style="color:#818181">删除</el-button>
  35. </div>
  36. <!-- <div>
  37. <el-button type="primary" size="mini" @click="deleteSetting(index)">删除</el-button>
  38. </div> -->
  39. </div>
  40. </div>
  41. </div>
  42. </div>
  43. <span slot="footer" class="dialog-footer">
  44. <el-button @click="close">Cancel</el-button>
  45. <el-button type="primary" @click="addV">确定</el-button>
  46. </span>
  47. </el-dialog>
  48. <choiceDialog :dialogVisibleChoice.sync="dialogVisibleChoice" :json="settingJson" @add="addSettingJson"
  49. v-if="dialogVisibleVideo"></choiceDialog>
  50. <div v-if="proVisible" class="mask">
  51. <div class="progressBox">
  52. <div class="lbox">
  53. <img src="../../../assets/loading.gif" />Uploading
  54. </div>
  55. <div style="margin-bottom: 10px">
  56. <span>{{ isFinishSize }}M</span> / <span>{{ isAllSize }}M</span>
  57. </div>
  58. <el-progress :text-inside="true" :stroke-width="20" :percentage="progress" style="width: 80%"></el-progress>
  59. </div>
  60. </div>
  61. </div>
  62. </template>
  63. <script>
  64. import choiceDialog from '../choice/index.vue'
  65. export default {
  66. props: {
  67. dialogVisibleVideo: {
  68. type: Boolean,
  69. default: false
  70. },
  71. videoJson: {
  72. type: Object
  73. }
  74. },
  75. components: {
  76. choiceDialog,
  77. },
  78. data() {
  79. return {
  80. json: {},
  81. playerOptions: {
  82. playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度
  83. autoplay: false, //如果true,浏览器准备好时开始回放。
  84. muted: false, // 默认情况下将会消除任何音频。
  85. loop: false, // 导致视频一结束就重新开始。
  86. preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
  87. language: "zh-CN",
  88. aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
  89. fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
  90. sources: [
  91. {
  92. type: "video/mp4", //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目 || "video/ogg"|| "video/webm"
  93. src: "", //url地址require("../../assets/media/aaa.mp4")
  94. },
  95. ],
  96. // poster: require("../../assets/tu31.png"), //你的封面地址
  97. // poster: dataRes.imgUrl, //你的封面地址
  98. notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
  99. controlBar: {
  100. timeDivider: true, //当前时间和持续时间的分隔符
  101. durationDisplay: true, //显示持续时间
  102. remainingTimeDisplay: false, //是否显示剩余时间功能
  103. fullscreenToggle: true, //全屏按钮
  104. },
  105. },
  106. playerO: {},
  107. videoTime: 0,
  108. settingIndex: 0,
  109. settingJson: {},
  110. dialogVisibleChoice: false,
  111. proVisible: false,
  112. isFinishSize: 0,
  113. isAllSize: 0,
  114. progress: 0,
  115. }
  116. },
  117. watch: {
  118. dialogVisibleVideo(newValue, oldValue) {
  119. this.json = JSON.parse(JSON.stringify(this.videoJson));
  120. if (this.json.video) {
  121. this.playerO = JSON.parse(JSON.stringify(this.playerOptions));
  122. this.playerO.sources[0].src = this.json.video
  123. this.$nextTick(() => {
  124. setTimeout(() => {
  125. this.videoTime = parseInt(this.$refs['videoPlayer'].player.cache_.duration)
  126. }, 500)
  127. })
  128. }
  129. }
  130. },
  131. methods: {
  132. handleClose(done) {
  133. this.close()
  134. done()
  135. },
  136. close() {
  137. this.$emit("update:dialogVisibleVideo", false)
  138. },
  139. addV() {
  140. if (!this.json.video) {
  141. this.$message.error("请上传视频")
  142. return;
  143. }
  144. if (!this.json.setting.length) {
  145. this.$message.error("请添加至少一个交互")
  146. return;
  147. }
  148. let settingType = 1
  149. let settingToolType = 1
  150. let settingTypeArray = []
  151. for (var i = 0; i < this.json.setting.length; i++) {
  152. if (settingTypeArray.indexOf(this.json.setting[i].time) == -1) {
  153. settingTypeArray.push(this.json.setting[i].time)
  154. } else {
  155. settingType = 2
  156. break
  157. }
  158. if (!this.json.setting[i].tool.tool) {
  159. settingToolType = 2
  160. }
  161. }
  162. if (settingType == 2) {
  163. this.$message.error("不能设置一样的触发时间")
  164. return;
  165. }
  166. if (settingToolType == 2) {
  167. this.$message.error("请对交互设置工具")
  168. return;
  169. }
  170. this.$emit('add', this.json)
  171. },
  172. addSettingJson(tool, json) {
  173. this.json.setting[this.settingIndex].tool.tool = tool
  174. this.json.setting[this.settingIndex].tool.toolJson = json
  175. this.dialogVisibleChoice = false
  176. },
  177. setting(index) {
  178. this.settingIndex = index
  179. let testJson = {}
  180. if (this.json.setting[index].tool.tool) {
  181. testJson = JSON.parse(JSON.stringify(this.json.setting[index].tool.toolJson))
  182. } else {
  183. testJson = {
  184. testCount: 1,
  185. testTitle: "",
  186. testJson: [
  187. {
  188. teststitle: "",
  189. testItem: 1,
  190. checkList: [],
  191. timuList: [],
  192. answer: "",
  193. type: "1",
  194. },
  195. ],
  196. };
  197. }
  198. this.settingJson = testJson
  199. this.dialogVisibleChoice = true
  200. },
  201. changeTime(time, index) {
  202. if (time > this.videoTime) {
  203. this.$message.error("设置时间不能大于视频播放时长")
  204. this.$nextTick(() => {
  205. this.json.setting[index].time = 1
  206. this.$forceUpdate();
  207. })
  208. }
  209. },
  210. addSetting() {
  211. this.json.setting.push({
  212. time: "",
  213. tool: {
  214. tool: "",
  215. toolJson: {}
  216. }
  217. })
  218. },
  219. deleteSetting(index) {
  220. this
  221. .$confirm("确定删除么?", "Notification", {
  222. confirmButtonText: "Save",
  223. cancelButtonText: "Cancel",
  224. type: "warning",
  225. })
  226. .then(() => {
  227. this.json.setting.splice(index, 1)
  228. })
  229. .catch(() => {
  230. return;
  231. });
  232. },
  233. reset() {
  234. this
  235. .$confirm("确定重置么?", "Notification", {
  236. confirmButtonText: "Save",
  237. cancelButtonText: "Cancel",
  238. type: "warning",
  239. })
  240. .then(() => {
  241. this.json = {
  242. video: "",
  243. setting: []
  244. }
  245. })
  246. .catch(() => {
  247. return;
  248. });
  249. },
  250. addImg(e) {
  251. var el = e.currentTarget;
  252. el.getElementsByTagName("input")[0].click();
  253. e.target.value = "";
  254. },
  255. addVideo(event) {
  256. var file = event.target.files[0];
  257. var credentials = {
  258. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  259. secretAccessKey: "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",
  260. }; //秘钥形式的登录上传
  261. window.AWS.config.update(credentials);
  262. window.AWS.config.region = "cn-northwest-1"; //设置区域
  263. var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
  264. var _this = this;
  265. _this.progress = 0;
  266. _this.proVisible = true;
  267. _this.isFinishSize = 0;
  268. _this.isAllSize = (file.size / 1024 / 1024).toFixed(2);
  269. if (file) {
  270. var params = {
  271. Key:
  272. file.name.split(".")[0] +
  273. new Date().getTime() +
  274. "." +
  275. file.name.split(".")[file.name.split(".").length - 1],
  276. ContentType: file.type,
  277. Body: file,
  278. "Access-Control-Allow-Credentials": "*",
  279. ACL: "public-read",
  280. }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
  281. var options = {
  282. partSize: 2048 * 1024 * 1024,
  283. queueSize: 2,
  284. leavePartsOnError: true,
  285. };
  286. bucket
  287. .upload(params, options)
  288. .on("httpUploadProgress", function (evt) {
  289. //这里可以写进度条
  290. // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
  291. _this.progress = parseInt((evt.loaded / evt.total) * 100);
  292. _this.isFinishSize = (evt.loaded / 1024 / 1024).toFixed(2);
  293. _this.$forceUpdate();
  294. })
  295. .send(function (err, data) {
  296. _this.progress = 100;
  297. _this.isFinishSize = _this.isAllSize;
  298. _this.$forceUpdate();
  299. setTimeout(() => {
  300. _this.proVisible = false;
  301. _this.$forceUpdate();
  302. }, 500);
  303. if (err) {
  304. _this.$message.error("上传失败");
  305. } else {
  306. _this.json.video = data.Location;
  307. _this.playerO = JSON.parse(JSON.stringify(_this.playerOptions));
  308. _this.playerO.sources[0].src = data.Location
  309. _this.$nextTick(() => {
  310. setTimeout(() => {
  311. console.log(_this.$refs['videoPlayer']);
  312. _this.videoTime = parseInt(_this.$refs['videoPlayer'].player.cache_.duration)
  313. }, 500)
  314. })
  315. _this.$forceUpdate();
  316. }
  317. });
  318. }
  319. }
  320. },
  321. mounted() {
  322. this.json = JSON.parse(JSON.stringify(this.videoJson));
  323. if (this.json.video) {
  324. this.playerO = JSON.parse(JSON.stringify(this.playerOptions));
  325. this.playerO.sources[0].src = this.json.video
  326. this.$nextTick(() => {
  327. setTimeout(() => {
  328. this.videoTime = parseInt(this.$refs['videoPlayer'].player.cache_.duration)
  329. }, 500)
  330. })
  331. }
  332. },
  333. }
  334. </script>
  335. <style scoped>
  336. .dialog_diy>>>.el-dialog__header {
  337. background: #3c3c3c !important;
  338. padding: 15px 20px;
  339. }
  340. .dialog_diy>>>.el-dialog__title {
  341. color: #fff;
  342. }
  343. .dialog_diy>>>.el-dialog__headerbtn {
  344. top: 19px;
  345. }
  346. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
  347. color: #fff;
  348. }
  349. .dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
  350. color: #fff;
  351. }
  352. .dialog_diy>>>.el-dialog__body,
  353. .dialog_diy>>>.el-dialog__footer {
  354. background: #fafafa;
  355. }
  356. .videoBox {
  357. width: 100%;
  358. height: 600px;
  359. background: #fff;
  360. margin-top: 20px;
  361. position: relative;
  362. }
  363. .btnBox{
  364. display:flex;
  365. justify-content: flex-end;
  366. }
  367. .videoBox>.content {
  368. /* position: absolute; */
  369. line-height: 600px;
  370. text-align: center;
  371. font-size: 18px;
  372. user-select: none;
  373. }
  374. .videoBox .video-player {
  375. height: 100%;
  376. width: auto;
  377. }
  378. .videoBox>>>.vjs-fluid {
  379. padding: 0 !important;
  380. height: 100%;
  381. }
  382. .settingBox {
  383. margin-top: 20px;
  384. }
  385. .settingBtn {
  386. display: flex;
  387. align-items: center;
  388. }
  389. .settingBtn>.title {
  390. font-size: 22px;
  391. margin-right: 10px;
  392. color: #222;
  393. }
  394. .settingContent {
  395. width: 100%;
  396. display: flex;
  397. flex-wrap: wrap;
  398. margin-top: 20px;
  399. }
  400. .setting_b {
  401. margin: 0 60px 20px 0;
  402. }
  403. .setting_b div+div {
  404. margin: 10px 0 0 0;
  405. }
  406. .time_box {}
  407. .setting_fool {}
  408. .settingBox>>>.el-input-number.is-without-controls .el-input__inner {
  409. text-align: left;
  410. }
  411. .mask {
  412. background-color: rgb(0 0 0 / 30%);
  413. position: fixed;
  414. /* position: absolute; */
  415. top: 0;
  416. left: 0;
  417. width: 100%;
  418. height: 100%;
  419. z-index: 99999;
  420. display: flex;
  421. align-items: center;
  422. justify-content: center;
  423. }
  424. .progressBox {
  425. width: 300px;
  426. height: 150px;
  427. background: #fff;
  428. border-radius: 10px;
  429. box-shadow: 0 0 6px 1px #bfbfbf;
  430. display: flex;
  431. align-items: center;
  432. justify-content: center;
  433. flex-direction: column;
  434. position: relative;
  435. color: #6c6c6c;
  436. }
  437. .progressBox>>>.el-progress-bar__outer {
  438. background-color: #d1dfff !important;
  439. }
  440. .progressBox .lbox {
  441. height: 50px;
  442. font-size: 19px;
  443. display: flex;
  444. align-items: center;
  445. color: #747474;
  446. }
  447. .progressBox .lbox img {
  448. width: 40px;
  449. margin-right: 20px;
  450. }
  451. </style>