feat:重构模块
This commit is contained in:
180
src/components/Journal/JournalCard.vue
Normal file
180
src/components/Journal/JournalCard.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div class="journal-note card-item">
|
||||
<div class="journal-head" @click="editClick">
|
||||
<div class="head-left">
|
||||
<span class="time">
|
||||
{{ formatDate(props.item.date, 'HH:mm') }}
|
||||
</span>
|
||||
|
||||
<span v-if="props.item?.location" class="location">
|
||||
<i class="fa-solid fa-location-dot"></i>
|
||||
{{ props.item.location }}
|
||||
</span>
|
||||
|
||||
<el-tag v-if="props.item?.category" type="primary">
|
||||
{{ props.item.category }}
|
||||
</el-tag>
|
||||
|
||||
<span v-if="props.item?.mood" class="emoji">
|
||||
{{ props.item.mood }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<svgIcon v-if="props.item?.weather" :name="`weather-${props.item?.weather}`"/>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="journal-body">
|
||||
<!-- 可折叠文本 -->
|
||||
<div
|
||||
ref="textContent"
|
||||
class="text-content"
|
||||
:class="{ 'collapsed': !expanded }"
|
||||
@click="toggleExpand">
|
||||
{{ props.item.content }}
|
||||
</div>
|
||||
|
||||
<!-- 单媒体内容(图片优先于视频) -->
|
||||
<el-image
|
||||
v-if="props.item?.imageUrl"
|
||||
:src="serviceFileRootPath + props.item?.imageUrl"
|
||||
:preview-src-list="[serviceFileRootPath + props.item?.imageUrl]"
|
||||
fit="contain"
|
||||
class="media-content"/>
|
||||
|
||||
<video
|
||||
v-if="props.item?.videoUrl"
|
||||
controls
|
||||
class="media-content">
|
||||
<source :src="serviceFileRootPath + props.item?.videoUrl" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<!-- 展开/收起按钮 -->
|
||||
<el-button
|
||||
v-if="showToggle"
|
||||
text class="toggle-btn"
|
||||
@click.stop="toggleExpand">
|
||||
<el-icon>
|
||||
<ArrowUp v-if="expanded" />
|
||||
<ArrowDown v-else />
|
||||
</el-icon>
|
||||
{{ expanded ? '收起内容' : `展开全文(${overflowLines}行)` }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, PropType, defineProps } from 'vue'
|
||||
import { ElTag, ElButton, ElImage } from 'element-plus'
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
import { ArrowUp, ArrowDown } from '@element-plus/icons-vue'
|
||||
import { IJournal } from '@/types/journal.ts'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { useJournalStore } from '@/store/journal.ts'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
required: true,
|
||||
type: Object as PropType<IJournal>
|
||||
}
|
||||
})
|
||||
|
||||
const journalStore = useJournalStore()
|
||||
const appStore = useAppStore()
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
if (overflowLines.value <= 0) expanded.value = true
|
||||
})
|
||||
|
||||
// 文本展开逻辑
|
||||
const expanded = ref(false)
|
||||
const textContent = ref<HTMLElement>()
|
||||
const maxLines = 3
|
||||
|
||||
const overflowLines = computed(() => {
|
||||
if (!textContent.value) return 0
|
||||
const lineHeight = parseInt(getComputedStyle(textContent.value).lineHeight, 10)
|
||||
return Math.round((textContent.value.scrollHeight - textContent.value.clientHeight) / lineHeight)
|
||||
})
|
||||
|
||||
const showToggle = computed(() => overflowLines.value > 0)
|
||||
|
||||
const toggleExpand = () => expanded.value = !expanded.value
|
||||
|
||||
const editClick = async () => {
|
||||
const id = props.item?.id
|
||||
journalStore.journalApiType = 'UPDATE'
|
||||
journalStore.currentJournal = props.item
|
||||
|
||||
if (isMobile()) {
|
||||
appStore.isShowMobileBack = true
|
||||
await router.push({ name: 'JournalDetailId', params: { id } })
|
||||
} else {
|
||||
journalStore.journalDialog = {
|
||||
title: '编辑日记',
|
||||
visible: true,
|
||||
data: deepCopyObject(journalStore.currentJournal)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.journal-note {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.journal-head {
|
||||
padding: 10px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.head-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: darkgrey;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.journal-body {
|
||||
padding: 10px;
|
||||
|
||||
.text-content {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
line-height: 2;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&.collapsed {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: v-bind(maxLines);
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.media-content {
|
||||
width: 100%;
|
||||
// height: 300px;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
padding: 0;
|
||||
color: #999;
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user