vue3-admin-gpt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.env.development +14 -0
  2. package/.env.production +14 -0
  3. package/LICENSE +21 -0
  4. package/README.en.md +106 -0
  5. package/README.md +104 -0
  6. package/build-zip.cjs +53 -0
  7. package/cli.js +110 -0
  8. package/jsconfig.json +9 -0
  9. package/package.json +92 -0
  10. package/public/index.html +20 -0
  11. package/public/robots.txt +2 -0
  12. package/rspack.config.js +282 -0
  13. package/rspack.js +162 -0
  14. package/src/App.vue +9 -0
  15. package/src/api/icon.js +9 -0
  16. package/src/api/router.js +9 -0
  17. package/src/api/table.js +25 -0
  18. package/src/api/tree.js +9 -0
  19. package/src/api/user.js +34 -0
  20. package/src/assets/error_images/401.png +0 -0
  21. package/src/assets/error_images/404.png +0 -0
  22. package/src/assets/error_images/cloud.png +0 -0
  23. package/src/assets/login_images/background.jpg +0 -0
  24. package/src/assets/logo.png +0 -0
  25. package/src/assets/qr_logo/lqr_logo.png +0 -0
  26. package/src/assets/vuejs-fill.svg +4 -0
  27. package/src/components/VabPageHeader/index.vue +133 -0
  28. package/src/config/index.js +7 -0
  29. package/src/config/net.config.js +20 -0
  30. package/src/config/permission.js +136 -0
  31. package/src/config/setting.config.js +62 -0
  32. package/src/config/settings.js +6 -0
  33. package/src/config/theme.config.js +14 -0
  34. package/src/layouts/EmptyLayout.vue +3 -0
  35. package/src/layouts/components/VabAppMain/index.vue +109 -0
  36. package/src/layouts/components/VabAvatar/index.vue +255 -0
  37. package/src/layouts/components/VabBreadcrumb/index.vue +61 -0
  38. package/src/layouts/components/VabFullScreen/index.vue +61 -0
  39. package/src/layouts/components/VabLogo/index.vue +94 -0
  40. package/src/layouts/components/VabNav/index.vue +176 -0
  41. package/src/layouts/components/VabSide/components/VabMenuItem.vue +80 -0
  42. package/src/layouts/components/VabSide/components/VabSideItem.vue +100 -0
  43. package/src/layouts/components/VabSide/components/VabSubmenu.vue +56 -0
  44. package/src/layouts/components/VabSide/index.vue +123 -0
  45. package/src/layouts/components/VabTabs/index.vue +500 -0
  46. package/src/layouts/components/VabTheme/index.vue +603 -0
  47. package/src/layouts/components/VabTop/index.vue +286 -0
  48. package/src/layouts/export.js +29 -0
  49. package/src/layouts/index.vue +339 -0
  50. package/src/main.js +40 -0
  51. package/src/plugins/echarts.js +4 -0
  52. package/src/plugins/index.js +44 -0
  53. package/src/plugins/support.js +16 -0
  54. package/src/router/index.js +400 -0
  55. package/src/store/index.js +26 -0
  56. package/src/store/modules/errorLog.js +27 -0
  57. package/src/store/modules/routes.js +60 -0
  58. package/src/store/modules/settings.js +73 -0
  59. package/src/store/modules/table.js +22 -0
  60. package/src/store/modules/tabsBar.js +109 -0
  61. package/src/store/modules/user.js +131 -0
  62. package/src/styles/element-variables.scss +13 -0
  63. package/src/styles/loading.scss +345 -0
  64. package/src/styles/nav-icons.scss +52 -0
  65. package/src/styles/normalize.scss +353 -0
  66. package/src/styles/spinner/dots.css +68 -0
  67. package/src/styles/spinner/gauge.css +104 -0
  68. package/src/styles/spinner/inner-circles.css +51 -0
  69. package/src/styles/spinner/plus.css +341 -0
  70. package/src/styles/themes/default.scss +1 -0
  71. package/src/styles/transition.scss +18 -0
  72. package/src/styles/vab.scss +476 -0
  73. package/src/styles/variables.scss +69 -0
  74. package/src/utils/accessToken.js +56 -0
  75. package/src/utils/eventBus.js +8 -0
  76. package/src/utils/handleRoutes.js +100 -0
  77. package/src/utils/index.js +231 -0
  78. package/src/utils/message.js +67 -0
  79. package/src/utils/pageTitle.js +11 -0
  80. package/src/utils/password.js +43 -0
  81. package/src/utils/permission.js +19 -0
  82. package/src/utils/request.js +187 -0
  83. package/src/utils/static.js +81 -0
  84. package/src/utils/vab.js +218 -0
  85. package/src/utils/validate.js +48 -0
  86. package/src/views/401.vue +302 -0
  87. package/src/views/404.vue +302 -0
  88. package/src/views/demo/index.vue +591 -0
  89. package/src/views/index/index.vue +1489 -0
  90. package/src/views/login/index.vue +456 -0
  91. package/src/views/register/index.vue +524 -0
  92. package/src/views/vab/calendar.vue +488 -0
  93. package/src/views/vab/campaign.vue +1006 -0
  94. package/src/views/vab/chart.vue +189 -0
  95. package/src/views/vab/customer.vue +666 -0
  96. package/src/views/vab/editor.vue +84 -0
  97. package/src/views/vab/form.vue +151 -0
  98. package/src/views/vab/help.vue +390 -0
  99. package/src/views/vab/icon.vue +113 -0
  100. package/src/views/vab/knowledge.vue +820 -0
  101. package/src/views/vab/nested/menu1/menu2/menu3.vue +29 -0
  102. package/src/views/vab/nested/menu1/menu2.vue +33 -0
  103. package/src/views/vab/nested/menu1.vue +33 -0
  104. package/src/views/vab/nested.vue +97 -0
  105. package/src/views/vab/notification.vue +416 -0
  106. package/src/views/vab/order.vue +507 -0
  107. package/src/views/vab/permissions.vue +214 -0
  108. package/src/views/vab/product.vue +724 -0
  109. package/src/views/vab/project.vue +559 -0
  110. package/src/views/vab/settings.vue +319 -0
  111. package/src/views/vab/statistics.vue +431 -0
  112. package/src/views/vab/table.vue +110 -0
  113. package/src/views/vab/task.vue +613 -0
  114. package/src/views/vab/team.vue +662 -0
  115. package/src/views/vab/tree.vue +44 -0
  116. package/src/views/vab/upload.vue +180 -0
  117. package/src/views/vab/vue3Demo/index.vue +103 -0
  118. package/src/views/vab/workflow.vue +863 -0
