Restore-ZQ-admin-quality-while-trimming-AI-routes

This commit is contained in:
Codex
2026-06-08 20:39:22 +08:00
parent 2e7099cb6d
commit 4bb3593fc8
10 changed files with 342 additions and 362 deletions
@@ -3,10 +3,27 @@ import type { DesignPreviewData } from './types';
import { computed, ref, watch } from 'vue';
import { ElButton, ElDialog, ElInput, ElMessage } from 'element-plus';
import { Check, Copy, X } from '@vben/icons';
import {
ElButton,
ElDescriptions,
ElDescriptionsItem,
ElDialog,
ElDivider,
ElInput,
ElMessage,
ElScrollbar,
ElTag,
} from 'element-plus';
const props = defineProps<{
basicInfo?: Record<string, any>;
data?: Record<string, any>;
design?: DesignPreviewData | Record<string, any>;
preview?: DesignPreviewData;
publishData?: Record<string, any>;
settings?: Record<string, any>;
title?: string;
visible: boolean;
}>();
@@ -19,15 +36,56 @@ const emit = defineEmits<{
const draftText = ref('{}');
const dialogTitle = computed(
() => props.preview?.title || props.title || '确认内容',
);
const sourceData = computed(() => {
return (
props.preview ||
props.design ||
props.basicInfo ||
props.settings ||
props.publishData ||
props.data ||
{}
);
});
const payload = computed(() => {
const source = sourceData.value as Record<string, any>;
if ('data' in source && source.data && typeof source.data === 'object') {
return source.data;
}
return source;
});
const dialogTitle = computed(() => {
const source = sourceData.value as Record<string, any>;
return source.title || props.title || 'AI 方案确认';
});
const summaryItems = computed(() => {
const source = payload.value as Record<string, any>;
const keys = ['name', 'title', 'description', 'code', 'type', 'category'];
return keys
.filter((key) => source[key] !== undefined && source[key] !== null)
.map((key) => ({ key, value: String(source[key]) }));
});
const fieldItems = computed(() => {
const source = payload.value as Record<string, any>;
const fields = source.form_fields || source.fields || source.schema?.fields;
return Array.isArray(fields) ? fields.slice(0, 12) : [];
});
const tableItems = computed(() => {
const source = payload.value as Record<string, any>;
const tables = source.table_configs || source.tables || source.data_sources;
return Array.isArray(tables) ? tables.slice(0, 8) : [];
});
watch(
() => [props.visible, props.preview] as const,
() => [props.visible, sourceData.value] as const,
() => {
if (props.visible) {
draftText.value = JSON.stringify(props.preview?.data || {}, null, 2);
draftText.value = JSON.stringify(payload.value || {}, null, 2);
}
},
{ immediate: true },
@@ -48,6 +106,11 @@ function handleConfirm() {
}
}
async function copyJson() {
await navigator.clipboard?.writeText(draftText.value);
ElMessage.success('已复制');
}
function handleVisibleChange(visible: boolean) {
if (!visible) handleClose();
}
@@ -57,25 +120,160 @@ function handleVisibleChange(visible: boolean) {
<ElDialog
:model-value="visible"
:title="dialogTitle"
width="720px"
width="860px"
append-to-body
destroy-on-close
class="ai-design-preview-dialog"
@update:model-value="handleVisibleChange"
>
<div class="space-y-3">
<div class="text-muted-foreground text-sm">
{{ preview?.type || 'design_preview' }}
</div>
<ElInput
v-model="draftText"
type="textarea"
:rows="18"
resize="vertical"
/>
<div class="ai-design-preview">
<section class="preview-panel">
<div class="panel-title">方案概要</div>
<ElDescriptions v-if="summaryItems.length" :column="2" border size="small">
<ElDescriptionsItem
v-for="item in summaryItems"
:key="item.key"
:label="item.key"
>
{{ item.value }}
</ElDescriptionsItem>
</ElDescriptions>
<div v-else class="empty-hint">当前方案未提供概要字段</div>
<ElDivider v-if="fieldItems.length || tableItems.length" />
<template v-if="fieldItems.length">
<div class="panel-subtitle">字段设计</div>
<div class="tag-grid">
<ElTag
v-for="(field, index) in fieldItems"
:key="field.id || field.name || index"
effect="plain"
>
{{ field.label || field.title || field.name || `字段 ${index + 1}` }}
</ElTag>
</div>
</template>
<template v-if="tableItems.length">
<div class="panel-subtitle">数据表配置</div>
<div class="table-list">
<div
v-for="(table, index) in tableItems"
:key="table.id || table.name || index"
class="table-item"
>
<span>{{ table.title || table.name || `数据表 ${index + 1}` }}</span>
<small>{{ table.code || table.table_name || table.type || '' }}</small>
</div>
</div>
</template>
</section>
<section class="editor-panel">
<div class="panel-title-row">
<span class="panel-title">结构化数据</span>
<ElButton size="small" :icon="Copy" @click="copyJson">复制</ElButton>
</div>
<ElScrollbar height="390px">
<ElInput
v-model="draftText"
type="textarea"
:autosize="{ minRows: 18, maxRows: 24 }"
resize="none"
class="json-editor"
/>
</ElScrollbar>
</section>
</div>
<template #footer>
<ElButton @click="handleClose">取消</ElButton>
<ElButton type="primary" @click="handleConfirm">确认</ElButton>
<ElButton :icon="X" @click="handleClose">取消</ElButton>
<ElButton type="primary" :icon="Check" @click="handleConfirm">
确认采用
</ElButton>
</template>
</ElDialog>
</template>
<style scoped>
.ai-design-preview {
display: grid;
grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
gap: 16px;
}
.preview-panel,
.editor-panel {
min-width: 0;
border: 1px solid var(--el-border-color-light);
border-radius: 8px;
padding: 14px;
background: var(--el-bg-color);
}
.panel-title,
.panel-title-row {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
}
.panel-title-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.panel-subtitle {
font-size: 13px;
font-weight: 600;
margin: 12px 0 8px;
}
.empty-hint {
color: var(--el-text-color-secondary);
font-size: 13px;
padding: 24px 0;
text-align: center;
}
.tag-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.table-list {
display: grid;
gap: 8px;
}
.table-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
border: 1px solid var(--el-border-color-lighter);
border-radius: 6px;
padding: 8px 10px;
}
.table-item small {
color: var(--el-text-color-secondary);
}
.json-editor :deep(textarea) {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
font-size: 12px;
line-height: 1.55;
}
@media (max-width: 900px) {
.ai-design-preview {
grid-template-columns: 1fr;
}
}
</style>