feat: 实现文章状态管理及分类标签展示功能
新增文章状态管理功能,支持草稿、已发表和已删除状态的显示与切换 重构分类和标签展示模块,添加点击跳转功能 优化文章列表页面,增加状态筛选和分页功能 完善疯言疯语模块,支持编辑和删除操作 修复路由跳转和页面刷新问题
This commit is contained in:
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<div class="nonsense-container">
|
||||
<div class="nonsense-list">
|
||||
<div
|
||||
class="nonsense-item"
|
||||
v-for="item in nonsenseList"
|
||||
:key="item.id"
|
||||
>
|
||||
<div class="nonsense-item" v-for="item in nonsenseList" :key="item.id">
|
||||
<div class="nonsense-meta-info">
|
||||
<span class="nonsense-time">{{ formatRelativeTime(item.time) }}</span>
|
||||
<div class="article-status-badge-container" v-if="globalStore.Login">
|
||||
<span v-if="item.status === 0" class="article-status-badge badge badge-warning">未发表</span>
|
||||
<span v-if="item.status === 1" class="article-status-badge badge badge-success">已发表</span>
|
||||
<span v-if="item.status === 2" class="article-status-badge badge badge-danger">已删除</span>
|
||||
<span class="edit-button" @click="handleEdit(item)">编辑</span>
|
||||
<span class="delete-button" @click="handleDelete(item.id)">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nonsense-content">
|
||||
<span
|
||||
v-for="(char, index) in item.content.split('')"
|
||||
:key="index"
|
||||
:ref="el => setCharRef(el, item.id, index)"
|
||||
:style="getCharStyle(item.id, index)"
|
||||
>{{ char }}</span>
|
||||
<span v-for="(char, index) in item.content.split('')" :key="index" :ref="el => setCharRef(el, item.id, index)"
|
||||
:style="getCharStyle(item.id, index)">{{ char }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -24,9 +23,11 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import {nonsenseService } from '@/services'
|
||||
import { nonsenseService } from '@/services'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { formatRelativeTime} from '@/utils/dateUtils'
|
||||
import { formatRelativeTime } from '@/utils/dateUtils'
|
||||
import { useGlobalStore } from '@/store/globalStore'
|
||||
const globalStore = useGlobalStore()
|
||||
/**
|
||||
* 吐槽数据列表
|
||||
* 仅站长可见/可发
|
||||
@@ -45,10 +46,10 @@ let colorChangeTimer = null
|
||||
*/
|
||||
const loadNonsenseList = async () => {
|
||||
try {
|
||||
const response = await nonsenseService.getAllNonsense()
|
||||
const response = await nonsenseService.getNonsenseByStatus(1)
|
||||
if (response.code === 200) {
|
||||
nonsenseList.value = response.data
|
||||
}else{
|
||||
} else {
|
||||
ElMessage.error('加载吐槽内容失败')
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -57,7 +58,62 @@ const loadNonsenseList = async () => {
|
||||
console.log('加载吐槽内容完成')
|
||||
}
|
||||
}
|
||||
// 编辑吐槽内容
|
||||
const handleEdit = (item) => {
|
||||
// 清除更新文章状态
|
||||
|
||||
ElMessageBox.prompt('请输入您的疯言疯语:', '发表疯言疯语', {
|
||||
confirmButtonText: '保存',
|
||||
cancelButtonText: '取消',
|
||||
inputValue: item.content,
|
||||
inputType: 'textarea',
|
||||
inputRows: 4,
|
||||
showCancelButton: true
|
||||
}).then(({ value }) => {
|
||||
// 保存疯言疯语
|
||||
updateNonsense(value, item.id)
|
||||
}).catch(() => {
|
||||
// 取消操作,静默处理
|
||||
})
|
||||
}
|
||||
// 更新吐槽内容
|
||||
const updateNonsense = (content, id) => {
|
||||
if (!content || content.trim() === '') {
|
||||
ElMessage.warning('内容不能为空')
|
||||
return
|
||||
}
|
||||
// 调用服务更新疯言疯语
|
||||
nonsenseService.updateNonsense({
|
||||
id,
|
||||
content: content.trim(),
|
||||
time: new Date(),
|
||||
status: 1
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('疯言疯语更新成功')
|
||||
loadNonsenseList()
|
||||
} else {
|
||||
ElMessage.error(response.message || '更新失败')
|
||||
}
|
||||
}).catch(err => handleErrorResponse(err, '更新失败'))
|
||||
}
|
||||
// 删除吐槽内容
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
// 确认删除
|
||||
const confirm = window.confirm('确定删除吗?')
|
||||
if (!confirm) return
|
||||
const response = await nonsenseService.deleteNonsense(id)
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
loadNonsenseList()
|
||||
} else {
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error)
|
||||
}
|
||||
}
|
||||
// 设置字符引用
|
||||
const setCharRef = (el, itemId, index) => {
|
||||
if (el) {
|
||||
@@ -82,12 +138,12 @@ const getRandomColor = () => {
|
||||
const randomChangeColors = () => {
|
||||
const keys = Array.from(charRefs.value.keys())
|
||||
if (keys.length === 0) return
|
||||
|
||||
|
||||
// 随机选择20-30%的字符改变颜色
|
||||
const countToChange = Math.floor(keys.length * (Math.random() * 0.1 + 0.2))
|
||||
const shuffledKeys = [...keys].sort(() => 0.5 - Math.random())
|
||||
const selectedKeys = shuffledKeys.slice(0, countToChange)
|
||||
|
||||
|
||||
// 创建信号故障效果
|
||||
createSignalGlitchEffect(selectedKeys)
|
||||
}
|
||||
@@ -96,29 +152,29 @@ const randomChangeColors = () => {
|
||||
const createDigitalNoiseEffect = (selectedKeys) => {
|
||||
// 噪点字符集
|
||||
const noiseChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()_+-=[]{}|;:,.<>?';
|
||||
|
||||
|
||||
selectedKeys.forEach(key => {
|
||||
const charElement = charRefs.value.get(key);
|
||||
if (charElement) {
|
||||
const rect = charElement.getBoundingClientRect();
|
||||
const container = charElement.closest('.nonsense-item');
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
|
||||
|
||||
// 生成3-5个噪点
|
||||
const noiseCount = Math.floor(Math.random() * 3) + 3;
|
||||
|
||||
|
||||
for (let i = 0; i < noiseCount; i++) {
|
||||
const noiseEl = document.createElement('span');
|
||||
noiseEl.textContent = noiseChars.charAt(Math.floor(Math.random() * noiseChars.length));
|
||||
|
||||
|
||||
// 随机位置相对于原字符
|
||||
const x = Math.random() * 30 - 20; // -20到20px
|
||||
const y = Math.random() * 20 - 15; // -15到15px
|
||||
|
||||
|
||||
// 随机大小和透明度
|
||||
const size = Math.random() * 8 + 8; // 8-16px
|
||||
const opacity = Math.random() * 0.6 + 0.2; // 0.2-0.8
|
||||
|
||||
|
||||
noiseEl.style.cssText = `
|
||||
position: absolute;
|
||||
left: ${rect.left - containerRect.left + x}px;
|
||||
@@ -132,7 +188,7 @@ const createDigitalNoiseEffect = (selectedKeys) => {
|
||||
transition: all 0.2s ease;
|
||||
`;
|
||||
container.appendChild(noiseEl);
|
||||
|
||||
|
||||
// 噪点逐渐消失
|
||||
setTimeout(() => {
|
||||
noiseEl.style.opacity = '0';
|
||||
@@ -158,10 +214,10 @@ const createSignalGlitchEffect = (selectedKeys) => {
|
||||
}
|
||||
charStyles.value.set(key, glitchOffset)
|
||||
})
|
||||
|
||||
|
||||
// 同时创建数字噪点效果
|
||||
createDigitalNoiseEffect(selectedKeys);
|
||||
|
||||
|
||||
// 第二步:快速恢复并闪烁
|
||||
setTimeout(() => {
|
||||
selectedKeys.forEach(key => {
|
||||
@@ -172,7 +228,7 @@ const createSignalGlitchEffect = (selectedKeys) => {
|
||||
}
|
||||
charStyles.value.set(key, flashStyle)
|
||||
})
|
||||
|
||||
|
||||
// 第三步:最终设置新颜色
|
||||
setTimeout(() => {
|
||||
selectedKeys.forEach(key => {
|
||||
@@ -185,12 +241,13 @@ const createSignalGlitchEffect = (selectedKeys) => {
|
||||
charStyles.value.set(key, finalStyle)
|
||||
})
|
||||
}, 80)
|
||||
}, 100)}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据并启动定时器
|
||||
onMounted(() => {
|
||||
loadNonsenseList()
|
||||
|
||||
|
||||
// 启动定时器
|
||||
colorChangeTimer = setInterval(randomChangeColors, 1000)
|
||||
})
|
||||
@@ -216,7 +273,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
/* 吐槽页面主容器悬浮效果 */
|
||||
.nonsense-container:hover {
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.08);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 吐槽头部样式 */
|
||||
@@ -247,27 +304,34 @@ onBeforeUnmount(() => {
|
||||
|
||||
/* 吐槽项样式 */
|
||||
.nonsense-item {
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
background-color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px 14px 20px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.03);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
|
||||
position: relative;
|
||||
transition: box-shadow 0.2s, transform 0.2s ease;
|
||||
}
|
||||
|
||||
/* 吐槽项悬浮效果 */
|
||||
.nonsense-item:hover {
|
||||
box-shadow: 0 4px 16px rgba(64,158,255,0.12);
|
||||
box-shadow: 0 4px 16px rgba(64, 158, 255, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 吐槽元信息样式 */
|
||||
.nonsense-meta-info {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
margin-bottom: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.nonsense-meta-info span {
|
||||
padding: 4px 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 吐槽内容样式 */
|
||||
.nonsense-content {
|
||||
@@ -284,20 +348,20 @@ onBeforeUnmount(() => {
|
||||
padding: 14px 4px 10px 4px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
|
||||
.nonsense-header h1 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
|
||||
.nonsense-content {
|
||||
font-size: 0.98rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
|
||||
.nonsense-list {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
.nonsense-item {
|
||||
padding: 14px 16px 10px 16px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user