fix: 修复前端TypeScript类型错误

This commit is contained in:
Sens 2026-02-21 00:19:44 +08:00
parent b0a8c6edb9
commit 20d4048543
3 changed files with 30 additions and 36 deletions

View File

@ -107,7 +107,7 @@ const extractMetadata = (content: string) => {
// 1. Extract Title (First H1 or first non-empty line) // 1. Extract Title (First H1 or first non-empty line)
const titleMatch = content.match(/^#\s+(.*)/m) const titleMatch = content.match(/^#\s+(.*)/m)
if (titleMatch) { if (titleMatch && titleMatch[1]) {
title = titleMatch[1].trim() title = titleMatch[1].trim()
} else { } else {
const firstLine = lines.find(l => l.trim().length > 0) const firstLine = lines.find(l => l.trim().length > 0)
@ -136,12 +136,12 @@ const extractMetadata = (content: string) => {
// Look for "Core Logic", "Architecture", "Introduction" headers // Look for "Core Logic", "Architecture", "Introduction" headers
const logicRegex = /##\s*(Core Logic|Architecture|Introduction|About|Features)([\s\S]*?)(##|$)/i const logicRegex = /##\s*(Core Logic|Architecture|Introduction|About|Features)([\s\S]*?)(##|$)/i
const logicMatch = content.match(logicRegex) const logicMatch = content.match(logicRegex)
if (logicMatch) { if (logicMatch && logicMatch[2]) {
coreLogic = logicMatch[2].trim().substring(0, 300) + (logicMatch[2].length > 300 ? '...' : '') coreLogic = logicMatch[2].trim().substring(0, 300) + (logicMatch[2].length > 300 ? '...' : '')
} else { } else {
// Fallback: First paragraph after title // Fallback: First paragraph after title
const paragraphs = content.split(/\n\s*\n/) const paragraphs = content.split(/\n\s*\n/)
if (paragraphs.length > 1) { if (paragraphs.length > 1 && paragraphs[1]) {
coreLogic = paragraphs[1].trim().substring(0, 200) + '...' coreLogic = paragraphs[1].trim().substring(0, 200) + '...'
} }
} }

View File

@ -52,7 +52,7 @@
</div> </div>
<!-- 选中提供商说明 --> <!-- 选中提供商说明 -->
<div class="provider-info"> <div class="provider-info" v-if="selectedProvider">
<span class="provider-desc">{{ selectedProvider.desc }}</span> <span class="provider-desc">{{ selectedProvider.desc }}</span>
<span class="provider-pricing">{{ selectedProvider.pricing }}</span> <span class="provider-pricing">{{ selectedProvider.pricing }}</span>
</div> </div>
@ -63,7 +63,7 @@
<h2 class="section-title">连接配置</h2> <h2 class="section-title">连接配置</h2>
<!-- API 密钥配置 --> <!-- API 密钥配置 -->
<div v-if="showApiKey" class="form-group"> <div v-if="showApiKey && selectedProvider" class="form-group">
<label class="form-label">API 密钥</label> <label class="form-label">API 密钥</label>
<input <input
v-model="apiKey" v-model="apiKey"
@ -161,13 +161,6 @@ const KeyIcon = () => h('svg', { viewBox: '0 0 24 24', fill: 'none', stroke: 'cu
h('path', { d: 'M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4' }) h('path', { d: 'M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4' })
]) ])
const ServerIcon = () => h('svg', { viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': 1.5 }, [
h('rect', { x: '2', y: '2', width: '20', height: '8', rx: '2', ry: '2' }),
h('rect', { x: '2', y: '14', width: '20', height: '8', rx: '2', ry: '2' }),
h('line', { x1: '6', y1: '6', x2: '6.01', y2: '6' }),
h('line', { x1: '6', y1: '18', x2: '6.01', y2: '18' })
])
// 侧边栏导航 // 侧边栏导航
const navItems = [ const navItems = [
{ key: 'provider', label: '模型服务', icon: CloudIcon }, { key: 'provider', label: '模型服务', icon: CloudIcon },

View File

@ -21,11 +21,11 @@
<div class="progress-comparison"> <div class="progress-comparison">
<div class="progress-row"> <div class="progress-row">
<span class="label">我方</span> <span class="label">我方</span>
<n-progress type="line" :percentage="getOurProgress(selectedSignal)" color="#63e2b7" rail-color="rgba(255,255,255,0.1)" height="6" processing /> <n-progress type="line" :percentage="Number(getOurProgress(selectedSignal))" color="#63e2b7" rail-color="rgba(255,255,255,0.1)" :height="6" processing />
</div> </div>
<div class="progress-row"> <div class="progress-row">
<span class="label">竞品</span> <span class="label">竞品</span>
<n-progress type="line" :percentage="getCompetitorProgress(selectedSignal)" color="#f5222d" rail-color="rgba(255,255,255,0.1)" height="6" /> <n-progress type="line" :percentage="Number(getCompetitorProgress(selectedSignal))" color="#f5222d" rail-color="rgba(255,255,255,0.1)" :height="6" />
</div> </div>
<div class="lead-time-text" v-if="selectedSignal"> <div class="lead-time-text" v-if="selectedSignal">
{{ getLeadTimeText(selectedSignal) }} {{ getLeadTimeText(selectedSignal) }}
@ -134,18 +134,18 @@
'new-signal-flash': isNewSignal(signal.latestSignal) 'new-signal-flash': isNewSignal(signal.latestSignal)
}" }"
> >
<template #header> <n-card-header>
<div style="display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; justify-content: space-between; align-items: center;">
<div style="display: flex; align-items: center; gap: 6px;"> <div style="display: flex; align-items: center; gap: 6px;">
<n-tag :type="getPriorityType(signal.latestSignal.priority)" size="small" round> <n-tag :type="getPriorityType(signal.latestSignal?.priority || 'medium')" size="small" round>
{{ getPriorityLabel(signal.latestSignal.priority) }} {{ getPriorityLabel(signal.latestSignal?.priority || 'medium') }}
</n-tag> </n-tag>
<n-tag :type="getImpactColor(signal.impact_score)" size="small" round>{{ signal.source }}</n-tag> <n-tag :type="getImpactColor(signal.impact_score)" size="small" round>{{ signal.source }}</n-tag>
<n-tag v-if="signal.count > 1" type="info" size="tiny" round>{{ signal.count }}条</n-tag> <n-tag v-if="signal.count > 1" type="info" size="tiny" round>{{ signal.count }}条</n-tag>
</div> </div>
<n-text depth="3" style="font-size: 12px;">{{ formatDate(signal.created_at) }}</n-text> <n-text depth="3" style="font-size: 12px;">{{ formatDate(signal.created_at) }}</n-text>
</div> </div>
</template> </n-card-header>
<n-text class="signal-content" :depth="1">{{ signal.content }}</n-text> <n-text class="signal-content" :depth="1">{{ signal.content }}</n-text>
</n-list-item> </n-list-item>
</n-list> </n-list>
@ -493,7 +493,7 @@
:percentage="getTaskProgress(task)" :percentage="getTaskProgress(task)"
:color="getTaskTypeColor(task.type, true)" :color="getTaskTypeColor(task.type, true)"
rail-color="rgba(255,255,255,0.1)" rail-color="rgba(255,255,255,0.1)"
height="4" :height="4"
:show-indicator="false" :show-indicator="false"
style="margin-top: 8px;" style="margin-top: 8px;"
:class="{ 'progress-pulse': taskExecuting }" :class="{ 'progress-pulse': taskExecuting }"
@ -543,7 +543,7 @@
<n-modal v-model:show="showCompetitorModal"> <n-modal v-model:show="showCompetitorModal">
<n-card style="width: 400px" title="⚔️ 设定主要竞品(某漫、某画等)" :bordered="false" size="large" role="dialog" aria-modal="true" class="glass-modal"> <n-card style="width: 400px" title="⚔️ 设定主要竞品(某漫、某画等)" :bordered="false" size="large" role="dialog" aria-modal="true" class="glass-modal">
<n-space vertical> <n-space vertical>
<n-input v-for="(comp, index) in competitors" :key="index" v-model:value="competitors[index]" placeholder="输入竞品名称..." /> <n-input v-for="(_comp, index) in competitors" :key="index" v-model:value="competitors[index]" placeholder="输入竞品名称..." />
</n-space> </n-space>
<template #footer> <template #footer>
<n-button type="primary" block @click="saveCompetitors"> <n-button type="primary" block @click="saveCompetitors">
@ -573,11 +573,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, watch, nextTick, computed } from 'vue' import { ref, onMounted, onUnmounted, reactive, watch, computed } from 'vue'
import { import {
NLayout, NLayoutSider, NLayoutContent, NSpace, NCard, NButton, NList, NListItem, NTag, NText, NLayout, NLayoutSider, NLayoutContent, NSpace, NCard, NButton, NList, NListItem, NTag, NText,
NGrid, NGridItem, NDivider, NResult, NCheckbox, NThing, NScrollbar, NSpin, NModal, NForm, NFormItem, NGrid, NGridItem, NScrollbar, NSpin, NModal, NForm, NFormItem,
NInput, useMessage, NEmpty, NProgress, NIcon, NSteps, NStep, NStatistic NInput, useMessage, NProgress, NIcon, NSteps, NStep, NStatistic, NCardHeader
} from 'naive-ui' } from 'naive-ui'
import axios from 'axios' import axios from 'axios'
import VChart from 'vue-echarts' import VChart from 'vue-echarts'
@ -859,12 +859,14 @@ const factSnippets: Record<string, { title: string; source: string; content: str
const showFactSnippetModal = (type: string) => { const showFactSnippetModal = (type: string) => {
const snippet = factSnippets[type] || factSnippets['Whitepaper'] const snippet = factSnippets[type] || factSnippets['Whitepaper']
currentFactSnippet.value = { if (snippet) {
title: snippet.title, currentFactSnippet.value = {
source: snippet.source, title: snippet.title,
content: snippet.content source: snippet.source,
content: snippet.content
}
showFactSnippet.value = true
} }
showFactSnippet.value = true
} }
// --- Logic --- // --- Logic ---
@ -947,8 +949,8 @@ const getRelatedKnowledgeNodes = () => {
return Array.isArray(knowledgeNodes) ? knowledgeNodes : [] return Array.isArray(knowledgeNodes) ? knowledgeNodes : []
} }
const getTechStackType = (category: string) => { const getTechStackType = (category: string): 'default' | 'error' | 'success' | 'warning' | 'info' | 'primary' => {
const typeMap: Record<string, string> = { const typeMap: Record<string, 'default' | 'error' | 'success' | 'warning' | 'info' | 'primary'> = {
framework: 'success', framework: 'success',
model: 'error', model: 'error',
tool: 'info', tool: 'info',
@ -994,8 +996,8 @@ const getKnowledgeNodeIcon = (category: string) => {
return iconMap[category] || '🧠' return iconMap[category] || '🧠'
} }
const getKnowledgeNodeType = (category: string) => { const getKnowledgeNodeType = (category: string): 'default' | 'error' | 'success' | 'warning' | 'info' | 'primary' => {
const typeMap: Record<string, string> = { const typeMap: Record<string, 'default' | 'error' | 'success' | 'warning' | 'info' | 'primary'> = {
tech_stack: 'success', tech_stack: 'success',
competitor: 'error', competitor: 'error',
market: 'info' market: 'info'
@ -1163,13 +1165,13 @@ const getPriorityLabel = (priority: string) => {
} }
// Voting helpers // Voting helpers
const getModelVoteType = (modelName: string, strategicInfo: any) => { const getModelVoteType = (_modelName: string, strategicInfo: any) => {
// For now, since we don't have individual model results in the response, // For now, since we don't have individual model results in the response,
// we'll return a default type. This could be enhanced if we send individual results. // we'll return a default type. This could be enhanced if we send individual results.
return strategicInfo.is_strategic ? 'success' : 'default' return strategicInfo.is_strategic ? 'success' : 'default'
} }
const getModelVoteLabel = (modelName: string, strategicInfo: any) => { const getModelVoteLabel = (_modelName: string, strategicInfo: any) => {
// Similar to above, placeholder logic // Similar to above, placeholder logic
return strategicInfo.is_strategic ? '战略级' : '非战略' return strategicInfo.is_strategic ? '战略级' : '非战略'
} }
@ -1617,7 +1619,6 @@ const checkOllamaStatus = async () => {
// 本地 Ollama // 本地 Ollama
ollamaReady.value = data.status === 'ready' ollamaReady.value = data.status === 'ready'
const modelCount = data.model_count || 0 const modelCount = data.model_count || 0
const models = data.available_models || []
if (ollamaReady.value) { if (ollamaReady.value) {
message.success(`✅ Ollama 已就绪 (${modelCount} 个模型)`) message.success(`✅ Ollama 已就绪 (${modelCount} 个模型)`)
@ -1651,7 +1652,7 @@ const startOllama = async () => {
} else { } else {
message.error(response.data.message) message.error(response.data.message)
} }
} catch (error) { } catch (error: any) {
message.error('启动失败: ' + (error.response?.data?.detail || error.message)) message.error('启动失败: ' + (error.response?.data?.detail || error.message))
} finally { } finally {
startingOllama.value = false startingOllama.value = false