fix: 修复4个已知BUG — P1 ChatList/P2 错误密码/P3 Copilot崩溃/P3超管entrypoint

P1: ChatList.vue — 添加 conversationStats watch fallback,检测 stats>0
   但 chatList 为空时自动触发 fetchConversations
P2: api.js parseAPIErrorResponse — 处理 error 字段为对象时提取 .message
P3: CopilotContainer.vue — activeAssistant 判空保护
P3: 新建 super_admin.html + vite.config.ts rewrite 规则
    (注:entrypoint 仅含样式,Vue应用代码待补)
防御: NotificationTable.vue — 可选链保护 primary_actor.meta 路径
This commit is contained in:
Rogee
2026-07-29 09:55:24 +08:00
parent 9f354b503c
commit 83386dff75
6 changed files with 118 additions and 7 deletions
@@ -797,6 +797,19 @@ onMounted(() => {
}
});
// Fallback: if fetchAllConversations wasn't triggered during onMounted
// (e.g., race condition with route transition), retry after store settles.
// Uses a one-shot watch on conversationStats to detect empty state.
const _fallbackWatch = watch(conversationStats, (stats) => {
if (!stats) return;
const totalCount = (stats.mineCount || 0) + (stats.unAssignedCount || 0) + (stats.allCount || 0);
// If stats show conversations exist but chatList is empty, re-fetch
if (totalCount > 0 && chatLists.value.length === 0 && !chatListLoading.value) {
_fallbackWatch(); // unwatch immediately — one-shot
fetchConversations();
}
}, { immediate: false });
const deleteConversationDialogRef = ref(null);
const selectedConversationId = ref(null);
@@ -66,7 +66,10 @@ const activeAssistant = computed(() => {
if (inboxMatchedAssistant) return inboxMatchedAssistant;
}
// If neither of the above is available, the first assistant in the account takes preference.
return assistants.value[0];
if (assistants.value && assistants.value.length > 0) {
return assistants.value[0];
}
return null;
});
const closeCopilotPanel = () => {
@@ -108,10 +108,10 @@ export default {
</td>
<td class="thumbnail--column">
<Avatar
v-if="notificationItem.primary_actor.meta.assignee"
:src="notificationItem.primary_actor.meta.assignee.thumbnail"
v-if="notificationItem.primary_actor?.meta?.assignee"
:src="notificationItem.primary_actor?.meta?.assignee?.thumbnail || ''"
:size="28"
:name="notificationItem.primary_actor.meta.assignee.name"
:name="notificationItem.primary_actor?.meta?.assignee?.name || ''"
rounded-full
/>
</td>
@@ -92,12 +92,27 @@ export const parseAPIErrorResponse = error => {
return error?.response?.data?.message;
}
if (error?.response?.data?.error) {
return error?.response?.data?.error;
// error could be a string or an object with a message property
if (typeof error.response.data.error === 'string') {
return error.response.data.error;
}
if (error.response.data.error?.message) {
return error.response.data.error.message;
}
return JSON.stringify(error.response.data.error);
}
if (error?.response?.data?.errors) {
return error?.response?.data?.errors[0];
const first = error.response.data.errors;
if (Array.isArray(first)) {
return first[0];
}
return first;
}
return error;
// Fallback: return a useful default message rather than the raw error object
if (error?.message && typeof error.message === 'string') {
return error.message;
}
return 'Request failed. Please try again.';
};
export const throwErrorMessage = error => {