当前位置: 首页 > article >正文

Element Plus图片上传组件二次扩展

Element Plus 的图片上传组件主要通过 <el-upload> 实现,该组件支持多种配置和功能,如文件类型限制、文件大小限制、自动上传、手动上传、预览、删除等。以下是对 Element Plus 图片上传组件的详细介绍和使用示例:

功能概述

  • 文件类型限制:可以指定允许上传的文件类型,如图片(jpg, png等)。
  • 文件大小限制:可以限制上传文件的大小。
  • 自动上传:选择文件后自动上传到服务器。
  • 手动上传:选择文件后不立即上传,而是通过按钮手动触发上传。
  • 预览:上传前或上传后可以预览图片。
  • 删除:已上传的文件可以删除。

使用示例

以下是一个基本的 Element Plus 图片上传组件的使用示例,包括前端 Vue 组件代码和后端 Spring Boot 接口代码。

前端 Vue 组件基本调用

<template>  
  <el-upload  
    class="upload-demo"  
    action="http://localhost:8080/upload/image" <!-- 后端接收上传文件的接口 -->  
    :on-preview="handlePreview"  
    :on-remove="handleRemove"  
    :file-list="fileList"  
    :auto-upload="false" <!-- 设置为false,则不自动上传 -->  
    :http-request="customUpload"  
    list-type="picture-card"  
  >  
    <i class="el-icon-plus"></i>  
  </el-upload>  
  <el-button slot="trigger" size="small" type="primary">选取文件</el-button>  
  <el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>  
  <el-dialog  
    title="预览"  
    :visible.sync="dialogVisible"  
    width="30%"  
  >  
    <img :src="dialogImageUrl" class="dialog-image" />  
  </el-dialog>  
</template>  
  
<script>  
import { ref } from 'vue';  
  
export default {  
  setup() {  
    const fileList = ref([]);  
    const dialogImageUrl = ref('');  
    const dialogVisible = ref(false);  
  
    const handlePreview = (file) => {  
      dialogImageUrl.value = file.url;  
      dialogVisible.value = true;  
    };  
  
    const handleRemove = (file, fileList) => {  
      console.log(file, fileList);  
    };  
  
    const customUpload = (options) => {  
      // 这里可以自定义上传逻辑,比如使用axios发送请求  
      // 这里只是简单模拟  
      console.log('自定义上传', options.file);  
      // 假设上传成功,更新fileList  
      // fileList.value.push({ name: options.file.name, url: '上传后的文件URL' });  
    };  
  
    const submitUpload = () => {  
      // 这里可以调用自定义的上传函数,或者将fileList中的文件逐个上传  
      // 这里只是示例,没有实现具体的上传逻辑  
      console.log('提交上传', fileList.value);  
    };  
  
    return {  
      fileList,  
      dialogImageUrl,  
      dialogVisible,  
      handlePreview,  
      handleRemove,  
      customUpload,  
      submitUpload,  
    };  
  },  
};  
</script>  
  
<style>  
.dialog-image {  
  width: 100%;  
}  
</style>

DIY可视化二次扩展

输入上传式图片组件

<template>
	<el-input
		type="text"
		ref="formImg"
		:class="url && type == 'image' ? 'diygw-img-input' : ''"
		v-model="url"
		clearable
		:placeholder="'请选择' + title"
	>
		<template #append>
			<el-image
				:src="url"
				v-if="url && type == 'image'"
				style="width: 30px; height: 30px"
				:preview-teleported="true"
				:preview-src-list="[url]"
			></el-image>
			<el-button @click="handleStorage"> 选择{{ title }} </el-button>
		</template>
	</el-input>
	<diy-storage ref="storage" :type="type" :accept="accept" :limit="1" @confirm="getAttachmentFileList"></diy-storage>
</template>

<script lang="ts" setup>
import { nextTick, ref, defineEmits, watch } from 'vue';
import { useVModel } from '@vueuse/core';
import DiyStorage from './storage.vue';

const props = defineProps({
	// 外部v-model值
	modelValue: {
		type: String,
		default: '',
	},
	title: {
		type: String,
		default: '',
	},
	type: {
		type: String,
		default: 'image',
	},
	accept: {
		type: String,
		default: 'image/*',
	},
});

