Build lightweight AI agent admin

This commit is contained in:
Codex
2026-06-08 18:14:59 +08:00
commit e164840f43
2530 changed files with 435693 additions and 0 deletions
@@ -0,0 +1,278 @@
<script lang="ts" setup>
import type {
AgentImportCheckResult,
AgentImportInput,
} from '#/api/ai-platform/ai-platform';
import { computed, ref, watch } from 'vue';
import { Upload } from '@vben/icons';
import { $t } from '@vben/locales';
import {
ElAlert,
ElButton,
ElDescriptions,
ElDescriptionsItem,
ElInput,
ElMessage,
ElUpload,
} from 'element-plus';
import {
checkImportAgentApi,
importAgentConfigApi,
} from '#/api/ai-platform/ai-platform';
import { ZqDialog } from '#/components/zq-dialog';
import { useAppContextStore } from '#/store/app-context';
interface Props {
modelValue: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
imported: [];
'update:modelValue': [value: boolean];
}>();
const appContextStore = useAppContextStore();
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
type Step = 'check' | 'result' | 'upload';
const step = ref<Step>('upload');
const loading = ref(false);
const importData = ref<AgentImportInput | null>(null);
const checkResult = ref<AgentImportCheckResult | null>(null);
const newCode = ref('');
function resetState() {
step.value = 'upload';
loading.value = false;
importData.value = null;
checkResult.value = null;
newCode.value = '';
}
watch(dialogVisible, (val) => {
if (!val) resetState();
});
const canConfirmImport = computed(() => {
if (!checkResult.value || !importData.value) return false;
if (checkResult.value.code_exists && !newCode.value.trim()) return false;
return true;
});
const showFooter = computed(() => step.value === 'result');
function readFileAsJson(file: File): Promise<AgentImportInput> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
try {
resolve(JSON.parse(reader.result as string));
} catch {
reject(new Error('parse'));
}
};
reader.onerror = () => reject(new Error('read'));
reader.readAsText(file);
});
}
async function handleFileChange(file: any) {
const rawFile = (file.raw || file) as File;
if (!rawFile) return;
loading.value = true;
try {
const data = await readFileAsJson(rawFile);
if (!data.name || !data.code) {
ElMessage.error($t('ai-platform.agent.importExport.fileParseError'));
return;
}
importData.value = data;
newCode.value = data.code;
step.value = 'check';
await runCheck();
} catch {
ElMessage.error($t('ai-platform.agent.importExport.fileParseError'));
} finally {
loading.value = false;
}
}
async function runCheck() {
if (!importData.value) return;
loading.value = true;
try {
checkResult.value = await checkImportAgentApi({
code: importData.value.code,
});
step.value = 'result';
} catch (error: any) {
ElMessage.error(
error?.message || $t('ai-platform.agent.importExport.importFailed'),
);
} finally {
loading.value = false;
}
}
async function handleConfirmImport() {
if (!importData.value || !canConfirmImport.value) return;
loading.value = true;
try {
const payload: AgentImportInput = {
...importData.value,
application_id: appContextStore.currentApp?.id,
code: checkResult.value?.code_exists
? newCode.value.trim()
: importData.value.code,
};
await importAgentConfigApi(payload);
ElMessage.success($t('ai-platform.agent.importExport.importSuccess'));
dialogVisible.value = false;
emit('imported');
} catch (error: any) {
ElMessage.error(
error?.message || $t('ai-platform.agent.importExport.importFailed'),
);
} finally {
loading.value = false;
}
}
</script>
<template>
<ZqDialog
v-model="dialogVisible"
:title="$t('ai-platform.agent.importExport.importTitle')"
:confirm-loading="loading"
width="520px"
:show-footer="showFooter"
@confirm="handleConfirmImport"
>
<template #footer>
<ElButton @click="resetState">
{{ $t('ai-platform.agent.importExport.reselect') }}
</ElButton>
<ElButton
type="primary"
:loading="loading"
:disabled="!canConfirmImport"
@click="handleConfirmImport"
>
{{ $t('ai-platform.agent.importExport.confirmImport') }}
</ElButton>
</template>
<div v-if="step === 'upload' || step === 'check'" class="py-4">
<ElUpload
drag
:auto-upload="false"
:show-file-list="false"
accept=".json"
:disabled="loading"
@change="handleFileChange"
>
<div class="flex flex-col items-center py-6">
<Upload class="text-muted-foreground mb-3 h-10 w-10" />
<div class="text-sm">
{{ $t('ai-platform.agent.importExport.dragOrClick') }}
</div>
<div class="text-muted-foreground mt-1 text-xs">
{{ $t('ai-platform.agent.importExport.onlyJson') }}
</div>
</div>
</ElUpload>
<div
v-if="loading && step === 'check'"
class="text-muted-foreground mt-4 text-center text-sm"
>
{{ $t('ai-platform.agent.importExport.checking') }}
</div>
</div>
<div v-if="step === 'result' && importData && checkResult" class="space-y-4">
<ElAlert
:title="$t('ai-platform.agent.importExport.appTip')"
type="info"
show-icon
:closable="false"
/>
<ElAlert
:title="$t('ai-platform.agent.importExport.refTip')"
type="warning"
show-icon
:closable="false"
/>
<div>
<div class="mb-2 text-sm font-medium">
{{ $t('ai-platform.agent.importExport.agentInfo') }}
</div>
<ElDescriptions :column="1" border size="small">
<ElDescriptionsItem :label="$t('ai-platform.agent.form.name')">
{{ importData.name }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="$t('ai-platform.agent.form.code')">
{{ importData.code }}
</ElDescriptionsItem>
<ElDescriptionsItem
v-if="importData.mode"
:label="$t('ai-platform.agent.form.mode')"
>
{{ importData.mode }}
</ElDescriptionsItem>
<ElDescriptionsItem
v-if="importData.description"
:label="$t('ai-platform.agent.form.description')"
>
{{ importData.description }}
</ElDescriptionsItem>
<ElDescriptionsItem
v-if="importData.model_name"
:label="$t('ai-platform.agent.importExport.modelName')"
>
{{ importData.model_name }}
</ElDescriptionsItem>
<ElDescriptionsItem
v-if="importData.workflow_code"
:label="$t('ai-platform.agent.importExport.workflowCode')"
>
{{ importData.workflow_code }}
</ElDescriptionsItem>
</ElDescriptions>
</div>
<div v-if="checkResult.code_exists">
<ElAlert
:title="$t('ai-platform.agent.importExport.codeConflictTip')"
type="warning"
show-icon
:closable="false"
class="mb-3"
/>
<ElInput
v-model="newCode"
:placeholder="$t('ai-platform.agent.importExport.newCodePlaceholder')"
/>
</div>
<ElAlert
v-else
:title="$t('ai-platform.agent.importExport.codeAvailable')"
type="success"
show-icon
:closable="false"
/>
</div>
</ZqDialog>
</template>