feat: 优化AI助手输出界面,简化状态显示逻辑,增强用户交互体验

This commit is contained in:
kuaifan 2025-11-08 23:43:06 +00:00
parent a5adbf80a9
commit 8e518a044a

View File

@ -18,29 +18,27 @@
v-for="response in responses" v-for="response in responses"
:key="response.id || response.localId" :key="response.id || response.localId"
class="ai-assistant-output-item"> class="ai-assistant-output-item">
<div class="ai-assistant-output-apply">
<template v-if="response.status === 'error'">
<span class="ai-assistant-output-error">{{ response.error || $L('发送失败') }}</span>
</template>
<template v-else-if="response.rawOutput">
<Button
type="primary"
size="small"
:loading="response.applyLoading"
class="ai-assistant-apply-btn"
@click="applyResponse(response)">
{{ applyButtonText || $L('应用此内容') }}
</Button>
</template>
<template v-else>
<Icon type="ios-sync" class="ai-assistant-output-icon icon-loading"/>
<span class="ai-assistant-output-status">{{ $L('生成中...') }}</span>
</template>
</div>
<div class="ai-assistant-output-meta"> <div class="ai-assistant-output-meta">
<div class="ai-assistant-output-meta-left"> <span class="ai-assistant-output-model">{{ response.modelLabel || response.model }}</span>
<span class="ai-assistant-output-model">{{ response.modelLabel || response.model }}</span>
</div>
<div class="ai-assistant-output-meta-right">
<template v-if="response.status === 'error'">
<span class="ai-assistant-output-error">{{ response.error || $L('发送失败') }}</span>
</template>
<template v-else-if="response.rawOutput">
<Button
type="primary"
size="small"
:loading="response.applyLoading"
class="ai-assistant-apply-btn"
@click="applyResponse(response)">
{{ $L('应用此内容') }}
</Button>
</template>
<template v-else>
<Icon type="ios-sync" class="ai-assistant-output-icon icon-loading"/>
<span class="ai-assistant-output-status">{{ $L('生成中...') }}</span>
</template>
</div>
</div> </div>
<div v-if="response.prompt" class="ai-assistant-output-question">{{ response.prompt }}</div> <div v-if="response.prompt" class="ai-assistant-output-question">{{ response.prompt }}</div>
<DialogMarkdown <DialogMarkdown
@ -53,13 +51,13 @@
</div> </div>
</div> </div>
<div class="ai-assistant-input"> <div class="ai-assistant-input">
<Input <Input
v-model="inputValue" v-model="inputValue"
type="textarea" type="textarea"
:placeholder="inputPlaceholder" :placeholder="inputPlaceholder || $L('请输入你的问题...')"
:rows="inputRows" :rows="inputRows || 2"
:autosize="inputAutosize" :autosize="inputAutosize || {minRows:2, maxRows:6}"
:maxlength="inputMaxlength" /> :maxlength="inputMaxlength || 500" />
</div> </div>
</div> </div>
<div slot="footer" class="ai-assistant-footer"> <div slot="footer" class="ai-assistant-footer">
@ -86,7 +84,7 @@
</div> </div>
<div class="ai-assistant-footer-btns"> <div class="ai-assistant-footer-btns">
<Button type="text" @click="showModal=false">{{ $L('取消') }}</Button> <Button type="text" @click="showModal=false">{{ $L('取消') }}</Button>
<Button type="primary" :loading="loadIng > 0" @click="onSubmit">{{ $L('确定') }}</Button> <Button type="primary" :loading="loadIng > 0" @click="onSubmit">{{ submitButtonText || $L('确定') }}</Button>
</div> </div>
</div> </div>
</Modal> </Modal>
@ -101,24 +99,6 @@ import DialogMarkdown from "../pages/manage/components/DialogMarkdown.vue";
export default { export default {
name: 'AIAssistant', name: 'AIAssistant',
components: {DialogMarkdown}, components: {DialogMarkdown},
props: {
defaultPlaceholder: {
type: String,
default: '',
},
defaultInputRows: {
type: Number,
default: 2,
},
defaultInputAutosize: {
type: Object,
default: () => ({minRows:2, maxRows:6}),
},
defaultInputMaxlength: {
type: Number,
default: 500,
},
},
data() { data() {
return { return {
// //
@ -127,13 +107,15 @@ export default {
loadIng: 0, loadIng: 0,
pendingAutoSubmit: false, pendingAutoSubmit: false,
autoSubmitTimer: null, autoSubmitTimer: null,
applyButtonText: null,
submitButtonText: null,
// //
inputValue: '', inputValue: '',
inputPlaceholder: this.defaultPlaceholder, inputPlaceholder: null,
inputRows: this.defaultInputRows, inputRows: null,
inputAutosize: this.defaultInputAutosize, inputAutosize: null,
inputMaxlength: this.defaultInputMaxlength, inputMaxlength: null,
// //
applyHook: null, applyHook: null,
@ -186,12 +168,14 @@ export default {
params = {}; params = {};
} }
this.inputValue = params.value || ''; this.inputValue = params.value || '';
this.inputPlaceholder = params.placeholder || this.defaultPlaceholder || this.$L('请输入你的问题...'); this.inputPlaceholder = params.placeholder || null;
this.inputRows = params.rows || this.defaultInputRows; this.inputRows = params.rows || null;
this.inputAutosize = params.autosize || this.defaultInputAutosize; this.inputAutosize = params.autosize || null;
this.inputMaxlength = params.maxlength || this.defaultInputMaxlength; this.inputMaxlength = params.maxlength || null;
this.applyHook = params.onApply || null; this.applyHook = params.onApply || null;
this.beforeSendHook = params.onBeforeSend || null; this.beforeSendHook = params.onBeforeSend || null;
this.applyButtonText = params.applyButtonText || null;
this.submitButtonText = params.submitButtonText || null;
this.renderHook = params.onRender || null; this.renderHook = params.onRender || null;
this.pendingAutoSubmit = !!params.autoSubmit; this.pendingAutoSubmit = !!params.autoSubmit;
// //
@ -841,21 +825,51 @@ export default {
border-top: 1px solid rgba(0, 0, 0, 0.05); border-top: 1px solid rgba(0, 0, 0, 0.05);
} }
.ai-assistant-output-meta { .ai-assistant-output-apply {
position: sticky;
top: 0;
right: 0;
z-index: 1;
display: flex; display: flex;
justify-content: space-between; justify-content: flex-end;
align-items: center; align-items: center;
gap: 12px; height: 24px;
color: #999;
gap: 4px;
} }
.ai-assistant-output-meta-left { .ai-assistant-output-icon {
flex: 1; font-size: 16px;
min-width: 0; color: #52c41a;
}
.ai-assistant-apply-btn {
font-size: 13px;
display: flex;
align-items: center;
justify-content: center;
}
.ai-assistant-output-status {
color: #52c41a;
}
.ai-assistant-output-error {
color: #ff4d4f;
}
.ai-assistant-output-meta {
display: flex;
align-items: center;
height: 24px;
margin-top: -24px;
} }
.ai-assistant-output-model { .ai-assistant-output-model {
display: inline-flex; max-width: 50%;
align-items: center; white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: #2f54eb; color: #2f54eb;
@ -875,34 +889,6 @@ export default {
margin-top: 8px; margin-top: 8px;
} }
.ai-assistant-output-meta-right {
display: flex;
align-items: center;
font-size: 12px;
color: #999;
gap: 4px;
}
.ai-assistant-output-icon {
font-size: 16px;
color: #2f54eb;
}
.ai-assistant-apply-btn {
font-size: 13px;
display: flex;
align-items: center;
justify-content: center;
}
.ai-assistant-output-status {
color: #52c41a;
}
.ai-assistant-output-error {
color: #ff4d4f;
}
.ai-assistant-output-placeholder { .ai-assistant-output-placeholder {
margin-top: 12px; margin-top: 12px;
font-size: 13px; font-size: 13px;