+
-
共 {{ categoryGroup.attributes.length }} 篇
+
+
共 {{ categoryGroup.attributes.reduce((total, cat) => total + (cat.articles && cat.articles.length ? cat.articles.length : 0), 0) }} 篇
@@ -66,90 +65,61 @@ const error = ref('')
const fetchCategories = async () => {
try {
loading.value = true;
- error.value = '';
-
- const res = await categoryService.getAllCategories();
-
- if (res.data && res.data.length > 0) {
- // 创建处理后的分类数组
- const processedCategories = res.data.map(item => ({
+ const res = await categoryService.getAllCategories();
+ let processedCategories: any[] = [];
+ if (res.code === 200) {
+ processedCategories = res.data.map(item => ({
...item,
attributes: [] // 使用更清晰的命名
}));
-
- // 并行处理所有分类
+
+ // 使用Promise.all等待所有异步操作完成
await Promise.all(
- processedCategories.map(async (category) => {
- try {
- if (await categoryAttributeService.checkAttributeExists(category.typeid, category.typename || '')) {
- // 获取分类的所有属性
- const attributesRes = await categoryAttributeService.getAttributeById(category.typeid);
- // 存储属性数据
- if (attributesRes.data) {
- category.attributes = Array.isArray(attributesRes.data) ? attributesRes.data : [attributesRes.data];
- }
- }
- } catch (err) {
- // console.error(`处理分类失败 (分类ID: ${category.typeid}):`, err);
+ processedCategories.map(async category => {
+ const attributes = await categoryAttributeService.getAttributesByCategory(category.typeid);
+ if (attributes.code === 200 && Array.isArray(attributes.data)) {
+ const processedAttributes = await Promise.all(
+ attributes.data.map(async item => {
+ const articleItem = {
+ ...item,
+ articles: []
+ };
+ const articlesRes = await articleService.getArticlesByAttributeId(item.attributeid);
+ if(articlesRes.code === 200 && Array.isArray(articlesRes.data)){
+ articleItem.articles = articlesRes.data;
+ }
+ return articleItem;
+ })
+ );
+ category.attributes = processedAttributes;
}
})
);
- await getCategoryAttributes(processedCategories);
- console.log('获取分类列表成功:', categories.value);
- } else {
- categories.value = [];
}
+ categories.value = processedCategories;
+ console.log('获取分类列表成功:', categories.value);
} catch (err) {
- error.value = '获取分类列表失败,请稍后重试';
console.error('获取分类列表失败:', err);
- ElMessage.error(error.value);
- } finally {
+ ElMessage.error('获取分类列表失败,请稍后重试');
+ }finally{
loading.value = false;
- console.log('分类列表加载完成');
}
-};
-// 处理所有分类及其属性的文章数据
-const getCategoryAttributes = async (processedCategories: any[]) => {
- // 遍历所有分类
- for (const category of processedCategories) {
- if (category && category.attributes && category.attributes.length > 0) {
- console.log(`处理分类: ${category.typename || '未命名分类'}`);
- // 并行获取每个属性下的文章
- await Promise.all(
- category.attributes.map(async (attribute) => {
- try {
- // 使用正确的方法名获取属性下的文章,优先使用typeid
- const idToUse = attribute.typeid || attribute.categoryid;
- const articlesRes = await articleService.getArticlesByAttributeId(idToUse);
- // 处理文章数据
- attribute.articles = articlesRes.data && Array.isArray(articlesRes.data) ?
- articlesRes.data : [];
- } catch (err) {
- console.error(`获取属性文章失败 (属性ID: ${attribute.typeid || attribute.categoryid}):`, err);
- attribute.articles = [];
- }
- })
- );
- }
- }
-
- // 更新分类列表
- categories.value = processedCategories;
- console.log('所有分类属性文章数据处理完成');
}
/**
- * 处理分类点击事件
- * 注意:现在实际上使用的是属性ID而不是分类ID
- * @param {string | number} attributeId - 属性ID
- */
+* 处理分类点击事件
+* 注意:现在实际上使用的是属性ID而不是分类ID
+* @param {string | number} attributeId - 属性ID
+*/
const handleCategoryClick = (attribute: any) => {
+ globalStore.removeValue('attribute')
globalStore.setValue('attribute', {
- id: attribute.typeid || attribute.categoryid,
- name: attribute.attributename || attribute.typename || '未命名属性',
+ id: attribute.attributeid,
+ name: attribute.typename
})
console.log(attribute)
router.push({
path: '/home/aericletype',
+
})
}
diff --git a/src/views/articlecontents.vue b/src/views/articlecontents.vue
index e281452..12c8715 100644
--- a/src/views/articlecontents.vue
+++ b/src/views/articlecontents.vue
@@ -19,18 +19,16 @@
{{ article.title }}
-
-
- {{ formatDate(article.createdAt) }}
-
-
-
- {{ article.categoryName || '未分类' }}
-
-
-
- {{ article.viewCount || 0 }} 阅读
-
+
{{ formatRelativeTime(article.createdAt) }}
+
{{ article.categoryName }}
+
{{ article.viewCount }} 阅读
+
{{ article.likes }} 点赞
+
{{ article.commentCount }} 评论
+
+ 草稿
+ 已发表
+ 已删除
+
@@ -85,12 +83,10 @@
// 导入必要的依赖
import { useRoute, useRouter } from 'vue-router'
import { ref, onMounted } from 'vue'
-import { articleService } from '@/services'
-import { categoryAttributeService } from '@/services'
import { useGlobalStore } from '@/store/globalStore'
import { ElMessage } from 'element-plus'
import type { Article } from '@/types'
-import { formatDate } from '@/utils/dateUtils'
+import { formatRelativeTime } from '@/utils/dateUtils'
import messageboard from './messageboard.vue'
import markdownViewer from './markdown.vue'
// 路由相关
@@ -122,22 +118,19 @@ const fetchArticleDetail = async () => {
if (response) {
article.value = response
// 获取并设置分类名称
- const categoryResponse = await categoryAttributeService.getAttributeById(Number(article.value.attributeid))
- article.value.categoryName = categoryResponse.data.attributename || '未分类'
-
- // 增加文章浏览量
- try {
- await articleService.incrementArticleViews(Number(articleId))
- // 更新前端显示的浏览量
- if (article.value.viewCount) {
- article.value.viewCount++
- } else {
- article.value.viewCount = 1
- }
- } catch (err) {
- console.error('增加文章浏览量失败:', err)
- // 不阻止主流程
+ // const categoryResponse = await categoryAttributeService.getAttributeById(Number(article.value.attributeid))
+ // article.value.categoryName = categoryResponse.data.attributename || '未分类'
+ // 获取并设置评论量
+ // const commentResponse = await messageService.getMessagesByArticleId(articleId)
+ // article.value.commentCount = commentResponse.data.length || 0
+ // 更新浏览量
+ // 更新前端显示的浏览量
+ if (article.value.viewCount) {
+ article.value.viewCount++
+ } else {
+ article.value.viewCount = 1
}
+
} else {
throw new Error('文章不存在或已被删除')
}
@@ -181,7 +174,7 @@ onMounted(() => {
.article-detail-wrapper {
max-width: 900px;
margin: 0 auto;
- background-color: rgba(255, 255, 255, 0.85);
+ background-color: rgba(255, 255, 255, 0.85);
border-radius: 12px;
padding: 40px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
@@ -193,7 +186,7 @@ onMounted(() => {
max-width: 900px;
margin: 0 auto;
padding: 40px;
- background-color: rgba(255, 255, 255, 0.85);
+ background-color: rgba(255, 255, 255, 0.85);
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
@@ -203,7 +196,7 @@ onMounted(() => {
max-width: 900px;
margin: 0 auto;
padding: 60px 40px;
- background-color: rgba(255, 255, 255, 0.85);
+ background-color: rgba(255, 255, 255, 0.85);
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
@@ -218,7 +211,7 @@ onMounted(() => {
max-width: 900px;
margin: 0 auto;
padding: 80px 40px;
- background-color: rgba(255, 255, 255, 0.85);
+ background-color: rgba(255, 255, 255, 0.85);
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
diff --git a/src/views/articlesave.vue b/src/views/articlesave.vue
index 3d8d03b..7bc45d3 100644
--- a/src/views/articlesave.vue
+++ b/src/views/articlesave.vue
@@ -14,7 +14,7 @@
-
+
@@ -45,6 +45,8 @@ import 'md-editor-v3/lib/style.css';
import { categoryService, categoryAttributeService, articleService } from '@/services';
import type { Article } from '@/types/index.ts';
import { ElMessage } from 'element-plus';
+import { useGlobalStore } from '@/store/globalStore'
+const globalStore = useGlobalStore()
// 路由
import router from '@/router/Router';
const Articleform = ref({
@@ -55,7 +57,8 @@ const Articleform = ref({
categoryName: '',
createdAt: '',
updatedAt: '',
- markdownscontent: ''
+ markdownscontent: '',
+ status: 0 // 默认状态为草稿
})
// 用于级联选择器的值绑定
@@ -63,16 +66,29 @@ const selectedValues = ref([]);
const categorieoptions = ref([]);
const statusoptions = ref([
{
- label: '未发布',
+ label: '草稿',
value: '0'
},
{
label: '发布',
value: '1'
+ },
+ {
+ label: '删除',
+ value: '2'
}
]);
const categories = ref([]);
+// 编辑文章
+const editArticle = globalStore.getValue('updatearticle')
+if (editArticle) {
+ Articleform.value = {
+ ...editArticle,
+ // 确保status是字符串格式,与statusoptions的value格式匹配
+ status: editArticle.status !== undefined ? String(editArticle.status) : '0'
+ }
+}
// 初始化加载分类和属性,构建级联选择器的options
const loadCategories = async () => {
try {
@@ -107,7 +123,22 @@ const loadCategories = async () => {
})
);
categorieoptions.value = optionsData;
- console.log(optionsData);
+
+ // 如果是编辑模式且有attributeid,设置级联选择器的默认值
+ if (Articleform.value.articleid !== 0 && Articleform.value.attributeid !== 0) {
+ // 查找属性所属的分类
+ for (const category of optionsData) {
+ const foundAttribute = category.children.find(attr => attr.value === Articleform.value.attributeid.toString());
+ if (foundAttribute) {
+ // 设置级联选择器的值:[分类ID, 属性ID]
+ selectedValues.value = [category.value, foundAttribute.value];
+ break;
+ }
+ }
+ }
+
+ console.log('分类选项:', optionsData);
+ console.log('选中的值:', selectedValues.value);
}
} catch (error) {
console.error('加载分类失败:', error);
@@ -138,6 +169,7 @@ const handleSave = (markdown) => {
// 构建请求数据
const articleData = {
+ articleid: Articleform.value.articleid,
title: Articleform.value.title,
content: Articleform.value.content,
attributeid: Number(Articleform.value.attributeid),
@@ -150,28 +182,37 @@ const handleSave = (markdown) => {
console.log('发送文章数据:', articleData);
console.log('当前认证token是否存在:', !!localStorage.getItem('token'));
- // 保存文章
- articleService.createArticle(articleData)
+ // 根据articleid决定调用创建还是更新接口
+ const savePromise = Articleform.value.articleid === 0
+ ? articleService.createArticle(articleData)
+ : articleService.updateArticle(Articleform.value.articleid, articleData);
+ savePromise
.then(res => {
console.log('API响应:', res);
if (res.code === 200) {
- ElMessage.success('文章保存成功')
- // 重置表单
- Articleform.value = {
- articleid: 0,
- title: '',
- content: '',
- attributeid: 0,
- categoryName: '',
- createdAt: '',
- updatedAt: '',
- markdownscontent: ''
- };
- selectedValues.value = [];
+ ElMessage.success(Articleform.value.articleid === 0 ? '文章创建成功' : '文章更新成功');
+ // 清除全局存储中的article
+ globalStore.removeValue('updatearticle');
+ // 重置表单或保留编辑状态
+ if (Articleform.value.articleid === 0) {
+ // 创建新文章后重置表单
+ Articleform.value = {
+ articleid: 0,
+ title: '',
+ content: '',
+ attributeid: 0,
+ categoryName: '',
+ createdAt: '',
+ updatedAt: '',
+ markdownscontent: ''
+ };
+ selectedValues.value = [];
+ }
+
// 返回列表页
router.push('/home');
} else {
- ElMessage.error(res.message || '文章保存失败')
+ ElMessage.error(res.message || (Articleform.value.articleid === 0 ? '文章创建失败' : '文章更新失败'));
}
})
.catch(err => {
@@ -181,17 +222,19 @@ const handleSave = (markdown) => {
console.error('错误状态码:', err.response.status);
console.error('错误响应数据:', err.response.data);
+ const operationType = Articleform.value.articleid === 0 ? '创建' : '更新';
+
if (err.response.status === 401) {
ElMessage.error('未授权访问,请先登录');
} else if (err.response.status === 403) {
- ElMessage.error('没有权限创建文章,请检查账号权限');
+ ElMessage.error(`没有权限${operationType}文章,请检查账号权限`);
} else if (err.response.status === 400) {
ElMessage.error('数据验证失败: ' + (err.response.data?.message || '请检查输入'));
} else {
- ElMessage.error('请求被拒绝,错误代码: ' + err.response.status);
+ ElMessage.error(`请求被拒绝,错误代码: ${err.response.status}`);
}
} else {
- ElMessage.error(err.message || '文章保存失败')
+ ElMessage.error(err.message || (Articleform.value.articleid === 0 ? '文章创建失败' : '文章更新失败'));
}
})
};
diff --git a/src/views/home.vue b/src/views/home.vue
index a315a1b..d5ef8d4 100644
--- a/src/views/home.vue
+++ b/src/views/home.vue
@@ -20,10 +20,15 @@
{{ formatContentPreview(article.content, 150) }}
{{ formatRelativeTime(article.createdAt || article.createTime) }}
+
{{ article.categoryName }}
{{ article.viewCount }} 阅读
-
{{ article.categoryName }}
{{ article.likes }} 点赞
{{ article.commentCount }} 评论
+
+ 草稿
+ 已发表
+ 已删除
+