更新知识库功能

This commit is contained in:
李志强 2025-10-31 17:34:14 +08:00
parent 931b999e1d
commit 186dcb86d9
5 changed files with 326 additions and 320 deletions

View File

@ -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,
});
}

View File

@ -66,6 +66,16 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20"> <!-- Second row for share -->
<el-col :span="6">
<el-form-item label="是否共享" prop="share">
<el-radio-group v-model="formData.share">
<el-radio :label="0">个人</el-radio>
<el-radio :label="1">共享</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
<el-divider /> <el-divider />
<div class="meta-actions"> <div class="meta-actions">
@ -105,14 +115,9 @@
import { ref, reactive, computed, onMounted, watch } from "vue"; import { ref, reactive, computed, onMounted, watch } from "vue";
import { useRouter, useRoute } from "vue-router"; import { useRouter, useRoute } from "vue-router";
import { marked } from "marked"; import { marked } from "marked";
import { // @ts-ignore
getKnowledgeDetail, import { getKnowledgeDetail, createKnowledge, updateKnowledge, getCategoryList, getTagList } from "@/api/knowledge"; // Changed to getKnowledgeDetail
updateKnowledge, import { ElMessage, ElMessageBox } from "element-plus";
createKnowledge,
getCategoryList,
getTagList,
} from "@/api/knowledge";
import { ElMessage } from "element-plus";
import type { FormInstance, FormRules } from "element-plus"; import type { FormInstance, FormRules } from "element-plus";
// DOM // DOM
@ -129,6 +134,7 @@ const formData = reactive<{
tags: string[]; tags: string[];
author: string; author: string;
content: string; content: string;
share: number;
}>({ }>({
title: "", title: "",
category: "", category: "",
@ -136,15 +142,13 @@ const formData = reactive<{
tags: [], tags: [],
author: "", author: "",
content: "", content: "",
share: 0,
}); });
// //
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
title: [{ required: true, message: "请输入标题", trigger: "blur" }], title: [{ required: true, message: "请输入标题", trigger: "blur" }],
category: [{ required: true, message: "请选择分类", trigger: "change" }], category: [{ required: true, message: "请选择分类", trigger: "change" }],
// tags: [
// { type: "array", required: true, message: "", trigger: "change" },
// ],
author: [{ required: true, message: "请输入作者", trigger: "blur" }], author: [{ required: true, message: "请输入作者", trigger: "blur" }],
content: [{ required: true, message: "请输入正文", trigger: "blur" }], content: [{ required: true, message: "请输入正文", trigger: "blur" }],
}); });
@ -195,9 +199,9 @@ const fetchDetail = async () => {
try { try {
const currentId = id.value as string; const currentId = id.value as string;
if (currentId && currentId !== "new") { 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" } // 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 // categoryName
@ -206,6 +210,7 @@ const fetchDetail = async () => {
formData.categoryId = data.categoryId || 0; formData.categoryId = data.categoryId || 0;
formData.author = data.author || ""; formData.author = data.author || "";
formData.content = data.content || ""; formData.content = data.content || "";
formData.share = data.share || 0; // Added share loading
// categoryId categoryName // categoryId categoryName
if (!formData.categoryId && formData.category) { if (!formData.categoryId && formData.category) {
@ -220,7 +225,8 @@ const fetchDetail = async () => {
// Tags JSON // Tags JSON
if (data.tags) { if (data.tags) {
try { 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 : []; formData.tags = Array.isArray(parsed) ? parsed : [];
} catch { } catch {
// //
@ -242,14 +248,18 @@ const loadCategoryAndTag = async () => {
getCategoryList(), getCategoryList(),
getTagList(), getTagList(),
]); ]);
// API : { code: 0, data: [...], message: "success" } // API : { code: 0, data: [...], message: "success" }
const categories = (catRes.code === 0 && catRes.data) ? catRes.data : (catRes.data || []); const categories =
const tags = (tagRes.code === 0 && tagRes.data) ? tagRes.data : (tagRes.data || []); 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 : []; categoryList.value = Array.isArray(categories) ? categories : [];
// { tagId, tagName } tagName // { 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) { } catch (e: any) {
console.error("加载分类和标签失败:", e); console.error("加载分类和标签失败:", e);
categoryList.value = []; categoryList.value = [];
@ -293,14 +303,16 @@ const handleSubmit = () => {
categoryId: Number(formData.categoryId) || 0, categoryId: Number(formData.categoryId) || 0,
author: formData.author || "", author: formData.author || "",
content: formData.content || "", content: formData.content || "",
tags: Array.isArray(formData.tags) && formData.tags.length > 0 tags:
? JSON.stringify(formData.tags) Array.isArray(formData.tags) && formData.tags.length > 0
: "[]", ? JSON.stringify(formData.tags)
: "[]",
status: 1, // 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") { if (isEdit.value && currentId !== "new") {
// id JSON tag "id" // id JSON tag "id"
@ -321,7 +333,8 @@ const handleSubmit = () => {
} }
} catch (e: any) { } catch (e: any) {
console.error("保存失败:", e); console.error("保存失败:", e);
const errorMessage = e.response?.data?.message || e.message || "保存失败"; const errorMessage =
e.response?.data?.message || e.message || "保存失败";
ElMessage.error(errorMessage); ElMessage.error(errorMessage);
} }
} else { } else {
@ -337,13 +350,33 @@ const handleSubmit = () => {
} }
} catch (e: any) { } catch (e: any) {
console.error("创建失败:", e); console.error("创建失败:", e);
const errorMessage = e.response?.data?.message || e.message || "创建失败"; const errorMessage =
e.response?.data?.message || e.message || "创建失败";
ElMessage.error(errorMessage); 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 () => { onMounted(async () => {
// //
@ -490,7 +523,10 @@ defineExpose({
margin-bottom: 8px; margin-bottom: 8px;
} }
:deep(p), :deep(li), :deep(span), :deep(div) { :deep(p),
:deep(li),
:deep(span),
:deep(div) {
color: var(--text-color-primary); color: var(--text-color-primary);
margin: 8px 0; margin: 8px 0;
} }
@ -498,7 +534,7 @@ defineExpose({
:deep(a) { :deep(a) {
color: var(--primary-color); color: var(--primary-color);
text-decoration: none; text-decoration: none;
&:hover { &:hover {
opacity: 0.8; opacity: 0.8;
} }
@ -510,7 +546,7 @@ defineExpose({
border-color: var(--border-color-lighter); border-color: var(--border-color-lighter);
padding: 2px 6px; padding: 2px 6px;
border-radius: 3px; border-radius: 3px;
font-family: 'Courier New', monospace; font-family: "Courier New", monospace;
} }
:deep(pre) { :deep(pre) {
@ -519,7 +555,7 @@ defineExpose({
padding: 12px; padding: 12px;
border-radius: 8px; border-radius: 8px;
overflow-x: auto; overflow-x: auto;
code { code {
background-color: transparent; background-color: transparent;
border: none; border: none;
@ -558,7 +594,8 @@ defineExpose({
margin-bottom: 0px; 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; background-color: var(--fill-color-blank) !important;
border-color: var(--border-color) !important; border-color: var(--border-color) !important;
color: var(--text-color-primary) !important; color: var(--text-color-primary) !important;
@ -568,7 +605,7 @@ defineExpose({
&::placeholder { &::placeholder {
color: var(--text-color-placeholder) !important; color: var(--text-color-placeholder) !important;
} }
&:focus { &:focus {
border-color: var(--primary-color) !important; border-color: var(--primary-color) !important;
} }

View File

@ -83,92 +83,176 @@
</div> </div>
</div> </div>
<div class="repos-grid"> <!-- Tabs for personal and shared -->
<div class="repo-card" v-for="repo in repoList" :key="repo.id"> <el-tabs v-model="activeTab" type="card" class="knowledge-tabs">
<!-- 卡片头部 --> <el-tab-pane label="个人知识库" name="personal">
<div class="repo-header"> <div class="repos-grid">
<div class="repo-icon"> <div class="repo-card" v-for="repo in personalList" :key="repo.id">
<i class="fa-solid fa-book"></i> <!-- 卡片头部 -->
</div> <div class="repo-header">
<div class="repo-title-container"> <div class="repo-icon">
<h3 class="repo-name">{{ repo.title }}</h3> <i class="fa-solid fa-book"></i>
<div class="repo-meta"> </div>
<el-tag size="small" class="category-tag"> <div class="repo-title-container">
{{ repo.categoryName || "未分类" }} <h3 class="repo-name">{{ repo.title }}</h3>
</el-tag> <div class="repo-meta">
<span class="repo-owner"> <el-tag size="small" class="category-tag">
<i class="fa-solid fa-user"></i> {{ repo.categoryName || "未分类" }}
{{ repo.author }} </el-tag>
</span> <span class="repo-owner">
<i class="fa-solid fa-user"></i>
{{ repo.author }}
</span>
</div>
</div>
</div>
<!-- 卡片内容 -->
<div class="repo-content">
<div class="repo-tags" v-if="repo.tags">
<el-tag
v-for="tag in parseTags(repo.tags)"
:key="tag"
size="small"
class="tag-item"
>
{{ tag }}
</el-tag>
</div>
<div class="repo-stats">
<div class="stat-item">
<i class="fa-solid fa-eye"></i>
<span>{{ repo.viewCount || 0 }} 浏览</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-heart"></i>
<span>{{ repo.likeCount || 0 }} 点赞</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-clock"></i>
<span>{{ formatDate(repo.createTime) }}</span>
</div>
</div>
</div>
<!-- 卡片底部操作区 -->
<div class="repo-actions">
<button class="action-btn view-btn" @click.stop="handleView(repo)">
<i class="el-icon-view"></i> 查看
</button>
<button class="action-btn edit-btn" @click.stop="handleEdit(repo)">
<i class="el-icon-edit"></i> 编辑
</button>
<button
class="action-btn delete-btn"
@click.stop="handleDelete(repo)"
>
<i class="el-icon-delete"></i> 删除
</button>
</div> </div>
</div> </div>
</div>
<!-- 卡片内容 --> <!-- 空状态 for personal -->
<div class="repo-content"> <div class="empty-state" v-if="personalList.length === 0">
<div class="repo-tags" v-if="repo.tags"> <div class="empty-icon">
<el-tag <i class="fa-solid fa-folder-open"></i>
v-for="tag in parseTags(repo.tags)"
:key="tag"
size="small"
class="tag-item"
>
{{ tag }}
</el-tag>
</div>
<div class="repo-stats">
<div class="stat-item">
<i class="fa-solid fa-eye"></i>
<span>{{ repo.viewCount || 0 }} 浏览</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-heart"></i>
<span>{{ repo.likeCount || 0 }} 点赞</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-clock"></i>
<span>{{ formatDate(repo.createTime) }}</span>
</div> </div>
<h3>暂无个人知识库</h3>
<p>点击下方按钮创建您的第一个知识库</p>
<el-button type="primary" @click="handleCreate">
<i class="fa-solid fa-plus"></i>
新建知识库
</el-button>
</div> </div>
</div> </div>
</el-tab-pane>
<!-- 卡片底部操作区 --> <el-tab-pane label="共享知识库" name="shared">
<div class="repo-actions"> <div class="repos-grid">
<button class="action-btn view-btn" @click.stop="handleView(repo)"> <div class="repo-card" v-for="repo in sharedList" :key="repo.id">
<i class="el-icon-view"></i> 查看 <!-- 卡片头部 -->
</button> <div class="repo-header">
<button class="action-btn edit-btn" @click.stop="handleEdit(repo)"> <div class="repo-icon">
<i class="el-icon-edit"></i> 编辑 <i class="fa-solid fa-book"></i>
</button> </div>
<button <div class="repo-title-container">
class="action-btn delete-btn" <h3 class="repo-name">{{ repo.title }}</h3>
@click.stop="handleDelete(repo)" <div class="repo-meta">
> <el-tag size="small" class="category-tag">
<i class="el-icon-delete"></i> 删除 {{ repo.categoryName || "未分类" }}
</button> </el-tag>
</div> <span class="repo-owner">
</div> <i class="fa-solid fa-user"></i>
{{ repo.author }}
</span>
</div>
</div>
</div>
<!-- 空状态 --> <!-- 卡片内容 -->
<div class="empty-state" v-if="repoList.length === 0"> <div class="repo-content">
<div class="empty-icon"> <div class="repo-tags" v-if="repo.tags">
<i class="fa-solid fa-folder-open"></i> <el-tag
v-for="tag in parseTags(repo.tags)"
:key="tag"
size="small"
class="tag-item"
>
{{ tag }}
</el-tag>
</div>
<div class="repo-stats">
<div class="stat-item">
<i class="fa-solid fa-eye"></i>
<span>{{ repo.viewCount || 0 }} 浏览</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-heart"></i>
<span>{{ repo.likeCount || 0 }} 点赞</span>
</div>
<div class="stat-item">
<i class="fa-solid fa-clock"></i>
<span>{{ formatDate(repo.createTime) }}</span>
</div>
</div>
</div>
<!-- 卡片底部操作区 -->
<div class="repo-actions">
<button class="action-btn view-btn" @click.stop="handleView(repo)">
<i class="el-icon-view"></i> 查看
</button>
<button class="action-btn edit-btn" @click.stop="handleEdit(repo)">
<i class="el-icon-edit"></i> 编辑
</button>
<button
class="action-btn delete-btn"
@click.stop="handleDelete(repo)"
>
<i class="el-icon-delete"></i> 删除
</button>
</div>
</div>
<!-- 空状态 for shared -->
<div class="empty-state" v-if="sharedList.length === 0">
<div class="empty-icon">
<i class="fa-solid fa-folder-open"></i>
</div>
<h3>暂无共享知识库</h3>
<p>暂无共享内容</p>
</div>
</div> </div>
<h3>暂无知识库</h3> </el-tab-pane>
<p>点击下方按钮创建您的第一个知识库</p> </el-tabs>
<el-button type="primary" @click="handleCreate">
<i class="fa-solid fa-plus"></i>
新建知识库
</el-button>
</div>
</div>
<!-- 分页 --> <!-- 分页 -->
<el-pagination <el-pagination
background background
layout="prev, pager, next" layout="prev, pager, next"
:total="total" :total="activeTab === 'personal' ? personalList.length : sharedList.length"
:page-size="pageSize" :page-size="pageSize"
:current-page="currentPage" :current-page="currentPage"
@current-change="handlePageChange" @current-change="handlePageChange"
@ -182,6 +266,8 @@
import { ref, reactive, computed, onMounted } from "vue"; import { ref, reactive, computed, onMounted } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
// @ts-ignore
import { getKnowledgeList, deleteKnowledge } from "@/api/knowledge"; import { getKnowledgeList, deleteKnowledge } from "@/api/knowledge";
// //
@ -196,6 +282,7 @@ interface Knowledge {
likeCount: number; likeCount: number;
createTime: string; createTime: string;
updateTime: string; updateTime: string;
share: number;
} }
interface Stats { interface Stats {
@ -216,9 +303,10 @@ const router = useRouter();
// //
const keyword = ref(""); const keyword = ref("");
const searchType = ref("knowledge");
const hotTags = ref(["化妆品", "汽车零部件", "口罩", "工业用品", "食品"]); const hotTags = ref(["化妆品", "汽车零部件", "口罩", "工业用品", "食品"]);
const activeTab = ref('personal');
const stats = reactive<Stats>({ const stats = reactive<Stats>({
total: 0, total: 0,
docCount: 0, docCount: 0,
@ -232,6 +320,10 @@ const pageSize = ref(12);
const currentPage = ref(1); const currentPage = ref(1);
const loading = ref(false); 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<StatItem[]>(() => [ const statsList = computed<StatItem[]>(() => [
{ {
@ -258,12 +350,8 @@ const statsList = computed<StatItem[]>(() => [
// //
function updateStats() { function updateStats() {
// API
stats.total = total.value; stats.total = total.value;
stats.docCount = total.value; stats.docCount = total.value;
//
// stats.memberCount = 0; //
// stats.viewCount = 0; //
} }
function handleSearch() { function handleSearch() {
@ -276,61 +364,54 @@ function handlePageChange(page: number) {
fetchRepoList(); fetchRepoList();
} }
// fetchRepoList // fetchRepoList (no share filter)
async function fetchRepoList() { async function fetchRepoList() {
loading.value = true; loading.value = true;
try { try {
const result = await getKnowledgeList({ const result = await getKnowledgeList({
page: currentPage.value, page: currentPage.value,
pageSize: pageSize.value, pageSize: pageSize.value,
status: 1, // status: 1,
keyword: keyword.value, // keyword: keyword.value,
share: -1,
}); });
// API : { code: 0, data: { list: [...], total: 1 }, message: "success" }
if (result.code === 0 && result.data) { if (result.code === 0 && result.data) {
repoList.value = result.data.list || []; repoList.value = result.data.list || [];
total.value = result.data.total || 0; total.value = result.data.total || 0;
} else { } else {
// list total
repoList.value = result.list || result.data?.list || []; repoList.value = result.list || result.data?.list || [];
total.value = result.total || result.data?.total || 0; total.value = result.total || result.data?.total || 0;
} }
// API
updateStats(); updateStats();
} catch (error: any) { } catch (error: any) {
ElMessage.error(error.message || "获取知识库列表失败"); ElMessage.error(error.message || "获取知识库列表失败");
repoList.value = []; repoList.value = [];
total.value = 0; total.value = 0;
updateStats(); // 使 updateStats();
} finally { } finally {
loading.value = false; loading.value = false;
} }
} }
function handleCreate() { function handleCreate() {
// 使
router.push(`/apps/knowledge/edit/new`); router.push(`/apps/knowledge/edit/new`);
} }
function handleView(repo: Knowledge) { function handleView(repo: Knowledge) {
// 使
router.push(`/apps/knowledge/detail/${repo.id}`); router.push(`/apps/knowledge/detail/${repo.id}`);
} }
function handleEdit(repo: Knowledge) { function handleEdit(repo: Knowledge) {
// 使
router.push(`/apps/knowledge/edit/${repo.id}`); router.push(`/apps/knowledge/edit/${repo.id}`);
} }
function handleCategory() { function handleCategory() {
// 使
router.push(`/apps/knowledge/category`); router.push(`/apps/knowledge/category`);
} }
function handleTags() { function handleTags() {
// 使
router.push(`/apps/knowledge/tags`); router.push(`/apps/knowledge/tags`);
} }
@ -344,14 +425,12 @@ function handleDelete(repo: Knowledge) {
try { try {
await deleteKnowledge(repo.id); await deleteKnowledge(repo.id);
ElMessage.success("删除成功"); ElMessage.success("删除成功");
fetchRepoList(); // fetchRepoList();
} catch (error: any) { } catch (error: any) {
ElMessage.error(error.message || "删除失败"); ElMessage.error(error.message || "删除失败");
} }
}) })
.catch(() => { .catch(() => {});
//
});
} }
function handleHotSearch(tag: string) { function handleHotSearch(tag: string) {
@ -397,7 +476,7 @@ function formatDate(dateStr: string): string {
// //
onMounted(() => { onMounted(() => {
fetchRepoList(); // fetchRepoList();
}); });
</script> </script>
@ -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) { @media (max-width: 768px) {
.hero.new-style { .hero.new-style {

View File

@ -19,11 +19,13 @@ func (c *KnowledgeController) List() {
// 获取查询参数 // 获取查询参数
page, _ := c.GetInt("page", 1) page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 10) pageSize, _ := c.GetInt("pageSize", 10)
status, _ := c.GetInt8("status", -1) // -1表示查询所有 status, _ := c.GetInt8("status", 1) // -1表示查询所有
categoryId, _ := c.GetInt("categoryId", 0) categoryId, _ := c.GetInt("categoryId", 0)
share, _ := c.GetInt8("share", -1) // Default -1 to query all
keyword := c.GetString("keyword", "") 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 { if err != nil {
c.Data["json"] = map[string]interface{}{ c.Data["json"] = map[string]interface{}{
"code": 1, "code": 1,
@ -107,6 +109,10 @@ func (c *KnowledgeController) Create() {
return return
} }
// Add share parsing (default 0 for personal)
share, _ := c.GetInt8("share", 0)
knowledge.Share = share
id, err := models.AddKnowledge(&knowledge) id, err := models.AddKnowledge(&knowledge)
if err != nil { if err != nil {
c.Data["json"] = map[string]interface{}{ c.Data["json"] = map[string]interface{}{
@ -153,6 +159,10 @@ func (c *KnowledgeController) Update() {
return return
} }
// Add share parsing
share, _ := c.GetInt8("share", 0) // Default 0 if not provided
knowledge.Share = share
err = models.UpdateKnowledge(knowledge.Id, &knowledge) err = models.UpdateKnowledge(knowledge.Id, &knowledge)
if err != nil { if err != nil {
c.Data["json"] = map[string]interface{}{ c.Data["json"] = map[string]interface{}{

View File

@ -18,6 +18,7 @@ type Knowledge struct {
Summary string `orm:"column(summary);size(500);null" json:"summary"` Summary string `orm:"column(summary);size(500);null" json:"summary"`
CoverUrl string `orm:"column(cover_url);size(500);null" json:"coverUrl"` CoverUrl string `orm:"column(cover_url);size(500);null" json:"coverUrl"`
Status int8 `orm:"column(status);default(0)" json:"status"` 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"` ViewCount int `orm:"column(view_count);default(0)" json:"viewCount"`
LikeCount int `orm:"column(like_count);default(0)" json:"likeCount"` LikeCount int `orm:"column(like_count);default(0)" json:"likeCount"`
IsRecommend int8 `orm:"column(is_recommend);default(0)" json:"isRecommend"` IsRecommend int8 `orm:"column(is_recommend);default(0)" json:"isRecommend"`
@ -139,103 +140,78 @@ func GetKnowledgeById(id int) (*Knowledge, error) {
return knowledge, err return knowledge, err
} }
// GetAllKnowledge 获取所有知识(支持分页和筛选) // LightKnowledge for limited fields (with orm and json tags for direct mapping)
func GetAllKnowledge(page, pageSize int, status int8, categoryId int, keyword string) ([]*Knowledge, int64, error) { 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() o := orm.NewOrm()
// 构建 WHERE 条件(只查询未删除的记录)
whereSQL := "delete_time IS NULL" whereSQL := "delete_time IS NULL"
params := []interface{}{} params := []interface{}{}
// 状态筛选 addCondition(&whereSQL, &params, "status = ?", func() interface{} {
if status >= 0 { if status >= 0 {
whereSQL += " AND status = ?" return status
params = append(params, status) }
} return nil
}())
addCondition(&whereSQL, &params, "category_id = ?", func() interface{} {
if categoryId > 0 {
return categoryId
}
return nil
}())
addCondition(&whereSQL, &params, "share = ?", func() interface{} {
if share >= 0 {
return share
}
return nil
}())
addCondition(&whereSQL, &params, "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 var total int64
countSQL := "SELECT COUNT(*) FROM yz_knowledge WHERE " + whereSQL err := o.Raw("SELECT COUNT(*) FROM yz_knowledge WHERE "+whereSQL, params).QueryRow(&total)
err := o.Raw(countSQL, params...).QueryRow(&total)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
// 联查获取分类名称 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
querySQL := ` FROM yz_knowledge k LEFT JOIN yz_knowledge_category c ON k.category_id = c.category_id
SELECT k.*, c.category_name WHERE ` + whereSQL + ` ORDER BY k.is_top DESC, k.create_time DESC LIMIT ? OFFSET ?`
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) params = append(params, pageSize, (page-1)*pageSize)
var knowledges []*Knowledge var knowledges []*LightKnowledge
var results []struct { _, err = o.Raw(querySQL, params).QueryRows(&knowledges)
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)
if err != nil { if err != nil {
return nil, 0, err 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 return knowledges, total, nil
} }