600c536367
Co-authored-by: Cursor <cursoragent@cursor.com>
166 lines
3.6 KiB
Vue
166 lines
3.6 KiB
Vue
<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>
|