Files
admin-ui/src/views/home/components/InputBar.vue
T
2026-07-07 17:53:32 +08:00

375 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="input-shell">
<div class="input-card" :class="{ 'is-focused': isFocused }">
<!-- 已选中状态标签栏 -->
<div v-if="selectedWorkflowId !== null" class="selected-tags">
<div class="selected-tag workflow-tag">
<el-icon><Promotion /></el-icon>
<span>{{ commonWorkflows.find((w) => w.id === selectedWorkflowId)?.name }}</span>
<el-icon v-if="!workflowLocked" class="tag-close" @click="clearWorkflow"><Close /></el-icon>
</div>
</div>
<!-- 文本输入区 -->
<el-input
v-model="message"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6 }"
placeholder="有什么想聊的?按 Enter 发送,Shift+Enter 换行"
class="message-input"
@focus="isFocused = true"
@blur="isFocused = false"
@keydown.enter.exact.prevent="handleSend"
@keydown.enter.shift.exact="() => {}"
/>
<!-- 底部工具栏 -->
<div class="input-toolbar">
<div class="toolbar-left">
<button class="tool-icon-btn" title="上传文件" @click="handleAttachment">
<el-icon><Paperclip /></el-icon>
</button>
<!-- 技能暂已禁用 -->
<button class="tool-icon-btn" title="技能暂不可用" disabled>
<el-icon><MagicStick /></el-icon>
<span class="tool-label">技能</span>
</button>
</div>
<div class="toolbar-right">
<span class="hint-text">Shift+Enter 换行</span>
<button class="send-btn" :disabled="selectedWorkflowId === null || sendDisabled" @click="handleSend">
<el-icon><Top /></el-icon>
</button>
</div>
</div>
</div>
<!-- 快捷工作流胶囊输入框下方 -->
<div v-if="visibleWorkflows.length > 0" class="workflow-shortcuts">
<button
v-for="wf in visibleWorkflows"
:key="wf.id"
class="shortcut-pill"
:class="{ active: selectedWorkflowId === wf.id }"
@click="toggleWorkflow(wf.id)"
>
<el-icon><Promotion /></el-icon>
{{ wf.name }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { Top, MagicStick, Promotion, Close, Paperclip } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { getWorkflowList } from '/@/api/settings/creation';
interface Props {
sendDisabled?: boolean;
workflowLocked?: boolean;
}
interface Emits {
(e: 'send', message: string): void;
(e: 'workflow-select', workflowId: string | null): void;
}
interface Workflow {
id: string;
name: string;
prefix: string;
}
const props = withDefaults(defineProps<Props>(), {
sendDisabled: false,
workflowLocked: false,
});
const emit = defineEmits<Emits>();
const message = ref('');
const isFocused = ref(false);
const selectedWorkflowId = ref<string | null>(null);
const commonWorkflows = ref<Workflow[]>([]);
const visibleWorkflows = computed(() => {
if (props.workflowLocked && selectedWorkflowId.value) {
return commonWorkflows.value.filter((w) => w.id === selectedWorkflowId.value);
}
return commonWorkflows.value;
});
const fetchWorkflows = async () => {
try {
const res = await getWorkflowList();
const workflows = res.data?.listFlowUserRes?.list || [];
commonWorkflows.value = workflows.map((w) => ({
id: String(w.id),
name: w.flowName || '未命名',
prefix: '[工作流] ' + (w.flowName || '') + ':\n',
}));
} catch {
commonWorkflows.value = [];
}
};
const handleSend = () => {
if (selectedWorkflowId.value === null || props.sendDisabled) return;
const msg = message.value.trim();
emit('send', msg || '');
message.value = '';
// 不清除工作流选择,保持表单可见
};
const handleAttachment = () => {
ElMessage.info('附件上传功能开发中...');
};
const toggleWorkflow = (id: string) => {
if (props.workflowLocked) return;
const newId = selectedWorkflowId.value === id ? null : id;
selectedWorkflowId.value = newId;
emit('workflow-select', newId);
};
const resetAll = () => {
message.value = '';
selectedWorkflowId.value = null;
emit('workflow-select', null);
};
const clearWorkflow = () => {
if (props.workflowLocked) return;
selectedWorkflowId.value = null;
emit('workflow-select', null);
};
onMounted(() => {
fetchWorkflows();
});
defineExpose({ resetAll, clearWorkflow, selectedWorkflowId, commonWorkflows });
</script>
<style scoped lang="scss">
.input-shell {
padding: 0 24px 24px;
background: transparent;
}
.input-card {
max-width: 860px;
margin: 0 auto;
background: #fff;
border: 1.5px solid #e2e8f0;
border-radius: 16px;
box-shadow: 0 2px 12px rgba(15, 23, 42, 0.07);
transition:
border-color 0.2s,
box-shadow 0.2s;
overflow: hidden;
&.is-focused {
border-color: #93c5fd;
box-shadow:
0 0 0 3px rgba(59, 130, 246, 0.12),
0 2px 12px rgba(15, 23, 42, 0.07);
}
}
/* 已选标签栏 */
.selected-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 10px 14px 0;
}
.selected-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 8px 3px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
line-height: 1.4;
&.workflow-tag {
background: #eff6ff;
color: #2563eb;
border: 1px solid #bfdbfe;
}
.tag-close {
margin-left: 2px;
cursor: pointer;
opacity: 0.6;
transition: opacity 0.15s;
&:hover {
opacity: 1;
}
}
}
/* 文本域 */
.message-input {
:deep(.el-textarea__inner) {
border: none;
box-shadow: none;
resize: none;
padding: 14px 16px 8px;
font-size: 15px;
line-height: 1.6;
color: #0f172a;
background: transparent;
min-height: 28px !important;
&::placeholder {
color: #94a3b8;
font-weight: 400;
}
}
}
/* 底部工具栏 */
.input-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 10px 10px 12px;
}
.toolbar-left,
.toolbar-right {
display: flex;
align-items: center;
gap: 4px;
}
.tool-icon-btn {
display: inline-flex;
align-items: center;
gap: 4px;
height: 30px;
padding: 0 10px;
border: none;
border-radius: 8px;
background: transparent;
color: #64748b;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition:
background 0.15s,
color 0.15s;
outline: none;
.el-icon {
font-size: 15px;
}
&:hover {
background: #f1f5f9;
color: #334155;
}
&.active {
background: #eff6ff;
color: #2563eb;
}
}
.tool-label {
font-size: 12px;
}
.hint-text {
font-size: 11px;
color: #cbd5e1;
margin-right: 6px;
user-select: none;
}
.send-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: none;
border-radius: 8px;
background: #2563eb;
color: #fff;
cursor: pointer;
transition:
background 0.15s,
transform 0.1s,
opacity 0.15s;
outline: none;
.el-icon {
font-size: 16px;
}
&:hover:not(:disabled) {
background: #1d4ed8;
transform: translateY(-1px);
}
&:disabled {
background: #e2e8f0;
color: #94a3b8;
cursor: not-allowed;
transform: none;
}
}
/* 快捷工作流胶囊 */
.workflow-shortcuts {
max-width: 860px;
margin: 10px auto 0;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.shortcut-pill {
display: inline-flex;
align-items: center;
gap: 5px;
height: 28px;
padding: 0 12px;
border: 1px solid #e2e8f0;
border-radius: 999px;
background: rgba(255, 255, 255, 0.7);
color: #475569;
font-size: 12px;
font-weight: 500;
cursor: pointer;
backdrop-filter: blur(4px);
transition: all 0.15s;
outline: none;
.el-icon {
font-size: 12px;
}
&:hover {
border-color: #93c5fd;
color: #2563eb;
background: rgba(239, 246, 255, 0.9);
}
&.active {
border-color: #3b82f6;
background: #2563eb;
color: #fff;
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
}
}
</style>