Spaces:
Sleeping
Sleeping
| <template> | |
| <div class="resource-card"> | |
| <div v-for="item in dataList" :key="item.id" class="resource-card__item"> | |
| <!-- 内容区域 --> | |
| <div class="item__content"> | |
| <!-- 左侧图片 --> | |
| <div class="content__image"> | |
| <van-image | |
| :src="getProxyImageUrl(item.image as string)" | |
| :fit="item.image ? 'cover' : 'contain'" | |
| lazy-load | |
| /> | |
| <!-- 来源标签移到图片左上角 --> | |
| <van-tag class="image__tag" :color="getTagColor(item.cloudType)" round> | |
| {{ item.cloudType }} | |
| </van-tag> | |
| </div> | |
| <!-- 右侧信息 --> | |
| <div class="content__info"> | |
| <!-- 标题 --> | |
| <div class="info__title" @click="copyUrl(item.cloudLinks[0])"> | |
| {{ item.title }} | |
| </div> | |
| <!-- 描述 - 添加展开收起功能 --> | |
| <div | |
| class="info__desc" | |
| :class="{ | |
| 'is-expanded': expandedItems[(item.messageId || '') + (item.channelId || '')], | |
| }" | |
| @click="toggleExpand((item.messageId || '') + (item.channelId || ''))" | |
| v-html="item.content" | |
| /> | |
| <!-- 底部区域:标签 --> | |
| <div class="info__footer"> | |
| <div v-if="item.tags?.length" class="info__tags"> | |
| <van-tag | |
| v-for="tag in item.tags" | |
| :key="tag" | |
| type="primary" | |
| plain | |
| round | |
| @click.stop="searchMovieforTag(tag)" | |
| > | |
| {{ tag }} | |
| </van-tag> | |
| </div> | |
| <!-- 转存按钮 --> | |
| <div class="info__action"> | |
| <van-button type="primary" size="mini" round plain @click="handleJump(item)"> | |
| 跳转 | |
| </van-button> | |
| <van-button | |
| v-if="item.isSupportSave" | |
| type="primary" | |
| size="mini" | |
| round | |
| @click="handleSave(item)" | |
| >转存</van-button | |
| > | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <script setup lang="ts"> | |
| import { computed, ref } from "vue"; | |
| import { useResourceStore } from "@/stores/resource"; | |
| import { showNotify } from "vant"; | |
| import type { ResourceItem } from "@/types"; | |
| import { getProxyImageUrl } from "@/utils/image"; | |
| // Props 定义 | |
| const props = defineProps<{ | |
| currentChannelId: string; | |
| }>(); | |
| // 事件定义 | |
| const emit = defineEmits<{ | |
| (e: "save", resource: ResourceItem): void; | |
| (e: "jump", resource: ResourceItem): void; | |
| (e: "searchMovieforTag", tag: string): void; | |
| }>(); | |
| // 状态管理 | |
| const store = useResourceStore(); | |
| // 计算属性 | |
| const dataList = computed(() => { | |
| const channel = store.resources.find((item) => item.id === props.currentChannelId); | |
| return channel?.list || []; | |
| }); | |
| // 标签颜色映射 | |
| const getTagColor = (type?: string) => { | |
| const colorMap: Record<string, string> = { | |
| pan115: "#07c160", | |
| quark: "#1989fa", | |
| }; | |
| return colorMap[type || ""] || "#ff976a"; | |
| }; | |
| // 方法定义 | |
| const handleSave = (resource: ResourceItem) => { | |
| emit("save", resource); | |
| }; | |
| const handleJump = (resource: ResourceItem) => { | |
| emit("jump", resource); | |
| }; | |
| const copyUrl = async (url: string) => { | |
| try { | |
| await navigator.clipboard.writeText(url); | |
| showNotify({ | |
| type: "success", | |
| message: "链接已复制到剪贴板", | |
| duration: 1500, | |
| }); | |
| } catch (err) { | |
| const input = document.createElement("input"); | |
| input.value = url; | |
| document.body.appendChild(input); | |
| input.select(); | |
| document.execCommand("copy"); | |
| document.body.removeChild(input); | |
| showNotify({ | |
| type: "success", | |
| message: "链接已复制到剪贴板", | |
| duration: 1500, | |
| }); | |
| } | |
| }; | |
| const searchMovieforTag = (tag: string) => { | |
| emit("searchMovieforTag", tag); | |
| }; | |
| // 展开状态管理 | |
| const expandedItems = ref<Record<string, boolean>>({}); | |
| // 切换展开状态 | |
| const toggleExpand = (id: string) => { | |
| expandedItems.value[id] = !expandedItems.value[id]; | |
| }; | |
| </script> | |
| <style lang="scss" scoped> | |
| // 文本省略混入 - 移到最前面 | |
| @mixin text-ellipsis($lines) { | |
| display: -webkit-box; | |
| -webkit-box-orient: vertical; | |
| -webkit-line-clamp: $lines; | |
| overflow: hidden; | |
| } | |
| .resource-card { | |
| padding: 5px 10px; | |
| &__item { | |
| margin-bottom: 12px; | |
| background: var(--theme-other_background); | |
| border-radius: var(--border-radius-lg); | |
| overflow: hidden; | |
| } | |
| } | |
| .item { | |
| &__content { | |
| display: flex; | |
| gap: 16px; | |
| padding: 16px; | |
| } | |
| } | |
| .content { | |
| &__image { | |
| position: relative; // 为标签定位 | |
| flex-shrink: 0; | |
| width: 100px; | |
| height: 140px; | |
| border-radius: var(--border-radius-sm); | |
| overflow: hidden; | |
| background: var(--van-gray-2); | |
| :deep(.van-image) { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .image__tag { | |
| position: absolute; | |
| top: 8px; | |
| left: 8px; | |
| font-size: 10px; | |
| padding: 0 6px; | |
| } | |
| } | |
| &__info { | |
| flex: 1; | |
| min-width: 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: var(--spacing-xs); | |
| } | |
| } | |
| .info { | |
| &__title { | |
| font-size: 15px; | |
| font-weight: 500; | |
| line-height: 1.4; | |
| color: var(--theme-color); | |
| @include text-ellipsis(2); | |
| &:active { | |
| opacity: 0.7; | |
| } | |
| } | |
| &__desc { | |
| position: relative; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| color: var(--van-gray-7); | |
| @include text-ellipsis(3); | |
| margin: 4px 0; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| &.is-expanded { | |
| -webkit-line-clamp: 8; | |
| } | |
| &::after { | |
| content: "展开"; | |
| position: absolute; | |
| right: 0; | |
| bottom: 0; | |
| padding: 0 4px; | |
| font-size: 12px; | |
| color: var(--theme-theme); | |
| background: var(--theme-other_background); | |
| } | |
| &.is-expanded::after { | |
| content: "收起"; | |
| } | |
| } | |
| &__footer { | |
| display: flex; | |
| flex-direction: column; | |
| gap: var(--spacing-xs); | |
| margin-top: auto; | |
| } | |
| &__tags { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 6px; | |
| :deep(.van-tag) { | |
| font-size: 11px; | |
| padding: 0 8px; | |
| } | |
| } | |
| &__action { | |
| display: flex; | |
| justify-content: flex-end; | |
| padding: 4px 0; | |
| .van-button { | |
| font-size: 13px; | |
| height: 32px; | |
| padding: 0 20px; | |
| :deep(.van-button__text) { | |
| font-weight: 500; | |
| font-size: 14px; | |
| } | |
| &:active { | |
| opacity: 0.8; | |
| } | |
| } | |
| } | |
| } | |
| </style> | |