const storage = ref(null);

const emit = defineEmits(['update:modelValue', 'change', 'blur']);

const url = useVModel(props, 'modelValue', emit);

const handleStorage = () => {
	nextTick(() => {
		storage.value!.handleStorageDlg('', '上传' + props.title);
	});
};
watch(url, () => {
	emit('change');
	emit('blur');
});
const formImg: EmptyObjectType = ref(null);
// 获取商品相册资源
const getAttachmentFileList = (files = <any>[]) => {
	if (!files.length) {
		return;
	}
	url.value = files[0].url;
	nextTick(() => {
		formImg.value?.input.focus();
		formImg.value?.input.blur();
	});
};
</script>

<style lang="scss" scoped>
.diygw-img-input ::v-deep(.el-input-group__append) {
	padding-left: 0px;
}
.diygw-img-input {
	::v-deep(.el-image) {
		margin-right: 15px;
	}
}
.sortable-ghost {
	opacity: 0;
}
</style>

多图式上传

<template>
	<div style="line-height: 0; width: 100%" class="flex justify-start">
		<VueDraggable
			tag="ul"
			v-bind="{ animation: 200, disabled: false, ghostClass: 'ghost' }"
			v-model="imageList"
			class="el-upload-list el-upload-list--picture-card"
			group="subform"
			item-key="url"
		>
			<li v-for="(item, index) in imageList" class="el-upload-list__item" :style="`width:${width}px; height:${height}px;`">
				<el-image :style="`width:100%; height:100%`" :src="item" fit="contain" />
				<span v-show="!state.isdrag" :class="{ 'el-upload-list__item-actions': true, 'diy-cm': isMove }">
					<SvgIcon style="color: #fff" name="ele-Plus" class="margin-right" @click="handleStorage(index)" :size="20" />
					<SvgIcon style="color: #fff" name="ele-Delete" @click="remove(index)" :size="20" />
				</span>
			</li>
		</VueDraggable>

		<div
			v-if="limit == 0 || (imageList.length == 0 && limit == 1)"
			tabindex="0"
			style="margin-bottom: 8px"
			class="el-upload el-upload--picture-card"
			:style="`width:${width}px; height:${height}px;`"
			@click="handleStorage(-1)"
		>
			<SvgIcon name="ele-Plus" :size="20" />
		</div>
		<diy-storage ref="storage" :limit="limit" @confirm="getAttachmentFileList"></diy-storage>
	</div>
</template>

<script setup>
import { nextTick, ref, reactive } from 'vue';
import { useVModel } from '@vueuse/core';
import { VueDraggable } from 'vue-draggable-plus';

import { ElMessageBox, useFormItem } from 'element-plus';

import DiyStorage from './storage.vue';
const { formItem } = useFormItem();

const props = defineProps({
	// 外部v-model值
	modelValue: {
		type: Array,
		default: () => [],
	},
	width: {
		default: 80,
	},
	height: {
		default: 80,
	},
	isMove: {
		default: true,
	},
	limit: {
		default: 0,
	},
	customer: {
		type: Object,
		required: false,
		default: () => {},
	},
	contractId: {
		required: false,
		default: '',
	},
	storageType: {
		type: String,
		required: false,
		default: '',
	},
});

const state = reactive({
	isdrag: false,
	index: -1,
});
const storage = ref();

const emit = defineEmits(['update:modelValue']);

const imageList = useVModel(props, 'modelValue', emit);

const handleStorage = (index = -1) => {
	state.index = index;
	nextTick(() => {
		storage.value.handleStorageDlg('', '上传图片');
	});
};
// 获取商品相册资源
const getAttachmentFileList = (files = []) => {
	if (!files.length) {
		return;
	}
	if (state.index != -1) {
		imageList.value[state.index] = files[state.index].url;
	} else {
		files.forEach((item) => {
			imageList.value.push(item.url);
		});
	}
	nextTick(() => {
		formItem?.validate?.('blur').catch((err) => {
			console.log(err);
		});
		formItem?.validate?.('change').catch((err) => {
			console.log(err);
		});
	});
};

