feat:重构模块

This commit is contained in:
2025-10-08 22:06:39 +08:00
parent f60d78fe8b
commit 2e81e57774
222 changed files with 2267 additions and 3059 deletions

View 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>