mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 03:07:20 +08:00
修复文件编辑器状态栏功能问题 (#1274)
* Initial plan * 修复文件编辑器语言切换高亮和JSON文件识别问题 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * 修复行分隔符切换不生效问题 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * 移除编码切换功能(需要后端支持,范围较大) Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * 修复行分隔符切换的竞态条件和冗余操作 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: 编辑器问题 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> Co-authored-by: 耗子 <haozi@loli.email>
This commit is contained in:
@@ -28,6 +28,11 @@ function handleTabsWheel(e: WheelEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取编辑器主题(nginx 特殊处理)
|
||||
function getEditorTheme(language: string) {
|
||||
return (language === 'nginx' ? 'nginx-theme' : 'vs') + (themeStore.darkMode ? '-dark' : '')
|
||||
}
|
||||
|
||||
// 初始化编辑器
|
||||
async function initEditor() {
|
||||
if (!containerRef.value) return
|
||||
@@ -105,11 +110,7 @@ function updateEditorContent() {
|
||||
if (model) {
|
||||
const language = languageByPath(tab.path)
|
||||
monacoRef.value.editor.setModelLanguage(model, language)
|
||||
|
||||
// 更新主题(nginx 特殊处理)
|
||||
const theme =
|
||||
(language === 'nginx' ? 'nginx-theme' : 'vs') + (themeStore.darkMode ? '-dark' : '')
|
||||
monacoRef.value.editor.setTheme(theme)
|
||||
monacoRef.value.editor.setTheme(getEditorTheme(language))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +270,35 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// 监听语言变化(用户手动切换语言时更新 Monaco 高亮)
|
||||
watch(
|
||||
() => editorStore.activeTab?.language,
|
||||
(newLanguage) => {
|
||||
if (!editorRef.value || !monacoRef.value || !newLanguage) return
|
||||
const model = editorRef.value.getModel()
|
||||
if (model) {
|
||||
monacoRef.value.editor.setModelLanguage(model, newLanguage)
|
||||
monacoRef.value.editor.setTheme(getEditorTheme(newLanguage))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 监听行分隔符变化(用户手动切换行分隔符时更新 Monaco)
|
||||
watch(
|
||||
() => editorStore.activeTab?.lineEnding,
|
||||
(newLineEnding) => {
|
||||
if (!editorRef.value || !monacoRef.value || !newLineEnding) return
|
||||
const model = editorRef.value.getModel()
|
||||
if (model) {
|
||||
const eol =
|
||||
newLineEnding === 'CRLF'
|
||||
? monacoRef.value.editor.EndOfLineSequence.CRLF
|
||||
: monacoRef.value.editor.EndOfLineSequence.LF
|
||||
model.setEOL(eol)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 监听当前标签页内容变化(外部更新)
|
||||
watch(
|
||||
() => editorStore.activeTab?.content,
|
||||
@@ -287,9 +317,7 @@ watch(
|
||||
() => {
|
||||
if (!monacoRef.value || !editorStore.activeTab) return
|
||||
const language = languageByPath(editorStore.activeTab.path)
|
||||
const theme =
|
||||
(language === 'nginx' ? 'nginx-theme' : 'vs') + (themeStore.darkMode ? '-dark' : '')
|
||||
monacoRef.value.editor.setTheme(theme)
|
||||
monacoRef.value.editor.setTheme(getEditorTheme(language))
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -35,9 +35,6 @@ const languages = [
|
||||
'dockerfile'
|
||||
]
|
||||
|
||||
// 支持的编码列表
|
||||
const encodings = ['utf-8', 'gbk', 'gb2312', 'iso-8859-1', 'utf-16', 'utf-16le', 'utf-16be']
|
||||
|
||||
// 缩进选项
|
||||
const indentOptions = computed(() => [
|
||||
{ label: `${$gettext('Spaces')}: 2`, value: '2-spaces' },
|
||||
@@ -59,13 +56,6 @@ function handleLineEndingChange(value: 'LF' | 'CRLF') {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新编码
|
||||
function handleEncodingChange(value: string) {
|
||||
if (editorStore.activeTab) {
|
||||
editorStore.updateEncoding(editorStore.activeTab.path, value)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新语言
|
||||
function handleLanguageChange(value: string) {
|
||||
if (editorStore.activeTab) {
|
||||
@@ -121,18 +111,6 @@ function handleIndentChange(value: string) {
|
||||
</div>
|
||||
</n-popselect>
|
||||
|
||||
<!-- 编码 -->
|
||||
<n-popselect
|
||||
:value="editorStore.activeTab.encoding"
|
||||
:options="encodings.map((e) => ({ label: e.toUpperCase(), value: e }))"
|
||||
@update:value="handleEncodingChange"
|
||||
scrollable
|
||||
>
|
||||
<div class="status-item clickable">
|
||||
{{ $gettext('Encoding') }}: {{ editorStore.activeTab.encoding }}
|
||||
</div>
|
||||
</n-popselect>
|
||||
|
||||
<!-- 语言 -->
|
||||
<n-popselect
|
||||
:value="editorStore.activeTab.language"
|
||||
|
||||
@@ -142,7 +142,7 @@ function handleSelect(keys: string[], option: (TreeOption | null)[]) {
|
||||
editorStore.switchTab(path)
|
||||
} else {
|
||||
// 加载文件内容
|
||||
editorStore.openFile(path, '', 'utf-8')
|
||||
editorStore.openFile(path, '')
|
||||
editorStore.setLoading(path, true)
|
||||
useRequest(file.content(encodeURIComponent(path)))
|
||||
.onSuccess(({ data }) => {
|
||||
@@ -302,7 +302,7 @@ async function confirmInlineCreate() {
|
||||
|
||||
// 如果是文件,自动打开
|
||||
if (!isDir) {
|
||||
editorStore.openFile(createdPath, '', 'utf-8')
|
||||
editorStore.openFile(createdPath, '')
|
||||
}
|
||||
})
|
||||
.onError(() => {
|
||||
@@ -568,7 +568,7 @@ function confirmInlineRename() {
|
||||
const tab = editorStore.tabs.find((t) => t.path === oldPath)
|
||||
if (tab) {
|
||||
editorStore.closeTab(oldPath)
|
||||
editorStore.openFile(newPath, tab.content, tab.encoding)
|
||||
editorStore.openFile(newPath, tab.content)
|
||||
if (!tab.modified) {
|
||||
editorStore.markSaved(newPath)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ export interface EditorTab {
|
||||
language: string // 语言类型
|
||||
modified: boolean // 是否已修改
|
||||
loading: boolean // 是否正在加载
|
||||
encoding: string // 文件编码
|
||||
lineEnding: 'LF' | 'CRLF' // 行分隔符
|
||||
cursorLine: number // 光标行
|
||||
cursorColumn: number // 光标列
|
||||
@@ -48,7 +47,18 @@ const defaultSettings: EditorSettings = {
|
||||
insertSpaces: true,
|
||||
wordWrap: 'on',
|
||||
fontSize: 14,
|
||||
minimap: true
|
||||
minimap: true,
|
||||
lineNumbers: 'on',
|
||||
renderWhitespace: 'selection',
|
||||
cursorBlinking: 'blink',
|
||||
cursorStyle: 'line',
|
||||
smoothScrolling: true,
|
||||
mouseWheelZoom: true,
|
||||
bracketPairColorization: true,
|
||||
guides: true,
|
||||
folding: true,
|
||||
formatOnPaste: false,
|
||||
formatOnType: false
|
||||
}
|
||||
|
||||
export const useEditorStore = defineStore('editor', {
|
||||
@@ -85,7 +95,7 @@ export const useEditorStore = defineStore('editor', {
|
||||
|
||||
actions: {
|
||||
// 打开文件(添加标签页)
|
||||
openFile(path: string, content: string = '', encoding: string = 'utf-8') {
|
||||
openFile(path: string, content: string = '') {
|
||||
const existingTab = this.tabs.find((tab) => tab.path === path)
|
||||
if (existingTab) {
|
||||
// 文件已打开,切换到该标签页
|
||||
@@ -105,7 +115,6 @@ export const useEditorStore = defineStore('editor', {
|
||||
language: languageByPath(path),
|
||||
modified: false,
|
||||
loading: false,
|
||||
encoding,
|
||||
lineEnding,
|
||||
cursorLine: 1,
|
||||
cursorColumn: 1
|
||||
@@ -169,7 +178,10 @@ export const useEditorStore = defineStore('editor', {
|
||||
const tab = this.tabs.find((t) => t.path === path)
|
||||
if (tab) {
|
||||
tab.content = content
|
||||
tab.modified = content !== tab.originalContent
|
||||
// 将内容规范化为 LF 后比较,避免行分隔符差异影响修改状态判断
|
||||
const normalizedContent = content.replace(/\r\n/g, '\n')
|
||||
const normalizedOriginal = tab.originalContent.replace(/\r\n/g, '\n')
|
||||
tab.modified = normalizedContent !== normalizedOriginal
|
||||
}
|
||||
},
|
||||
|
||||
@@ -196,21 +208,6 @@ export const useEditorStore = defineStore('editor', {
|
||||
const tab = this.tabs.find((t) => t.path === path)
|
||||
if (tab && tab.lineEnding !== lineEnding) {
|
||||
tab.lineEnding = lineEnding
|
||||
// 转换内容中的行分隔符
|
||||
if (lineEnding === 'CRLF') {
|
||||
tab.content = tab.content.replace(/(?<!\r)\n/g, '\r\n')
|
||||
} else {
|
||||
tab.content = tab.content.replace(/\r\n/g, '\n')
|
||||
}
|
||||
tab.modified = tab.content !== tab.originalContent
|
||||
}
|
||||
},
|
||||
|
||||
// 更新编码
|
||||
updateEncoding(path: string, encoding: string) {
|
||||
const tab = this.tabs.find((t) => t.path === path)
|
||||
if (tab) {
|
||||
tab.encoding = encoding
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -286,6 +286,8 @@ const languageByPath = (path: string) => {
|
||||
case 'yaml':
|
||||
case 'yml': // yaml 扩展名
|
||||
return 'yaml'
|
||||
case 'json':
|
||||
return 'json'
|
||||
default:
|
||||
return 'plaintext'
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ function loadFile(path: string) {
|
||||
}
|
||||
|
||||
// 打开新文件
|
||||
editorStore.openFile(path, '', 'utf-8')
|
||||
editorStore.openFile(path, '')
|
||||
editorStore.setLoading(path, true)
|
||||
|
||||
useRequest(file.content(encodeURIComponent(path)))
|
||||
|
||||
Reference in New Issue
Block a user