Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 38 additions & 36 deletions packages/core/src/components/Sender/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ const internalValue = computed({
return props.modelValue;
},
set(val) {
if (props.readOnly || props.disabled)
return;
if (props.readOnly || props.disabled) return;
emits('update:modelValue', val);
}
});
Expand Down Expand Up @@ -87,8 +86,7 @@ const popoverVisible = computed({
return props.triggerPopoverVisible;
},
set(value) {
if (props.readOnly || props.disabled)
return;
if (props.readOnly || props.disabled) return;
emits('update:triggerPopoverVisible', value);
}
});
Expand All @@ -100,8 +98,7 @@ const triggerString = ref('');
watch(
() => internalValue.value,
(newVal, oldVal) => {
if (isComposing.value)
return;
if (isComposing.value) return;
// 触发逻辑:当输入值等于数组中的任意一个指令字符时触发
// 确保 oldVal 是字符串类型
const triggerStrings = props.triggerStrings || []; // 如果为 undefined,就使用空数组
Expand All @@ -120,8 +117,7 @@ watch(
isOpen: true
});
popoverVisible.value = true;
}
else {
} else {
popoverVisible.value = true;
}
}
Expand All @@ -135,8 +131,7 @@ watch(
isOpen: false
});
popoverVisible.value = false;
}
else {
} else {
popoverVisible.value = false;
}
}
Expand All @@ -151,8 +146,7 @@ watch(
isOpen: true
});
popoverVisible.value = true;
}
else {
} else {
popoverVisible.value = true;
}
}
Expand All @@ -166,26 +160,30 @@ function onContentMouseDown(e: MouseEvent) {
if (e.target !== senderRef.value.querySelector(`.el-textarea__inner`)) {
e.preventDefault();
}
// 点击右边操作选项时,阻止事件穿透
if (
senderRef.value.querySelector(`.el-sender-action-list`).contains(e.target)
) {
e.stopPropagation();
inputRef.value.blur();
return false;
}
inputRef.value.focus();
}
/* 内容容器聚焦 结束 */

/* 头部显示隐藏 开始 */
const visiableHeader = ref(false);
function openHeader() {
if (!slots.header)
return false;
if (!slots.header) return false;

if (props.readOnly)
return false;
if (props.readOnly) return false;

visiableHeader.value = true;
}
function closeHeader() {
if (!slots.header)
return;
if (props.readOnly)
return;
if (!slots.header) return;
if (props.readOnly) return;
visiableHeader.value = false;
}
/* 头部显示隐藏 结束 */
Expand All @@ -195,8 +193,7 @@ const recognition = ref<SpeechRecognition | null>(null);
const speechLoading = ref<boolean>(false);

function startRecognition() {
if (props.readOnly)
return; // 直接返回,不执行后续逻辑
if (props.readOnly) return; // 直接返回,不执行后续逻辑
if (hasOnRecordingChangeListener.value) {
speechLoading.value = true;
emits('recordingChange', true);
Expand Down Expand Up @@ -227,8 +224,7 @@ function startRecognition() {
speechLoading.value = false;
};
recognition.value.start();
}
else {
} else {
console.error('浏览器不支持 Web Speech API');
}
}
Expand Down Expand Up @@ -261,22 +257,19 @@ function submit() {
}
// 取消按钮
function cancel() {
if (props.readOnly)
return;
if (props.readOnly) return;
emits('cancel', internalValue.value);
}

function clear() {
if (props.readOnly)
return; // 直接返回,不执行后续逻辑
if (props.readOnly) return; // 直接返回,不执行后续逻辑
inputRef.value.clear();
internalValue.value = '';
}

// 在这判断组合键的回车键 (目前支持四种模式)
function handleKeyDown(e: { target: HTMLTextAreaElement } & KeyboardEvent) {
if (props.readOnly)
return; // 直接返回,不执行后续逻辑
if (props.readOnly) return; // 直接返回,不执行后续逻辑
const _resetSelectionRange = () => {
const cursorPosition = e.target.selectionStart; // 获取光标位置
const textBeforeCursor = internalValue.value.slice(0, cursorPosition); // 光标前的文本
Expand Down Expand Up @@ -307,8 +300,7 @@ function handleKeyDown(e: { target: HTMLTextAreaElement } & KeyboardEvent) {
e.preventDefault();
if (props.submitType === 'enter') {
_isComKeyDown ? _resetSelectionRange() : submit();
}
else {
} else {
_isComKeyDown ? submit() : _resetSelectionRange();
}
}
Expand All @@ -329,11 +321,9 @@ function focus(type = 'all') {
}
if (type === 'all') {
inputRef.value.select();
}
else if (type === 'start') {
} else if (type === 'start') {
focusToStart();
}
else if (type === 'end') {
} else if (type === 'end') {
focusToEnd();
}
}
Expand Down Expand Up @@ -383,6 +373,16 @@ function handleInternalPaste(e: ClipboardEvent) {
}
}

