175 lines
4.3 KiB
Vue
175 lines
4.3 KiB
Vue
<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="`${props.item?.imageUrl}${ThumbImageQuery}`"
|
||
fit="contain" @click="viewImageClick"
|
||
class="media-content"/>
|
||
|
||
<video
|
||
v-if="props.item?.videoUrl"
|
||
controls
|
||
class="media-content">
|
||
<source :src="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 { useJournalStore } from '@/store/journal.ts'
|
||
import { useAppStore } from '@/store/app.ts'
|
||
import { useRouter } from 'vue-router'
|
||
import { RawImageQuery, ThumbImageQuery } from '@/utils'
|
||
import { showImagePreview } from 'vant'
|
||
|
||
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 viewImageClick = () => {
|
||
showImagePreview({ images: [props.item?.imageUrl + RawImageQuery], closeable: true })
|
||
}
|
||
|
||
const editClick = async () => {
|
||
const id = props.item?.id
|
||
journalStore.journalApiType = 'UPDATE'
|
||
journalStore.currentJournal = props.item
|
||
|
||
appStore.isShowMobileBack = true
|
||
await router.push({ name: 'JournalDetailId', params: { id } })
|
||
}
|
||
</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>
|