|
|
@@ -15,7 +15,20 @@
|
|
|
<el-table-column prop="title" :label="$t('common.title')" align="center" min-width="150" />
|
|
|
<el-table-column prop="image" :label="$t('banner.image')" align="center" width="120">
|
|
|
<template #default="{ row }">
|
|
|
- <el-image v-if="row.image" :src="row.image" style="width: 100px; height: 50px" fit="cover" :preview-src-list="[row.image]" />
|
|
|
+ <video
|
|
|
+ v-if="row.image && isBannerVideoUrl(row.image)"
|
|
|
+ :src="row.image"
|
|
|
+ muted
|
|
|
+ playsinline
|
|
|
+ style="width: 100px; height: 50px; object-fit: cover; border-radius: 4px; vertical-align: middle"
|
|
|
+ />
|
|
|
+ <el-image
|
|
|
+ v-else-if="row.image"
|
|
|
+ :src="row.image"
|
|
|
+ style="width: 100px; height: 50px"
|
|
|
+ fit="cover"
|
|
|
+ :preview-src-list="[row.image]"
|
|
|
+ />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="position" :label="$t('banner.position')" align="center" width="100">
|
|
|
@@ -68,9 +81,18 @@
|
|
|
:show-file-list="false"
|
|
|
:http-request="handleBannerUpload"
|
|
|
:before-upload="beforeBannerUpload"
|
|
|
- accept="image/*"
|
|
|
+ accept="image/*,video/mp4,video/webm,video/quicktime,video/ogg,video/x-m4v"
|
|
|
>
|
|
|
- <el-image v-if="formData.image" :src="formData.image" class="banner-preview" fit="cover" />
|
|
|
+ <video
|
|
|
+ v-if="formData.image && isBannerVideoUrl(formData.image)"
|
|
|
+ :src="formData.image"
|
|
|
+ class="banner-preview"
|
|
|
+ muted
|
|
|
+ loop
|
|
|
+ playsinline
|
|
|
+ controls
|
|
|
+ />
|
|
|
+ <el-image v-else-if="formData.image" :src="formData.image" class="banner-preview" fit="cover" />
|
|
|
<el-icon v-else class="banner-uploader-icon"><Plus /></el-icon>
|
|
|
</el-upload>
|
|
|
<div class="upload-actions">
|
|
|
@@ -79,15 +101,16 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="image-url-input">
|
|
|
- <el-input v-model="formData.image" :placeholder="$t('banner.orInputImageUrl')" size="small" clearable />
|
|
|
+ <el-input v-model="formData.image" :placeholder="$t('banner.orInputMediaUrl')" size="small" clearable />
|
|
|
</div>
|
|
|
- <div class="upload-tip">{{ $t('banner.imageSizeTip') }}</div>
|
|
|
+ <div class="upload-tip">{{ $t('banner.mediaSizeTip') }}</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item :label="$t('banner.position')" prop="position">
|
|
|
<el-select v-model="formData.position" :placeholder="$t('banner.placeholderPosition')" style="width: 100%">
|
|
|
<el-option :label="$t('banner.positionHome')" value="home" />
|
|
|
<el-option :label="$t('banner.positionTask')" value="task" />
|
|
|
<el-option :label="$t('banner.positionMine')" value="mine" />
|
|
|
+ <el-option :label="$t('banner.positionLogin')" value="login" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item :label="$t('banner.linkType')" prop="linkType">
|
|
|
@@ -135,6 +158,7 @@ import Pagination from "@/components/Pangination/Pagination.vue";
|
|
|
import MaterialPicker from "@/components/MaterialPicker/index.vue";
|
|
|
import { getBannerList, createBanner, updateBanner, deleteBanner } from "@/api/modules/daytask.js";
|
|
|
import { uploadImg } from "@/api/modules/upload.js";
|
|
|
+import { isBannerVideoUrl } from "@/utils/bannerMedia";
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
const tableData = ref([]);
|
|
|
@@ -143,7 +167,8 @@ const pageable = reactive({ pageNum: 1, pageSize: 30, total: 0 });
|
|
|
const positionMap = computed(() => ({
|
|
|
home: t("banner.positionHome"),
|
|
|
task: t("banner.positionTask"),
|
|
|
- mine: t("banner.positionMine")
|
|
|
+ mine: t("banner.positionMine"),
|
|
|
+ login: t("banner.positionLogin")
|
|
|
}));
|
|
|
const linkTypeMap = computed(() => ({
|
|
|
0: t("banner.linkNone"),
|
|
|
@@ -169,7 +194,7 @@ const formData = ref({
|
|
|
});
|
|
|
const formRules = computed(() => ({
|
|
|
title: [{ required: true, message: t("common.placeholderTitle"), trigger: "blur" }],
|
|
|
- image: [{ required: true, message: t("banner.placeholderImageUrl"), trigger: "blur" }],
|
|
|
+ image: [{ required: true, message: t("banner.placeholderMediaUrl"), trigger: "blur" }],
|
|
|
position: [{ required: true, message: t("banner.placeholderPosition"), trigger: "change" }],
|
|
|
status: [{ required: true, message: t("common.placeholderStatus"), trigger: "change" }]
|
|
|
}));
|
|
|
@@ -218,8 +243,8 @@ const openDialog = (row) => {
|
|
|
title: row.title || "",
|
|
|
image: row.image || "",
|
|
|
position: row.position || "home",
|
|
|
- linkType: row.linkType ?? 0,
|
|
|
- linkUrl: row.linkUrl || "",
|
|
|
+ linkType: row.linkType ?? row.link_type ?? 0,
|
|
|
+ linkUrl: row.linkUrl ?? row.link_url ?? "",
|
|
|
sort: row.sort ?? 0,
|
|
|
status: row.status ?? 1
|
|
|
};
|
|
|
@@ -248,14 +273,14 @@ const handleSubmit = async () => {
|
|
|
submitLoading.value = true;
|
|
|
try {
|
|
|
const api = isEdit.value ? updateBanner : createBanner;
|
|
|
- // 后端期望 { id, data: {...} } 格式,字段名需要转为 snake_case
|
|
|
+ // 后端 go_server DtBanner 使用 json:"linkType"/"linkUrl";StructToMapWithZero 只认与 json 标签一致的键名
|
|
|
const { id } = formData.value;
|
|
|
const data = {
|
|
|
title: formData.value.title,
|
|
|
image: formData.value.image,
|
|
|
position: formData.value.position,
|
|
|
- link_type: formData.value.linkType,
|
|
|
- link_url: formData.value.linkUrl,
|
|
|
+ linkType: formData.value.linkType,
|
|
|
+ linkUrl: formData.value.linkUrl,
|
|
|
sort: formData.value.sort,
|
|
|
status: formData.value.status
|
|
|
};
|
|
|
@@ -308,18 +333,25 @@ const handleMaterialSelect = (material) => {
|
|
|
};
|
|
|
|
|
|
// ==================== 图片上传 ====================
|
|
|
+const MAX_IMAGE_MB = 5;
|
|
|
+const MAX_VIDEO_MB = 50;
|
|
|
+
|
|
|
const beforeBannerUpload = (file) => {
|
|
|
const isImage = file.type.startsWith("image/");
|
|
|
- const isLt5M = file.size / 1024 / 1024 < 5;
|
|
|
-
|
|
|
- if (!isImage) {
|
|
|
- ElNotification.warning(t("banner.imageOnly"));
|
|
|
+ const isVideo = file.type.startsWith("video/");
|
|
|
+ if (!isImage && !isVideo) {
|
|
|
+ ElNotification.warning(t("banner.imageOrVideoOnly"));
|
|
|
return false;
|
|
|
}
|
|
|
- if (!isLt5M) {
|
|
|
+ const sizeMb = file.size / 1024 / 1024;
|
|
|
+ if (isImage && sizeMb > MAX_IMAGE_MB) {
|
|
|
ElNotification.warning(t("banner.imageSizeLimit"));
|
|
|
return false;
|
|
|
}
|
|
|
+ if (isVideo && sizeMb > MAX_VIDEO_MB) {
|
|
|
+ ElNotification.warning(t("banner.videoSizeLimit"));
|
|
|
+ return false;
|
|
|
+ }
|
|
|
return true;
|
|
|
};
|
|
|
|