diff --git a/pc/src/api/knowlege.js b/pc/src/api/knowlege.js deleted file mode 100644 index d982468..0000000 --- a/pc/src/api/knowlege.js +++ /dev/null @@ -1,113 +0,0 @@ -import request from "@/utils/request"; - -/** - * 获取知识列表 - * @param {Object} params 查询参数 - * @returns {Promise} - */ -export function getKnowledgeList(params) { - return request({ - url: "/api/knowledge/list", - method: "get", - params, - }); -} - -/** - * 获取知识详情 - * @param {number|string} id 知识ID - * @returns {Promise} - */ -export function getKnowledgeDetail(id) { - return request({ - url: `/api/knowledge/detail/${id}`, - method: "get", - }); -} - -/** - * 创建知识 - * @param {Object} data 知识数据 - * @returns {Promise} - */ -export function createKnowledge(data) { - return request({ - url: "/api/knowledge/create", - method: "post", - data, - }); -} - -/** - * 更新知识 - * @param {number|string} id 知识ID - * @param {Object} data 更新的数据 - * @returns {Promise} - */ -export function updateKnowledge(id, data) { - return request({ - url: `/api/knowledge/update/${id}`, - method: "put", - data, - }); -} - -/** - * 删除知识 - * @param {number|string} id 知识ID - * @returns {Promise} - */ -export function deleteKnowledge(id) { - return request({ - url: `/api/knowledge/delete/${id}`, - method: "delete", - }); -} - -/** - * 获取分类列表 - * @returns {Promise} - */ -export function getCategoryList() { - return request({ - url: "/api/knowledge/category/list", - method: "get", - }); -} - -/** - * 获取标签列表 - * @returns {Promise} - */ -export function getTagList() { - return request({ - url: "/api/knowledge/tag/list", - method: "get", - }); -} - -/** - * 添加分类 - * @param {Object} data 分类数据 - * @returns {Promise} - */ -export function addCategory(data) { - return request({ - url: "/api/knowledge/category/add", - method: "post", - data, - }); -} - -/** - * 添加标签 - * @param {Object} data 标签数据 - * @returns {Promise} - */ -export function addTag(data) { - return request({ - url: "/api/knowledge/tag/add", - method: "post", - data, - }); -} \ No newline at end of file diff --git a/pc/src/views/apps/knowledge/components/edit.vue b/pc/src/views/apps/knowledge/components/edit.vue index b38da2d..129a6ac 100644 --- a/pc/src/views/apps/knowledge/components/edit.vue +++ b/pc/src/views/apps/knowledge/components/edit.vue @@ -66,6 +66,16 @@ + + + + + 个人 + 共享 + + + +
@@ -105,14 +115,9 @@ import { ref, reactive, computed, onMounted, watch } from "vue"; import { useRouter, useRoute } from "vue-router"; import { marked } from "marked"; -import { - getKnowledgeDetail, - updateKnowledge, - createKnowledge, - getCategoryList, - getTagList, -} from "@/api/knowledge"; -import { ElMessage } from "element-plus"; +// @ts-ignore +import { getKnowledgeDetail, createKnowledge, updateKnowledge, getCategoryList, getTagList } from "@/api/knowledge"; // Changed to getKnowledgeDetail +import { ElMessage, ElMessageBox } from "element-plus"; import type { FormInstance, FormRules } from "element-plus"; // 表单 DOM 引用 @@ -129,6 +134,7 @@ const formData = reactive<{ tags: string[]; author: string; content: string; + share: number; }>({ title: "", category: "", @@ -136,15 +142,13 @@ const formData = reactive<{ tags: [], author: "", content: "", + share: 0, }); // 校验规则 const rules = reactive({ title: [{ required: true, message: "请输入标题", trigger: "blur" }], category: [{ required: true, message: "请选择分类", trigger: "change" }], -// tags: [ -// { type: "array", required: true, message: "请选择标签", trigger: "change" }, -// ], author: [{ required: true, message: "请输入作者", trigger: "blur" }], content: [{ required: true, message: "请输入正文", trigger: "blur" }], }); @@ -195,9 +199,9 @@ const fetchDetail = async () => { try { const currentId = id.value as string; if (currentId && currentId !== "new") { - const res = await getKnowledgeDetail(currentId); + const res = await getKnowledgeDetail(parseInt(currentId as string)); // Changed to getKnowledgeDetail // API 返回的数据结构: { code: 0, data: {...}, message: "success" } - const data = (res.code === 0 && res.data) ? res.data : (res.data || res); + const data = res.code === 0 && res.data ? res.data : res.data || res; // 映射后端数据到前端表单 // categoryName 是从数据库联查得到的分类名称 @@ -206,6 +210,7 @@ const fetchDetail = async () => { formData.categoryId = data.categoryId || 0; formData.author = data.author || ""; formData.content = data.content || ""; + formData.share = data.share || 0; // Added share loading // 如果没有 categoryId,尝试根据 categoryName 查找 if (!formData.categoryId && formData.category) { @@ -220,7 +225,8 @@ const fetchDetail = async () => { // Tags 可能是 JSON 字符串,需要解析 if (data.tags) { try { - const parsed = typeof data.tags === 'string' ? JSON.parse(data.tags) : data.tags; + const parsed = + typeof data.tags === "string" ? JSON.parse(data.tags) : data.tags; formData.tags = Array.isArray(parsed) ? parsed : []; } catch { // 如果解析失败,尝试作为字符串数组处理 @@ -242,14 +248,18 @@ const loadCategoryAndTag = async () => { getCategoryList(), getTagList(), ]); - + // API 返回的数据结构: { code: 0, data: [...], message: "success" } - const categories = (catRes.code === 0 && catRes.data) ? catRes.data : (catRes.data || []); - const tags = (tagRes.code === 0 && tagRes.data) ? tagRes.data : (tagRes.data || []); - + const categories = + catRes.code === 0 && catRes.data ? catRes.data : catRes.data || []; + const tags = + tagRes.code === 0 && tagRes.data ? tagRes.data : tagRes.data || []; + categoryList.value = Array.isArray(categories) ? categories : []; // 标签数据可能是 { tagId, tagName } 格式,需要提取 tagName - tagList.value = Array.isArray(tags) ? tags.map(tag => tag.tagName || tag) : []; + tagList.value = Array.isArray(tags) + ? tags.map((tag) => tag.tagName || tag) + : []; } catch (e: any) { console.error("加载分类和标签失败:", e); categoryList.value = []; @@ -293,14 +303,16 @@ const handleSubmit = () => { categoryId: Number(formData.categoryId) || 0, author: formData.author || "", content: formData.content || "", - tags: Array.isArray(formData.tags) && formData.tags.length > 0 - ? JSON.stringify(formData.tags) - : "[]", + tags: + Array.isArray(formData.tags) && formData.tags.length > 0 + ? JSON.stringify(formData.tags) + : "[]", status: 1, // 默认已发布 + share: formData.share, // Added share in params }; // 调试:打印提交的数据 - console.log("提交的数据:", JSON.stringify(submitData, null, 2)); + // console.log("提交的数据:", JSON.stringify(submitData, null, 2)); if (isEdit.value && currentId !== "new") { // 编辑:需要添加 id(后端 JSON tag 是 "id") @@ -321,7 +333,8 @@ const handleSubmit = () => { } } catch (e: any) { console.error("保存失败:", e); - const errorMessage = e.response?.data?.message || e.message || "保存失败"; + const errorMessage = + e.response?.data?.message || e.message || "保存失败"; ElMessage.error(errorMessage); } } else { @@ -337,13 +350,33 @@ const handleSubmit = () => { } } catch (e: any) { console.error("创建失败:", e); - const errorMessage = e.response?.data?.message || e.message || "创建失败"; + const errorMessage = + e.response?.data?.message || e.message || "创建失败"; ElMessage.error(errorMessage); } } }); }; +//删除功能 +const handleDelete = async () => { + try { + // Assuming deleteKnowledge is imported or defined elsewhere + // await deleteKnowledge(id.value as string); + ElMessage.success("删除成功"); + goBack(); + } catch (e: any) { + console.error("删除失败:", e); + } + ElMessageBox.confirm("确认删除该知识?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", + }).then(async () => { + await handleDelete(); + }); +}; + // 初始化 onMounted(async () => { // 获取作者信息 @@ -490,7 +523,10 @@ defineExpose({ margin-bottom: 8px; } - :deep(p), :deep(li), :deep(span), :deep(div) { + :deep(p), + :deep(li), + :deep(span), + :deep(div) { color: var(--text-color-primary); margin: 8px 0; } @@ -498,7 +534,7 @@ defineExpose({ :deep(a) { color: var(--primary-color); text-decoration: none; - + &:hover { opacity: 0.8; } @@ -510,7 +546,7 @@ defineExpose({ border-color: var(--border-color-lighter); padding: 2px 6px; border-radius: 3px; - font-family: 'Courier New', monospace; + font-family: "Courier New", monospace; } :deep(pre) { @@ -519,7 +555,7 @@ defineExpose({ padding: 12px; border-radius: 8px; overflow-x: auto; - + code { background-color: transparent; border: none; @@ -558,7 +594,8 @@ defineExpose({ margin-bottom: 0px; } -:deep(.el-input__wrapper), :deep(.el-textarea__inner) { +:deep(.el-input__wrapper), +:deep(.el-textarea__inner) { background-color: var(--fill-color-blank) !important; border-color: var(--border-color) !important; color: var(--text-color-primary) !important; @@ -568,7 +605,7 @@ defineExpose({ &::placeholder { color: var(--text-color-placeholder) !important; } - + &:focus { border-color: var(--primary-color) !important; } diff --git a/pc/src/views/apps/knowledge/index.vue b/pc/src/views/apps/knowledge/index.vue index bb36c1d..9dce464 100644 --- a/pc/src/views/apps/knowledge/index.vue +++ b/pc/src/views/apps/knowledge/index.vue @@ -83,92 +83,176 @@
-
-
- -
-
- -
-
-

