|
@@ -10,14 +10,73 @@
|
|
|
|
|
|
<div style="margin-top: 16px;"></div>
|
|
|
|
|
|
- <!-- 只上传图片 -->
|
|
|
- <van-uploader
|
|
|
- v-model="fileList"
|
|
|
- :after-read="afterRead"
|
|
|
- :max-count="9"
|
|
|
- multiple
|
|
|
- accept="image/*"
|
|
|
- />
|
|
|
+ <!-- 上传类型选择 -->
|
|
|
+ <div v-if="!uploadType" class="upload-type">
|
|
|
+ <van-button type="default" @click="setUploadType('image')">上传图片</van-button>
|
|
|
+ <van-button type="default" @click="setUploadType('video')">上传视频</van-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 图片上传 -->
|
|
|
+ <div v-if="uploadType === 'image'">
|
|
|
+ <van-uploader
|
|
|
+ v-model="fileList"
|
|
|
+ :after-read="afterRead"
|
|
|
+ :max-count="9"
|
|
|
+ multiple
|
|
|
+ accept="image/*"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <van-button class="btn-cancel" v-if="uploadType === 'image'"
|
|
|
+ type="danger"
|
|
|
+ plain
|
|
|
+ size="large"
|
|
|
+ @click="cancelImageUpload"
|
|
|
+ >
|
|
|
+ 取消上传图片
|
|
|
+ </van-button>
|
|
|
+
|
|
|
+ <!-- 视频上传 -->
|
|
|
+ <div v-if="uploadType === 'video'" class="preview-list">
|
|
|
+ <div v-if="videoFile.status === 'done'" class="preview-item">
|
|
|
+ <video controls :src="videoFile.url"></video>
|
|
|
+ <van-icon name="close" class="close-icon" @click="removeVideo" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 上传中 -->
|
|
|
+ <div v-else-if="videoFile.status === 'uploading'" class="preview-item">
|
|
|
+ <van-loading size="24px" type="spinner">上传中...</van-loading>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 上传失败 -->
|
|
|
+ <div v-else-if="videoFile.status === 'failed'" class="preview-item">
|
|
|
+ <span style="color:red;">上传失败</span>
|
|
|
+ <van-button size="small" type="primary" @click="triggerVideoInput">
|
|
|
+ 重新上传
|
|
|
+ </van-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 初始状态 -->
|
|
|
+ <div v-else class="van-uploader__upload" @click="triggerVideoInput">
|
|
|
+ <van-icon name="plus" size="24"/>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <input
|
|
|
+ type="file"
|
|
|
+ ref="videoInput"
|
|
|
+ accept="video/*"
|
|
|
+ @change="handleVideoChange"
|
|
|
+ style="display:none"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <!-- 取消按钮 -->
|
|
|
+ <van-button class="btn-cancel" v-if="uploadType === 'video'"
|
|
|
+ type="danger"
|
|
|
+ plain
|
|
|
+ size="large"
|
|
|
+ @click="cancelVideoUpload"
|
|
|
+ >
|
|
|
+ 取消上传视频
|
|
|
+ </van-button>
|
|
|
|
|
|
<!-- 发布按钮 -->
|
|
|
<van-button
|
|
@@ -35,22 +94,37 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed } from "vue";
|
|
|
import { addDynamic, hostUploadImg } from "@/api/path/im.api";
|
|
|
import { showToast } from "vant";
|
|
|
|
|
|
const router = useRouter();
|
|
|
const message = ref("");
|
|
|
-const fileList = ref([]);
|
|
|
+const fileList = ref([]);
|
|
|
+const videoFile = ref({});
|
|
|
const loading = ref(false);
|
|
|
-const uploading = ref(false); // 标记是否正在上传
|
|
|
+const uploading = ref(false);
|
|
|
+const videoUploading = ref(false);
|
|
|
+const videoInput = ref(null);
|
|
|
+const uploadType = ref("");
|
|
|
|
|
|
-// 按钮禁用:没有文字 && 没有文件 时禁用;或者正在上传时禁用
|
|
|
-const isDisabled = computed(() => {
|
|
|
- return uploading.value || !(message.value.trim() || fileList.value.length > 0);
|
|
|
-});
|
|
|
+// 设置上传类型
|
|
|
+const setUploadType = (type) => {
|
|
|
+ uploadType.value = type;
|
|
|
+};
|
|
|
+
|
|
|
+// 取消图片上传
|
|
|
+const cancelImageUpload = () => {
|
|
|
+ fileList.value = [];
|
|
|
+ uploadType.value = "";
|
|
|
+};
|
|
|
|
|
|
-// 选中文件后的处理逻辑(支持多选上传)——逐个上传
|
|
|
+// 取消视频上传
|
|
|
+const cancelVideoUpload = () => {
|
|
|
+ videoFile.value = {};
|
|
|
+ uploadType.value = "";
|
|
|
+};
|
|
|
+
|
|
|
+// 图片上传
|
|
|
const afterRead = async (file) => {
|
|
|
const files = Array.isArray(file) ? file : [file];
|
|
|
uploading.value = true;
|
|
@@ -60,7 +134,6 @@ const afterRead = async (file) => {
|
|
|
const formData = new FormData();
|
|
|
formData.append("image", rawFile);
|
|
|
|
|
|
- // 先标记状态
|
|
|
f.status = "uploading";
|
|
|
|
|
|
try {
|
|
@@ -77,26 +150,83 @@ const afterRead = async (file) => {
|
|
|
uploading.value = false;
|
|
|
};
|
|
|
|
|
|
-// 发布按钮逻辑
|
|
|
+// 点击上传视频
|
|
|
+const triggerVideoInput = () => {
|
|
|
+ if (videoInput.value) {
|
|
|
+ videoInput.value.value = null;
|
|
|
+ videoInput.value.click();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 视频选择与上传
|
|
|
+const handleVideoChange = async (event) => {
|
|
|
+ const file = event.target.files[0];
|
|
|
+ if (!file) return;
|
|
|
+
|
|
|
+ videoFile.value = {
|
|
|
+ file,
|
|
|
+ status: "uploading",
|
|
|
+ url: URL.createObjectURL(file),
|
|
|
+ };
|
|
|
+
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append("image", file);
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await hostUploadImg(formData);
|
|
|
+ videoFile.value = {
|
|
|
+ file,
|
|
|
+ url: res.data.all_url,
|
|
|
+ status: "done",
|
|
|
+ };
|
|
|
+ } catch (err) {
|
|
|
+ console.error("视频上传失败:", err);
|
|
|
+ showToast("视频上传失败");
|
|
|
+ videoFile.value.status = "failed";
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 删除视频
|
|
|
+const removeVideo = () => {
|
|
|
+ videoFile.value = {};
|
|
|
+};
|
|
|
+
|
|
|
+// 按钮禁用
|
|
|
+const isDisabled = computed(() => {
|
|
|
+ return (
|
|
|
+ uploading.value ||
|
|
|
+ videoUploading.value ||
|
|
|
+ !(message.value.trim() || fileList.value.length > 0 || videoFile.value.url)
|
|
|
+ );
|
|
|
+});
|
|
|
+
|
|
|
+// 发布逻辑
|
|
|
const confirm = async () => {
|
|
|
- // 检查是否有图片没上传完
|
|
|
- if (fileList.value.some((item) => item.status === "uploading")) {
|
|
|
- showToast("请等待图片上传完成");
|
|
|
+ if (
|
|
|
+ fileList.value.some((item) => item.status === "uploading") ||
|
|
|
+ videoFile.value.status === "uploading"
|
|
|
+ ) {
|
|
|
+ showToast("请等待上传完成");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
let params = {
|
|
|
content: message.value,
|
|
|
- type: "0", // 图片
|
|
|
- images: "",
|
|
|
+ type: uploadType.value === "video" ? "1" : "0",
|
|
|
+ images: [],
|
|
|
+ video: "",
|
|
|
};
|
|
|
|
|
|
- if (fileList.value.length > 0) {
|
|
|
+ if (uploadType.value === "image" && fileList.value.length > 0) {
|
|
|
params.images = fileList.value
|
|
|
- .filter((item) => item.status === "done") // 只取成功的
|
|
|
+ .filter((item) => item.status === "done")
|
|
|
.map((item) => item.url);
|
|
|
}
|
|
|
|
|
|
+ if (uploadType.value === "video" && videoFile.value.status === "done") {
|
|
|
+ params.video = videoFile.value.url;
|
|
|
+ }
|
|
|
+
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
const res = await addDynamic(params);
|
|
@@ -104,6 +234,8 @@ const confirm = async () => {
|
|
|
showToast("发布成功");
|
|
|
message.value = "";
|
|
|
fileList.value = [];
|
|
|
+ videoFile.value = {};
|
|
|
+ uploadType.value = "";
|
|
|
router.back();
|
|
|
} else {
|
|
|
showToast(res.message || "发布失败");
|
|
@@ -119,6 +251,13 @@ const confirm = async () => {
|
|
|
<style lang="less" scoped>
|
|
|
.container {
|
|
|
margin: 16px;
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .upload-type {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-around;
|
|
|
+ // margin-bottom: 16px;
|
|
|
+ }
|
|
|
|
|
|
:deep(.van-cell) {
|
|
|
border-radius: 12px !important;
|
|
@@ -138,10 +277,20 @@ const confirm = async () => {
|
|
|
|
|
|
:deep(.van-uploader__upload) {
|
|
|
background: #f2f2f2 !important;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100px;
|
|
|
+ height: 100px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 16px;
|
|
|
+ color: #8d8d8d;
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
- margin-top: 40px;
|
|
|
+ margin-top: 20px;
|
|
|
background: #4765dd;
|
|
|
height: 40px;
|
|
|
line-height: 40px;
|
|
@@ -154,9 +303,15 @@ const confirm = async () => {
|
|
|
color: #ffffff;
|
|
|
border: none !important;
|
|
|
}
|
|
|
+ .btn-cancel{
|
|
|
+ margin-top: 20px;
|
|
|
+ height: 40px;
|
|
|
+ line-height: 40px;
|
|
|
+ border-radius: 50px;
|
|
|
+ }
|
|
|
|
|
|
.preview-list {
|
|
|
- margin-top: 20px;
|
|
|
+ margin-top: 10px;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
@@ -164,6 +319,32 @@ const confirm = async () => {
|
|
|
|
|
|
.preview-item {
|
|
|
position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 300px;
|
|
|
+ border-radius: 12px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background: #f2f2f2;
|
|
|
+
|
|
|
+ video {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+
|
|
|
+ .close-icon {
|
|
|
+ position: absolute;
|
|
|
+ top: 4px;
|
|
|
+ right: 4px;
|
|
|
+ font-size: 20px;
|
|
|
+ color: #fff;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 2px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|