feat(web): Skills管理页面新增markdown格式的预览
This commit is contained in:
parent
c288c04a82
commit
30df84efc1
@ -125,17 +125,30 @@
|
|||||||
<span>{{ selectedPath || '未选择文件' }}</span>
|
<span>{{ selectedPath || '未选择文件' }}</span>
|
||||||
<span v-if="canSave" class="save-hint">●</span>
|
<span v-if="canSave" class="save-hint">●</span>
|
||||||
</div>
|
</div>
|
||||||
<a-button
|
<div class="header-actions">
|
||||||
type="primary"
|
<a-button
|
||||||
size="small"
|
v-if="isMarkdownFile && selectedPath"
|
||||||
@click="saveCurrentFile"
|
size="small"
|
||||||
:disabled="!canSave"
|
@click="viewMode = viewMode === 'edit' ? 'preview' : 'edit'"
|
||||||
:loading="savingFile"
|
class="lucide-icon-btn view-toggle-btn"
|
||||||
class="lucide-icon-btn"
|
:title="viewMode === 'edit' ? '预览' : '编辑'"
|
||||||
>
|
>
|
||||||
<Save :size="14" />
|
<Eye v-if="viewMode === 'edit'" :size="14" />
|
||||||
<span>保存</span>
|
<Edit3 v-else :size="14" />
|
||||||
</a-button>
|
<span>{{ viewMode === 'edit' ? '预览' : '编辑' }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="saveCurrentFile"
|
||||||
|
:disabled="!canSave"
|
||||||
|
:loading="savingFile"
|
||||||
|
class="lucide-icon-btn"
|
||||||
|
>
|
||||||
|
<Save :size="14" />
|
||||||
|
<span>保存</span>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-main">
|
<div class="editor-main">
|
||||||
<a-empty
|
<a-empty
|
||||||
@ -143,12 +156,21 @@
|
|||||||
description="选择文件以开始编辑"
|
description="选择文件以开始编辑"
|
||||||
class="mt-40"
|
class="mt-40"
|
||||||
/>
|
/>
|
||||||
<a-textarea
|
<template v-else>
|
||||||
v-else
|
<MdPreview
|
||||||
v-model:value="fileContent"
|
v-if="viewMode === 'preview'"
|
||||||
class="pure-editor"
|
:modelValue="fileContent"
|
||||||
spellcheck="false"
|
:theme="theme"
|
||||||
/>
|
previewTheme="github"
|
||||||
|
class="markdown-preview flat-md-preview"
|
||||||
|
/>
|
||||||
|
<a-textarea
|
||||||
|
v-else
|
||||||
|
v-model:value="fileContent"
|
||||||
|
class="pure-editor"
|
||||||
|
spellcheck="false"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -237,11 +259,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||||
import { message, Modal } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import 'dayjs/locale/zh-cn'
|
import 'dayjs/locale/zh-cn'
|
||||||
|
import { MdPreview } from 'md-editor-v3'
|
||||||
|
import 'md-editor-v3/lib/preview.css'
|
||||||
|
import { useThemeStore } from '@/stores/theme'
|
||||||
import {
|
import {
|
||||||
Upload,
|
Upload,
|
||||||
RotateCw,
|
RotateCw,
|
||||||
@ -255,7 +280,9 @@ import {
|
|||||||
File,
|
File,
|
||||||
Search,
|
Search,
|
||||||
Box,
|
Box,
|
||||||
FileCode
|
FileCode,
|
||||||
|
Eye,
|
||||||
|
Edit3
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { skillApi } from '@/apis/skill_api'
|
import { skillApi } from '@/apis/skill_api'
|
||||||
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
||||||
@ -263,6 +290,9 @@ import FileTreeComponent from '@/components/FileTreeComponent.vue'
|
|||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
dayjs.locale('zh-cn')
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
|
const themeStore = useThemeStore()
|
||||||
|
const theme = computed(() => (themeStore.isDark ? 'dark' : 'light'))
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const importing = ref(false)
|
const importing = ref(false)
|
||||||
const savingFile = ref(false)
|
const savingFile = ref(false)
|
||||||
@ -270,6 +300,7 @@ const creatingNode = ref(false)
|
|||||||
const savingDependencies = ref(false)
|
const savingDependencies = ref(false)
|
||||||
const activeTab = ref('editor')
|
const activeTab = ref('editor')
|
||||||
const searchQuery = ref('')
|
const searchQuery = ref('')
|
||||||
|
const viewMode = ref('edit') // 'edit' | 'preview'
|
||||||
|
|
||||||
const skills = ref([])
|
const skills = ref([])
|
||||||
const currentSkill = ref(null)
|
const currentSkill = ref(null)
|
||||||
@ -303,6 +334,18 @@ const canSave = computed(() => {
|
|||||||
return fileContent.value !== originalFileContent.value
|
return fileContent.value !== originalFileContent.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isMarkdownFile = computed(() => {
|
||||||
|
if (!selectedPath.value) return false
|
||||||
|
return selectedPath.value.toLowerCase().endsWith('.md')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 切换到非markdown文件时重置为编辑模式
|
||||||
|
watch(selectedPath, (newPath) => {
|
||||||
|
if (newPath && !newPath.toLowerCase().endsWith('.md')) {
|
||||||
|
viewMode.value = 'edit'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const formatRelativeTime = (time) => (time ? dayjs(time).fromNow() : '-')
|
const formatRelativeTime = (time) => (time ? dayjs(time).fromNow() : '-')
|
||||||
|
|
||||||
const toolDependencyOptions = computed(() =>
|
const toolDependencyOptions = computed(() =>
|
||||||
@ -336,6 +379,7 @@ const resetFileState = () => {
|
|||||||
expandedKeys.value = []
|
expandedKeys.value = []
|
||||||
fileContent.value = ''
|
fileContent.value = ''
|
||||||
originalFileContent.value = ''
|
originalFileContent.value = ''
|
||||||
|
viewMode.value = 'edit'
|
||||||
}
|
}
|
||||||
|
|
||||||
const expandAllKeys = (nodes) =>
|
const expandAllKeys = (nodes) =>
|
||||||
@ -744,6 +788,21 @@ defineExpose({
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-toggle-btn {
|
||||||
|
background-color: var(--gray-100);
|
||||||
|
border-color: var(--gray-300);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--gray-200);
|
||||||
|
border-color: var(--gray-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-main {
|
.editor-main {
|
||||||
@ -779,6 +838,19 @@ defineExpose({
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown-preview {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
:deep(.md-editor) {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--gray-0);
|
||||||
|
}
|
||||||
|
:deep(.md-editor-preview-wrapper) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 依赖配置 */
|
/* 依赖配置 */
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="skills-view">
|
|
||||||
<SkillsManagerComponent />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import SkillsManagerComponent from '@/components/SkillsManagerComponent.vue'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.skills-view {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Loading…
Reference in New Issue
Block a user