feat: AI dialog box, left mouse button menu (#2005)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
Typos Check / Spell Check with Typos (push) Waiting to run

This commit is contained in:
shaohuzhang1 2025-01-09 17:59:44 +08:00 committed by GitHub
parent 2a63cd6bea
commit de85895ad6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 119 additions and 2 deletions

View File

@ -47,7 +47,8 @@
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",
"vue-i18n": "^9.13.1",
"vue-router": "^4.2.4"
"vue-router": "^4.2.4",
"vue3-menus": "^1.1.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.2",

View File

@ -5,7 +5,7 @@
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
<LogoIcon v-else height="32px" width="32px" />
</div>
<div class="content">
<div class="content" @click.stop @mouseup="openControl">
<el-card shadow="always" class="dialog-card mb-8">
<MdRenderer
v-if="
@ -56,6 +56,7 @@ import MdRenderer from '@/components/markdown/MdRenderer.vue'
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
import { type chatType } from '@/api/type/application'
import { computed } from 'vue'
import bus from '@/bus'
const props = defineProps<{
chatRecord: chatType
application: any
@ -79,6 +80,13 @@ const chatMessage = (question: string, type: 'old' | 'new', other_params_data?:
const add_answer_text_list = (answer_text_list: Array<any>) => {
answer_text_list.push({ content: '' })
}
const openControl = (event: any) => {
if (props.type !== 'log') {
bus.emit('open-control', event)
}
}
const answer_text_list = computed(() => {
return props.chatRecord.answer_text_list.map((item) => {
if (typeof item == 'string') {

View File

@ -182,6 +182,7 @@ import { MsgAlert } from '@/utils/message'
import { type chatType } from '@/api/type/application'
import { useRoute, useRouter } from 'vue-router'
import { getImgUrl } from '@/utils/utils'
import bus from '@/bus'
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine'
@ -542,6 +543,9 @@ function mouseleave() {
}
onMounted(() => {
bus.on('chat-input', (message: string) => {
inputValue.value = message
})
if (question) {
inputValue.value = decodeURIComponent(question.trim())
sendChatHandle()

View File

@ -0,0 +1,81 @@
<template>
<div>
<vue3-menus v-model:open="isOpen" :event="eventVal" :menus="menus" hasIcon>
<template #icon="{ menu }"
><AppIcon v-if="menu.icon" :iconName="menu.icon"></AppIcon
></template>
<template #label="{ menu }"> {{ menu.label }}</template>
</vue3-menus>
</div>
</template>
<script setup lang="ts">
import { Vue3Menus } from 'vue3-menus'
import { MsgSuccess } from '@/utils/message'
import AppIcon from '@/components/icons/AppIcon.vue'
import bus from '@/bus'
import { ref, nextTick, onMounted } from 'vue'
const isOpen = ref<boolean>(false)
const eventVal = ref({})
function getSelection() {
const selection = window.getSelection()
if (selection && selection.anchorNode == null) {
return null
}
const text = selection?.anchorNode?.textContent
return text && text.substring(selection.anchorOffset, selection.focusOffset)
}
/**
* 打开控制台
* @param event
*/
const openControl = (event: any) => {
const c = getSelection()
isOpen.value = false
if (c) {
nextTick(() => {
eventVal.value = event
isOpen.value = true
})
event.preventDefault()
}
}
const menus = ref([
{
label: '复制',
icon: 'app-copy',
click: () => {
const selectionText = getSelection()
if (selectionText) {
clearSelectedText()
navigator.clipboard.writeText(selectionText).then(() => {
MsgSuccess('复制成功')
})
}
}
},
{
label: '引用',
icon: 'app-quote',
click: () => {
bus.emit('chat-input', getSelection())
clearSelectedText()
}
}
])
/**
* 清除选中文本
*/
const clearSelectedText = () => {
if (window.getSelection) {
var selection = window.getSelection()
if (selection) {
selection.removeAllRanges()
}
}
}
onMounted(() => {
bus.on('open-control', openControl)
})
</script>
<style lang="scss"></style>

View File

@ -46,6 +46,7 @@
>
<template #operateBefore> <slot name="operateBefore" /> </template>
</ChatInputOperate>
<Control></Control>
</div>
</template>
<script setup lang="ts">
@ -63,6 +64,7 @@ import QuestionContent from '@/components/ai-chat/component/question-content/ind
import ChatInputOperate from '@/components/ai-chat/component/chat-input-operate/index.vue'
import PrologueContent from '@/components/ai-chat/component/prologue-content/index.vue'
import UserForm from '@/components/ai-chat/component/user-form/index.vue'
import Control from '@/components/ai-chat/component/control/index.vue'
defineOptions({ name: 'AiChat' })
const route = useRoute()
const {

View File

@ -1374,4 +1374,25 @@ export const iconMap: any = {
])
}
},
'app-quote': {
iconReader: () => {
return h('i', [
h(
'svg',
{
style: { height: '100%', width: '100%' },
viewBox: '0 0 1024 1024',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M800.768 477.184c-14.336 0-30.72 2.048-45.056 4.096 18.432-51.2 77.824-188.416 237.568-315.392 36.864-28.672-20.48-86.016-59.392-57.344-155.648 116.736-356.352 317.44-356.352 573.44v20.48c0 122.88 100.352 223.232 223.232 223.232S1024 825.344 1024 702.464c0-124.928-100.352-225.28-223.232-225.28zM223.232 477.184c-14.336 0-30.72 2.048-45.056 4.096 18.432-51.2 77.824-188.416 237.568-315.392 36.864-28.672-20.48-86.016-59.392-57.344C200.704 225.28 0 425.984 0 681.984v20.48c0 122.88 100.352 223.232 223.232 223.232s223.232-100.352 223.232-223.232c0-124.928-100.352-225.28-223.232-225.28z',
fill: 'currentColor'
})
]
)
])
}
}
}