Restore-ZQ-admin-quality-while-trimming-AI-routes
This commit is contained in:
@@ -291,9 +291,12 @@ async function handlePreviewDoc(doc: KnowledgeDocumentListItem) {
|
||||
docPreviewUrlList.value = [url];
|
||||
docPreviewVisible.value = true;
|
||||
} else {
|
||||
const ext = getDocFileExt(doc);
|
||||
const query = new URLSearchParams({ name: doc.name || '', ext });
|
||||
window.open(`/file-preview/${doc.file_id}?${query.toString()}`, '_blank');
|
||||
try {
|
||||
const url = await getFileUrl(doc.file_id);
|
||||
window.open(url, '_blank');
|
||||
} catch {
|
||||
ElMessage.error($t('ai-platform.knowledge.chunkPreview.error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import type { WorkflowRunListItem } from '#/api/ai-platform/ai-platform';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { Eye, RefreshCw, Search } from '@vben/icons';
|
||||
import { Eye } from '@vben/icons';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
ElButton,
|
||||
ElCard,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElOption,
|
||||
ElPagination,
|
||||
ElSelect,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElTag,
|
||||
} from 'element-plus';
|
||||
import { ElButton, ElTag } from 'element-plus';
|
||||
|
||||
import {
|
||||
getAllWorkflowRunsApi,
|
||||
getWorkflowListApi,
|
||||
} from '#/api/ai-platform/ai-platform';
|
||||
import { getAllWorkflowRunsApi } from '#/api/ai-platform/ai-platform';
|
||||
import { useZqTable } from '#/components/zq-table';
|
||||
|
||||
import RunStatusTag from './components/RunStatusTag.vue';
|
||||
import {
|
||||
@@ -31,245 +18,97 @@ import {
|
||||
formatTime,
|
||||
getTagLabel,
|
||||
getTagType,
|
||||
getStatusOptions,
|
||||
getTriggerOptions,
|
||||
useSearchFormSchema,
|
||||
useZqTableColumns,
|
||||
} from './data';
|
||||
import DetailDialog from './modules/detail-dialog.vue';
|
||||
|
||||
defineOptions({ name: 'WorkflowRunHistory' });
|
||||
|
||||
const detailRef = ref<InstanceType<typeof DetailDialog>>();
|
||||
const loading = ref(false);
|
||||
const rows = ref<WorkflowRunListItem[]>([]);
|
||||
const total = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const workflowOptions = ref<Array<{ label: string; value: string }>>([]);
|
||||
const filters = ref({
|
||||
workflowId: '',
|
||||
status: '',
|
||||
triggerType: '',
|
||||
});
|
||||
const statusOptions = getStatusOptions();
|
||||
const triggerOptions = getTriggerOptions();
|
||||
const rowIndex = computed(() => (currentPage.value - 1) * pageSize.value);
|
||||
|
||||
async function loadWorkflowOptions() {
|
||||
const res = await getWorkflowListApi({ page: 1, pageSize: 200 });
|
||||
workflowOptions.value = (res.items || []).map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
}
|
||||
const fetchRunList = async (params: any) => {
|
||||
const res = await getAllWorkflowRunsApi({
|
||||
page: params.page.currentPage,
|
||||
pageSize: params.page.pageSize,
|
||||
workflowId: params.form?.workflowId || undefined,
|
||||
status: params.form?.status || undefined,
|
||||
triggerType: params.form?.triggerType || undefined,
|
||||
});
|
||||
return {
|
||||
items: res.items,
|
||||
total: res.total,
|
||||
};
|
||||
};
|
||||
|
||||
async function loadRuns() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getAllWorkflowRunsApi({
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
workflowId: filters.value.workflowId || undefined,
|
||||
status: filters.value.status || undefined,
|
||||
triggerType: filters.value.triggerType || undefined,
|
||||
});
|
||||
rows.value = res.items || [];
|
||||
total.value = res.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
currentPage.value = 1;
|
||||
loadRuns();
|
||||
}
|
||||
|
||||
function handleSizeChange(size: number) {
|
||||
pageSize.value = size;
|
||||
currentPage.value = 1;
|
||||
loadRuns();
|
||||
}
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
currentPage.value = page;
|
||||
loadRuns();
|
||||
}
|
||||
const [Grid] = useZqTable({
|
||||
gridOptions: {
|
||||
columns: useZqTableColumns(),
|
||||
border: true,
|
||||
stripe: true,
|
||||
showIndex: true,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
ajax: {
|
||||
query: fetchRunList,
|
||||
},
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: true,
|
||||
pageSize: 20,
|
||||
},
|
||||
toolbarConfig: {
|
||||
search: true,
|
||||
refresh: true,
|
||||
zoom: true,
|
||||
custom: true,
|
||||
},
|
||||
},
|
||||
formOptions: {
|
||||
schema: useSearchFormSchema(),
|
||||
showCollapseButton: true,
|
||||
submitOnChange: true,
|
||||
},
|
||||
});
|
||||
|
||||
function openDetail(row: WorkflowRunListItem) {
|
||||
detailRef.value?.open(row.id);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadWorkflowOptions();
|
||||
loadRuns();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<DetailDialog ref="detailRef" />
|
||||
|
||||
<ElCard shadow="never" class="h-full">
|
||||
<ElForm :model="filters" inline class="mb-3">
|
||||
<ElFormItem :label="$t('ai-platform.workflowRuns.filters.workflow')">
|
||||
<ElSelect
|
||||
v-model="filters.workflowId"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 220px"
|
||||
:placeholder="$t('ai-platform.workflowRuns.filters.allWorkflows')"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in workflowOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('ai-platform.workflowRuns.filters.status')">
|
||||
<ElSelect
|
||||
v-model="filters.status"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
:placeholder="$t('ai-platform.workflowRuns.filters.allStatus')"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem :label="$t('ai-platform.workflowRuns.filters.trigger')">
|
||||
<ElSelect
|
||||
v-model="filters.triggerType"
|
||||
clearable
|
||||
style="width: 160px"
|
||||
:placeholder="$t('ai-platform.workflowRuns.filters.allTriggers')"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in triggerOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<ElButton type="primary" :icon="Search" @click="handleSearch">
|
||||
{{ $t('common.search') }}
|
||||
</ElButton>
|
||||
<ElButton :icon="RefreshCw" @click="loadRuns">
|
||||
{{ $t('common.refresh') }}
|
||||
</ElButton>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<Grid>
|
||||
<template #cell-status="{ row }">
|
||||
<RunStatusTag :status="row.status" />
|
||||
</template>
|
||||
|
||||
<ElTable
|
||||
v-loading="loading"
|
||||
:data="rows"
|
||||
border
|
||||
stripe
|
||||
height="calc(100vh - 285px)"
|
||||
>
|
||||
<ElTableColumn type="index" width="60" :index="rowIndex + 1" />
|
||||
<ElTableColumn
|
||||
prop="workflow_name"
|
||||
:label="$t('ai-platform.workflowRuns.columns.workflow')"
|
||||
min-width="160"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<ElTableColumn
|
||||
:label="$t('ai-platform.workflowRuns.columns.status')"
|
||||
width="110"
|
||||
align="center"
|
||||
<template #cell-trigger_type="{ row }">
|
||||
<ElTag
|
||||
:type="getTagType(row.trigger_type, triggerOptions)"
|
||||
size="small"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<RunStatusTag :status="row.status" />
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
:label="$t('ai-platform.workflowRuns.columns.trigger')"
|
||||
width="140"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<ElTag
|
||||
:type="getTagType(row.trigger_type, triggerOptions)"
|
||||
size="small"
|
||||
>
|
||||
{{ getTagLabel(row.trigger_type, triggerOptions) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
prop="total_steps"
|
||||
:label="$t('ai-platform.workflowRuns.columns.steps')"
|
||||
width="80"
|
||||
align="center"
|
||||
/>
|
||||
<ElTableColumn
|
||||
prop="total_tokens"
|
||||
:label="$t('ai-platform.workflowRuns.columns.tokens')"
|
||||
width="90"
|
||||
align="center"
|
||||
/>
|
||||
<ElTableColumn
|
||||
:label="$t('ai-platform.workflowRuns.columns.duration')"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatDuration(row.elapsed_time) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
:label="$t('ai-platform.workflowRuns.columns.startedAt')"
|
||||
width="180"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.started_at) }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
prop="error_message"
|
||||
:label="$t('ai-platform.workflowRuns.columns.error')"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<ElTableColumn
|
||||
:label="$t('ai-platform.workflowRuns.columns.actions')"
|
||||
width="120"
|
||||
fixed="right"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<ElButton
|
||||
link
|
||||
type="primary"
|
||||
:icon="Eye"
|
||||
@click.stop="openDetail(row)"
|
||||
>
|
||||
{{ $t('common.view') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
{{ getTagLabel(row.trigger_type, triggerOptions) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
|
||||
<div class="mt-4 flex justify-end">
|
||||
<ElPagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
@current-change="handlePageChange"
|
||||
@size-change="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</ElCard>
|
||||
<template #cell-elapsed_time="{ row }">
|
||||
{{ formatDuration(row.elapsed_time) }}
|
||||
</template>
|
||||
|
||||
<template #cell-started_at="{ row }">
|
||||
{{ formatTime(row.started_at) }}
|
||||
</template>
|
||||
|
||||
<template #cell-actions="{ row }">
|
||||
<ElButton link type="primary" :icon="Eye" @click.stop="openDetail(row)">
|
||||
{{ $t('common.view') }}
|
||||
</ElButton>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
@@ -8,15 +8,9 @@ import {
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElOption,
|
||||
ElRadioButton,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
ElSwitch,
|
||||
} from 'element-plus';
|
||||
|
||||
import { getFormListApi } from '#/api/online-dev/form-manager';
|
||||
|
||||
import SmartInput from '../components/SmartInput.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -96,30 +90,6 @@ const form = ref({
|
||||
...props.data,
|
||||
});
|
||||
|
||||
// 表单输入模式:variable(变量输入)或 select(下拉选择)
|
||||
const formInputMode = ref<'select' | 'variable'>('variable');
|
||||
|
||||
// 表单列表
|
||||
const formList = ref<any[]>([]);
|
||||
const formLoading = ref(false);
|
||||
|
||||
// 加载表单列表
|
||||
const loadFormList = async () => {
|
||||
try {
|
||||
formLoading.value = true;
|
||||
const res = await getFormListApi({ page: 1, pageSize: 100, status: 'published' });
|
||||
formList.value = res.items || [];
|
||||
} catch (error) {
|
||||
console.error('加载表单列表失败:', error);
|
||||
} finally {
|
||||
formLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadFormList();
|
||||
});
|
||||
|
||||
watch(
|
||||
form,
|
||||
(val) => {
|
||||
@@ -166,48 +136,17 @@ const showField = (field: string) => {
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem :label="$t('ai-platform.workflow.panels.formData.formCode')">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<ElRadioGroup v-model="formInputMode" size="small">
|
||||
<ElRadioButton value="variable">{{ $t('ai-platform.workflow.panels.formData.variableInput') }}</ElRadioButton>
|
||||
<ElRadioButton value="select">{{ $t('ai-platform.workflow.panels.formData.selectFromList') }}</ElRadioButton>
|
||||
</ElRadioGroup>
|
||||
<SmartInput
|
||||
v-model="form.form_code"
|
||||
:current-node-id="nodeId"
|
||||
:placeholder="
|
||||
$t('ai-platform.workflow.panels.formData.formCodeInputPlaceholder') +
|
||||
' {{form_code}}'
|
||||
"
|
||||
/>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
{{ $t('ai-platform.workflow.panels.formData.formCodeInputHint') }}
|
||||
</div>
|
||||
|
||||
<!-- 变量输入模式 -->
|
||||
<template v-if="formInputMode === 'variable'">
|
||||
<SmartInput
|
||||
v-model="form.form_code"
|
||||
:current-node-id="nodeId"
|
||||
:placeholder="
|
||||
$t('ai-platform.workflow.panels.formData.formCodeInputPlaceholder') +
|
||||
' {{form_code}}'
|
||||
"
|
||||
/>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
{{ $t('ai-platform.workflow.panels.formData.formCodeInputHint') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 下拉选择模式 -->
|
||||
<template v-else>
|
||||
<ElSelect
|
||||
v-model="form.form_code"
|
||||
:placeholder="$t('ai-platform.workflow.panels.formData.selectFormPlaceholder')"
|
||||
filterable
|
||||
:loading="formLoading"
|
||||
class="w-full"
|
||||
>
|
||||
<ElOption
|
||||
v-for="f in formList"
|
||||
:key="f.code"
|
||||
:label="`${f.name} (${f.code})`"
|
||||
:value="f.code"
|
||||
/>
|
||||
</ElSelect>
|
||||
<div class="text-muted-foreground mt-1 text-xs">
|
||||
{{ $t('ai-platform.workflow.panels.formData.selectedFormHint', { code: form.form_code || '-' }) }}
|
||||
</div>
|
||||
</template>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 记录ID(读取/更新/删除) -->
|
||||
|
||||
Reference in New Issue
Block a user