Compare commits
5 Commits
07ce8409e1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8193bab566 | ||
|
|
f4263af343 | ||
|
|
0dc24cfa85 | ||
|
|
183d98a699 | ||
|
|
ede67faafd |
@@ -34,12 +34,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="bot" :class="{ 'botrelative': scrollY }">
|
<div id="bot" :class="{ 'botrelative': scrollY }">
|
||||||
<el-tabs v-model="activeName" stretch="true" class="demo-tabs">
|
<el-tabs v-model="activeName" :stretch="true" class="demo-tabs">
|
||||||
<el-tab-pane label="个人简介" name="first">
|
<el-tab-pane label="个人简介" name="first">
|
||||||
<div class="mylogo">
|
<div class="mylogo">
|
||||||
<el-avatar class="mylogo_avatar" :src="state.circleUrl" />
|
<el-avatar class="mylogo_avatar" :src="state.circleUrl" />
|
||||||
</div>
|
</div>
|
||||||
<a href="#">
|
<a href="http://www.qf1121.top/">
|
||||||
<h6 class="mylogo_name logo-text">清疯不颠</h6>
|
<h6 class="mylogo_name logo-text">清疯不颠</h6>
|
||||||
</a>
|
</a>
|
||||||
<h6 class="mylogo_description">重度精神失常患者</h6>
|
<h6 class="mylogo_description">重度精神失常患者</h6>
|
||||||
@@ -51,13 +51,13 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="#" class="stat-link" @click.prevent="showCategories">
|
<a href="#" class="stat-link" @click.prevent="showCategoriesModel">
|
||||||
<span class="site-state-item-count">{{ categoryCount }}</span>
|
<span class="site-state-item-count">{{ categoryCount }}</span>
|
||||||
<span class="site-state-item-name">分类</span>
|
<span class="site-state-item-name">分类</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="#" class="stat-link" @click.prevent="showAttributes">
|
<a href="#" class="stat-link" @click.prevent="showAttributesModel">
|
||||||
<span class="site-state-item-count">{{ AttributeCount }}</span>
|
<span class="site-state-item-count">{{ AttributeCount }}</span>
|
||||||
<span class="site-state-item-name">标签</span>
|
<span class="site-state-item-name">标签</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -70,49 +70,7 @@
|
|||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 分类蒙板组件 -->
|
|
||||||
<Transition name="modal">
|
|
||||||
<div v-if="showCategoryModal" class="category-modal" @click.self="closeCategoryModal">
|
|
||||||
<div class="category-modal-content">
|
|
||||||
<div class="category-modal-header">
|
|
||||||
<h3>所有分类</h3>
|
|
||||||
<button class="category-modal-close" @click="closeCategoryModal">×</button>
|
|
||||||
</div>
|
|
||||||
<div class="category-modal-body">
|
|
||||||
<button
|
|
||||||
v-for="category in categories"
|
|
||||||
:key="category.typeid"
|
|
||||||
class="category-button"
|
|
||||||
@click="handleCategoryClick(category)"
|
|
||||||
>
|
|
||||||
{{ category.typename }} <span class="category-button-count">({{ category.count || 0 }})</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
|
|
||||||
<!-- 标签蒙板组件 -->
|
|
||||||
<Transition name="modal">
|
|
||||||
<div v-if="showAttributeModal" class="category-modal" @click.self="closeAttributeModal">
|
|
||||||
<div class="category-modal-content">
|
|
||||||
<div class="category-modal-header">
|
|
||||||
<h3>所有标签</h3>
|
|
||||||
<button class="category-modal-close" @click="closeAttributeModal">×</button>
|
|
||||||
</div>
|
|
||||||
<div class="category-modal-body">
|
|
||||||
<button
|
|
||||||
v-for="attribute in attributes"
|
|
||||||
:key="attribute.attributeid"
|
|
||||||
class="category-button"
|
|
||||||
@click="handleAttributeClick(attribute)"
|
|
||||||
>
|
|
||||||
{{ attribute.attributename }} <span class="category-button-count">({{ attribute.count || 0 }})</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -120,27 +78,22 @@
|
|||||||
import { reactive, ref, onMounted, onUnmounted } from 'vue'
|
import { reactive, ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { articleService, categoryService, categoryAttributeService } from "@/services";
|
import { articleService, categoryService, categoryAttributeService } from "@/services";
|
||||||
import { useGlobalStore } from '@/store/globalStore'
|
|
||||||
const globalStore = useGlobalStore()
|
|
||||||
|
|
||||||
// 当前激活菜单
|
// 当前激活菜单
|
||||||
const activeIndex = ref('/:type')
|
const activeIndex = ref('/:type')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const activeName = ref('first')
|
const activeName = ref('first')
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
circleUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
|
circleUrl: '/blogicon.jpg',
|
||||||
squareUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
|
squareUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
|
||||||
sizeList: ['small', '', 'large'] as const,
|
sizeList: ['small', '', 'large'] as const,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 分类相关状态
|
// 分类相关状态
|
||||||
const categories = ref<any[]>([])
|
// defineEmits
|
||||||
const showCategoryModal = ref(false)
|
const emit = defineEmits(['update-data', 'CategoryModal', 'AttributeModal'])
|
||||||
|
|
||||||
// 标签相关状态
|
// 标签相关状态
|
||||||
const attributes = ref<any[]>([])
|
|
||||||
const showAttributeModal = ref(false)
|
|
||||||
|
|
||||||
// 处理菜单选择跳转
|
// 处理菜单选择跳转
|
||||||
const handleSelect = (key: string) => {
|
const handleSelect = (key: string) => {
|
||||||
router.push({ path: key })
|
router.push({ path: key })
|
||||||
@@ -171,6 +124,9 @@ const fetchArticleCount = async () => {
|
|||||||
|
|
||||||
// 获取分类数据
|
// 获取分类数据
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async () => {
|
||||||
|
|
||||||
|
// 分类数据状态
|
||||||
|
const categories = ref<any[]>([])
|
||||||
try {
|
try {
|
||||||
const response = await categoryService.getAllCategories();
|
const response = await categoryService.getAllCategories();
|
||||||
// 如果API返回的数据结构不包含count属性,我们可以模拟一些数据
|
// 如果API返回的数据结构不包含count属性,我们可以模拟一些数据
|
||||||
@@ -179,7 +135,7 @@ const fetchCategories = async () => {
|
|||||||
count: 0
|
count: 0
|
||||||
})) || [];
|
})) || [];
|
||||||
categories.value.forEach(async (category: any) => {
|
categories.value.forEach(async (category: any) => {
|
||||||
const attributeResponse = await categoryAttributeService.getAttributesByCategory(category.typeid)
|
const attributeResponse = await categoryAttributeService.getAttributesByCategory(category.categoryid)
|
||||||
if (attributeResponse.data?.length) {
|
if (attributeResponse.data?.length) {
|
||||||
category.count = attributeResponse.data?.length || 0
|
category.count = attributeResponse.data?.length || 0
|
||||||
}
|
}
|
||||||
@@ -194,10 +150,13 @@ const fetchCategories = async () => {
|
|||||||
];
|
];
|
||||||
categoryCount.value = categories.value.length
|
categoryCount.value = categories.value.length
|
||||||
}
|
}
|
||||||
|
return categories.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取标签数据
|
// 获取标签数据
|
||||||
const fetchAttributes = async () => {
|
const fetchAttributes = async () => {
|
||||||
|
// 标签数据状态
|
||||||
|
const attributes = ref<any[]>([])
|
||||||
try {
|
try {
|
||||||
const response = await categoryAttributeService.getAllAttributes();
|
const response = await categoryAttributeService.getAllAttributes();
|
||||||
// 如果API返回的数据结构不包含count属性,我们可以模拟一些数据
|
// 如果API返回的数据结构不包含count属性,我们可以模拟一些数据
|
||||||
@@ -216,56 +175,27 @@ const fetchAttributes = async () => {
|
|||||||
console.error('获取标签失败:', error)
|
console.error('获取标签失败:', error)
|
||||||
// 如果API调用失败,使用模拟数据
|
// 如果API调用失败,使用模拟数据
|
||||||
attributes.value = [
|
attributes.value = [
|
||||||
|
|
||||||
];
|
];
|
||||||
AttributeCount.value = attributes.value.length
|
AttributeCount.value = attributes.value.length
|
||||||
}
|
}
|
||||||
|
return attributes.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示分类蒙板
|
// 向父组件传递标签数据
|
||||||
const showCategories = () => {
|
const sendData = () => {
|
||||||
showCategoryModal.value = true
|
const data = { fetchAttributes: fetchAttributes(), fetchCategories: fetchCategories() }
|
||||||
|
emit('update-data', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭分类蒙板
|
// 显示标签蒙版
|
||||||
const closeCategoryModal = () => {
|
const showAttributesModel = () => {
|
||||||
showCategoryModal.value = false
|
emit('AttributeModal', { ifmodal: true })
|
||||||
|
}
|
||||||
|
// 显示分类蒙版
|
||||||
|
const showCategoriesModel = () => {
|
||||||
|
emit('CategoryModal', { ifmodal: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理分类点击
|
|
||||||
const handleCategoryClick = (category: any) => {
|
|
||||||
// 这里可以根据实际需求跳转到对应分类的文章列表页
|
|
||||||
console.log('点击了分类:', category.typename)
|
|
||||||
// 示例:router.push(`/article-list?category=${category.typeid}`)
|
|
||||||
closeCategoryModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示标签蒙板
|
|
||||||
const showAttributes = () => {
|
|
||||||
showAttributeModal.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭标签蒙板
|
|
||||||
const closeAttributeModal = () => {
|
|
||||||
showAttributeModal.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理标签点击
|
|
||||||
const handleAttributeClick = (attribute: any) => {
|
|
||||||
// 重置全局属性状态
|
|
||||||
globalStore.removeValue('attribute')
|
|
||||||
|
|
||||||
globalStore.setValue('attribute', {
|
|
||||||
id: attribute.attributeid,
|
|
||||||
name: attribute.attributename
|
|
||||||
})
|
|
||||||
console.log(attribute)
|
|
||||||
router.push({
|
|
||||||
path: '/home/aericletype',
|
|
||||||
|
|
||||||
})
|
|
||||||
closeAttributeModal()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 控制底部模块吸顶效果
|
// 控制底部模块吸顶效果
|
||||||
const scrollY = ref(false)
|
const scrollY = ref(false)
|
||||||
@@ -277,8 +207,7 @@ const handleScroll = () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('scroll', handleScroll)
|
window.addEventListener('scroll', handleScroll)
|
||||||
fetchArticleCount() // 组件挂载时获取文章数量
|
fetchArticleCount() // 组件挂载时获取文章数量
|
||||||
fetchCategories() // 组件挂载时获取分类数据
|
sendData() // 组件挂载时获取标签数据和分类数据
|
||||||
fetchAttributes() // 组件挂载时获取标签数据
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -319,9 +248,11 @@ onUnmounted(() => {
|
|||||||
background-color: rgba(255, 255, 255, 0.9);
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
/* 白色半透明背景 */
|
/* 白色半透明背景 */
|
||||||
}
|
}
|
||||||
|
|
||||||
#cont .cont1 {
|
#cont .cont1 {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cont .cont2 {
|
#cont .cont2 {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
@@ -349,16 +280,12 @@ onUnmounted(() => {
|
|||||||
background-color: rgba(0, 0, 0, 0);
|
background-color: rgba(0, 0, 0, 0);
|
||||||
/* 白色半透明背景 */
|
/* 白色半透明背景 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.cont2 .el-menu-vertical-demo li {
|
.cont2 .el-menu-vertical-demo li {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cont2 .el-menu-vertical-demo .el-menu-item:nth-child(3) {
|
|
||||||
/* border-radius: 0 0 10px 10px; */
|
|
||||||
/* margin-bottom: 10px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.cont2 .el-menu-vertical-demo .el-menu-item:hover {
|
.cont2 .el-menu-vertical-demo .el-menu-item:hover {
|
||||||
background-color: rgba(64, 158, 255, 0.9);
|
background-color: rgba(64, 158, 255, 0.9);
|
||||||
}
|
}
|
||||||
@@ -429,6 +356,7 @@ onUnmounted(() => {
|
|||||||
/* 白色半透明背景 */
|
/* 白色半透明背景 */
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-state-item-count {
|
.site-state-item-count {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -444,9 +372,11 @@ onUnmounted(() => {
|
|||||||
margin-left: 100px;
|
margin-left: 100px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mylogo_name {
|
.mylogo_name {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mylogo_description {
|
.mylogo_description {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@@ -508,145 +438,6 @@ onUnmounted(() => {
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 分类蒙板样式 */
|
|
||||||
.category-modal {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1000;
|
|
||||||
/* 确保在任何情况下都能居中显示 */
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-content {
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 10px;
|
|
||||||
width: 90%;
|
|
||||||
max-width: 500px;
|
|
||||||
max-height: 80vh;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
||||||
/* 确保内容块在父容器中完美居中 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 15px 20px;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-close {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
font-size: 24px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #999;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-close:hover {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-body {
|
|
||||||
padding: 20px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
max-height: 60vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-button {
|
|
||||||
background-color: rgba(102, 161, 216, 0.1);
|
|
||||||
border: 1px solid rgba(102, 161, 216, 0.3);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 10px 15px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-button:hover {
|
|
||||||
background-color: rgba(102, 161, 216, 0.3);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 2px 8px rgba(102, 161, 216, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-button-count {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #66a1d8;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 蒙板动画 */
|
|
||||||
.modal-enter-active,
|
|
||||||
.modal-leave-active {
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter-from,
|
|
||||||
.modal-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter-active .category-modal-content,
|
|
||||||
.modal-leave-active .category-modal-content {
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-enter-from .category-modal-content {
|
|
||||||
transform: scale(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-leave-to .category-modal-content {
|
|
||||||
transform: scale(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 滚动条样式 */
|
|
||||||
.category-modal-body::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-body::-webkit-scrollbar-track {
|
|
||||||
background: #f1f1f1;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-body::-webkit-scrollbar-thumb {
|
|
||||||
background: #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-modal-body::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #999;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<el-row justify="center">
|
<el-row justify="center">
|
||||||
<el-col :span="6" v-if="windowwidth">
|
<el-col :span="6" v-if="windowwidth">
|
||||||
<div class="grid-content ep-bg-purple-dark">
|
<div class="grid-content ep-bg-purple-dark">
|
||||||
<div class="logo-text"> <a href="/">清疯不颠</a></div>
|
<div class="logo-text"> <a href="http://www.qf1121.top/">清疯不颠</a></div>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="14" justify="center">
|
<el-col :span="14" justify="center">
|
||||||
@@ -48,12 +48,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hero 区域 -->
|
<!-- Hero 区域 -->
|
||||||
<div class="hero" :class="{ 'newhero': classhero }" v-if="windowwidth">
|
<div class="hero" :class="{ 'small-hero': classsmallhero }" v-if="windowwidth"
|
||||||
|
:style="{ marginBottom: heroMarginBottom, transform: heroTransform }">
|
||||||
<h1 class="typewriter">{{ heroText }}</h1>
|
<h1 class="typewriter">{{ heroText }}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提示区域 -->
|
<!-- 内容区域 -->
|
||||||
<div id="content-section" :class="{ 'visible': isconts }">
|
<div id="content-section" :class="{ 'visible': iscontentvisible }">
|
||||||
<div class="nonsensetitle" v-if="classnonsenset">
|
<div class="nonsensetitle" v-if="classnonsenset">
|
||||||
<div class="nonsensetitleconst">
|
<div class="nonsensetitleconst">
|
||||||
<h1>{{ Cardtitle }}</h1>
|
<h1>{{ Cardtitle }}</h1>
|
||||||
@@ -62,18 +63,54 @@
|
|||||||
|
|
||||||
<!-- 左侧模块 -->
|
<!-- 左侧模块 -->
|
||||||
<div class="leftmodluecontainer" v-if="isleftmodluecontainer">
|
<div class="leftmodluecontainer" v-if="isleftmodluecontainer">
|
||||||
<LeftModule class="leftmodluepage" :class="{ 'nonsensetmargintop': classmoduleorrouter }" v-if="windowwidth" />
|
<LeftModule class="leftmodluepage" @update-data="updateData" @CategoryModal="CategoryModal"
|
||||||
|
@AttributeModal="AttributeModal" :class="{ 'nonsensetmargintop': classmoduleorrouter }" v-if="windowwidth" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 内容模块 -->
|
<!-- 内容模块 -->
|
||||||
<RouterView class="RouterViewpage"
|
<div class="RouterViewpage">
|
||||||
:class="{ 'forbidwidth': !isleftmodluecontainer, 'nonsensetmargintop': classmoduleorrouter }" />
|
<RouterView :class="{ 'forbidwidth': !isleftmodluecontainer, 'nonsensetmargintop': classmoduleorrouter }" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 分页区域 -->
|
|
||||||
<Establish class="establish-container" v-if="Login" />
|
|
||||||
<!-- 页脚 -->
|
<!-- 页脚 -->
|
||||||
<Footer class="footer-container" v-if="windowwidth" />
|
<Footer class="footer-container" v-if="windowwidth" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 分类蒙板组件 -->
|
||||||
|
<Transition name="modal">
|
||||||
|
<div v-if="showCategoryModal" class="category-modal" @click.self="closeCategoryModal">
|
||||||
|
<div class="category-modal-content">
|
||||||
|
<div class="category-modal-header">
|
||||||
|
<h3>所有分类</h3>
|
||||||
|
<button class="category-modal-close" @click="closeCategoryModal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="category-modal-body">
|
||||||
|
<button v-for="category in categories" :key="category.typeid" class="category-button"
|
||||||
|
@click="handleCategoryClick(category)">
|
||||||
|
{{ category.typename }} <span class="category-button-count">({{ category.count || 0 }})</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<!-- 标签蒙板组件 -->
|
||||||
|
<Transition name="modal">
|
||||||
|
<div v-if="showAttributeModal" class="category-modal" @click.self="closeAttributeModal">
|
||||||
|
<div class="category-modal-content">
|
||||||
|
<div class="category-modal-header">
|
||||||
|
<h3>所有标签</h3>
|
||||||
|
<button class="category-modal-close" @click="closeAttributeModal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="category-modal-body">
|
||||||
|
<button v-for="attribute in attributes" :key="attribute.attributeid" class="category-button"
|
||||||
|
@click="handleAttributeClick(attribute)">
|
||||||
|
{{ attribute.attributename }} <span class="category-button-count">({{ attribute.count || 0 }})</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
<!-- 管理员 -->
|
||||||
|
<Establish class="establish-container" v-if="Login" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -95,17 +132,27 @@ const globalStore = useGlobalStore();
|
|||||||
const Login = computed(() => globalStore.Login);
|
const Login = computed(() => globalStore.Login);
|
||||||
|
|
||||||
// ========== 响应式状态定义 ==========
|
// ========== 响应式状态定义 ==========
|
||||||
|
|
||||||
// 页面标题和样式相关状态
|
// 页面标题和样式相关状态
|
||||||
const Cardtitle = ref('');
|
const Cardtitle = ref('');
|
||||||
const classmoduleorrouter = ref(false);
|
const classmoduleorrouter = ref(false);
|
||||||
const classnonsenset = ref(false);
|
const classnonsenset = ref(false);
|
||||||
const classhero = ref(false);
|
const classsmallhero = ref(false);
|
||||||
const elrowtop = ref('transparent');
|
const elrowtop = ref('transparent');
|
||||||
|
// hero区域的margin值,用于实现滚动时动态变化
|
||||||
|
const heroMarginBottom = ref('45%');
|
||||||
|
// hero区域的初始margin值,从CSS变量获取
|
||||||
|
const initialHeroMarginBottom = 45;
|
||||||
|
// hero是否开始向上移动
|
||||||
|
const heroIsMoving = ref(false);
|
||||||
|
// hero的transform值,用于实现向上移动和吸附效果
|
||||||
|
const heroTransform = ref('translateY(450px)');
|
||||||
|
const heroTransformValue = 450;
|
||||||
|
// hero的位置状态:static(静态)、moving(移动中)、sticky(吸附顶部)
|
||||||
|
const heroPosition = ref('static');
|
||||||
|
|
||||||
// 布局相关状态
|
// 布局相关状态
|
||||||
const isleftmodluecontainer = ref(true);
|
const isleftmodluecontainer = ref(true);
|
||||||
const isconts = ref(false);
|
const iscontentvisible = ref(false);
|
||||||
const isScrollingleftmodlue = ref(false);
|
const isScrollingleftmodlue = ref(false);
|
||||||
const windowwidth = ref(true);
|
const windowwidth = ref(true);
|
||||||
|
|
||||||
@@ -125,13 +172,103 @@ const heroText = ref('');
|
|||||||
let heroIndex = 0;
|
let heroIndex = 0;
|
||||||
let heroTimer: number | undefined;
|
let heroTimer: number | undefined;
|
||||||
|
|
||||||
|
// 蒙版相关状态
|
||||||
|
const categories = ref<any[]>([])
|
||||||
|
const showCategoryModal = ref(false)
|
||||||
|
const attributes = ref<any[]>([])
|
||||||
|
const showAttributeModal = ref(false)
|
||||||
|
// 显示分类蒙板
|
||||||
|
const openCategoryModal = () => {
|
||||||
|
showCategoryModal.value = true;
|
||||||
|
}
|
||||||
|
// 关闭分类蒙板
|
||||||
|
const closeCategoryModal = () => {
|
||||||
|
showCategoryModal.value = false;
|
||||||
|
}
|
||||||
|
// 显示标签蒙板
|
||||||
|
const openAttributeModal = () => {
|
||||||
|
showAttributeModal.value = true;
|
||||||
|
}
|
||||||
|
// 关闭标签蒙板
|
||||||
|
const closeAttributeModal = () => {
|
||||||
|
showAttributeModal.value = false;
|
||||||
|
}
|
||||||
|
// 左侧状态栏传值
|
||||||
|
const updateData = (data: any) => {
|
||||||
|
// 处理异步数据
|
||||||
|
if (data.fetchCategories && typeof data.fetchCategories.then === 'function') {
|
||||||
|
data.fetchCategories.then(result => {
|
||||||
|
categories.value = result || []
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
categories.value = data.fetchCategories || []
|
||||||
|
}
|
||||||
|
if (data.fetchAttributes && typeof data.fetchAttributes.then === 'function') {
|
||||||
|
data.fetchAttributes.then(result => {
|
||||||
|
attributes.value = result || []
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
attributes.value = data.fetchAttributes || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 分类相关状态
|
||||||
|
const CategoryModal = async (data: any) => {
|
||||||
|
if (data.ifmodal) {
|
||||||
|
openCategoryModal()
|
||||||
|
// console.log('打开分类蒙板')
|
||||||
|
} else {
|
||||||
|
closeCategoryModal()
|
||||||
|
// console.log('关闭分类蒙板')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 标签相关状态
|
||||||
|
const AttributeModal = async (data: any) => {
|
||||||
|
if (data.ifmodal) {
|
||||||
|
openAttributeModal()
|
||||||
|
// console.log('打开标签蒙板')
|
||||||
|
} else {
|
||||||
|
closeAttributeModal()
|
||||||
|
// console.log('关闭标签蒙板')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ========== 蒙版事件 ==========
|
||||||
|
|
||||||
|
// 处理分类点击
|
||||||
|
const handleCategoryClick = (category: any) => {
|
||||||
|
// 这里可以根据实际需求跳转到对应分类的文章列表页
|
||||||
|
// 重置全局属性状态
|
||||||
|
globalStore.removeValue('category')
|
||||||
|
|
||||||
|
globalStore.setValue('category', {
|
||||||
|
id: category.categoryid,
|
||||||
|
name: category.categoryname
|
||||||
|
})
|
||||||
|
console.log(category)
|
||||||
|
router.push('/home/aericlecategory',)
|
||||||
|
closeCategoryModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理标签点击
|
||||||
|
const handleAttributeClick = (attribute: any) => {
|
||||||
|
// 重置全局属性状态
|
||||||
|
globalStore.removeValue('attribute')
|
||||||
|
|
||||||
|
globalStore.setValue('attribute', {
|
||||||
|
id: attribute.attributeid,
|
||||||
|
name: attribute.attributename
|
||||||
|
})
|
||||||
|
console.log(attribute)
|
||||||
|
router.push('/home/aericletype',)
|
||||||
|
closeAttributeModal()
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 打字机效果模块 ==========
|
// ========== 打字机效果模块 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化并启动打字机效果
|
* 初始化并启动打字机效果
|
||||||
* @param {string} text - 要显示的完整文本
|
* @param {string} text - 要显示的完整文本
|
||||||
*/
|
*/
|
||||||
const startTypewriter = () => {
|
const startTypewriter = (text: string) => {
|
||||||
// 重置状态
|
// 重置状态
|
||||||
heroText.value = '';
|
heroText.value = '';
|
||||||
heroIndex = 0;
|
heroIndex = 0;
|
||||||
@@ -143,8 +280,8 @@ const startTypewriter = () => {
|
|||||||
|
|
||||||
// 设置新的定时器,逐字显示文本
|
// 设置新的定时器,逐字显示文本
|
||||||
heroTimer = window.setInterval(() => {
|
heroTimer = window.setInterval(() => {
|
||||||
if (heroIndex < fullHeroText.length) {
|
if (heroIndex < text.length) {
|
||||||
heroText.value += fullHeroText[heroIndex];
|
heroText.value += text[heroIndex];
|
||||||
heroIndex++;
|
heroIndex++;
|
||||||
} else {
|
} else {
|
||||||
// 文本显示完毕,清除定时器
|
// 文本显示完毕,清除定时器
|
||||||
@@ -161,8 +298,13 @@ const stopTypewriter = () => {
|
|||||||
if (heroTimer) {
|
if (heroTimer) {
|
||||||
clearInterval(heroTimer);
|
clearInterval(heroTimer);
|
||||||
}
|
}
|
||||||
// 直接显示完整文本
|
// 非首页时清空hero内容
|
||||||
|
if (rpsliturl[1] !== localhome) {
|
||||||
|
heroText.value = '';
|
||||||
|
} else {
|
||||||
|
// 首页直接显示完整文本
|
||||||
heroText.value = fullHeroText;
|
heroText.value = fullHeroText;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== 导航和路由处理模块 ==========
|
// ========== 导航和路由处理模块 ==========
|
||||||
@@ -199,8 +341,7 @@ const setActiveIndex = (path: string) => {
|
|||||||
*/
|
*/
|
||||||
const updatePageState = () => {
|
const updatePageState = () => {
|
||||||
// 根据是否为主页根路径设置hero区域状态
|
// 根据是否为主页根路径设置hero区域状态
|
||||||
classhero.value = !(rpsliturl[1] == localhome && rpsliturl[2] == undefined);
|
classsmallhero.value = !(rpsliturl[1] == localhome && rpsliturl[2] == undefined);
|
||||||
|
|
||||||
// 控制左侧模块容器的显示/隐藏
|
// 控制左侧模块容器的显示/隐藏
|
||||||
isleftmodluecontainer.value = rpsliturl[1] !== "articlesave";
|
isleftmodluecontainer.value = rpsliturl[1] !== "articlesave";
|
||||||
};
|
};
|
||||||
@@ -210,16 +351,14 @@ const updatePageState = () => {
|
|||||||
*/
|
*/
|
||||||
const updateArticleTitle = () => {
|
const updateArticleTitle = () => {
|
||||||
let articledata: any = null;
|
let articledata: any = null;
|
||||||
|
|
||||||
// 根据不同路由参数获取文章标题数据
|
// 根据不同路由参数获取文章标题数据
|
||||||
if (rpsliturl[2] === 'aericletype') {
|
if (rpsliturl[2] === 'aericleattribute') {
|
||||||
// 按属性类型获取
|
// 按属性类型获取
|
||||||
articledata = globalStore.getValue('attribute')?.name;
|
articledata = globalStore.getValue('attribute')?.name;
|
||||||
console.log('attributeId参数:', articledata);
|
|
||||||
}
|
}
|
||||||
else if (rpsliturl[2] === 'aericletitle') {
|
else if (rpsliturl[2] === 'aericletitle') {
|
||||||
// 按标题搜索获取
|
// 按标题搜索获取
|
||||||
articledata = globalStore.getValue('title')?.name;
|
articledata = globalStore.getValue('articleserarch')?.name;
|
||||||
}
|
}
|
||||||
else if (rpsliturl[1] === 'nonsense') {
|
else if (rpsliturl[1] === 'nonsense') {
|
||||||
// 疯言疯语页面特殊处理
|
// 疯言疯语页面特殊处理
|
||||||
@@ -229,7 +368,7 @@ const updateArticleTitle = () => {
|
|||||||
// 确定标题区域的显示状态
|
// 确定标题区域的显示状态
|
||||||
const shouldHideTitle =
|
const shouldHideTitle =
|
||||||
// 特殊页面不需要显示标题
|
// 特殊页面不需要显示标题
|
||||||
(rpsliturl[1] === 'article-list' ||
|
(rpsliturl[1] === 'articlelist' ||
|
||||||
rpsliturl[1] === 'message' ||
|
rpsliturl[1] === 'message' ||
|
||||||
rpsliturl[1] === 'about') ||
|
rpsliturl[1] === 'about') ||
|
||||||
// 在主页且无标题数据时,不显示标题
|
// 在主页且无标题数据时,不显示标题
|
||||||
@@ -289,11 +428,12 @@ const closeSearchBoxWithDelay = () => {
|
|||||||
const performSearch = () => {
|
const performSearch = () => {
|
||||||
// 验证搜索关键词不为空
|
// 验证搜索关键词不为空
|
||||||
if (searchKeyword.value.trim()) {
|
if (searchKeyword.value.trim()) {
|
||||||
|
// 清除全局搜索关键词
|
||||||
|
globalStore.removeValue('articleserarch');
|
||||||
// 存储搜索关键词到全局状态
|
// 存储搜索关键词到全局状态
|
||||||
globalStore.setValue('articleserarch', {
|
globalStore.setValue('articleserarch', {
|
||||||
name: searchKeyword.value
|
name: searchKeyword.value
|
||||||
});
|
});
|
||||||
|
|
||||||
// 跳转到搜索结果页面
|
// 跳转到搜索结果页面
|
||||||
router.push({ path: `/home/aericletitle` });
|
router.push({ path: `/home/aericletitle` });
|
||||||
}
|
}
|
||||||
@@ -311,13 +451,16 @@ const performSearch = () => {
|
|||||||
* 根据屏幕宽度调整布局和内容显示
|
* 根据屏幕宽度调整布局和内容显示
|
||||||
*/
|
*/
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
|
||||||
// 更新窗口宽度状态
|
// 更新窗口宽度状态
|
||||||
windowwidth.value = window.innerWidth > 768;
|
windowwidth.value = window.innerWidth > 768;
|
||||||
|
|
||||||
// 首页特殊处理:小屏幕下默认显示内容区
|
// 首页特殊处理:小屏幕下默认显示内容区
|
||||||
if (rpsliturl[1] === localhome) {
|
if (rpsliturl[1] === localhome) {
|
||||||
isconts.value = window.innerWidth <= 768;
|
iscontentvisible.value = window.innerWidth <= 768;
|
||||||
}
|
}
|
||||||
|
// 移动端首页默认显示内容区,桌面端初始隐藏
|
||||||
|
iscontentvisible.value = window.innerWidth <= 768;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,33 +468,58 @@ const handleResize = () => {
|
|||||||
* 根据滚动位置调整导航栏样式和内容显示动画
|
* 根据滚动位置调整导航栏样式和内容显示动画
|
||||||
*/
|
*/
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
|
let scrollY = 0;
|
||||||
|
scrollY = window.scrollY;
|
||||||
// 小屏幕设备只切换导航栏样式
|
// 小屏幕设备只切换导航栏样式
|
||||||
if (window.innerWidth < 768) {
|
if (window.innerWidth <= 768) {
|
||||||
updateNavbarStyle(window.scrollY);
|
updateNavbarStyle(scrollY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 大屏幕设备完整处理
|
// 大屏幕设备完整处理
|
||||||
updateNavbarStyle(window.scrollY);
|
updateNavbarStyle(scrollY);
|
||||||
|
|
||||||
// 仅在首页根路径应用滚动动画
|
// 仅在首页根路径应用滚动动画
|
||||||
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
||||||
// 控制内容区和左侧模块的滚动状态
|
// 首页滚动动画处理
|
||||||
isconts.value = window.scrollY > 200;
|
if (scrollY <= 350) {
|
||||||
isScrollingleftmodlue.value = window.scrollY > 600;
|
HeroState(scrollY);
|
||||||
|
}
|
||||||
|
// 控制左侧模块的滚动状态
|
||||||
|
isScrollingleftmodlue.value = scrollY > 600;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// ========== 首页滚动动画模块 ==========
|
||||||
|
const HeroState = (scrollY: number) => {
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
// 计算滚动距离与窗口高度的比例,用于内容渐显
|
||||||
|
const contentScrollRatio = Math.min(scrollY / windowHeight, 1);
|
||||||
|
// 计算新的底部margin值,从初始值45%逐渐减少到25%
|
||||||
|
const newMarginBottom = Math.max(initialHeroMarginBottom - (initialHeroMarginBottom * contentScrollRatio), 0);
|
||||||
|
// 更新hero的底部margin值
|
||||||
|
heroMarginBottom.value = `${newMarginBottom}%`;
|
||||||
|
// 计算新的translateY值,从初始值450px逐渐减少到150px
|
||||||
|
const translateYValue = Math.max(heroTransformValue - (heroTransformValue * contentScrollRatio * 5), 90);
|
||||||
|
heroTransform.value = `translateY(${translateYValue}px)`;
|
||||||
|
// 当滚动超过100px时开始显示,滚动到一屏高度时完全显示
|
||||||
|
iscontentvisible.value = scrollY > 100;
|
||||||
|
// 当滚动超过287px时logo被顶出屏幕,触发移动状态
|
||||||
|
if (scrollY > 287) {
|
||||||
|
heroPosition.value = 'moving';
|
||||||
|
const translateYValue = Math.min(heroTransformValue - (heroTransformValue * contentScrollRatio * 5), 0);
|
||||||
|
heroTransform.value = `translateY(${translateYValue / 2}px)`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据滚动位置更新导航栏样式
|
* 根据滚动位置更新导航栏样式
|
||||||
* @param {number} scrollY - 当前滚动位置
|
* @param {number} scrollY - 当前滚动位置
|
||||||
*/
|
*/
|
||||||
const updateNavbarStyle = (scrollY: number) => {
|
const updateNavbarStyle = (scrollY: number) => {
|
||||||
// 根据滚动位置设置导航栏样式
|
// 根据滚动位置设置导航栏样式
|
||||||
if (scrollY > 1200) {
|
// 当滚动超过1200px且屏幕宽度大于768px时隐藏导航栏
|
||||||
|
if (scrollY > 1200 && window.innerWidth > 768) {
|
||||||
elrowtop.value = 'hide'; // 隐藏导航栏
|
elrowtop.value = 'hide'; // 隐藏导航栏
|
||||||
} else {
|
} else {
|
||||||
elrowtop.value = scrollY > 100 ? 'solid' : 'transparent'; // 固定或透明样式
|
elrowtop.value = scrollY > 50 ? 'solid' : 'transparent'; // 固定或透明样式
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -364,7 +532,6 @@ const updateNavbarStyle = (scrollY: number) => {
|
|||||||
const handleRouteChange = () => {
|
const handleRouteChange = () => {
|
||||||
// 重新解析路由路径
|
// 重新解析路由路径
|
||||||
rpsliturl = route.path.split('/');
|
rpsliturl = route.path.split('/');
|
||||||
|
|
||||||
// 更新页面相关状态
|
// 更新页面相关状态
|
||||||
updatePageState();
|
updatePageState();
|
||||||
setActiveIndex(rpsliturl[1]);
|
setActiveIndex(rpsliturl[1]);
|
||||||
@@ -376,11 +543,18 @@ const handleRouteChange = () => {
|
|||||||
// 根据是否为首页决定是否启动打字机效果
|
// 根据是否为首页决定是否启动打字机效果
|
||||||
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
||||||
// 首页启动打字机效果
|
// 首页启动打字机效果
|
||||||
startTypewriter();
|
startTypewriter(fullHeroText);
|
||||||
|
// 重置hero的margin值为初始值
|
||||||
|
heroMarginBottom.value = `${initialHeroMarginBottom}%`;
|
||||||
|
// 重置hero的移动状态
|
||||||
|
heroTransform.value = `translateY(${heroTransformValue}px)`;
|
||||||
|
heroIsMoving.value = false;
|
||||||
|
heroPosition.value = 'static';
|
||||||
} else {
|
} else {
|
||||||
// 非首页直接显示完整文本
|
iscontentvisible.value = true;
|
||||||
isconts.value = true;
|
startTypewriter(fullHeroText);
|
||||||
stopTypewriter();
|
heroMarginBottom.value = `${5}%`;
|
||||||
|
heroTransform.value = ``;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -392,8 +566,20 @@ const initializePage = () => {
|
|||||||
handleResize();
|
handleResize();
|
||||||
// 启动打字机效果(如果是首页)
|
// 启动打字机效果(如果是首页)
|
||||||
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
|
||||||
startTypewriter();
|
// 首页启动打字机效果
|
||||||
|
startTypewriter(fullHeroText);
|
||||||
|
// 重置hero的margin值为初始值
|
||||||
|
heroMarginBottom.value = `${initialHeroMarginBottom}%`;
|
||||||
|
// 重置hero的移动状态
|
||||||
|
heroIsMoving.value = false;
|
||||||
|
heroPosition.value = 'static';
|
||||||
|
// 移动端首页默认显示内容区,桌面端初始隐藏
|
||||||
|
iscontentvisible.value = window.innerWidth <= 768;
|
||||||
|
} else {
|
||||||
|
startTypewriter(fullHeroText);
|
||||||
|
heroMarginBottom.value = `${2.5}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== 生命周期钩子 ==========
|
// ========== 生命周期钩子 ==========
|
||||||
@@ -435,8 +621,7 @@ onUnmounted(() => {
|
|||||||
*/
|
*/
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
handleRouteChange,
|
handleRouteChange
|
||||||
{ immediate: true } // 立即执行一次,确保初始状态正确
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -514,9 +699,140 @@ watch(
|
|||||||
.close-btn:hover {
|
.close-btn:hover {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-container {
|
.footer-container {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 搜索框样式 */
|
||||||
|
/* ... 现有搜索框样式 ... */
|
||||||
|
|
||||||
|
/* 蒙版样式 */
|
||||||
|
.category-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-content {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-close:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-button {
|
||||||
|
background-color: rgba(102, 161, 216, 0.1);
|
||||||
|
border: 1px solid rgba(102, 161, 216, 0.3);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-button:hover {
|
||||||
|
background-color: rgba(102, 161, 216, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 2px 8px rgba(102, 161, 216, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-button-count {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #66a1d8;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 蒙板动画 */
|
||||||
|
.modal-enter-active,
|
||||||
|
.modal-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-enter-from,
|
||||||
|
.modal-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-enter-active .category-modal-content,
|
||||||
|
.modal-leave-active .category-modal-content {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-enter-from .category-modal-content {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-leave-to .category-modal-content {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页脚样式 */
|
||||||
|
.footer-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 防止搜索框在小屏幕上重叠 */
|
/* 防止搜索框在小屏幕上重叠 */
|
||||||
@media screen and (max-width: 1200px) {
|
@media screen and (max-width: 1200px) {
|
||||||
.search-box-container.open {
|
.search-box-container.open {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="establish-component-root">
|
||||||
<div class="establish-container">
|
<div class="establish-container">
|
||||||
<!-- 弹出的按钮容器 -->
|
<!-- 弹出的按钮容器 -->
|
||||||
<!-- <div class="expanded-buttons" :class="{ 'show': isExpanded }"> -->
|
<!-- <div class="expanded-buttons" :class="{ 'show': isExpanded }"> -->
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -335,14 +337,14 @@ const createCategory = () => {
|
|||||||
* @param {string} typename - 分类名称
|
* @param {string} typename - 分类名称
|
||||||
*/
|
*/
|
||||||
const saveCategory = (typename) => {
|
const saveCategory = (typename) => {
|
||||||
console.log('保存分类')
|
// console.log('保存分类')
|
||||||
categoryService.createCategory({
|
categoryService.createCategory({
|
||||||
typename: typename
|
typename: typename
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
ElMessage.success('分类创建成功');
|
ElMessage.success('分类创建成功');
|
||||||
// 刷新页面
|
// 刷新页面
|
||||||
router.push({ path: `/aericlelist`});
|
router.push({ path: `/articlelist`});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(response.message || '创建分类失败');
|
ElMessage.error(response.message || '创建分类失败');
|
||||||
}
|
}
|
||||||
@@ -365,7 +367,7 @@ const createAttribute = async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleErrorResponse(error, '获取分类失败');
|
handleErrorResponse(error, '获取分类失败');
|
||||||
} finally {
|
} finally {
|
||||||
console.log(categories.value)
|
// console.log(categories.value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -387,7 +389,7 @@ const saveAttribute = (dto) => {
|
|||||||
closeAttributeModal()
|
closeAttributeModal()
|
||||||
ElMessage.success('标签创建成功');
|
ElMessage.success('标签创建成功');
|
||||||
// 刷新页面
|
// 刷新页面
|
||||||
router.push({ path: `/aericlelist`});
|
router.push({ path: `/articlelist`});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(response.message || '创建标签失败');
|
ElMessage.error(response.message || '创建标签失败');
|
||||||
}
|
}
|
||||||
@@ -488,7 +490,7 @@ const reloadPage = () => {
|
|||||||
* @param {{id: string, label: string, icon: string}} button - 按钮对象
|
* @param {{id: string, label: string, icon: string}} button - 按钮对象
|
||||||
*/
|
*/
|
||||||
const handleButtonClick = (button) => {
|
const handleButtonClick = (button) => {
|
||||||
console.log('点击了按钮:', button.id, button.label)
|
// console.log('点击了按钮:', button.id, button.label)
|
||||||
|
|
||||||
// 使用按钮ID进行处理,更可靠且易于维护
|
// 使用按钮ID进行处理,更可靠且易于维护
|
||||||
switch (button.id) {
|
switch (button.id) {
|
||||||
|
|||||||
@@ -25,10 +25,16 @@ const routes = [
|
|||||||
meta: { title: '首页' },
|
meta: { title: '首页' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'aericletype',
|
path: 'aericleattribute',
|
||||||
name: 'homeByType',
|
name: 'homeByAttribute',
|
||||||
component: HomePage
|
component: HomePage
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'aericlecategory',
|
||||||
|
name: 'homeByCategory',
|
||||||
|
component: HomePage
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'aericletitle',
|
path: 'aericletitle',
|
||||||
name: 'homeByTitle',
|
name: 'homeByTitle',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ElMessage } from 'element-plus'
|
|||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: 'http://www.qf1121.top/api', // API基础URL
|
baseURL: '/api', // API基础URL,使用相对路径,通过Vite代理转发
|
||||||
timeout: 10000, // 请求超时时间
|
timeout: 10000, // 请求超时时间
|
||||||
withCredentials: true // 允许跨域请求携带凭证(如cookies)
|
withCredentials: true // 允许跨域请求携带凭证(如cookies)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ class ArticleService {
|
|||||||
* @param size 每页大小(可选,默认为10,最大为100)
|
* @param size 每页大小(可选,默认为10,最大为100)
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
||||||
*/
|
*/
|
||||||
getArticles(params = {}) {
|
getPagedArticles(params = {}) {
|
||||||
return api.get(`/articles/status/page/${params.status}/${params.page}/${params.size}`, { params })
|
return api.get(`/articles/status/page?title=${params.title || ''}&categoryid=${params.categoryid || 0}&attributeid=${params.attributeid || 0}&status=${params.status || 1}&page=${params.page || 0}&size=${params.size || 10}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已发布文章列表
|
* 获取已发布文章列表
|
||||||
* @param {import('../types').PaginationParams} params - 查询参数
|
* @param {import('../types').PaginationParams} params - 查询参数
|
||||||
@@ -25,14 +24,6 @@ class ArticleService {
|
|||||||
getAllArticles(params = {}) {
|
getAllArticles(params = {}) {
|
||||||
return api.get('/articles/published', { params })
|
return api.get('/articles/published', { params })
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 根据状态获取文章列表
|
|
||||||
* @param {number} status - 文章状态(0:未发表 1:已发表 2:已删除)
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
|
||||||
*/
|
|
||||||
getArticlesByStatus(status) {
|
|
||||||
return api.get(`/articles/status/${status}`)
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 获取所有文章列表(包含已删除)
|
* 获取所有文章列表(包含已删除)
|
||||||
* @param {import('../types').PaginationParams} params - 查询参数
|
* @param {import('../types').PaginationParams} params - 查询参数
|
||||||
@@ -41,15 +32,6 @@ class ArticleService {
|
|||||||
getAllArticlesWithDeleted(params = {}) {
|
getAllArticlesWithDeleted(params = {}) {
|
||||||
return api.get('/articles', { params })
|
return api.get('/articles', { params })
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* 根据ID获取文章详情
|
|
||||||
* @param {number} articleid - 文章ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article>>}
|
|
||||||
*/
|
|
||||||
getArticleById(articleid) {
|
|
||||||
return api.get(`/articles/${articleid}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据属性ID获取文章列表
|
* 根据属性ID获取文章列表
|
||||||
* @param {number} attributeid - 属性ID
|
* @param {number} attributeid - 属性ID
|
||||||
@@ -59,15 +41,6 @@ class ArticleService {
|
|||||||
return api.get(`/articles/attribute/${attributeid}`)
|
return api.get(`/articles/attribute/${attributeid}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据标题查询文章列表
|
|
||||||
* @param {string} title - 文章标题
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
|
||||||
*/
|
|
||||||
getArticlesByTitle(title) {
|
|
||||||
return api.get(`/articles/title/${title}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取热门文章
|
* 获取热门文章
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
||||||
@@ -104,15 +77,6 @@ class ArticleService {
|
|||||||
return api.delete(`/articles/${articleid}`)
|
return api.delete(`/articles/${articleid}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据分类获取文章
|
|
||||||
* @param {number} categoryid - 分类ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
|
||||||
*/
|
|
||||||
getArticlesByCategory(categoryid) {
|
|
||||||
return api.get(`/articles/category/${categoryid}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加文章浏览量
|
* 增加文章浏览量
|
||||||
* @param {number} articleid - 文章ID
|
* @param {number} articleid - 文章ID
|
||||||
@@ -122,23 +86,7 @@ class ArticleService {
|
|||||||
return api.post(`/articles/view/${articleid}`)
|
return api.post(`/articles/view/${articleid}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据属性ID获取最新文章
|
|
||||||
* @param {number} attributeid - 属性ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Article[]>>}
|
|
||||||
*/
|
|
||||||
getLatestArticlesByAttribute(attributeid) {
|
|
||||||
return api.get(`/articles/attribute/${attributeid}/latest`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 点赞文章
|
|
||||||
* @param {number} articleid - 文章ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
|
||||||
*/
|
|
||||||
likeArticle(articleid) {
|
|
||||||
return api.post(`/articles/like/${articleid}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并导出服务实例
|
// 创建并导出服务实例
|
||||||
|
|||||||
@@ -39,35 +39,6 @@ class CategoryAttributeService {
|
|||||||
createAttribute(attributeData) {
|
createAttribute(attributeData) {
|
||||||
return api.post('/category-attributes', attributeData)
|
return api.post('/category-attributes', attributeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新分类属性
|
|
||||||
* @param {number} attributeid - 属性ID
|
|
||||||
* @param {import('../types').CategoryAttributeDto} attributeData - 属性数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').CategoryAttribute>>}
|
|
||||||
*/
|
|
||||||
updateAttribute(attributeid, attributeData) {
|
|
||||||
return api.put(`/category-attributes/${attributeid}`, attributeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除分类属性
|
|
||||||
* @param {number} attributeid - 属性ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
|
||||||
*/
|
|
||||||
deleteAttribute(attributeid) {
|
|
||||||
return api.delete(`/category-attributes/${attributeid}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查分类下是否存在指定名称的属性
|
|
||||||
* @param {number} categoryid - 分类ID
|
|
||||||
* @param {string} attributename - 属性名称
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
|
||||||
*/
|
|
||||||
checkAttributeExists(categoryid, attributename) {
|
|
||||||
return api.get(`/category-attributes/check-exists?categoryid=${categoryid}&attributename=${encodeURIComponent(attributename)}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并导出服务实例
|
// 创建并导出服务实例
|
||||||
|
|||||||
@@ -11,43 +11,6 @@ class CategoryService {
|
|||||||
getAllCategories() {
|
getAllCategories() {
|
||||||
return api.get('/categories')
|
return api.get('/categories')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定分类
|
|
||||||
* @param {number} typeid - 分类ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Category>>}
|
|
||||||
*/
|
|
||||||
getCategory(typeid) {
|
|
||||||
return api.get(`/categories/${typeid}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新分类
|
|
||||||
* @param {import('../types').CategoryDto} categoryData - 分类数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Category>>}
|
|
||||||
*/
|
|
||||||
createCategory(categoryData) {
|
|
||||||
return api.post('/categories', categoryData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新分类
|
|
||||||
* @param {number} typeid - 分类ID
|
|
||||||
* @param {import('../types').CategoryDto} categoryData - 分类数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Category>>}
|
|
||||||
*/
|
|
||||||
updateCategory(typeid, categoryData) {
|
|
||||||
return api.put(`/categories/${typeid}`, categoryData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除分类
|
|
||||||
* @param {number} typeid - 分类ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
|
||||||
*/
|
|
||||||
deleteCategory(typeid) {
|
|
||||||
return api.delete(`/categories/${typeid}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并导出服务实例
|
// 创建并导出服务实例
|
||||||
|
|||||||
@@ -14,48 +14,11 @@ class LoginService {
|
|||||||
return api.post("/auth/login", loginData);
|
return api.post("/auth/login", loginData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户注册
|
|
||||||
* @param {import('../types').RegisterDto} registerData - 注册数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').User>>}
|
|
||||||
*/
|
|
||||||
register(registerData) {
|
|
||||||
return api.post("/auth/register", registerData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登出
|
* 登出
|
||||||
*/
|
*/
|
||||||
logout() {
|
logout() {
|
||||||
|
|
||||||
return api.post("/auth/logout");
|
return api.post("/auth/logout");
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前用户信息
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').User>>}
|
|
||||||
*/
|
|
||||||
getCurrentUser() {
|
|
||||||
return api.get("/user/info");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户信息
|
|
||||||
* @param {import('../types').UserDto} userData - 用户数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').User>>}
|
|
||||||
*/
|
|
||||||
updateUser(userData) {
|
|
||||||
return api.put("/user/update", userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改密码
|
|
||||||
* @param {import('../types').ChangePasswordDto} passwordData - 密码数据
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
|
||||||
*/
|
|
||||||
changePassword(passwordData) {
|
|
||||||
return api.post("/user/change-password", passwordData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default new LoginService();
|
export default new LoginService();
|
||||||
@@ -6,22 +6,28 @@ import apiService from './apiService'
|
|||||||
*/
|
*/
|
||||||
class MessageService {
|
class MessageService {
|
||||||
/**
|
/**
|
||||||
* 获取所有留言
|
* 获取留言数量
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
* @param {number} articleid - 文章ID
|
||||||
|
* @returns {Promise<import('../types').ApiResponse<number>>}
|
||||||
*/
|
*/
|
||||||
getAllMessages() {
|
getMessageCountByArticleId(articleid) {
|
||||||
return apiService.get('/messages')
|
return apiService.get(`/messages/count?articleid=${articleid}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取单条留言
|
* 获取分页留言
|
||||||
* @param {number} messageid - 留言ID
|
* @param {number} articleid - 文章ID
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Message>>}
|
* @param {number} pagenum - 页码
|
||||||
|
* @param {number} pagesize - 每页数量
|
||||||
|
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
||||||
*/
|
*/
|
||||||
getMessageById(messageid) {
|
getMessagesByPage(articleid, pagenum, pagesize) {
|
||||||
return apiService.get(`/messages/${messageid}`)
|
// 如果文章ID不存在,查询所有留言
|
||||||
|
if (!articleid) {
|
||||||
|
return apiService.get(`/messages/page?pageNum=${pagenum}&pageSize=${pagesize}`)
|
||||||
|
}
|
||||||
|
return apiService.get(`/messages/page?articleid=${articleid}&pageNum=${pagenum}&pageSize=${pagesize}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文章ID获取留言
|
* 根据文章ID获取留言
|
||||||
* @param {number} articleid - 文章ID
|
* @param {number} articleid - 文章ID
|
||||||
@@ -32,38 +38,11 @@ class MessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取根留言
|
* 获取所有留言
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
||||||
*/
|
*/
|
||||||
getRootMessages() {
|
getAllMessages() {
|
||||||
return apiService.get('/messages/root')
|
return apiService.get('/messages')
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据父留言ID获取回复
|
|
||||||
* @param {number} parentid - 父留言ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
|
||||||
*/
|
|
||||||
getRepliesByParentId(parentid) {
|
|
||||||
return apiService.get(`/messages/${parentid}/replies`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据昵称搜索留言
|
|
||||||
* @param {string} nickname - 昵称
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<import('../types').Message[]>>}
|
|
||||||
*/
|
|
||||||
searchMessagesByNickname(nickname) {
|
|
||||||
return apiService.get(`/messages/search?nickname=${nickname}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取文章评论数量
|
|
||||||
* @param {number} articleid - 文章ID
|
|
||||||
* @returns {Promise<import('../types').ApiResponse<number>>}
|
|
||||||
*/
|
|
||||||
getMessageCountByArticleId(articleid) {
|
|
||||||
return apiService.get(`/messages/count/article/${articleid}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +67,6 @@ class MessageService {
|
|||||||
* 点赞留言
|
* 点赞留言
|
||||||
* @param {number} messageid - 留言ID
|
* @param {number} messageid - 留言ID
|
||||||
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
* @returns {Promise<import('../types').ApiResponse<boolean>>}
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
likeMessage(messageid) {
|
likeMessage(messageid) {
|
||||||
return apiService.post(`/messages/${messageid}/like`)
|
return apiService.post(`/messages/${messageid}/like`)
|
||||||
|
|||||||
@@ -17,13 +17,12 @@
|
|||||||
/* 内容区圆角 */
|
/* 内容区圆角 */
|
||||||
|
|
||||||
/* 首页 hero 区域高度和间距 */
|
/* 首页 hero 区域高度和间距 */
|
||||||
--hero-height: 768px;
|
--hero-height: 100px;
|
||||||
/* hero 默认高度 */
|
/* hero 默认高度 */
|
||||||
--hero-height-small: 100px;
|
--hero-height-small: 100px;
|
||||||
/* hero 收缩后高度 */
|
/* hero 收缩后高度 */
|
||||||
/* --hero-margin-top: 2%; */
|
--hero-margin-top-small: 6%;
|
||||||
/* hero 顶部外边距 */
|
/* hero 收缩后顶部外边距 */
|
||||||
|
|
||||||
/* 标题样式 */
|
/* 标题样式 */
|
||||||
--title-font-size: 3.5rem;
|
--title-font-size: 3.5rem;
|
||||||
/* hero 主标题字号 */
|
/* hero 主标题字号 */
|
||||||
@@ -368,18 +367,24 @@ p {
|
|||||||
/* 首页 hero 区域样式 */
|
/* 首页 hero 区域样式 */
|
||||||
.hero {
|
.hero {
|
||||||
height: var(--hero-height);
|
height: var(--hero-height);
|
||||||
|
margin: var(--hero-margin);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: white;
|
||||||
transition: height 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hero 收缩状态 */
|
/* hero 收缩状态 */
|
||||||
.hero.newhero {
|
.hero.small-hero {
|
||||||
height: var(--hero-height-small);
|
height: var(--hero-height-small);
|
||||||
margin-top: 10%;
|
margin-top: var(--hero-margin-top-small);
|
||||||
|
/* 去除 hero 收缩状态下的position */
|
||||||
|
position: static;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 打字机效果 */
|
/* 打字机效果 */
|
||||||
@@ -387,6 +392,10 @@ p {
|
|||||||
white-space: pre;
|
white-space: pre;
|
||||||
min-height: 2.5em;
|
min-height: 2.5em;
|
||||||
font-size: var(--title-font-size, 2rem);
|
font-size: var(--title-font-size, 2rem);
|
||||||
|
/* 水平居中 */
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
@@ -445,8 +454,7 @@ p {
|
|||||||
--nonsense-title-margin-bottom: 15px;
|
--nonsense-title-margin-bottom: 15px;
|
||||||
--nav-padding-small: 0 8px;
|
--nav-padding-small: 0 8px;
|
||||||
--nonsenset-margin-top: 10px;
|
--nonsenset-margin-top: 10px;
|
||||||
--body-background-img: url();
|
--body-background-img: url(../img/bg.jpg);
|
||||||
/* 移动端不显示背景图片 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.elrow-top {
|
.elrow-top {
|
||||||
@@ -506,3 +514,23 @@ p {
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* 移动端适配:屏幕宽度小于820px时生效 */
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
:root {
|
||||||
|
--nav-padding: 0;
|
||||||
|
--hero-height:300px;
|
||||||
|
}
|
||||||
|
.RouterViewpage {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
margin-top: 50%;
|
||||||
|
}
|
||||||
|
.el-col-14 {
|
||||||
|
flex: 0 0 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.elrow-top {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export interface MessageDto {
|
|||||||
* 分类类型接口
|
* 分类类型接口
|
||||||
*/
|
*/
|
||||||
export interface Category {
|
export interface Category {
|
||||||
typeid: number
|
Categoryid: number
|
||||||
typename: string
|
typename: string
|
||||||
description?: string
|
description?: string
|
||||||
createdAt?: string
|
createdAt?: string
|
||||||
@@ -162,7 +162,7 @@ export interface ApiResponse<T = any> {
|
|||||||
* 分页参数接口
|
* 分页参数接口
|
||||||
*/
|
*/
|
||||||
export interface PaginationParams {
|
export interface PaginationParams {
|
||||||
page?: number
|
pagenum?: number
|
||||||
size?: number
|
pagesize?: number
|
||||||
status?: number
|
status?: number
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,136 @@
|
|||||||
<!-- 页脚 -->
|
<!-- 页脚 -->
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="footer-content">
|
<div class="footer-content">
|
||||||
<p>© 2023 我的网站. 所有权利保留.</p>
|
<!-- 备案信息 -->
|
||||||
<p>联系我们:<a href="mailto:xxxx@exxxx.com">xxxx@exxxx.com</a></p>
|
<p class="footer-beian">
|
||||||
<!-- 备案 -->
|
备案号:
|
||||||
<p>
|
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
|
||||||
备案号:<a href="https://beian.miit.gov.cn/" target="_blank">
|
皖ICP备2025105428号-1 || 皖公网安备34120202001634号
|
||||||
皖ICP备2025105428号-1
|
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- 版权信息 -->
|
||||||
|
<p class="footer-copyright">
|
||||||
|
网站所有权利保留 © {{ new Date().getFullYear() }} 清疯不颠
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- 运行时间 -->
|
||||||
|
<p class="footer-runtime">
|
||||||
|
运行时间:<span>{{ runtime }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<script setup lang="ts">
|
||||||
.footer {
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
// 运行时间响应式状态
|
||||||
|
const runtime = ref('计算中...')
|
||||||
|
// 网站上线时间 (时间戳)
|
||||||
|
const LAUNCH_DATE = new Date('2025-12-01T00:00:00').getTime()
|
||||||
|
// 定时器引用
|
||||||
|
let runtimeTimer: number | null = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新网站运行时间
|
||||||
|
*/
|
||||||
|
const updateRuntime = () => {
|
||||||
|
const now = Date.now()
|
||||||
|
const diff = now - LAUNCH_DATE
|
||||||
|
|
||||||
|
// 计算天、时、分、秒
|
||||||
|
const days = Math.floor(diff / 86400000)
|
||||||
|
const hours = Math.floor((diff % 86400000) / 3600000)
|
||||||
|
const minutes = Math.floor((diff % 3600000) / 60000)
|
||||||
|
const seconds = Math.floor((diff % 60000) / 1000)
|
||||||
|
|
||||||
|
// 更新运行时间显示
|
||||||
|
runtime.value = `${days} 天 ${hours} 小时 ${minutes} 分钟 ${seconds} 秒`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
// 立即更新一次
|
||||||
|
updateRuntime()
|
||||||
|
// 每秒更新一次
|
||||||
|
runtimeTimer = window.setInterval(updateRuntime, 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时清理定时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (runtimeTimer) {
|
||||||
|
clearInterval(runtimeTimer)
|
||||||
|
runtimeTimer = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 页脚容器 */
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding: 2rem 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页脚内容 */
|
||||||
.footer-content {
|
.footer-content {
|
||||||
width: 80%;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background-color: #f5f5f5;
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
border-radius: 10px;
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem 2rem;
|
||||||
|
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 通用段落样式 */
|
||||||
|
.footer-content p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 备案信息 */
|
||||||
|
.footer-beian a {
|
||||||
|
color: #409eff;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-beian a:hover {
|
||||||
|
color: #66b1ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 版权信息 */
|
||||||
|
.footer-copyright {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 运行时间 */
|
||||||
|
.footer-runtime {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.footer {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content p {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="about-content-wrapper">
|
<div class="about-content-wrapper">
|
||||||
<!-- 页面头部 -->
|
<!-- 页面头部 -->
|
||||||
<div class="about-page-header">
|
<div class="about-page-header">
|
||||||
<h1 class="about-page-title">关于</h1>
|
<h3 class="about-page-title">关于</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 关于内容 -->
|
<!-- 关于内容 -->
|
||||||
@@ -281,14 +281,19 @@ const goToMessageBoard = () => {
|
|||||||
.about-page-container {
|
.about-page-container {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
.about-page-header{
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
.about-content-wrapper {
|
.about-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 0 15px;
|
margin: 0 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-page-title {
|
.about-page-title {
|
||||||
font-size: 1.8rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.about-page-subtitle {
|
.about-page-subtitle {
|
||||||
@@ -306,7 +311,6 @@ const goToMessageBoard = () => {
|
|||||||
.contact-options {
|
.contact-options {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skills-display-list {
|
.skills-display-list {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,16 @@
|
|||||||
<div v-else-if="categories.length > 0" class="article-content" id="category-list">
|
<div v-else-if="categories.length > 0" class="article-content" id="category-list">
|
||||||
<p><strong></strong></p>
|
<p><strong></strong></p>
|
||||||
<div class="alert alert-primary"><strong><span class="alert-inner-text">文章分类如下,点击跳转</span> </strong></div>
|
<div class="alert alert-primary"><strong><span class="alert-inner-text">文章分类如下,点击跳转</span> </strong></div>
|
||||||
<div v-for="categoryGroup in categories" :key="categoryGroup.typeid" class="category-group-container">
|
<div v-for="categoryGroup in categories" :key="categoryGroup.Categoryid" class="category-group-container">
|
||||||
<div v-if="categoryGroup.attributes.length > 0 && categoryGroup.attributes.some(cat => cat.articles && cat.articles.length > 0)">
|
<div v-if="categoryGroup.attributes.length > 0 && categoryGroup.attributes.some(cat => cat.articles && cat.articles.length > 0)">
|
||||||
<h2 id="header-id-1">{{ categoryGroup.typename }}</h2>
|
<h2 id="header-id-1">{{ categoryGroup.typename }}</h2>
|
||||||
<!-- 计算该分类组中实际有文章的属性数量 -->
|
<!-- 计算该分类组中实际有文章的属性数量 -->
|
||||||
<span class="badge badge-primary">共 {{ categoryGroup.attributes.reduce((total, cat) => total + (cat.articles && cat.articles.length ? cat.articles.length : 0), 0) }} 篇</span>
|
<span class="badge badge-primary">共 {{ categoryGroup.attributes.reduce((total, cat) => total + (cat.articles && cat.articles.length ? cat.articles.length : 0), 0) }} 篇</span>
|
||||||
<ul class="category-item-list">
|
<ul class="category-item-list">
|
||||||
<div v-for="category in categoryGroup.attributes" :key="category.attributeid">
|
<div v-for="attribute in categoryGroup.attributes" :key="attribute.attributeid">
|
||||||
<li v-if="category.articles && category.articles.length > 0">
|
<li v-if="attribute.articles && attribute.articles.length > 0">
|
||||||
<a class="category-link" @click="handleCategoryClick(category)"><kbd>{{ category.attributename}}</kbd></a>
|
<a class="category-link" @click="handleCategoryClick(attribute)"><kbd>{{ attribute.attributename}}</kbd></a>
|
||||||
— —({{ category.articles.length }})
|
— —({{ attribute.articles.length }})
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -78,7 +78,7 @@ const fetchCategories = async () => {
|
|||||||
// 使用Promise.all等待所有异步操作完成
|
// 使用Promise.all等待所有异步操作完成
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
processedCategories.map(async category => {
|
processedCategories.map(async category => {
|
||||||
const attributes = await categoryAttributeService.getAttributesByCategory(category.typeid);
|
const attributes = await categoryAttributeService.getAttributesByCategory(category.categoryid);
|
||||||
if (attributes.code === 200 && Array.isArray(attributes.data)) {
|
if (attributes.code === 200 && Array.isArray(attributes.data)) {
|
||||||
const processedAttributes = await Promise.all(
|
const processedAttributes = await Promise.all(
|
||||||
attributes.data.map(async item => {
|
attributes.data.map(async item => {
|
||||||
@@ -99,7 +99,7 @@ const fetchCategories = async () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
categories.value = processedCategories;
|
categories.value = processedCategories;
|
||||||
console.log('获取分类列表成功:', categories.value);
|
// console.log('获取分类列表成功:', categories.value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取分类列表失败:', err);
|
console.error('获取分类列表失败:', err);
|
||||||
ElMessage.error('获取分类列表失败,请稍后重试');
|
ElMessage.error('获取分类列表失败,请稍后重试');
|
||||||
@@ -116,11 +116,10 @@ const handleCategoryClick = (attribute: any) => {
|
|||||||
globalStore.removeValue('attribute')
|
globalStore.removeValue('attribute')
|
||||||
globalStore.setValue('attribute', {
|
globalStore.setValue('attribute', {
|
||||||
id: attribute.attributeid,
|
id: attribute.attributeid,
|
||||||
name: attribute.typename
|
name: attribute.attributename
|
||||||
})
|
})
|
||||||
console.log(attribute)
|
|
||||||
router.push({
|
router.push({
|
||||||
path: '/home/aericletype',
|
path: '/home/aericleattribute',
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -483,18 +483,17 @@ onMounted(() => {
|
|||||||
.error-state-container,
|
.error-state-container,
|
||||||
.empty-state-container {
|
.empty-state-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 0 15px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-main-title {
|
.article-main-title {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
.article-actions-group {
|
||||||
|
/* 文章操作按钮组 - 右对齐 */
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
.article-meta-info {
|
.article-meta-info {
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ const loadCategories = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('分类选项:', optionsData);
|
// console.log('分类选项:', optionsData);
|
||||||
console.log('选中的值:', selectedValues.value);
|
// console.log('选中的值:', selectedValues.value);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载分类失败:', error);
|
console.error('加载分类失败:', error);
|
||||||
@@ -196,8 +196,8 @@ const handleSave = (markdown) => {
|
|||||||
markdownscontent: Articleform.value.markdownscontent
|
markdownscontent: Articleform.value.markdownscontent
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('发送文章数据:', articleData);
|
// console.log('发送文章数据:', articleData);
|
||||||
console.log('当前认证token是否存在:', !!localStorage.getItem('token'));
|
// console.log('当前认证token是否存在:', !!localStorage.getItem('token'));
|
||||||
|
|
||||||
// 根据articleid决定调用创建还是更新接口
|
// 根据articleid决定调用创建还是更新接口
|
||||||
const savePromise = Articleform.value.articleid === 0
|
const savePromise = Articleform.value.articleid === 0
|
||||||
@@ -205,7 +205,7 @@ const handleSave = (markdown) => {
|
|||||||
: articleService.updateArticle(Articleform.value.articleid, articleData);
|
: articleService.updateArticle(Articleform.value.articleid, articleData);
|
||||||
savePromise
|
savePromise
|
||||||
.then(res => {
|
.then(res => {
|
||||||
console.log('API响应:', res);
|
// console.log('API响应:', res);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ElMessage.success(Articleform.value.articleid === 0 ? '文章创建成功' : '文章更新成功');
|
ElMessage.success(Articleform.value.articleid === 0 ? '文章创建成功' : '文章更新成功');
|
||||||
// 清除全局存储中的article
|
// 清除全局存储中的article
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<!-- 文章列表 -->
|
<!-- 文章列表 -->
|
||||||
<transition-group name="article-item" tag="div" class="article-list-content" v-else>
|
<transition-group name="article-item" tag="div" class="article-list-content" v-else>
|
||||||
<div class="article-card" v-for="article in displayedArticles" :key="article.articleId"
|
<div class="article-card" v-for="article in articleList" :key="article.articleId"
|
||||||
@click="handleArticleClick(article)">
|
@click="handleArticleClick(article)">
|
||||||
<h6 class="article-title">{{ article.title }}</h6>
|
<h6 class="article-title">{{ article.title }}</h6>
|
||||||
<div v-if="article.marked" class="article-special-tag">标记文章</div>
|
<div v-if="article.marked" class="article-special-tag">标记文章</div>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 分页区域 -->
|
<!-- 分页区域 -->
|
||||||
<PaginationComponent class="pagination-container" :list="articleList" :pageSize="10" @changePage="handleCurrentDataUpdate" />
|
<el-pagination size="medium" background :layout="pageLayout" v-model:current-page="pageNum" hide-on-single-page="true" @current-change="changePage" :page-size="pageSize" :page-count="totalPages" class="mt-4" />
|
||||||
</transition-group>
|
</transition-group>
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<div v-if="!loading && articleList.length === 0" class="empty-state-container">
|
<div v-if="!loading && articleList.length === 0" class="empty-state-container">
|
||||||
@@ -55,22 +55,24 @@ const globalStore = useGlobalStore()
|
|||||||
// 路由相关
|
// 路由相关
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
// 分页属性
|
||||||
|
const pageNum = ref(1) // 当前页码
|
||||||
|
const pageSize = ref(10) // 每页数量
|
||||||
|
const totalPages = ref(0) // 总页数
|
||||||
|
const pageLayout = ref('pager, next')// 分页布局
|
||||||
|
|
||||||
// 响应式状态
|
// 响应式状态
|
||||||
const articleList = ref([])
|
const articleList = ref([])
|
||||||
const displayedArticles = ref([])
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ========== 分页数据处理 ==========
|
// ========== 分页数据处理 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理分页组件的数据更新
|
* 处理分页组件的数据更新
|
||||||
* @param {Array} data - 分页组件传递的当前页数据
|
* @param {Array} data - 分页组件传递的当前页数据
|
||||||
*/
|
*/
|
||||||
const handleCurrentDataUpdate = (data) => {
|
|
||||||
displayedArticles.value = data
|
|
||||||
console.log('更新后的当前页数据:', data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 文章数据获取模块 ==========
|
// ========== 文章数据获取模块 ==========
|
||||||
|
|
||||||
@@ -81,25 +83,30 @@ const handleCurrentDataUpdate = (data) => {
|
|||||||
const getArticlesByRoute = async () => {
|
const getArticlesByRoute = async () => {
|
||||||
// 检查URL参数,确定获取文章的方式
|
// 检查URL参数,确定获取文章的方式
|
||||||
const pathSegment = route.path.split('/')[2]
|
const pathSegment = route.path.split('/')[2]
|
||||||
console.log('当前路由分段:', pathSegment)
|
// console.log('当前路由分段:', pathSegment)
|
||||||
|
|
||||||
switch (pathSegment) {
|
switch (pathSegment) {
|
||||||
case 'aericletype':
|
case 'aericleattribute':
|
||||||
// 按属性类型获取文章
|
// 按属性类型获取文章
|
||||||
const attributeData = globalStore.getValue('attribute')
|
const attributeData = globalStore.getValue('attribute')
|
||||||
return await articleService.getArticlesByAttributeId(attributeData?.id)
|
return await articleService.getPagedArticles({attributeid: attributeData?.id}, pageNum.value, pageSize.value)
|
||||||
|
case 'aericlecategory':
|
||||||
|
// 按分类类型获取文章
|
||||||
|
const categoryData = globalStore.getValue('category')
|
||||||
|
return await articleService.getPagedArticles({categoryid: categoryData?.id}, pageNum.value, pageSize.value)
|
||||||
case 'aericletitle':
|
case 'aericletitle':
|
||||||
// 按标题搜索文章
|
// 按标题搜索文章
|
||||||
const titleData = globalStore.getValue('articleserarch')
|
const titleData = globalStore.getValue('articleserarch')
|
||||||
return await articleService.getArticlesByTitle(titleData?.name)
|
console.log('按标题搜索文章:', titleData.name)
|
||||||
|
return await articleService.getPagedArticles({title: titleData?.name}, pageNum.value, pageSize.value)
|
||||||
case 'aericlestatus':
|
case 'aericlestatus':
|
||||||
// 按状态获取文章
|
// 按状态获取文章
|
||||||
const statusData = globalStore.getValue('articlestatus')
|
const statusData = globalStore.getValue('articlestatus')
|
||||||
return await articleService.getArticlesByStatus(statusData?.status)
|
return await articleService.getPagedArticles({status: statusData?.status}, pageNum.value, pageSize.value)
|
||||||
default:
|
default:
|
||||||
// 默认获取所有文章
|
// 默认获取所有文章
|
||||||
console.log('获取所有文章列表')
|
// console.log('获取所有文章列表')
|
||||||
return await articleService.getAllArticles()
|
return await articleService.getPagedArticles({status: 1}, pageNum.value, pageSize.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,14 +155,6 @@ const enrichArticlesWithExtraInfo = async (articles) => {
|
|||||||
return enrichedArticles
|
return enrichedArticles
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化显示文章列表
|
|
||||||
* @param {Array} articles - 完整文章列表
|
|
||||||
*/
|
|
||||||
const initializeDisplayedArticles = (articles) => {
|
|
||||||
// 初始显示前3条数据
|
|
||||||
displayedArticles.value = articles.slice(0, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文章列表主函数
|
* 获取文章列表主函数
|
||||||
@@ -168,28 +167,23 @@ const fetchArticles = async () => {
|
|||||||
|
|
||||||
// 1. 根据路由获取文章列表
|
// 1. 根据路由获取文章列表
|
||||||
response = await getArticlesByRoute()
|
response = await getArticlesByRoute()
|
||||||
|
// console.log('更新后的文章列表:', response)
|
||||||
|
|
||||||
// 2. 确保数据存在
|
// 2. 确保数据存在
|
||||||
if (!response.data || !Array.isArray(response.data)) {
|
if (!response.data.content || !Array.isArray(response.data.content)) {
|
||||||
articleList.value = []
|
articleList.value = []
|
||||||
displayedArticles.value = []
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 为文章列表补充额外信息
|
// 3. 为文章列表补充额外信息
|
||||||
const enrichedArticles = await enrichArticlesWithExtraInfo(response.data)
|
const enrichedArticles = await enrichArticlesWithExtraInfo(response.data.content)
|
||||||
|
|
||||||
// 4. 更新文章列表
|
// 4. 更新文章列表
|
||||||
articleList.value = enrichedArticles
|
articleList.value = enrichedArticles
|
||||||
|
|
||||||
// 5. 初始化显示的文章
|
|
||||||
initializeDisplayedArticles(enrichedArticles)
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取文章列表失败:', error)
|
console.error('获取文章列表失败:', error)
|
||||||
ElMessage.error('获取文章列表失败,请稍后重试')
|
ElMessage.error('获取文章列表失败,请稍后重试')
|
||||||
} finally {
|
} finally {
|
||||||
console.log('最终文章列表数据:', articleList.value)
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,7 +216,24 @@ const handleArticleClick = (article) => {
|
|||||||
ElMessage.error('操作失败,请稍后重试')
|
ElMessage.error('操作失败,请稍后重试')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 处理分页变化事件
|
||||||
|
* @param {number} newPage - 新的页码
|
||||||
|
*/
|
||||||
|
const changePage = (newPage) => {
|
||||||
|
fetchArticles()
|
||||||
|
// 根据当前页码优化分页布局
|
||||||
|
if (page === 1) {
|
||||||
|
// 第一页只显示页码和下一页按钮
|
||||||
|
pageLayout.value = 'pager, next'
|
||||||
|
} else if (page === totalPages.value) {
|
||||||
|
// 最后一页只显示上一页按钮和页码
|
||||||
|
pageLayout.value = 'prev, pager'
|
||||||
|
} else {
|
||||||
|
// 中间页显示完整的上一页、页码、下一页
|
||||||
|
pageLayout.value = 'prev, pager, next'
|
||||||
|
}
|
||||||
|
}
|
||||||
// ========== 生命周期和监听器 ==========
|
// ========== 生命周期和监听器 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,18 +241,13 @@ const handleArticleClick = (article) => {
|
|||||||
*/
|
*/
|
||||||
const handleRouteChange = () => {
|
const handleRouteChange = () => {
|
||||||
fetchArticles()
|
fetchArticles()
|
||||||
console.log('路由变化,重新获取文章列表')
|
// console.log('路由变化,重新获取文章列表')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理文章列表变化的回调函数
|
* 处理文章列表变化的回调函数
|
||||||
* @param {Array} newList - 新的文章列表
|
* @param {Array} newList - 新的文章列表
|
||||||
*/
|
*/
|
||||||
const handleArticleListChange = (newList) => {
|
|
||||||
if (newList && newList.length > 0) {
|
|
||||||
displayedArticles.value = newList.slice(0, 3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件挂载时获取数据
|
// 组件挂载时获取数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -260,7 +266,6 @@ watch(
|
|||||||
// 监听原始文章列表变化,确保初始数据正确显示
|
// 监听原始文章列表变化,确保初始数据正确显示
|
||||||
watch(
|
watch(
|
||||||
() => articleList.value,
|
() => articleList.value,
|
||||||
handleArticleListChange,
|
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
@@ -414,7 +419,6 @@ watch(
|
|||||||
/* 响应式设计 - 平板和手机 */
|
/* 响应式设计 - 平板和手机 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.article-list-container {
|
.article-list-container {
|
||||||
padding: 0 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-card {
|
.article-card {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const togglePassword = () => {
|
|||||||
|
|
||||||
// 验证单个字段
|
// 验证单个字段
|
||||||
const validateField = (field) => {
|
const validateField = (field) => {
|
||||||
console.log('validateField', field)
|
// console.log('validateField', field)
|
||||||
errors.value = {}
|
errors.value = {}
|
||||||
|
|
||||||
if (field === 'username' && !loginForm.username) {
|
if (field === 'username' && !loginForm.username) {
|
||||||
@@ -150,7 +150,7 @@ const handleLogin = async () => {
|
|||||||
ElMessage.error('登录失败,请检查用户名和密码')
|
ElMessage.error('登录失败,请检查用户名和密码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('登录成功', user)
|
// console.log('登录成功', user)
|
||||||
// 这里应该是实际的登录API调用
|
// 这里应该是实际的登录API调用
|
||||||
// console.log('登录请求数据:', loginForm)
|
// console.log('登录请求数据:', loginForm)
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ const handleLogin = async () => {
|
|||||||
globalStore.setValue('loginhomestatus', {
|
globalStore.setValue('loginhomestatus', {
|
||||||
status: 1 // 2:删除 1:已发布 0:发布登录
|
status: 1 // 2:删除 1:已发布 0:发布登录
|
||||||
})
|
})
|
||||||
console.log('globalStore.Login', globalStore.Login)
|
// console.log('globalStore.Login', globalStore.Login)
|
||||||
// 保存登录状态token
|
// 保存登录状态token
|
||||||
if (user.token) {
|
if (user.token) {
|
||||||
localStorage.setItem('token', user.token)
|
localStorage.setItem('token', user.token)
|
||||||
|
|||||||
@@ -15,8 +15,10 @@
|
|||||||
<div class="comment-header-info">
|
<div class="comment-header-info">
|
||||||
<!-- 头像 -->
|
<!-- 头像 -->
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<img v-if="getAvatarUrl(comment.messageimg)" :src="getAvatarUrl(comment.messageimg)" class="user-avatar" alt="头像">
|
<img v-if="getAvatarUrl(comment.messageimg)" :src="getAvatarUrl(comment.messageimg)"
|
||||||
<div v-else class="letter-avatar" :style="getLetterAvatarStyle(comment.displayName || comment.nickname)">
|
class="user-avatar" alt="头像">
|
||||||
|
<div v-else class="letter-avatar"
|
||||||
|
:style="getLetterAvatarStyle(comment.displayName || comment.nickname)">
|
||||||
{{ getInitialLetter(comment.displayName || comment.nickname) }}
|
{{ getInitialLetter(comment.displayName || comment.nickname) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="comment-content-text" v-html="comment.content"></div>
|
<div class="comment-content-text" v-html="comment.content"></div>
|
||||||
<div class="comment-actions-bar">
|
<div class="comment-actions-bar">
|
||||||
<span class="like-button" @click="handleLike(comment)">
|
<span class="like-button" v-if="false" @click="handleLike(comment)">
|
||||||
<span v-if="comment.likes && comment.likes > 0" class="like-count">{{ comment.likes
|
<span v-if="comment.likes && comment.likes > 0" class="like-count">{{ comment.likes
|
||||||
}}</span>
|
}}</span>
|
||||||
👍 赞
|
👍 赞
|
||||||
@@ -43,8 +45,10 @@
|
|||||||
<div v-for="reply in comment.replies" :key="reply.messageid" class="reply-item-wrapper">
|
<div v-for="reply in comment.replies" :key="reply.messageid" class="reply-item-wrapper">
|
||||||
<div class="reply-header-info">
|
<div class="reply-header-info">
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<img v-if="getAvatarUrl(reply.messageimg)" :src="getAvatarUrl(reply.messageimg)" class="user-avatar" alt="头像">
|
<img v-if="getAvatarUrl(reply.messageimg)" :src="getAvatarUrl(reply.messageimg)"
|
||||||
<div v-else class="letter-avatar" :style="getLetterAvatarStyle(reply.displayName || reply.nickname)">
|
class="user-avatar" alt="头像">
|
||||||
|
<div v-else class="letter-avatar"
|
||||||
|
:style="getLetterAvatarStyle(reply.displayName || reply.nickname)">
|
||||||
{{ getInitialLetter(reply.displayName || reply.nickname) }}
|
{{ getInitialLetter(reply.displayName || reply.nickname) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +59,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="reply-content-text">{{ reply.content }}</div>
|
<div class="reply-content-text">{{ reply.content }}</div>
|
||||||
<div class="reply-actions-bar">
|
<div class="reply-actions-bar">
|
||||||
<span class="like-button" @click="handleLike(reply)">
|
|
||||||
|
<span class="like-button" v-if="false" @click="handleLike(reply)">
|
||||||
<span v-if="reply.likes && reply.likes > 0" class="like-count">{{ reply.likes
|
<span v-if="reply.likes && reply.likes > 0" class="like-count">{{ reply.likes
|
||||||
}}</span>
|
}}</span>
|
||||||
👍 赞
|
👍 赞
|
||||||
@@ -74,7 +79,10 @@
|
|||||||
还没有留言,快来抢沙发吧!
|
还没有留言,快来抢沙发吧!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 分页按钮 -->
|
||||||
|
<div class="pagination-controls" v-if="totalPages > 1">
|
||||||
|
<el-pagination size="medium" background :layout="pageLayout" v-model:current-page="pageNum" hide-on-single-page="true" @current-change="changePage" :page-size="pageSize" :page-count="totalPages" class="mt-4" />
|
||||||
|
</div>
|
||||||
<!-- 留言输入区 -->
|
<!-- 留言输入区 -->
|
||||||
<div class="comment-form-section">
|
<div class="comment-form-section">
|
||||||
<h2 class="comment-form-title">发送评论(请正确填写邮箱地址,否则将会当成垃圾评论处理)</h2>
|
<h2 class="comment-form-title">发送评论(请正确填写邮箱地址,否则将会当成垃圾评论处理)</h2>
|
||||||
@@ -111,8 +119,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-input-row">
|
<div class="form-input-row">
|
||||||
<el-form-item>
|
<el-form-item class="submit-button-container">
|
||||||
<el-button type="primary" @click="onSubmit" :loading="submitting"
|
<el-button type="primary" @click="onSubmit" class="form-submit-button" :loading="submitting"
|
||||||
:disabled="!form.content || !form.nickname">
|
:disabled="!form.content || !form.nickname">
|
||||||
发送
|
发送
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -132,7 +140,6 @@ import { useGlobalStore } from '@/store/globalStore'
|
|||||||
import { formatDate } from '@/utils/dateUtils'
|
import { formatDate } from '@/utils/dateUtils'
|
||||||
|
|
||||||
// ============================== 组件初始化 ==============================
|
// ============================== 组件初始化 ==============================
|
||||||
|
|
||||||
// 定义组件属性
|
// 定义组件属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
comments: {
|
comments: {
|
||||||
@@ -157,7 +164,11 @@ const formRef = ref() // 表单引用
|
|||||||
const captchaHint = ref('') // 验证码提示
|
const captchaHint = ref('') // 验证码提示
|
||||||
const captchaAnswer = ref('') // 验证码答案
|
const captchaAnswer = ref('') // 验证码答案
|
||||||
const showCaptchaHint = ref(false) // 是否显示验证码提示
|
const showCaptchaHint = ref(false) // 是否显示验证码提示
|
||||||
|
// 分页状态
|
||||||
|
const pageNum = ref(1) // 当前页码
|
||||||
|
const pageSize = ref(5) // 每页数量
|
||||||
|
const totalPages = ref(0) // 总页数
|
||||||
|
const pageLayout = ref('pager, next')// 分页布局
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
parentid: null, // 父留言ID
|
parentid: null, // 父留言ID
|
||||||
@@ -168,7 +179,6 @@ const form = reactive({
|
|||||||
email: '', // 邮箱
|
email: '', // 邮箱
|
||||||
captcha: '' // 验证码
|
captcha: '' // 验证码
|
||||||
})
|
})
|
||||||
|
|
||||||
// ============================== 表单验证规则 ==============================
|
// ============================== 表单验证规则 ==============================
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -397,7 +407,24 @@ const processReplyDisplayName = (reply, allReplies) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================== API调用模块 ==============================
|
// ============================== API调用模块 ==============================
|
||||||
|
/**
|
||||||
|
* 切换分页
|
||||||
|
* @param {number} page - 目标页码
|
||||||
|
*/
|
||||||
|
const changePage = (page) => {
|
||||||
|
fetchMessages()
|
||||||
|
// 根据当前页码优化分页布局
|
||||||
|
if (page === 1) {
|
||||||
|
// 第一页只显示页码和下一页按钮
|
||||||
|
pageLayout.value = 'pager, next'
|
||||||
|
} else if (page === totalPages.value) {
|
||||||
|
// 最后一页只显示上一页按钮和页码
|
||||||
|
pageLayout.value = 'prev, pager'
|
||||||
|
} else {
|
||||||
|
// 中间页显示完整的上一页、页码、下一页
|
||||||
|
pageLayout.value = 'prev, pager, next'
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 从后端获取留言列表
|
* 从后端获取留言列表
|
||||||
* 根据articleid决定获取文章留言还是全局留言
|
* 根据articleid决定获取文章留言还是全局留言
|
||||||
@@ -410,13 +437,13 @@ const fetchMessages = async () => {
|
|||||||
// 获取文章ID(优先使用props,其次使用全局状态)
|
// 获取文章ID(优先使用props,其次使用全局状态)
|
||||||
const articleid = getArticleId()
|
const articleid = getArticleId()
|
||||||
form.articleid = articleid
|
form.articleid = articleid
|
||||||
|
// 获取留言数量
|
||||||
// 根据是否有文章ID选择不同的API调用
|
messageService.getMessageCountByArticleId(articleid).then(res => {
|
||||||
res = await (articleid
|
if (res.code === 200) {
|
||||||
? messageService.getMessagesByArticleId(articleid)
|
totalPages.value = Math.ceil(res.data / pageSize.value)
|
||||||
: fetchAllMessages()
|
}
|
||||||
)
|
})
|
||||||
|
res = await (messageService.getMessagesByPage(articleid, pageNum.value - 1, pageSize.value))
|
||||||
// 验证响应结果
|
// 验证响应结果
|
||||||
if (!res || !res.data) {
|
if (!res || !res.data) {
|
||||||
handleEmptyResponse()
|
handleEmptyResponse()
|
||||||
@@ -425,6 +452,18 @@ const fetchMessages = async () => {
|
|||||||
|
|
||||||
// 处理留言数据
|
// 处理留言数据
|
||||||
messageBoardData.value = processMessageData(res.data)
|
messageBoardData.value = processMessageData(res.data)
|
||||||
|
|
||||||
|
// 根据当前页码更新分页布局
|
||||||
|
if (pageNum.value === 1) {
|
||||||
|
// 第一页只显示页码和下一页按钮
|
||||||
|
pageLayout.value = 'pager, next'
|
||||||
|
} else if (pageNum.value === totalPages.value) {
|
||||||
|
// 最后一页只显示上一页按钮和页码
|
||||||
|
pageLayout.value = 'prev, pager'
|
||||||
|
} else {
|
||||||
|
// 中间页显示完整的上一页、页码、下一页
|
||||||
|
pageLayout.value = 'prev, pager, next'
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleFetchError(error)
|
handleFetchError(error)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -460,6 +499,7 @@ const fetchAllMessages = async () => {
|
|||||||
const res = await messageService.getAllMessages()
|
const res = await messageService.getAllMessages()
|
||||||
// 过滤掉articleid不为空的留言,只保留articleid为空或不存在的留言
|
// 过滤掉articleid不为空的留言,只保留articleid为空或不存在的留言
|
||||||
if (res && res.data) {
|
if (res && res.data) {
|
||||||
|
|
||||||
res.data = res.data.filter(msg => !msg.articleid || msg.articleid === '')
|
res.data = res.data.filter(msg => !msg.articleid || msg.articleid === '')
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
@@ -600,7 +640,7 @@ const onSubmit = async () => {
|
|||||||
try {
|
try {
|
||||||
// 表单验证
|
// 表单验证
|
||||||
await validateForm()
|
await validateForm()
|
||||||
console.log('提交留言表单:', form)
|
// console.log('提交留言表单:', form)
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
|
|
||||||
@@ -641,7 +681,7 @@ const handleMessageSubmission = async () => {
|
|||||||
if (res.success) {
|
if (res.success) {
|
||||||
// 提交成功
|
// 提交成功
|
||||||
ElMessage.success(form.parentid ? '回复成功' : '留言成功')
|
ElMessage.success(form.parentid ? '回复成功' : '留言成功')
|
||||||
await fetchMessages() // 重新获取列表
|
await fetchMessages(0) // 重新获取列表
|
||||||
resetForm()
|
resetForm()
|
||||||
|
|
||||||
// 如果是回复模式,取消回复状态
|
// 如果是回复模式,取消回复状态
|
||||||
@@ -902,16 +942,24 @@ onMounted(() => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 内联表单输入行样式
|
* 内联表单输入行样式
|
||||||
* 用于将表单输入项与标签或其他元素对齐
|
* 用于将表单输入项水平均匀分布
|
||||||
*/
|
*/
|
||||||
.form-input-row--inline {
|
.form-input-row--inline {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input-row--inline div:nth-child(2) {
|
.form-input-row--inline .el-form-item {
|
||||||
margin-left: 9%;
|
flex: 1;
|
||||||
margin-right: 9%;
|
margin-right: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
width: calc(33.33% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input-row--inline .el-input {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 回复项容器 */
|
/* 回复项容器 */
|
||||||
@@ -959,6 +1007,64 @@ onMounted(() => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分页控件样式优化 */
|
||||||
|
.pagination-controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 30px 0;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination.is-background .el-pager li {
|
||||||
|
margin: 0 4px;
|
||||||
|
background-color: rgba(102, 161, 216, 0.1);
|
||||||
|
border: 1px solid rgba(102, 161, 216, 0.3);
|
||||||
|
color: #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination.is-background .el-pager li:hover {
|
||||||
|
background-color: rgba(102, 161, 216, 0.3);
|
||||||
|
border-color: rgba(102, 161, 216, 0.5);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination.is-background .el-pager li.is-active {
|
||||||
|
background-color: rgba(102, 161, 216, 0.8);
|
||||||
|
border-color: rgba(102, 161, 216, 0.8);
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination.is-background .btn-prev,
|
||||||
|
.pagination-controls .el-pagination.is-background .btn-next {
|
||||||
|
background-color: rgba(102, 161, 216, 0.1);
|
||||||
|
border: 1px solid rgba(102, 161, 216, 0.3);
|
||||||
|
color: #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
padding: 0 12px;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-controls .el-pagination.is-background .btn-prev:hover,
|
||||||
|
.pagination-controls .el-pagination.is-background .btn-next:hover {
|
||||||
|
background-color: rgba(102, 161, 216, 0.3);
|
||||||
|
border-color: rgba(102, 161, 216, 0.5);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
/* 评论表单区域 */
|
/* 评论表单区域 */
|
||||||
.comment-form-section {
|
.comment-form-section {
|
||||||
background-color: rgba(255, 255, 255, 0.85);
|
background-color: rgba(255, 255, 255, 0.85);
|
||||||
@@ -1056,17 +1162,27 @@ onMounted(() => {
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.form-input-row {
|
.form-input-row {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
}
|
||||||
|
|
||||||
|
.form-input-row--inline .el-form-item {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-header-info,
|
.comment-header-info,
|
||||||
.reply-header-info {
|
.reply-header-info {
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-submit-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.user-avatar {
|
.user-avatar {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="nonsense-container">
|
<div class="nonsense-container">
|
||||||
<div class="nonsense-list">
|
<div class="nonsense-list">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="loading-state-container">
|
||||||
|
<el-skeleton :count="5" />
|
||||||
|
</div>
|
||||||
|
<!-- 错误状态 -->
|
||||||
|
<div v-if="error" class="error-state-container">
|
||||||
|
<div>加载失败,请稍后重试</div>
|
||||||
|
<el-button type="primary" @click="handleRetry">重试</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-if="displayedNonsenseList.length === 0" class="empty-state-container">
|
||||||
|
<div>暂无吐槽内容</div>
|
||||||
|
</div>
|
||||||
<div class="nonsense-item" v-for="item in displayedNonsenseList" :key="item.id">
|
<div class="nonsense-item" v-for="item in displayedNonsenseList" :key="item.id">
|
||||||
<div class="nonsense-meta-info">
|
<div class="nonsense-meta-info">
|
||||||
<span class="nonsense-time">{{ formatRelativeTime(item.time) }}</span>
|
<span class="nonsense-time">{{ formatRelativeTime(item.time) }}</span>
|
||||||
@@ -44,10 +57,21 @@ const charRefs = ref(new Map())
|
|||||||
const charStyles = ref(new Map())
|
const charStyles = ref(new Map())
|
||||||
// 显示的吐槽内容列表
|
// 显示的吐槽内容列表
|
||||||
const displayedNonsenseList = ref([])
|
const displayedNonsenseList = ref([])
|
||||||
|
// 加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
// 错误状态
|
||||||
|
const error = ref(false)
|
||||||
|
|
||||||
// 处理分页数据更新
|
// 处理分页数据更新
|
||||||
const handleCurrentDataUpdate = (data) => {
|
const handleCurrentDataUpdate = (data) => {
|
||||||
displayedNonsenseList.value = data
|
displayedNonsenseList.value = data
|
||||||
console.log(data)
|
// console.log(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重试加载
|
||||||
|
const handleRetry = () => {
|
||||||
|
error.value = false
|
||||||
|
loadNonsenseList()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定时器引用
|
// 定时器引用
|
||||||
@@ -57,17 +81,24 @@ let colorChangeTimer = null
|
|||||||
* 加载所有吐槽内容
|
* 加载所有吐槽内容
|
||||||
*/
|
*/
|
||||||
const loadNonsenseList = async () => {
|
const loadNonsenseList = async () => {
|
||||||
|
// 设置加载状态
|
||||||
|
loading.value = true
|
||||||
|
error.value = false
|
||||||
try {
|
try {
|
||||||
const response = await nonsenseService.getNonsenseByStatus(1)
|
const response = await nonsenseService.getNonsenseByStatus(1)
|
||||||
if (response.code === 200) {
|
if (response.code === 200) {
|
||||||
nonsenseList.value = response.data
|
nonsenseList.value = response.data
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error('加载吐槽内容失败')
|
ElMessage.error('加载吐槽内容失败')
|
||||||
|
error.value = true
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error('加载吐槽内容失败:', error)
|
console.error('加载吐槽内容失败:', err)
|
||||||
|
error.value = true
|
||||||
} finally {
|
} finally {
|
||||||
console.log('加载吐槽内容完成')
|
// 结束加载状态
|
||||||
|
loading.value = false
|
||||||
|
// console.log('加载吐槽内容完成')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 编辑吐槽内容
|
// 编辑吐槽内容
|
||||||
@@ -357,8 +388,7 @@ onBeforeUnmount(() => {
|
|||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.nonsense-container {
|
.nonsense-container {
|
||||||
padding: 14px 4px 10px 4px;
|
margin: 0;
|
||||||
margin: 0 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nonsense-header h1 {
|
.nonsense-header h1 {
|
||||||
@@ -370,9 +400,9 @@ onBeforeUnmount(() => {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nonsense-list {
|
/* .nonsense-list {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.nonsense-item {
|
.nonsense-item {
|
||||||
padding: 14px 16px 10px 16px;
|
padding: 14px 16px 10px 16px;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const totalPages = computed(() => {
|
|||||||
if (!props.list || props.list.length === 0) return 0
|
if (!props.list || props.list.length === 0) return 0
|
||||||
// 如果列表长度小于pageSize,不进行分组,返回0表示不分页
|
// 如果列表长度小于pageSize,不进行分组,返回0表示不分页
|
||||||
if (props.list.length <= props.pageSize) return 0
|
if (props.list.length <= props.pageSize) return 0
|
||||||
console.log(props.list.length, props.pageSize)
|
// console.log(props.list.length, props.pageSize)
|
||||||
|
|
||||||
// 列表长度小于等于pageSize时,正常计算页数
|
// 列表长度小于等于pageSize时,正常计算页数
|
||||||
// 如果能整除,直接返回商
|
// 如果能整除,直接返回商
|
||||||
|
|||||||
@@ -26,7 +26,16 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0'
|
host: '0.0.0.0',
|
||||||
|
proxy: {
|
||||||
|
// 配置API代理
|
||||||
|
'/api': {
|
||||||
|
// target: 'http://www.qf1121.top',
|
||||||
|
target: 'http://localhost:7071',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user