Files
jiapuapp/pages/family/f10-video-list.vue
T

151 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="video-page" :class="`video-state--${pageState}`">
<ModulePageBackground module="family" />
<view class="page-header">
<PageHeader title="家族视频" custom-back @back="returnToFamily" />
</view>
<view class="page-content">
<view v-if="pageState === 'form'" class="video-panel">
<text class="video-panel__title">发布家族视频</text>
<text class="video-panel__note">视频文件由上传回执自动关联发布时不需要填写文件 ID</text>
<view class="video-field video-field--upload">
<text class="video-field__label"><text class="required-mark">*</text>视频文件</text>
<button class="upload-button" :disabled="uploading || submitting" @click="selectVideo">
{{ uploading ? "上传中…" : receipt ? "重新选择视频" : "选择视频" }}
</button>
<text v-if="receipt" class="upload-receipt">已上传:{{ receipt.fileName || "视频" }}</text>
</view>
<text v-if="uploadError" class="field-error">{{ uploadError }}</text>
<view class="video-field">
<text class="video-field__label"><text class="required-mark">*</text>视频标题</text>
<input v-model="form.videoTitle" maxlength="100" placeholder="例如:2026 年清明祭祖活动" placeholder-class="placeholder" @input="submitError = ''" />
</view>
<view class="video-field video-field--textarea">
<text class="video-field__label">视频说明</text>
<textarea v-model="form.videoDesc" maxlength="500" auto-height placeholder="补充视频中的人物、场景或故事" placeholder-class="placeholder" @input="submitError = ''" />
</view>
<text v-if="submitError" class="field-error">{{ submitError }}</text>
<AppButton block :disabled="uploading || submitting" :label="submitting ? '正在发布…' : '发布视频'" @click="submitVideo" />
</view>
<view v-else class="video-state-card">
<text class="video-state-card__title">{{ stateCopy.title }}</text>
<text class="video-state-card__copy">{{ stateCopy.copy }}</text>
<AppButton block :label="stateCopy.action" @click="handleStateAction" />
</view>
</view>
</view>
</template>
<script setup>
import { computed, onUnmounted, reactive, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
import { isVideoPickCancelled, pickAndUploadVideo } from "@/utils/resumable-image-upload.js";
import { goBack, returnTo } from "@/utils/navigation.js";
const genealogyId = ref("");
const pageState = ref("form");
const receipt = ref(null);
const uploading = ref(false);
const submitting = ref(false);
const uploadError = ref("");
const submitError = ref("");
const form = reactive({ videoTitle: "", videoDesc: "" });
const controller = createRequestController();
const hasValidContext = computed(() => /^[1-9]\d*$/.test(genealogyId.value));
const stateCopy = computed(() => pageState.value === "success"
? {
title: "视频已提交服务端",
copy: "视频已按上传回执关联到当前家谱。视频列表接口尚未提供可消费的返回字段,因此此处不猜测播放地址或卡片内容。",
action: "返回家族动态",
}
: {
title: "视频入口无效",
copy: "没有取得有效的家谱标识。",
action: "返回上一页",
});
onLoad((query) => {
genealogyId.value = String(query?.genealogyId || "");
if (!hasValidContext.value) pageState.value = "invalid";
});
onUnmounted(() => controller.abort());
const selectVideo = async () => {
if (uploading.value || submitting.value) return;
uploading.value = true;
uploadError.value = "";
try {
receipt.value = await pickAndUploadVideo({ requestController: controller });
} catch (error) {
if (!isVideoPickCancelled(error) && !isRequestCancelled(error)) {
uploadError.value = error?.message || "视频上传失败,请稍后重试";
}
} finally {
uploading.value = false;
}
};
const submitVideo = async () => {
if (uploading.value || submitting.value || !hasValidContext.value) return;
const videoTitle = form.videoTitle.trim();
if (!receipt.value) {
submitError.value = "请先选择并上传视频";
return;
}
if (!videoTitle) {
submitError.value = "请填写视频标题";
return;
}
submitting.value = true;
submitError.value = "";
try {
await appApi.createVideo(genealogyId.value, {
videoTitle,
videoDesc: form.videoDesc.trim(),
videoOssId: receipt.value.ossId,
}, { requestController: controller });
pageState.value = "success";
} catch (error) {
if (!isRequestCancelled(error)) submitError.value = error?.message || "视频发布失败,请稍后重试";
} finally {
submitting.value = false;
}
};
const returnToFamily = () => hasValidContext.value
? returnTo("F01", { genealogyId: genealogyId.value })
: goBack();
const handleStateAction = () => returnToFamily();
</script>
<style scoped lang="scss">
@import "../../styles/adaptive-frame-profiles.scss";
.video-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.page-header, .page-content { z-index: 1; }
.page-content { flex: 1; padding: 18rpx 24rpx 72rpx; }
.video-panel, .video-state-card { box-sizing: border-box; @include adaptive-family-content; }
.video-panel { padding: 30rpx; }
.video-panel__title, .video-panel__note, .video-field__label, .video-state-card text { display: block; }
.video-panel__title, .video-state-card__title { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: clamp(19px, 34rpx, 24px); font-weight: 700; }
.video-panel__note, .video-state-card__copy { margin-top: 12rpx; color: $ink-muted; font-size: clamp(14px, 23rpx, 17px); line-height: 1.6; }
.video-field { margin-top: 24rpx; }
.video-field__label { margin-bottom: 10rpx; color: $ink; font-size: clamp(15px, 24rpx, 18px); font-weight: 700; }
.video-field input, .video-field textarea { width: 100%; box-sizing: border-box; border: 1rpx solid rgba(143, 108, 63, .34); border-radius: 8rpx; background: rgba(255, 253, 247, .8); color: $ink; font-size: clamp(15px, 24rpx, 18px); }
.video-field input { height: 76rpx; padding: 0 18rpx; }
.video-field textarea { min-height: 140rpx; padding: 16rpx 18rpx; }
.video-field--upload { display: flex; flex-wrap: wrap; align-items: center; gap: 12rpx; }
.video-field--upload .video-field__label { width: 100%; }
.upload-button { margin: 0; padding: 0 26rpx; border: 1rpx solid #b78a42; border-radius: 8rpx; background: #fffaf0; color: #805723; font-size: clamp(14px, 23rpx, 17px); line-height: 64rpx; }
.upload-receipt { color: $ink-muted; font-size: clamp(13px, 21rpx, 16px); }
.required-mark, .field-error { color: $brand-red; }
.field-error { display: block; margin-top: 12rpx; font-size: clamp(14px, 22rpx, 17px); }
.video-panel .app-button { margin-top: 28rpx; }
.video-state-card { min-height: 340rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.video-state-card .app-button { margin-top: 28rpx; }
</style>