feat: 配套完善——管理端趋势表可查看/编辑人话解读,看板加解读覆盖率,llms.txt提及解读能力
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
<div class="card"><b>{{ d.stats?.approvedKnowledge ?? '-' }}</b><span>已审知识</span><i>共 {{ d.stats?.totalKnowledge ?? 0 }} · 含题 {{ d.stats?.withQuiz ?? 0 }}</i></div>
|
<div class="card"><b>{{ d.stats?.approvedKnowledge ?? '-' }}</b><span>已审知识</span><i>共 {{ d.stats?.totalKnowledge ?? 0 }} · 含题 {{ d.stats?.withQuiz ?? 0 }}</i></div>
|
||||||
<div class="card"><b>{{ d.stats?.totalLits ?? '-' }}</b><span>点亮星球</span><i>{{ d.stats?.litUsers ?? 0 }} 人参与</i></div>
|
<div class="card"><b>{{ d.stats?.totalLits ?? '-' }}</b><span>点亮星球</span><i>{{ d.stats?.litUsers ?? 0 }} 人参与</i></div>
|
||||||
<div class="card"><b>{{ d.stats?.aiChatUsed ?? '-' }}</b><span>AI 问答次数</span><i>{{ d.stats?.aiChatUsers ?? 0 }} 人用过</i></div>
|
<div class="card"><b>{{ d.stats?.aiChatUsed ?? '-' }}</b><span>AI 问答次数</span><i>{{ d.stats?.aiChatUsers ?? 0 }} 人用过</i></div>
|
||||||
<div class="card"><b>{{ d.stats?.totalTrends ?? '-' }}</b><span>趋势资讯</span><i>待审反馈 {{ d.stats?.pendingFeedbacks ?? 0 }}</i></div>
|
<div class="card"><b>{{ d.stats?.totalTrends ?? '-' }}</b><span>趋势资讯</span><i>含人话解读 {{ d.stats?.trendsWithExplain ?? 0 }}</i></div>
|
||||||
<div class="card"><b>{{ d.stats?.paidOrders ?? '-' }}</b><span>付费订单</span><i>共 {{ d.stats?.totalOrders ?? 0 }} 单</i></div>
|
<div class="card"><b>{{ d.stats?.paidOrders ?? '-' }}</b><span>付费订单</span><i>共 {{ d.stats?.totalOrders ?? 0 }} 单</i></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,12 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="category" label="分类" width="80" />
|
<el-table-column prop="category" label="分类" width="80" />
|
||||||
<el-table-column prop="source" label="来源" width="110" show-overflow-tooltip />
|
<el-table-column prop="source" label="来源" width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column label="人话解读" min-width="170" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.plainExplain">{{ row.plainExplain }}<template v-if="row.takeAway"> | {{ row.takeAway }}</template></span>
|
||||||
|
<el-tag v-else type="info" size="small">待补</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="日期" width="110">
|
<el-table-column label="日期" width="110">
|
||||||
<template #default="{ row }">{{ (row.newsDate || row.createdAt || '').slice(0, 10) }}</template>
|
<template #default="{ row }">{{ (row.newsDate || row.createdAt || '').slice(0, 10) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -35,6 +41,8 @@
|
|||||||
<el-input v-model="form.source" style="width: 140px" />
|
<el-input v-model="form.source" style="width: 140px" />
|
||||||
<el-input v-model="form.sourceUrl" placeholder="原文链接(选填)" style="flex:1; margin-left:8px" />
|
<el-input v-model="form.sourceUrl" placeholder="原文链接(选填)" style="flex:1; margin-left:8px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="人话解读"><el-input v-model="form.plainExplain" placeholder="一句大白话说清(30字内)" /></el-form-item>
|
||||||
|
<el-form-item label="对你"><el-input v-model="form.takeAway" placeholder="对普通人意味着什么(30字内)" /></el-form-item>
|
||||||
<el-form-item label="分类">
|
<el-form-item label="分类">
|
||||||
<el-select v-model="form.category" style="width: 130px"><el-option v-for="c in ['产品','论文','工具','公司','政策','其他']" :key="c" :label="c" :value="c" /></el-select>
|
<el-select v-model="form.category" style="width: 130px"><el-option v-for="c in ['产品','论文','工具','公司','政策','其他']" :key="c" :label="c" :value="c" /></el-select>
|
||||||
<el-switch v-model="form.hot" active-text="热门" style="margin-left: 16px" />
|
<el-switch v-model="form.hot" active-text="热门" style="margin-left: 16px" />
|
||||||
@@ -66,7 +74,7 @@ async function load() {
|
|||||||
} finally { loading.value = false }
|
} finally { loading.value = false }
|
||||||
}
|
}
|
||||||
function openEdit(row) {
|
function openEdit(row) {
|
||||||
form.value = row ? { ...row } : { title: '', summary: '', source: '', sourceUrl: '', category: '产品', hot: false, picked: false }
|
form.value = row ? { ...row } : { title: '', summary: '', source: '', sourceUrl: '', category: '产品', hot: false, picked: false, plainExplain: '', takeAway: '' }
|
||||||
dlg.value = true
|
dlg.value = true
|
||||||
}
|
}
|
||||||
async function save() {
|
async function save() {
|
||||||
|
|||||||
@@ -1097,9 +1097,9 @@ router.get('/trends', requirePermission('trend:read'), async (req, res) => {
|
|||||||
|
|
||||||
router.post('/trends', requirePermission('trend:write'), async (req, res) => {
|
router.post('/trends', requirePermission('trend:write'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { title, summary, source, sourceUrl, category, tags, hot, picked, newsDate } = req.body
|
const { title, summary, source, sourceUrl, category, tags, hot, picked, newsDate, plainExplain, takeAway } = req.body
|
||||||
if (!title) return ApiResponse.error(res, '标题不能为空', 400)
|
if (!title) return ApiResponse.error(res, '标题不能为空', 400)
|
||||||
const item = new Trend({ title, summary, source, sourceUrl, category, tags, hot: !!hot, picked: !!picked, newsDate: newsDate || new Date() })
|
const item = new Trend({ title, summary, source, sourceUrl, category, tags, hot: !!hot, picked: !!picked, newsDate: newsDate || new Date(), plainExplain: plainExplain || '', takeAway: takeAway || '' })
|
||||||
await item.save()
|
await item.save()
|
||||||
await logAction(req, 'create', 'trend', item._id, { title })
|
await logAction(req, 'create', 'trend', item._id, { title })
|
||||||
return ApiResponse.success(res, item, '创建成功')
|
return ApiResponse.success(res, item, '创建成功')
|
||||||
@@ -1111,9 +1111,9 @@ router.post('/trends', requirePermission('trend:write'), async (req, res) => {
|
|||||||
|
|
||||||
router.put('/trends/:id', requirePermission('trend:write'), async (req, res) => {
|
router.put('/trends/:id', requirePermission('trend:write'), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { title, summary, source, sourceUrl, category, tags, hot, picked, newsDate } = req.body
|
const { title, summary, source, sourceUrl, category, tags, hot, picked, newsDate, plainExplain, takeAway } = req.body
|
||||||
const item = await Trend.findByIdAndUpdate(req.params.id,
|
const item = await Trend.findByIdAndUpdate(req.params.id,
|
||||||
{ title, summary, source, sourceUrl, category, tags, hot: !!hot, picked: !!picked, ...(newsDate ? { newsDate } : {}) },
|
{ title, summary, source, sourceUrl, category, tags, hot: !!hot, picked: !!picked, plainExplain: plainExplain || '', takeAway: takeAway || '', ...(newsDate ? { newsDate } : {}) },
|
||||||
{ new: true })
|
{ new: true })
|
||||||
if (!item) return ApiResponse.notFound(res, '趋势不存在')
|
if (!item) return ApiResponse.notFound(res, '趋势不存在')
|
||||||
await logAction(req, 'update', 'trend', item._id, { title })
|
await logAction(req, 'update', 'trend', item._id, { title })
|
||||||
@@ -1144,7 +1144,7 @@ router.get('/dashboard-v2', requirePermission('dashboard:read'), async (req, res
|
|||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
totalUsers, newUsers7d, totalKnowledge, approvedKnowledge, withQuiz,
|
totalUsers, newUsers7d, totalKnowledge, approvedKnowledge, withQuiz,
|
||||||
totalTrends, totalFeedbacks, pendingFeedbacks, totalOrders, paidOrders,
|
totalTrends, trendsWithExplain, totalFeedbacks, pendingFeedbacks, totalOrders, paidOrders,
|
||||||
totalLits, litUsers, recentLogins, litByDimAgg, aiStatsRaw
|
totalLits, litUsers, recentLogins, litByDimAgg, aiStatsRaw
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
User.countDocuments({}),
|
User.countDocuments({}),
|
||||||
@@ -1153,6 +1153,7 @@ router.get('/dashboard-v2', requirePermission('dashboard:read'), async (req, res
|
|||||||
Knowledge.countDocuments({ status: 'approved' }),
|
Knowledge.countDocuments({ status: 'approved' }),
|
||||||
Knowledge.countDocuments({ 'options.0': { $exists: true } }),
|
Knowledge.countDocuments({ 'options.0': { $exists: true } }),
|
||||||
Trend.countDocuments({}),
|
Trend.countDocuments({}),
|
||||||
|
Trend.countDocuments({ plainExplain: { $exists: true, $ne: '' } }),
|
||||||
Feedback.countDocuments({}),
|
Feedback.countDocuments({}),
|
||||||
Feedback.countDocuments({ status: 'pending' }),
|
Feedback.countDocuments({ status: 'pending' }),
|
||||||
Order.countDocuments({}),
|
Order.countDocuments({}),
|
||||||
@@ -1175,7 +1176,7 @@ router.get('/dashboard-v2', requirePermission('dashboard:read'), async (req, res
|
|||||||
stats: {
|
stats: {
|
||||||
totalUsers, newUsers7d,
|
totalUsers, newUsers7d,
|
||||||
totalKnowledge, approvedKnowledge, withQuiz,
|
totalKnowledge, approvedKnowledge, withQuiz,
|
||||||
totalTrends, totalFeedbacks, pendingFeedbacks,
|
totalTrends, trendsWithExplain, totalFeedbacks, pendingFeedbacks,
|
||||||
totalOrders, paidOrders,
|
totalOrders, paidOrders,
|
||||||
totalLits, litUsers: litUsers.length,
|
totalLits, litUsers: litUsers.length,
|
||||||
aiChatUsed: ai.used, aiChatUsers: ai.users,
|
aiChatUsed: ai.used, aiChatUsers: ai.users,
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ router.get('/llms.txt', (req, res) => {
|
|||||||
|
|
||||||
## 核心内容
|
## 核心内容
|
||||||
- 五维度知识体系:AI起源、发展脉络、当前格局、学习工具、趋势跟踪
|
- 五维度知识体系:AI起源、发展脉络、当前格局、学习工具、趋势跟踪
|
||||||
- 每日AI趋势:每天 8:00/20:00 自动采集 Hacker News / arXiv 热点并中文归类(产品/论文/工具/公司/政策)
|
- 每日AI趋势:每天 8:00/20:00 自动采集 Hacker News / arXiv 热点并中文归类(产品/论文/工具/公司/政策),每条附「人话解读」:一句大白话说清 + 对普通人意味着什么
|
||||||
- AI问答:基于知识库的智能问答
|
- AI问答:基于知识库的智能问答
|
||||||
|
|
||||||
## 免费与付费
|
## 免费与付费
|
||||||
|
|||||||
Reference in New Issue
Block a user