// 当选择器的输入框获得焦点时触发
function handleFocus(event: FocusEvent) {
emits('focus', event);
}

// 当选择器的输入框失去焦点时触发
function handleBlur(event: FocusEvent) {
emits('blur', event);
}

defineExpose({
openHeader, // 打开头部
closeHeader, // 关闭头部
Expand Down Expand Up @@ -463,6 +463,8 @@ defineExpose({
:read-only="readOnly || disabled"
:disabled="disabled"
@keydown="handleKeyDown"
@focus="handleFocus"
@blur="handleBlur"
@compositionstart="handleCompositionStart"
@compositionend="handleCompositionEnd"
@paste="handleInternalPaste"
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/components/Sender/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export interface SenderEmits {
(event: 'update:triggerPopoverVisible', visible: boolean): void;
// 操作事件
(event: 'submit', internalValue: string): void;
(event: 'focus', internalValue: FocusEvent): void;
(event: 'blur', internalValue: FocusEvent): void;
Comment on lines +59 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Verify TS DOM lib availability and SFC wiring.

FocusEvent requires the DOM lib; also confirm the SFC actually emits and exposes as intended.

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Check DOM lib presence in tsconfig..."
fd -a 'tsconfig*.json' | while read -r f; do
  echo "==> $f"
  rg -n '"lib"\s*:\s*\[[^]]*dom' -n "$f" || true
done

echo; echo "2) Confirm Sender emits include focus/blur..."
rg -nP -C2 '(defineEmits|emits\s*:)\s*' packages/core/src/components/Sender/index.vue || true
rg -nP "@focus=|@blur=" packages/core/src/components/Sender/index.vue || true

echo; echo "3) Confirm programmatic methods are exposed..."
rg -nP 'defineExpose\s*<' packages/core/src/components/Sender/index.vue || true
rg -nP '\bfunction\s+focus\b|\bfocus\s*\(\)\s*{' -n packages/core/src/components/Sender/index.vue || true
rg -nP '\bfunction\s+blur\b|\bblur\s*\(\)\s*{' -n packages/core/src/components/Sender/index.vue || true

Length of output: 450


Enable DOM lib in TS config & expose Sender focus/blur

  • TS config: tsconfig files exist at apps/docs/tsconfig.json and packages/core/tsconfig.json but the scan found no explicit DOM lib — add "DOM" to packages/core/tsconfig.json compilerOptions.lib (or ensure the package inherits lib.dom) so FocusEvent types resolve.
  • Sender SFC: defineEmits() is present (packages/core/src/components/Sender/index.vue:37) and the template listens to @focus/@blur (≈466–467); focus() and blur() functions exist (≈311–318) but no typed defineExpose was detected — expose the methods (e.g., defineExpose({ focus, blur }) or defineExpose(...)) and ensure SenderEmits includes the FocusEvent signatures.
🤖 Prompt for AI Agents
In packages/core/src/components/Sender/types.d.ts around lines 59-60, the
FocusEvent types and exposed focus/blur methods are not properly resolved/typed:
update packages/core/tsconfig.json to include "DOM" in compilerOptions.lib (or
ensure it inherits lib.dom) so FocusEvent is available, add the FocusEvent
signatures to the SenderEmits type so defineEmits<SenderEmits>() covers (event:
'focus', internalValue: FocusEvent): void and (event: 'blur', internalValue:
FocusEvent): void, and in packages/core/src/components/Sender/index.vue add a
typed defineExpose to expose the existing focus and blur functions (e.g.,
defineExpose({ focus, blur }) or defineExpose<ExposeType>(...)) so consumers and
the template have proper typings.

(event: 'cancel', internalValue: string): void;
// 录音状态变更事件
(event: 'recordingChange', isRecording: boolean): void;
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/stories/Sender/index.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts" setup>
import type { TriggerEvent } from '@components/Sender/types';
import { action } from '@storybook/addon-actions';
import { ElMessage } from 'element-plus';
import { Sender } from '../../components';

Expand All @@ -18,6 +19,14 @@ function handleTrigger(value: TriggerEvent) {
function handleRecordingChange() {
ElMessage.success(`RecordingChange`);
}

function handleFocus(event: FocusEvent) {
action('获得焦点')(event);
}

function handleBlur(event: FocusEvent) {
action('失去焦点')(event);
}
</script>

<template>
Expand All @@ -26,6 +35,8 @@ function handleRecordingChange() {
v-bind="$attrs"
@submit="handleSubmit"
@cancel="handleCancel"
@focus="handleFocus"
@blur="handleBlur"
@trigger="handleTrigger"
@recording-change="handleRecordingChange"
/>
Expand Down