Restore-ZQ-admin-quality-while-trimming-AI-routes
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user