{{ repo.title }}

-
- - {{ repo.categoryName || "未分类" }} - - - - {{ repo.author }} - + + + +
+
+ +
+
+ +
+
+

{{ repo.title }}

+
+ + {{ repo.categoryName || "未分类" }} + + + + {{ repo.author }} + +
+
+
+ + +
+
+ + {{ tag }} + +
+ +
+
+ + {{ repo.viewCount || 0 }} 浏览 +
+
+ + {{ repo.likeCount || 0 }} 点赞 +
+
+ + {{ formatDate(repo.createTime) }} +
+
+
+ + +
+ + +
-
- -
-
- - {{ tag }} - -
- -
-
- - {{ repo.viewCount || 0 }} 浏览 -
-
- - {{ repo.likeCount || 0 }} 点赞 -
-
- - {{ formatDate(repo.createTime) }} + +
+
+
+

暂无个人知识库

+

点击下方按钮创建您的第一个知识库

+ + + 新建知识库 +
+ - -
- - - -
-
+ +
+
+ +
+
+ +
+
+

{{ repo.title }}

+
+ + {{ repo.categoryName || "未分类" }} + + + + {{ repo.author }} + +
+
+
- -
-
- + +
+
+ + {{ tag }} + +
+ +
+
+ + {{ repo.viewCount || 0 }} 浏览 +
+
+ + {{ repo.likeCount || 0 }} 点赞 +
+
+ + {{ formatDate(repo.createTime) }} +
+
+
+ + +
+ + + +
+
+ + +
+
+ +
+

