|
@@ -0,0 +1,313 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import axios from "axios";
|
|
|
+import Sortable from "sortablejs";
|
|
|
+import UploadForm from "./form.vue";
|
|
|
+import { ref, computed } from "vue";
|
|
|
+import { useRouter } from "vue-router";
|
|
|
+import { message } from "@/utils/message";
|
|
|
+import type { UploadFile } from "element-plus";
|
|
|
+import { getKeyList, extractFields, downloadByData } from "@pureadmin/utils";
|
|
|
+
|
|
|
+import Add from "@iconify-icons/ep/plus";
|
|
|
+import Eye from "@iconify-icons/ri/eye-line";
|
|
|
+import Delete from "@iconify-icons/ri/delete-bin-7-line";
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: "PureUpload"
|
|
|
+});
|
|
|
+
|
|
|
+const fileList = ref([]);
|
|
|
+const router = useRouter();
|
|
|
+const curOpenImgIndex = ref(0);
|
|
|
+const dialogVisible = ref(false);
|
|
|
+
|
|
|
+const urlList = computed(() => getKeyList(fileList.value, "url"));
|
|
|
+const imgInfos = computed(() => extractFields(fileList.value, "name", "size"));
|
|
|
+
|
|
|
+const getImgUrl = name => new URL(`./imgs/${name}.jpg`, import.meta.url).href;
|
|
|
+const srcList = Array.from({ length: 3 }).map((_, index) => {
|
|
|
+ return getImgUrl(index + 1);
|
|
|
+});
|
|
|
+
|
|
|
+/** 上传文件前校验 */
|
|
|
+const onBefore = file => {
|
|
|
+ if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) {
|
|
|
+ message("只能上传图片");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ const isExceed = file.size / 1024 / 1024 > 2;
|
|
|
+ if (isExceed) {
|
|
|
+ message(`单个图片大小不能超过2MB`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/** 超出最大上传数时触发 */
|
|
|
+const onExceed = () => {
|
|
|
+ message("最多上传3张图片,请先删除在上传");
|
|
|
+};
|
|
|
+
|
|
|
+/** 移除上传的文件 */
|
|
|
+const handleRemove = (file: UploadFile) => {
|
|
|
+ fileList.value.splice(fileList.value.indexOf(file), 1);
|
|
|
+};
|
|
|
+
|
|
|
+/** 大图预览 */
|
|
|
+const handlePictureCardPreview = (file: UploadFile) => {
|
|
|
+ curOpenImgIndex.value = fileList.value.findIndex(img => img.uid === file.uid);
|
|
|
+ dialogVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const getUploadItem = () => document.querySelectorAll("#pure-upload-item");
|
|
|
+
|
|
|
+/** 缩略图拖拽排序 */
|
|
|
+const imgDrop = uid => {
|
|
|
+ const CLASSNAME = "el-upload-list";
|
|
|
+ const _curIndex = fileList.value.findIndex(img => img.uid === uid);
|
|
|
+ getUploadItem()?.[_curIndex]?.classList?.add(`${CLASSNAME}__item-actions`);
|
|
|
+ const wrapper: HTMLElement = document.querySelector(`.${CLASSNAME}`);
|
|
|
+ Sortable.create(wrapper, {
|
|
|
+ handle: `.${CLASSNAME}__item`,
|
|
|
+ onEnd: ({ newIndex, oldIndex }) => {
|
|
|
+ const oldFile = fileList.value[oldIndex];
|
|
|
+ fileList.value.splice(oldIndex, 1);
|
|
|
+ fileList.value.splice(newIndex, 0, oldFile);
|
|
|
+ // fix: https://github.com/SortableJS/Sortable/issues/232 (firefox is ok, but chromium is bad. see https://bugs.chromium.org/p/chromium/issues/detail?id=410328)
|
|
|
+ getUploadItem().forEach(ele => {
|
|
|
+ ele.classList.remove(`${CLASSNAME}__item-actions`);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 下载图片 */
|
|
|
+const onDownload = () => {
|
|
|
+ [
|
|
|
+ { name: "巴旦木.jpeg", type: "img" },
|
|
|
+ { name: "恭喜发财.png", type: "img" },
|
|
|
+ { name: "可爱动物.gif", type: "gif" },
|
|
|
+ { name: "pure-upload.csv", type: "other" },
|
|
|
+ { name: "pure-upload.txt", type: "other" }
|
|
|
+ ].forEach(img => {
|
|
|
+ axios
|
|
|
+ .get(`https://xiaoxian521.github.io/hyperlink/${img.type}/${img.name}`, {
|
|
|
+ responseType: "blob"
|
|
|
+ })
|
|
|
+ .then(({ data }) => {
|
|
|
+ downloadByData(data, img.name);
|
|
|
+ });
|
|
|
+ });
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-card shadow="never">
|
|
|
+ <template #header>
|
|
|
+ <div class="card-header">
|
|
|
+ <el-link
|
|
|
+ v-tippy="{
|
|
|
+ content: '点击查看详细文档'
|
|
|
+ }"
|
|
|
+ href="https://element-plus.org/zh-CN/component/upload.html"
|
|
|
+ target="_blank"
|
|
|
+ style="font-size: 16px; font-weight: 800"
|
|
|
+ >
|
|
|
+ 文件上传
|
|
|
+ </el-link>
|
|
|
+ <span class="header-right">
|
|
|
+ <el-button class="ml-1" text bg @click="onDownload">
|
|
|
+ 点击下载安全文件进行上传测试
|
|
|
+ </el-button>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <p class="mb-4">
|
|
|
+ 综合示例<span class="text-[14px]">
|
|
|
+ ( <span class="text-[red]">自动上传</span>
|
|
|
+ 、拖拽上传、拖拽排序、设置请求头、上传进度、大图预览、多选文件、最大文件数量、文件类型限制、文件大小限制、删除文件)
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <p v-show="fileList.length > 0" class="mb-4">
|
|
|
+ {{ imgInfos }}
|
|
|
+ </p>
|
|
|
+ <el-upload
|
|
|
+ v-model:file-list="fileList"
|
|
|
+ drag
|
|
|
+ multiple
|
|
|
+ class="pure-upload"
|
|
|
+ list-type="picture-card"
|
|
|
+ accept="image/jpeg,image/png,image/gif"
|
|
|
+ action="https://run.mocky.io/v3/3aa761d7-b0b3-4a03-96b3-6168d4f7467b"
|
|
|
+ :limit="3"
|
|
|
+ :headers="{ Authorization: 'eyJhbGciOiJIUzUxMiJ9.admin' }"
|
|
|
+ :on-exceed="onExceed"
|
|
|
+ :before-upload="onBefore"
|
|
|
+ >
|
|
|
+ <IconifyIconOffline :icon="Add" class="m-auto mt-4" width="30" />
|
|
|
+ <template #file="{ file }">
|
|
|
+ <div
|
|
|
+ v-if="file.status == 'ready' || file.status == 'uploading'"
|
|
|
+ class="mt-[35%] m-auto"
|
|
|
+ >
|
|
|
+ <p class="font-medium">文件上传中</p>
|
|
|
+ <el-progress
|
|
|
+ class="mt-2"
|
|
|
+ :stroke-width="2"
|
|
|
+ :text-inside="true"
|
|
|
+ :show-text="false"
|
|
|
+ :percentage="file.percentage"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div v-else @mouseenter.stop="imgDrop(file.uid)">
|
|
|
+ <img
|
|
|
+ class="el-upload-list__item-thumbnail select-none"
|
|
|
+ :src="file.url"
|
|
|
+ />
|
|
|
+ <span
|
|
|
+ id="pure-upload-item"
|
|
|
+ :class="[
|
|
|
+ 'el-upload-list__item-actions',
|
|
|
+ fileList.length > 1 && '!cursor-move'
|
|
|
+ ]"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ title="查看"
|
|
|
+ class="hover:text-primary"
|
|
|
+ @click="handlePictureCardPreview(file)"
|
|
|
+ >
|
|
|
+ <IconifyIconOffline
|
|
|
+ :icon="Eye"
|
|
|
+ class="hover:scale-125 duration-100"
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ class="el-upload-list__item-delete"
|
|
|
+ @click="handleRemove(file)"
|
|
|
+ >
|
|
|
+ <span title="移除" class="hover:text-[var(--el-color-danger)]">
|
|
|
+ <IconifyIconOffline
|
|
|
+ :icon="Delete"
|
|
|
+ class="hover:scale-125 duration-100"
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ <!-- 有时文档没写并不代表没有,多看源码好处多多😝 https://github.com/element-plus/element-plus/tree/dev/packages/components/image-viewer/src (emm...这让我想起刚开始写这个项目时,很多东西只有英文或者没有文档,需要看源码时,想笑🥹。那些美好时光都给这些坑了,giao) -->
|
|
|
+ <el-image-viewer
|
|
|
+ v-if="dialogVisible"
|
|
|
+ :initialIndex="curOpenImgIndex"
|
|
|
+ :url-list="urlList"
|
|
|
+ :zoom-rate="1.2"
|
|
|
+ :max-scale="7"
|
|
|
+ :min-scale="0.2"
|
|
|
+ @close="dialogVisible = false"
|
|
|
+ @switch="index => (curOpenImgIndex = index)"
|
|
|
+ />
|
|
|
+ <!-- 将自定义内容插入到body里,有了它在图片预览的时候,想插入个分页器或者别的东东在预览区某个位置就很方便咯(用户需求可以很灵活,开源组件库几乎不可能尽善尽美,很多时候寻找别的解决途径或许更好) -->
|
|
|
+ <teleport to="body">
|
|
|
+ <div
|
|
|
+ v-if="fileList[curOpenImgIndex] && dialogVisible"
|
|
|
+ effect="dark"
|
|
|
+ round
|
|
|
+ size="large"
|
|
|
+ type="info"
|
|
|
+ class="img-name"
|
|
|
+ >
|
|
|
+ <p class="text-[#fff] dark:text-black">
|
|
|
+ {{ fileList[curOpenImgIndex].name }}
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </teleport>
|
|
|
+ <p class="el-upload__tip">
|
|
|
+ 可拖拽上传最多3张单个不超过2MB且格式为jpeg/png/gif的图片
|
|
|
+ </p>
|
|
|
+ <el-divider />
|
|
|
+
|
|
|
+ <p class="mb-4 mt-4">
|
|
|
+ 结合表单校验进行<span class="text-[red]">手动上传</span>
|
|
|
+ <span class="text-[14px]">
|
|
|
+ (可先打开浏览器控制台找到Network,然后填写表单内容后点击点提交观察请求变化)
|
|
|
+ </span>
|
|
|
+ </p>
|
|
|
+ <div class="flex justify-between">
|
|
|
+ <UploadForm />
|
|
|
+ <div>
|
|
|
+ <p class="text-center">上传接口相关截图</p>
|
|
|
+ <el-image
|
|
|
+ class="w-[200px] rounded-md"
|
|
|
+ :src="srcList[0]"
|
|
|
+ :preview-src-list="srcList"
|
|
|
+ fit="cover"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-divider />
|
|
|
+
|
|
|
+ <div class="flex flex-wrap">
|
|
|
+ <p>
|
|
|
+ 裁剪、上传头像请参考
|
|
|
+ <span
|
|
|
+ class="font-bold text-[18x] cursor-pointer hover:text-[red]"
|
|
|
+ @click="router.push({ name: 'SystemUser' })"
|
|
|
+ >
|
|
|
+ 系统管理-用户管理
|
|
|
+ </span>
|
|
|
+ 表格操作栏中的上传头像功能
|
|
|
+ </p>
|
|
|
+ <p class="text-[red] text-[12px] flex flex-auto items-center justify-end">
|
|
|
+ 免责声明:上传接口使用免费开源的
|
|
|
+ <el-link
|
|
|
+ href="https://designer.mocky.io/"
|
|
|
+ target="_blank"
|
|
|
+ style="font-size: 16px; font-weight: 800"
|
|
|
+ >
|
|
|
+ Mocky
|
|
|
+ </el-link>
|
|
|
+ <span class="font-bold text-[18x]"> 请不要上传重要信息 </span
|
|
|
+ >,如果造成任何损失,我们概不负责
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+:deep(.card-header) {
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ .header-right {
|
|
|
+ display: flex;
|
|
|
+ flex: auto;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.pure-upload) {
|
|
|
+ .el-upload-dragger {
|
|
|
+ background-color: transparent;
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.img-name {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 80px;
|
|
|
+ left: 50%;
|
|
|
+ z-index: 4000;
|
|
|
+ padding: 5px 23px;
|
|
|
+ background-color: var(--el-text-color-regular);
|
|
|
+ border-radius: 22px;
|
|
|
+ transform: translateX(-50%);
|
|
|
+
|
|
|
+ /** 将下面的 left: 50%; bottom: 80px; transform: translateX(-50%); 注释掉
|
|
|
+ * 解开下面 left: 40px; top: 40px; 注释,体验不一样的感觉。啊?还是差强人意,自己调整位置吧🥹
|
|
|
+ */
|
|
|
+ // left: 40px;
|
|
|
+ // top: 40px;
|
|
|
+}
|
|
|
+</style>
|