优化:CSV编辑模式/PDF导出重构/收藏夹bug修复/移除useLocalStorage
- FileEditorPanel: CSV新增预览/编辑切换、PDF导出;提取openPrintWindow公共函数 - useFavorites: 修复find回调中fav变量遮蔽bug(f.path)、sort改为副本排序 - useFavoriteFiles/DeviceTest: 移除useLocalStorage抽象层,直接管理localStorage - system.ts: createDir/createFile签名改为(parent, name)两参数拼接 - useFileOperations: createNewFile移除无用content参数 - 清理OpenClaw相关Wails绑定
This commit is contained in:
@@ -110,23 +110,25 @@ export async function deletePath(path: string): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建目录
|
* 创建目录(parentPath + dirname 拼接为完整路径)
|
||||||
*/
|
*/
|
||||||
export async function createDir(path: string): Promise<any> {
|
export async function createDir(parentPath: string, dirname: string): Promise<any> {
|
||||||
if (!window.go?.main?.App?.CreateDir) {
|
if (!window.go?.main?.App?.CreateDir) {
|
||||||
throw new Error('CreateDir API 不可用')
|
throw new Error('CreateDir API 不可用')
|
||||||
}
|
}
|
||||||
return await window.go.main.App.CreateDir(path)
|
const fullPath = parentPath.replace(/\/$/, '') + '/' + dirname
|
||||||
|
return await window.go.main.App.CreateDir(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文件
|
* 创建文件(dirPath + filename 拼接为完整路径)
|
||||||
*/
|
*/
|
||||||
export async function createFile(path: string): Promise<any> {
|
export async function createFile(dirPath: string, filename: string): Promise<any> {
|
||||||
if (!window.go?.main?.App?.CreateFile) {
|
if (!window.go?.main?.App?.CreateFile) {
|
||||||
throw new Error('CreateFile API 不可用')
|
throw new Error('CreateFile API 不可用')
|
||||||
}
|
}
|
||||||
return await window.go.main.App.CreateFile(path)
|
const fullPath = dirPath.replace(/\/$/, '') + '/' + filename
|
||||||
|
return await window.go.main.App.CreateFile(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -221,7 +221,6 @@ import { formatBytes, sortFileList } from '@/utils/fileUtils'
|
|||||||
// 导入 composables
|
// 导入 composables
|
||||||
import { useFileOperations } from '@/composables/useFileOperations'
|
import { useFileOperations } from '@/composables/useFileOperations'
|
||||||
import { useFavoriteFiles } from '@/composables/useFavoriteFiles'
|
import { useFavoriteFiles } from '@/composables/useFavoriteFiles'
|
||||||
import { useLocalStorage } from '@/composables/useLocalStorage'
|
|
||||||
|
|
||||||
// ========== 使用 Composables ==========
|
// ========== 使用 Composables ==========
|
||||||
|
|
||||||
@@ -251,20 +250,21 @@ const {
|
|||||||
} = useFavoriteFiles(STORAGE_KEYS.DEVICE_TEST.FAVORITE_FILES)
|
} = useFavoriteFiles(STORAGE_KEYS.DEVICE_TEST.FAVORITE_FILES)
|
||||||
|
|
||||||
// localStorage管理
|
// localStorage管理
|
||||||
const { storedValue: fileContentHeight } = useLocalStorage(
|
const fileContentHeight = ref(DEFAULTS.DEFAULT_CONTENT_HEIGHT)
|
||||||
STORAGE_KEYS.DEVICE_TEST.FILE_CONTENT_HEIGHT,
|
const filePanelWidth = ref({ left: 50, right: 50 })
|
||||||
DEFAULTS.DEFAULT_CONTENT_HEIGHT
|
const pathHistory = ref([])
|
||||||
)
|
|
||||||
|
|
||||||
const { storedValue: filePanelWidth } = useLocalStorage(
|
// 从 localStorage 恢复
|
||||||
STORAGE_KEYS.DEVICE_TEST.PANEL_WIDTH,
|
try {
|
||||||
{ left: 50, right: 50 }
|
const h = localStorage.getItem(STORAGE_KEYS.DEVICE_TEST.FILE_CONTENT_HEIGHT)
|
||||||
)
|
if (h) fileContentHeight.value = JSON.parse(h)
|
||||||
|
const w = localStorage.getItem(STORAGE_KEYS.DEVICE_TEST.PANEL_WIDTH)
|
||||||
const { storedValue: pathHistory } = useLocalStorage(
|
if (w) filePanelWidth.value = JSON.parse(w)
|
||||||
STORAGE_KEYS.DEVICE_TEST.PATH_HISTORY,
|
const p = localStorage.getItem(STORAGE_KEYS.DEVICE_TEST.PATH_HISTORY)
|
||||||
[]
|
if (p) pathHistory.value = JSON.parse(p)
|
||||||
)
|
} catch (e) {
|
||||||
|
console.error('[DeviceTest] 加载 localStorage 失败:', e)
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 立即清理旧的文件内容缓存 ==========
|
// ========== 立即清理旧的文件内容缓存 ==========
|
||||||
// 在组件初始化之前清理,防止加载大文件导致空白
|
// 在组件初始化之前清理,防止加载大文件导致空白
|
||||||
@@ -387,6 +387,11 @@ const addToHistory = (path) => {
|
|||||||
if (pathHistory.value.length > 20) {
|
if (pathHistory.value.length > 20) {
|
||||||
pathHistory.value = pathHistory.value.slice(0, 20)
|
pathHistory.value = pathHistory.value.slice(0, 20)
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEYS.DEVICE_TEST.PATH_HISTORY, JSON.stringify(pathHistory.value))
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[DeviceTest] 保存路径历史失败:', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 文件选择(重写以添加历史记录) ==========
|
// ========== 文件选择(重写以添加历史记录) ==========
|
||||||
|
|||||||
@@ -108,14 +108,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- CSV 预览 -->
|
<!-- CSV 预览/编辑 -->
|
||||||
<div v-else-if="config.isCsvFile" class="office-preview">
|
<div v-else-if="config.isCsvFile" class="csv-preview-wrapper">
|
||||||
<div class="office-preview-container" ref="csvPreviewRef">
|
<div class="preview-mode-switch">
|
||||||
|
<a-tooltip v-if="config.isEditMode && config.canResetContent" position="left" content="恢复原始内容">
|
||||||
|
<a-button type="outline" size="small" @click="handleReset">
|
||||||
|
<template #icon><icon-undo /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip v-if="config.canSaveFile" position="left" content="保存 (Ctrl+S)">
|
||||||
|
<a-button type="primary" size="small" @click="handleSave">
|
||||||
|
<template #icon><icon-save /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip v-if="!config.isEditMode" position="left" content="导出 PDF">
|
||||||
|
<a-button type="outline" size="small" @click="handleExportCsvPDF">
|
||||||
|
<template #icon><icon-file-pdf /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip position="left" :content="getModeSwitchTooltip()">
|
||||||
|
<a-button type="primary" size="small" @click="handleToggleEditMode">
|
||||||
|
<template #icon>
|
||||||
|
<icon-eye v-if="config.isEditMode" />
|
||||||
|
<icon-edit v-else />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 预览模式 -->
|
||||||
|
<div v-if="!config.isEditMode" class="office-preview-container" ref="csvPreviewRef">
|
||||||
<a-spin v-if="config.officeLoading" :loading="true" tip="加载中...">
|
<a-spin v-if="config.officeLoading" :loading="true" tip="加载中...">
|
||||||
<div class="loading-placeholder"></div>
|
<div class="loading-placeholder"></div>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
<a-alert v-else-if="config.officeError" type="error" :message="config.officeError" />
|
<a-alert v-else-if="config.officeError" type="error" :message="config.officeError" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑模式 -->
|
||||||
|
<div v-else class="csv-edit-wrapper">
|
||||||
|
<AsyncCodeEditor
|
||||||
|
:model-value="config.fileContent"
|
||||||
|
:file-extension="config.currentFileExtension"
|
||||||
|
@update:model-value="handleContentUpdate"
|
||||||
|
class="code-editor"
|
||||||
|
/>
|
||||||
|
<div class="resize-handle-v" @mousedown="handleStartResize" title="拖拽调整高度">
|
||||||
|
<div class="resize-dots"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HTML 预览/编辑 -->
|
<!-- HTML 预览/编辑 -->
|
||||||
@@ -307,7 +347,7 @@
|
|||||||
import { computed, watch, nextTick, defineAsyncComponent, ref, onMounted, onUnmounted } from 'vue'
|
import { computed, watch, nextTick, defineAsyncComponent, ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { IconSave, IconEdit, IconEye, IconUndo, IconCopy, IconFilePdf, IconFullscreen, IconFullscreenExit } from '@arco-design/web-vue/es/icon'
|
import { IconSave, IconEdit, IconEye, IconUndo, IconCopy, IconFilePdf, IconFullscreen, IconFullscreenExit } from '@arco-design/web-vue/es/icon'
|
||||||
import { getFileName } from '@/utils/fileUtils'
|
import { getFileName, escapeHtml } from '@/utils/fileUtils'
|
||||||
import type { FileEditorPanelConfig } from '@/types/file-system'
|
import type { FileEditorPanelConfig } from '@/types/file-system'
|
||||||
import { renderMermaidDiagrams } from '@/utils/markedExtensions'
|
import { renderMermaidDiagrams } from '@/utils/markedExtensions'
|
||||||
import { previewExcel, previewWord, previewCsv } from '@/utils/filePreviewHandlers'
|
import { previewExcel, previewWord, previewCsv } from '@/utils/filePreviewHandlers'
|
||||||
@@ -442,163 +482,98 @@ const handleImageError = () => {
|
|||||||
emit('imageError')
|
emit('imageError')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Markdown PDF 导出处理
|
// 打印窗口导出 PDF 公共函数
|
||||||
const handleExportPDF = async () => {
|
const openPrintWindow = (title: string, bodyHtml: string, extraStyle = '') => {
|
||||||
try {
|
const printWindow = window.open('', '_blank')
|
||||||
// 获取 Markdown 预览容器
|
if (!printWindow) {
|
||||||
const markdownContent = markdownPreviewRef.value
|
Message.error('无法打开打印窗口,请检查浏览器设置')
|
||||||
if (!markdownContent) {
|
return
|
||||||
Message.error('无法获取 Markdown 内容')
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开打印窗口
|
printWindow.document.write(`
|
||||||
const printWindow = window.open('', '_blank')
|
<!DOCTYPE html>
|
||||||
if (!printWindow) {
|
<html>
|
||||||
Message.error('无法打开打印窗口,请检查浏览器设置')
|
<head>
|
||||||
return
|
<meta charset="UTF-8">
|
||||||
}
|
<title>${escapeHtml(title)}</title>
|
||||||
|
<style>
|
||||||
// 设置打印样式
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
const style = `
|
|
||||||
<style>
|
|
||||||
@media print {
|
|
||||||
body {
|
body {
|
||||||
font-size: 12pt;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
line-height: 1.4;
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin: 0;
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
@media print {
|
||||||
.no-print {
|
body { padding: 0; }
|
||||||
display: none !important;
|
@page { margin: 15mm; size: A4; }
|
||||||
}
|
}
|
||||||
|
${extraStyle}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>${bodyHtml}</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
printWindow.document.close()
|
||||||
|
|
||||||
.markdown-content {
|
setTimeout(() => { printWindow.print() }, 500)
|
||||||
max-width: 100%;
|
Message.success('PDF 导出窗口已打开')
|
||||||
margin: 0;
|
}
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h1 {
|
// Markdown PDF 导出处理
|
||||||
font-size: 24pt;
|
const handleExportPDF = async () => {
|
||||||
margin-bottom: 12pt;
|
const markdownContent = markdownPreviewRef.value
|
||||||
border-bottom: 2px solid #333;
|
if (!markdownContent) {
|
||||||
}
|
Message.error('无法获取 Markdown 内容')
|
||||||
|
return
|
||||||
.markdown-content h2 {
|
|
||||||
font-size: 18pt;
|
|
||||||
margin-bottom: 10pt;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content h3 {
|
|
||||||
font-size: 14pt;
|
|
||||||
margin-bottom: 8pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content p {
|
|
||||||
margin-bottom: 10pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content ul,
|
|
||||||
.markdown-content ol {
|
|
||||||
margin-bottom: 10pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content li {
|
|
||||||
margin-bottom: 4pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-bottom: 12pt;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content th,
|
|
||||||
.markdown-content td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content th {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content blockquote {
|
|
||||||
border-left: 4px solid #ddd;
|
|
||||||
margin: 16px 0;
|
|
||||||
padding: 10px 20px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content code {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content pre {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-content pre code {
|
|
||||||
background-color: transparent;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 优化屏幕显示 */
|
|
||||||
.markdown-content {
|
|
||||||
background: white;
|
|
||||||
padding: 20px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
`
|
|
||||||
|
|
||||||
// 构建打印页面
|
|
||||||
printWindow.document.write(`
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>${props.config.currentFileName || 'Markdown 导出 PDF'}</title>
|
|
||||||
${style}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="markdown-content">
|
|
||||||
${markdownContent.innerHTML}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`)
|
|
||||||
printWindow.document.close()
|
|
||||||
|
|
||||||
// 延迟一点时间让样式加载完成
|
|
||||||
setTimeout(() => {
|
|
||||||
printWindow.print()
|
|
||||||
// 不关闭窗口,让用户可以手动关闭或继续打印
|
|
||||||
}, 500)
|
|
||||||
|
|
||||||
Message.success('PDF 导出窗口已打开')
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[handleExportPDF] 导出失败:', error)
|
|
||||||
Message.error('PDF 导出失败,请重试')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openPrintWindow(
|
||||||
|
props.config.currentFileName || 'Markdown 导出 PDF',
|
||||||
|
`<div class="markdown-content">${markdownContent.innerHTML}</div>`,
|
||||||
|
`
|
||||||
|
.markdown-content { background: white; padding: 20px; max-width: 100%; }
|
||||||
|
@media print {
|
||||||
|
.markdown-content { padding: 0; }
|
||||||
|
.markdown-content h1 { font-size: 24pt; margin-bottom: 12pt; border-bottom: 2px solid #333; }
|
||||||
|
.markdown-content h2 { font-size: 18pt; margin-bottom: 10pt; border-bottom: 1px solid #ccc; }
|
||||||
|
.markdown-content h3 { font-size: 14pt; margin-bottom: 8pt; }
|
||||||
|
.markdown-content p { margin-bottom: 10pt; }
|
||||||
|
.markdown-content ul, .markdown-content ol { margin-bottom: 10pt; }
|
||||||
|
.markdown-content li { margin-bottom: 4pt; }
|
||||||
|
.markdown-content table { border-collapse: collapse; margin-bottom: 12pt; width: 100%; }
|
||||||
|
.markdown-content th, .markdown-content td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||||
|
.markdown-content th { background-color: #f5f5f5; font-weight: bold; }
|
||||||
|
.markdown-content img { max-width: 100%; height: auto; }
|
||||||
|
.markdown-content blockquote { border-left: 4px solid #ddd; margin: 16px 0; padding: 10px 20px; color: #666; }
|
||||||
|
.markdown-content code { background-color: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-family: 'Consolas', 'Monaco', monospace; }
|
||||||
|
.markdown-content pre { background-color: #f5f5f5; padding: 12px; border-radius: 4px; overflow-x: auto; }
|
||||||
|
.markdown-content pre code { background-color: transparent; padding: 0; }
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSV PDF 导出处理
|
||||||
|
const handleExportCsvPDF = async () => {
|
||||||
|
const csvContent = csvPreviewRef.value?.querySelector('.csv-content table')
|
||||||
|
if (!csvContent) {
|
||||||
|
Message.error('无法获取 CSV 内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openPrintWindow(
|
||||||
|
props.config.currentFileName || 'CSV 导出 PDF',
|
||||||
|
csvContent.outerHTML,
|
||||||
|
`
|
||||||
|
table { border-collapse: collapse; width: 100%; margin-bottom: 12pt; page-break-inside: auto; }
|
||||||
|
tr { page-break-inside: avoid; page-break-after: auto; }
|
||||||
|
th, td { border: 1px solid #dfe2e5; padding: 6px 10px; text-align: left; white-space: nowrap; }
|
||||||
|
th { background-color: #f6f8fa; font-weight: 600; }
|
||||||
|
tr:nth-child(even) { background-color: #f8f8f8; }
|
||||||
|
`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听模式切换,切换到预览模式时渲染 Mermaid 图表
|
// 监听模式切换,切换到预览模式时渲染 Mermaid 图表
|
||||||
@@ -628,9 +603,9 @@ watch(() => [props.config.isWordFile, props.config.currentFileFullPath] as const
|
|||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
// 监听 CSV 文件变化,触发预览
|
// 监听 CSV 文件变化或编辑模式切换,触发预览
|
||||||
watch(() => [props.config.isCsvFile, props.config.currentFileFullPath] as const, async ([isCsv, filePath]) => {
|
watch(() => [props.config.isCsvFile, props.config.currentFileFullPath, props.config.isEditMode] as const, async ([isCsv, filePath, isEditMode]) => {
|
||||||
if (isCsv && filePath) {
|
if (isCsv && filePath && !isEditMode) {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
if (csvPreviewRef.value) {
|
if (csvPreviewRef.value) {
|
||||||
await loadCsvPreview(filePath)
|
await loadCsvPreview(filePath)
|
||||||
@@ -704,9 +679,9 @@ const loadCsvPreview = async (filePath: string) => {
|
|||||||
try {
|
try {
|
||||||
csvPreviewRef.value.innerHTML = '<div class="loading-hint">加载中...</div>'
|
csvPreviewRef.value.innerHTML = '<div class="loading-hint">加载中...</div>'
|
||||||
|
|
||||||
const fileUrl = props.config.previewUrl
|
const blob = props.config.fileContent && !props.config.isBinaryFile
|
||||||
const response = await fetch(fileUrl)
|
? new Blob([props.config.fileContent], { type: 'text/csv' })
|
||||||
const blob = await response.blob()
|
: await (await fetch(props.config.previewUrl)).blob()
|
||||||
const file = new File([blob], getFileName(filePath), { type: 'text/csv' })
|
const file = new File([blob], getFileName(filePath), { type: 'text/csv' })
|
||||||
|
|
||||||
const result = await previewCsv(file, csvPreviewRef.value)
|
const result = await previewCsv(file, csvPreviewRef.value)
|
||||||
@@ -1203,6 +1178,22 @@ onUnmounted(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSV 预览/编辑 */
|
||||||
|
.csv-preview-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.csv-edit-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.office-preview-container {
|
.office-preview-container {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function useFavorites() {
|
|||||||
* 排序收藏列表:置顶项在前(按 pinnedAt 降序),非置顶项按添加时间降序
|
* 排序收藏列表:置顶项在前(按 pinnedAt 降序),非置顶项按添加时间降序
|
||||||
*/
|
*/
|
||||||
const sortFavorites = () => {
|
const sortFavorites = () => {
|
||||||
favorites.value.sort((a, b) => {
|
favorites.value = [...favorites.value].sort((a, b) => {
|
||||||
// 置顶项优先
|
// 置顶项优先
|
||||||
if (a.pinnedAt && !b.pinnedAt) return -1
|
if (a.pinnedAt && !b.pinnedAt) return -1
|
||||||
if (!a.pinnedAt && b.pinnedAt) return 1
|
if (!a.pinnedAt && b.pinnedAt) return 1
|
||||||
@@ -135,7 +135,7 @@ export function useFavorites() {
|
|||||||
*/
|
*/
|
||||||
const togglePin = (path: string) => {
|
const togglePin = (path: string) => {
|
||||||
const normalizedPath = normalizePath(path)
|
const normalizedPath = normalizePath(path)
|
||||||
const fav = favorites.value.find(f => normalizePath(fav.path) === normalizedPath)
|
const fav = favorites.value.find(f => normalizePath(f.path) === normalizedPath)
|
||||||
if (fav) {
|
if (fav) {
|
||||||
fav.pinnedAt = fav.pinnedAt ? undefined : Date.now()
|
fav.pinnedAt = fav.pinnedAt ? undefined : Date.now()
|
||||||
sortFavorites()
|
sortFavorites()
|
||||||
@@ -148,7 +148,7 @@ export function useFavorites() {
|
|||||||
*/
|
*/
|
||||||
const isPinned = (path: string): boolean => {
|
const isPinned = (path: string): boolean => {
|
||||||
const normalizedPath = normalizePath(path)
|
const normalizedPath = normalizePath(path)
|
||||||
const fav = favorites.value.find(f => normalizePath(fav.path) === normalizedPath)
|
const fav = favorites.value.find(f => normalizePath(f.path) === normalizedPath)
|
||||||
return !!fav?.pinnedAt
|
return !!fav?.pinnedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ export function useFavorites() {
|
|||||||
*/
|
*/
|
||||||
const updateFavoritePath = (oldPath: string, newName: string) => {
|
const updateFavoritePath = (oldPath: string, newName: string) => {
|
||||||
const normalizedOld = normalizePath(oldPath)
|
const normalizedOld = normalizePath(oldPath)
|
||||||
const fav = favorites.value.find(f => normalizePath(fav.path) === normalizedOld)
|
const fav = favorites.value.find(f => normalizePath(f.path) === normalizedOld)
|
||||||
if (!fav) return
|
if (!fav) return
|
||||||
|
|
||||||
const separator = getPathSeparator(oldPath)
|
const separator = getPathSeparator(oldPath)
|
||||||
|
|||||||
@@ -99,11 +99,10 @@ export function useFileOperations(options: UseFileOperationsOptions = {}) {
|
|||||||
*/
|
*/
|
||||||
const createNewFile = async (
|
const createNewFile = async (
|
||||||
dirPath: string,
|
dirPath: string,
|
||||||
filename: string,
|
filename: string
|
||||||
content: string = ''
|
|
||||||
): Promise<FileItem> => {
|
): Promise<FileItem> => {
|
||||||
try {
|
try {
|
||||||
const result = await createFile(dirPath, filename, content)
|
const result = await createFile(dirPath, filename)
|
||||||
onSuccess?.('createFile', { dirPath, filename, result })
|
onSuccess?.('createFile', { dirPath, filename, result })
|
||||||
return result as FileItem
|
return result as FileItem
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { useLocalStorage } from './useLocalStorage'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收藏夹 composable
|
* 收藏夹 composable
|
||||||
@@ -40,11 +39,27 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
onRemove = () => {},
|
onRemove = () => {},
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
// 使用 localStorage composable 管理收藏列表
|
// 收藏列表
|
||||||
const { storedValue: favoriteFiles, load, save } = useLocalStorage(
|
const favoriteFiles = ref([])
|
||||||
storageKey,
|
|
||||||
[]
|
const load = () => {
|
||||||
)
|
try {
|
||||||
|
const stored = localStorage.getItem(storageKey)
|
||||||
|
if (stored) {
|
||||||
|
favoriteFiles.value = JSON.parse(stored)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载收藏列表失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = (data) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(storageKey, JSON.stringify(data || favoriteFiles.value))
|
||||||
|
} catch (e) {
|
||||||
|
console.error('保存收藏列表失败:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断文件/目录是否已收藏
|
* 判断文件/目录是否已收藏
|
||||||
@@ -70,6 +85,7 @@ export function useFavoriteFiles(storageKey, options = {}) {
|
|||||||
const timeB = b.addedAt || 0
|
const timeB = b.addedAt || 0
|
||||||
return timeB - timeA // 倒序:最新的在上面
|
return timeB - timeA // 倒序:最新的在上面
|
||||||
})
|
})
|
||||||
|
save()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
8
web/src/wailsjs/wailsjs/go/main/App.d.ts
vendored
8
web/src/wailsjs/wailsjs/go/main/App.d.ts
vendored
@@ -60,12 +60,6 @@ export function GetIndexes(arg1:number,arg2:string,arg3:string):Promise<Array<Re
|
|||||||
|
|
||||||
export function GetMemoryInfo():Promise<Record<string, any>>;
|
export function GetMemoryInfo():Promise<Record<string, any>>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function GetRecycleBinEntries():Promise<Array<Record<string, any>>>;
|
export function GetRecycleBinEntries():Promise<Array<Record<string, any>>>;
|
||||||
|
|
||||||
export function GetResultHistory(arg1:any,arg2:string,arg3:number,arg4:number):Promise<Record<string, any>>;
|
export function GetResultHistory(arg1:any,arg2:string,arg3:number,arg4:number):Promise<Record<string, any>>;
|
||||||
@@ -118,7 +112,6 @@ export function SaveAppConfig(arg1:main.SaveAppConfigRequest):Promise<Record<str
|
|||||||
|
|
||||||
export function SaveDbConnection(arg1:api.SaveConnectionRequest):Promise<void>;
|
export function SaveDbConnection(arg1:api.SaveConnectionRequest):Promise<void>;
|
||||||
|
|
||||||
|
|
||||||
export function SaveResult(arg1:number,arg2:string,arg3:string,arg4:string,arg5:any,arg6:Array<string>,arg7:number,arg8:number):Promise<Record<string, any>>;
|
export function SaveResult(arg1:number,arg2:string,arg3:string,arg4:string,arg5:any,arg6:Array<string>,arg7:number,arg8:number):Promise<Record<string, any>>;
|
||||||
|
|
||||||
export function SaveSqlTabs(arg1:Array<Record<string, any>>):Promise<void>;
|
export function SaveSqlTabs(arg1:Array<Record<string, any>>):Promise<void>;
|
||||||
@@ -127,7 +120,6 @@ export function SelectPDFSaveDirectory():Promise<string>;
|
|||||||
|
|
||||||
export function SetUpdateConfig(arg1:boolean,arg2:number,arg3:string):Promise<Record<string, any>>;
|
export function SetUpdateConfig(arg1:boolean,arg2:number,arg3:string):Promise<Record<string, any>>;
|
||||||
|
|
||||||
|
|
||||||
export function TestDbConnection(arg1:number):Promise<void>;
|
export function TestDbConnection(arg1:number):Promise<void>;
|
||||||
|
|
||||||
export function TestDbConnectionWithParams(arg1:api.TestConnectionRequest):Promise<void>;
|
export function TestDbConnectionWithParams(arg1:api.TestConnectionRequest):Promise<void>;
|
||||||
|
|||||||
@@ -114,30 +114,6 @@ export function GetMemoryInfo() {
|
|||||||
return window['go']['main']['App']['GetMemoryInfo']();
|
return window['go']['main']['App']['GetMemoryInfo']();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetOpenClawConfig() {
|
|
||||||
return window['go']['main']['App']['GetOpenClawConfig']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetOpenClawModelUsage() {
|
|
||||||
return window['go']['main']['App']['GetOpenClawModelUsage']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetOpenClawSessionHistory(arg1, arg2) {
|
|
||||||
return window['go']['main']['App']['GetOpenClawSessionHistory'](arg1, arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetOpenClawSessions() {
|
|
||||||
return window['go']['main']['App']['GetOpenClawSessions']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetOpenClawStatus() {
|
|
||||||
return window['go']['main']['App']['GetOpenClawStatus']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetOpenClawSystemUsage() {
|
|
||||||
return window['go']['main']['App']['GetOpenClawSystemUsage']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetRecycleBinEntries() {
|
export function GetRecycleBinEntries() {
|
||||||
return window['go']['main']['App']['GetRecycleBinEntries']();
|
return window['go']['main']['App']['GetRecycleBinEntries']();
|
||||||
}
|
}
|
||||||
@@ -242,10 +218,6 @@ export function SaveDbConnection(arg1) {
|
|||||||
return window['go']['main']['App']['SaveDbConnection'](arg1);
|
return window['go']['main']['App']['SaveDbConnection'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SaveOpenClawConfig(arg1) {
|
|
||||||
return window['go']['main']['App']['SaveOpenClawConfig'](arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SaveResult(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
|
export function SaveResult(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
|
||||||
return window['go']['main']['App']['SaveResult'](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
return window['go']['main']['App']['SaveResult'](arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||||
}
|
}
|
||||||
@@ -262,10 +234,6 @@ export function SetUpdateConfig(arg1, arg2, arg3) {
|
|||||||
return window['go']['main']['App']['SetUpdateConfig'](arg1, arg2, arg3);
|
return window['go']['main']['App']['SetUpdateConfig'](arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SwitchOpenClawSession(arg1) {
|
|
||||||
return window['go']['main']['App']['SwitchOpenClawSession'](arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TestDbConnection(arg1) {
|
export function TestDbConnection(arg1) {
|
||||||
return window['go']['main']['App']['TestDbConnection'](arg1);
|
return window['go']['main']['App']['TestDbConnection'](arg1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user