const remove = (index = -1) => {
	ElMessageBox({
		message: '是否删除该文件?',
		title: '警告',
		showCancelButton: true,
		confirmButtonText: '确定',
		cancelButtonText: '取消',
		type: 'warning',
	}).then(function () {
		imageList.value.splice(index, 1);
	});
};
</script>

<style lang="scss" scoped>
.sortable-ghost {
	opacity: 0;
}
</style>

上传组件调用

<template>
	<el-dialog :title="title" top="5vh" v-model="visible" append-to-body center width="800px">
		<div class="flex storages">
			<div class="categorys diy-tree file-border flex flex-direction-column">
				<el-button type="primary" @click="handleAdd()">新增目录</el-button>
				<div class="mt-1 text-center" plain :class="[!form.parentId ? 'selected' : '']" @click="selectCategory('')">全部</div>
				<div
					class="mt-1 p-1 text-center"
					plain
					:key="item"
					:class="[form.parentId == item.storageId ? 'selected' : '']"
					@click="selectCategory(item.storageId)"
					v-for="item in categorys"
				>
					{{ item.name }}
				</div>
			</div>
			<div class="flex1 file-border">
				<!-- 文件列表开始 -->
				<div class="flex justify-between">
					<div class="diy-mb-20">
						<el-input
							v-model="form.name"
							label-width="0px"
							placeholder="输入名称搜索"
							style="width: 250px"
							@keyup.enter.native="handleSearch()"
							@clear="handleSearch()"
							:clearable="true"
						>
							<template #suffix>
								<SvgIcon @click="handleSearch" name="ele-Search" :size="20" />
							</template>
						</el-input>
					</div>

					<diy-upload
						ref="upload"
						:upload-tip="uploadConfig.uploadTip"
						:multiple="uploadConfig.multiple"
						:type="type"
						:accept="accept"
						:limit="uploadConfig.limit"
						:parent-id="form.parentId"
						@confirm="_getUploadFileList"
					></diy-upload>
				</div>
				<div class="files" :max="limit !== 0 ? limit : 100">
					<div class="file-list" v-loading="loading">
						<div :key="index" v-for="(item, index) in currentTableData" class="item" :data-label="item.name" :class="item.selectclz">
							<div
								class="file-image"
								:style="{
									backgroundImage: 'url(' + getImageThumb(item) + ')',
								}"
								@click="selectFile(item)"
							></div>
							<div class="file-name">
								{{ item.name }}
							</div>
							<div class="mask" @click="selectFile(item)">
								<SvgIcon name="ele-Check" :size="20" />
							</div>
							<div class="el-dropdown" @click="handleDelete(item.storageId)">
								<SvgIcon class="delete" name="ele-Delete" :size="20" />
							</div>
						</div>
					</div>
				</div>

				<!-- 翻页开始 -->
				<diy-pagefooter
					style="margin: 0; padding: 20px 0 0 0"
					:current="page.current"
					:size="page.size"
					:total="page.total"
					:is-size="false"
					@change="handlePaginationChange"
				/>
			</div>
		</div>

		<!-- 确认,取消 -->
		<template #footer v-if="isSelect == '1'" class="dialog-footer">
			<div style="float: left; font-size: 13px">
				<span v-if="checkList.length > limit && limit !== 0" style="color: #f56c6c">
					当前已选 {{ checkList.length }} 个,最多允许选择 {{ limit }} 个文件
				</span>
				<span v-else>当前已选 {{ checkList.length }} 个文件</span>
			</div>
			<el-button @click="visible = false">取消</el-button>
			<el-button type="primary" :loading="loadingCollection" :disabled="checkList.length > limit && limit !== 0" @click="handleConfirm"
				>确定</el-button
			>
		</template>
	</el-dialog>

	<el-dialog :title="nameMap[nameStatus]" v-model="nameFormVisible" append-to-body center top="5vh" width="600px">
		<el-form :model="nameForm" :rules="rules" ref="refform" label-width="50px" label-position="left" @submit.native.prevent>
			<el-form-item label="名称" prop="name">
				<el-input v-model="nameForm.name" placeholder="请输入目录名称" @keyup.enter.native="nameStatus === 'add' ? add() : rename()" ref="input" />
			</el-form-item>
		</el-form>

		<template #footer>
			<el-button @click="nameFormVisible = false">取消</el-button>

			<el-button v-if="nameStatus === 'add'" type="primary" :loading="dialogLoading" @click="add">确定</el-button>

			<el-button v-else type="primary" :loading="dialogLoading" @click="rename">修改</el-button>
		</template>
	</el-dialog>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, nextTick, toRefs } from 'vue';
