feat: 优化前端布局和代理配置

refactor: 移除调试日志并优化代码结构
style: 调整响应式设计和UI细节
fix: 修复路由和导航相关的问题
This commit is contained in:
qingfeng1121
2025-12-12 17:14:04 +08:00
parent 07ce8409e1
commit ede67faafd
14 changed files with 296 additions and 128 deletions

View File

@@ -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>
@@ -128,7 +128,7 @@ 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,
}) })

View File

@@ -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="{ marginTop: heroMarginTop, marginBottom: heroMarginBottom, transform: heroTransform, transition: 'all 0.3s ease' }">
<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>
@@ -100,12 +101,23 @@ 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 heroMarginTop = ref('45%');
const heroMarginBottom = ref('45%');
// hero区域的初始margin值从CSS变量获取
const initialHeroMargin = 45;
// hero是否开始向上移动
const heroIsMoving = ref(false);
// hero的transform值用于实现向上移动和吸附效果
const heroTransform = ref('translateY(0)');
// 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);
@@ -131,7 +143,7 @@ let heroTimer: number | undefined;
* 初始化并启动打字机效果 * 初始化并启动打字机效果
* @param {string} text - 要显示的完整文本 * @param {string} text - 要显示的完整文本
*/ */
const startTypewriter = () => { const startTypewriter = (text: string) => {
// 重置状态 // 重置状态
heroText.value = ''; heroText.value = '';
heroIndex = 0; heroIndex = 0;
@@ -143,8 +155,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 +173,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 +216,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";
}; };
@@ -316,7 +332,7 @@ const handleResize = () => {
// 首页特殊处理:小屏幕下默认显示内容区 // 首页特殊处理:小屏幕下默认显示内容区
if (rpsliturl[1] === localhome) { if (rpsliturl[1] === localhome) {
isconts.value = window.innerWidth <= 768; iscontentvisible.value = window.innerWidth <= 768;
} }
}; };
@@ -336,10 +352,50 @@ const handleScroll = () => {
// 仅在首页根路径应用滚动动画 // 仅在首页根路径应用滚动动画
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) { if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
// 控制内容区和左侧模块的滚动状态 const scrollY = window.scrollY;
isconts.value = window.scrollY > 200; const windowHeight = window.innerHeight;
isScrollingleftmodlue.value = window.scrollY > 600;
// 计算滚动距离与窗口高度的比例,用于内容渐显
const contentScrollRatio = Math.min(scrollY / windowHeight, 1);
// 计算新的底部margin值从初始值45%逐渐减少到25%
const newMarginBottom = Math.max(initialHeroMargin - (initialHeroMargin * contentScrollRatio), 0);
// 更新hero的底部margin值
heroMarginBottom.value = `${newMarginBottom}%`;
// 顶部margin保持不变
heroMarginTop.value = `${initialHeroMargin}%`;
// 内容区域随滚动逐渐显现
// 当滚动超过50px时开始显示滚动到一屏高度时完全显示
iscontentvisible.value = scrollY > 50;
// 计算hero的移动阶段
if (scrollY >= 555) {
// 滚动超过555px标题开始向上移动
heroIsMoving.value = true;
heroPosition.value = 'moving';
// 计算移动距离从0到初始marginTop值(45%)
// 使用滚动距离减去起始位置(555px),除以移动范围(500px),得到移动比例
const moveScrollRatio = Math.min((scrollY - 555) / 500, 1);
const moveDistance = initialHeroMargin * moveScrollRatio;
heroTransform.value = `translateY(-${moveDistance*10}%)`;
// 当移动距离达到初始marginTop值时吸附到顶部
if (moveScrollRatio >= 1) {
heroPosition.value = 'sticky';
heroTransform.value = `translateY(-${initialHeroMargin*10}%)`;
} }
} else {
// 滚动未超过555px标题保持静态
heroIsMoving.value = false;
heroPosition.value = 'static';
heroTransform.value = 'translateY(0)';
}
// 控制左侧模块的滚动状态
isScrollingleftmodlue.value = scrollY > 600;
}
}; };
/** /**
@@ -376,12 +432,26 @@ const handleRouteChange = () => {
// 根据是否为首页决定是否启动打字机效果 // 根据是否为首页决定是否启动打字机效果
if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) { if (rpsliturl[1] === localhome && rpsliturl[2] == undefined) {
// 首页启动打字机效果 // 首页启动打字机效果
startTypewriter(); startTypewriter(fullHeroText);
// 重置hero的margin值为初始值
heroMarginBottom.value = `${initialHeroMargin}%`;
heroMarginTop.value = `${initialHeroMargin}%`;
// 重置hero的移动状态
heroIsMoving.value = false;
heroPosition.value = 'static';
heroTransform.value = 'translateY(0)';
// 移动端首页默认显示内容区,桌面端初始隐藏
iscontentvisible.value = window.innerWidth <= 768;
} else { } else {
// 非首页直接显示完整文本 // 非首页直接显示完整文本
isconts.value = true; iscontentvisible.value = true;
stopTypewriter(); stopTypewriter();
} }
// 不在首页时hero的margin为0
if (rpsliturl[1] !== localhome) {
heroMarginBottom.value = '0%';
heroMarginTop.value = '0%';
}
}; };
/** /**
@@ -392,7 +462,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 = `${initialHeroMargin}%`;
heroMarginTop.value = `${initialHeroMargin}%`;
// 重置hero的移动状态
heroIsMoving.value = false;
heroPosition.value = 'static';
heroTransform.value = 'translateY(0)';
// 移动端首页默认显示内容区,桌面端初始隐藏
iscontentvisible.value = window.innerWidth <= 768;
} else {
// 非首页清空hero内容
heroText.value = '';
} }
}; };
@@ -514,9 +597,11 @@ watch(
.close-btn:hover { .close-btn:hover {
color: #409eff; color: #409eff;
} }
.footer-container { .footer-container {
margin-top: 20px; margin-top: 20px;
} }
/* 防止搜索框在小屏幕上重叠 */ /* 防止搜索框在小屏幕上重叠 */
@media screen and (max-width: 1200px) { @media screen and (max-width: 1200px) {
.search-box-container.open { .search-box-container.open {

View File

@@ -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) {

View File

@@ -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
}) })

