addVideoDlg.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <script setup>
  2. import { ref, onMounted, watch } from "vue";
  3. import { raiInterface, raiVideoApi, getOSSSign } from "@/api/api.js";
  4. import { ElMessageBox, ElMessage, ElLoading } from "element-plus";
  5. import MD5 from "js-md5";
  6. const props = defineProps({
  7. addEditdialogVisib: {
  8. type: Boolean,
  9. default: false,
  10. },
  11. chartPermissionList: {
  12. type: Array,
  13. default: [],
  14. },
  15. playDetailsList: {
  16. default: {},
  17. type: Object,
  18. },
  19. });
  20. const addEditVideo = ref({
  21. videoName: "", //音频名称
  22. industryId: "", //行业id
  23. property: "", //产业名称
  24. publishDate: "", //发布时间
  25. videoUrl: "", //视频链接
  26. VideoSeconds: "", //时长
  27. imgUrl: "", // 视频封面
  28. shareImgUrl: "", // 分享的视频封面
  29. detailImgUrl: "", // 详情的视频封面
  30. });
  31. const rules = ref({
  32. videoName: [{ required: true, message: "请上传视频", trigger: "blur" }],
  33. industryId: [{ required: true, message: "请选择行业", trigger: "change" }],
  34. property: { required: true, message: "请选择产业", trigger: "change" },
  35. publishDate: [{ required: true, message: "请选择发布时间", trigger: "change" }],
  36. imgUrl: { required: true, message: "请选择列表页封面", trigger: "change" },
  37. shareImgUrl: { required: true, message: "请选择视频分享图", trigger: "change" },
  38. detailImgUrl: { required: true, message: "请选择详情页封面", trigger: "change" },
  39. });
  40. const videoId = ref(0);
  41. const publishStatus = ref(0);
  42. const industryArr = ref([]);
  43. const selectedIndustryArr = ref([]);
  44. const defaultProps = ref({
  45. label: "PermissionName",
  46. children: "List",
  47. value: "ChartPermissionId",
  48. });
  49. const percentage = ref(0);
  50. const startUpload = ref(false); //开始上传
  51. watch(
  52. () => props.playDetailsList,
  53. async (newval) => {
  54. if (JSON.stringify(newval) != "{}") {
  55. await getIndustry(newval.ChartPermissionId);
  56. console.log(newval);
  57. addEditVideo.value = {
  58. videoName: newval.VideoName, //音频名称
  59. industryId: newval.ChartPermissionId, //行业id
  60. property: newval.IndustryId || "", //产业名称
  61. publishDate: newval.PublishDate, //发布时间
  62. videoUrl: newval.VideoUrl, //视频链接
  63. VideoSeconds: newval.VideoDuration, //时长
  64. imgUrl: newval.ImgUrl, // 视频封面
  65. shareImgUrl: newval.ShareImgUrl, // 分享的视频封面
  66. detailImgUrl: newval.DetailImgUrl,
  67. };
  68. videoId.value = newval.VideoId;
  69. publishStatus.value = newval.PublishStatus;
  70. } else {
  71. videoId.value = 0;
  72. selectedIndustryArr.value = [];
  73. publishStatus.value = 0;
  74. }
  75. },
  76. {
  77. deep: true,
  78. }
  79. );
  80. /* 获取全部的行业 前的判断 */
  81. function industrySelectFocus() {
  82. if (!addEditVideo.value.industryId) {
  83. ElMessage.error("请先选择行业");
  84. }
  85. }
  86. // 行业更改
  87. function selectChangeHandle(value) {
  88. addEditVideo.value.property = "";
  89. value ? getIndustry(value) : (selectedIndustryArr.value = []);
  90. }
  91. /* 获取选择的行业 */
  92. function getIndustry(industryId) {
  93. if (industryArr.value.length) {
  94. selectedIndustryArr.value = industryArr.value.filter((item) => item.ChartPermissionId == industryId)[0].List;
  95. return;
  96. }
  97. raiInterface.getListIndustrial().then((res) => {
  98. if (res.Ret === 200) {
  99. industryArr.value = res.Data.List || [];
  100. selectedIndustryArr.value = industryArr.value.filter((item) => item.ChartPermissionId == industryId)[0].List;
  101. }
  102. });
  103. }
  104. //获取视频时长的promise
  105. function handleGetDuration(file) {
  106. return new Promise((resolve, reject) => {
  107. const fileUrl = URL.createObjectURL(file);
  108. const audioEl = new Audio(fileUrl);
  109. audioEl.addEventListener("loadedmetadata", (e) => {
  110. resolve(audioEl.duration);
  111. });
  112. });
  113. }
  114. //上传视频判断格式
  115. function handelBeforeUploadVideo(e) {
  116. if (e.type != "video/mp4") {
  117. ElMessage.warning("上传失败,上传视频格式不正确");
  118. return false;
  119. }
  120. }
  121. // 上传视频
  122. async function handleUpload(e) {
  123. const duration = await handleGetDuration(e.file);
  124. addEditVideo.value.videoName = e.file.name;
  125. addEditVideo.value.VideoSeconds = duration + ""; //时常
  126. const res = await getOSSSign();
  127. if (res.Ret === 200) {
  128. let accessKeyId = res.Data.AccessKeyId;
  129. let accessKeySecret = res.Data.AccessKeySecret;
  130. let stsToken = res.Data.SecurityToken;
  131. handleUploadToOSS(e.file, accessKeyId, accessKeySecret, stsToken);
  132. }
  133. }
  134. //上传到阿里云
  135. async function handleUploadToOSS(file, accessKeyId, accessKeySecret, stsToken) {
  136. startUpload.value = true;
  137. const ALOSSINS = new OSS({
  138. // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
  139. region: "oss-cn-shanghai",
  140. // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
  141. accessKeyId: accessKeyId,
  142. accessKeySecret: accessKeySecret,
  143. // 从STS服务获取的安全令牌(SecurityToken)。
  144. stsToken: stsToken,
  145. // 填写Bucket名称,例如examplebucket。
  146. bucket: "hzchart",
  147. endpoint: "hzstatic.hzinsights.com",
  148. cname: true,
  149. timeout: 600000,
  150. });
  151. // 生成文件名
  152. const t = new Date().getTime().toString();
  153. const temName = `static/yb/video/${MD5(t)}.${file.name.split(".")[1]}`;
  154. const options = {
  155. // 获取分片上传进度、断点和返回值。
  156. progress: (p, cpt, res) => {
  157. percentage.value = parseInt(p * 100);
  158. },
  159. // 设置并发上传的分片数量。
  160. parallel: 10,
  161. // 设置分片大小。默认值为1 MB,最小值为100 KB。
  162. partSize: 1024 * 1024 * 10, // 10MB
  163. };
  164. try {
  165. const res = await ALOSSINS.multipartUpload(temName, file, { ...options });
  166. if (res.res.status === 200) {
  167. addEditVideo.value.videoUrl = "https://hzstatic.hzinsights.com/" + res.name;
  168. startUpload.value = false;
  169. percentage.value = 0;
  170. }
  171. } catch (error) {
  172. ElMessage.warning("上传失败,请刷新重试");
  173. startUpload.value = false;
  174. percentage.value = 0;
  175. }
  176. }
  177. const $emit = defineEmits(["getVideoList"]);
  178. const ruleFormVideo = ref(null);
  179. //保存或发布
  180. function confirmSubmit(type) {
  181. ruleFormVideo.value.validate(async (valid) => {
  182. if (valid) {
  183. const res = await raiVideoApi.addVideo({
  184. VideoName: addEditVideo.value.videoName,
  185. ChartPermissionId: addEditVideo.value.industryId,
  186. IndustryId: addEditVideo.value.property || 0,
  187. VideoUrl: addEditVideo.value.videoUrl,
  188. VideoDuration: addEditVideo.value.VideoSeconds,
  189. PublishDate: addEditVideo.value.publishDate,
  190. PublishOrSave: type == 1 ? type : publishStatus.value,
  191. VideoId: videoId.value,
  192. ImgUrl: addEditVideo.value.imgUrl,
  193. ShareImgUrl: addEditVideo.value.shareImgUrl,
  194. DetailImgUrl: addEditVideo.value.detailImgUrl,
  195. });
  196. if (res.Ret === 200) {
  197. ElMessage.success("添加成功");
  198. cancelHandle();
  199. $emit("getVideoList");
  200. }
  201. } else {
  202. console.log("error submit!!");
  203. return false;
  204. }
  205. });
  206. }
  207. // ------------------图片上传
  208. function handleImageUpload(e, type) {
  209. let formData = new FormData();
  210. formData.append("file", e.file);
  211. raiInterface.upload(formData).then((res) => {
  212. if (type == "imgUrl") {
  213. addEditVideo.value.imgUrl = res.Data.ResourceUrl;
  214. } else if (type == "shareImgUrl") {
  215. addEditVideo.value.shareImgUrl = res.Data.ResourceUrl;
  216. } else {
  217. addEditVideo.value.detailImgUrl = res.Data.ResourceUrl;
  218. }
  219. // 触发表单验证
  220. ruleFormVideo.value.validateField(type);
  221. });
  222. }
  223. //详情进来编辑
  224. async function editVideo() {
  225. const res = await raiVideoApi.editVideo({});
  226. }
  227. function cancelHandle() {
  228. ruleFormVideo.value.resetFields();
  229. addEditVideo.value = {
  230. videoName: "", //音频名称
  231. industryId: "", //行业id
  232. property: "", //产业名称
  233. publishDate: "", //发布时间
  234. videoUrl: "", //视频链接
  235. VideoSeconds: "", //时长
  236. imgUrl: "", // 视频封面
  237. shareImgUrl: "", // 分享的视频封面
  238. detailImgUrl: "",
  239. };
  240. $emit("update:addEditdialogVisib", false);
  241. $emit("update:playDetailsList", {});
  242. }
  243. </script>
  244. <template>
  245. <div class="container add-edit-video">
  246. <el-dialog
  247. draggable
  248. :close-on-click-modal="false"
  249. :modal-append-to-body="false"
  250. center
  251. width="568px"
  252. title="添加视频"
  253. v-model="props.addEditdialogVisib"
  254. customClass="add-edit-video-dlg"
  255. :before-close="cancelHandle"
  256. >
  257. <div>
  258. <el-form :model="addEditVideo" :rules="rules" ref="ruleFormVideo" class="demo-ruleForm">
  259. <el-form-item prop="audioName">
  260. <div style="display: flex; justify-content: space-between">
  261. <el-input style="width: 75%" clearable placeholder="请上传视频" v-model="addEditVideo.videoName"></el-input>
  262. <el-upload action="" accept=".mp4" :http-request="handleUpload" :before-upload="handelBeforeUploadVideo" :show-file-list="false" :disabled="startUpload">
  263. <el-button type="primary" :loading="startUpload">上传视频</el-button>
  264. </el-upload>
  265. <el-progress type="circle" :percentage="percentage" width="40" style="margin-left: 10px" v-if="startUpload"></el-progress>
  266. </div>
  267. <p style="color: #ff3737">注:视频格式支持mp4</p>
  268. </el-form-item>
  269. <el-form-item prop="industryId">
  270. <el-select style="width: 100%" placeholder="请选择行业" v-model="addEditVideo.industryId" clearable @change="selectChangeHandle">
  271. <el-option
  272. v-for="item in props.chartPermissionList.filter((item) => item.PermissionName != '宏观' && item.PermissionName != '策略' && item.PermissionName != '研选订阅')"
  273. :label="item.PermissionName"
  274. :key="item.ChartPermissionId"
  275. :value="item.ChartPermissionId"
  276. ></el-option>
  277. </el-select>
  278. </el-form-item>
  279. <el-form-item prop="property">
  280. <!-- <el-cascader
  281. style="width: 100%"
  282. @focus="industrySelectFocus"
  283. placeholder="请选择产业"
  284. v-model="addEditVideo.property"
  285. :props="{ ...defaultProps }"
  286. :options="selectedIndustryArr"
  287. :key="addEditVideo.industryId"
  288. filterable
  289. clearable
  290. ></el-cascader> -->
  291. <el-select v-model="addEditVideo.property" placeholder="请选择产业" clearable filterable style="width: 100%" @focus="industrySelectFocus">
  292. <el-option :label="item.PermissionName" :value="item.ChartPermissionId" v-for="item in selectedIndustryArr" :key="item.ChartPermissionId"></el-option>
  293. </el-select>
  294. </el-form-item>
  295. <el-form-item prop="publishDate">
  296. <el-date-picker
  297. style="width: 100%"
  298. v-model="addEditVideo.publishDate"
  299. type="date"
  300. placeholder="请选择发布时间"
  301. format="YYYY 年 MM 月 DD 日"
  302. value-format="YYYY-MM-DD"
  303. @change="conditionChange"
  304. >
  305. </el-date-picker>
  306. </el-form-item>
  307. <div class="uploadImageContain">
  308. <el-form-item prop="imgUrl">
  309. <div class="imgUploadBox">
  310. <el-upload action="" :http-request="(e) => handleImageUpload(e, 'imgUrl')" :multiple="false" :show-file-list="false" v-if="!addEditVideo.imgUrl">
  311. <div class="image-upload-item">
  312. <img src="~@/assets/icons/uploadImg-blue.svg" />
  313. <span>添加列表页封面</span>
  314. </div>
  315. </el-upload>
  316. <div v-else class="uploaded-image-item">
  317. <el-image :src="addEditVideo.imgUrl" class="videoImage" :preview-src-list="[addEditVideo.imgUrl]" />
  318. <span class="del-image" @click="addEditVideo.imgUrl = ''">删除</span>
  319. </div>
  320. </div>
  321. </el-form-item>
  322. <el-form-item prop="detailImgUrl">
  323. <div class="imgUploadBox">
  324. <el-upload action="" :http-request="(e) => handleImageUpload(e, 'detailImgUrl')" :multiple="false" :show-file-list="false" v-if="!addEditVideo.detailImgUrl">
  325. <div class="image-upload-item">
  326. <img src="~@/assets/icons/uploadImg-blue.svg" />
  327. <span>添加详情页封面</span>
  328. </div>
  329. </el-upload>
  330. <div v-else class="uploaded-image-item">
  331. <el-image :src="addEditVideo.detailImgUrl" class="videoImage" :preview-src-list="[addEditVideo.detailImgUrl]" />
  332. <span class="del-image" @click="addEditVideo.detailImgUrl = ''">删除</span>
  333. </div>
  334. </div>
  335. </el-form-item>
  336. <el-form-item prop="shareImgUrl">
  337. <div class="imgUploadBox">
  338. <el-upload action="" :http-request="(e) => handleImageUpload(e, 'shareImgUrl')" :multiple="false" :show-file-list="false" v-if="!addEditVideo.shareImgUrl">
  339. <div class="image-upload-item">
  340. <img src="~@/assets/icons/uploadImg-blue.svg" />
  341. <span>添加视频分享图</span>
  342. </div>
  343. </el-upload>
  344. <div v-else class="uploaded-image-item">
  345. <el-image :src="addEditVideo.shareImgUrl" class="videoImage" :preview-src-list="[addEditVideo.shareImgUrl]" />
  346. <span class="del-image" @click="addEditVideo.shareImgUrl = ''">删除</span>
  347. </div>
  348. </div>
  349. </el-form-item>
  350. </div>
  351. </el-form>
  352. </div>
  353. <template #footer>
  354. <span class="dialog-footer">
  355. <el-button type="primary" @click="confirmSubmit(0)">保存</el-button>
  356. <el-button type="primary" v-if="publishStatus != 1" @click="confirmSubmit(1)">发布</el-button>
  357. <el-button @click="cancelHandle">取消</el-button>
  358. </span>
  359. </template>
  360. </el-dialog>
  361. </div>
  362. </template>
  363. <style lang="scss">
  364. .add-edit-video {
  365. .add-edit-video-dlg {
  366. .el-input {
  367. width: 100%;
  368. }
  369. }
  370. }
  371. </style>
  372. <style lang="scss" scoped>
  373. .uploadImageContain {
  374. display: flex;
  375. justify-content: space-between;
  376. .imgUploadBox {
  377. display: flex;
  378. align-items: center;
  379. justify-content: center;
  380. width: 110px;
  381. height: 110px;
  382. .image-upload-item {
  383. display: flex;
  384. flex-direction: column;
  385. justify-content: center;
  386. img {
  387. height: 32px;
  388. }
  389. span {
  390. color: #409eff;
  391. font-weight: 400;
  392. font-size: 14px;
  393. }
  394. }
  395. .uploaded-image-item {
  396. height: 110px;
  397. width: 110px;
  398. display: flex;
  399. justify-content: center;
  400. .videoImage {
  401. border-radius: 1px;
  402. }
  403. .del-image {
  404. display: none;
  405. color: #fff;
  406. position: absolute;
  407. bottom: 0;
  408. left: 0;
  409. right: 0;
  410. text-align: center;
  411. line-height: 24px;
  412. width: 110px;
  413. background-color: rgba($color: #000000, $alpha: 0.8);
  414. cursor: pointer;
  415. }
  416. &:hover {
  417. .del-image {
  418. display: block;
  419. }
  420. }
  421. }
  422. }
  423. }
  424. </style>