准备实现富文本编辑器

This commit is contained in:
锦恢 2025-05-07 02:46:43 +08:00
parent 5a2a699a51
commit ea44620dee
7 changed files with 149 additions and 48 deletions

BIN
chi_sim.traineddata Normal file

Binary file not shown.

BIN
eng.traineddata Normal file

Binary file not shown.

View File

@ -0,0 +1,115 @@
<template>
<div class="k-rich-textarea">
<div
ref="editor"
contenteditable="true"
class="rich-editor"
:placeholder="placeholder"
@input="handleInput"
@keydown.enter="handleKeydown"
@compositionstart="handleCompositionStart"
@compositionend="handleCompositionEnd"
></div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, defineProps, defineEmits, watch } from 'vue';
import type { RichTextItem } from './textarea.dto';
const props = defineProps({
modelValue: {
type: Array as () => RichTextItem[],
required: true
},
placeholder: {
type: String,
default: '输入消息...'
},
customClass: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue', 'pressEnter']);
const editor = ref<HTMLElement | null>(null);
const renderRichText = (items: RichTextItem[]) => {
return items.map(item => {
if (item.type === 'prompt' || item.type === 'resource') {
return `<span class="rich-item rich-item-${item.type}">${item.text}</span>`;
}
return item.text;
}).join('');
};
const handleInput = (event: Event) => {
if (editor.value) {
console.log(editor.value);
const items: RichTextItem[] = [];
// RichTextItem[]
// ...
emit('update:modelValue', items);
}
};
watch(() => props.modelValue, (newValue) => {
if (editor.value) {
editor.value.innerHTML = renderRichText(newValue);
}
}, { immediate: true });
const isComposing = ref(false);
const handleKeydown = (event: KeyboardEvent) => {
if (event.key === 'Enter' && !event.shiftKey && !isComposing.value) {
event.preventDefault();
emit('pressEnter', event);
}
};
const handleCompositionStart = () => {
isComposing.value = true;
};
const handleCompositionEnd = () => {
isComposing.value = false;
};
</script>
<style>
.k-rich-textarea {
border-radius: .9em;
border: 1px solid #DCDFE6;
padding: 5px;
}
.rich-editor {
min-height: 100px;
outline: none;
}
.rich-editor:empty::before {
content: attr(placeholder);
color: #C0C4CC;
}
.rich-item {
padding: 2px 4px;
border-radius: 4px;
margin: 0 2px;
}
.rich-item-prompt {
background-color: #e8f0fe;
color: #1a73e8;
}
.rich-item-resource {
background-color: #f1f3f4;
color: #202124;
}
</style>

View File

@ -0,0 +1,17 @@
interface PromptTextItem {
type: 'prompt'
text: string
}
interface ResourceTextItem {
type: 'resource'
text: string
}
interface TextItem {
type: 'text'
text: string
}
export type RichTextItem = PromptTextItem | ResourceTextItem | TextItem;

View File

@ -59,6 +59,8 @@ function whenGetPromptResponse(msg: PromptsGetResponse) {
try {
const content = msg.messages[0].content;
selectPrompt.value = undefined;
if (content) {
emits('update:modelValue', props.modelValue + content);
}

View File

@ -3,7 +3,7 @@
<Model />
<SystemPrompt />
<ToolUse />
<Prompt v-model="val" />
<Prompt v-model="modelValue" />
<Websearch />
<Temperature />
<ContextLength />
@ -11,7 +11,7 @@
</template>
<script setup lang="ts">
import { defineProps, provide, ref } from 'vue';
import { defineProps, defineEmits, provide, ref, computed } from 'vue';
import { llmManager } from '@/views/setting/llm';
import { tabs } from '../../panel';
import type { ChatSetting, ChatStorage } from '../chat';
@ -35,7 +35,17 @@ const props = defineProps({
}
});
const val = ref('');
const emits = defineEmits(['update:modelValue']);
const modelValue = computed({
get() {
return props.modelValue;
},
set(value) {
emits('update:modelValue', value);
}
});
const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ChatStorage & { settings: ChatSetting };

View File

@ -1,5 +1,5 @@
{
"currentIndex": 1,
"currentIndex": 0,
"tabs": [
{
"name": "交互测试",
@ -9,50 +9,7 @@
"storage": {
"messages": [],
"settings": {
"modelIndex": 15,
"enableTools": [
{
"name": "add",
"description": "对两个数字进行实数域的加法",
"enabled": true
},
{
"name": "multiply",
"description": "对两个数字进行实数域的乘法运算",
"enabled": true
},
{
"name": "is_even",
"description": "判断一个整数是否为偶数",
"enabled": true
},
{
"name": "capitalize",
"description": "将字符串首字母大写",
"enabled": true
},
{
"name": "get_weather_by_city_code",
"description": "根据城市天气预报的城市编码 (int),获取指定城市的天气信息",
"enabled": true
}
],
"enableWebSearch": false,
"temperature": 0.7,
"contextLength": 20,
"systemPrompt": ""
}
}
},
{
"name": "交互测试",
"icon": "icon-robot",
"type": "blank",
"componentIndex": 3,
"storage": {
"messages": [],
"settings": {
"modelIndex": 15,
"modelIndex": 0,
"enableTools": [
{
"name": "add",