mirror of
https://github.com/rachelos/we-mp-rss.git
synced 2026-09-24 16:02:42 +08:00
1.4.8-Fix
This commit is contained in:
+337
-296
@@ -1,297 +1,338 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status as fast_status, Query
|
||||
from core.auth import get_current_user
|
||||
from core.db import DB
|
||||
from core.models.base import DATA_STATUS
|
||||
from core.models.article import Article,ArticleBase
|
||||
from sqlalchemy import and_, or_, desc
|
||||
from .base import success_response, error_response
|
||||
from core.config import cfg
|
||||
from apis.base import format_search_kw
|
||||
from core.print import print_warning, print_info, print_error, print_success
|
||||
router = APIRouter(prefix=f"/articles", tags=["文章管理"])
|
||||
|
||||
|
||||
|
||||
@router.delete("/clean", summary="清理无效文章(MP_ID不存在于Feeds表中的文章)")
|
||||
async def clean_orphan_articles(
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
from core.models.feed import Feed
|
||||
from core.models.article import Article
|
||||
|
||||
# 找出Articles表中mp_id不在Feeds表中的记录
|
||||
subquery = session.query(Feed.id).subquery()
|
||||
deleted_count = session.query(Article)\
|
||||
.filter(~Article.mp_id.in_(subquery))\
|
||||
.delete(synchronize_session=False)
|
||||
|
||||
session.commit()
|
||||
|
||||
return success_response({
|
||||
"message": "清理无效文章成功",
|
||||
"deleted_count": deleted_count
|
||||
})
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
print(f"清理无效文章错误: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_201_CREATED,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message="清理无效文章失败"
|
||||
)
|
||||
)
|
||||
|
||||
@router.delete("/clean_duplicate_articles", summary="清理重复文章")
|
||||
async def clean_duplicate(
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
try:
|
||||
from tools.clean import clean_duplicate_articles
|
||||
(msg, deleted_count) =clean_duplicate_articles()
|
||||
return success_response({
|
||||
"message": msg,
|
||||
"deleted_count": deleted_count
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"清理重复文章: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_201_CREATED,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message="清理重复文章"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.api_route("", summary="获取文章列表",methods= ["GET", "POST"], operation_id="get_articles_list")
|
||||
async def get_articles(
|
||||
offset: int = Query(0, ge=0),
|
||||
limit: int = Query(5, ge=1, le=100),
|
||||
status: str = Query(None),
|
||||
search: str = Query(None),
|
||||
mp_id: str = Query(None),
|
||||
has_content:bool=Query(False),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
|
||||
|
||||
# 构建查询条件
|
||||
query = session.query(ArticleBase)
|
||||
if has_content:
|
||||
query=session.query(Article)
|
||||
if status:
|
||||
query = query.filter(Article.status == status)
|
||||
else:
|
||||
query = query.filter(Article.status != DATA_STATUS.DELETED)
|
||||
if mp_id:
|
||||
query = query.filter(Article.mp_id == mp_id)
|
||||
if search:
|
||||
query = query.filter(
|
||||
format_search_kw(search)
|
||||
)
|
||||
|
||||
# 获取总数
|
||||
total = query.count()
|
||||
query= query.order_by(Article.publish_time.desc()).offset(offset).limit(limit)
|
||||
# query= query.order_by(Article.id.desc()).offset(offset).limit(limit)
|
||||
# 分页查询(按发布时间降序)
|
||||
articles = query.all()
|
||||
|
||||
# 打印生成的 SQL 语句(包含分页参数)
|
||||
print_warning(query.statement.compile(compile_kwargs={"literal_binds": True}))
|
||||
|
||||
# 查询公众号名称
|
||||
from core.models.feed import Feed
|
||||
mp_names = {}
|
||||
for article in articles:
|
||||
if article.mp_id and article.mp_id not in mp_names:
|
||||
feed = session.query(Feed).filter(Feed.id == article.mp_id).first()
|
||||
mp_names[article.mp_id] = feed.mp_name if feed else "未知公众号"
|
||||
|
||||
# 合并公众号名称到文章列表
|
||||
article_list = []
|
||||
for article in articles:
|
||||
article_dict = article.__dict__
|
||||
article_dict["mp_name"] = mp_names.get(article.mp_id, "未知公众号")
|
||||
article_list.append(article_dict)
|
||||
|
||||
from .base import success_response
|
||||
return success_response({
|
||||
"list": article_list,
|
||||
"total": total
|
||||
})
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取文章列表失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}", summary="获取文章详情")
|
||||
async def get_article_detail(
|
||||
article_id: str,
|
||||
content: bool = False,
|
||||
# current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
article = session.query(Article).filter(Article.id==article_id).filter(Article.status != DATA_STATUS.DELETED).first()
|
||||
if not article:
|
||||
from .base import error_response
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="文章不存在"
|
||||
)
|
||||
)
|
||||
return success_response(article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取文章详情失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.delete("/{article_id}", summary="删除文章")
|
||||
async def delete_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
from core.models.article import Article
|
||||
|
||||
# 检查文章是否存在
|
||||
article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="文章不存在"
|
||||
)
|
||||
)
|
||||
# 逻辑删除文章(更新状态为deleted)
|
||||
article.status = DATA_STATUS.DELETED
|
||||
if cfg.get("article.true_delete", False):
|
||||
session.delete(article)
|
||||
session.commit()
|
||||
|
||||
return success_response(None, message="文章已标记为删除")
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"删除文章失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}/next", summary="获取下一篇文章")
|
||||
async def get_next_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
# 获取当前文章的发布时间
|
||||
current_article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not current_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="当前文章不存在"
|
||||
)
|
||||
)
|
||||
|
||||
# 查询发布时间更晚的第一篇文章
|
||||
next_article = session.query(Article)\
|
||||
.filter(Article.publish_time > current_article.publish_time)\
|
||||
.filter(Article.status != DATA_STATUS.DELETED)\
|
||||
.filter(Article.mp_id == current_article.mp_id)\
|
||||
.order_by(Article.publish_time.asc())\
|
||||
.first()
|
||||
|
||||
if not next_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40402,
|
||||
message="没有下一篇文章"
|
||||
)
|
||||
)
|
||||
|
||||
return success_response(next_article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取下一篇文章失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}/prev", summary="获取上一篇文章")
|
||||
async def get_prev_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
# 获取当前文章的发布时间
|
||||
current_article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not current_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="当前文章不存在"
|
||||
)
|
||||
)
|
||||
|
||||
# 查询发布时间更早的第一篇文章
|
||||
prev_article = session.query(Article)\
|
||||
.filter(Article.publish_time < current_article.publish_time)\
|
||||
.filter(Article.status != DATA_STATUS.DELETED)\
|
||||
.filter(Article.mp_id == current_article.mp_id)\
|
||||
.order_by(Article.publish_time.desc())\
|
||||
.first()
|
||||
|
||||
if not prev_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40403,
|
||||
message="没有上一篇文章"
|
||||
)
|
||||
)
|
||||
|
||||
return success_response(prev_article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取上一篇文章失败: {str(e)}"
|
||||
)
|
||||
from fastapi import APIRouter, Depends, HTTPException, status as fast_status, Query
|
||||
from core.auth import get_current_user
|
||||
from core.db import DB
|
||||
from core.models.base import DATA_STATUS
|
||||
from core.models.article import Article,ArticleBase
|
||||
from sqlalchemy import and_, or_, desc
|
||||
from .base import success_response, error_response
|
||||
from core.config import cfg
|
||||
from apis.base import format_search_kw
|
||||
from core.print import print_warning, print_info, print_error, print_success
|
||||
router = APIRouter(prefix=f"/articles", tags=["文章管理"])
|
||||
|
||||
|
||||
|
||||
@router.delete("/clean", summary="清理无效文章(MP_ID不存在于Feeds表中的文章)")
|
||||
async def clean_orphan_articles(
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
from core.models.feed import Feed
|
||||
from core.models.article import Article
|
||||
|
||||
# 找出Articles表中mp_id不在Feeds表中的记录
|
||||
subquery = session.query(Feed.id).subquery()
|
||||
deleted_count = session.query(Article)\
|
||||
.filter(~Article.mp_id.in_(subquery))\
|
||||
.delete(synchronize_session=False)
|
||||
|
||||
session.commit()
|
||||
|
||||
return success_response({
|
||||
"message": "清理无效文章成功",
|
||||
"deleted_count": deleted_count
|
||||
})
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
print(f"清理无效文章错误: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_201_CREATED,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message="清理无效文章失败"
|
||||
)
|
||||
)
|
||||
|
||||
@router.put("/{article_id}/read", summary="改变文章阅读状态")
|
||||
async def toggle_article_read_status(
|
||||
article_id: str,
|
||||
is_read: bool = Query(..., description="阅读状态: true为已读, false为未读"),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
from core.models.article import Article
|
||||
|
||||
# 检查文章是否存在
|
||||
article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="文章不存在"
|
||||
)
|
||||
)
|
||||
|
||||
# 更新阅读状态
|
||||
article.is_read = 1 if is_read else 0
|
||||
session.commit()
|
||||
|
||||
return success_response({
|
||||
"message": f"文章已标记为{'已读' if is_read else '未读'}",
|
||||
"is_read": is_read
|
||||
})
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"更新文章阅读状态失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.delete("/clean_duplicate_articles", summary="清理重复文章")
|
||||
async def clean_duplicate(
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
try:
|
||||
from tools.clean import clean_duplicate_articles
|
||||
(msg, deleted_count) =clean_duplicate_articles()
|
||||
return success_response({
|
||||
"message": msg,
|
||||
"deleted_count": deleted_count
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"清理重复文章: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_201_CREATED,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message="清理重复文章"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.api_route("", summary="获取文章列表",methods= ["GET", "POST"], operation_id="get_articles_list")
|
||||
async def get_articles(
|
||||
offset: int = Query(0, ge=0),
|
||||
limit: int = Query(5, ge=1, le=100),
|
||||
status: str = Query(None),
|
||||
search: str = Query(None),
|
||||
mp_id: str = Query(None),
|
||||
has_content:bool=Query(False),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
|
||||
|
||||
# 构建查询条件
|
||||
query = session.query(ArticleBase)
|
||||
if has_content:
|
||||
query=session.query(Article)
|
||||
if status:
|
||||
query = query.filter(Article.status == status)
|
||||
else:
|
||||
query = query.filter(Article.status != DATA_STATUS.DELETED)
|
||||
if mp_id:
|
||||
query = query.filter(Article.mp_id == mp_id)
|
||||
if search:
|
||||
query = query.filter(
|
||||
format_search_kw(search)
|
||||
)
|
||||
|
||||
# 获取总数
|
||||
total = query.count()
|
||||
query= query.order_by(Article.publish_time.desc()).offset(offset).limit(limit)
|
||||
# query= query.order_by(Article.id.desc()).offset(offset).limit(limit)
|
||||
# 分页查询(按发布时间降序)
|
||||
articles = query.all()
|
||||
|
||||
# 打印生成的 SQL 语句(包含分页参数)
|
||||
print_warning(query.statement.compile(compile_kwargs={"literal_binds": True}))
|
||||
|
||||
# 查询公众号名称
|
||||
from core.models.feed import Feed
|
||||
mp_names = {}
|
||||
for article in articles:
|
||||
if article.mp_id and article.mp_id not in mp_names:
|
||||
feed = session.query(Feed).filter(Feed.id == article.mp_id).first()
|
||||
mp_names[article.mp_id] = feed.mp_name if feed else "未知公众号"
|
||||
|
||||
# 合并公众号名称到文章列表
|
||||
article_list = []
|
||||
for article in articles:
|
||||
article_dict = article.__dict__
|
||||
article_dict["mp_name"] = mp_names.get(article.mp_id, "未知公众号")
|
||||
article_list.append(article_dict)
|
||||
|
||||
from .base import success_response
|
||||
return success_response({
|
||||
"list": article_list,
|
||||
"total": total
|
||||
})
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取文章列表失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}", summary="获取文章详情")
|
||||
async def get_article_detail(
|
||||
article_id: str,
|
||||
content: bool = False,
|
||||
# current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
article = session.query(Article).filter(Article.id==article_id).filter(Article.status != DATA_STATUS.DELETED).first()
|
||||
if not article:
|
||||
from .base import error_response
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="文章不存在"
|
||||
)
|
||||
)
|
||||
return success_response(article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取文章详情失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.delete("/{article_id}", summary="删除文章")
|
||||
async def delete_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
from core.models.article import Article
|
||||
|
||||
# 检查文章是否存在
|
||||
article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="文章不存在"
|
||||
)
|
||||
)
|
||||
# 逻辑删除文章(更新状态为deleted)
|
||||
article.status = DATA_STATUS.DELETED
|
||||
if cfg.get("article.true_delete", False):
|
||||
session.delete(article)
|
||||
session.commit()
|
||||
|
||||
return success_response(None, message="文章已标记为删除")
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"删除文章失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}/next", summary="获取下一篇文章")
|
||||
async def get_next_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
# 获取当前文章的发布时间
|
||||
current_article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not current_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="当前文章不存在"
|
||||
)
|
||||
)
|
||||
|
||||
# 查询发布时间更晚的第一篇文章
|
||||
next_article = session.query(Article)\
|
||||
.filter(Article.publish_time > current_article.publish_time)\
|
||||
.filter(Article.status != DATA_STATUS.DELETED)\
|
||||
.filter(Article.mp_id == current_article.mp_id)\
|
||||
.order_by(Article.publish_time.asc())\
|
||||
.first()
|
||||
|
||||
if not next_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40402,
|
||||
message="没有下一篇文章"
|
||||
)
|
||||
)
|
||||
|
||||
return success_response(next_article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取下一篇文章失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
|
||||
@router.get("/{article_id}/prev", summary="获取上一篇文章")
|
||||
async def get_prev_article(
|
||||
article_id: str,
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
session = DB.get_session()
|
||||
try:
|
||||
# 获取当前文章的发布时间
|
||||
current_article = session.query(Article).filter(Article.id == article_id).first()
|
||||
if not current_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_404_NOT_FOUND,
|
||||
detail=error_response(
|
||||
code=40401,
|
||||
message="当前文章不存在"
|
||||
)
|
||||
)
|
||||
|
||||
# 查询发布时间更早的第一篇文章
|
||||
prev_article = session.query(Article)\
|
||||
.filter(Article.publish_time < current_article.publish_time)\
|
||||
.filter(Article.status != DATA_STATUS.DELETED)\
|
||||
.filter(Article.mp_id == current_article.mp_id)\
|
||||
.order_by(Article.publish_time.desc())\
|
||||
.first()
|
||||
|
||||
if not prev_article:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=40403,
|
||||
message="没有上一篇文章"
|
||||
)
|
||||
)
|
||||
|
||||
return success_response(prev_article)
|
||||
except HTTPException as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=fast_status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail=error_response(
|
||||
code=50001,
|
||||
message=f"获取上一篇文章失败: {str(e)}"
|
||||
)
|
||||
)
|
||||
@@ -13,5 +13,6 @@ class ArticleBase(Base):
|
||||
created_at = Column(DateTime)
|
||||
updated_at = Column(DateTime)
|
||||
is_export = Column(Integer)
|
||||
is_read = Column(Integer, default=0)
|
||||
class Article(ArticleBase):
|
||||
content = Column(Text)
|
||||
|
||||
@@ -10,6 +10,7 @@ import http from './http'
|
||||
* @property status 文章状态
|
||||
* @property link 文章链接
|
||||
* @property created_at 创建时间
|
||||
* @property is_read 阅读状态
|
||||
*/
|
||||
export interface Article {
|
||||
id: number
|
||||
@@ -20,6 +21,7 @@ export interface Article {
|
||||
status: number
|
||||
link: string
|
||||
created_at: string
|
||||
is_read?: number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,3 +133,15 @@ export const ClearDuplicateArticle = (id: number) => {
|
||||
return http.delete<{code: number, message: string}>(`/wx/articles/clean_duplicate_articles`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换文章阅读状态
|
||||
* @param id 文章ID
|
||||
* @param is_read 阅读状态
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export const toggleArticleReadStatus = (id: number, is_read: boolean) => {
|
||||
return http.put<{code: number, message: string, is_read: boolean}>(`/wx/articles/${id}/read`, null, {
|
||||
params: { is_read }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -190,8 +190,8 @@ import { Avatar } from '@/utils/constants'
|
||||
import { translatePage, setCurrentLanguage } from '@/utils/translate';
|
||||
import { ref, onMounted, h } from 'vue'
|
||||
import axios from 'axios'
|
||||
import { IconApps, IconAtt, IconDelete, IconEdit, IconEye, IconRefresh, IconScan, IconWeiboCircleFill, IconWifi, IconCode } from '@arco-design/web-vue/es/icon'
|
||||
import { getArticles, deleteArticle as deleteArticleApi, ClearArticle, ClearDuplicateArticle, getArticleDetail } from '@/api/article'
|
||||
import { IconApps, IconAtt, IconDelete, IconEdit, IconEye, IconRefresh, IconScan, IconWeiboCircleFill, IconWifi, IconCode, IconCheck, IconClose } from '@arco-design/web-vue/es/icon'
|
||||
import { getArticles, deleteArticle as deleteArticleApi, ClearArticle, ClearDuplicateArticle, getArticleDetail, toggleArticleReadStatus } from '@/api/article'
|
||||
import { ExportOPML, ExportMPS, ImportMPS } from '@/api/export'
|
||||
import ExportModal from '@/components/ExportModal.vue'
|
||||
import { getSubscriptions, UpdateMps } from '@/api/subscription'
|
||||
@@ -245,6 +245,30 @@ const statusColorMap = {
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '阅读状态',
|
||||
dataIndex: 'is_read',
|
||||
width: '100',
|
||||
render: ({ record }) => {
|
||||
const isRead = record.is_read === 1;
|
||||
return h('div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
color: isRead ? 'var(--color-success)' : 'var(--color-text-3)'
|
||||
},
|
||||
onClick: () => toggleReadStatus(record)
|
||||
}, [
|
||||
h(isRead ? IconCheck : IconClose, {
|
||||
style: { marginRight: '4px' }
|
||||
}),
|
||||
h('span', {
|
||||
style: { fontSize: '12px' }
|
||||
}, isRead ? '已读' : '未读')
|
||||
]);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '文章标题',
|
||||
dataIndex: 'title',
|
||||
@@ -254,7 +278,11 @@ const columns = [
|
||||
href: record.url || '#',
|
||||
title: record.title,
|
||||
target: '_blank',
|
||||
style: { color: 'var(--color-text-1)' }
|
||||
style: {
|
||||
color: 'var(--color-text-1)',
|
||||
textDecoration: record.is_read === 1 ? 'line-through' : 'none',
|
||||
opacity: record.is_read === 1 ? 0.7 : 1
|
||||
}
|
||||
}, record.title)
|
||||
},
|
||||
{
|
||||
@@ -540,6 +568,11 @@ const viewArticle = async (record: any,action_type: number) => {
|
||||
}
|
||||
articleModalVisible.value = true
|
||||
window.location="#topreader"
|
||||
|
||||
// 自动标记为已读(仅在查看当前文章时,不是上一篇/下一篇)
|
||||
if (action_type === 0 && record.is_read !== 1) {
|
||||
await toggleReadStatus(record)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取文章详情错误:', error)
|
||||
Message.error(error)
|
||||
@@ -732,6 +765,25 @@ const exportArticles = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
Message.success('导出成功');
|
||||
};
|
||||
|
||||
// 切换文章阅读状态
|
||||
const toggleReadStatus = async (record: any) => {
|
||||
try {
|
||||
const newReadStatus = record.is_read === 1 ? false : true;
|
||||
await toggleArticleReadStatus(record.id, newReadStatus);
|
||||
|
||||
// 更新本地数据
|
||||
const index = articles.value.findIndex(item => item.id === record.id);
|
||||
if (index !== -1) {
|
||||
articles.value[index].is_read = newReadStatus ? 1 : 0;
|
||||
}
|
||||
|
||||
Message.success(`文章已标记为${newReadStatus ? '已读' : '未读'}`);
|
||||
} catch (error) {
|
||||
console.error('更新阅读状态失败:', error);
|
||||
Message.error('更新阅读状态失败');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user