|
|
@ -1,5 +1,5 @@ |
|
|
|
<template> |
|
|
|
<div v-show="fieldMethodsClass.getShow(props, modelValue)"> |
|
|
|
<div v-show="fieldMethods.getShow(props, modelValue)"> |
|
|
|
<q-field |
|
|
|
ref="fieldRef" |
|
|
|
v-model="modelValue" |
|
|
@ -9,23 +9,16 @@ |
|
|
|
:dense="true" |
|
|
|
v-bind="attrs" |
|
|
|
style="position: relative" |
|
|
|
:rules="fieldMethodsClass.getRules(props, modelValue, fieldRef, undefined)" |
|
|
|
:readonly="fieldMethodsClass.getReadOnly(props, modelValue)" |
|
|
|
:disable="fieldMethodsClass.getDisable(props, modelValue)" |
|
|
|
@focus.stop.prevent="focus" |
|
|
|
@blur.stop.prevent="blur" |
|
|
|
:rules="fieldMethods.getRules(props, modelValue, fieldRef, undefined)" |
|
|
|
:readonly="fieldMethods.getReadOnly(props, modelValue)" |
|
|
|
:disable="fieldMethods.getDisable(props, modelValue)" |
|
|
|
> |
|
|
|
<template #label><w-label :required="fieldMethodsClass.getRequired(props, modelValue)" :label="attrs.label"></w-label></template> |
|
|
|
<template #label><w-label :required="fieldMethods.getRequired(props, modelValue)" :label="attrs.label"></w-label></template> |
|
|
|
<template #control> |
|
|
|
<div v-if="editable && toolbar" class="w-full pt-2 border-b border-b-gray-200"> |
|
|
|
<Toolbar :source-code-editor="editorView" :user-defined-functions="userDefinedFunctions"></Toolbar> |
|
|
|
</div> |
|
|
|
<div |
|
|
|
ref="codemirrorRef" |
|
|
|
:style="`width:100%; padding-top: ${lineNumber ? 4 : 2}px; overflow: auto;`" |
|
|
|
@focus.stop.prevent="() => {}" |
|
|
|
@click.stop.prevent="() => {}" |
|
|
|
></div> |
|
|
|
<div ref="codemirrorContainerRef" :style="`width:100%; padding-top: ${lineNumber ? 4 : 2}px; overflow: auto;`"></div> |
|
|
|
</template> |
|
|
|
<template v-if="!Tools.isEmpty(modelValue) && attrs.button" #append> |
|
|
|
<q-btn |
|
|
@ -57,12 +50,12 @@ import * as search from '@codemirror/search'; |
|
|
|
import * as autocomplete from '@codemirror/autocomplete'; |
|
|
|
|
|
|
|
import { LanguageSupport } from '@codemirror/language'; |
|
|
|
import { html, htmlLanguage } from '@codemirror/lang-html'; |
|
|
|
import { java, javaLanguage } from '@codemirror/lang-java'; |
|
|
|
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript'; |
|
|
|
import { json, jsonLanguage } from '@codemirror/lang-json'; |
|
|
|
import { html } from '@codemirror/lang-html'; |
|
|
|
import { java } from '@codemirror/lang-java'; |
|
|
|
import { javascript } from '@codemirror/lang-javascript'; |
|
|
|
import { json } from '@codemirror/lang-json'; |
|
|
|
import { sql } from '@codemirror/lang-sql'; |
|
|
|
import { xml, xmlLanguage } from '@codemirror/lang-xml'; |
|
|
|
import { xml } from '@codemirror/lang-xml'; |
|
|
|
|
|
|
|
import PlaceholderPlugin from './w-code-mirror/PlaceholderPlugin'; |
|
|
|
import Toolbar from '@/platform/components/math/toolbar/Toolbar.vue'; |
|
|
@ -71,9 +64,10 @@ import { FormFieldProps } from '@/platform/components/form/FormField.ts'; |
|
|
|
import { FormFieldMethods } from '../form/FormField'; |
|
|
|
|
|
|
|
const fieldRef = ref(); |
|
|
|
const codemirrorRef = ref(); |
|
|
|
const codemirrorContainerRef = ref(); |
|
|
|
const attrs = useAttrs(); |
|
|
|
const modelValue = defineModel<string>(); |
|
|
|
let editorView = null; |
|
|
|
|
|
|
|
interface FieldProps extends FormFieldProps { |
|
|
|
// 语言 |
|
|
@ -100,8 +94,11 @@ interface FieldProps extends FormFieldProps { |
|
|
|
lineBreak?: boolean; |
|
|
|
// 是否开启变量替换 |
|
|
|
placeholder?: boolean; |
|
|
|
// 自动完成函数 |
|
|
|
autoCompletion?: () => void; |
|
|
|
// 用户自定义函数 |
|
|
|
userDefinedFunctions?: Array<() => void>; |
|
|
|
// |
|
|
|
activateOnCompletion?: () => void; |
|
|
|
} |
|
|
|
const props = withDefaults(defineProps<FieldProps>(), { |
|
|
@ -143,16 +140,14 @@ class FieldMethods extends FormFieldMethods { |
|
|
|
return editorView.state.doc.toString(); |
|
|
|
}; |
|
|
|
clearValue = () => { |
|
|
|
modelValue.value = undefined; |
|
|
|
modelValue.value = ''; |
|
|
|
}; |
|
|
|
} |
|
|
|
const fieldMethodsClass = new FieldMethods(); |
|
|
|
const fieldMethods = new FieldMethods(); |
|
|
|
|
|
|
|
const basicSetup = [ |
|
|
|
EditorState.allowMultipleSelections.of(true), |
|
|
|
|
|
|
|
//view.lineNumbers(), |
|
|
|
//view.highlightActiveLine(), |
|
|
|
view.highlightActiveLineGutter(), |
|
|
|
view.highlightSpecialChars(), |
|
|
|
view.drawSelection(), |
|
|
@ -162,13 +157,10 @@ const basicSetup = [ |
|
|
|
|
|
|
|
commands.history(), |
|
|
|
|
|
|
|
//language.foldGutter(), |
|
|
|
language.indentOnInput(), |
|
|
|
language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }), |
|
|
|
language.bracketMatching(), |
|
|
|
|
|
|
|
//search.highlightSelectionMatches(), |
|
|
|
|
|
|
|
view.keymap.of([ |
|
|
|
...commands.defaultKeymap, |
|
|
|
...commands.historyKeymap, |
|
|
@ -200,9 +192,14 @@ const getLanguage = (lang: string): LanguageSupport => { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let editorView; |
|
|
|
let isFocus = false; |
|
|
|
// 配置编辑器的特性 |
|
|
|
// 是否可编辑 |
|
|
|
basicSetup.push(EditorView.editable.of(props.editable)); |
|
|
|
|
|
|
|
if (props.lineWrap) { |
|
|
|
// 自动换行 |
|
|
|
basicSetup.push(EditorView.lineWrapping); |
|
|
|
} |
|
|
|
if (props.lineNumber) { |
|
|
|
// 行号 |
|
|
|
basicSetup.push(view.lineNumbers()); |
|
|
@ -219,23 +216,20 @@ if (props.placeholder) { |
|
|
|
basicSetup.push(PlaceholderPlugin); |
|
|
|
} |
|
|
|
|
|
|
|
if (props.lineWrap) { |
|
|
|
basicSetup.push(EditorView.lineWrapping); |
|
|
|
} |
|
|
|
|
|
|
|
// 是否可编辑 |
|
|
|
basicSetup.push(EditorView.editable.of(props.editable)); |
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
if (codemirrorRef.value) { |
|
|
|
let language = new Compartment(); |
|
|
|
let tabSize = new Compartment(); |
|
|
|
editorView = new EditorView({ |
|
|
|
extensions: [ |
|
|
|
// 基本安装 |
|
|
|
basicSetup, |
|
|
|
// 语言 |
|
|
|
language.of(getLanguage(props.lang)), |
|
|
|
// tab size |
|
|
|
tabSize.of(EditorState.tabSize.of(props.tabSize)), |
|
|
|
EditorState.readOnly.of(fieldMethodsClass.getReadOnly(props, modelValue.value)), |
|
|
|
// 只读 |
|
|
|
EditorState.readOnly.of(fieldMethods.getReadOnly(props, modelValue.value)), |
|
|
|
// 主题 |
|
|
|
EditorView.theme({ |
|
|
|
'&': { |
|
|
|
outline: 'none !important', |
|
|
@ -247,27 +241,38 @@ onMounted(() => { |
|
|
|
}, |
|
|
|
}), |
|
|
|
|
|
|
|
// 以下代码可以添加内容变化后的操作, 为避免重复操作, 更新操作放到 blur 方法中了 |
|
|
|
EditorView.updateListener.of(function (e) { |
|
|
|
// emits('update:modelValue', e.state.doc.toString()); |
|
|
|
// 事件监听 |
|
|
|
EditorView.updateListener.of((e) => { |
|
|
|
// 如果文档内容发生变化时 |
|
|
|
if (e.docChanged) { |
|
|
|
let content = e.state.doc.toString(); |
|
|
|
// 如果不允许换行, 则将回车换行替换掉 |
|
|
|
if (!props.lineBreak) { |
|
|
|
content = content?.replace(/[\r\n]/g, ''); |
|
|
|
} |
|
|
|
if (content !== modelValue.value) { |
|
|
|
fieldMethodsClass.updateValue(content); |
|
|
|
// 如果开启变量替换 |
|
|
|
if (props.placeholder) { |
|
|
|
const reg = /\$\{(.+?)\}\.\$\{(.+?)\}/g; |
|
|
|
while (reg.test(content)) { |
|
|
|
content = content.replace(reg, '${$1.$2}'); |
|
|
|
} |
|
|
|
} |
|
|
|
// 更新表单字段的模型值 |
|
|
|
modelValue.value = content; |
|
|
|
} |
|
|
|
//updateModelValue(content); |
|
|
|
}), |
|
|
|
], |
|
|
|
parent: codemirrorRef.value, |
|
|
|
parent: codemirrorContainerRef.value, |
|
|
|
// 内容 |
|
|
|
doc: modelValue.value, |
|
|
|
}); |
|
|
|
|
|
|
|
watch( |
|
|
|
() => modelValue.value, |
|
|
|
() => { |
|
|
|
// 当未获得焦点时,更新变更, 当获得焦点时不能更新 |
|
|
|
if (!isFocus) { |
|
|
|
// 如果当前没有处于编辑状态时, 需要更新编辑器的内容, 以便外部更新内容后能够在编辑器中保持一致 |
|
|
|
// 如果当前正处于编辑状态时, 不需要更新编辑器的内容, |
|
|
|
if (!editorView.hasFocus) { |
|
|
|
let content = modelValue.value; |
|
|
|
if (!props.lineBreak && !Tools.isEmpty(content)) { |
|
|
|
content = content.replace(/[\r\n]/g, ''); |
|
|
@ -276,30 +281,12 @@ onMounted(() => { |
|
|
|
} |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
onUnmounted(() => { |
|
|
|
editorView.destroy(); |
|
|
|
}); |
|
|
|
|
|
|
|
const focus = () => { |
|
|
|
isFocus = true; |
|
|
|
}; |
|
|
|
|
|
|
|
const blur = () => { |
|
|
|
isFocus = false; |
|
|
|
let content = editorView.state.doc.toString(); |
|
|
|
if (Tools.isUndefinedOrNull(content) || content === modelValue.value) { |
|
|
|
return; |
|
|
|
} |
|
|
|
const reg = /\$\{(.+?)\}\.\$\{(.+?)\}/g; |
|
|
|
while (reg.test(content)) { |
|
|
|
content = content.replace(reg, '${$1.$2}'); |
|
|
|
} |
|
|
|
modelValue.value = content; |
|
|
|
}; |
|
|
|
|
|
|
|
const configure = (values) => { |
|
|
|
editorView.dispatch({ effects: StateEffect.reconfigure.of(values) }); |
|
|
|
}; |
|
|
@ -314,10 +301,10 @@ const buttonClick = (button) => { |
|
|
|
}; |
|
|
|
|
|
|
|
defineExpose({ |
|
|
|
validate: fieldMethodsClass.validate, |
|
|
|
setValue: fieldMethodsClass.setValue, |
|
|
|
getValue: fieldMethodsClass.getValue, |
|
|
|
clearValue: fieldMethodsClass.clearValue, |
|
|
|
validate: fieldMethods.validate, |
|
|
|
setValue: fieldMethods.setValue, |
|
|
|
getValue: fieldMethods.getValue, |
|
|
|
clearValue: fieldMethods.clearValue, |
|
|
|
configure, |
|
|
|
}); |
|
|
|
</script> |
|
|
|