Files
gxwebsoft 2b69686795 feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
2026-09-08 12:13:44 +08:00

123 lines
3.4 KiB
Vue
Raw Permalink 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.
<template>
<div v-if="list.length" class="mt-10 border-t border-gray-100 pt-8">
<h2 class="text-xl font-bold text-gray-900 mb-5">{{ title }}</h2>
<ul class="space-y-3">
<li
v-for="(file, idx) in sortedList"
:key="(file.url || '') + idx"
class="flex items-center gap-3 rounded-lg border border-gray-200 px-4 py-3 transition hover:border-blue-400 hover:shadow-sm"
>
<!-- 文件图标按扩展名换色统一用内联 SVG避免引入图标库依赖 -->
<span class="shrink-0" :style="{ color: iconColor(file.ext) }">
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14 2v6h6" />
</svg>
</span>
<!-- 文件名可点击下载 -->
<a
:href="resolveUrl(file.url)"
:download="file.name"
target="_blank"
rel="noopener"
class="min-w-0 flex-1 truncate text-blue-600 hover:underline"
:title="file.name"
>
{{ file.name }}
</a>
<!-- 扩展名徽标 -->
<span
v-if="file.ext"
class="shrink-0 rounded bg-gray-100 px-2 py-0.5 text-xs font-medium uppercase text-gray-500"
>{{ file.ext }}</span>
<!-- 文件大小 -->
<span v-if="file.size" class="shrink-0 text-xs text-gray-400">{{ formatSize(file.size) }}</span>
<!-- 下载入口 -->
<a
:href="resolveUrl(file.url)"
:download="file.name"
target="_blank"
rel="noopener"
class="shrink-0 text-sm text-blue-600 hover:underline"
>下载</a>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { PageAttachment } from '~/types'
import { ensureFullUrl } from '~/utils/image'
const props = withDefaults(
defineProps<{
/** 附件列表(来自 pageData.attachments */
attachments?: PageAttachment[]
/** 区块标题,默认「相关附件」 */
title?: string
}>(),
{
attachments: () => [],
title: '相关附件'
}
)
const list = computed<PageAttachment[]>(() => props.attachments || [])
/** 按 sort 升序,sort 缺失的排后面 */
const sortedList = computed<PageAttachment[]>(() =>
[...list.value].sort((a, b) => (a.sort ?? 9999) - (b.sort ?? 9999))
)
function resolveUrl(url?: string) {
return url ? ensureFullUrl(url) : ''
}
/** 字节转可读大小 */
function formatSize(bytes?: number) {
if (!bytes || bytes <= 0) return ''
const units = ['B', 'KB', 'MB', 'GB']
let val = bytes
let i = 0
while (val >= 1024 && i < units.length - 1) {
val /= 1024
i++
}
return `${val.toFixed(i === 0 ? 0 : 1)} ${units[i]}`
}
/** 不同扩展名用不同图标色,便于一眼区分文件类型 */
function iconColor(ext?: string) {
const map: Record<string, string> = {
pdf: '#e5484d',
doc: '#2b6cee',
docx: '#2b6cee',
xls: '#1f9d55',
xlsx: '#1f9d55',
csv: '#1f9d55',
ppt: '#e8833a',
pptx: '#e8833a',
zip: '#8a6df0',
rar: '#8a6df0',
'7z': '#8a6df0',
txt: '#6b7280'
}
return (ext && map[ext.toLowerCase()]) || '#6b7280'
}
</script>