照片上传组件
# 照片上传组件
# 1. 组件封装
<template>
<div class="photo-upload">
<el-tooltip content="点击上传照片" placement="top">
<div
class="photo-container"
@click="triggerFileInput"
>
<el-image
v-if="internalPhotoUrl"
:src="internalPhotoUrl"
class="photo"
fit="cover"
></el-image>
<div v-else class="upload-placeholder">
<el-icon><Plus /></el-icon>
</div>
</div>
</el-tooltip>
<!-- 隐藏的文件输入框 -->
<input
type="file"
ref="fileInputRef"
style="display: none"
@change="handleFileChange"
accept="image/*"
/>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import request from "@/request/index.js";
import { ElMessage } from 'element-plus';
// 接收 v-model 传递的值
const props = defineProps({
modelValue: {
type: String,
default: ''
}
});
// 用于触发父组件更新 v-model 的事件
const emit = defineEmits(['update:modelValue']);
// 照片 URL 的内部状态,初始化为父组件传递的值
const internalPhotoUrl = ref(props.modelValue);
// 文件输入框的引用
const fileInputRef = ref(null);
// 监听父组件传递的值变化,并同步到内部状态
watch(() => props.modelValue, (newVal) => {
internalPhotoUrl.value = newVal;
});
// 触发文件选择框的方法
const triggerFileInput = () => {
if (fileInputRef.value) {
fileInputRef.value.click();
}
};
// 处理文件选择变化的方法
const handleFileChange = async (event) => {
const file = event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
try {
// 发送上传请求
const response = await request.post('/upload/uploadAvatar', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
if (response.code === 200) {
// 更新照片 URL
internalPhotoUrl.value = response.data;
ElMessage.success('照片上传成功!');
// 自动更新父组件的 v-model
emit('update:modelValue', internalPhotoUrl.value);
} else {
ElMessage.error(response.msg || '照片上传失败,请重试。');
}
} catch (error) {
console.error('Upload error:', error);
ElMessage.error('照片上传失败,请重试。');
}
};
</script>
<style scoped>
.photo-upload {
width: 120px;
height: 120px;
}
.photo-container {
width: 100%;
height: 100%;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.photo-container:hover {
border-color: #409EFF;
}
.photo {
width: 100%;
height: 100%;
object-fit: cover;
}
.upload-placeholder {
font-size: 28px;
color: #8c939d;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 2. 父组件中如何使用
<template>
<div>
<photo-uploader v-model="avatarUrl"></photo-uploader>
<p>当前照片 URL: {{ avatarUrl }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import PhotoUploader from '@/components/PhotoUploader.vue'; // 请根据实际路径修改
// 父组件中声明一个变量用于绑定照片 URL
const avatarUrl = ref('https://example.com/initial-photo.jpg');
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
解释
modelValue
Prop 和update:modelValue
事件:v-model
在 Vue 3 中等效于modelValue
prop 和update:modelValue
事件。modelValue
接收父组件传入的照片 URL,update:modelValue
用于向父组件发送更新后的 URL。
内部状态
internalPhotoUrl
:- 组件内部使用
internalPhotoUrl
维护当前的照片 URL。 - 通过监听
props.modelValue
的变化,确保内部状态与父组件传递的值保持同步。
- 组件内部使用
文件上传成功后自动更新
v-model
:- 当文件上传成功后,组件会自动调用
emit('update:modelValue', internalPhotoUrl.value)
,将新的照片 URL 传递给父组件,更新v-model
绑定的变量。
- 当文件上传成功后,组件会自动调用
这样做的好处
- 简化父组件逻辑:父组件不需要手动监听事件来更新照片 URL,所有的逻辑都封装在
PhotoUploader
组件内部。 - 增强可复用性:通过
v-model
实现双向绑定,可以方便地在不同的父组件中使用PhotoUploader
组件,而无需担心事件处理的繁琐。
编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08