feat: add admin permissions voice input analytics and feedback

This commit is contained in:
2026-08-11 17:17:59 +08:00
parent 3b9215cc3d
commit fd2da26d10
46 changed files with 1836 additions and 192 deletions

View File

@@ -44,6 +44,10 @@ const statusText = ref("连接后端中");
const toastText = ref("");
const followingOutput = ref(true);
const messageList = ref<InstanceType<typeof MessageList> | null>(null);
const feedbackDialogOpen = ref(false);
const feedbackMessageId = ref<number | null>(null);
const feedbackContent = ref("");
const feedbackSubmitting = ref(false);
const activeAbortController = ref<AbortController | null>(null);
let toastTimer: number | null = null;
let settlementPollVersion = 0;
@@ -234,6 +238,13 @@ async function runGeneration(sessionId: number, message: string, assistantIndex:
currentAssistant().streaming = false;
currentAssistant().retryQuestion = undefined;
currentAssistant().createdAt = new Date().toISOString();
try {
const latestHistory = await api.history(sessionId);
const latestAssistant = [...latestHistory].reverse().find((item) => item.role === "assistant");
if (latestAssistant) currentAssistant().id = String(latestAssistant.id);
} catch {
// 回答已经成功,历史记录偶发刷新失败不应将本次回答标记为失败。
}
await refreshSessionList();
await refreshProfile();
} catch (error) {
@@ -252,6 +263,29 @@ async function runGeneration(sessionId: number, message: string, assistantIndex:
}
}
function openFeedback(messageId: string) {
const parsed = Number(messageId);
if (!Number.isInteger(parsed)) return;
feedbackMessageId.value = parsed;
feedbackContent.value = "";
feedbackDialogOpen.value = true;
}
async function submitFeedback() {
const content = feedbackContent.value.trim();
if (!feedbackMessageId.value || !content || feedbackSubmitting.value) return;
feedbackSubmitting.value = true;
try {
await api.submitFeedback(feedbackMessageId.value, content);
feedbackDialogOpen.value = false;
showToast("感谢反馈,管理员会查看你提交的问题");
} catch (error) {
showToast(error instanceof Error ? error.message : "反馈提交失败");
} finally {
feedbackSubmitting.value = false;
}
}
function chatErrorMessage(error: unknown) {
const message = error instanceof Error ? error.message : "";
if (/429|too many|请求过多|排队/i.test(message)) return "当前请求较多AI 服务暂时繁忙。";
@@ -607,6 +641,7 @@ async function copyText(text: string) {
:loading-session="loadingSession"
@follow-change="followingOutput = $event"
@retry="retryMessage"
@feedback="openFeedback"
/>
<ChatComposer :loading="sending" :disabled="!activeSessionId || loadingSession" @send="send" @stop="stop" />
<SessionDrawer
@@ -626,6 +661,14 @@ async function copyText(text: string) {
<div v-if="toastText" class="chat-toast" role="status">{{ toastText }}</div>
<AppDialog v-if="feedbackDialogOpen" title="反馈这次回答" labelled-by="feedback-dialog-title" @close="feedbackDialogOpen = false">
<section class="feedback-dialog">
<p>请简单描述这次回答存在的问题管理员可以结合当时的对话记录查看</p>
<textarea v-model="feedbackContent" maxlength="200" rows="5" aria-label="反馈内容" placeholder="例如:回答没有解决我的问题、内容不准确……" />
<div class="feedback-dialog-footer"><span>{{ feedbackContent.length }}/200</span><button type="button" :disabled="!feedbackContent.trim() || feedbackSubmitting" @click="submitFeedback">{{ feedbackSubmitting ? "提交中" : "提交反馈" }}</button></div>
</section>
</AppDialog>
<PersonalCenterDialog
v-if="personalCenterOpen && user"
:user="user"