Vue封装自定义组件上传图片,并支持v-model语法

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
		<meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no" />
		<title></title>
		<script type="text/javascript" src="../js/vue.min.js"></script>
		<script type="text/javascript" src="../js/vconsole.min.js"></script>
		<style>
			.mgt15{
				margin-top: 15px;
			}
			.imageBox{
				display: flex;
				flex-wrap: wrap;
			}
			.itemBox{
				position: relative;
				width: 80px;
				height: 80px;
				margin:0 8px 8px 0;
			}
			.itemDel{
				position: absolute;
				top: 0;
				right: 0;
				z-index: 9;
				color: red;
			}
			.itemImage{
				width: 80px;
				height: 80px;
				object-fit: cover;
			}
			.textarea{
				width: 100%;
				height: 200px;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<form action="">
				<div class="mgt15">
					<upload v-model="formData.file1"></upload>
				</div>
				<div class="mgt15">
					<upload max="3" v-model="formData.file2"></upload>
				</div>
				<div class="mgt15">
					<button type="button" @click="formSubmit">提交表单</button>
				</div>
				<div class="mgt15" v-if="formDataStr">
					<textarea class="textarea" v-model="formDataStr"></textarea>
				</div>
			</form>
		</div>
		<script type="text/javascript">
			var vConsole = new VConsole();
			//定义组件
			Vue.component('upload', {
				template: `
				<div>
					<div class="imageBox">
						<div class="itemBox" v-for="(item,index) in showImage">
							<button class="itemDel" type="button" @click="imageDel(index)">删除</button>
							<img class="itemImage" :src="item" />
						</div>
						<div class="itemBox" v-if="loading">上传中...</div>
					</div>
					<input v-if="visible" type="file" @change="fileChange" />
				</div>`,
				model: {
					prop: 'value',
					event: 'change'
				},
				props: {
					value: [String, Array], //指定value值的类型
					max: {
						type: Number,
						default: 1 //默认最多上传一张
					}
				},
				data() {
					return {
						tempValue: [], //暂存
						loading: false, //表示是否正在上传文件
					}
				},
				computed: {
					visible() {
						if (this.loading) {
							return false;
						}
						if (!this.value) {
							return true;
						}
						return this.value.length < this.max
					},
					/**
					 * 格式化图片路径
					 */
					showImage() {
						var tempValue = this.tempValue;
						//在这里进行类型的转换,解决 "1.jpg,2.jpg"
						var fttValue = [];
						for (var i = 0; i < tempValue.length; i++) {
							//一般数据库只是存放相对路径,给文件添加文件服务器前缀,这一步视情况而定
							fttValue.push("https://api.jiuwusan.cn" + tempValue[i]);
						}
						return fttValue;
					}
				},
				watch: {
					value: function(newVal, oldVal) {
						console.log("更新 :");
						this.setInitValue(newVal);
					}
				},
				created: function() {
					console.log("创建 :", this.value);
					this.setInitValue(this.value);
				},
				methods: {
					/**
					 * 监听input的变化,进行图片上传
					 * @param {Object} e
					 */
					fileChange(e) {
						var that = this;
						that.loading = true;
						upload(e.target.files, function(res) {
							that.loading = false;
							if (res.code == 200) {
								// var value = JSON.parse(JSON.stringify(that.value || []));
								that.tempValue.push(res.path);
								this.$emit('change', that.tempValue);
							}
						});
					},
					/**
					 * 对初始值进行格式化
					 * @param {Object} value
					 */
					setInitValue(value) {
						if (!value) {
							this.value = [];
							return;
						}
						//在这里进行类型的转换
						if (Object.prototype.toString.call(value) != '[object Array]') {
							value = value.split(",");
						}
						this.value = value;
						this.tempValue = value;
						this.$emit('change', this.value);
					},
					imageDel(index) {
						console.log("删除===", index);
						this.tempValue.splice(index, 1);
						this.$emit('change', this.tempValue);
					}
				}
			})
			// 创建实例
			var app = new Vue({
				el: '#app',
				data() {
					return {
						formData: {
							file1: "",
							file2: []
						},
						formDataStr: ""
					}
				},
				computed: {},
				methods: {
					formSubmit() {
						console.log("提交了表单===", this.formData);
						this.formDataStr = JSON.stringify(this.formData);
					}
				}
			});

			/**
			 * 文件上传的方法
			 * @param {Object} files
			 * @param {Object} callback
			 */
			var upload = function(files, callback) {
				if (!files) {
					console.log("未选择文件");
					return;
				}

				var formData = new FormData();
				formData.append("file", files[0]);

				var xhr = new XMLHttpRequest();
				xhr.onreadystatechange = function() {
					if (xhr.readyState === 4 && xhr.status === 200) {
						var rsJson = xhr.responseText;
						if (callback != undefined && Object.prototype.toString.call(callback) === '[object Function]') {
							try {
								var parseJSON = JSON.parse(rsJson);
								callback(parseJSON);
							} catch (e) {
								// callback({
								// 	code: -99
								// });
							}

						}
					}
				};

				xhr.open("POST", "https://api.jiuwusan.cn/common/upload/img");
				xhr.send(formData);
			};
		</script>
	</body>
</html>

点击运行,查看运行效果

为了方便修改、复用,样式比较简单。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,你的问题是关于Vue3和TypeScript结合使用Element Plus组件库进行图片上传并使用v-model进行值传递的问题。那么我来为你解答一下: 首先,在Vue3中,我们可以使用`<script setup>`语法来编写组件逻辑,这样可以更加简洁和易于理解。同时,使用TypeScript可以让我们在开发过程中更加规范和高效。 其次,对于Element Plus组件库的使用,我们可以通过`import`语句引入需要使用的组件。在图片上传组件中,我们可以使用`<el-upload>`组件来进行上传操作。同时,我们可以通过`v-model`来进行值的双向绑定,从而实现父子组件之间的数据传递。 下面是一个示例代码,供你参考: ```html <template> <el-upload class="upload-demo" action="https://jsonplaceholder.typicode.com/posts/" :on-success="handleSuccess" :before-upload="beforeUpload" v-bind="$attrs" v-model="fileList" :auto-upload="false" > <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip"> 只能上传jpg/png文件,且不超过500kb </div> </el-upload> </template> <script setup lang="ts"> import { defineProps } from 'vue'; import { ElUpload } from 'element-plus'; interface UploadProps { value: any; onChange: (val: any) => void; } const props = defineProps<UploadProps>({ value: {}, onChange: () => {}, }); const fileList = props.value; const handleSuccess = (res: any, file: any) => { console.log(res, file); }; const beforeUpload = (file: any) => { const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; const isLt500Kb = file.size / 1024 < 500; if (!isJpgOrPng) { this.$message.error('只能上传jpg/png文件'); return false; } if (!isLt500Kb) { this.$message.error('文件大小不能超过500kb'); return false; } return true; }; watch( () => fileList, (newVal) => { props.onChange(newVal); } ); </script> ``` 在父组件中,我们可以使用`v-model`来进行值的双向绑定,如下所示: ```html <template> <div> <Upload v-model="imageUrl" /> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import Upload from './Upload.vue'; const imageUrl = ref(''); </script> ``` 这样,当子组件中的值发生变化时,父组件中的`imageUrl`变量也会相应地更新。 希望这个回答能够帮助到你,如有疑问,欢迎继续追问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值