| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- <template>
- <div class="sc-upload-multiple">
- <el-upload ref="uploader" list-type="picture-card"
- v-model:file-list="defaultFileList"
- action=""
- accept="image/gif, image/jpeg, image/png, video/mp4 , video/avi"
- :limit="limit"
- :multiple="multiple"
- :disabled="disabled"
- show-file-list
- :http-request="request"
- :before-upload="before"
- :on-success="success"
- :on-error="error"
- :on-exceed="handleExceed">
- <slot>
- <el-icon><el-icon-plus /></el-icon>
- </slot>
- <template #tip>
- <div v-if="tip" class="el-upload__tip">{{ tip }}</div>
- </template>
- <template #file="{ file }">
- <div class="sc-upload-list-item">
- <el-image v-if="isImage(file.mineType)" class="el-upload-list__item-thumbnail" :src="'/api/folder/' + file.path" :preview-src-list="preview" fit="cover" preview-teleported :z-index="9999">
- <template #placeholder>
- <div class="sc-upload-multiple-image-slot">Loading...</div>
- </template>
- </el-image>
- <sc-video v-if="isVideo(file.mineType)" :src="'/api/folder/' + file.path" showMask @play="videoPlay(file)"></sc-video>
- <div v-if="!disabled && file.status == 'success'" class="sc-upload__item-actions">
- <el-button class="download" :loading="loading" type="primary" @click="handleDownload(file)">
- <sc-iconify icon="ant-design:download-outlined"></sc-iconify>
- </el-button>
- <el-button class="del" :loading="loading" type="danger" @click="handleRemove(file)">
- <sc-iconify icon="ant-design:delete-outlined"></sc-iconify>
- </el-button>
- </div>
- <div v-if="file.status == 'ready' || file.status == 'uploading'" class="sc-upload__item-progress">
- <el-progress :percentage="file.percentage" text-inside :stroke-width="16" />
- </div>
- </div>
- </template>
- </el-upload>
- <span style="display:none!important"><el-input v-model="value"></el-input></span>
- </div>
- <file-viewer v-if="showViewer" ref="fileViewer" @closed="showViewer = false"></file-viewer>
- </template>
- <script>
- import { fileTypes } from "./main";
- export default {
- props: {
- modelValue: { type: Array, default: () => [] },
- tip: { type: String, default: "" },
- maxSize: { type: Number, default: 50 },
- limit: { type: Number, default: 0 },
- multiple: { type: Boolean, default: true },
- disabled: { type: Boolean, default: false },
- onSuccess: { type: Function, default: () => { return true } }
- },
- data() {
- return {
- value: "",
- defaultFileList: [],
- loading: false,
- showViewer: false
- }
- },
- watch: {
- modelValue(val) {
- if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
- this.defaultFileList = val;
- this.value = val;
- }
- },
- defaultFileList: {
- deep: true,
- handler(val) {
- this.$emit("update:modelValue", this.formatArr(val));
- this.value = val.map(v => v.path).join(",");
- }
- }
- },
- computed: {
- preview() {
- return this.defaultFileList.map(v => "/api/folder/" + v.path);
- }
- },
- mounted() {
- this.defaultFileList = this.modelValue;
- this.value = this.modelValue;
- },
- methods: {
- isImage(type) {
- return fileTypes[type] == "image"
- },
- isVideo(type) {
- return fileTypes[type] == "video"
- },
- // 格式化数组值
- formatArr(arr) {
- return arr.map(item => ({ id: item.id, name: item.name, mineType: item.mineType, path: item.path }));
- },
- before(file) {
- if (!this.isImage(file.type) && !this.isVideo(file.type)) {
- this.$message.warning({ title: "上传文件警告", message: "选择的文件非图像类/视频类文件" });
- return false;
- }
- const maxSize = file.size / 1024 / 1024 < this.maxSize;
- if (!maxSize) {
- this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
- return false;
- }
- },
- success(res, file) {
- let os = this.onSuccess(res, file);
- if (os != undefined && os == false) return false;
-
- file.name = res.fileName;
- file.path = res.path;
- file.mineType = res.mineType;
- },
- error(message) {
- this.$notify.error({ title: "上传文件未成功", message });
- },
- beforeRemove({ id, name }) {
- return this.$confirm(`是否移除 ${name}? 此操作不可逆!`, "提示", {
- type: "warning",
- confirmButtonText: "移除"
- }).then(() => {
- if (id) {
- this.$API.common.folder.rm(id).then(res => {
- if (res.code == 200) return true;
- else return false;
- }).catch(() => {
- return false;
- });
- } return true;
- }).catch(() => {
- return false;
- });
- },
-
- handleRemove(file) {
- this.$refs.uploader.handleRemove(file);
- },
- handleExceed() {
- this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`);
- },
- request(param) {
- const data = new FormData();
- data.append(param.filename, param.file);
- this.$API.common.folder.up(data, {
- onUploadProgress: e => {
- const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10)
- param.onProgress({ percent });
- }
- }).then(res => {
- if (res.code == 200) param.onSuccess({ path: res.expands.file, fileName: param.file.name, mineType: param.file.type })
- else param.onError(res.message || "未知错误");
- }).catch(err => param.onError(err));
- },
- videoPlay(file) {
- this.showViewer = true;
- nextTick(() => this.$refs.fileViewer.init(file));
- },
- handleDownload(file) {
- this.loading = true;
- this.$API.common.folder.download(file.path).then(res => {
- this.loading = false;
- const a = document.createElement("a");
- const blob = new Blob([res.data], { type: file.mineType });
- a.download = this.file.name;
- a.href = URL.createObjectURL(blob);
- a.click();
- }).catch(() => this.loading = false);
- }
- }
- }
- </script>
- <style scoped>
- .el-form-item.is-error .sc-upload-multiple:deep(.el-upload--picture-card) {
- border-color: var(--el-color-danger);
- }
- :deep(.el-upload-list__item) {
- transition: none;
- border-radius: 0;
- }
- .sc-upload-multiple:deep(.el-upload-list__item.el-list-leave-active) {
- position: static !important;
- }
- .sc-upload-multiple:deep(.el-upload--picture-card) {
- border-radius: 0;
- }
- .sc-upload-list-item {
- width: 100%;
- height: 100%;
- position: relative;
- }
- .sc-upload-multiple .el-image {
- display: block;
- }
- .sc-upload-multiple .el-image:deep(img) {
- -webkit-user-drag: none;
- }
- .sc-upload-multiple-image-slot {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- height: 100%;
- font-size: 12px;
- }
- .sc-upload-multiple .el-upload-list__item:hover .sc-upload__item-actions {
- display: block;
- }
- .sc-upload__item-actions {
- position: absolute;
- top: 0;
- right: 0;
- display: none;
- }
- .sc-upload__item-actions span {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 25px;
- height: 25px;
- cursor: pointer;
- color: #fff;
- }
- .sc-upload__item-actions span i {
- font-size: 12px;
- }
- .sc-upload__item-actions .del {
- background: #f56c6c;
- }
- .sc-upload__item-progress {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- background-color: var(--el-overlay-color-lighter);
- }
- </style>
|