Private
Public Access
1
0

优化:文件服务器安全重构+编辑器增强+搜索排序+更新面板Markdown渲染

- 路径校验提取validateFilePath+sentinel error替代字符串匹配
- requireUpdateAPI收敛7处重复nil检查
- 端口18765统一为8073,消除分散魔法数字
- CodeMirror添加搜索功能+滚动位置LRU缓存恢复
- 文件列表新增列排序+搜索过滤
- Toolbar重排:快捷访问内嵌+搜索框集成+历史改图标
- 重命名零闪烁:updateFilePath草稿迁移
- changelog用marked渲染+sanitizeHtml防XSS
- MigrateTabConfig扩展map驱动覆盖openclaw-manager→version迁移

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 21:53:31 +08:00
parent 691e38604f
commit 72fef3e56f
23 changed files with 614 additions and 257 deletions

View File

@@ -30,7 +30,7 @@
<div class="changelog-title">
{{ updateInfo.has_update ? '最新版本更新内容' : '当前版本更新内容' }}
</div>
<div class="changelog">{{ updateInfo.changelog }}</div>
<div class="changelog" v-html="renderChangelog(updateInfo.changelog)" />
</div>
</a-card>
@@ -92,8 +92,8 @@
:status="downloadStatus"
/>
<div class="progress-info">
<span>{{ formatFileSize(progressInfo.downloaded) }} / {{ formatFileSize(progressInfo.total) }}</span>
<span v-if="progressInfo.speed > 0">{{ formatSpeed(progressInfo.speed) }}</span>
<span>{{ updateStore.formatFileSize(progressInfo.downloaded) }} / {{ updateStore.formatFileSize(progressInfo.total) }}</span>
<span v-if="progressInfo.speed > 0">{{ updateStore.formatSpeed(progressInfo.speed) }}</span>
</div>
</div>
@@ -118,6 +118,8 @@ import { Message, Modal } from '@arco-design/web-vue'
import { storeToRefs } from 'pinia'
import { IconHistory } from '@arco-design/web-vue/es/icon'
import { useUpdateStore } from '../stores/update'
import { marked } from '../utils/markedExtensions'
import { sanitizeHtml } from '@/utils/fileUtils'
// Emits
defineEmits(['open-version-history'])
@@ -134,13 +136,10 @@ const lastCheckTime = ref('-')
const installResult = ref(null)
const downloadedFile = ref(null)
// 工具函数
const formatFileSize = (bytes) => {
return updateStore.formatFileSize(bytes)
}
const formatSpeed = (bytesPerSecond) => {
return updateStore.formatSpeed(bytesPerSecond)
/** 渲染 changelogMarkdown → HTML */
function renderChangelog(text: string): string {
if (!text) return ''
try { return sanitizeHtml(marked.parse(text) as string) } catch { return text }
}
// 加载当前版本
@@ -283,29 +282,70 @@ onUnmounted(() => {
}
.changelog-section {
margin-top: 16px;
margin-top: 12px;
}
.changelog-title {
font-size: 14px;
font-size: 13px;
font-weight: 500;
color: var(--color-text-1);
margin-bottom: 8px;
color: var(--color-text-2);
margin-bottom: 6px;
}
.changelog {
background: var(--color-fill-2);
padding: 12px;
background: var(--color-fill-1);
padding: 10px 12px;
border-radius: 4px;
white-space: pre-wrap;
margin: 8px 0;
max-height: 200px;
margin: 0;
max-height: 280px;
overflow-y: auto;
font-size: 13px;
line-height: 1.6;
font-size: 12px;
line-height: 1.65;
color: var(--color-text-2);
}
.changelog :deep(h4) {
font-size: 12px;
font-weight: 600;
color: var(--color-text-1);
margin: 8px 0 3px;
}
.changelog :deep(h4:first-child) {
margin-top: 0;
}
.changelog :deep(ul) {
list-style: none;
padding: 0;
margin: 1px 0;
}
.changelog :deep(li) {
position: relative;
padding-left: 14px;
margin: 1px 0;
}
.changelog :deep(li::before) {
content: '·';
position: absolute;
left: 4px;
color: var(--color-text-4);
font-weight: bold;
}
.changelog :deep(code) {
background: var(--color-fill-3);
padding: 0 4px;
border-radius: 3px;
font-size: 11px;
}
.changelog :deep(p) {
margin: 2px 0;
}
.download-progress {
margin-top: 16px;
padding: 16px;