ct/app/components/news/new.vue
2025-10-13 09:07:28 +08:00

525 lines
14 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 lang="ts" setup>
import {onMounted, ref, watch} from "vue";
// 组件属性
interface Props {
articleData?: {
title: string
publishDate: string
category: string
content: string
prevArticle: {
title: string
url: string
}
nextArticle: {
title: string
url: string
}
}
loading?: boolean
error?: string
}
const props = withDefaults(defineProps<Props>(), {
articleData: () => ({
title: '',
publishDate: '',
category: '',
content: '',
prevArticle: {
title: '',
url: '#'
},
nextArticle: {
title: '',
url: '#'
}
}),
loading: true,
error: ''
})
// API基础地址
const API_BASE_URL = 'http://192.168.2.77:4101'
// 内部文章数据(当没有外部传入时使用)
const internalArticleData = ref({
title: '',
publishDate: '',
category: '',
content: '',
prevArticle: {
title: '',
url: '#'
},
nextArticle: {
title: '',
url: '#'
}
})
// 内部加载状态
const internalLoading = ref(true)
const internalError = ref('')
// 使用外部传入的数据或内部数据
const articleData = computed(() => props.articleData || internalArticleData.value)
const loading = computed(() => props.loading !== undefined ? props.loading : internalLoading.value)
const error = computed(() => props.error || internalError.value)
// 获取文章详情
const fetchArticle = async (id: number) => {
try {
internalLoading.value = true
internalError.value = ''
const response = await fetch(`${API_BASE_URL}/app/owArticle/get/${id}`)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const res = await response.json()
const data = res.data
console.log('api请求数据', data)
// 分类映射配置
const categoryMapping: Record<string, string> = {
'solution': '解决方案',
'developKnowledge': '开发知识',
'industryTrend': '行业动态',
'aboutUs': '关于我们'
}
data.code = categoryMapping[data.code] || '暂无分类'
// 更新内部文章数据
internalArticleData.value = {
title: data.title || '暂无标题',
publishDate: data.createTime || '暂无日期',
category: data.code || '暂无分类',
content: data.content || '暂无内容',
prevArticle: data.prevArticle || {
title: '暂无上一篇',
url: '#'
},
nextArticle: data.nextArticle || {
title: '暂无下一篇',
url: '#'
}
}
} catch (err) {
console.error('获取文章失败:', err)
internalError.value = '获取文章失败,请稍后重试'
} finally {
internalLoading.value = false
}
}
// 热门标签
const hotTags = ref([
'景区单车', '政务平台', '共享经济', '共享汽车APP开发', '共享类',
'共享汽车软件开发', '共享雨伞APP开发', '共享雨伞软件开发', '共享货车开发'
])
// 推荐文章数据
const recommendedArticles = ref({
solutions: [
{
title: '创特景区SaaS平台 打造专业景区共享出行综合解决方案',
date: '2025-07-08',
url: '/article/437'
},
{title: '景区共享自行车运营方案都有哪些?', date: '2023-05-12', url: '/article/407'},
{
title: '为什么说景区共享代步车的未来发展空间巨大?',
date: '2023-05-08',
url: '/article/401'
},
{
title: '景区共享单车具备什么功能特点和收费标准?',
date: '2023-04-14',
url: '/article/394'
},
{title: '新冠疫情成为共享电单车行业新契机!', date: '2020-09-08', url: '/article/300'}
],
knowledge: [
{
title: '共享电动车为何能在共享经济中独树一帜?',
date: '2023-06-29',
url: '/article/426'
},
{title: '共享经济项目为何能受到大众的青睐?', date: '2023-05-16', url: '/article/409'},
{title: '景区共享智能代步车运营方案有哪些?', date: '2023-05-10', url: '/article/402'},
{
title: '创特景区共享代步车系统具备什么样的优势?',
date: '2023-04-07',
url: '/article/391'
},
{title: '做共享电动车能赚到钱吗', date: '2020-03-07', url: '/article/308'}
],
industry: [
{
title: '景区共享电动代步车如何引领景区发展趋势?',
date: '2024-12-11',
url: '/article/432'
},
{title: '景区共享电动代步车有几种合作模式?', date: '2023-07-19', url: '/article/431'},
{title: '景区共享观光代步车为何如此火爆?', date: '2023-07-13', url: '/article/430'},
{
title: '2023年了共享电动车项目还具备投资价值吗',
date: '2023-07-04',
url: '/article/428'
},
{title: '共享电动车项目投资大吗?', date: '2023-07-03', url: '/article/427'}
]
})
onMounted(() => {
// 加载CSS样式文件
loadCSSFiles()
// 加载JavaScript文件
loadJSFiles()
// 如果没有外部传入的文章数据则获取文章详情使用id=1进行测试
if (!props.articleData || !props.articleData.title) {
fetchArticle(1)
}
})
const loadJSFiles = () => {
const jsFiles = [
'/news/jquery.1.11.3.min.js',
'/news/bootstrap.min.js',
'/news/float.js',
]
jsFiles.forEach(src => {
// 检查是否已经加载过该JS文件
const existingScript = document.querySelector(`script[src="${src}"]`)
if (!existingScript) {
const script = document.createElement('script')
script.src = src
script.type = 'text/javascript'
document.head.appendChild(script)
}
})
}
const loadCSSFiles = () => {
const cssFiles = [
'/news/bootstrap.min.css',
'/news/main22.css',
'/news/new_index.css',
'/news/float.css',
'/news/animate.min.css'
]
cssFiles.forEach(href => {
// 检查是否已经加载过该CSS文件
const existingLink = document.querySelector(`link[href="${href}"]`)
if (!existingLink) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = href
link.type = 'text/css'
document.head.appendChild(link)
}
})
}
</script>
<template>
<view>
<!-- 顶部 -->
<nav class="navbar navbar-default navbar-fixed-top" style="background: rgba(255,255,255,1);">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" data-target="#mynav" data-toggle="collapse">
<span>创特科技</span>
</button>
<a href="/"><img
class="img-responsive"
src="/news/top_logo.png"
style='margin-top:4px'></a>
</div>
<div id="mynav" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="hidden-xs hidden-sm"><a href="/">首页 <span class="sr-only">(current)</span></a>
</li>
<li class="dropdown">
<a data-toggle="dropdown" role="button">解决方案 <span class="caret"/></a>
<ul class="dropdown-menu">
<li><a class="menu-group" href="javascript:void(0);">共享</a></li>
<li><a href="/sharedSolutions/bike">共享单车</a></li>
<li><a href="/sharedSolutions/carShare">共享汽车</a></li>
<li><a href="/sharedSolutions/sharedbed">共享陪护床</a></li>
<li><a href="/sharedSolutions/scooter">共享滑板车</a></li>
<li><a href="/sharedSolutions/eBike">共享助力车</a></li>
</ul>
</li>
<li class="dropdown">
<a
aria-expanded="false" aria-haspopup="true" class="dropdown-toggle"
data-toggle="dropdown" href="/article/437#" role="button">定制开发 <span
class="caret"/></a>
<ul class="dropdown-menu">
<li><a href="/softwareDevelopment/app">APP开发</a></li>
<li class="divider" role="separator"/>
<li><a href="/softwareDevelopment/miniprogram">小程序开发</a></li>
</ul>
</li>
<li><a href="/about">关于创特 </a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="/">电话0755-85225123
<!-- <span class="hidden-xs hidden-sm"> 0755-85225123 / </span> 18123752516 --></a></li>
</ul>
</div><!-- /.navbar-collapse -->
</div>
</nav>
<!-- 文章正文 -->
<section class="container" style="margin-top: 88px;">
<div class="row">
<!-- 左边部分 -->
<div class="col-md-8 wow fadeInLeft animated" style="visibility: visible; animation-name: fadeInLeft;">
<!-- 加载状态 -->
<div v-if="loading" class="loading-container" style="text-align: center; padding: 50px;">
<div
class="loading-spinner"
style="border: 4px solid #f3f3f3; border-top: 4px solid #ff8200; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 0 auto;"/>
<p style="margin-top: 20px; color: #666;">正在加载文章...</p>
</div>
<!-- 错误提示 -->
<div v-else-if="error" class="error-container" style="text-align: center; padding: 50px; color: #ff6b6b;">
<p>{{ error }}</p>
<button
style="margin-top: 20px; padding: 10px 20px; background: #ff8200; color: white; border: none; border-radius: 4px; cursor: pointer;"
@click="() => { if (!props.articleData || !props.articleData.title) fetchArticle(1) }">
重新加载
</button>
</div>
<!-- 文章内容 -->
<div v-else class="row">
<div class="col-xs-12">
<h2>{{ articleData.title }}</h2>
<p id="label" style="border-bottom: 1px solid #ddd; margin-top: 20px;">
<span>{{ articleData.publishDate }}</span>
<span class="pull-right">分类:{{ articleData.category }}</span>
</p>
<div class="article-content" v-html="articleData.content"/>
</div>
</div>
<!-- 上一篇/下一篇 -->
<div class="nextorpre" style="margin-top: 38px; border-top: 1px solid #ddd; padding-top: 8px;">
<li class="pull-left">上一篇:
<a :href="articleData.prevArticle.url">{{ articleData.prevArticle.title }}</a>
</li>
<li class="pull-right">下一篇:
<a :href="articleData.nextArticle.url">{{ articleData.nextArticle.title }}</a>
</li>
</div>
<br><br><br><br>
</div><!--左边部分-->
<!-- 右边部分 -->
<aside class="col-md-4" style="border-left: 1px solid #eee; background: #fefefe;">
<!-- 热门标签 -->
<div
class="widget tags wow fadeInRight animated"
style="margin-bottom: 30px !important; visibility: visible; animation-name: fadeInRight;">
<h4>热门标签</h4>
<ul class="tag-cloud">
<li v-for="tag in hotTags" :key="tag">
<a class="btn btn-xs" href="#" @click.prevent>{{ tag }}</a>
</li>
</ul>
</div><!-- 热门标签 -->
<!-- 推荐文章 -->
<div
class="sy_news animated" data-wow-delay="200ms"
style="visibility: visible; animation-delay: 200ms; animation-name: fadeInDown;">
<RecommendedArticles
:articles-per-type="3"
:show-title="false"
:show-types="['solution', 'developKnowledge', 'industryTrend']"
/>
</div>
</aside><!-- 右边部分 -->
</div><!--/.row-->
</section><!--/#blog-->
</view>
</template>
<style scoped>
/* 文章内容样式 */
.article-content {
margin-top: 20px;
line-height: 1.8;
font-size: 14px;
color: #333;
}
.article-content p {
margin-bottom: 15px;
}
.article-content img {
max-width: 100%;
height: auto;
display: block;
margin: 15px 0;
}
/* 文本截断样式 */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 响应式调整 */
@media (max-width: 1024px) {
.grid-cols-4 {
grid-template-columns: 1fr;
}
.col-md-8, .col-md-4 {
width: 100%;
margin-bottom: 20px;
}
}
/* 悬停效果 */
.hover\:bg-gray-50:hover {
background-color: rgb(249 250 251);
}
.dark .hover\:bg-gray-50:hover {
background-color: rgb(55 65 81);
}
.hover\:bg-gray-100:hover {
background-color: rgb(243 244 246);
}
.dark .hover\:bg-gray-100:hover {
background-color: rgb(75 85 99);
}
/* 标签云样式 */
.tag-cloud li {
display: inline-block;
margin: 2px;
}
.tag-cloud a {
display: inline-block;
padding: 4px 8px;
background-color: #f5f5f5;
color: #666;
text-decoration: none;
border-radius: 3px;
font-size: 12px;
transition: all 0.3s ease;
}
.tag-cloud a:hover {
background-color: #ff8200;
color: #fff;
}
/* 推荐文章样式 */
.sy_news ul li {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.sy_news ul li:last-child {
border-bottom: none;
}
.sy_news ul li a {
color: #333;
text-decoration: none;
font-size: 13px;
line-height: 1.4;
display: block;
transition: color 0.3s ease;
}
.sy_news ul li a:hover {
color: #ff8200;
}
.sy_news ul li a span {
color: #999;
font-size: 12px;
}
/* 上一篇下一篇样式 */
.nextorpre {
display: flex;
justify-content: space-between;
align-items: center;
}
.nextorpre li {
list-style: none;
margin: 0;
}
.nextorpre a {
color: #ff8200;
text-decoration: none;
}
.nextorpre a:hover {
text-decoration: underline;
}
/* 加载动画 */
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 加载状态样式 */
.loading-container {
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* 错误状态样式 */
.error-container {
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>