upload-image.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <div class="upload-video">
  3. <div class="upload-box">
  4. <input
  5. class="upload-input"
  6. ref="chooseFile"
  7. type="file"
  8. @change="addFile"
  9. multiple
  10. accept=".jpg,.png"
  11. />
  12. <i class="iconfont icon-empty"></i>
  13. <p>点击或将文件拖拽到这里上传</p>
  14. <p class="gray-font">
  15. 建议最佳:横版大图:宽高比16:9,1280*720≤尺寸≤2560*1440,
  16. </p>
  17. <p class="gray-font">竖版大图:宽高比9:16,720*1280≤尺寸≤1440*2560,</p>
  18. <p class="gray-font">小图:宽高比1.52,456*300≤尺寸≤1368*900,</p>
  19. <p class="gray-font">
  20. 支持JPG,PNG等图片格式。命名格式:推广书籍名称-需求方-文案
  21. </p>
  22. </div>
  23. <div class="upload-list">
  24. <div class="list-item" v-for="(item, index) in fileList" :key="index">
  25. <i class="iconfont icon-paper-clip"></i>
  26. <p>
  27. {{ item.name }}
  28. <i class="iconfont icon-guanbi" @click="deleteItem(index)"></i
  29. ><a-progress
  30. :showInfo="false"
  31. :percent="item.percent"
  32. status="active"
  33. />
  34. </p>
  35. </div>
  36. </div>
  37. <p class="gray-font footer-num">已选择:{{ fileList.length }}/20</p>
  38. </div>
  39. </template>
  40. <script lang="ts">
  41. import { defineComponent, reactive, toRefs, ref, watch, h } from "vue";
  42. import { uploadImage } from "@/api";
  43. import { message, notification } from "ant-design-vue";
  44. import { LoadingOutlined, CheckOutlined } from "@ant-design/icons-vue";
  45. const UploadVideo = defineComponent({
  46. components: {},
  47. props: ["searchForm"],
  48. setup() {
  49. const formRef = ref();
  50. const state = reactive({
  51. fileList: ref<any[]>([]),
  52. });
  53. return { ...toRefs(state) };
  54. },
  55. mounted() {},
  56. methods: {
  57. // 选择视频后
  58. addFile(event: any) {
  59. event.target.files.forEach((item: any) => {
  60. this.getImgInfo(item).then((imgInfo: any) => {
  61. let { width, height } = imgInfo;
  62. // 校验文件尺寸大小比例
  63. // if (
  64. // height / width !== 16 / 9 &&
  65. // height / width !== 9 / 16 &&
  66. // height / width !== 1.52 &&
  67. // width / height !== 1.52
  68. // )
  69. // return message.warning(
  70. // `图片:${item.name} 宽高比例不符合要求比例,请修改后重新上传`
  71. // );
  72. // 校验文件是否已存在于fileList
  73. let flag = true;
  74. if (this.fileList.length > 0) {
  75. this.fileList.forEach((itm) => {
  76. if (itm.name === item.name) flag = false;
  77. });
  78. }
  79. if (!flag) return message.warning("文件已存在");
  80. if (this.fileList.length === 20)
  81. return message.warning("图片数量不能超过20");
  82. if (height > 1368 || width > 1368)
  83. return message.warning(
  84. `图片:${item.name} 尺寸不符合要求尺寸,请修改后重新上传`
  85. );
  86. // 正则 /^[\u4E00-\u9FA5A-Za-z0-9]+(-|\/)[\u4E00-\u9FA5A-Za-z0-9]+(-|\/)[\u4E00-\u9FA5A-Za-z0-9]+$/ XX-XX-XX
  87. if (
  88. this.checkNumber(item.name) !== 2 &&
  89. this.checkNumber(item.name) !== 3
  90. )
  91. return message.warning(
  92. `图片:${item.name} 不符合命名要求,请修改后重新上传`
  93. );
  94. this.fileList.push({
  95. file: item,
  96. name: item.name,
  97. percent: 0,
  98. materialSize: (item.size / 1024 / 1024).toFixed(2) + "MB",
  99. type: item.type.split("/")[0],
  100. width: width,
  101. height: height,
  102. });
  103. });
  104. });
  105. let inputFile: any = this.$refs.chooseFile;
  106. inputFile.value = "";
  107. console.log("新增文件列表", this.fileList);
  108. },
  109. // 确定上传图片
  110. async onUploadImage() {
  111. if (this.fileList.length === 0) return message.warning("请选择图片");
  112. this.$emit("close");
  113. // message.info("素材上传中...");
  114. const key = "start";
  115. notification.open({
  116. key,
  117. message: "状态提示",
  118. description: "素材上传中...",
  119. duration: 2,
  120. icon: h(LoadingOutlined, { style: "color: #108ee9" }),
  121. });
  122. let num = 0;
  123. if (this.fileList.length === 0) return message.error("请选择图片");
  124. this.fileList.forEach((item) => {
  125. let form: any = new FormData();
  126. form.append("is_public", this.$props.searchForm.is_public);
  127. form.append("image", item.file); //,item.name
  128. uploadImage(form)
  129. .catch((err) => {
  130. console.log("ERR", err);
  131. })
  132. .finally(() => {
  133. num++;
  134. console.log("NUM", num);
  135. if (num === this.fileList.length) {
  136. const key2 = "final";
  137. notification.open({
  138. key2,
  139. message: "状态提示",
  140. description: "素材上传完成!",
  141. duration: 2,
  142. icon: h(CheckOutlined, { style: "color: greenyellow" }),
  143. });
  144. }
  145. this.$emit("getList");
  146. });
  147. });
  148. },
  149. // 获取图片宽高信息
  150. getImgInfo(file: any) {
  151. return new Promise((resolve, reject) => {
  152. let reader = new FileReader();
  153. reader.readAsDataURL(file); // 必须用file.raw
  154. reader.onload = () => {
  155. // 让页面中的img标签的src指向读取的路径
  156. let img: any = new Image();
  157. img.src = reader.result;
  158. img.onload = () => {
  159. resolve({
  160. width: img.width,
  161. height: img.height,
  162. });
  163. };
  164. };
  165. });
  166. },
  167. // 删除item
  168. deleteItem(index: number) {
  169. this.fileList.splice(index, 1);
  170. },
  171. // 工具函数 返回-出现在字符串中的次数
  172. checkNumber(str: string) {
  173. let num = 0;
  174. str.split("").forEach((item: any) => {
  175. if (item === "-") {
  176. num++;
  177. }
  178. });
  179. return num;
  180. },
  181. clearList() {
  182. this.fileList = [];
  183. },
  184. },
  185. });
  186. export default UploadVideo;
  187. </script>
  188. <style lang="scss" scoped>
  189. @import "@/assets/common-style/frame.scss";
  190. .upload-video {
  191. width: 100%;
  192. padding: 0 10%;
  193. .gray-font {
  194. color: gray;
  195. }
  196. .upload-box {
  197. cursor: pointer;
  198. background: rgb(250, 250, 250);
  199. border: 2px dashed #39a4ff;
  200. border-radius: 6px;
  201. transition: all 0.3s;
  202. padding-bottom: 10px;
  203. position: relative;
  204. i {
  205. color: rgb(168, 167, 167);
  206. display: block;
  207. font-size: 60px;
  208. }
  209. i,
  210. p {
  211. text-align: center;
  212. transition: all 0.3s;
  213. color: #39a4ff;
  214. }
  215. // 选择文件
  216. .upload-input {
  217. position: absolute;
  218. display: block;
  219. width: 100%;
  220. height: 100%;
  221. opacity: 0;
  222. cursor: pointer;
  223. }
  224. }
  225. .upload-box:hover {
  226. i,
  227. p {
  228. color: #0829e6 !important;
  229. }
  230. border-color: #0829e6;
  231. }
  232. .upload-list {
  233. overflow: hidden;
  234. .list-item {
  235. margin-top: 10px;
  236. .icon-paper-clip {
  237. width: 14px;
  238. font-size: 12px;
  239. float: left;
  240. padding-top: 3px;
  241. }
  242. p {
  243. float: left;
  244. width: calc(100% - 14px);
  245. overflow: hidden;
  246. .icon-guanbi {
  247. display: block;
  248. float: right;
  249. font-size: 13px;
  250. cursor: pointer;
  251. &:hover {
  252. color: rgb(248, 10, 157);
  253. }
  254. }
  255. }
  256. }
  257. }
  258. .footer-num {
  259. position: relative;
  260. top: 56px;
  261. }
  262. }
  263. </style>