hookehuyr

feat(Feedback): 支持多图片上传并添加大小限制

refactor(JoinFamily): 使用原子类替换自定义样式
......@@ -17,31 +17,45 @@
<!-- Upload Screenshot Section -->
<view class="mb-6">
<view class="text-lg font-medium mb-2">上传截图 (可选)</view>
<view v-if="screenshot" class="mb-4">
<view class="relative inline-block">
<!-- 已上传图片显示 -->
<view class="flex flex-wrap gap-2 mb-4">
<view
v-for="(item, index) in screenshots"
:key="index"
class="relative"
>
<image
:src="screenshot"
:src="item.url"
class="w-24 h-24 rounded-lg object-cover"
mode="aspectFill"
@tap="previewImage"
@tap="() => previewImage(index)"
/>
<view
@click="deleteImage"
class="absolute -top-2 -right-2 w-5 h-5 bg-red-500 rounded-full flex items-center justify-center"
@click="() => deleteImage(index)"
class="absolute -top-2 -right-2 w-4 h-4 bg-red-500 rounded-full flex items-center justify-center"
>
<view class="text-white text-xs">×</view>
</view>
</view>
</view>
<!-- 上传按钮 -->
<view
v-if="!screenshot"
class="border border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center"
v-if="screenshots.length < maxImages"
class="w-24 h-24 border border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center"
@click="chooseImage"
>
<view class="text-gray-400 mb-2">
<Photograph size="24" />
</view>
<view class="text-center text-gray-400">添加图片</view>
<view class="text-center text-gray-400 text-xs">添加图片</view>
</view>
</view>
<!-- 提示信息 -->
<view class="text-xs text-gray-500 mt-2">
<view>• 图片大小不能超过10MB</view>
<view>• 最多只能上传3张图片</view>
<view>• 当前已上传 {{ screenshots.length }}/{{ maxImages }} 张</view>
</view>
</view>
......@@ -54,6 +68,7 @@
v-model="name"
class="w-full text-gray-600 focus:outline-none"
placeholder="请输入您的姓名"
:cursorSpacing="100"
/>
</view>
</view>
......@@ -67,6 +82,7 @@
v-model="contact"
class="w-full text-gray-600 focus:outline-none"
placeholder="请输入您的手机号或微信号"
:cursorSpacing="100"
/>
</view>
</view>
......@@ -94,13 +110,15 @@
import { ref } from 'vue';
import Taro from '@tarojs/taro';
import { Photograph } from '@nutui/icons-vue-taro';
import BASE_URL from '@/utils/config';
const feedbackText = ref('');
const screenshot = ref('');
const screenshots = ref([]);
const name = ref('');
const contact = ref('');
const previewVisible = ref(false);
const previewImages = ref([]);
const maxImages = 3;
/**
* 显示提示信息
......@@ -118,13 +136,34 @@ const showToast = (message, type = 'success') => {
* 选择图片
*/
const chooseImage = () => {
if (screenshots.value.length >= maxImages) {
showToast(`最多只能上传${maxImages}张图片`, 'error');
return;
}
const remainingCount = maxImages - screenshots.value.length;
Taro.chooseImage({
count: 1,
count: remainingCount,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
const tempFilePath = res.tempFilePaths[0];
screenshot.value = tempFilePath;
res.tempFilePaths.forEach(tempFilePath => {
// 检查文件大小(10MB = 10 * 1024 * 1024 bytes)
Taro.getFileInfo({
filePath: tempFilePath,
success: function (fileInfo) {
if (fileInfo.size > 10 * 1024 * 1024) {
showToast('图片大小不能超过10MB', 'error');
return;
}
uploadImage(tempFilePath);
},
fail: function () {
// 如果获取文件信息失败,直接上传
uploadImage(tempFilePath);
}
});
});
},
fail: function () {
showToast('选择图片失败', 'error');
......@@ -133,10 +172,53 @@ const chooseImage = () => {
};
/**
* 上传图片到服务器
*/
const uploadImage = (filePath) => {
// 显示上传中提示
Taro.showLoading({
title: '上传中',
mask: true
});
wx.uploadFile({
url: BASE_URL + '/admin/?m=srv&a=upload',
filePath,
name: 'file',
header: {
'content-type': 'multipart/form-data',
},
success: function (res) {
let upload_data = JSON.parse(res.data);
Taro.hideLoading({
success: () => {
if (res.statusCode === 200) {
screenshots.value.push({
url: upload_data.data.src,
localPath: filePath
});
showToast('上传成功', 'success');
} else {
showToast('服务器错误,稍后重试!', 'error');
}
},
});
},
fail: function (res) {
Taro.hideLoading({
success: () => {
showToast('上传失败,稍后重试!', 'error');
}
});
}
});
};
/**
* 预览图片
*/
const previewImage = () => {
previewImages.value = [{ src: screenshot.value }];
const previewImage = (index) => {
previewImages.value = screenshots.value.map(item => ({ src: item.url }));
previewVisible.value = true;
};
......@@ -150,8 +232,9 @@ const closePreview = () => {
/**
* 删除图片
*/
const deleteImage = () => {
screenshot.value = '';
const deleteImage = (index) => {
screenshots.value.splice(index, 1);
showToast('图片已删除', 'success');
};
/**
......@@ -176,7 +259,7 @@ const submitFeedback = () => {
// 提交成功后清空表单
feedbackText.value = '';
screenshot.value = '';
screenshots.value = [];
name.value = '';
contact.value = '';
};
......
......@@ -36,34 +36,3 @@
font-weight: 700;
margin-bottom: 1rem;
}
\ No newline at end of file
.identity-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.identity-item {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
}
.identity-item-selected {
border-color: #3b82f6;
background-color: #eff6ff;
}
.identity-item-content {
display: flex;
flex-direction: column;
align-items: center;
}
.identity-item-icon {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
\ No newline at end of file
......
......@@ -33,7 +33,7 @@
</view>
</view>
<!-- Help text -->
<view class="text-gray-500 text-center text-sm mb-8">
<view class="text-gray-500 text-center text-sm mb-4">
没有口令?请联系您的大家长获取
</view>
<!-- Role selection -->
......@@ -41,20 +41,20 @@
<h3 class="identity-title">
选择您的身份
</h3>
<view class="identity-grid">
<view class="flex gap-2 flex-wrap">
<view
v-for="role in familyRoles"
:key="role.id"
@click="selectedRole = role.id"
:class="[
'identity-item',
{ 'identity-item-selected': selectedRole === role.id }
'w-[calc(49%-4rpx)] py-3 rounded-lg border text-center flex flex-col items-center gap-1',
selectedRole === role.id
? 'border-blue-500 bg-blue-50 text-blue-500'
: 'border-gray-200 text-gray-700'
]"
@click="selectedRole = role.id"
>
<view class="identity-item-content">
<My size="20" class="identity-item-icon" />
<span class="identity-item-label">{{ role.label }}</span>
</view>
<My size="20" />
<span class="text-sm">{{ role.label }}</span>
</view>
</view>
</view>
......@@ -63,7 +63,7 @@
@click="handleJoinFamily"
:disabled="!isComplete"
:class="[
'w-full py-4 text-white text-lg font-medium rounded-lg mt-auto text-center',
'w-full py-3 text-white text-lg font-medium rounded-lg mt-auto text-center',
isComplete ? 'bg-blue-500' : 'bg-gray-300'
]"
>
......