myUpload.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. <template>
  2. <div class="upload_wrap">
  3. <div v-if="waitFileList.length > 0">
  4. <div class="el-upload-list el-upload-list--picture-card" v-if="listType == 'picture-card' || listType == 'picture'">
  5. <div class="el-upload-list__item is-success" v-for="( item, index ) in waitFileList " :key="index">
  6. <el-image style="width:148px;height:148px;" :src="item.url" fit="fill" :lazy="true"></el-image>
  7. <div v-if="!props.isDisableUpload" class="el-upload-list__item-actions el-upload-list__item-delete">
  8. <el-icon class="cursor-pointer" @click="removeFile(item)">
  9. <DeleteFilled />
  10. </el-icon>
  11. </div>
  12. </div>
  13. </div>
  14. <div class="template_list" v-if="listType == 'text'">
  15. <div class="template" v-for="(item, index) in waitFileList" :key="index">
  16. <span>
  17. <el-icon>
  18. <Link />
  19. </el-icon>
  20. </span>
  21. <span class="documentName">{{ item.name }}</span>
  22. <span v-if="!props.isDisableUpload">
  23. <el-icon color="#000000a6" size="16" @click="removeFile(item)">
  24. <Close />
  25. </el-icon>
  26. </span>
  27. <span v-if="isDownLoad" style="paddingleft: 5px">
  28. <el-icon @click="handleDownLoad(item)">
  29. <Download />
  30. </el-icon>
  31. </span>
  32. </div>
  33. </div>
  34. </div>
  35. <el-upload v-else class="w-auto upload" ref="uploadRef" :file-list="waitFileList" :multiple="props.isMultiple"
  36. :limit="props.limitNum" :list-type="listType" :accept="props.acceptType" :auto-upload="false"
  37. :show-file-list="false" :disabled="props.isDisableUpload" :on-change="handleChange" :on-remove="handleRemove">
  38. <div class="el-upload__text" v-if="listType == 'text'">
  39. <el-icon>
  40. <Upload />
  41. </el-icon>
  42. <span>上传文件</span>
  43. </div>
  44. <div v-else>
  45. <el-icon>
  46. <Plus />
  47. </el-icon>
  48. </div>
  49. <template #tip v-if="!props.isDisableUpload && isShowTips">
  50. <div class="el-upload__tip">
  51. <span>支持{{ acceptType.replace(",", "/") }};</span>
  52. <span v-if="isLimitSize">
  53. 文件大小不能超过{{ props.maxFileSize }}M;
  54. </span>
  55. <span v-if="isCheckMM">
  56. 尺寸:{{ props.widthLimit }}*{{ props.heightLimit }}px
  57. </span>
  58. </div>
  59. </template>
  60. </el-upload>
  61. <el-dialog v-model="dialogVisible">
  62. <img w-full :src="dialogImageUrl" alt="Preview Image" />
  63. </el-dialog>
  64. </div>
  65. </template>
  66. <script lang="ts" setup>
  67. import { ref, watch } from "vue";
  68. import { ElLoading, ElMessage } from "element-plus";
  69. import http from '@/api/http';
  70. import { Close, Upload, Download, Plus, Link, DeleteFilled } from "@element-plus/icons-vue";
  71. import type { UploadProps, UploadUserFile } from 'element-plus'
  72. const emits = defineEmits(["fileSuccess", "fileRemove"]);
  73. interface Props {
  74. acceptType?: string; // 上传文件类型
  75. isMultiple?: boolean; // 是否可批量上传
  76. isCheckName?: boolean; // 是否检查文件名
  77. limitNum?: number; // 允许上传文件的最大数量
  78. isDisableUpload?: boolean; // 是否禁用上传
  79. maxFileSize?: number; // 文件大小
  80. widthLimit?: number; // 图片宽度
  81. heightLimit?: number; // 图片高度
  82. isLimitSize?: boolean;//是否限制大小
  83. isShowTips?: boolean;
  84. action?: string;
  85. formType?: string;//上传的文件字段名称
  86. fileList?: any; // 回显的文件
  87. isDownLoad?: boolean; // 是否可以下载
  88. isCheckMM?: boolean; // 是否检查图片尺寸
  89. listType?: string;//文件上传样式类型
  90. }
  91. // 接收父组件传递过来的参数
  92. const props = withDefaults(defineProps<Props>(), {
  93. acceptType: ".jpeg,.png",
  94. formType: "photo",
  95. isMultiple: false,
  96. isCheckName: false,
  97. isCheckMM: false,
  98. limitNum: 10,
  99. isDisableUpload: false,
  100. maxFileSize: 0.5,
  101. isShowTips: true,
  102. isLimitSize: true,
  103. action: "/qiniu/upload/image",
  104. fileList: [],
  105. widthLimit: 690,
  106. heightLimit: 280,
  107. isDownLoad: false,
  108. listType: 'picture-card'
  109. });
  110. let waitFileList = ref<any[]>([]);
  111. waitFileList.value = props.fileList;
  112. const dialogImageUrl = ref('')
  113. const dialogVisible = ref(false)
  114. const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
  115. dialogImageUrl.value = uploadFile.url!
  116. dialogVisible.value = true
  117. }
  118. const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
  119. console.log(uploadFile, uploadFiles)
  120. }
  121. watch(
  122. () => props.fileList,
  123. () => {
  124. console.log("props.fileList====>", props.fileList);
  125. waitFileList.value = props.fileList;
  126. }
  127. );
  128. // 文件变化Handle 这里监听上传文件的变化是一个一个接收到变化的,所以文件也是一个一个上传到服务器上面的
  129. const handleChange = async (file: any, fileList: any[]) => {
  130. if (props.isCheckMM) {
  131. const test = await limitFileWH(file)
  132. if (!test) return
  133. }
  134. if (!/^[0-9]+_/.test(file.name) && props.isCheckName) {
  135. ElMessage.error(`文件上传格式错误`);
  136. return false
  137. }
  138. // 防止多次执行change
  139. const rawFile = file.raw;
  140. const list = props.acceptType.split(",");
  141. let acceptTypeList = list.map((its: string) => {
  142. return getType(its)
  143. })
  144. console.log(list, acceptTypeList, rawFile.type, 'acceptTypeListacceptTypeList');
  145. // 如果要检索的字符串值没有出现,则该方法返回 -1
  146. const ars = acceptTypeList.filter((q: string) => {
  147. return rawFile.type.indexOf(q) > -1
  148. })
  149. // 用于校验是否符合上传条件
  150. const type = props.acceptType
  151. if (ars.length < 1) {
  152. ElMessage.error(`仅支持格式为${type}的图片`);
  153. return false;
  154. } else if (rawFile.size / 1024 / 1024 > props.maxFileSize && props.isLimitSize) {
  155. ElMessage.error(`文件大小不能超过${props.maxFileSize}MB!`);
  156. const arr = [...waitFileList.value];
  157. waitFileList.value = arr.filter((item: any) => {
  158. return item.uid != rawFile.uid;
  159. });
  160. return false;
  161. }
  162. // else {
  163. // let formData = new FormData();
  164. // formData.append(props.formType, rawFile);
  165. // const loadingInstance = ElLoading.service({
  166. // text: "正在上传",
  167. // background: "rgba(0,0,0,.2)",
  168. // });
  169. // // 上传到服务器上面
  170. // const requestURL: string = props.action;
  171. // http.post(requestURL, formData)
  172. // .then(async (res: any) => {
  173. // if (res.code == 10000) {
  174. // loadingInstance.close();
  175. // waitFileList.value.push({ uid: file.uid, url: res.data })
  176. // emits("fileSuccess", res.data);
  177. // } else {
  178. // loadingInstance.close();
  179. // ElMessage.warning(`文件上传失败`);
  180. // }
  181. // })
  182. // .catch(() => {
  183. // loadingInstance.close();
  184. // // ElMessage.warning(`文件上传失败`);
  185. // });
  186. // }
  187. return true;
  188. };
  189. //限制图片尺寸
  190. const limitFileWH = (file) => {
  191. const isSize = new Promise(function (resolve, reject) {
  192. const reader = new FileReader();
  193. reader.onload = (event) => {
  194. const image = new Image();
  195. image.onload = () => {
  196. const { width, height, size } = image;
  197. if (width !== props.widthLimit || height !== props.heightLimit) {
  198. reject(image);
  199. } else {
  200. resolve(image);
  201. }
  202. };
  203. image.onerror = (error) => {
  204. reject(`图片加载错误:${error}`);
  205. };
  206. image.src = event.target.result;
  207. };
  208. reader.onerror = (error) => {
  209. reject(`文件读取错误:${error}`);
  210. };
  211. reader.readAsDataURL(file.raw);
  212. }).then((e) => {
  213. return true;
  214. }, (e) => {
  215. console.log(props.widthLimit);
  216. ElMessage.error({
  217. message: '上传文件的图片大小不合符标准,宽需要为' + props.widthLimit + 'px,高需要为' + props.heightLimit + 'px。当前上传图片的宽高分别为:' + e.width + 'px和' +
  218. e.height + 'px',
  219. })
  220. return false;
  221. });
  222. return isSize
  223. }
  224. // 校验上传文件格式
  225. const getType = (acceptType: string) => {
  226. let val = "";
  227. switch (acceptType) {
  228. case ".xls":
  229. val = "excel";
  230. break;
  231. case ".doc":
  232. val = "word";
  233. break;
  234. case ".pdf":
  235. val = "pdf";
  236. break;
  237. case ".zip":
  238. val = "zip";
  239. break;
  240. case ".xlsx":
  241. val = "sheet";
  242. break;
  243. case ".pptx":
  244. val = "presentation";
  245. break;
  246. case ".docx":
  247. val = "document";
  248. break;
  249. case ".text":
  250. val = "text";
  251. break;
  252. case ".jpeg":
  253. val = "jpeg";
  254. break;
  255. case ".png":
  256. val = "png";
  257. break;
  258. case ".gif":
  259. val = "gif";
  260. break;
  261. case ".mp4":
  262. val = "mp4";
  263. break;
  264. case ".mp3":
  265. val = "mp3";
  266. break;
  267. }
  268. return val
  269. };
  270. // 移除文件
  271. const removeFile = (file: any) => {
  272. const arr: any[] = [...waitFileList.value];
  273. waitFileList.value = arr.filter((its: any) => {
  274. console.log(its, file);
  275. return its.uid != file.uid;
  276. });
  277. console.log(file, 'file', arr, waitFileList.value);
  278. emits("fileRemove", file);
  279. };
  280. const handleDownLoad = (row: { ossFile: string }) => {
  281. const str = window.location.href.split("#")[0];
  282. const herf = str.slice(0, str.length - 1);
  283. window.location.href = herf + row.ossFile;
  284. };
  285. </script>
  286. <style lang="scss" scoped>
  287. .upload_wrap {
  288. .upload {
  289. width: auto;
  290. padding-bottom: 10px;
  291. }
  292. .tips {
  293. display: block;
  294. font-size: 14px;
  295. font-family: PingFangSC-Regular, PingFang SC;
  296. font-weight: 400;
  297. color: rgba(0, 0, 0, 0.65);
  298. }
  299. }
  300. :deep().el-upload__text {
  301. width: 106px;
  302. height: 32px;
  303. display: flex;
  304. align-items: center;
  305. justify-content: center;
  306. background: #ffffff;
  307. border: 1px solid rgba(0, 0, 0, 0.15);
  308. img {
  309. display: block;
  310. width: 14px;
  311. height: 14px;
  312. }
  313. span {
  314. font-size: 14px;
  315. padding-left: 6px;
  316. font-family: PingFangSC-Regular, PingFang SC;
  317. font-weight: 400;
  318. color: rgba(0, 0, 0, 0.65);
  319. }
  320. }
  321. .template_list {
  322. padding-bottom: 4px;
  323. }
  324. .template {
  325. display: flex;
  326. align-items: center;
  327. padding: 5px 0;
  328. span {
  329. line-height: 16px;
  330. }
  331. img {
  332. margin-right: 8px;
  333. width: 16px;
  334. height: 16px;
  335. }
  336. .documentName {
  337. margin-right: 12px;
  338. font-size: 14px;
  339. color: rgba(0, 0, 0, 0.65);
  340. }
  341. }
  342. </style>