feat: 音频组件的字段对接

This commit is contained in:
2026-08-06 16:22:04 +08:00
parent cc1615162a
commit 0020c8bb47

View File

@@ -1,7 +1,7 @@
<template> <template>
<view class="discovery-audio-tool-card" hover-class="is-pressed" hover-stay-time="80" @tap="handleSelect"> <view class="discovery-audio-tool-card" hover-class="is-pressed" hover-stay-time="80" @tap="handleSelect">
<view class="audio-visual"> <view class="audio-visual">
<image v-if="normalizedItem.coverImage" class="audio-cover" :src="normalizedItem.coverImage" mode="aspectFill" /> <image v-if="normalizedItem.coverUrl" class="audio-cover" :src="normalizedItem.coverUrl" mode="aspectFill" />
<view v-else class="audio-cover audio-cover-fallback" /> <view v-else class="audio-cover audio-cover-fallback" />
<view class="audio-play"> <view class="audio-play">
<view class="audio-play-triangle" /> <view class="audio-play-triangle" />
@@ -13,12 +13,8 @@
<text class="audio-subtitle block w-full truncate">{{ normalizedItem.subTitle }}</text> <text class="audio-subtitle block w-full truncate">{{ normalizedItem.subTitle }}</text>
<view class="audio-progress-row w-full min-w-0"> <view class="audio-progress-row w-full min-w-0">
<view class="audio-progress-track"> <view class="audio-progress-track" />
<view class="audio-progress-fill" :style="{ width: `${normalizedItem.progress}%` }" /> <text class="audio-time block w-[90px] truncate">{{ normalizedItem.duration }}</text>
</view>
<text class="audio-time block w-[90px] truncate">
{{ normalizedItem.currentTime }} / {{ normalizedItem.duration }}
</text>
</view> </view>
</view> </view>
</view> </view>
@@ -27,15 +23,6 @@
<script setup> <script setup>
import { computed } from "vue"; import { computed } from "vue";
const DEFAULT_ITEM = {
id: "audio-guide",
title: "卧龙潭语音讲解",
subTitle: "解说:响水洞瀑布",
progress: 31,
currentTime: "02:15",
duration: "05:40",
};
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object, type: Object,
@@ -45,24 +32,36 @@ const props = defineProps({
const emit = defineEmits(["didSelectItem"]); const emit = defineEmits(["didSelectItem"]);
const clampProgress = (value) => { const formatDuration = (durationMs) => {
const progress = Number(value); if (durationMs === null || durationMs === undefined || durationMs === "") {
if (Number.isNaN(progress)) return 0; return "";
return Math.min(Math.max(progress, 0), 100); }
const value = Number(durationMs);
if (!Number.isFinite(value) || value < 0) return "";
const totalSeconds = Math.floor(value / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
}; };
const normalizedItem = computed(() => { const normalizedItem = computed(() => {
const source = Object.keys(props.item).length > 0 ? props.item : DEFAULT_ITEM; const source = props.item && typeof props.item === "object" ? props.item : {};
const resource = source.resource || {};
const summary = resource.summary || {};
const payload = resource.payload || {};
return { return {
raw: source, raw: source,
id: source.id ?? source.tabContentId ?? DEFAULT_ITEM.id, code: resource.code || "",
title: source.title || DEFAULT_ITEM.title, title: summary.title || "",
subTitle: source.subTitle || source.desc || DEFAULT_ITEM.subTitle, subTitle: summary.subtitle || "",
coverImage: source.coverImage || source.image || DEFAULT_ITEM.coverImage || "", coverUrl: summary.coverUrl || "",
progress: clampProgress(source.progress ?? DEFAULT_ITEM.progress), url: payload.url || "",
currentTime: source.currentTime || DEFAULT_ITEM.currentTime, mimeType: payload.mimeType || "",
duration: source.duration || DEFAULT_ITEM.duration, durationMs: payload.durationMs,
duration: formatDuration(payload.durationMs),
}; };
}); });