import { listData, listAllData, addData, delData } from '@/api/index';
import { ElMessageBox, ElMessage } from 'element-plus';
import DiyUpload from './upload.vue';
import DiyPagefooter from './pagefooter.vue';

export default defineComponent({
	name: 'DiyStorage',
	components: {
		DiyUpload,
		DiyPagefooter,
	},
	props: {
		type: {
			default: 'image',
		},
		accept: {
			default: 'image/*',
		},
		// 最大选择数(0表示不限制)
		limit: {
			type: Number,
			required: false,
			default: 0,
		},
		isSelect: {
			default: '1',
		},
	},
	setup(props, { emit }) {
		const data = reactive({
			baseUrl: '',
			title: '',
			isLoad: false,
			icontype: 'default',
			visible: false,
			loading: false,
			uploadConfig: {
				uploadTip: '请选择文件进行上传,',
				multiple: true,
				accept: 'image/*',
				limit: 0,
				type: 'image',
				replace: false,
			},
			storageType: props.type,
			source: '',
			loadingCollection: false,
			checkList: [],
			categorys: [],
			syscategorys: [],
			currentTableData: [],
			currentSysTableData: [],
			searchSysTableData: [],
			dialogLoading: false,
			nameForm: {
				type: '',
				name: undefined,
				parentId: undefined,
			},
			nameFormVisible: false,
			nameStatus: 'edit',
			nameMap: {
				edit: '重命名',
				add: '新增目录',
			},
			iconfonturl: '',
			rules: {
				name: [
					{
						required: true,
						message: '目录名称不能为空',
						trigger: 'blur',
					},
					{
						max: 255,
						message: '长度不能大于 255 个字符',
						trigger: 'blur',
					},
				],
			},
			form: {
				name: undefined,
				parentId: undefined,
				order_type: 'desc',
				order_field: 'storageId',
			},
			sysform: {
				parentId: 'icon1',
				name: '',
				type: 'system',
			},
			page: {
				current: 1,
				size: 15,
				total: 0,
			},
		});

		const upload = ref(null);

		const getImageThumb = (item: any) => {
			if (item.type == 'mp3') {
				return '/static/images/mp3.png';
			} else if (item.type == 'mp4') {
				return '/static/images/mp3.png';
			} else if (item.type == 'image' || item.type == 'scene') {
				return item.url;
			} else {
				return '/static/images/file.png';
			}
		};

		const handleStorageDlg = (source = '', title = '') => {
			data.title = title ? title : '文件管理';
			// if (type == 'mp3') {
			// 	data.uploadConfig.accept = '.mp3';
			// } else if (type == 'mp4') {
			// 	data.uploadConfig.accept = '.mp4';
			// } else {
			// 	data.uploadConfig.accept = 'image/*';
			// }
			data.visible = true;
			data.storageType = props.type;
			data.source = source;
			data.checkList = [];
			data.currentTableData.forEach((item: any) => {
				item.selectclz = '';
			});

			if (!data.isLoad) {
				data.loadingCollection = false;
				data.currentTableData = [];
				data.uploadConfig.type = props.type;
				handleSubmit();
				nextTick(() => {
					getStorageDirectory();
					if (upload.value) {
						upload.value?.handleClose();
					}
				});
			}
		};
		const handlePaginationChange = (val: any) => {
			data.page = val;
			nextTick(() => {
				handleSubmit();
			});
		};
		const handleSearch = () => {
			data.page.current = 1;
			handleSubmit();
		};

		const handleSubmit = () => {
			data.loading = true;
			listData('/sys/storage', {
				...data.form,
				type: data.storageType,
				pageNum: data.page.current,
				pageSize: data.page.size,
			})
				.then((res) => {
					data.currentTableData = res.rows || [];
					data.page.total = res.total;
					data.isLoad = true;
				})
				.finally(() => {
					data.loading = false;
				});
		};

		const handleSysSubmit = () => {
			data.loading = true;
			listData('/sys/storage', {
				...data.sysform,
			})
				.then((res) => {
					data.currentSysTableData = res.rows || [];
					data.searchSysTableData = res.rows || [];
				})
				.finally(() => {
					data.loading = false;
				});
		};

		const handleSyscatesSubmit = () => {
			data.loading = true;
			listData('/sys/storage', {
				...data.sysform,
			})
				.then((res) => {
					data.syscategorys = res.rows || [];
					data.sysform.type = 'system';
					data.sysform.parentId = res.rows[0].storageId;
					handleSysSubmit();
				})
				.finally(() => {
					data.loading = false;
				});
		};

		const handleSysSearch = () => {
			let findData = data.currentSysTableData.filter((item: any) => {
				return item.name.indexOf(data.sysform.name) >= 0 || item.cname.indexOf(data.sysform.name) >= 0;
			});
			data.searchSysTableData = findData;
		};

		const handleAllSearch = () => {
			if (data.loading) {
				ElMessage.error('点击过快,正在加载数据');
				return;
			}
			data.sysform.parentId = '';
			handleSysSubmit();
		};

		const handleConfirm = () => {
			if (data.checkList.length <= 0) {
				emit('confirm', [], data.source);
				data.visible = false;
				return;
			}
			let checkList = data.checkList;
			// let finddata = data.currentTableData.filter((item) => {
			//   return checkList.includes(item.storageId);
			// });
			// let finddata2 = data.currentSysTableData.filter((item) => {
			//   return checkList.includes(item.storageId);
			// });
			// finddata = finddata.concat(finddata2)
			data.loadingCollection = false;
			//data.checkList = [];
			data.visible = false;
			emit('confirm', checkList || [], data.source);
		};
		// 上传文件
		// const handleUpload = () => {
		// 	upload.value.handleUploadDlg();
		// };

		const selectCategory = (id: any) => {
			if (data.loading) {
				ElMessage.error('点击过快,正在加载数据');
				return;
			}
			data.form.parentId = id;
			handleSubmit();
		};

		const selectSystemCategory = (item: any) => {
			if (data.loading) {
				ElMessage.error('点击过快,正在加载数据');
				return;
			}
			data.sysform.name = '';
			data.iconfonturl = item.url;
			data.sysform.parentId = item.storageId;
			handleSysSubmit();
		};

		const selectFile = (item: any) => {
			if (props.limit === 1) {
				data.currentSysTableData.forEach((item: any) => {
					item.selectclz = '';
				});
				data.currentTableData.forEach((item: any) => {
					item.selectclz = '';
				});
				let index = data.checkList.findIndex((checkitem: any) => {
					return checkitem == item.storageId;
				});
				if (index >= 0) {
					data.checkList.splice(index, 1);
					item.selectclz = '';
				} else if (data.checkList.length > 0) {
					data.checkList.splice(0, 1, item);
					item.selectclz = 'active';
				} else {
					data.checkList.push(item);
					item.selectclz = 'active';
				}
			} else {
				let index = data.checkList.findIndex((checkitem: any) => {
					return checkitem.storageId == item.storageId;
				});
				if (index >= 0) {
					data.checkList.splice(index, 1);
					item.selectclz = '';
				} else {
					data.checkList.push(item);
					item.selectclz = 'active';
				}
			}
		};

		// 批量删除
		const handleDelete = (val: any) => {
			const storageId = val ? [val] : data.checkList;
			if (storageId.length === 0) {
				ElMessage.error('请选择要操作的数据');
				return;
			}
			ElMessageBox.confirm('确定要执行该操作吗?', '提示', {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				type: 'warning',
				closeOnClickModal: false,
			})
				.then(() => {
					delData('/sys/storage', storageId).then(() => {
						for (let i = data.currentTableData.length - 1; i >= 0; i--) {
							if (storageId.indexOf(data.currentTableData[i].storageId) !== -1) {
								data.currentTableData.splice(i, 1);
							}
						}
						ElMessage.success('操作成功');
					});
				})
				.catch(() => {});
		};
		// 文件上传成功后处理
		const _getUploadFileList = (files: any) => {
			for (const value of files) {
				let stroage = value.response.data;
				let index = data.currentTableData.findIndex((item: any) => {
					return item.url == stroage.url;
				});
				if (index == 0) {
					continue;
				}
				if (index > 0) {
					data.currentTableData.splice(index, 1);
				}
				data.currentTableData.splice(0, 0, stroage);
			}
		};

		const refform = ref();
		const refinput = ref();

		// 获取可选择目录
		const getStorageDirectory = () => {
			//if (!this.directoryList.length) {
			listAllData('/sys/storage', { type: 'category' }).then((res) => {
				data.categorys = res.rows;
			});
		};

		const add = () => {
			refform.value.validate((valid: boolean) => {
				if (valid) {
					data.dialogLoading = true;
					data.nameForm.type = 'category';

					addData('/sys/storage', { ...data.nameForm })
						.then((res) => {
							data.categorys.unshift({
								...res.data,
								is_default: 0,
							});
							//getStorageDirectory();
							data.nameFormVisible = false;
							ElMessage.success('操作成功');
						})
						.catch(() => {
							data.dialogLoading = false;
						});
				}
			});
		};

		const rename = () => {
			refform.value.validate((valid: boolean) => {
				if (valid) {
					data.dialogLoading = true;
					// renameStorageItem(data.nameForm.storageId, data.nameForm.name)
					//   .then(res => {
					//     data.currentTableData[data.nameForm.index].name = data.nameForm.name
					//     // data.directoryList = []
					//     getStorageDirectory()
					//     data.nameFormVisible = false
					//     data.$message.success('操作成功')
					//   })
					//   .catch(() => {
					//     data.dialogLoading = false
					//   })
				}
			});
		};

		const handleAdd = () => {
			data.nameForm['name'] = undefined;
			data.nameForm['parentId'] = data.form.parentId;

			data.dialogLoading = false;
			data.nameStatus = 'add';
			data.nameFormVisible = true;

			nextTick(() => {
				refform.value.clearValidate();
			});
		};

		// for(let i=1 ;i<=9;i++){
		//   data.syscategorys.push({
		//     id:'icon'+i,
		//     name:'图标库'+i
		//   })
		// }
		const handleClick = () => {
			if (data.syscategorys.length == 0 && data.icontype == 'system') {
				data.sysform.type = 'systemcategorys';
				handleSyscatesSubmit();
			}
		};
		return {
			refform,
			refinput,
			selectSystemCategory,
			rename,
			add,
			handleAdd,
			handleClick,
			...toRefs(data),
			_getUploadFileList,
			selectCategory,
			getImageThumb,
			selectFile,
			handleDelete,
			handleSearch,
			handleAllSearch,
			handleConfirm,
			handlePaginationChange,
			handleStorageDlg,
			handleSysSearch,
		};
	},
});
</script>
<style>
.storages .el-input__suffix {
	top: 5px;
	height: auto;
}
</style>
<style lang="scss" scoped>
.breadcrumb {
	border: 1px solid #dcdfe6;
	padding: 10px !important;
}
.files {
	height: 50vh;
}
.file-height {
	min-height: calc(40vw - 190px);
}
.file-list {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	flex-wrap: wrap;
	:deep(.el-loading-mask) {
		background-color: transparent;
	}
}
.mt-1 {
	margin-top: 0.25rem;
}
.p-1 {
	padding: 0.25rem;
}
.delete {
	color: #fff;
}
.file-list .item {
	flex: none;
	position: relative;
	width: calc(20% - 20px);
	margin: 10px;
	text-align: center;
	vertical-align: middle;

	.file-image {
		width: 100%;
		height: 100%;
		background-color: #eee;
		border-radius: 4px;
		background-size: contain;
		background-repeat: no-repeat;
		background-position: 50% 50%;
		position: absolute;
		left: 0;
		top: 0;
	}
	&:before {
		content: '';
		display: inline-block;
		padding-bottom: 100%;
		width: 0.1px;
		vertical-align: middle;
	}
	.file-name {
		position: absolute;
		bottom: 0;
		left: 0;
		width: 100%;
		line-height: 34px;
		height: 34px;
		background: rgba(0, 0, 0, 0.5);
		color: #fff;

		text-align: center;
		z-index: 2;
		border-bottom-left-radius: 4px;
		border-bottom-right-radius: 4px;
		text-overflow: ellipsis;
		overflow: hidden;
		white-space: nowrap;
		box-sizing: border-box;
	}
	.mask {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		z-index: 5;
		background-color: rgba(0, 0, 0, 0.5);
		text-align: center;
		display: none;
		width: 100%;
		height: 100%;
		color: #fff;
		font-size: 40px;
		background: rgba(66, 139, 202, 0.8);
	}
	.el-dropdown {
		position: absolute;
		width: 34px;
		line-height: 34px;
		text-align: center;
		background-color: #3296fa;
		cursor: pointer;
		bottom: 0;
		right: 0;
		z-index: 6;
		display: none;
	}

	&:hover {
		&:after {
			position: absolute;
			bottom: -20px;
			left: 0px;
			position: absolute;
			padding: 3px 5px;
			font-size: 12px;
			font-weight: 700;
			color: #fff;
			white-space: nowrap;
			background-color: #006eff;
			border-radius: 3px;
			content: attr(data-label);
		}
		.el-dropdown {
			display: block;
		}
	}
	&.active {
		.mask {
			display: flex;
			align-items: center;
			justify-content: center;
		}
	}
}

