Files
MincedPad/frontend/src/views/ItemView.vue
T
2026-07-14 13:02:11 +08:00

166 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import MarkdownIt from 'markdown-it'
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { fetchItem, itemFileUrl, type Item } from '../api/client'
const md = new MarkdownIt({
html: false,
linkify: true,
breaks: true,
})
const route = useRoute()
const item = ref<Item | null>(null)
const loading = ref(true)
const error = ref('')
const showBurnAlert = ref(false)
const renderedBody = computed(() => {
if (!item.value?.body) return ''
return md.render(item.value.body)
})
const isImage = computed(() => {
const mime = item.value?.mime || ''
if (!mime.startsWith('image/')) return false
if (mime === 'image/svg+xml') return false
const name = (item.value?.file_name || '').toLowerCase()
return !name.endsWith('.svg')
})
const fileUrl = computed(() => {
if (!item.value || item.value.kind !== 'file') return ''
return itemFileUrl(item.value.slug)
})
async function load() {
const slug = String(route.params.slug || '')
loading.value = true
error.value = ''
item.value = null
showBurnAlert.value = false
try {
const data = await fetchItem(slug)
item.value = data
showBurnAlert.value = data.burn_after_read
} catch (err) {
error.value = err instanceof Error ? err.message : '加载失败'
ElMessage.error(error.value)
} finally {
loading.value = false
}
}
onMounted(load)
watch(
() => route.params.slug,
() => {
void load()
},
)
</script>
<template>
<div v-loading="loading" class="item-view">
<el-alert
v-if="showBurnAlert"
class="burn-alert"
type="warning"
title="阅后即焚"
description="此内容仅可查看一次,离开或刷新后将无法再次打开。"
show-icon
:closable="true"
@close="showBurnAlert = false"
/>
<el-result v-if="!loading && error" icon="warning" :title="error || '未找到'" />
<template v-else-if="item">
<h2 class="title">{{ item.title || item.file_name || item.slug }}</h2>
<div class="meta">
<span>{{ item.kind === 'file' ? '文件' : '文本' }}</span>
<span v-if="item.burn_after_read">阅后即焚</span>
<span v-if="!item.is_public">仅链接可见</span>
</div>
<div v-if="item.kind === 'text'" class="markdown" v-html="renderedBody" />
<div v-else class="file-block">
<template v-if="isImage">
<img class="preview" :src="fileUrl" :alt="item.file_name || item.title" />
</template>
<template v-else>
<p>文件{{ item.file_name || item.slug }}</p>
<el-button type="primary" class="full-width-xs" tag="a" :href="fileUrl" download>
下载文件
</el-button>
</template>
</div>
</template>
</div>
</template>
<style scoped>
.item-view {
min-height: 200px;
}
.burn-alert {
margin-bottom: 16px;
}
.title {
margin: 0 0 8px;
font-size: 1.25rem;
word-break: break-word;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
margin-bottom: 16px;
color: #909399;
font-size: 0.9rem;
}
.markdown {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 14px 16px;
overflow-x: auto;
line-height: 1.6;
word-break: break-word;
}
.markdown :deep(pre) {
overflow-x: auto;
padding: 10px;
background: #f5f7fa;
border-radius: 6px;
}
.markdown :deep(img) {
max-width: 100%;
height: auto;
}
.file-block {
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 14px;
}
.preview {
display: block;
max-width: 100%;
height: auto;
border-radius: 4px;
}
</style>