View File

@@ -17,12 +17,14 @@
/* 内容区圆角 */ /* 内容区圆角 */
/* 首页 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: 45% 0;
/* hero 顶部外边距 */ /* hero 顶边距 */
--hero-min-margin-top: 45%;
/* hero 最小顶边距 */
/* 标题样式 */ /* 标题样式 */
--title-font-size: 3.5rem; --title-font-size: 3.5rem;
@@ -368,18 +370,21 @@ 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%;
} }
/* 打字机效果 */ /* 打字机效果 */
@@ -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 {
}
}

View File

@@ -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;
} }

View File

@@ -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('获取分类列表失败,请稍后重试');
@@ -118,7 +118,7 @@ const handleCategoryClick = (attribute: any) => {
id: attribute.attributeid, id: attribute.attributeid,
name: attribute.typename name: attribute.typename
}) })
console.log(attribute) // console.log(attribute)
router.push({ router.push({
path: '/home/aericletype', path: '/home/aericletype',
@@ -385,6 +385,7 @@ onMounted(() => {
.article-content { .article-content {
padding: 15px; padding: 15px;
height: 600px;
} }
.category-group-container { .category-group-container {

View File

@@ -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

View File

@@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<!-- 分页区域 --> <!-- 分页区域 -->
<PaginationComponent class="pagination-container" :list="articleList" :pageSize="10" @changePage="handleCurrentDataUpdate" /> <PaginationComponent class="pagination-container" :list="articleList" :pageSize="10" @changePage="handleCurrentDataUpdate" :key="'pagination'" />
</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">
@@ -69,7 +69,7 @@ const loading = ref(false)
*/ */
const handleCurrentDataUpdate = (data) => { const handleCurrentDataUpdate = (data) => {
displayedArticles.value = data displayedArticles.value = data
console.log('更新后的当前页数据:', data) // console.log('更新后的当前页数据:', data)
} }
// ========== 文章数据获取模块 ========== // ========== 文章数据获取模块 ==========
@@ -81,7 +81,7 @@ 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 'aericletype':
@@ -98,7 +98,7 @@ const getArticlesByRoute = async () => {
return await articleService.getArticlesByStatus(statusData?.status) return await articleService.getArticlesByStatus(statusData?.status)
default: default:
// 默认获取所有文章 // 默认获取所有文章
console.log('获取所有文章列表') // console.log('获取所有文章列表')
return await articleService.getAllArticles() return await articleService.getAllArticles()
} }
} }
@@ -189,7 +189,7 @@ const fetchArticles = async () => {
console.error('获取文章列表失败:', error) console.error('获取文章列表失败:', error)
ElMessage.error('获取文章列表失败,请稍后重试') ElMessage.error('获取文章列表失败,请稍后重试')
} finally { } finally {
console.log('最终文章列表数据:', articleList.value) // console.log('最终文章列表数据:', articleList.value)
loading.value = false loading.value = false
} }
} }
@@ -230,7 +230,7 @@ const handleArticleClick = (article) => {
*/ */
const handleRouteChange = () => { const handleRouteChange = () => {
fetchArticles() fetchArticles()
console.log('路由变化,重新获取文章列表') // console.log('路由变化,重新获取文章列表')
} }
/** /**
@@ -414,7 +414,6 @@ watch(
/* 响应式设计 - 平板和手机 */ /* 响应式设计 - 平板和手机 */
@media (max-width: 768px) { @media (max-width: 768px) {
.article-list-container { .article-list-container {
padding: 0 10px;
} }
.article-card { .article-card {

View File

@@ -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)

View File

@@ -27,7 +27,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>
👍 👍
@@ -55,7 +55,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>
👍 👍
@@ -111,8 +112,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>
@@ -600,7 +601,7 @@ const onSubmit = async () => {
try { try {
// 表单验证 // 表单验证
await validateForm() await validateForm()
console.log('提交留言表单:', form) // console.log('提交留言表单:', form)
submitting.value = true submitting.value = true
@@ -902,16 +903,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%;
} }
/* 回复项容器 */ /* 回复项容器 */
@@ -1055,18 +1064,20 @@ onMounted(() => {
.comment-form-section { .comment-form-section {
padding: 16px; padding: 16px;
} }
.el-form-item {
width: 100%;
}
.form-input-row { .form-input-row {
flex-direction: column; flex-direction: column;
gap: 8px;
} }
.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;

View File

@@ -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;

View File

@@ -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时正常计算页数
// 如果能整除,直接返回商 // 如果能整除,直接返回商

View File

@@ -26,7 +26,15 @@ export default defineConfig({
}, },
}, },
server: { server: {
host: '0.0.0.0' host: '0.0.0.0',
proxy: {
// 配置API代理
'/api': {
target: 'http://localhost:7071',
changeOrigin: true,
rewrite: (path) => path
}
}
}, },
}) })