重构: 前端DRY收口+后端测试类型对齐

- 新增 useToast composable: 消除 AiChat/Settings/Projects 4处 toast 重复
  统一默认3000ms(Projects原4000ms为操作类提示保留参数覆盖)
- 新增 utils/json.ts parseJsonArray: 消除 parseStack/parseTags/ModuleNode
  3处JSON字符串数组解析重复
- 新增 utils/html.ts escapeHtml: 消除 useMarkdown/FilePreview 2处重复
- ProjectDetail score-bar 内联三元改用 scoreTier(消除最后一处阈值硬编码)
- ConversationSidebar 删除 formatTime 透传包装(直接用 formatRelative)
- 清理死代码: parseTs/stringifyError/ErrorSink/_Unused 改私有或删除
  wrapNakedDiff 改私有(无外部 import)
- ModuleNode shortPath 改名 truncatedPath(与 useToolCard.shortPath 语义不同)
This commit is contained in:
lxy
2026-07-03 00:18:21 +08:00
parent 249b3b9ea8
commit 3d8b755229
22 changed files with 163 additions and 124 deletions
+1 -4
View File
@@ -91,6 +91,7 @@ import { ref, watch, computed, onUnmounted } from 'vue'
import { moduleApi } from '@/api/module'
import hljs from 'highlight.js/lib/common'
import { useMarkdown, useRendered } from '@/composables/useMarkdown'
import { escapeHtml } from '@/utils/html'
const props = defineProps<{
moduleId: string
@@ -284,10 +285,6 @@ async function loadFile() {
}
}
function escapeHtml(s: string): string {
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
function joinPath(root: string, rel: string): string {
const sep = root.includes('\\') ? '\\' : '/'
const normRel = rel.split('/').join(sep)
+4 -11
View File
@@ -4,7 +4,7 @@
<span class="module-node__icon">📦</span>
<span class="module-node__name">{{ data.name }}</span>
</div>
<div class="module-node__path">{{ shortPath }}</div>
<div class="module-node__path">{{ truncatedPath }}</div>
<div v-if="stackList.length" class="module-node__tags">
<span v-for="s in stackList" :key="s" class="module-node__tag">{{ s }}</span>
</div>
@@ -13,6 +13,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import { parseJsonArray } from '@/utils/json'
const props = defineProps<{
data: {
@@ -24,21 +25,13 @@ const props = defineProps<{
selected?: boolean
}>()
const shortPath = computed(() => {
const truncatedPath = computed(() => {
const p = props.data?.path || ''
if (p.length <= 32) return p
return '...' + p.slice(-29)
})
const stackList = computed(() => {
if (!props.data?.stack) return []
try {
const parsed = JSON.parse(props.data.stack)
return Array.isArray(parsed) ? parsed.slice(0, 3) : []
} catch {
return []
}
})
const stackList = computed(() => parseJsonArray(props.data?.stack).slice(0, 3))
</script>
<style scoped>