618 lines
15 KiB
Vue
618 lines
15 KiB
Vue
<template>
|
|
<Header />
|
|
<div class="detail">
|
|
<div class="backimage"></div>
|
|
<div class="content">
|
|
<div class="content-left">
|
|
<div class="detail-header">
|
|
<h1>{{ article.title }}</h1>
|
|
<el-divider></el-divider>
|
|
<div class="detail-meta">
|
|
<div>
|
|
<i class="fa-regular fa-calendar"></i>
|
|
{{ article.publish_date }}
|
|
</div>
|
|
|
|
<div>
|
|
<i class="fa-solid fa-user-pen"></i>
|
|
{{ article.author }}
|
|
</div>
|
|
|
|
<div>
|
|
<i class="fa-solid fa-list"></i>
|
|
{{ article.catename }}
|
|
</div>
|
|
|
|
<div>
|
|
<i class="fa-regular fa-thumbs-up"></i>
|
|
{{ article.likes }}
|
|
</div>
|
|
|
|
<div>
|
|
<i class="fa-regular fa-eye"></i>
|
|
{{ article.views }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="detail-content" ref="contentRef">
|
|
<div v-html="article.content" class="content-html"></div>
|
|
</div>
|
|
|
|
<div class="theend">
|
|
<el-divider></el-divider>
|
|
<div class="the-end">The End</div>
|
|
<el-divider></el-divider>
|
|
</div>
|
|
|
|
<div class="endtool">
|
|
<div
|
|
style="
|
|
margin-bottom: 65px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 40px;
|
|
"
|
|
>
|
|
<div class="tool-item" @click="handleShareClick">
|
|
<div class="tool-icon">
|
|
<i class="fa-solid fa-share-nodes"></i>
|
|
</div>
|
|
<span class="tool-text">分享</span>
|
|
</div>
|
|
<div class="tool-item" @click="handleLikeClick">
|
|
<div class="tool-icon">
|
|
<i
|
|
class="fa-regular fa-thumbs-up"
|
|
:class="{ liked: article.liked }"
|
|
></i>
|
|
</div>
|
|
<span class="tool-text">
|
|
{{ article.liked ? '取消点赞' : '点赞' }}
|
|
</span>
|
|
</div>
|
|
<div class="tool-item">
|
|
<div class="tool-icon">
|
|
<i class="fa-regular fa-comment"></i>
|
|
</div>
|
|
<span class="tool-text">评论</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="article-nav">
|
|
<div
|
|
class="nav-item"
|
|
v-if="nextPreviousArticles.previous"
|
|
@click="goToArticle(nextPreviousArticles.previous.id)"
|
|
>
|
|
<div class="nav-label">
|
|
<i class="fa-solid fa-arrow-left"></i>
|
|
上一篇
|
|
</div>
|
|
<div class="nav-title">
|
|
{{ nextPreviousArticles.previous.title }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="nav-item"
|
|
v-if="nextPreviousArticles.next"
|
|
@click="goToArticle(nextPreviousArticles.next.id)"
|
|
>
|
|
<div class="nav-label">
|
|
下一篇
|
|
<i class="fa-solid fa-arrow-right"></i>
|
|
</div>
|
|
<div class="nav-title">{{ nextPreviousArticles.next.title }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="content-right">
|
|
<div class="card-item">
|
|
<div class="card-header">相关文章</div>
|
|
<el-divider></el-divider>
|
|
<div class="card-content">
|
|
<div
|
|
class="article-item"
|
|
v-for="item in relatedArticles"
|
|
:key="item.id"
|
|
>
|
|
<div class="thumb-img">
|
|
<img :src="imagePath(item.image)" alt="" />
|
|
</div>
|
|
<div class="article-title">{{ item.title }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Footer />
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { addLike, reduceLike } from '@/api/article'
|
|
import Header from '@/views/components/header.vue'
|
|
import Footer from '@/views/components/footer.vue'
|
|
import { getKingdeeNewsDetail, getCompanyNewsDetail } from '@/api/newscenter'
|
|
|
|
const route = useRoute()
|
|
const article = ref({})
|
|
const relatedArticles = ref([])
|
|
const nextPreviousArticles = ref<{
|
|
previous?: { id: number; title: string }
|
|
next?: { id: number; title: string }
|
|
}>({})
|
|
const loading = ref(true)
|
|
const { proxy } = getCurrentInstance()
|
|
const router = useRouter()
|
|
const contentRef = ref<HTMLElement | null>(null)
|
|
const VITE_APP_API_URL = import.meta.env.VITE_APP_API_URL
|
|
|
|
// 处理代码块复制功能
|
|
const handleCopyCode = async (e: Event) => {
|
|
const target = e.target as HTMLElement
|
|
const preElement = target.closest('.code-block-wrapper')
|
|
if (!preElement) return
|
|
|
|
const codeElement = preElement.querySelector('code')
|
|
if (!codeElement) return
|
|
|
|
const code = codeElement.innerText
|
|
try {
|
|
await navigator.clipboard.writeText(code)
|
|
proxy.$message.success('复制成功')
|
|
} catch (err) {
|
|
proxy.$message.error('复制失败,请手动复制')
|
|
}
|
|
}
|
|
|
|
// 为代码块添加复制按钮
|
|
const initCodeBlocks = () => {
|
|
if (!contentRef.value) return
|
|
|
|
const preElements = contentRef.value.querySelectorAll('pre')
|
|
preElements.forEach((pre) => {
|
|
if (pre.parentElement?.classList.contains('code-block-wrapper')) return
|
|
|
|
const wrapper = document.createElement('div')
|
|
wrapper.className = 'code-block-wrapper'
|
|
pre.parentNode?.insertBefore(wrapper, pre)
|
|
wrapper.appendChild(pre)
|
|
|
|
const copyBtn = document.createElement('button')
|
|
copyBtn.className = 'copy-code-btn'
|
|
copyBtn.innerHTML = '<i class="fa-regular fa-copy"></i>'
|
|
copyBtn.title = '复制代码'
|
|
copyBtn.onclick = handleCopyCode
|
|
wrapper.appendChild(copyBtn)
|
|
})
|
|
}
|
|
|
|
//分享功能
|
|
const handleShareClick = async () => {
|
|
const url = window.location.href
|
|
try {
|
|
await navigator.clipboard.writeText(url)
|
|
proxy.$message.success('复制地址成功')
|
|
} catch (err) {
|
|
proxy.$message.error('复制失败,请手动复制地址')
|
|
}
|
|
}
|
|
|
|
//点赞增加
|
|
const handleLikeClick = async () => {
|
|
if (article.value.liked) {
|
|
await reduceLike(article.value.id)
|
|
article.value.likes--
|
|
article.value.liked = false
|
|
} else {
|
|
await addLike(article.value.id)
|
|
article.value.likes++
|
|
article.value.liked = true
|
|
}
|
|
}
|
|
|
|
//拼接接口图片路径
|
|
const imagePath = (path: string) => {
|
|
if (!path) {
|
|
return new URL('@/assets/images/noimage.png', import.meta.url).href
|
|
}
|
|
return VITE_APP_API_URL + path
|
|
}
|
|
|
|
const goToArticle = (id: number) => {
|
|
if (route.path.includes('companyNews')) {
|
|
router.push(`/companyNews/detail/${id}`)
|
|
} else if (route.path.includes('kingdeeNews')) {
|
|
router.push(`/kingdeeNews/detail/${id}`)
|
|
} else {
|
|
router.push(`/kingdeeNews/detail/${id}`)
|
|
}
|
|
}
|
|
|
|
onMounted(async () => {
|
|
const id = route.params.id
|
|
loading.value = true
|
|
|
|
try {
|
|
let res
|
|
const path = route.path
|
|
|
|
if (path.includes('companyNews')) {
|
|
res = await getCompanyNewsDetail(id)
|
|
} else if (path.includes('kingdeeNews')) {
|
|
res = await getKingdeeNewsDetail(id)
|
|
} else {
|
|
res = await getKingdeeNewsDetail(id)
|
|
}
|
|
|
|
if (res.code === 200) {
|
|
article.value = res.data
|
|
nextTick(() => {
|
|
initCodeBlocks()
|
|
})
|
|
if (res.data.relatedArticles && res.data.relatedArticles.list) {
|
|
relatedArticles.value = res.data.relatedArticles.list.map(
|
|
(item: any) => ({
|
|
...item,
|
|
catename: item.cate,
|
|
}),
|
|
)
|
|
}
|
|
if (res.data.nextPreviousArticles) {
|
|
nextPreviousArticles.value = res.data.nextPreviousArticles
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('获取文章详情失败:', err)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.detail {
|
|
padding-top: 120px;
|
|
background-color: #f8f8f8;
|
|
padding-bottom: 120px;
|
|
|
|
.content {
|
|
width: 1200px;
|
|
margin: 0 auto;
|
|
display: flex;
|
|
gap: 20px;
|
|
position: relative;
|
|
z-index: 1;
|
|
|
|
.content-left {
|
|
width: 70%;
|
|
border-radius: 10px;
|
|
background-color: #fff;
|
|
opacity: 0.9;
|
|
.detail-header {
|
|
margin-bottom: 20px;
|
|
padding: 50px 50px 0 50px;
|
|
|
|
.detail-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
color: #707070;
|
|
font-size: 14px;
|
|
padding-bottom: 20px;
|
|
}
|
|
}
|
|
|
|
.detail-content {
|
|
padding: 0 50px;
|
|
|
|
.content-html {
|
|
:deep(pre) {
|
|
position: relative;
|
|
padding-right: 50px;
|
|
}
|
|
}
|
|
|
|
/* 代码块包装器 */
|
|
:deep(.code-block-wrapper) {
|
|
position: relative;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
margin: 20px 0;
|
|
|
|
pre {
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
/* 复制按钮 */
|
|
:deep(.copy-code-btn) {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
width: 25px;
|
|
height: 25px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: rgba(255, 255, 255, 0.7);
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
transition: all 0.2s ease;
|
|
z-index: 10;
|
|
|
|
&:hover {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
color: #fff;
|
|
}
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
}
|
|
}
|
|
|
|
/* 代码样式 */
|
|
:deep(code) {
|
|
border-radius: 4px;
|
|
padding: 2px 6px;
|
|
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
|
font-size: 13px;
|
|
color: #f06b6b;
|
|
}
|
|
|
|
/* 代码块样式 */
|
|
:deep(pre) {
|
|
background: linear-gradient(135deg, #1e1e2e 0%, #2d2d3f 100%);
|
|
border-radius: 12px;
|
|
padding: 10px;
|
|
overflow-x: auto;
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
|
|
code {
|
|
background-color: transparent;
|
|
padding: 0;
|
|
padding-left: 10px;
|
|
color: #cdd6f4;
|
|
font-size: 13px;
|
|
line-height: 1.7;
|
|
}
|
|
}
|
|
|
|
:deep(img) {
|
|
width: 100%;
|
|
height: auto;
|
|
max-width: 100%;
|
|
display: block;
|
|
}
|
|
|
|
:deep(p) {
|
|
font-size: 14px;
|
|
line-height: 25px;
|
|
color: #606266;
|
|
}
|
|
}
|
|
|
|
.theend {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 20px;
|
|
margin: 60px;
|
|
.the-end {
|
|
width: 200px;
|
|
text-align: center;
|
|
color: #dcdfe6;
|
|
}
|
|
}
|
|
|
|
.endtool {
|
|
padding: 65px;
|
|
|
|
.tool-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
cursor: pointer;
|
|
transition: transform 0.3s ease;
|
|
|
|
&:hover {
|
|
transform: translateY(-5px);
|
|
|
|
.tool-icon {
|
|
background-color: #ff6b6b;
|
|
box-shadow: 0 8px 20px rgba(64, 158, 255, 0.4);
|
|
}
|
|
}
|
|
|
|
&:active {
|
|
transform: translateY(-2px);
|
|
|
|
.tool-icon {
|
|
background-color: #337ecc;
|
|
box-shadow: 0 4px 10px rgba(64, 158, 255, 0.3);
|
|
transform: scale(0.95);
|
|
}
|
|
}
|
|
|
|
&:has(.liked) {
|
|
.tool-icon {
|
|
background-color: #ff6b6b;
|
|
box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4);
|
|
}
|
|
|
|
.tool-text {
|
|
color: #ff6b6b;
|
|
}
|
|
}
|
|
|
|
.tool-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
background-color: #409eff;
|
|
color: #fff;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-size: 20px;
|
|
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.3);
|
|
transition: all 0.3s ease;
|
|
|
|
.fa-thumbs-up.liked {
|
|
color: #fff;
|
|
animation: likeAnimation 0.4s ease;
|
|
}
|
|
}
|
|
|
|
.tool-text {
|
|
font-size: 14px;
|
|
color: #606266;
|
|
font-weight: 500;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
&:hover .tool-text {
|
|
color: #409eff;
|
|
}
|
|
}
|
|
}
|
|
|
|
.article-nav {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 30px 65px;
|
|
border-top: 1px solid #f0f0f0;
|
|
|
|
.nav-item {
|
|
max-width: 45%;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
|
|
&:hover {
|
|
.nav-label {
|
|
color: #409eff;
|
|
}
|
|
.nav-title {
|
|
color: #409eff;
|
|
}
|
|
}
|
|
|
|
.nav-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 14px;
|
|
color: #909399;
|
|
margin-bottom: 10px;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.nav-title {
|
|
font-size: 14px;
|
|
color: #303133;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
transition: color 0.3s ease;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.content-right {
|
|
width: 30%;
|
|
opacity: 0.9;
|
|
|
|
.card-item {
|
|
// position: sticky;
|
|
top: 100px;
|
|
padding: 30px;
|
|
border-radius: 10px;
|
|
background-color: #fff;
|
|
|
|
.card-header {
|
|
font-size: 18px;
|
|
font-weight: 500;
|
|
}
|
|
.card-content {
|
|
.article-item {
|
|
padding-bottom: 20px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
margin-bottom: 20px;
|
|
&:last-child {
|
|
padding-bottom: 0;
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.thumb-img {
|
|
width: 100%;
|
|
overflow: hidden;
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
border-radius: 10px;
|
|
}
|
|
}
|
|
.article-title {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
margin-top: 10px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
color: #409eff;
|
|
transition: color 0.3s ease;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.backimage {
|
|
width: 800px;
|
|
height: 400px;
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
position: fixed;
|
|
right: 100px;
|
|
bottom: 100px;
|
|
pointer-events: none;
|
|
background-image: url('@/assets/images/sologen.png');
|
|
opacity: 0.2;
|
|
}
|
|
|
|
@keyframes likeAnimation {
|
|
0% {
|
|
transform: scale(1);
|
|
}
|
|
25% {
|
|
transform: scale(1.3);
|
|
}
|
|
50% {
|
|
transform: scale(0.9);
|
|
}
|
|
100% {
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
</style>
|