From f70992fba8e60bf1663454369482fb0ec48adfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?it=E6=B0=91=E5=B7=A5?= <1433711899@qq.com> Date: Thu, 20 Nov 2025 16:40:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(md):=20=E6=B7=BB=E5=8A=A0=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/apps/knowledge/__init__.py | 2 + backend/apps/knowledge/admin.py | 13 + backend/apps/knowledge/apps.py | 9 + backend/apps/knowledge/migrations/__init__.py | 2 + backend/apps/knowledge/models.py | 39 ++ backend/apps/knowledge/serializers.py | 82 +++ backend/apps/knowledge/urls.py | 15 + backend/apps/knowledge/views.py | 63 +++ .../__pycache__/init_rbac.cpython-312.pyc | Bin 21086 -> 22497 bytes .../rbac/management/commands/init_rbac.py | 14 +- backend/db.sqlite3 | Bin 958464 -> 1011712 bytes .../__pycache__/settings.cpython-312.pyc | Bin 6412 -> 6449 bytes .../__pycache__/urls.cpython-312.pyc | Bin 2078 -> 2160 bytes backend/django_vue_adminx/settings.py | 1 + backend/django_vue_adminx/urls.py | 1 + front-end/src/api/knowledge.js | 56 ++ .../src/views/knowledge/article/index.vue | 477 ++++++++++++++++++ 17 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 backend/apps/knowledge/__init__.py create mode 100644 backend/apps/knowledge/admin.py create mode 100644 backend/apps/knowledge/apps.py create mode 100644 backend/apps/knowledge/migrations/__init__.py create mode 100644 backend/apps/knowledge/models.py create mode 100644 backend/apps/knowledge/serializers.py create mode 100644 backend/apps/knowledge/urls.py create mode 100644 backend/apps/knowledge/views.py create mode 100644 front-end/src/api/knowledge.js create mode 100644 front-end/src/views/knowledge/article/index.vue diff --git a/backend/apps/knowledge/__init__.py b/backend/apps/knowledge/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/backend/apps/knowledge/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/backend/apps/knowledge/admin.py b/backend/apps/knowledge/admin.py new file mode 100644 index 0000000..38b5564 --- /dev/null +++ b/backend/apps/knowledge/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from .models import KnowledgeArticle + + +@admin.register(KnowledgeArticle) +class KnowledgeArticleAdmin(admin.ModelAdmin): + list_display = ['id', 'title', 'category', 'is_published', 'is_pinned', 'created_at', 'updated_at'] + list_filter = ['category', 'is_published', 'is_pinned', 'created_at'] + search_fields = ['title', 'summary', 'content', 'tags'] + readonly_fields = ['created_at', 'updated_at', 'created_by', 'updated_by'] + + diff --git a/backend/apps/knowledge/apps.py b/backend/apps/knowledge/apps.py new file mode 100644 index 0000000..a2bf5b7 --- /dev/null +++ b/backend/apps/knowledge/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class KnowledgeConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.knowledge' + verbose_name = '知识库' + + diff --git a/backend/apps/knowledge/migrations/__init__.py b/backend/apps/knowledge/migrations/__init__.py new file mode 100644 index 0000000..139597f --- /dev/null +++ b/backend/apps/knowledge/migrations/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/backend/apps/knowledge/models.py b/backend/apps/knowledge/models.py new file mode 100644 index 0000000..061ff7e --- /dev/null +++ b/backend/apps/knowledge/models.py @@ -0,0 +1,39 @@ +"""知识库模型。""" + +from django.db import models +from apps.common.models import BaseAuditModel + + +class KnowledgeArticle(BaseAuditModel): + """知识库文章。""" + + CATEGORY_CHOICES = [ + ('guide', '使用指南'), + ('faq', '常见问题'), + ('troubleshooting', '故障排查'), + ('other', '其他'), + ] + + title = models.CharField(max_length=200, verbose_name='标题') + summary = models.CharField(max_length=500, blank=True, default='', verbose_name='摘要') + content = models.TextField(blank=True, default='', verbose_name='内容') + category = models.CharField(max_length=32, choices=CATEGORY_CHOICES, default='guide', verbose_name='分类') + tags = models.CharField(max_length=255, blank=True, default='', verbose_name='标签', help_text='逗号分隔') + is_published = models.BooleanField(default=True, verbose_name='已发布') + is_pinned = models.BooleanField(default=False, verbose_name='置顶') + + class Meta: + verbose_name = '知识库文章' + verbose_name_plural = '知识库文章' + ordering = ['-is_pinned', '-is_published', '-updated_at', '-id'] + indexes = [ + models.Index(fields=['category']), + models.Index(fields=['is_published']), + models.Index(fields=['is_pinned']), + models.Index(fields=['created_at']), + ] + + def __str__(self) -> str: + return self.title + + diff --git a/backend/apps/knowledge/serializers.py b/backend/apps/knowledge/serializers.py new file mode 100644 index 0000000..fc02455 --- /dev/null +++ b/backend/apps/knowledge/serializers.py @@ -0,0 +1,82 @@ +"""知识库序列化器。""" + +from apps.common.serializers import BaseModelSerializer +from apps.rbac.serializers import OrganizationSerializer +from django.contrib.auth import get_user_model +from rest_framework import serializers + +from .models import KnowledgeArticle + +User = get_user_model() + + +class SimpleUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['id', 'username'] + + +class KnowledgeArticleListSerializer(BaseModelSerializer): + owner_organization = OrganizationSerializer(read_only=True) + created_by = SimpleUserSerializer(read_only=True) + + class Meta: + model = KnowledgeArticle + fields = [ + 'id', + 'title', + 'summary', + 'category', + 'tags', + 'is_published', + 'is_pinned', + 'created_at', + 'updated_at', + 'created_by', + 'owner_organization', + ] + + +class KnowledgeArticleDetailSerializer(BaseModelSerializer): + owner_organization = OrganizationSerializer(read_only=True) + created_by = SimpleUserSerializer(read_only=True) + updated_by = SimpleUserSerializer(read_only=True) + + class Meta: + model = KnowledgeArticle + fields = '__all__' + read_only_fields = ['created_at', 'updated_at', 'created_by', 'updated_by'] + + +class KnowledgeArticleCreateSerializer(BaseModelSerializer): + class Meta: + model = KnowledgeArticle + fields = [ + 'title', + 'summary', + 'content', + 'category', + 'tags', + 'is_published', + 'is_pinned', + 'owner_organization', + 'remark', + ] + + +class KnowledgeArticleUpdateSerializer(BaseModelSerializer): + class Meta: + model = KnowledgeArticle + fields = [ + 'title', + 'summary', + 'content', + 'category', + 'tags', + 'is_published', + 'is_pinned', + 'owner_organization', + 'remark', + ] + + diff --git a/backend/apps/knowledge/urls.py b/backend/apps/knowledge/urls.py new file mode 100644 index 0000000..e310234 --- /dev/null +++ b/backend/apps/knowledge/urls.py @@ -0,0 +1,15 @@ +"""知识库 URL 路由。""" + +from django.urls import include, path +from rest_framework.routers import DefaultRouter + +from .views import KnowledgeArticleViewSet + +router = DefaultRouter() +router.register(r'articles', KnowledgeArticleViewSet, basename='knowledge-article') + +urlpatterns = [ + path('', include(router.urls)), +] + + diff --git a/backend/apps/knowledge/views.py b/backend/apps/knowledge/views.py new file mode 100644 index 0000000..68e11ca --- /dev/null +++ b/backend/apps/knowledge/views.py @@ -0,0 +1,63 @@ +"""知识库视图集。""" + +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import filters, permissions, viewsets +from rest_framework.decorators import action +from rest_framework.response import Response + +from apps.common.data_mixins import DataScopeFilterMixin +from apps.common.mixins import AuditOwnerPopulateMixin, SoftDeleteMixin +from apps.common.viewsets import ActionSerializerMixin + +from .models import KnowledgeArticle +from .serializers import ( + KnowledgeArticleListSerializer, + KnowledgeArticleDetailSerializer, + KnowledgeArticleCreateSerializer, + KnowledgeArticleUpdateSerializer, +) + + +class KnowledgeArticleViewSet( + DataScopeFilterMixin, + AuditOwnerPopulateMixin, + SoftDeleteMixin, + ActionSerializerMixin, + viewsets.ModelViewSet, +): + """知识库文章 CRUD。""" + + queryset = ( + KnowledgeArticle.objects.select_related( + 'owner_organization', + 'created_by', + 'updated_by', + ) + .all() + .order_by('-is_pinned', '-updated_at', '-id') + ) + permission_classes = [permissions.IsAuthenticated] + + serializer_class = KnowledgeArticleDetailSerializer + list_serializer_class = KnowledgeArticleListSerializer + retrieve_serializer_class = KnowledgeArticleDetailSerializer + create_serializer_class = KnowledgeArticleCreateSerializer + update_serializer_class = KnowledgeArticleUpdateSerializer + partial_update_serializer_class = KnowledgeArticleUpdateSerializer + + filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] + filterset_fields = ['category', 'is_published', 'is_pinned', 'owner_organization'] + search_fields = ['title', 'summary', 'content', 'tags'] + ordering_fields = ['id', 'title', 'created_at', 'updated_at', 'is_pinned'] + + @action(detail=False, methods=['get']) + def categories(self, request): + """返回可选分类。""" + return Response( + [ + {'value': key, 'label': label} + for key, label in KnowledgeArticle.CATEGORY_CHOICES + ] + ) + + diff --git a/backend/apps/rbac/management/commands/__pycache__/init_rbac.cpython-312.pyc b/backend/apps/rbac/management/commands/__pycache__/init_rbac.cpython-312.pyc index eaacc210133d9bcdd5e59cab115d679e0de54f24..92665c736aac9e5babfef3f0e82835a465580d10 100644 GIT binary patch delta 4400 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 Z8_M0hnWLYVT}+6nf$x*SW>2xhOaSi08a)62 delta 48 zcmdmJ)MLbVnwOW00SI_@$Ys8h+sJo~jZtLtQ?{#&o9}aU@Un{vF*Wdg(%YOTc9;nO DU|J6& diff --git a/backend/django_vue_adminx/__pycache__/urls.cpython-312.pyc b/backend/django_vue_adminx/__pycache__/urls.cpython-312.pyc index 87b2189f185ac095399b4b7bac6af03b028bea0c..8ac8feaaac8c18389c2bf8f16f1e9e5ccfcbb49b 100644 GIT binary patch delta 190 zcmbOy@IgTRG%qg~0}#|*lFJNaXJB{?;=q6al=1n_M)hb$4JL+EmJ}%<$(AC$nh_$m znh8Rub4PIlWq49KQe-CgF>12PrgEjoP2R(($u3_BgqjMQ-!Oh) zNli&l)vppxEGQ_}g9&WD&8o-9C_9;p{ixJU7LE&?k~7UOC|g{Xw!F+@^+A%CpQ(ZS Kqr&7H>}~)HF)%g& delta 113 zcmew$Fi$}JG%qg~0}$}+kjoThXJB{?;=lkul=1n%M)hb$o^-A#ZYG9Qo>Yz$smZGt zHCd%oxl&{%-(b{am#qXsO}Wh+OkbHdE3s)XGV)CJW +

+ + + +
+ + + + 新建文章 + + + + + {{ item.label }} + + + + 已发布 + 草稿 + + +
+ + + + +
+ + +
+ + + + + + {{ item.label }} + + + + +
+ + 发布状态 + + + + 置顶 + + +
+ +
+ + +
+ + +
+
+
+ + +
+
+ + {{ getCategoryLabel(previewData.category) }} + + {{ previewData.is_published ? '已发布' : '草稿' }} + + 置顶 + +
更新时间:{{ formatDate(previewData.updated_at) }}
+
+
+
+
+
+ + + + +