|
|
|
@ -14,6 +14,52 @@ const emit = defineEmits<{ |
|
|
|
const editorContainer = ref<HTMLDivElement>() |
|
|
|
let quill: Quill | null = null |
|
|
|
|
|
|
|
const codeLang = ref('') |
|
|
|
const codeLanguages = [ |
|
|
|
'text', |
|
|
|
'javascript', |
|
|
|
'typescript', |
|
|
|
'html', |
|
|
|
'xml', |
|
|
|
'css', |
|
|
|
'json', |
|
|
|
'markdown', |
|
|
|
'python', |
|
|
|
'java', |
|
|
|
'c', |
|
|
|
'cpp', |
|
|
|
'csharp', |
|
|
|
'go', |
|
|
|
'rust', |
|
|
|
'php', |
|
|
|
'sql', |
|
|
|
'bash', |
|
|
|
'yaml' |
|
|
|
] |
|
|
|
|
|
|
|
function applyCodeLang() { |
|
|
|
if (!quill || !codeLang.value) return |
|
|
|
const lang = codeLang.value |
|
|
|
const sel = quill.getSelection() |
|
|
|
if (!sel) { |
|
|
|
codeLang.value = '' |
|
|
|
return |
|
|
|
} |
|
|
|
const [blot] = (quill.getLeaf(sel.index) as unknown as [{ domNode?: HTMLElement } | undefined, number]) |
|
|
|
const leafNode = blot?.domNode |
|
|
|
const line = leafNode?.closest?.('.ql-code-block') as HTMLElement | null |
|
|
|
if (!line) { |
|
|
|
codeLang.value = '' |
|
|
|
return |
|
|
|
} |
|
|
|
const container = (line.closest('.ql-code-block-container') || line.parentElement) as HTMLElement | null |
|
|
|
if (container) { |
|
|
|
container.setAttribute('data-language', lang) |
|
|
|
emit('update:modelValue', quill.root.innerHTML) |
|
|
|
} |
|
|
|
codeLang.value = '' |
|
|
|
} |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
if (!editorContainer.value) return |
|
|
|
|
|
|
|
@ -74,14 +120,60 @@ watch(() => props.modelValue, (val) => { |
|
|
|
</script> |
|
|
|
|
|
|
|
<template> |
|
|
|
<div ref="editorContainer" class="quill-editor" /> |
|
|
|
<div class="quill-wrap"> |
|
|
|
<div ref="editorContainer" class="quill-editor" /> |
|
|
|
<div class="code-lang-bar"> |
|
|
|
<label class="code-lang-label" for="code-lang-select">代码类型</label> |
|
|
|
<select id="code-lang-select" v-model="codeLang" class="code-lang-select" @change="applyCodeLang"> |
|
|
|
<option value="">自动识别</option> |
|
|
|
<option v-for="l in codeLanguages" :key="l" :value="l">{{ l }}</option> |
|
|
|
</select> |
|
|
|
<span class="code-lang-hint">将光标置于代码块内后选择</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
.quill-wrap { |
|
|
|
background: var(--ac-bg-card, #fff); |
|
|
|
} |
|
|
|
|
|
|
|
.quill-editor { |
|
|
|
min-height: 400px; |
|
|
|
background: var(--ac-bg-card, #fff); |
|
|
|
} |
|
|
|
|
|
|
|
.code-lang-bar { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
gap: 10px; |
|
|
|
padding: 10px 12px; |
|
|
|
border: 1px solid var(--ac-border); |
|
|
|
border-top: none; |
|
|
|
border-radius: 0 0 var(--song-radius-l) var(--song-radius-l); |
|
|
|
background: var(--ac-surface, #faf8f5); |
|
|
|
font-size: 13px; |
|
|
|
flex-wrap: wrap; |
|
|
|
} |
|
|
|
|
|
|
|
.code-lang-label { |
|
|
|
color: var(--ac-text-secondary); |
|
|
|
} |
|
|
|
|
|
|
|
.code-lang-select { |
|
|
|
padding: 5px 8px; |
|
|
|
border: 1px solid var(--ac-input-border); |
|
|
|
border-radius: var(--song-radius-m); |
|
|
|
background: var(--ac-bg-card); |
|
|
|
color: var(--ac-text); |
|
|
|
font-size: 13px; |
|
|
|
} |
|
|
|
|
|
|
|
.code-lang-hint { |
|
|
|
color: var(--ac-text-disabled); |
|
|
|
font-size: 12px; |
|
|
|
} |
|
|
|
|
|
|
|
.quill-editor :deep(.ql-editor) { |
|
|
|
min-height: 400px; |
|
|
|
font-size: 16px; |
|
|
|
|