.brother-showing i {
	width: 16px;
}
.diy-mb-5 {
	margin-bottom: 5px !important;
}
.diy-tree {
	width: 200px;
}
.file-border {
	border: 1px solid #dcdfe6;
	padding: 10px;
	:deep(.el-form-item__content) {
		margin-left: 0 !important;
	}
	.selected {
		border: 1px solid var(--el-color-primary);
		border-radius: 3px;
		color: var(--el-color-primary);
	}
}

.tree-scroll {
	overflow: auto;
	padding-bottom: 1px;
}
.custom-tree-node {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: 14px;
	padding-right: 8px;
}

.diy-table {
	display: table-row;
}
.diy-cell {
	display: table-cell;
}
</style>


http://www.kler.cn/news/318225.html

相关文章:

  • 《探索云原生与相关技术》
  • 【nvm管理多版本node】下载安装以及常见问题和解决方案
  • 分发饼干00
  • 苹果手机邮箱添加阿里云邮箱的设置步骤
  • Tomcat 乱码问题彻底解决
  • BigDecimal的使用
  • Vue.js props 子组件可以从父组件接收数据(通过 props)并可以向父组件发送事件(通过 $emit)
  • 力扣206.反转链表
  • 24/9/24 算法笔记 超参数优化算法
  • 模型验证 — 交叉验证Cross Validation的方法
  • 大数据新视界 --大数据大厂之算法在大数据中的核心作用:提升效率与智能决策
  • 【linux】file命令
  • 数据篇| 关于Selenium反爬杂谈
  • Python3将Excel数据转换为文本文件
  • 【鸿蒙 HarmonyOS NEXT】组件嵌套滚动:nestedScroll
  • 【html】基础(二)
  • 量化交易----数据透视表----融资融券优惠代码
  • 响应式布局-媒体查询父级布局容器
  • spring boot导入多个配置文件
  • #C++ enum枚举
  • Qt/C++ 多线程同步机制详解及应用
  • Shiro-550—漏洞分析(CVE-2016-4437)
  • 详解QT插件机制
  • ARM/Linux嵌入式面经(三三):大疆
  • zabbix email 告警
  • [大语言模型-论文精读] ACL2024-长尾知识在检索增强型大型语言模型中的作用
  • Invalid Executable The executable contains bitcode
  • 报错error: RPC failed,curl 16 Error in the HTTP2 framing layer解决方法
  • 自动化学习3:日志记录及测试报告的生成--自动化框架搭建
  • 数据库课程 CMU15-445 2023 Fall Project-2 Extendible Hash Index