@@ -0,0 +1,820 @@
1
+ <template>
2
+ <div class="knowledge-container">
3
+ <el-row :gutter="20">
4
+ <el-col :span="6">
5
+ <el-card shadow="never" class="category-sidebar">
6
+ <template #header>
7
+ <div class="card-header">
8
+ <span>知识分类</span>
9
+ <el-button type="primary" icon="Plus" circle size="small" @click="showAddCategoryDialog" />
10
+ </div>
11
+ </template>
12
+
13
+ <el-tree
14
+ :data="categories"
15
+ :props="categoryProps"
16
+ :expand-on-click-node="false"
17
+ :default-expanded-keys="[1]"
18
+ node-key="id"
19
+ @node-click="handleNodeClick"
20
+ >
21
+ <template #default="{ node, data }">
22
+ <div class="category-node">
23
+ <el-icon v-if="data.icon"><component :is="data.icon" /></el-icon>
24
+ <span style="margin-left: 5px">{{ node.label }}</span>
25
+ </div>
26
+ </template>
27
+ </el-tree>
28
+ </el-card>
29
+
30
+ <el-card shadow="never" style="margin-top: 20px">
31
+ <template #header>
32
+ <span>热门标签</span>
33
+ </template>
34
+
35
+ <div class="tag-cloud">
36
+ <el-tag
37
+ v-for="tag in tags"
38
+ :key="tag.id"
39
+ :type="tag.type"
40
+ effect="plain"
41
+ style="margin: 5px; cursor: pointer"
42
+ @click="filterByTag(tag)"
43
+ >
44
+ {{ tag.name }}
45
+ </el-tag>
46
+ </div>
47
+ </el-card>
48
+ </el-col>
49
+
50
+ <el-col :span="18">
51
+ <el-card shadow="never" class="content-main">
52
+ <template #header>
53
+ <div class="card-header">
54
+ <span>{{ currentCategory.name || '全部知识' }}</span>
55
+ <div class="header-actions">
56
+ <el-input
57
+ v-model="searchText"
58
+ placeholder="搜索知识..."
59
+ clearable
60
+ style="width: 200px; margin-right: 10px"
61
+ >
62
+ <template #prefix>
63
+ <el-icon><Search /></el-icon>
64
+ </template>
65
+ </el-input>
66
+ <el-button type="primary" @click="showAddKnowledgeDialog">添加知识</el-button>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <div class="knowledge-list">
72
+ <el-card
73
+ v-for="knowledge in filteredKnowledges"
74
+ :key="knowledge.id"
75
+ class="knowledge-item"
76
+ shadow="hover"
77
+ @click="viewKnowledge(knowledge)"
78
+ >
79
+ <div class="knowledge-header">
80
+ <h3>{{ knowledge.title }}</h3>
81
+ <el-tag :type="getCategoryType(knowledge.categoryId)">
82
+ {{ getCategoryName(knowledge.categoryId) }}
83
+ </el-tag>
84
+ </div>
85
+
86
+ <div class="knowledge-content">
87
+ {{ knowledge.summary }}
88
+ </div>
89
+
90
+ <div class="knowledge-meta">
91
+ <div class="meta-item">
92
+ <el-icon><User /></el-icon>
93
+ <span>{{ knowledge.author }}</span>
94
+ </div>
95
+ <div class="meta-item">
96
+ <el-icon><Calendar /></el-icon>
97
+ <span>{{ knowledge.createTime }}</span>
98
+ </div>
99
+ <div class="meta-item">
100
+ <el-icon><View /></el-icon>
101
+ <span>{{ knowledge.views }} 次浏览</span>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="knowledge-tags">
106
+ <el-tag
107
+ v-for="tag in knowledge.tags"
108
+ :key="tag"
109
+ :type="getTagType(tag)"
110
+ style="margin-right: 5px"
111
+ >
112
+ {{ tag }}
113
+ </el-tag>
114
+ </div>
115
+ </el-card>
116
+ </div>
117
+
118
+ <div class="pagination-container">
119
+ <el-pagination
120
+ v-model:current-page="currentPage"
121
+ v-model:page-size="pageSize"
122
+ :page-sizes="[10, 20, 50, 100]"
123
+ :total="totalKnowledges"
124
+ layout="total, sizes, prev, pager, next, jumper"
125
+ @size-change="handleSizeChange"
126
+ @current-change="handleCurrentChange"
127
+ />
128
+ </div>
129
+ </el-card>
130
+ </el-col>
131
+ </el-row>
132
+
133
+ <!-- 添加/编辑分类对话框 -->
134
+ <el-dialog
135
+ v-model="categoryDialogVisible"
136
+ :title="editingCategory ? '编辑分类' : '添加分类'"
137
+ width="500px"
138
+ >
139
+ <el-form
140
+ ref="categoryFormRef"
141
+ :model="categoryForm"
142
+ :rules="categoryRules"
143
+ label-width="80px"
144
+ >
145
+ <el-form-item label="分类名称" prop="name">
146
+ <el-input v-model="categoryForm.name" />
147
+ </el-form-item>
148
+
149
+ <el-form-item label="图标">
150
+ <el-select v-model="categoryForm.icon" placeholder="请选择图标" style="width: 100%">
151
+ <el-option label="文档" value="Document">
152
+ <el-icon><Document /></el-icon>
153
+ <span style="margin-left: 10px">文档</span>
154
+ </el-option>
155
+ <el-option label="指南" value="Guide">
156
+ <el-icon><Guide /></el-icon>
157
+ <span style="margin-left: 10px">指南</span>
158
+ </el-option>
159
+ <el-option label="教程" value="Reading">
160
+ <el-icon><Reading /></el-icon>
161
+ <span style="margin-left: 10px">教程</span>
162
+ </el-option>
163
+ <el-option label="FAQ" value="QuestionFilled">
164
+ <el-icon><QuestionFilled /></el-icon>
165
+ <span style="margin-left: 10px">FAQ</span>
166
+ </el-option>
167
+ </el-select>
168
+ </el-form-item>
169
+
170
+ <el-form-item label="父级分类">
171
+ <el-tree-select
172
+ v-model="categoryForm.parentId"
173
+ :data="categoryTree"
174
+ :props="{ value: 'id', label: 'name', children: 'children' }"
175
+ node-key="id"
176
+ style="width: 100%"
177
+ placeholder="请选择父级分类"
178
+ clearable
179
+ />
180
+ </el-form-item>
181
+ </el-form>
182
+
183
+ <template #footer>
184
+ <span class="dialog-footer">
185
+ <el-button @click="categoryDialogVisible = false">取消</el-button>
186
+ <el-button
187
+ type="primary"
188
+ @click="saveCategory"
189
+ >
190
+ 保存
191
+ </el-button>
192
+ </span>
193
+ </template>
194
+ </el-dialog>
195
+
196
+ <!-- 添加/编辑知识对话框 -->
197
+ <el-dialog
198
+ v-model="knowledgeDialogVisible"
199
+ :title="editingKnowledge ? '编辑知识' : '添加知识'"
200
+ width="800px"
201
+ >
202
+ <el-form
203
+ ref="knowledgeFormRef"
204
+ :model="knowledgeForm"
205
+ :rules="knowledgeRules"
206
+ label-width="100px"
207
+ >
208
+ <el-row :gutter="20">
209
+ <el-col :span="16">
210
+ <el-form-item label="标题" prop="title">
211
+ <el-input v-model="knowledgeForm.title" />
212
+ </el-form-item>
213
+ </el-col>
214
+
215
+ <el-col :span="8">
216
+ <el-form-item label="分类" prop="categoryId">
217
+ <el-select v-model="knowledgeForm.categoryId" placeholder="请选择分类" style="width: 100%">
218
+ <el-option
219
+ v-for="category in categories"
220
+ :key="category.id"
221
+ :label="category.name"
222
+ :value="category.id"
223
+ />
224
+ </el-select>
225
+ </el-form-item>
226
+ </el-col>
227
+ </el-row>
228
+
229
+ <el-form-item label="摘要" prop="summary">
230
+ <el-input
231
+ v-model="knowledgeForm.summary"
232
+ type="textarea"
233
+ :rows="3"
234
+ placeholder="请输入知识摘要"
235
+ />
236
+ </el-form-item>
237
+
238
+ <el-form-item label="内容" prop="content">
239
+ <el-input
240
+ v-model="knowledgeForm.content"
241
+ type="textarea"
242
+ :rows="10"
243
+ placeholder="请输入知识详细内容"
244
+ />
245
+ </el-form-item>
246
+
247
+ <el-form-item label="标签">
248
+ <el-select
249
+ v-model="knowledgeForm.tags"
250
+ multiple
251
+ filterable
252
+ allow-create
253
+ default-first-option
254
+ placeholder="请选择或创建标签"
255
+ style="width: 100%"
256
+ >
257
+ <el-option
258
+ v-for="tag in allTags"
259
+ :key="tag"
260
+ :label="tag"
261
+ :value="tag"
262
+ />
263
+ </el-select>
264
+ </el-form-item>
265
+
266
+ <el-form-item label="作者">
267
+ <el-input v-model="knowledgeForm.author" disabled />
268
+ </el-form-item>
269
+ </el-form>
270
+
271
+ <template #footer>
272
+ <span class="dialog-footer">
273
+ <el-button @click="knowledgeDialogVisible = false">取消</el-button>
274
+ <el-button
275
+ type="primary"
276
+ @click="saveKnowledge"
277
+ >
278
+ 保存
279
+ </el-button>
280
+ </span>
281
+ </template>
282
+ </el-dialog>
283
+
284
+ <!-- 知识详情对话框 -->
285
+ <el-dialog
286
+ v-model="detailDialogVisible"
287
+ :title="detailKnowledge.title"
288
+ width="800px"
289
+ >
290
+ <div class="knowledge-detail">
291
+ <div class="detail-meta">
292
+ <el-tag :type="getCategoryType(detailKnowledge.categoryId)">
293
+ {{ getCategoryName(detailKnowledge.categoryId) }}
294
+ </el-tag>
295
+ <span class="meta-item">
296
+ <el-icon><User /></el-icon>
297
+ {{ detailKnowledge.author }}
298
+ </span>
299
+ <span class="meta-item">
300
+ <el-icon><Calendar /></el-icon>
301
+ {{ detailKnowledge.createTime }}
302
+ </span>
303
+ <span class="meta-item">
304
+ <el-icon><View /></el-icon>
305
+ {{ detailKnowledge.views }} 次浏览
306
+ </span>
307
+ </div>
308
+
309
+ <div class="detail-content">
310
+ {{ detailKnowledge.content }}
311
+ </div>
312
+
313
+ <div class="detail-tags" v-if="detailKnowledge.tags && detailKnowledge.tags.length > 0">
314
+ <el-tag
315
+ v-for="tag in detailKnowledge.tags"
316
+ :key="tag"
317
+ :type="getTagType(tag)"
318
+ style="margin-right: 10px"
319
+ >
320
+ {{ tag }}
321
+ </el-tag>
322
+ </div>
323
+ </div>
324
+
325
+ <template #footer>
326
+ <span class="dialog-footer">
327
+ <el-button @click="detailDialogVisible = false">关闭</el-button>
328
+ <el-button type="primary" @click="editKnowledge(detailKnowledge)">编辑</el-button>
329
+ </span>
330
+ </template>
331
+ </el-dialog>
332
+ </div>
333
+ </template>
334
+
335
+ <script>
336
+ import {
337
+ Search,
338
+ User,
339
+ Calendar,
340
+ View,
341
+ Document,
342
+ Guide,
343
+ Reading,
344
+ QuestionFilled,
345
+ Plus
346
+ } from "@element-plus/icons-vue";
347
+
348
+ export default {
349
+ name: "Knowledge",
350
+ components: {
351
+ Search,
352
+ User,
353
+ Calendar,
354
+ View,
355
+ Document,
356
+ Guide,
357
+ Reading,
358
+ QuestionFilled,
359
+ Plus
360
+ },
361
+ data() {
362
+ return {
363
+ searchText: "",
364
+ currentPage: 1,
365
+ pageSize: 10,
366
+ totalKnowledges: 0,
367
+ categoryDialogVisible: false,
368
+ knowledgeDialogVisible: false,
369
+ detailDialogVisible: false,
370
+ editingCategory: null,
371
+ editingKnowledge: null,
372
+ currentCategory: {},
373
+ categories: [
374
+ {
375
+ id: 1,
376
+ name: "技术文档",
377
+ icon: "Document",
378
+ children: [
379
+ {
380
+ id: 2,
381
+ name: "前端开发",
382
+ icon: "Document"
383
+ },
384
+ {
385
+ id: 3,
386
+ name: "后端开发",
387
+ icon: "Document"
388
+ }
389
+ ]
390
+ },
391
+ {
392
+ id: 4,
393
+ name: "使用指南",
394
+ icon: "Guide",
395
+ children: [
396
+ {
397
+ id: 5,
398
+ name: "系统安装",
399
+ icon: "Guide"
400
+ },
401
+ {
402
+ id: 6,
403
+ name: "功能说明",
404
+ icon: "Guide"
405
+ }
406
+ ]
407
+ },
408
+ {
409
+ id: 7,
410
+ name: "教程视频",
411
+ icon: "Reading"
412
+ },
413
+ {
414
+ id: 8,
415
+ name: "常见问题",
416
+ icon: "QuestionFilled"
417
+ }
418
+ ],
419
+ categoryProps: {
420
+ children: "children",
421
+ label: "name"
422
+ },
423
+ categoryForm: {
424
+ name: "",
425
+ icon: "",
426
+ parentId: null
427
+ },
428
+ categoryRules: {
429
+ name: [
430
+ { required: true, message: "请输入分类名称", trigger: "blur" }
431
+ ]
432
+ },
433
+ knowledges: [
434
+ {
435
+ id: 1,
436
+ title: "Vue 3 Composition API 使用指南",
437
+ summary: "详细介绍Vue 3中Composition API的使用方法和最佳实践",
438
+ content: "Vue 3 Composition API 是Vue 3中引入的一种新的组织和复用组件逻辑的方式。它提供了一种更灵活的方式来组织组件代码,使得组件更容易理解和维护。",
439
+ categoryId: 2,
440
+ author: "张三",
441
+ createTime: "2023-05-01",
442
+ views: 128,
443
+ tags: ["Vue", "前端", "教程"]
444
+ },
445
+ {
446
+ id: 2,
447
+ title: "Element Plus 表单验证详解",
448
+ summary: "深入讲解Element Plus中表单验证的各种用法和技巧",
449
+ content: "Element Plus 提供了强大的表单验证功能,支持多种验证规则和自定义验证器。通过合理使用表单验证,可以有效提升用户体验和数据质量。",
450
+ categoryId: 2,
451
+ author: "李四",
452
+ createTime: "2023-05-05",
453
+ views: 96,
454
+ tags: ["Element Plus", "表单", "验证"]
455
+ },
456
+ {
457
+ id: 3,
458
+ title: "数据库设计规范",
459
+ summary: "介绍数据库设计的基本原则和规范,帮助提高数据库性能和可维护性",
460
+ content: "良好的数据库设计是系统稳定运行的基础。本指南介绍了数据库设计的基本原则,包括范式设计、索引优化、表结构设计等方面的内容。",
461
+ categoryId: 3,
462
+ author: "王五",
463
+ createTime: "2023-05-10",
464
+ views: 75,
465
+ tags: ["数据库", "设计", "规范"]
466
+ },
467
+ {
468
+ id: 4,
469
+ title: "系统安装与部署",
470
+ summary: "详细说明系统的安装和部署步骤,包括环境要求和配置说明",
471
+ content: "系统安装需要满足一定的环境要求,包括操作系统、数据库、Web服务器等。本指南详细介绍了在不同环境下的安装和部署步骤。",
472
+ categoryId: 5,
473
+ author: "赵六",
474
+ createTime: "2023-04-28",
475
+ views: 210,
476
+ tags: ["安装", "部署", "环境"]
477
+ },
478
+ {
479
+ id: 5,
480
+ title: "常见问题解答",
481
+ summary: "整理了用户在使用过程中遇到的常见问题及其解决方案",
482
+ content: "本FAQ整理了用户在使用系统过程中经常遇到的问题,并提供了详细的解决方案。通过查阅本FAQ,可以快速解决大部分常见问题。",
483
+ categoryId: 8,
484
+ author: "钱七",
485
+ createTime: "2023-05-12",
486
+ views: 185,
487
+ tags: ["FAQ", "问题", "解答"]
488
+ }
489
+ ],
490
+ knowledgeForm: {
491
+ title: "",
492
+ summary: "",
493
+ content: "",
494
+ categoryId: null,
495
+ author: "当前用户",
496
+ tags: []
497
+ },
498
+ detailKnowledge: {},
499
+ knowledgeRules: {
500
+ title: [
501
+ { required: true, message: "请输入标题", trigger: "blur" }
502
+ ],
503
+ summary: [
504
+ { required: true, message: "请输入摘要", trigger: "blur" }
505
+ ],
506
+ content: [
507
+ { required: true, message: "请输入内容", trigger: "blur" }
508
+ ],
509
+ categoryId: [
510
+ { required: true, message: "请选择分类", trigger: "change" }
511
+ ]
512
+ },
513
+ tags: [
514
+ { id: 1, name: "Vue", type: "primary" },
515
+ { id: 2, name: "Element Plus", type: "success" },
516
+ { id: 3, name: "数据库", type: "warning" },
517
+ { id: 4, name: "教程", type: "danger" },
518
+ { id: 5, name: "前端", type: "info" },
519
+ { id: 6, name: "后端", type: "" },
520
+ { id: 7, name: "安装", type: "primary" },
521
+ { id: 8, name: "FAQ", type: "success" }
522
+ ],
523
+ allTags: ["Vue", "Element Plus", "数据库", "教程", "前端", "后端", "安装", "FAQ", "表单", "验证", "设计", "规范", "环境", "问题", "解答"]
524
+ };
525
+ },
526
+ computed: {
527
+ categoryTree() {
528
+ return this.categories.map(category => ({
529
+ id: category.id,
530
+ name: category.name,
531
+ children: category.children ? category.children.map(child => ({
532
+ id: child.id,
533
+ name: child.name
534
+ })) : undefined
535
+ }));
536
+ },
537
+ filteredKnowledges() {
538
+ let result = this.knowledges;
539
+
540
+ // 分类过滤
541
+ if (this.currentCategory.id) {
542
+ result = result.filter(knowledge =>
543
+ knowledge.categoryId === this.currentCategory.id ||
544
+ this.isChildCategory(knowledge.categoryId, this.currentCategory.id)
545
+ );
546
+ }
547
+
548
+ // 搜索过滤
549
+ if (this.searchText) {
550
+ result = result.filter(knowledge =>
551
+ knowledge.title.toLowerCase().includes(this.searchText.toLowerCase()) ||
552
+ knowledge.summary.toLowerCase().includes(this.searchText.toLowerCase()) ||
553
+ knowledge.content.toLowerCase().includes(this.searchText.toLowerCase())
554
+ );
555
+ }
556
+
557
+ // 分页处理
558
+ const start = (this.currentPage - 1) * this.pageSize;
559
+ const end = start + this.pageSize;
560
+ return result.slice(start, end);
561
+ }
562
+ },
563
+ methods: {
564
+ handleNodeClick(data) {
565
+ this.currentCategory = data;
566
+ this.currentPage = 1;
567
+ },
568
+ handleSizeChange(val) {
569
+ this.pageSize = val;
570
+ this.currentPage = 1;
571
+ },
572
+ handleCurrentChange(val) {
573
+ this.currentPage = val;
574
+ },
575
+ getCategoryName(categoryId) {
576
+ for (const category of this.categories) {
577
+ if (category.id === categoryId) {
578
+ return category.name;
579
+ }
580
+ if (category.children) {
581
+ for (const child of category.children) {
582
+ if (child.id === categoryId) {
583
+ return child.name;
584
+ }
585
+ }
586
+ }
587
+ }
588
+ return "未知分类";
589
+ },
590
+ getCategoryType(categoryId) {
591
+ const typeMap = {
592
+ 1: "primary",
593
+ 2: "success",
594
+ 3: "warning",
595
+ 4: "danger",
596
+ 5: "info",
597
+ 6: "",
598
+ 7: "primary",
599
+ 8: "success"
600
+ };
601
+ return typeMap[categoryId] || "info";
602
+ },
603
+ getTagType(tag) {
604
+ const tagObj = this.tags.find(t => t.name === tag);
605
+ return tagObj ? tagObj.type : "info";
606
+ },
607
+ isChildCategory(childId, parentId) {
608
+ const parent = this.categories.find(c => c.id === parentId);
609
+ if (parent && parent.children) {
610
+ return parent.children.some(child => child.id === childId);
611
+ }
612
+ return false;
613
+ },
614
+ showAddCategoryDialog() {
615
+ this.editingCategory = null;
616
+ this.categoryForm = {
617
+ name: "",
618
+ icon: "",
619
+ parentId: null
620
+ };
621
+ this.categoryDialogVisible = true;
622
+ this.$nextTick(() => {
623
+ this.$refs.categoryFormRef.resetFields();
624
+ });
625
+ },
626
+ showAddKnowledgeDialog() {
627
+ this.editingKnowledge = null;
628
+ this.knowledgeForm = {
629
+ title: "",
630
+ summary: "",
631
+ content: "",
632
+ categoryId: null,
633
+ author: "当前用户",
634
+ tags: []
635
+ };
636
+ this.knowledgeDialogVisible = true;
637
+ this.$nextTick(() => {
638
+ this.$refs.knowledgeFormRef.resetFields();
639
+ });
640
+ },
641
+ editKnowledge(knowledge) {
642
+ this.editingKnowledge = knowledge;
643
+ this.knowledgeForm = { ...knowledge };
644
+ this.knowledgeDialogVisible = true;
645
+ this.detailDialogVisible = false;
646
+ },
647
+ viewKnowledge(knowledge) {
648
+ this.detailKnowledge = { ...knowledge };
649
+ // 增加浏览量
650
+ this.detailKnowledge.views = (this.detailKnowledge.views || 0) + 1;
651
+ this.detailDialogVisible = true;
652
+ },
653
+ filterByTag(tag) {
654
+ this.searchText = tag.name;
655
+ },
656
+ saveCategory() {
657
+ this.$refs.categoryFormRef.validate((valid) => {
658
+ if (valid) {
659
+ if (this.editingCategory) {
660
+ // 编辑分类
661
+ this.$message.success("分类更新成功");
662
+ } else {
663
+ // 添加分类
664
+ this.$message.success("分类添加成功");
665
+ }
666
+ this.categoryDialogVisible = false;
667
+ }
668
+ });
669
+ },
670
+ saveKnowledge() {
671
+ this.$refs.knowledgeFormRef.validate((valid) => {
672
+ if (valid) {
673
+ if (this.editingKnowledge) {
674
+ // 编辑知识
675
+ const index = this.knowledges.findIndex(k => k.id === this.editingKnowledge.id);
676
+ if (index !== -1) {
677
+ this.knowledges[index] = { ...this.editingKnowledge, ...this.knowledgeForm };
678
+ this.$message.success("知识更新成功");
679
+ }
680
+ } else {
681
+ // 添加知识
682
+ const newKnowledge = {
683
+ id: Date.now(),
684
+ createTime: new Date().toISOString().split('T')[0],
685
+ views: 0,
686
+ ...this.knowledgeForm
687
+ };
688
+ this.knowledges.push(newKnowledge);
689
+ this.totalKnowledges = this.knowledges.length;
690
+ this.$message.success("知识添加成功");
691
+ }
692
+ this.knowledgeDialogVisible = false;
693
+ }
694
+ });
695
+ }
696
+ }
697
+ };
698
+ </script>
699
+
700
+ <style lang="scss" scoped>
701
+ .knowledge-container {
702
+ padding: 20px;
703
+
704
+ .card-header {
705
+ display: flex;
706
+ justify-content: space-between;
707
+ align-items: center;
708
+ font-weight: bold;
709
+ }
710
+
711
+ .category-sidebar {
712
+ height: calc(100vh - 120px);
713
+
714
+ :deep(.el-tree) {
715
+ background-color: transparent;
716
+ border: 0;
717
+ }
718
+
719
+ .category-node {
720
+ display: flex;
721
+ align-items: center;
722
+ }
723
+ }
724
+
725
+ .content-main {
726
+ height: calc(100vh - 120px);
727
+ display: flex;
728
+ flex-direction: column;
729
+
730
+ .knowledge-list {
731
+ flex: 1;
732
+ overflow-y: auto;
733
+
734
+ .knowledge-item {
735
+ margin-bottom: 15px;
736
+ cursor: pointer;
737
+
738
+ .knowledge-header {
739
+ display: flex;
740
+ justify-content: space-between;
741
+ align-items: center;
742
+ margin-bottom: 10px;
743
+
744
+ h3 {
745
+ margin: 0;
746
+ font-size: 16px;
747
+ }
748
+ }
749
+
750
+ .knowledge-content {
751
+ color: #666;
752
+ margin-bottom: 15px;
753
+ line-height: 1.5;
754
+ }
755
+
756
+ .knowledge-meta {
757
+ display: flex;
758
+ margin-bottom: 10px;
759
+
760
+ .meta-item {
761
+ display: flex;
762
+ align-items: center;
763
+ margin-right: 15px;
764
+ font-size: 12px;
765
+ color: #999;
766
+
767
+ .el-icon {
768
+ margin-right: 3px;
769
+ }
770
+ }
771
+ }
772
+
773
+ .knowledge-tags {
774
+ margin-top: 10px;
775
+ }
776
+ }
777
+ }
778
+ }
779
+
780
+ .tag-cloud {
781
+ text-align: center;
782
+ }
783
+
784
+ .knowledge-detail {
785
+ .detail-meta {
786
+ display: flex;
787
+ align-items: center;
788
+ margin-bottom: 20px;
789
+ padding-bottom: 15px;
790
+ border-bottom: 1px solid #ebeef5;
791
+
792
+ .el-tag {
793
+ margin-right: 15px;
794
+ }
795
+
796
+ .meta-item {
797
+ display: flex;
798
+ align-items: center;
799
+ margin-right: 20px;
800
+ color: #999;
801
+ font-size: 14px;
802
+
803
+ .el-icon {
804
+ margin-right: 5px;
805
+ }
806
+ }
807
+ }
808
+
809
+ .detail-content {
810
+ line-height: 1.8;
811
+ margin-bottom: 20px;
812
+ }
813
+ }
814
+
815
+ .pagination-container {
816
+ margin-top: 20px;
817
+ text-align: right;
818
+ }
819
+ }
820
+ </style>