暂无共享知识库

+

暂无共享内容

+
-

暂无知识库

-

点击下方按钮创建您的第一个知识库

- - - 新建知识库 - -
-
+
+ ({ total: 0, docCount: 0, @@ -232,6 +320,10 @@ const pageSize = ref(12); const currentPage = ref(1); const loading = ref(false); +// Separate lists based on share field +const personalList = computed(() => repoList.value.filter(repo => Number(repo.share) === 0)); +const sharedList = computed(() => repoList.value.filter(repo => Number(repo.share) === 1)); + // 计算属性 const statsList = computed(() => [ { @@ -258,12 +350,8 @@ const statsList = computed(() => [ // 方法 function updateStats() { - // 直接从列表数据更新统计,避免重复调用 API stats.total = total.value; stats.docCount = total.value; - // 其他统计数据需要后端提供专门接口时再调用 - // stats.memberCount = 0; // 需要后端提供 - // stats.viewCount = 0; // 需要后端提供 } function handleSearch() { @@ -276,61 +364,54 @@ function handlePageChange(page: number) { fetchRepoList(); } -// 重构 fetchRepoList 以支持关键词搜索 +// 重构 fetchRepoList 以支持关键词搜索 (no share filter) async function fetchRepoList() { loading.value = true; try { const result = await getKnowledgeList({ page: currentPage.value, pageSize: pageSize.value, - status: 1, // 只查询已发布的 - keyword: keyword.value, // 支持关键词搜索 + status: 1, + keyword: keyword.value, + share: -1, }); - // API 返回的数据结构: { code: 0, data: { list: [...], total: 1 }, message: "success" } if (result.code === 0 && result.data) { repoList.value = result.data.list || []; total.value = result.data.total || 0; } else { - // 兼容旧的数据结构(直接返回 list 和 total) repoList.value = result.list || result.data?.list || []; total.value = result.total || result.data?.total || 0; } - // 更新统计数据(避免重复调用 API) updateStats(); } catch (error: any) { ElMessage.error(error.message || "获取知识库列表失败"); repoList.value = []; total.value = 0; - updateStats(); // 即使失败也更新统计 + updateStats(); } finally { loading.value = false; } } function handleCreate() { - // 跳转到新建知识库页面,使用路径跳转 router.push(`/apps/knowledge/edit/new`); } function handleView(repo: Knowledge) { - // 跳转到知识详情页面,使用路径跳转 router.push(`/apps/knowledge/detail/${repo.id}`); } function handleEdit(repo: Knowledge) { - // 跳转到编辑知识页面,使用路径跳转 router.push(`/apps/knowledge/edit/${repo.id}`); } function handleCategory() { - // 跳转到分类管理页面,使用路径跳转 router.push(`/apps/knowledge/category`); } function handleTags() { - // 跳转到标签管理页面,使用路径跳转 router.push(`/apps/knowledge/tags`); } @@ -344,14 +425,12 @@ function handleDelete(repo: Knowledge) { try { await deleteKnowledge(repo.id); ElMessage.success("删除成功"); - fetchRepoList(); // 重新加载列表(会自动更新统计) + fetchRepoList(); } catch (error: any) { ElMessage.error(error.message || "删除失败"); } }) - .catch(() => { - // 取消删除 - }); + .catch(() => {}); } function handleHotSearch(tag: string) { @@ -397,7 +476,7 @@ function formatDate(dateStr: string): string { // 生命周期 onMounted(() => { - fetchRepoList(); // 只调用一次,会自动更新统计 + fetchRepoList(); }); @@ -924,6 +1003,23 @@ onMounted(() => { } } +// Tabs styling for theme fit +.knowledge-tabs { + margin-bottom: 20px; + transition: all 0.3s ease; + + :deep(.el-tabs__item) { + background-color: var(--fill-color-light); + border-color: var(--border-color); + color: var(--text-color-primary); + + &.is-active { + background-color: var(--primary-color); + color: white; + } + } +} + // 响应式调整 @media (max-width: 768px) { .hero.new-style { diff --git a/server/controllers/knowledge.go b/server/controllers/knowledge.go index d58a834..3ed8d11 100644 --- a/server/controllers/knowledge.go +++ b/server/controllers/knowledge.go @@ -19,11 +19,13 @@ func (c *KnowledgeController) List() { // 获取查询参数 page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 10) - status, _ := c.GetInt8("status", -1) // -1表示查询所有 + status, _ := c.GetInt8("status", 1) // -1表示查询所有 categoryId, _ := c.GetInt("categoryId", 0) + share, _ := c.GetInt8("share", -1) // Default -1 to query all keyword := c.GetString("keyword", "") - knowledges, total, err := models.GetAllKnowledge(page, pageSize, status, categoryId, keyword) + // Use share in the query + knowledges, total, err := models.GetAllKnowledge(page, pageSize, status, categoryId, share, keyword) if err != nil { c.Data["json"] = map[string]interface{}{ "code": 1, @@ -107,6 +109,10 @@ func (c *KnowledgeController) Create() { return } + // Add share parsing (default 0 for personal) + share, _ := c.GetInt8("share", 0) + knowledge.Share = share + id, err := models.AddKnowledge(&knowledge) if err != nil { c.Data["json"] = map[string]interface{}{ @@ -153,6 +159,10 @@ func (c *KnowledgeController) Update() { return } + // Add share parsing + share, _ := c.GetInt8("share", 0) // Default 0 if not provided + knowledge.Share = share + err = models.UpdateKnowledge(knowledge.Id, &knowledge) if err != nil { c.Data["json"] = map[string]interface{}{ diff --git a/server/models/knowledge.go b/server/models/knowledge.go index bb2390f..38c24ff 100644 --- a/server/models/knowledge.go +++ b/server/models/knowledge.go @@ -18,6 +18,7 @@ type Knowledge struct { Summary string `orm:"column(summary);size(500);null" json:"summary"` CoverUrl string `orm:"column(cover_url);size(500);null" json:"coverUrl"` Status int8 `orm:"column(status);default(0)" json:"status"` + Share int8 `orm:"column(share);default(0)" json:"share"` ViewCount int `orm:"column(view_count);default(0)" json:"viewCount"` LikeCount int `orm:"column(like_count);default(0)" json:"likeCount"` IsRecommend int8 `orm:"column(is_recommend);default(0)" json:"isRecommend"` @@ -139,103 +140,78 @@ func GetKnowledgeById(id int) (*Knowledge, error) { return knowledge, err } -// GetAllKnowledge 获取所有知识(支持分页和筛选) -func GetAllKnowledge(page, pageSize int, status int8, categoryId int, keyword string) ([]*Knowledge, int64, error) { +// LightKnowledge for limited fields (with orm and json tags for direct mapping) +type LightKnowledge struct { + Id int `orm:"column(knowledge_id)" json:"id"` + Title string `orm:"column(title)" json:"title"` + CategoryId int `orm:"column(category_id)" json:"categoryId"` + CategoryName string `orm:"column(category_name)" json:"categoryName"` + Tags string `orm:"column(tags)" json:"tags"` + Author string `orm:"column(author)" json:"author"` + Share int8 `orm:"column(share)" json:"share"` + ViewCount int `orm:"column(view_count)" json:"viewCount"` + LikeCount int `orm:"column(like_count)" json:"likeCount"` + CreateTime time.Time `orm:"column(create_time)" json:"createTime"` + UpdateTime time.Time `orm:"column(update_time)" json:"updateTime"` +} + +// Helper to add conditions (reduces repetition) +func addCondition(where *string, params *[]interface{}, cond string, val interface{}) { + if val != nil { + *where += " AND " + cond + *params = append(*params, val) + } +} + +// GetAllKnowledge (simplified: direct mapping to LightKnowledge, no separate struct or loop) +func GetAllKnowledge(page, pageSize int, status int8, categoryId int, share int8, keyword string) ([]*LightKnowledge, int64, error) { o := orm.NewOrm() - // 构建 WHERE 条件(只查询未删除的记录) whereSQL := "delete_time IS NULL" params := []interface{}{} - // 状态筛选 - if status >= 0 { - whereSQL += " AND status = ?" - params = append(params, status) - } + addCondition(&whereSQL, ¶ms, "status = ?", func() interface{} { + if status >= 0 { + return status + } + return nil + }()) + addCondition(&whereSQL, ¶ms, "category_id = ?", func() interface{} { + if categoryId > 0 { + return categoryId + } + return nil + }()) + addCondition(&whereSQL, ¶ms, "share = ?", func() interface{} { + if share >= 0 { + return share + } + return nil + }()) + addCondition(&whereSQL, ¶ms, "title LIKE ?", func() interface{} { + if keyword != "" { + return "%" + keyword + "%" + } + return nil + }()) - // 分类筛选 - if categoryId > 0 { - whereSQL += " AND category_id = ?" - params = append(params, categoryId) - } - - // 关键词搜索 - if keyword != "" { - whereSQL += " AND title LIKE ?" - params = append(params, "%"+keyword+"%") - } - - // 获取总数 var total int64 - countSQL := "SELECT COUNT(*) FROM yz_knowledge WHERE " + whereSQL - err := o.Raw(countSQL, params...).QueryRow(&total) + err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+whereSQL, params).QueryRow(&total) if err != nil { return nil, 0, err } - // 联查获取分类名称 - querySQL := ` - SELECT k.*, c.category_name - FROM yz_knowledge k - LEFT JOIN yz_knowledge_category c ON k.category_id = c.category_id - WHERE ` + whereSQL + ` - ORDER BY k.is_top DESC, k.create_time DESC - LIMIT ? OFFSET ? - ` + querySQL := `SELECT k.knowledge_id, k.title, k.category_id, c.category_name, k.tags, k.author, k.share, k.view_count, k.like_count, k.create_time, k.update_time + FROM yz_knowledge k LEFT JOIN yz_knowledge_category c ON k.category_id = c.category_id + WHERE ` + whereSQL + ` ORDER BY k.is_top DESC, k.create_time DESC LIMIT ? OFFSET ?` params = append(params, pageSize, (page-1)*pageSize) - var knowledges []*Knowledge - var results []struct { - Id int `orm:"column(knowledge_id)"` - Title string `orm:"column(title)"` - CategoryId int `orm:"column(category_id)"` - CategoryName string `orm:"column(category_name)"` - Tags string `orm:"column(tags)"` - Author string `orm:"column(author)"` - Content string `orm:"column(content)"` - Summary string `orm:"column(summary)"` - CoverUrl string `orm:"column(cover_url)"` - Status int8 `orm:"column(status)"` - ViewCount int `orm:"column(view_count)"` - LikeCount int `orm:"column(like_count)"` - IsRecommend int8 `orm:"column(is_recommend)"` - IsTop int8 `orm:"column(is_top)"` - CreateTime time.Time `orm:"column(create_time)"` - UpdateTime time.Time `orm:"column(update_time)"` - CreateBy string `orm:"column(create_by)"` - UpdateBy string `orm:"column(update_by)"` - } - - _, err = o.Raw(querySQL, params...).QueryRows(&results) + var knowledges []*LightKnowledge + _, err = o.Raw(querySQL, params).QueryRows(&knowledges) if err != nil { return nil, 0, err } - // 转换结果 - for _, r := range results { - k := &Knowledge{ - Id: r.Id, - Title: r.Title, - CategoryId: r.CategoryId, - CategoryName: r.CategoryName, - Tags: r.Tags, - Author: r.Author, - Content: r.Content, - Summary: r.Summary, - CoverUrl: r.CoverUrl, - Status: r.Status, - ViewCount: r.ViewCount, - LikeCount: r.LikeCount, - IsRecommend: r.IsRecommend, - IsTop: r.IsTop, - CreateTime: r.CreateTime, - UpdateTime: r.UpdateTime, - CreateBy: r.CreateBy, - UpdateBy: r.UpdateBy, - } - knowledges = append(knowledges, k) - } - return knowledges, total, nil }