Browse Source

feat(component): code language picker in editor

main
Mohan 1 month ago
parent
commit
9fb40814fe
  1. 92
      src/components/QuillEditor.vue

92
src/components/QuillEditor.vue

@ -14,6 +14,52 @@ const emit = defineEmits<{
const editorContainer = ref<HTMLDivElement>() const editorContainer = ref<HTMLDivElement>()
let quill: Quill | null = null 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(() => { onMounted(() => {
if (!editorContainer.value) return if (!editorContainer.value) return
@ -74,14 +120,60 @@ watch(() => props.modelValue, (val) => {
</script> </script>
<template> <template>
<div class="quill-wrap">
<div ref="editorContainer" class="quill-editor" /> <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> </template>
<style scoped> <style scoped>
.quill-wrap {
background: var(--ac-bg-card, #fff);
}
.quill-editor { .quill-editor {
min-height: 400px; min-height: 400px;
background: var(--ac-bg-card, #fff); 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) { .quill-editor :deep(.ql-editor) {
min-height: 400px; min-height: 400px;
font-size: 16px; font-size: 16px;

Loading…
Cancel
Save