mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
refactor(ui): swap ElMessage for project-native mcToast, finish mcConfirm migration
This commit is contained in:
parent
15ee205ab9
commit
e10dd9d0a0
@ -70,7 +70,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { authApi } from '@/api/index'
|
||||
|
||||
const props = defineProps<{ visible: boolean }>()
|
||||
@ -98,21 +98,21 @@ function close() {
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!form.oldPassword || !form.newPassword) {
|
||||
ElMessage.warning(t('auth.fieldsRequired'))
|
||||
mcToast.warning(t('auth.fieldsRequired'))
|
||||
return
|
||||
}
|
||||
if (form.newPassword !== form.confirmPassword) {
|
||||
ElMessage.warning(t('auth.passwordMismatch'))
|
||||
mcToast.warning(t('auth.passwordMismatch'))
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const userId = Number(localStorage.getItem('userId') || '1')
|
||||
await authApi.changePassword(userId, form.oldPassword, form.newPassword)
|
||||
ElMessage.success(t('auth.passwordChanged'))
|
||||
mcToast.success(t('auth.passwordChanged'))
|
||||
close()
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.msg || error?.message || t('auth.passwordChangeFailed'))
|
||||
mcToast.error(error?.msg || error?.message || t('auth.passwordChangeFailed'))
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
|
||||
@ -468,7 +468,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { CHANNEL_FIELD_DEFS } from '@/types'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
import type { Agent, Channel, ChannelFieldDef } from '@/types'
|
||||
@ -695,16 +695,16 @@ async function copyWebhookUrl() {
|
||||
copyLabel.value = t('channels.webhook.copied')
|
||||
setTimeout(() => { copyLabel.value = t('channels.webhook.copy') }, 2000)
|
||||
} catch {
|
||||
ElMessage.warning(t('channels.webhook.copyFailed'))
|
||||
mcToast.warning(t('channels.webhook.copyFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
async function copyText(text: string) {
|
||||
try {
|
||||
await copyToClipboard(text)
|
||||
ElMessage.success(t('common.copied'))
|
||||
mcToast.success(t('common.copied'))
|
||||
} catch {
|
||||
ElMessage.warning(t('channels.webhook.copyFailed'))
|
||||
mcToast.warning(t('channels.webhook.copyFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -840,7 +840,7 @@ function save() {
|
||||
if (rawConfigJson.value.trim()) {
|
||||
try { JSON.parse(rawConfigJson.value) }
|
||||
catch {
|
||||
ElMessage.error(t('channels.messages.invalidJson'))
|
||||
mcToast.error(t('channels.messages.invalidJson'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { CHANNEL_FIELD_DEFS } from '@/types'
|
||||
import type { Agent, Channel, ChannelFieldDef } from '@/types'
|
||||
import { channelApi } from '@/api'
|
||||
@ -713,11 +713,11 @@ async function onDone() {
|
||||
enabled: true,
|
||||
}
|
||||
const res: any = await channelApi.create(payload)
|
||||
ElMessage.success(t('channels.messages.saveSuccess'))
|
||||
mcToast.success(t('channels.messages.saveSuccess'))
|
||||
emit('created', res.data as Channel)
|
||||
close()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('channels.wizard.saveFailed'))
|
||||
mcToast.error(e?.message || t('channels.wizard.saveFailed'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
|
||||
@ -407,7 +407,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import {
|
||||
ArrowDown,
|
||||
CloseBold,
|
||||
@ -1062,10 +1062,10 @@ function handleFeedbackAction(action: string) {
|
||||
`Timestamp: ${new Date(feedbackInfo.value?.timestamp || Date.now()).toISOString()}`,
|
||||
].join('\n')
|
||||
copyToClipboard(lines).then(() => {
|
||||
ElMessage.success(t('chat.feedback.reportCopied'))
|
||||
mcToast.success(t('chat.feedback.reportCopied'))
|
||||
}).catch(() => {
|
||||
console.error('[feedback_event] copy failed:\n' + lines)
|
||||
ElMessage.error(t('chat.feedback.reportFailed'))
|
||||
mcToast.error(t('chat.feedback.reportFailed'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { CloseBold, Loading, Microphone, Service } from '@element-plus/icons-vue'
|
||||
import { WavRecorder } from '@/utils/wavEncoder'
|
||||
|
||||
@ -186,7 +186,7 @@ function connectWebSocket() {
|
||||
playAudioUrl(data.url)
|
||||
break
|
||||
case 'error':
|
||||
ElMessage.error(data.message || t('talk.connectionError'))
|
||||
mcToast.error(data.message || t('talk.connectionError'))
|
||||
state.value = 'idle'
|
||||
break
|
||||
}
|
||||
@ -199,7 +199,7 @@ function connectWebSocket() {
|
||||
console.warn('[TalkMode] WS error', e)
|
||||
if (state.value === 'connecting') {
|
||||
state.value = 'failed'
|
||||
ElMessage.error(t('talk.connectionError'))
|
||||
mcToast.error(t('talk.connectionError'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@ async function startListening() {
|
||||
console.debug('[TalkMode] listening started')
|
||||
} catch (err) {
|
||||
console.warn('[TalkMode] startListening failed', err)
|
||||
ElMessage.error(t('talk.micError'))
|
||||
mcToast.error(t('talk.micError'))
|
||||
state.value = 'idle'
|
||||
recorder = null
|
||||
}
|
||||
@ -285,7 +285,7 @@ async function stopListening() {
|
||||
// firing (suspended AudioContext) or the recording was so short no
|
||||
// sample buffer landed. Surface a clear hint instead of a silent idle.
|
||||
console.warn('[TalkMode] stop returned no audio (recording too short or context suspended)')
|
||||
ElMessage.warning(t('talk.tooShort') || '录音过短,请按住按钮多说几秒')
|
||||
mcToast.warning(t('talk.tooShort') || '录音过短,请按住按钮多说几秒')
|
||||
state.value = 'idle'
|
||||
return
|
||||
}
|
||||
@ -302,7 +302,7 @@ async function stopListening() {
|
||||
// dropped before we got here. Tell the user instead of silently going
|
||||
// idle — they'd otherwise blame the mic.
|
||||
console.warn('[TalkMode] WS not open at stop time, readyState=', ws?.readyState)
|
||||
ElMessage.error(t('talk.connectionError'))
|
||||
mcToast.error(t('talk.connectionError'))
|
||||
state.value = 'idle'
|
||||
}
|
||||
}
|
||||
@ -323,7 +323,7 @@ async function playAudio(blob: Blob) {
|
||||
source.start(0)
|
||||
state.value = 'speaking'
|
||||
} catch {
|
||||
ElMessage.warning(t('talk.playbackError'))
|
||||
mcToast.warning(t('talk.playbackError'))
|
||||
state.value = 'idle'
|
||||
}
|
||||
}
|
||||
|
||||
138
mateclaw-ui/src/components/common/McToast.vue
Normal file
138
mateclaw-ui/src/components/common/McToast.vue
Normal file
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="mc-toast" :class="`mc-toast--${type}`" role="status" aria-live="polite">
|
||||
<span class="mc-toast__indicator">
|
||||
<svg
|
||||
v-if="type === 'success'"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else-if="type === 'error'"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else-if="type === 'warning'"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<line x1="12" y1="9" x2="12" y2="13" />
|
||||
<circle cx="12" cy="17" r="0.6" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<line x1="12" y1="11" x2="12" y2="16" />
|
||||
<circle cx="12" cy="8" r="0.6" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="mc-toast__message">{{ message }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// Single frosted toast row. Layout, animation, and stacking live in
|
||||
// useMcToast — this component just renders one row with the right
|
||||
// status accent.
|
||||
defineProps<{
|
||||
type: 'success' | 'error' | 'warning' | 'info'
|
||||
message: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mc-toast {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px 10px 12px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 250, 245, 0.82);
|
||||
backdrop-filter: blur(28px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(180%);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255, 255, 255, 0.6) inset,
|
||||
0 8px 28px rgba(25, 14, 8, 0.18);
|
||||
color: var(--mc-text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.005em;
|
||||
max-width: min(440px, calc(100vw - 32px));
|
||||
pointer-events: auto;
|
||||
}
|
||||
:global(html.dark .mc-toast) {
|
||||
background: rgba(32, 26, 22, 0.86);
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255, 255, 255, 0.06) inset,
|
||||
0 8px 28px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.mc-toast__indicator {
|
||||
flex-shrink: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mc-toast--success .mc-toast__indicator {
|
||||
background: var(--mc-success);
|
||||
box-shadow: 0 0 0 4px rgba(90, 138, 90, 0.16);
|
||||
}
|
||||
.mc-toast--error .mc-toast__indicator {
|
||||
background: var(--mc-danger);
|
||||
box-shadow: 0 0 0 4px rgba(200, 60, 60, 0.16);
|
||||
}
|
||||
.mc-toast--warning .mc-toast__indicator {
|
||||
background: var(--mc-primary);
|
||||
box-shadow: 0 0 0 4px rgba(217, 119, 87, 0.18);
|
||||
}
|
||||
.mc-toast--info .mc-toast__indicator {
|
||||
background: var(--mc-text-tertiary);
|
||||
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
:global(html.dark .mc-toast--info .mc-toast__indicator) {
|
||||
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.mc-toast__message {
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
@ -149,7 +149,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { skillInstallApi } from '@/api/index'
|
||||
import type { InstallTask, HubSkillInfo } from '@/types/index'
|
||||
|
||||
@ -215,7 +215,7 @@ async function doInstall(bundleUrl: string) {
|
||||
currentTask.value = res.data
|
||||
startPolling(res.data.taskId)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('skills.import.failed'))
|
||||
mcToast.error(e?.message || t('skills.import.failed'))
|
||||
installing.value = false
|
||||
}
|
||||
}
|
||||
@ -231,7 +231,7 @@ function startPolling(taskId: string) {
|
||||
stopPolling()
|
||||
installing.value = false
|
||||
if (status === 'COMPLETED') {
|
||||
ElMessage.success(t('skills.import.installed'))
|
||||
mcToast.success(t('skills.import.installed'))
|
||||
// The install task's result envelope carries `{name, enabled, sourceUrl, ...}`.
|
||||
// Hand the slug along so the parent can pop preflight for the
|
||||
// freshly-installed skill if its requirements aren't met.
|
||||
@ -256,7 +256,7 @@ async function cancelInstall() {
|
||||
if (!currentTask.value) return
|
||||
try {
|
||||
await skillInstallApi.cancelInstall(currentTask.value.taskId)
|
||||
ElMessage.info(t('skills.import.cancelled'))
|
||||
mcToast.info(t('skills.import.cancelled'))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@ -272,7 +272,7 @@ function handleFileSelect(e: Event) {
|
||||
if (file && file.name.endsWith('.zip')) {
|
||||
zipFile.value = file
|
||||
} else if (file) {
|
||||
ElMessage.warning(t('skills.import.invalidZip'))
|
||||
mcToast.warning(t('skills.import.invalidZip'))
|
||||
}
|
||||
input.value = ''
|
||||
}
|
||||
@ -283,7 +283,7 @@ function handleDrop(e: DragEvent) {
|
||||
if (file && file.name.endsWith('.zip')) {
|
||||
zipFile.value = file
|
||||
} else {
|
||||
ElMessage.warning(t('skills.import.invalidZip'))
|
||||
mcToast.warning(t('skills.import.invalidZip'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ function formatSize(bytes: number): string {
|
||||
async function uploadZip() {
|
||||
if (!zipFile.value) return
|
||||
if (zipFile.value.size > 50 * 1024 * 1024) {
|
||||
ElMessage.error(t('skills.import.tooLarge'))
|
||||
mcToast.error(t('skills.import.tooLarge'))
|
||||
return
|
||||
}
|
||||
installing.value = true
|
||||
@ -305,11 +305,11 @@ async function uploadZip() {
|
||||
enable: enableAfterInstall.value,
|
||||
overwrite: overwriteExisting.value,
|
||||
})
|
||||
ElMessage.success(t('skills.import.uploadSuccess'))
|
||||
mcToast.success(t('skills.import.uploadSuccess'))
|
||||
zipFile.value = null
|
||||
emit('installed', { name: res?.data?.name })
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.msg || e?.message || t('skills.import.uploadFailed'))
|
||||
mcToast.error(e?.response?.data?.msg || e?.message || t('skills.import.uploadFailed'))
|
||||
} finally {
|
||||
installing.value = false
|
||||
}
|
||||
@ -325,7 +325,7 @@ async function doSearch() {
|
||||
searchResults.value = res.data || []
|
||||
searchDone.value = true
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('skills.import.searchFailed'))
|
||||
mcToast.error(e?.message || t('skills.import.searchFailed'))
|
||||
searchResults.value = []
|
||||
searchDone.value = true
|
||||
} finally {
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { skillApi } from '@/api/index'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
|
||||
@ -133,7 +133,7 @@ async function reload() {
|
||||
featureStatuses.value = data.featureStatuses || {}
|
||||
activeFeatures.value = Array.isArray(data.activeFeatures) ? data.activeFeatures : []
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.preflight.loadFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.preflight.loadFailed'))
|
||||
statuses.value = []
|
||||
allMet.value = false
|
||||
} finally {
|
||||
@ -148,9 +148,9 @@ function handleClose() {
|
||||
async function copy(cmd: string) {
|
||||
try {
|
||||
await copyToClipboard(cmd)
|
||||
ElMessage.success(t('common.copied'))
|
||||
mcToast.success(t('common.copied'))
|
||||
} catch {
|
||||
ElMessage.warning(t('common.copyFailed'))
|
||||
mcToast.warning(t('common.copyFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -102,7 +102,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { skillApi, type SkillSecretSummary } from '@/api'
|
||||
|
||||
const props = defineProps<{
|
||||
@ -140,7 +141,7 @@ async function load() {
|
||||
rows.value = (res?.data ?? []) as SkillSecretSummary[]
|
||||
} catch (e: any) {
|
||||
rows.value = []
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretLoadFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretLoadFailed'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -173,11 +174,11 @@ async function save() {
|
||||
saving.value = true
|
||||
try {
|
||||
await skillApi.putSecret(props.skillId, form.value.key.trim(), form.value.value)
|
||||
ElMessage.success(t('skills.detail.secretSaveSuccess'))
|
||||
mcToast.success(t('skills.detail.secretSaveSuccess'))
|
||||
dialogVisible.value = false
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretSaveFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretSaveFailed'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
@ -185,21 +186,18 @@ async function save() {
|
||||
|
||||
async function removeSecret(key: string) {
|
||||
if (props.skillId == null) return
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
t('skills.detail.secretDeleteConfirm', { key }),
|
||||
t('common.confirm'),
|
||||
{ type: 'warning' },
|
||||
)
|
||||
} catch {
|
||||
return // user cancelled
|
||||
}
|
||||
const ok = await mcConfirm({
|
||||
title: t('common.confirm'),
|
||||
message: t('skills.detail.secretDeleteConfirm', { key }),
|
||||
tone: 'danger',
|
||||
})
|
||||
if (!ok) return
|
||||
try {
|
||||
await skillApi.deleteSecret(props.skillId, key)
|
||||
ElMessage.success(t('skills.detail.secretDeleteSuccess'))
|
||||
mcToast.success(t('skills.detail.secretDeleteSuccess'))
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretDeleteFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.detail.secretDeleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import {
|
||||
workflowApi,
|
||||
type GeneratedDraft,
|
||||
@ -272,7 +272,7 @@ async function onGenerate() {
|
||||
const res = await workflowApi.generateDraft(desc)
|
||||
result.value = res.data as unknown as GeneratedDraft
|
||||
} catch (e) {
|
||||
ElMessage.error(t('workflows.generate.failed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('workflows.generate.failed', { msg: (e as Error).message }))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { channelApi } from '@/api'
|
||||
|
||||
export type DingTalkRegisterStatus = '' | 'waiting' | 'confirmed' | 'expired' | 'denied'
|
||||
@ -52,13 +52,13 @@ export function useDingTalkAppRegister(onConfirmed: (r: DingTalkRegisterResult)
|
||||
sessionId = res?.data?.session_id || res?.session_id || ''
|
||||
if (!sessionId) {
|
||||
loading.value = false
|
||||
ElMessage.error(t('channels.dingtalkRegister.startFailed'))
|
||||
mcToast.error(t('channels.dingtalkRegister.startFailed'))
|
||||
return
|
||||
}
|
||||
status.value = 'waiting'
|
||||
} catch {
|
||||
loading.value = false
|
||||
ElMessage.error(t('channels.dingtalkRegister.startFailed'))
|
||||
mcToast.error(t('channels.dingtalkRegister.startFailed'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ export function useDingTalkAppRegister(onConfirmed: (r: DingTalkRegisterResult)
|
||||
const clientSecret = data.client_secret || ''
|
||||
if (clientId && clientSecret) {
|
||||
onConfirmed({ clientId, clientSecret })
|
||||
ElMessage.success(t('channels.dingtalkRegister.confirmed'))
|
||||
mcToast.success(t('channels.dingtalkRegister.confirmed'))
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -95,11 +95,11 @@ export function useDingTalkAppRegister(onConfirmed: (r: DingTalkRegisterResult)
|
||||
if (s === 'expired') {
|
||||
stopPolling()
|
||||
loading.value = false
|
||||
ElMessage.warning(t('channels.dingtalkRegister.expired'))
|
||||
mcToast.warning(t('channels.dingtalkRegister.expired'))
|
||||
} else if (s === 'denied') {
|
||||
stopPolling()
|
||||
loading.value = false
|
||||
ElMessage.warning(t('channels.dingtalkRegister.denied'))
|
||||
mcToast.warning(t('channels.dingtalkRegister.denied'))
|
||||
}
|
||||
} catch {
|
||||
// Silent — transient network errors should not abort the loop.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { channelApi } from '@/api'
|
||||
|
||||
export type FeishuRegisterStatus = '' | 'pending' | 'waiting' | 'confirmed' | 'expired' | 'denied' | 'error'
|
||||
@ -52,13 +52,13 @@ export function useFeishuAppRegister(onConfirmed: (r: FeishuRegisterResult) => v
|
||||
sessionId = res?.data?.session_id || res?.session_id || ''
|
||||
if (!sessionId) {
|
||||
loading.value = false
|
||||
ElMessage.error(t('channels.feishuRegister.startFailed'))
|
||||
mcToast.error(t('channels.feishuRegister.startFailed'))
|
||||
return
|
||||
}
|
||||
status.value = 'pending'
|
||||
} catch {
|
||||
loading.value = false
|
||||
ElMessage.error(t('channels.feishuRegister.startFailed'))
|
||||
mcToast.error(t('channels.feishuRegister.startFailed'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ export function useFeishuAppRegister(onConfirmed: (r: FeishuRegisterResult) => v
|
||||
const appSecret = data.client_secret || ''
|
||||
if (appId && appSecret) {
|
||||
onConfirmed({ appId, appSecret })
|
||||
ElMessage.success(t('channels.feishuRegister.confirmed'))
|
||||
mcToast.success(t('channels.feishuRegister.confirmed'))
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -99,15 +99,15 @@ export function useFeishuAppRegister(onConfirmed: (r: FeishuRegisterResult) => v
|
||||
if (s === 'expired') {
|
||||
stopPolling()
|
||||
loading.value = false
|
||||
ElMessage.warning(t('channels.feishuRegister.expired'))
|
||||
mcToast.warning(t('channels.feishuRegister.expired'))
|
||||
} else if (s === 'denied') {
|
||||
stopPolling()
|
||||
loading.value = false
|
||||
ElMessage.warning(t('channels.feishuRegister.denied'))
|
||||
mcToast.warning(t('channels.feishuRegister.denied'))
|
||||
} else if (s === 'error') {
|
||||
stopPolling()
|
||||
loading.value = false
|
||||
ElMessage.error(t('channels.feishuRegister.error'))
|
||||
mcToast.error(t('channels.feishuRegister.error'))
|
||||
}
|
||||
} catch {
|
||||
// Silent — transient network errors should not abort the loop.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
|
||||
const SDK_URL = 'https://wwcdn.weixin.qq.com/node/wework/js/wecom-aibot-sdk@0.1.0.min.js'
|
||||
const SOURCE = 'mateclaw'
|
||||
@ -48,14 +48,14 @@ export function useWecomBotAuth(onSuccess: (r: WecomBotAuthResult) => void) {
|
||||
try {
|
||||
await loadSDK()
|
||||
} catch {
|
||||
ElMessage.error(t('channels.wecom.sdkFailed'))
|
||||
mcToast.error(t('channels.wecom.sdkFailed'))
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
const sdk = (window as any).WecomAIBotSDK
|
||||
if (!sdk) {
|
||||
ElMessage.error(t('channels.wecom.sdkFailed'))
|
||||
mcToast.error(t('channels.wecom.sdkFailed'))
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
@ -68,16 +68,16 @@ export function useWecomBotAuth(onSuccess: (r: WecomBotAuthResult) => void) {
|
||||
(bot: WecomBotAuthResult) => {
|
||||
if (bot?.botid) {
|
||||
onSuccess(bot)
|
||||
ElMessage.success(t('channels.wecom.authSuccess'))
|
||||
mcToast.success(t('channels.wecom.authSuccess'))
|
||||
}
|
||||
},
|
||||
(error: { code: string; message: string }) => {
|
||||
if (error?.code === 'WINDOW_BLOCKED') {
|
||||
ElMessage.error(t('channels.wecom.windowBlocked'))
|
||||
mcToast.error(t('channels.wecom.windowBlocked'))
|
||||
} else if (error?.code === 'CANCELLED') {
|
||||
ElMessage.info(t('channels.wecom.authCancelled'))
|
||||
mcToast.info(t('channels.wecom.authCancelled'))
|
||||
} else {
|
||||
ElMessage.error(t('channels.wecom.authFailed') + ':' + (error?.message || error?.code || ''))
|
||||
mcToast.error(t('channels.wecom.authFailed') + ':' + (error?.message || error?.code || ''))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { channelApi } from '@/api'
|
||||
|
||||
export type WeixinPollStatus = '' | 'polling' | 'scanned' | 'confirmed' | 'expired'
|
||||
@ -52,7 +52,7 @@ export function useWeixinQrcodePoll(onConfirmed: (r: WeixinQrcodeResult) => void
|
||||
const qrcodeId = data?.qrcode || ''
|
||||
|
||||
if (!imgContent && !qrcodeId) {
|
||||
ElMessage.error(t('channels.weixin.qrcodeFailed'))
|
||||
mcToast.error(t('channels.weixin.qrcodeFailed'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -81,21 +81,21 @@ export function useWeixinQrcodePoll(onConfirmed: (r: WeixinQrcodeResult) => void
|
||||
qrcodeImg.value = ''
|
||||
pollStatus.value = 'confirmed'
|
||||
onConfirmed({ botToken: s.bot_token, baseUrl: s.base_url })
|
||||
ElMessage.success(t('channels.weixin.loginSuccess'))
|
||||
mcToast.success(t('channels.weixin.loginSuccess'))
|
||||
}
|
||||
|
||||
if (status === 'expired') {
|
||||
stopPolling()
|
||||
qrcodeImg.value = ''
|
||||
pollStatus.value = 'expired'
|
||||
ElMessage.warning(t('channels.weixin.qrcodeExpired'))
|
||||
mcToast.warning(t('channels.weixin.qrcodeExpired'))
|
||||
}
|
||||
} catch {
|
||||
// Silent — transient network errors should not abort the loop.
|
||||
}
|
||||
}, 2000)
|
||||
} catch {
|
||||
ElMessage.error(t('channels.weixin.qrcodeFailed'))
|
||||
mcToast.error(t('channels.weixin.qrcodeFailed'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
128
mateclaw-ui/src/composables/useMcToast.ts
Normal file
128
mateclaw-ui/src/composables/useMcToast.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { createApp, h, reactive, TransitionGroup } from 'vue'
|
||||
import McToast from '@/components/common/McToast.vue'
|
||||
|
||||
// Project-native toast — frosted glass, spring slide-in from the top,
|
||||
// status-colored indicator (check / x / triangle / circle), auto
|
||||
// dismiss with longer hold for failures so the user has time to read.
|
||||
//
|
||||
// Drop-in replacement for ElMessage's success/error/warning/info calls.
|
||||
// The host element + Vue subtree are mounted lazily on first use so
|
||||
// nothing renders until a toast is actually fired.
|
||||
|
||||
export type McToastType = 'success' | 'error' | 'warning' | 'info'
|
||||
|
||||
export interface McToastOptions {
|
||||
/** Override auto-dismiss in ms. Default: 3000 (success/info), 4000 (warning), 4500 (error). */
|
||||
duration?: number
|
||||
}
|
||||
|
||||
interface ToastEntry {
|
||||
id: number
|
||||
type: McToastType
|
||||
message: string
|
||||
}
|
||||
|
||||
const toasts = reactive<ToastEntry[]>([])
|
||||
let nextId = 1
|
||||
let mounted = false
|
||||
|
||||
function defaultDuration(type: McToastType): number {
|
||||
if (type === 'error') return 4500
|
||||
if (type === 'warning') return 4000
|
||||
return 3000
|
||||
}
|
||||
|
||||
function ensureHost(): void {
|
||||
if (mounted || typeof document === 'undefined') return
|
||||
// HMR safety: if a prior module instance left a host behind, drop it
|
||||
// so this module's reactive array is what renders.
|
||||
document.querySelector('.mc-toast-host')?.remove()
|
||||
mounted = true
|
||||
|
||||
const host = document.createElement('div')
|
||||
host.className = 'mc-toast-host'
|
||||
document.body.appendChild(host)
|
||||
|
||||
// Inject host CSS once — kept here (not scoped to McToast) because the
|
||||
// stack itself is a global overlay sibling of the app root, and the
|
||||
// enter/leave transition names need to match TransitionGroup's class
|
||||
// contract.
|
||||
if (!document.querySelector('#mc-toast-host-style')) {
|
||||
const style = document.createElement('style')
|
||||
style.id = 'mc-toast-host-style'
|
||||
style.textContent = `
|
||||
.mc-toast-host {
|
||||
position: fixed;
|
||||
top: 18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 3000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mc-toast-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.mc-toast-enter-active,
|
||||
.mc-toast-leave-active {
|
||||
transition:
|
||||
transform 0.32s cubic-bezier(0.32, 0.72, 0, 1),
|
||||
opacity 0.22s ease;
|
||||
}
|
||||
.mc-toast-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
.mc-toast-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
.mc-toast-move {
|
||||
transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
|
||||
createApp({
|
||||
render: () =>
|
||||
h(
|
||||
TransitionGroup as unknown as any,
|
||||
{ name: 'mc-toast', tag: 'div', class: 'mc-toast-stack' },
|
||||
() =>
|
||||
toasts.map((t) =>
|
||||
h(McToast, { key: t.id, type: t.type, message: t.message }),
|
||||
),
|
||||
),
|
||||
}).mount(host)
|
||||
}
|
||||
|
||||
function remove(id: number): void {
|
||||
const idx = toasts.findIndex((t) => t.id === id)
|
||||
if (idx >= 0) toasts.splice(idx, 1)
|
||||
}
|
||||
|
||||
function show(type: McToastType, message: string, opts?: McToastOptions): void {
|
||||
ensureHost()
|
||||
const id = nextId++
|
||||
toasts.push({ id, type, message })
|
||||
const duration = opts?.duration ?? defaultDuration(type)
|
||||
setTimeout(() => remove(id), duration)
|
||||
}
|
||||
|
||||
export const mcToast = {
|
||||
success(message: string, opts?: McToastOptions): void {
|
||||
show('success', message, opts)
|
||||
},
|
||||
error(message: string, opts?: McToastOptions): void {
|
||||
show('error', message, opts)
|
||||
},
|
||||
warning(message: string, opts?: McToastOptions): void {
|
||||
show('warning', message, opts)
|
||||
},
|
||||
info(message: string, opts?: McToastOptions): void {
|
||||
show('info', message, opts)
|
||||
},
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcpApi } from '@/api/index'
|
||||
import type { McpServer, McpServerForm, McpTestResult } from '@/views/mcp/types'
|
||||
import { mcpCatalog, type McpCatalogEntry } from '@/views/mcp/catalog'
|
||||
@ -81,7 +81,7 @@ export function useMcpServers() {
|
||||
installed.value = (res?.data ?? []) as McpServer[]
|
||||
}
|
||||
} catch {
|
||||
if (gen === loadGeneration) ElMessage.error(t('mcp.messages.loadFailed'))
|
||||
if (gen === loadGeneration) mcToast.error(t('mcp.messages.loadFailed'))
|
||||
} finally {
|
||||
if (gen === loadGeneration) isLoading.value = false
|
||||
}
|
||||
@ -91,10 +91,10 @@ export function useMcpServers() {
|
||||
isRefreshing.value = true
|
||||
try {
|
||||
await mcpApi.refresh()
|
||||
ElMessage.success(t('mcp.messages.refreshSuccess'))
|
||||
mcToast.success(t('mcp.messages.refreshSuccess'))
|
||||
await reload()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
}
|
||||
@ -104,15 +104,15 @@ export function useMcpServers() {
|
||||
try {
|
||||
if (editing) {
|
||||
await mcpApi.update(editing.id, form)
|
||||
ElMessage.success(t('mcp.messages.updateSuccess'))
|
||||
mcToast.success(t('mcp.messages.updateSuccess'))
|
||||
} else {
|
||||
await mcpApi.create(form)
|
||||
ElMessage.success(t('mcp.messages.createSuccess'))
|
||||
mcToast.success(t('mcp.messages.createSuccess'))
|
||||
}
|
||||
await reload()
|
||||
return true
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -120,11 +120,11 @@ export function useMcpServers() {
|
||||
async function removeServer(server: McpServer): Promise<boolean> {
|
||||
try {
|
||||
await mcpApi.delete(server.id)
|
||||
ElMessage.success(t('mcp.messages.deleteSuccess'))
|
||||
mcToast.success(t('mcp.messages.deleteSuccess'))
|
||||
await reload()
|
||||
return true
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -132,10 +132,10 @@ export function useMcpServers() {
|
||||
async function toggleServer(server: McpServer) {
|
||||
try {
|
||||
await mcpApi.toggle(server.id, !server.enabled)
|
||||
ElMessage.success(t('mcp.messages.toggleSuccess'))
|
||||
mcToast.success(t('mcp.messages.toggleSuccess'))
|
||||
await reload()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('mcp.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -227,7 +227,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { acpApi } from '@/api/index'
|
||||
|
||||
@ -428,7 +428,7 @@ async function loadEndpoints() {
|
||||
endpoints.value = res?.data || []
|
||||
} catch (e: any) {
|
||||
endpoints.value = []
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('acp.loadFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('acp.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -486,7 +486,7 @@ async function saveEndpoint() {
|
||||
if (form.argsJson) JSON.parse(form.argsJson)
|
||||
if (form.envJson) JSON.parse(form.envJson)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(t('acp.invalidJson') + ': ' + (e?.message || 'parse error'))
|
||||
mcToast.error(t('acp.invalidJson') + ': ' + (e?.message || 'parse error'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
@ -498,7 +498,7 @@ async function saveEndpoint() {
|
||||
closeModal()
|
||||
await loadEndpoints()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('acp.saveFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('acp.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -513,7 +513,7 @@ async function removeEndpoint(ep: AcpEndpoint) {
|
||||
await acpApi.delete(ep.id)
|
||||
await loadEndpoints()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('acp.deleteFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('acp.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,7 +522,7 @@ async function toggle(ep: AcpEndpoint) {
|
||||
await acpApi.toggle(ep.id, !ep.enabled)
|
||||
await loadEndpoints()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('acp.toggleFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('acp.toggleFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -205,7 +205,7 @@
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { agentApi, agentContextApi } from '@/api/index'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
@ -293,7 +293,7 @@ async function loadAgents() {
|
||||
selectedAgentId.value = agents.value[0].id
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.loadFailed'))
|
||||
mcToast.error(t('agentContext.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +307,7 @@ async function fetchFiles() {
|
||||
const res: any = await agentContextApi.listFiles(selectedAgentId.value)
|
||||
files.value = res.data || []
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.loadFailed'))
|
||||
mcToast.error(t('agentContext.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,7 +352,7 @@ async function onFileClick(file: WorkspaceFile) {
|
||||
fileContent.value = data?.content || ''
|
||||
originalContent.value = fileContent.value
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.loadFileFailed'))
|
||||
mcToast.error(t('agentContext.loadFileFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,10 +362,10 @@ async function saveContent() {
|
||||
try {
|
||||
await agentContextApi.saveFile(selectedAgentId.value, selectedFile.value.filename, fileContent.value)
|
||||
originalContent.value = fileContent.value
|
||||
ElMessage.success(t('agentContext.saveSuccess'))
|
||||
mcToast.success(t('agentContext.saveSuccess'))
|
||||
await fetchFiles()
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.saveFailed'))
|
||||
mcToast.error(t('agentContext.saveFailed'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
@ -387,9 +387,9 @@ async function toggleFileEnabled(file: WorkspaceFile) {
|
||||
// 同步本地 file 状态
|
||||
const f = files.value.find(x => x.filename === file.filename)
|
||||
if (f) f.enabled = isEnabling
|
||||
ElMessage.success(t('agentContext.promptUpdated'))
|
||||
mcToast.success(t('agentContext.promptUpdated'))
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.promptUpdateFailed'))
|
||||
mcToast.error(t('agentContext.promptUpdateFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,21 +405,21 @@ async function confirmDeleteFile() {
|
||||
|
||||
try {
|
||||
await agentContextApi.deleteFile(selectedAgentId.value, name)
|
||||
ElMessage.success(t('agentContext.deleteSuccess'))
|
||||
mcToast.success(t('agentContext.deleteSuccess'))
|
||||
selectedFile.value = null
|
||||
fileContent.value = ''
|
||||
originalContent.value = ''
|
||||
await fetchFiles()
|
||||
await fetchPromptFiles()
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.deleteFailed'))
|
||||
mcToast.error(t('agentContext.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
async function createNewFile() {
|
||||
const name = newFilename.value.trim()
|
||||
if (!isValidFilename.value) {
|
||||
ElMessage.warning(t('agentContext.invalidFilename'))
|
||||
mcToast.warning(t('agentContext.invalidFilename'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
@ -433,7 +433,7 @@ async function createNewFile() {
|
||||
onFileClick(newFile)
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error(t('agentContext.saveFailed'))
|
||||
mcToast.error(t('agentContext.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -488,7 +488,7 @@
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { agentApi, agentBindingApi, modelApi, skillApi, toolApi, templateApi, backstageApi } from '@/api/index'
|
||||
import type { Agent } from '@/types/index'
|
||||
@ -753,7 +753,7 @@ async function loadAgents() {
|
||||
const res: any = await agentApi.list()
|
||||
agents.value = res.data || []
|
||||
} catch {
|
||||
ElMessage.error(t('agents.messages.loadFailed'))
|
||||
mcToast.error(t('agents.messages.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -831,11 +831,11 @@ async function applyTemplate(id: string) {
|
||||
applyingTemplate.value = true
|
||||
try {
|
||||
await templateApi.apply(id)
|
||||
ElMessage.success(t('agents.templates.applied'))
|
||||
mcToast.success(t('agents.templates.applied'))
|
||||
showTemplateSelector.value = false
|
||||
await loadAgents()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('agents.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('agents.messages.saveFailed'))
|
||||
} finally {
|
||||
applyingTemplate.value = false
|
||||
}
|
||||
@ -930,11 +930,11 @@ async function saveAgent() {
|
||||
])
|
||||
}
|
||||
|
||||
ElMessage.success(t('agents.messages.saveSuccess'))
|
||||
mcToast.success(t('agents.messages.saveSuccess'))
|
||||
closeModal()
|
||||
await loadAgents()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('agents.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('agents.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -947,10 +947,10 @@ async function deleteAgent(agent: Agent) {
|
||||
if (!ok) return
|
||||
try {
|
||||
await agentApi.delete(agent.id)
|
||||
ElMessage.success(t('agents.messages.deleteSuccess'))
|
||||
mcToast.success(t('agents.messages.deleteSuccess'))
|
||||
await loadAgents()
|
||||
} catch {
|
||||
ElMessage.error(t('agents.messages.deleteFailed'))
|
||||
mcToast.error(t('agents.messages.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,10 +966,10 @@ function goToChat(agent: Agent) {
|
||||
async function toggleAgent(agent: Agent) {
|
||||
try {
|
||||
await agentApi.update(agent.id, { ...agent, enabled: !agent.enabled })
|
||||
ElMessage.success(t('agents.messages.toggleSuccess'))
|
||||
mcToast.success(t('agents.messages.toggleSuccess'))
|
||||
await loadAgents()
|
||||
} catch {
|
||||
ElMessage.error(t('agents.messages.toggleFailed'))
|
||||
mcToast.error(t('agents.messages.toggleFailed'))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -172,7 +172,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import SkillIcon from '@/components/common/SkillIcon.vue'
|
||||
import BackstageFocusPanel from '@/components/backstage/BackstageFocusPanel.vue'
|
||||
import { useBackstageAgent } from '@/composables/useBackstageAgent'
|
||||
@ -358,7 +358,7 @@ async function refresh() {
|
||||
if (fresh) detail.value = fresh
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (isInitialLoading.value) ElMessage.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
if (isInitialLoading.value) mcToast.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
} finally {
|
||||
isInitialLoading.value = false
|
||||
}
|
||||
@ -386,10 +386,10 @@ async function confirmStop(run: BackstageRunCard) {
|
||||
if (!ok) return
|
||||
try {
|
||||
await backstageApi.stop(run.conversationId)
|
||||
ElMessage.success(t('backstage.toast.stopped'))
|
||||
mcToast.success(t('backstage.toast.stopped'))
|
||||
refresh()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
mcToast.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,11 +404,11 @@ async function confirmRecycle(run: BackstageRunCard) {
|
||||
if (!ok) return
|
||||
try {
|
||||
await backstageApi.recycle(run.conversationId)
|
||||
ElMessage.success(t('backstage.toast.ended'))
|
||||
mcToast.success(t('backstage.toast.ended'))
|
||||
drawerOpen.value = false
|
||||
refresh()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
mcToast.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,10 +423,10 @@ async function confirmInterruptSub(sub: BackstageSubagentCard) {
|
||||
if (!ok) return
|
||||
try {
|
||||
await backstageApi.interruptSubagent(sub.subagentId)
|
||||
ElMessage.success(t('backstage.toast.subStopped'))
|
||||
mcToast.success(t('backstage.toast.subStopped'))
|
||||
refresh()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
mcToast.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,10 +443,10 @@ async function confirmSweep() {
|
||||
try {
|
||||
const res: any = await backstageApi.sweep()
|
||||
const recycled = res?.data?.recycled ?? 0
|
||||
ElMessage.success(t('backstage.toast.swept', { n: recycled }))
|
||||
mcToast.success(t('backstage.toast.swept', { n: recycled }))
|
||||
refresh()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
mcToast.error(e?.message || t('backstage.errors.loadFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -170,7 +170,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineAsyncComponent, onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { channelApi, agentApi } from '@/api'
|
||||
import type { Channel, Agent } from '@/types'
|
||||
@ -280,7 +280,7 @@ async function loadChannels() {
|
||||
const res: any = await channelApi.list()
|
||||
channels.value = (res.data || []).map((c: any) => ({ ...c }))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(t('channels.messages.loadFailed') + ': ' + (e?.message || ''))
|
||||
mcToast.error(t('channels.messages.loadFailed') + ': ' + (e?.message || ''))
|
||||
channels.value = []
|
||||
}
|
||||
}
|
||||
@ -498,7 +498,7 @@ async function handleSave(payload: Partial<Channel>) {
|
||||
editingChannel.value = null
|
||||
await loadChannels()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('channels.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('channels.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +515,7 @@ async function deleteChannel(id: string | number) {
|
||||
await channelApi.delete(id)
|
||||
await loadChannels()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('channels.messages.deleteFailed'))
|
||||
mcToast.error(e?.message || t('channels.messages.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,7 +525,7 @@ async function toggleChannel(channel: Channel) {
|
||||
await loadChannels()
|
||||
await loadStatus()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('channels.messages.toggleFailed'))
|
||||
mcToast.error(e?.message || t('channels.messages.toggleFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -341,7 +341,7 @@
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { ChatDotRound, Delete, Plus, Setting, UploadFilled } from '@element-plus/icons-vue'
|
||||
import { conversationApi, agentApi, modelApi, chatApi, cronJobApi } from '@/api/index'
|
||||
@ -488,7 +488,7 @@ async function selectModel(value: string) {
|
||||
activeModels.value = res.data || { activeLlm: { providerId, model } }
|
||||
await loadModelState()
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.switchModelFailed'))
|
||||
mcToast.error(t('chat.switchModelFailed'))
|
||||
} finally {
|
||||
modelSaving.value = false
|
||||
}
|
||||
@ -991,19 +991,41 @@ const eligibleModels = computed(() => {
|
||||
})
|
||||
|
||||
// ============ 生命周期 ============
|
||||
function handleKeyboardShortcuts(e: KeyboardEvent) {
|
||||
const mod = e.metaKey || e.ctrlKey
|
||||
if (mod && e.key === 'n') {
|
||||
e.preventDefault()
|
||||
// Global shortcuts (Ctrl+K agents, Ctrl+N new chat) live in MainLayout so they
|
||||
// work from any page; this view reacts to the dispatched event when mounted.
|
||||
function handleChatShortcut(e: Event) {
|
||||
const action = (e as CustomEvent).detail as 'newChat' | 'selectAgent' | undefined
|
||||
if (action === 'newChat') {
|
||||
newConversation()
|
||||
chatInputRef.value?.focus?.()
|
||||
}
|
||||
if (mod && e.key === 'k') {
|
||||
e.preventDefault()
|
||||
nextTick(() => chatInputRef.value?.focus?.())
|
||||
} else if (action === 'selectAgent') {
|
||||
agentDropdownOpen.value = !agentDropdownOpen.value
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-page hand-off from MainLayout's global shortcuts: read once on mount
|
||||
// (before loadAgents triggers syncRouteState, which would wipe the action key)
|
||||
// and apply after agents are loaded so the dropdown actually has something to show.
|
||||
let pendingRouteAction: 'newChat' | 'selectAgent' | '' = ''
|
||||
|
||||
function captureRouteAction() {
|
||||
const action = route.query.action
|
||||
if (action === 'newChat' || action === 'selectAgent') {
|
||||
pendingRouteAction = action
|
||||
}
|
||||
}
|
||||
|
||||
function applyPendingRouteAction() {
|
||||
const action = pendingRouteAction
|
||||
pendingRouteAction = ''
|
||||
if (action === 'newChat') {
|
||||
newConversation()
|
||||
nextTick(() => chatInputRef.value?.focus?.())
|
||||
} else if (action === 'selectAgent') {
|
||||
agentDropdownOpen.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 轮询定时器:让 ChatConsole 能实时感知外部渠道(WeChat/DingTalk/…)推进来的新消息,
|
||||
// 无需 F5 即可看到侧栏列表更新和选中会话的消息/流状态。
|
||||
let activityPollTimer: number | null = null
|
||||
@ -1117,7 +1139,8 @@ async function pollActivity() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
document.addEventListener('keydown', handleKeyboardShortcuts)
|
||||
captureRouteAction()
|
||||
window.addEventListener('mc:chat-shortcut', handleChatShortcut)
|
||||
document.addEventListener('click', handleCodeCopy)
|
||||
startECharts()
|
||||
startKatex()
|
||||
@ -1130,6 +1153,7 @@ onMounted(async () => {
|
||||
mediumQuery.addEventListener('change', handleConvMediumChange)
|
||||
await Promise.all([loadAgents(), loadModelState(), loadConversations()])
|
||||
await hydrateStateFromRoute()
|
||||
consumeRouteAction()
|
||||
activityPollTimer = window.setInterval(pollActivity, ACTIVITY_POLL_MS)
|
||||
elapsedTickTimer = window.setInterval(() => {
|
||||
if (activeCronRuns.value.length > 0) elapsedNow.value = Date.now()
|
||||
@ -1137,7 +1161,7 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('keydown', handleKeyboardShortcuts)
|
||||
window.removeEventListener('mc:chat-shortcut', handleChatShortcut)
|
||||
document.removeEventListener('click', handleCodeCopy)
|
||||
disposeECharts()
|
||||
disposeKatex()
|
||||
@ -1163,6 +1187,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
watch(() => route.query, () => {
|
||||
void hydrateStateFromRoute()
|
||||
consumeRouteAction()
|
||||
})
|
||||
|
||||
watch([selectedAgentId, currentConversationId], () => {
|
||||
@ -1195,7 +1220,7 @@ async function loadAgents() {
|
||||
selectedAgentId.value = agents.value[0].id
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.loadAgentsFailed'))
|
||||
mcToast.error(t('chat.loadAgentsFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1214,7 +1239,7 @@ async function loadModelState() {
|
||||
activeModels.value = activeRes.data || null
|
||||
enabledModels.value = enabledRes.data || []
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.loadModelFailed'))
|
||||
mcToast.error(t('chat.loadModelFailed'))
|
||||
blockingPrompt.value = true
|
||||
recoverablePrompt.value = false
|
||||
return
|
||||
@ -1229,7 +1254,7 @@ async function loadModelState() {
|
||||
providersUnavailable.value = true
|
||||
} else {
|
||||
// Non-403 failure is still a real problem worth surfacing.
|
||||
ElMessage.error(t('chat.loadModelFailed'))
|
||||
mcToast.error(t('chat.loadModelFailed'))
|
||||
}
|
||||
}
|
||||
recomputePromptFlags()
|
||||
@ -1289,7 +1314,7 @@ async function loadConversations() {
|
||||
const res: any = await conversationApi.list()
|
||||
conversations.value = res.data || []
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.loadConversationsFailed'))
|
||||
mcToast.error(t('chat.loadConversationsFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1496,7 +1521,7 @@ async function selectConversation(conv: Conversation) {
|
||||
await reconnectStream(requestedConvId)
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.loadMessagesFailed'))
|
||||
mcToast.error(t('chat.loadMessagesFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,7 +1543,7 @@ async function deleteConversation(conversationId: string) {
|
||||
currentConversationId.value = ''
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.deleteConversationFailed'))
|
||||
mcToast.error(t('chat.deleteConversationFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1603,7 +1628,7 @@ async function handleSendMessage(content: string) {
|
||||
const trimmed = content.trim().toLowerCase()
|
||||
if (trimmed === '/approve' || trimmed === '/deny') {
|
||||
if (!currentConversationId.value) {
|
||||
ElMessage.warning('No active conversation')
|
||||
mcToast.warning('No active conversation')
|
||||
inputText.value = ''
|
||||
chatInputRef.value?.clear?.()
|
||||
return
|
||||
@ -1614,7 +1639,7 @@ async function handleSendMessage(content: string) {
|
||||
m => m.role === 'assistant' && (m as any).metadata?.pendingApproval?.status === 'pending_approval'
|
||||
)
|
||||
if (!pendingMsg) {
|
||||
ElMessage.warning('No pending approval to process')
|
||||
mcToast.warning('No pending approval to process')
|
||||
inputText.value = ''
|
||||
chatInputRef.value?.clear?.()
|
||||
return
|
||||
@ -1638,7 +1663,7 @@ async function handleSendMessage(content: string) {
|
||||
console.error('Approval stream failed:', e)
|
||||
// 回滚乐观更新
|
||||
;(pendingMsg as any).metadata.pendingApproval.status = 'pending_approval'
|
||||
ElMessage.error(e?.message || 'Approval failed')
|
||||
mcToast.error(e?.message || 'Approval failed')
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1745,7 +1770,7 @@ async function reconnectStream(conversationId: string) {
|
||||
await reconnectChatStream(conversationId)
|
||||
} catch (e) {
|
||||
console.error('[ChatConsole] Reconnect failed:', e)
|
||||
ElMessage.warning(t('chat.reconnectFailed') || 'Stream reconnection failed')
|
||||
mcToast.warning(t('chat.reconnectFailed') || 'Stream reconnection failed')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1786,7 +1811,7 @@ async function handleFileSelect(files: File[]) {
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('chat.uploadFailed'))
|
||||
mcToast.error(t('chat.uploadFailed'))
|
||||
} finally {
|
||||
uploadingAttachment.value = false
|
||||
}
|
||||
@ -1986,7 +2011,7 @@ function handleCodeCopy(e: MouseEvent) {
|
||||
if (textEl) textEl.textContent = t('chat.copy')
|
||||
}, 1500)
|
||||
}).catch(() => {
|
||||
ElMessage.error(t('chat.copyFailed'))
|
||||
mcToast.error(t('chat.copyFailed'))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -324,7 +324,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { useCronJobStore } from '@/stores/useCronJobStore'
|
||||
import { useAgentStore } from '@/stores/useAgentStore'
|
||||
@ -436,14 +436,14 @@ async function saveJob() {
|
||||
try {
|
||||
if (editing.value) {
|
||||
await store.updateJob(editing.value.id, form.value)
|
||||
ElMessage.success(t('cronJobs.messages.updateSuccess'))
|
||||
mcToast.success(t('cronJobs.messages.updateSuccess'))
|
||||
} else {
|
||||
await store.createJob(form.value)
|
||||
ElMessage.success(t('cronJobs.messages.createSuccess'))
|
||||
mcToast.success(t('cronJobs.messages.createSuccess'))
|
||||
}
|
||||
closeModal()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || e)
|
||||
mcToast.error(e?.message || e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,9 +456,9 @@ async function handleDelete(job: CronJob) {
|
||||
if (!ok) return
|
||||
try {
|
||||
await store.deleteJob(job.id)
|
||||
ElMessage.success(t('cronJobs.messages.deleteSuccess'))
|
||||
mcToast.success(t('cronJobs.messages.deleteSuccess'))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || e)
|
||||
mcToast.error(e?.message || e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,20 +466,20 @@ async function handleToggle(job: CronJob) {
|
||||
try {
|
||||
const newEnabled = !job.enabled
|
||||
await store.toggleJob(job.id, newEnabled)
|
||||
ElMessage.success(newEnabled ? t('cronJobs.messages.enableSuccess') : t('cronJobs.messages.disableSuccess'))
|
||||
mcToast.success(newEnabled ? t('cronJobs.messages.enableSuccess') : t('cronJobs.messages.disableSuccess'))
|
||||
store.fetchJobs()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || e)
|
||||
mcToast.error(e?.message || e)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRunNow(job: CronJob) {
|
||||
try {
|
||||
await store.runNow(job.id)
|
||||
ElMessage.success(t('cronJobs.messages.runTriggered', { id: job.id }))
|
||||
mcToast.success(t('cronJobs.messages.runTriggered', { id: job.id }))
|
||||
setTimeout(() => store.fetchJobs(), 3000)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || e)
|
||||
mcToast.error(e?.message || e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -235,7 +235,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import {
|
||||
ArrowDown,
|
||||
@ -365,7 +365,7 @@ async function saveDs() {
|
||||
await loadDatasources()
|
||||
const id = saved?.data?.id
|
||||
if (id) autoTestAfterSave(id)
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('datasources.messages.saveFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('datasources.messages.saveFailed')) }
|
||||
}
|
||||
|
||||
async function autoTestAfterSave(id: number | string) {
|
||||
@ -373,7 +373,8 @@ async function autoTestAfterSave(id: number | string) {
|
||||
try {
|
||||
const res: any = await datasourceApi.test(id)
|
||||
const ok = res.data?.success
|
||||
ElMessage({ type: ok ? 'success' : 'warning', message: ok ? t('datasources.messages.testSuccess') : t('datasources.messages.testFailed') })
|
||||
if (ok) mcToast.success(t('datasources.messages.testSuccess'))
|
||||
else mcToast.warning(t('datasources.messages.testFailed'))
|
||||
await loadDatasources()
|
||||
} catch {
|
||||
} finally { testing.value = null }
|
||||
@ -387,7 +388,7 @@ async function testInModal() {
|
||||
form.value = { ...saved.data }
|
||||
await loadDatasources()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('datasources.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('datasources.messages.saveFailed'))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@ -395,7 +396,7 @@ async function testInModal() {
|
||||
await datasourceApi.update(editingDs.value.id, form.value)
|
||||
await loadDatasources()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('datasources.messages.saveFailed'))
|
||||
mcToast.error(e?.message || t('datasources.messages.saveFailed'))
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -420,14 +421,14 @@ async function deleteDs(id: string | number) {
|
||||
try {
|
||||
await datasourceApi.delete(id)
|
||||
await loadDatasources()
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('datasources.messages.deleteFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('datasources.messages.deleteFailed')) }
|
||||
}
|
||||
|
||||
async function toggleDs(ds: Datasource) {
|
||||
try {
|
||||
await datasourceApi.toggle(ds.id, !ds.enabled)
|
||||
await loadDatasources()
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('datasources.messages.toggleFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('datasources.messages.toggleFailed')) }
|
||||
}
|
||||
|
||||
async function testConnection(ds: Datasource) {
|
||||
@ -435,10 +436,11 @@ async function testConnection(ds: Datasource) {
|
||||
try {
|
||||
const res: any = await datasourceApi.test(ds.id)
|
||||
const ok = res.data?.success
|
||||
ElMessage({ type: ok ? 'success' : 'error', message: ok ? t('datasources.messages.testSuccess') : t('datasources.messages.testFailed') })
|
||||
if (ok) mcToast.success(t('datasources.messages.testSuccess'))
|
||||
else mcToast.error(t('datasources.messages.testFailed'))
|
||||
await loadDatasources()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message || t('datasources.messages.testFailed'))
|
||||
mcToast.error(e?.message || t('datasources.messages.testFailed'))
|
||||
} finally { testing.value = null }
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { http } from '@/api'
|
||||
import FactTrustBar from './FactTrustBar.vue'
|
||||
|
||||
@ -109,25 +109,25 @@ function search() { loadFacts() }
|
||||
async function feedback(factId: number, kind: string) {
|
||||
try {
|
||||
await http.post(`/memory/${props.agentId}/facts/${factId}/feedback`, { kind })
|
||||
ElMessage.success(kind === 'HELPFUL' ? '👍' : '👎')
|
||||
mcToast.success(kind === 'HELPFUL' ? '👍' : '👎')
|
||||
loadFacts()
|
||||
} catch (e: any) { ElMessage.error(e.message || 'Failed') }
|
||||
} catch (e: any) { mcToast.error(e.message || 'Failed') }
|
||||
}
|
||||
|
||||
async function forget(factId: number) {
|
||||
try {
|
||||
await http.post(`/memory/${props.agentId}/facts/${factId}/forget`)
|
||||
ElMessage.success(t('memory.facts.forgotten'))
|
||||
mcToast.success(t('memory.facts.forgotten'))
|
||||
loadFacts()
|
||||
} catch (e: any) { ElMessage.error(e.message || 'Failed') }
|
||||
} catch (e: any) { mcToast.error(e.message || 'Failed') }
|
||||
}
|
||||
|
||||
async function resolve(contradictionId: number, resolution: string) {
|
||||
try {
|
||||
await http.post(`/memory/${props.agentId}/facts/contradictions/${contradictionId}/resolve`, { resolution })
|
||||
ElMessage.success(t('memory.facts.resolved'))
|
||||
mcToast.success(t('memory.facts.resolved'))
|
||||
loadContradictions()
|
||||
} catch (e: any) { ElMessage.error(e.message || 'Failed') }
|
||||
} catch (e: any) { mcToast.error(e.message || 'Failed') }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { http, agentContextApi } from '@/api'
|
||||
|
||||
const props = defineProps<{ agentId: number }>()
|
||||
@ -146,12 +146,12 @@ async function saveSection(idx: number) {
|
||||
`/memory/${props.agentId}/dream/reports/0/entries/${encodeURIComponent(heading)}/edit`,
|
||||
{ content: editText.value }
|
||||
)
|
||||
ElMessage.success(t('memory.hil.saved'))
|
||||
mcToast.success(t('memory.hil.saved'))
|
||||
editingIdx.value = -1
|
||||
// Reload file to see changes
|
||||
await loadFile(currentFile.value)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || 'Save failed')
|
||||
mcToast.error(e.message || 'Save failed')
|
||||
} finally { saving.value = false }
|
||||
}
|
||||
|
||||
|
||||
@ -180,7 +180,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { http } from '@/api'
|
||||
import { useAgentStore } from '@/stores/useAgentStore'
|
||||
import { useMemoryStore, type DreamReportItem } from '@/stores/useMemoryStore'
|
||||
@ -251,12 +251,12 @@ async function triggerDream() {
|
||||
dreamRunning.value = true
|
||||
try {
|
||||
await http.post(`/memory/${selectedAgentId.value}/dreaming/focused`, { topic: dreamTopic.value.trim() })
|
||||
ElMessage.success(t('memory.focused.success'))
|
||||
mcToast.success(t('memory.focused.success'))
|
||||
dreamTopic.value = ''
|
||||
dreamInputOpen.value = false
|
||||
loadReports()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || 'Dream failed')
|
||||
mcToast.error(e.message || 'Dream failed')
|
||||
} finally {
|
||||
dreamRunning.value = false
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { pluginApi } from '@/api'
|
||||
|
||||
const { t } = useI18n()
|
||||
@ -145,7 +145,7 @@ async function loadPlugins() {
|
||||
const res = await pluginApi.list()
|
||||
plugins.value = res.data || []
|
||||
} catch (e: any) {
|
||||
ElMessage.error(t('plugins.loadFailed'))
|
||||
mcToast.error(t('plugins.loadFailed'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -156,14 +156,14 @@ async function togglePlugin(plugin: PluginInfo) {
|
||||
try {
|
||||
if (plugin.enabled) {
|
||||
await pluginApi.disable(plugin.name)
|
||||
ElMessage.success(t('plugins.disabled', { name: plugin.displayName || plugin.name }))
|
||||
mcToast.success(t('plugins.disabled', { name: plugin.displayName || plugin.name }))
|
||||
} else {
|
||||
await pluginApi.enable(plugin.name)
|
||||
ElMessage.success(t('plugins.enabled', { name: plugin.displayName || plugin.name }))
|
||||
mcToast.success(t('plugins.enabled', { name: plugin.displayName || plugin.name }))
|
||||
}
|
||||
await loadPlugins()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || t('plugins.toggleFailed'))
|
||||
mcToast.error(e.message || t('plugins.toggleFailed'))
|
||||
await loadPlugins()
|
||||
} finally {
|
||||
toggling.value = null
|
||||
|
||||
@ -119,7 +119,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { workspaceTeamApi } from '@/api/index'
|
||||
import { useWorkspaceStore } from '@/stores/useWorkspaceStore'
|
||||
|
||||
@ -155,7 +155,7 @@ async function fetchMembers() {
|
||||
const res: any = await workspaceTeamApi.listMembers(wsId)
|
||||
members.value = res.data || []
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message)
|
||||
mcToast.error(e.message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -171,12 +171,12 @@ async function addMember() {
|
||||
nickname: newMemberForm.nickname || undefined,
|
||||
role: newMemberForm.role,
|
||||
})
|
||||
ElMessage.success(t('security.members.messages.addSuccess'))
|
||||
mcToast.success(t('security.members.messages.addSuccess'))
|
||||
showAddDialog.value = false
|
||||
Object.assign(newMemberForm, defaultForm())
|
||||
fetchMembers()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || t('security.members.messages.addFailed'))
|
||||
mcToast.error(e?.msg || e?.message || t('security.members.messages.addFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,9 +186,9 @@ async function updateRole(member: Member, role: string) {
|
||||
try {
|
||||
await workspaceTeamApi.updateMemberRole(wsId, member.userId, role)
|
||||
member.role = role
|
||||
ElMessage.success(t('security.members.messages.updateSuccess'))
|
||||
mcToast.success(t('security.members.messages.updateSuccess'))
|
||||
} catch {
|
||||
ElMessage.error(t('security.members.messages.updateFailed'))
|
||||
mcToast.error(t('security.members.messages.updateFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,10 +198,10 @@ async function removeMember(member: Member) {
|
||||
if (!confirm(t('security.members.messages.removeConfirm'))) return
|
||||
try {
|
||||
await workspaceTeamApi.removeMember(wsId, member.userId)
|
||||
ElMessage.success(t('security.members.messages.removeSuccess'))
|
||||
mcToast.success(t('security.members.messages.removeSuccess'))
|
||||
fetchMembers()
|
||||
} catch {
|
||||
ElMessage.error(t('security.members.messages.removeFailed'))
|
||||
mcToast.error(t('security.members.messages.removeFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -220,7 +220,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { securityApi } from '@/api'
|
||||
import { parseJsonArray } from '../composables/helpers'
|
||||
import type { GuardRule } from '@/types'
|
||||
@ -357,15 +357,15 @@ function openEditRuleModal(rule: GuardRule) {
|
||||
|
||||
async function saveRule() {
|
||||
if (!editingRule.value && !ruleForm.ruleId.trim()) {
|
||||
ElMessage.error(t('security.toolGuard.messages.ruleIdRequired'))
|
||||
mcToast.error(t('security.toolGuard.messages.ruleIdRequired'))
|
||||
return
|
||||
}
|
||||
if (!ruleForm.name.trim()) {
|
||||
ElMessage.error(t('security.toolGuard.messages.nameRequired'))
|
||||
mcToast.error(t('security.toolGuard.messages.nameRequired'))
|
||||
return
|
||||
}
|
||||
if (!ruleForm.pattern.trim()) {
|
||||
ElMessage.error(t('security.toolGuard.messages.patternRequired'))
|
||||
mcToast.error(t('security.toolGuard.messages.patternRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
@ -380,9 +380,9 @@ async function saveRule() {
|
||||
} catch (e: any) {
|
||||
const raw = e?.msg || e?.message || ''
|
||||
if (typeof raw === 'string' && raw.toLowerCase().includes('already exists')) {
|
||||
ElMessage.error(t('security.toolGuard.messages.ruleIdDuplicate'))
|
||||
mcToast.error(t('security.toolGuard.messages.ruleIdDuplicate'))
|
||||
} else {
|
||||
ElMessage.error(raw || t('security.toolGuard.messages.saveFailed'))
|
||||
mcToast.error(raw || t('security.toolGuard.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { workspaceTeamApi } from '@/api/index'
|
||||
import { useWorkspaceStore, type Workspace } from '@/stores/useWorkspaceStore'
|
||||
|
||||
@ -170,7 +170,7 @@ async function fetchWorkspaces() {
|
||||
const res: any = await workspaceTeamApi.list()
|
||||
workspaces.value = res.data || []
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.message || 'Failed to fetch workspaces')
|
||||
mcToast.error(e.message || 'Failed to fetch workspaces')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@ -219,11 +219,11 @@ async function saveWorkspace() {
|
||||
})
|
||||
}
|
||||
showDialog.value = false
|
||||
ElMessage.success(t('security.workspaces.messages.saveSuccess'))
|
||||
mcToast.success(t('security.workspaces.messages.saveSuccess'))
|
||||
await fetchWorkspaces()
|
||||
wsStore.fetchWorkspaces()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(t('security.workspaces.messages.saveFailed'))
|
||||
mcToast.error(t('security.workspaces.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,11 +238,11 @@ async function deleteWorkspace() {
|
||||
await workspaceTeamApi.delete(deletingWs.value.id)
|
||||
showDeleteConfirm.value = false
|
||||
deletingWs.value = null
|
||||
ElMessage.success(t('security.workspaces.messages.deleteSuccess'))
|
||||
mcToast.success(t('security.workspaces.messages.deleteSuccess'))
|
||||
await fetchWorkspaces()
|
||||
wsStore.fetchWorkspaces()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(t('security.workspaces.messages.deleteFailed'))
|
||||
mcToast.error(t('security.workspaces.messages.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { conversationApi } from '@/api/index'
|
||||
import { channelIconUrl, sourceLabel } from '@/utils/channelSource'
|
||||
@ -120,7 +120,7 @@ async function loadSessions() {
|
||||
try {
|
||||
const res: any = await conversationApi.list()
|
||||
sessions.value = res.data || []
|
||||
} catch (e: any) { ElMessage.error(t('sessions.loadFailed')) }
|
||||
} catch (e: any) { mcToast.error(t('sessions.loadFailed')) }
|
||||
}
|
||||
|
||||
function viewSession(session: Conversation) {
|
||||
@ -137,7 +137,7 @@ async function deleteSession(conversationId: string) {
|
||||
try {
|
||||
await conversationApi.delete(conversationId)
|
||||
await loadSessions()
|
||||
} catch (e: any) { ElMessage.error(t('sessions.deleteFailed')) }
|
||||
} catch (e: any) { mcToast.error(t('sessions.deleteFailed')) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -71,7 +71,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElIcon, ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { ElIcon } from 'element-plus'
|
||||
import { Loading, WarningFilled } from '@element-plus/icons-vue'
|
||||
import { featureFlagApi, type FeatureFlag } from '@/api/index'
|
||||
|
||||
@ -135,10 +136,10 @@ async function onToggle(flag: FeatureFlag, next: boolean) {
|
||||
try {
|
||||
await featureFlagApi.update(flag.flagKey, { enabled: next })
|
||||
flag.enabled = next
|
||||
ElMessage.success(t(next ? 'settings.featureFlags.enabled' : 'settings.featureFlags.disabled',
|
||||
mcToast.success(t(next ? 'settings.featureFlags.enabled' : 'settings.featureFlags.disabled',
|
||||
{ key: flag.flagKey }))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('settings.featureFlags.toggleFailed'))
|
||||
mcToast.error(e?.message ?? t('settings.featureFlags.toggleFailed'))
|
||||
await load()
|
||||
} finally {
|
||||
pending[flag.flagKey] = false
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import type { ProviderInfo } from '@/types'
|
||||
import ProviderCatalogRow from './ProviderCatalogRow.vue'
|
||||
|
||||
@ -181,7 +181,7 @@ function onEscape(event: KeyboardEvent) {
|
||||
|
||||
async function onEnable(p: ProviderInfo) {
|
||||
await props.enableProvider(p.id)
|
||||
ElMessage.success(t('settings.model.enabledToast', { name: p.name }))
|
||||
mcToast.success(t('settings.model.enabledToast', { name: p.name }))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { computed, reactive, ref, type Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { modelApi } from '@/api'
|
||||
import type { DiscoverResult, ProviderInfo, ProviderModelInfo, TestResult } from '@/types'
|
||||
|
||||
@ -98,7 +98,7 @@ export function useProviderDiscovery(deps: ListDeps) {
|
||||
selectedNewModelIds.value = res.data.newModels.map((m: ProviderModelInfo) => m.id)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : String(error))
|
||||
mcToast.error(error instanceof Error ? error.message : String(error))
|
||||
} finally {
|
||||
discovering.value = false
|
||||
}
|
||||
@ -116,7 +116,7 @@ export function useProviderDiscovery(deps: ListDeps) {
|
||||
await deps.refreshCurrentProvider(deps.currentProvider.value.id)
|
||||
return added
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : String(error))
|
||||
mcToast.error(error instanceof Error ? error.message : String(error))
|
||||
return 0
|
||||
} finally {
|
||||
applyingModels.value = false
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref, type Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { modelApi } from '@/api'
|
||||
import type { EnableResult, ProviderInfo } from '@/types'
|
||||
|
||||
@ -55,7 +55,7 @@ export function useProviderEnablement(deps: ListDeps) {
|
||||
await Promise.all([loadCatalog(), deps.loadProviders()])
|
||||
return res.data as EnableResult
|
||||
} catch (err) {
|
||||
ElMessage.error(err instanceof Error ? err.message : String(err))
|
||||
mcToast.error(err instanceof Error ? err.message : String(err))
|
||||
return null
|
||||
} finally {
|
||||
togglingId.value = null
|
||||
@ -72,14 +72,14 @@ export function useProviderEnablement(deps: ListDeps) {
|
||||
// RFC-074 §2: silent switch + toast. Show the toast here so every caller
|
||||
// (drawer disable, card more-menu, etc.) gets consistent UX.
|
||||
if (result?.defaultSwitched) {
|
||||
ElMessage.success(t('settings.model.defaultSwitchedToast', {
|
||||
mcToast.success(t('settings.model.defaultSwitchedToast', {
|
||||
provider: result.newDefaultProviderId,
|
||||
model: result.newDefaultModel,
|
||||
}))
|
||||
}
|
||||
return result
|
||||
} catch (err) {
|
||||
ElMessage.error(err instanceof Error ? err.message : String(err))
|
||||
mcToast.error(err instanceof Error ? err.message : String(err))
|
||||
return null
|
||||
} finally {
|
||||
togglingId.value = null
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { modelApi } from '@/api'
|
||||
import type { ProviderInfo } from '@/types'
|
||||
@ -159,7 +159,7 @@ export function useProviderForm(deps: ListDeps) {
|
||||
if (!editingProvider.value) {
|
||||
const id = providerForm.id.trim()
|
||||
if (!id || !PROVIDER_ID_PATTERN.test(id)) {
|
||||
ElMessage.error(t('settings.model.providerIdInvalid'))
|
||||
mcToast.error(t('settings.model.providerIdInvalid'))
|
||||
return false
|
||||
}
|
||||
providerForm.id = id
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref, type Ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { claudeCodeOAuthApi, oauthApi } from '@/api'
|
||||
import type { ProviderInfo } from '@/types'
|
||||
|
||||
@ -74,12 +74,12 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
try {
|
||||
start = await oauthApi.deviceStart()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.msg || 'Device code request failed')
|
||||
mcToast.error(e.msg || 'Device code request failed')
|
||||
return
|
||||
}
|
||||
const data = start.data
|
||||
if (!data?.deviceAuthId || !data?.userCode) {
|
||||
ElMessage.error('Device code response was incomplete')
|
||||
mcToast.error('Device code response was incomplete')
|
||||
return
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
return
|
||||
}
|
||||
if (Date.now() > deviceCodeDialog.value.expiresAt) {
|
||||
ElMessage.warning(t('settings.model.oauthDeviceExpired'))
|
||||
mcToast.warning(t('settings.model.oauthDeviceExpired'))
|
||||
closeDeviceCodeDialog()
|
||||
return
|
||||
}
|
||||
@ -111,12 +111,12 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
activeDeviceAuthId = null
|
||||
stopDevicePolling()
|
||||
deviceCodeDialog.value.visible = false
|
||||
ElMessage.success(t('settings.model.oauthLoginSuccess'))
|
||||
mcToast.success(t('settings.model.oauthLoginSuccess'))
|
||||
await reloadProvidersAndSync()
|
||||
return
|
||||
}
|
||||
if (status === 'EXPIRED') {
|
||||
ElMessage.warning(t('settings.model.oauthDeviceExpired'))
|
||||
mcToast.warning(t('settings.model.oauthDeviceExpired'))
|
||||
closeDeviceCodeDialog()
|
||||
return
|
||||
}
|
||||
@ -131,13 +131,13 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
try {
|
||||
const res: any = await claudeCodeOAuthApi.reload()
|
||||
if (res.data?.connected && !res.data?.expired) {
|
||||
ElMessage.success(t('settings.model.oauthLoginSuccess'))
|
||||
mcToast.success(t('settings.model.oauthLoginSuccess'))
|
||||
} else {
|
||||
ElMessage.warning(t('settings.model.claudeCodeOauthInstructions'))
|
||||
mcToast.warning(t('settings.model.claudeCodeOauthInstructions'))
|
||||
}
|
||||
await reloadProvidersAndSync()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.msg || 'Claude Code OAuth detection failed')
|
||||
mcToast.error(e.msg || 'Claude Code OAuth detection failed')
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -160,14 +160,14 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
if (statusRes.data?.connected) {
|
||||
clearInterval(pollInterval)
|
||||
if (authWindow && !authWindow.closed) authWindow.close()
|
||||
ElMessage.success(t('settings.model.oauthLoginSuccess'))
|
||||
mcToast.success(t('settings.model.oauthLoginSuccess'))
|
||||
await reloadProvidersAndSync()
|
||||
}
|
||||
} catch { /* ignore polling errors */ }
|
||||
}, 2000)
|
||||
setTimeout(() => clearInterval(pollInterval), 30000)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.msg || 'OAuth login failed')
|
||||
mcToast.error(e.msg || 'OAuth login failed')
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,15 +176,15 @@ export function useProviderOAuth(deps: FormDeps & ListDeps) {
|
||||
// Code app, not MateClaw — direct the user to log out there instead of
|
||||
// clobbering their machine-level login.
|
||||
if (providerId === 'anthropic-claude-code') {
|
||||
ElMessage.info(t('settings.model.claudeCodeOauthRevokeHint'))
|
||||
mcToast.info(t('settings.model.claudeCodeOauthRevokeHint'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
await oauthApi.revoke()
|
||||
ElMessage.success(t('settings.model.oauthRevokeSuccess'))
|
||||
mcToast.success(t('settings.model.oauthRevokeSuccess'))
|
||||
await reloadProvidersAndSync()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.msg || 'OAuth revoke failed')
|
||||
mcToast.error(e.msg || 'OAuth revoke failed')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { providerPoolApi } from '@/api'
|
||||
import type { ProviderInfo } from '@/types'
|
||||
|
||||
@ -29,13 +29,13 @@ export function useProviderPool(deps: ListDeps) {
|
||||
// refreshes everything the UI needs (badge + status pill + dropdown filter).
|
||||
await deps.loadProviders()
|
||||
if (data.success) {
|
||||
ElMessage.success(t('settings.model.poolReprobeOk'))
|
||||
mcToast.success(t('settings.model.poolReprobeOk'))
|
||||
} else {
|
||||
ElMessage.warning(t('settings.model.poolReprobeFail', { error: data.errorMessage || '—' }))
|
||||
mcToast.warning(t('settings.model.poolReprobeFail', { error: data.errorMessage || '—' }))
|
||||
}
|
||||
return data
|
||||
} catch (err) {
|
||||
ElMessage.error(t('settings.model.poolReprobeFail', {
|
||||
mcToast.error(t('settings.model.poolReprobeFail', {
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
}))
|
||||
} finally {
|
||||
|
||||
@ -191,7 +191,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { ProviderInfo, ProviderModelInfo } from '@/types'
|
||||
@ -333,7 +333,7 @@ async function onSaveApiKey({ provider, apiKey }: { provider: ProviderInfo; apiK
|
||||
await saveProviderApiKey(provider, apiKey)
|
||||
showSavedTip(t('settings.model.inlineApiKeySaved'))
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : t('settings.model.inlineApiKeySaveFailed'))
|
||||
mcToast.error(error instanceof Error ? error.message : t('settings.model.inlineApiKeySaveFailed'))
|
||||
} finally {
|
||||
savingApiKeyId.value = null
|
||||
}
|
||||
@ -350,10 +350,10 @@ async function onSaveProvider() {
|
||||
const saved = await saveProvider()
|
||||
// Issue #39: saveProvider() returns false when client-side validation
|
||||
// (e.g. provider id format) blocks the request — it has already shown
|
||||
// its own ElMessage.error, so don't follow up with a "saved" toast.
|
||||
// its own mcToast.error, so don't follow up with a "saved" toast.
|
||||
if (saved) showSavedTip(t('settings.model.providerSaved'))
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : t('settings.messages.saveFailed'))
|
||||
mcToast.error(error instanceof Error ? error.message : t('settings.messages.saveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,7 +367,7 @@ async function onAddProviderModel() {
|
||||
await addProviderModel()
|
||||
showSavedTip(t('settings.model.modelAdded'))
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : t('settings.model.modelAddFailed'))
|
||||
mcToast.error(error instanceof Error ? error.message : t('settings.model.modelAddFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,7 +376,7 @@ async function onRemoveProviderModel(model: ProviderModelInfo) {
|
||||
await removeProviderModel(model)
|
||||
showSavedTip(t('settings.model.modelRemoved'))
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : t('settings.model.modelRemoveFailed'))
|
||||
mcToast.error(error instanceof Error ? error.message : t('settings.model.modelRemoveFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,7 +385,7 @@ async function onSetActiveModel(model: ProviderModelInfo) {
|
||||
await setActiveModel(model)
|
||||
showSavedTip(t('settings.model.activeChanged'))
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : t('settings.model.activeChangeFailed'))
|
||||
mcToast.error(error instanceof Error ? error.message : t('settings.model.activeChangeFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { copyToClipboard } from '@/utils/clipboard'
|
||||
|
||||
const props = defineProps<{
|
||||
@ -88,7 +88,7 @@ async function copyCode() {
|
||||
copied.value = true
|
||||
setTimeout(() => { copied.value = false }, 2000)
|
||||
} catch {
|
||||
ElMessage.warning(t('settings.model.copyFailed'))
|
||||
mcToast.warning(t('settings.model.copyFailed'))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -654,7 +654,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { skillApi, skillInstallApi } from '@/api/index'
|
||||
import type { Skill, SkillRuntimeStatus, SkillSecurityFinding } from '@/types/index'
|
||||
import ImportHubDialog from '@/components/skill/ImportHubDialog.vue'
|
||||
@ -867,7 +867,7 @@ async function clearLessons() {
|
||||
await skillApi.clearLessons(detailSkill.value.id)
|
||||
detailLessonsRaw.value = ''
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.deleteFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1046,7 +1046,7 @@ async function createSkillFromModal() {
|
||||
openDetailDrawer(fresh, 'overview', { editIdentity: true })
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
} finally {
|
||||
creating.value = false
|
||||
}
|
||||
@ -1124,9 +1124,9 @@ async function saveIdentity() {
|
||||
detailSkill.value = { ...detailSkill.value, ...updated }
|
||||
}
|
||||
editingIdentity.value = false
|
||||
ElMessage.success(t('skills.messages.saveSuccess'))
|
||||
mcToast.success(t('skills.messages.saveSuccess'))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
} finally {
|
||||
savingEdit.value = false
|
||||
}
|
||||
@ -1166,9 +1166,9 @@ async function saveBody() {
|
||||
// Body change → next resolve re-projects icon/version/author from the
|
||||
// new frontmatter, so refresh runtime status to pick those up.
|
||||
loadRuntimeStatus()
|
||||
ElMessage.success(t('skills.detail.sourceSavedReprojection'))
|
||||
mcToast.success(t('skills.detail.sourceSavedReprojection'))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.saveFailed'))
|
||||
} finally {
|
||||
savingEdit.value = false
|
||||
}
|
||||
@ -1207,7 +1207,7 @@ async function deleteSkill(idOrSkill: string | number | Skill) {
|
||||
await skillInstallApi.uninstall(skill.name)
|
||||
await loadAll()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.deleteFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1217,14 +1217,14 @@ async function toggleSkill(skill: Skill) {
|
||||
// toast accurate when the backend would otherwise return err.skill.not_found
|
||||
// on builds that pre-date the rejectVirtualSkillMutation guard).
|
||||
if (isSkillRowVirtual(skill)) {
|
||||
ElMessage.warning(t('skills.virtualReadonlyHint'))
|
||||
mcToast.warning(t('skills.virtualReadonlyHint'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
await skillApi.toggle(skill.id, !skill.enabled)
|
||||
await loadAll()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.messages.toggleFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.messages.toggleFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1233,9 +1233,9 @@ async function handleRefreshRuntime() {
|
||||
try {
|
||||
await skillApi.refreshRuntime()
|
||||
await loadRuntimeStatus()
|
||||
ElMessage.success(t('skills.refreshSuccess'))
|
||||
mcToast.success(t('skills.refreshSuccess'))
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.refreshFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.refreshFailed'))
|
||||
} finally {
|
||||
refreshing.value = false
|
||||
}
|
||||
@ -1270,7 +1270,7 @@ async function rescanSkill(skill: Skill) {
|
||||
// Patch the row in-place so the panel updates without a full page reload.
|
||||
const idx = skills.value.findIndex(s => s.id === skill.id)
|
||||
if (idx >= 0) skills.value.splice(idx, 1, { ...skills.value[idx], ...updated })
|
||||
ElMessage.success(
|
||||
mcToast.success(
|
||||
updated.securityScanStatus === 'FAILED'
|
||||
? t('skills.security.rescanStillFailed')
|
||||
: t('skills.security.rescanPassed')
|
||||
@ -1279,7 +1279,7 @@ async function rescanSkill(skill: Skill) {
|
||||
// Refresh runtime status too so the in-memory badges stay in sync.
|
||||
await loadRuntimeStatus()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skills.security.rescanFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skills.security.rescanFailed'))
|
||||
} finally {
|
||||
rescanning.value = { ...rescanning.value, [key]: false }
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { skillTemplateApi, wikiApi } from '@/api/index'
|
||||
|
||||
interface TemplateField {
|
||||
@ -286,7 +286,7 @@ async function installSkill() {
|
||||
await skillTemplateApi.instantiate(selectedTemplate.value.id, form)
|
||||
step.value = 3
|
||||
} catch (e: any) {
|
||||
ElMessage.error(typeof e === 'string' ? e : e?.message || t('skillTemplates.installFailed'))
|
||||
mcToast.error(typeof e === 'string' ? e : e?.message || t('skillTemplates.installFailed'))
|
||||
} finally {
|
||||
installing.value = false
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { tokenUsageApi } from '@/api/index'
|
||||
import type { TokenUsageSummary } from '@/types/tokenUsage'
|
||||
|
||||
@ -157,7 +157,7 @@ async function fetchData() {
|
||||
data.value = res.data || null
|
||||
} catch (e: any) {
|
||||
const msg = t('tokenUsage.loadFailed')
|
||||
ElMessage.error(msg)
|
||||
mcToast.error(msg)
|
||||
error.value = msg
|
||||
data.value = null
|
||||
} finally {
|
||||
|
||||
@ -130,7 +130,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { toolApi } from '@/api/index'
|
||||
import type { Tool } from '@/types/index'
|
||||
@ -183,7 +183,7 @@ async function saveTool() {
|
||||
}
|
||||
closeModal()
|
||||
await loadTools()
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('tools.messages.saveFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('tools.messages.saveFailed')) }
|
||||
}
|
||||
|
||||
async function deleteTool(id: string | number) {
|
||||
@ -196,14 +196,14 @@ async function deleteTool(id: string | number) {
|
||||
try {
|
||||
await toolApi.delete(id)
|
||||
await loadTools()
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('tools.messages.deleteFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('tools.messages.deleteFailed')) }
|
||||
}
|
||||
|
||||
async function toggleTool(tool: Tool) {
|
||||
try {
|
||||
await toolApi.toggle(tool.id, !tool.enabled)
|
||||
await loadTools()
|
||||
} catch (e: any) { ElMessage.error(e?.message || t('tools.messages.toggleFailed')) }
|
||||
} catch (e: any) { mcToast.error(e?.message || t('tools.messages.toggleFailed')) }
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -171,7 +171,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { triggerApi, type TriggerSummary, workflowApi, type WorkflowSummary } from '@/api'
|
||||
import { useWorkspaceStore } from '@/stores/useWorkspaceStore'
|
||||
@ -361,7 +361,7 @@ async function save() {
|
||||
formOpen.value = false
|
||||
await reload()
|
||||
} catch (e) {
|
||||
ElMessage.error(t('triggers.saveFailed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('triggers.saveFailed', { msg: (e as Error).message }))
|
||||
} finally {
|
||||
busy.value = false
|
||||
}
|
||||
@ -372,7 +372,7 @@ async function toggleEnabled(row: TriggerSummary) {
|
||||
await triggerApi.update(row.id, { ...row, enabled: !row.enabled })
|
||||
await reload()
|
||||
} catch (e) {
|
||||
ElMessage.error(t('triggers.toggleFailed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('triggers.toggleFailed', { msg: (e as Error).message }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ async function remove(row: TriggerSummary) {
|
||||
await triggerApi.delete(row.id)
|
||||
await reload()
|
||||
} catch (e) {
|
||||
ElMessage.error(t('triggers.deleteFailed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('triggers.deleteFailed', { msg: (e as Error).message }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +77,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElIcon, ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { ElIcon } from 'element-plus'
|
||||
import { Loading, WarningFilled } from '@element-plus/icons-vue'
|
||||
import { hotCacheApi, type WikiHotCache } from '@/api/index'
|
||||
import { useWikiStore } from '@/stores/useWikiStore'
|
||||
@ -109,13 +110,13 @@ async function onRegenerate() {
|
||||
regenerating.value = true
|
||||
try {
|
||||
await hotCacheApi.regenerate(store.currentKB.id)
|
||||
ElMessage.success(t('wiki.hotCache.regenerateQueued'))
|
||||
mcToast.success(t('wiki.hotCache.regenerateQueued'))
|
||||
// Background rebuild — poll once after a short delay so the operator sees
|
||||
// the row update without manual refresh. The LLM call typically returns
|
||||
// within 5-15s; if the user hits "regenerate" again, a fresh poll fires.
|
||||
setTimeout(load, 4000)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.hotCache.regenerateFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.hotCache.regenerateFailed'))
|
||||
} finally {
|
||||
regenerating.value = false
|
||||
}
|
||||
@ -126,10 +127,10 @@ async function onReset() {
|
||||
if (!confirm(t('wiki.hotCache.resetConfirm'))) return
|
||||
try {
|
||||
await hotCacheApi.reset(store.currentKB.id)
|
||||
ElMessage.success(t('wiki.hotCache.resetDone'))
|
||||
mcToast.success(t('wiki.hotCache.resetDone'))
|
||||
await load()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.hotCache.resetFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.hotCache.resetFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -286,7 +286,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { Download } from '@element-plus/icons-vue'
|
||||
import { useWikiStore } from '@/stores/useWikiStore'
|
||||
import { wikiApi } from '@/api/index'
|
||||
@ -550,7 +550,7 @@ async function uploadFile(kbId: number, file: File) {
|
||||
} catch (err: any) {
|
||||
item.status = 'error'
|
||||
item.errorMsg = err?.response?.data?.message || err?.message || t('wiki.uploadFailed', { name: file.name })
|
||||
ElMessage.error(t('wiki.uploadFailed', { name: file.name }))
|
||||
mcToast.error(t('wiki.uploadFailed', { name: file.name }))
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,7 +653,7 @@ async function downloadRaw(raw: { id: number; title?: string }) {
|
||||
setTimeout(() => URL.revokeObjectURL(url), 0)
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
ElMessage.error(`${t('wiki.downloadFailed')}: ${msg}`)
|
||||
mcToast.error(`${t('wiki.downloadFailed')}: ${msg}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -333,7 +333,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElIcon, ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import { ElIcon } from 'element-plus'
|
||||
import { Loading, WarningFilled } from '@element-plus/icons-vue'
|
||||
import { wikiApi, modelApi } from '@/api/index'
|
||||
import { useWikiStore, type WikiRawMaterial } from '@/stores/useWikiStore'
|
||||
@ -545,7 +546,7 @@ function closeEditor() {
|
||||
async function onSave() {
|
||||
if (!store.currentKB) return
|
||||
if (!form.name.trim() || !form.title.trim() || !form.promptTemplate.trim()) {
|
||||
ElMessage.warning(t('common.required', 'All fields are required'))
|
||||
mcToast.warning(t('common.required', 'All fields are required'))
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
@ -585,7 +586,7 @@ async function onSave() {
|
||||
closeEditor()
|
||||
await loadAll()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? String(e))
|
||||
mcToast.error(e?.message ?? String(e))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
@ -597,7 +598,7 @@ async function onDelete(tpl: WikiTransformation) {
|
||||
await wikiApi.deleteTransformation(tpl.id)
|
||||
await loadAll()
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? String(e))
|
||||
mcToast.error(e?.message ?? String(e))
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,7 +610,7 @@ async function onApply(tpl: WikiTransformation) {
|
||||
await wikiApi.applyTransformation(tpl.id, rawId, true)
|
||||
await loadRunsFor(tpl.id)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.transformations.runFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.transformations.runFailed'))
|
||||
} finally {
|
||||
runningTemplateId.value = null
|
||||
}
|
||||
@ -624,7 +625,7 @@ async function onSaveRunAsPage(tpl: WikiTransformation, run: WikiTransformationR
|
||||
if (payload.pageId) {
|
||||
run.outputPageId = payload.pageId
|
||||
}
|
||||
ElMessage.success(t('wiki.transformations.saveAsPageDone'))
|
||||
mcToast.success(t('wiki.transformations.saveAsPageDone'))
|
||||
// Refresh the page list in the wiki store so the new page is visible in
|
||||
// the sidebar / search results without a manual reload.
|
||||
if (store.currentKB) {
|
||||
@ -632,7 +633,7 @@ async function onSaveRunAsPage(tpl: WikiTransformation, run: WikiTransformationR
|
||||
}
|
||||
await loadRunsFor(tpl.id)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.transformations.saveAsPageFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.transformations.saveAsPageFailed'))
|
||||
} finally {
|
||||
savingRunId.value = null
|
||||
}
|
||||
@ -682,15 +683,15 @@ async function onAggregate(tpl: WikiTransformation) {
|
||||
const resp: any = await wikiApi.aggregateTransformation(tpl.id, store.currentKB.id)
|
||||
const payload = resp?.data ?? {}
|
||||
if (payload && payload.pageId) {
|
||||
ElMessage.success(`${t('wiki.transformations.aggregateDone')} · ${payload.sourcesUsed} sources`)
|
||||
mcToast.success(`${t('wiki.transformations.aggregateDone')} · ${payload.sourcesUsed} sources`)
|
||||
// Refresh the page list in the wiki store so the new aggregate page
|
||||
// shows up in the sidebar without a manual reload.
|
||||
try { await store.fetchPages(store.currentKB.id) } catch {}
|
||||
} else {
|
||||
ElMessage.info(t('wiki.transformations.aggregateNoRuns'))
|
||||
mcToast.info(t('wiki.transformations.aggregateNoRuns'))
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.transformations.aggregateFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.transformations.aggregateFailed'))
|
||||
} finally {
|
||||
aggregatingTemplateId.value = null
|
||||
}
|
||||
@ -700,10 +701,10 @@ async function onCancelRun(tpl: WikiTransformation, run: WikiTransformationRun)
|
||||
cancellingRunId.value = run.id
|
||||
try {
|
||||
await wikiApi.cancelTransformationRun(run.id)
|
||||
ElMessage.success(t('wiki.transformations.cancelDone'))
|
||||
mcToast.success(t('wiki.transformations.cancelDone'))
|
||||
await loadRunsFor(tpl.id)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.transformations.cancelFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.transformations.cancelFailed'))
|
||||
} finally {
|
||||
cancellingRunId.value = null
|
||||
}
|
||||
@ -716,7 +717,7 @@ async function onRerun(tpl: WikiTransformation, run: WikiTransformationRun) {
|
||||
await wikiApi.applyTransformation(tpl.id, run.rawId, true)
|
||||
await loadRunsFor(tpl.id)
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? t('wiki.transformations.runFailed'))
|
||||
mcToast.error(e?.message ?? t('wiki.transformations.runFailed'))
|
||||
} finally {
|
||||
rerunningRunId.value = null
|
||||
}
|
||||
@ -735,7 +736,7 @@ async function onOpenSavedPage(run: WikiTransformationRun) {
|
||||
await store.loadPage(store.currentKB.id, page.slug)
|
||||
}
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.message ?? String(e))
|
||||
mcToast.error(e?.message ?? String(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -217,7 +217,7 @@
|
||||
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { mcConfirm } from '@/components/common/useConfirm'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { mcToast } from '@/composables/useMcToast'
|
||||
import {
|
||||
agentApi,
|
||||
channelApi,
|
||||
@ -370,7 +370,7 @@ async function onGenerateAccept(draft: GeneratedDraft) {
|
||||
})
|
||||
const created = res.data as unknown as WorkflowSummary
|
||||
if (!created?.id) {
|
||||
ElMessage.error(t('workflows.generate.failed', { msg: 'workflow row not returned' }))
|
||||
mcToast.error(t('workflows.generate.failed', { msg: 'workflow row not returned' }))
|
||||
return
|
||||
}
|
||||
await workflowApi.saveDraft(created.id, draft.draftJson)
|
||||
@ -418,15 +418,15 @@ async function onGenerateAccept(draft: GeneratedDraft) {
|
||||
compileErrors.value = draft.compileErrors
|
||||
}
|
||||
if (triggersCreated > 0 && triggersSkipped === 0) {
|
||||
ElMessage.success(t('workflows.generate.acceptedWithTriggers', { count: triggersCreated }))
|
||||
mcToast.success(t('workflows.generate.acceptedWithTriggers', { count: triggersCreated }))
|
||||
} else if (triggersSkipped > 0) {
|
||||
ElMessage.warning(t('workflows.generate.acceptedSomeTriggersFailed',
|
||||
mcToast.warning(t('workflows.generate.acceptedSomeTriggersFailed',
|
||||
{ ok: triggersCreated, fail: triggersSkipped }))
|
||||
} else {
|
||||
ElMessage.success(t('workflows.generate.compileOk'))
|
||||
mcToast.success(t('workflows.generate.compileOk'))
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(t('workflows.generate.failed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('workflows.generate.failed', { msg: (e as Error).message }))
|
||||
} finally {
|
||||
busy.value = false
|
||||
}
|
||||
@ -640,11 +640,11 @@ async function onResume(entry: PausedRunSummary, outcome: ResumeOutcome) {
|
||||
resumingId.value = entry.run.id
|
||||
try {
|
||||
await workflowApi.resumeRun(entry.run.id, entry.pause.pauseToken, outcome)
|
||||
ElMessage.success(t('workflows.paused.resumeOk', { outcome }))
|
||||
mcToast.success(t('workflows.paused.resumeOk', { outcome }))
|
||||
await reloadPausedRuns()
|
||||
await reloadRuns()
|
||||
} catch (e) {
|
||||
ElMessage.error(t('workflows.paused.resumeFailed', { msg: (e as Error).message }))
|
||||
mcToast.error(t('workflows.paused.resumeFailed', { msg: (e as Error).message }))
|
||||
} finally {
|
||||
resumingId.value = null
|
||||
}
|
||||
@ -777,7 +777,7 @@ async function onPublishSubmit(payload: { note: string }) {
|
||||
// below the fold on smaller viewports.
|
||||
publishDialogOpen.value = false
|
||||
if (compileErrors.value.length) {
|
||||
ElMessage.error(t('workflows.status.compileFailed', { count: compileErrors.value.length }))
|
||||
mcToast.error(t('workflows.status.compileFailed', { count: compileErrors.value.length }))
|
||||
}
|
||||
} finally {
|
||||
busy.value = false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user