Restore AI design confirmation panels
This commit is contained in:
@@ -1,420 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { DesignPreviewData } from './types';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
Check,
|
||||
Close as X,
|
||||
CopyDocument as Copy,
|
||||
} from '@element-plus/icons-vue';
|
||||
|
||||
import {
|
||||
ElAlert,
|
||||
ElButton,
|
||||
ElDescriptions,
|
||||
ElDescriptionsItem,
|
||||
ElDialog,
|
||||
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;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void;
|
||||
(e: 'confirm', data: Record<string, any>): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const draftText = ref('{}');
|
||||
const contentText = ref('');
|
||||
|
||||
const sourceData = computed(() => {
|
||||
return (
|
||||
props.preview ||
|
||||
props.design ||
|
||||
props.basicInfo ||
|
||||
props.settings ||
|
||||
props.publishData ||
|
||||
props.data ||
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
const sourceType = computed(() => {
|
||||
const source = sourceData.value as Record<string, any>;
|
||||
if (source.type) return String(source.type);
|
||||
if (props.settings) return 'app_settings';
|
||||
if (props.publishData) return 'dashboard_publish';
|
||||
if (props.basicInfo) return 'dashboard_basic_info';
|
||||
return 'design';
|
||||
});
|
||||
|
||||
const payload = computed<Record<string, any>>(() => {
|
||||
const source = sourceData.value as Record<string, any>;
|
||||
if ('data' in source && source.data && typeof source.data === 'object') {
|
||||
return source.data;
|
||||
}
|
||||
return source;
|
||||
});
|
||||
|
||||
const typeLabelMap: Record<string, string> = {
|
||||
app_design: '应用设计方案',
|
||||
app_settings: '应用设置方案',
|
||||
dashboard_basic_info: '仪表盘基础信息',
|
||||
dashboard_design: '仪表盘设计方案',
|
||||
dashboard_publish: '仪表盘发布配置',
|
||||
database_design: '数据库设计方案',
|
||||
form_basic_info: '表单基础信息',
|
||||
form_ui_design: '表单界面设计',
|
||||
list_config: '列表配置方案',
|
||||
system_summary: '系统生成总结',
|
||||
};
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
const source = sourceData.value as Record<string, any>;
|
||||
return (
|
||||
source.title ||
|
||||
props.title ||
|
||||
typeLabelMap[sourceType.value] ||
|
||||
'AI 方案确认'
|
||||
);
|
||||
});
|
||||
|
||||
const primaryText = computed(() => {
|
||||
const source = payload.value;
|
||||
return (
|
||||
source.content ||
|
||||
source.design_suggestion ||
|
||||
source.summary ||
|
||||
source.description ||
|
||||
''
|
||||
);
|
||||
});
|
||||
|
||||
const summaryItems = computed(() => {
|
||||
const source = payload.value;
|
||||
const candidates: Array<[string, any]> = [
|
||||
['名称', source.name || source.title || source.design_title],
|
||||
['编码', source.code || source.dashboard_code || source.form_code],
|
||||
['类型', source.type || source.category],
|
||||
['路由', source.route_path || source.path],
|
||||
['菜单', source.menu_name],
|
||||
['图标', source.menu_icon],
|
||||
['排序', source.menu_order],
|
||||
['说明', source.description],
|
||||
];
|
||||
|
||||
return candidates
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([label, value]) => ({ label, value: String(value) }));
|
||||
});
|
||||
|
||||
const fieldItems = computed(() => {
|
||||
const source = payload.value;
|
||||
const fields =
|
||||
source.schema_fields ||
|
||||
source.form_fields ||
|
||||
source.fields ||
|
||||
source.schema?.fields;
|
||||
return Array.isArray(fields) ? fields.slice(0, 16) : [];
|
||||
});
|
||||
|
||||
const tableItems = computed(() => {
|
||||
const source = payload.value;
|
||||
const tables = source.data_sources || source.table_configs || source.tables;
|
||||
return Array.isArray(tables) ? tables.slice(0, 10) : [];
|
||||
});
|
||||
|
||||
const routePreview = computed(() => {
|
||||
const source = payload.value;
|
||||
if (source.route_path) return source.route_path;
|
||||
if (source.dashboard_code) return `/dashboard/${source.dashboard_code}`;
|
||||
if (source.form_code) return `/form-render/${source.form_code}`;
|
||||
return '';
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [props.visible, sourceData.value] as const,
|
||||
() => {
|
||||
if (props.visible) {
|
||||
draftText.value = JSON.stringify(payload.value || {}, null, 2);
|
||||
contentText.value = primaryText.value;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function mergedDraft() {
|
||||
const data = JSON.parse(draftText.value || '{}');
|
||||
if (
|
||||
contentText.value &&
|
||||
(sourceType.value === 'app_design' || sourceType.value === 'dashboard_design')
|
||||
) {
|
||||
if ('content' in data) data.content = contentText.value;
|
||||
else data.design_suggestion = contentText.value;
|
||||
}
|
||||
return data && typeof data === 'object' ? data : { value: data };
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
emit('update:visible', false);
|
||||
emit('close');
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
try {
|
||||
emit('confirm', mergedDraft());
|
||||
emit('update:visible', false);
|
||||
} catch {
|
||||
ElMessage.error('JSON 格式不正确,请检查后再确认');
|
||||
}
|
||||
}
|
||||
|
||||
async function copyJson() {
|
||||
await navigator.clipboard?.writeText(draftText.value);
|
||||
ElMessage.success('已复制');
|
||||
}
|
||||
|
||||
function handleVisibleChange(visible: boolean) {
|
||||
if (!visible) handleClose();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElDialog
|
||||
:model-value="visible"
|
||||
:title="dialogTitle"
|
||||
width="980px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="ai-design-preview-dialog"
|
||||
@update:model-value="handleVisibleChange"
|
||||
>
|
||||
<div class="ai-design-preview">
|
||||
<section class="preview-panel">
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<div class="panel-title">{{ typeLabelMap[sourceType] || '方案预览' }}</div>
|
||||
<div class="panel-subtext">确认后将把当前结构化配置回传给工作流继续执行</div>
|
||||
</div>
|
||||
<ElTag effect="dark" type="primary">{{ sourceType }}</ElTag>
|
||||
</div>
|
||||
|
||||
<ElAlert
|
||||
v-if="routePreview"
|
||||
class="mb-3"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>预计访问路径:{{ routePreview }}</template>
|
||||
</ElAlert>
|
||||
|
||||
<ElDescriptions v-if="summaryItems.length" :column="2" border size="small">
|
||||
<ElDescriptionsItem
|
||||
v-for="item in summaryItems"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.value }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
<div v-else class="empty-hint">当前方案未提供概要字段</div>
|
||||
|
||||
<template v-if="primaryText">
|
||||
<div class="section-title">方案说明</div>
|
||||
<ElScrollbar height="150px" class="text-preview">
|
||||
<pre>{{ primaryText }}</pre>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template v-if="fieldItems.length">
|
||||
<div class="section-title">字段设计</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="section-title">数据/页面配置</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 v-if="primaryText" class="content-editor">
|
||||
<div class="panel-title">可编辑方案正文</div>
|
||||
<ElInput
|
||||
v-model="contentText"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 7, maxRows: 10 }"
|
||||
resize="none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="panel-title-row">
|
||||
<span class="panel-title">结构化数据</span>
|
||||
<ElButton size="small" :icon="Copy" @click="copyJson">复制</ElButton>
|
||||
</div>
|
||||
<ElScrollbar height="330px">
|
||||
<ElInput
|
||||
v-model="draftText"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 16, maxRows: 22 }"
|
||||
resize="none"
|
||||
class="json-editor"
|
||||
/>
|
||||
</ElScrollbar>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<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.95fr) minmax(0, 1.05fr);
|
||||
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-heading,
|
||||
.panel-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-subtext {
|
||||
margin-top: 4px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 14px 0 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 13px;
|
||||
padding: 24px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-preview {
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 6px;
|
||||
background: var(--el-fill-color-lighter);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.text-preview pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--el-text-color-regular);
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content-editor {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.content-editor .panel-title {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.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>
|
||||
@@ -1,23 +0,0 @@
|
||||
import DesignPreviewDialog from './DesignPreviewDialog.vue';
|
||||
|
||||
type PreviewData = {
|
||||
data?: Record<string, any>;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export type DesignData = PreviewData;
|
||||
export type DesignType = string;
|
||||
export type AppDesignData = PreviewData;
|
||||
export type AppSettingsData = PreviewData;
|
||||
export type DashboardBasicInfoData = PreviewData;
|
||||
export type DashboardDesignData = PreviewData;
|
||||
export type DashboardPublishData = PreviewData;
|
||||
export type SystemSummaryData = PreviewData;
|
||||
|
||||
export const AppDesignPanel = DesignPreviewDialog;
|
||||
export const AppSettingsPanel = DesignPreviewDialog;
|
||||
export const DashboardBasicInfoConfirmPanel = DesignPreviewDialog;
|
||||
export const DashboardDesignConfirmPanel = DesignPreviewDialog;
|
||||
export const DashboardPublishConfirmPanel = DesignPreviewDialog;
|
||||
export const DesignEditorPanel = DesignPreviewDialog;
|
||||
export const SystemSummaryConfirmPanel = DesignPreviewDialog;
|
||||
@@ -1,29 +1,40 @@
|
||||
import DesignPreviewDialog from '#/components/ai-chat-panel/DesignPreviewDialog.vue';
|
||||
/**
|
||||
* 表单编辑器公共组件
|
||||
* 可用于:表单管理器、工作流设计预览编辑
|
||||
*/
|
||||
|
||||
type PreviewData = {
|
||||
data?: Record<string, any>;
|
||||
[key: string]: any;
|
||||
};
|
||||
export { default as BasicInfoEditor } from './BasicInfoEditor.vue';
|
||||
export type { BasicFormData } from './BasicInfoEditor.vue';
|
||||
|
||||
export type AppDesignData = PreviewData;
|
||||
export type AppSettingsData = PreviewData;
|
||||
export type BasicFormData = PreviewData;
|
||||
export type DashboardBasicInfoData = PreviewData;
|
||||
export type DashboardDesignData = PreviewData;
|
||||
export type DashboardPublishData = PreviewData;
|
||||
export type DesignData = PreviewData;
|
||||
export type DesignType = string;
|
||||
export type FormBasicData = PreviewData;
|
||||
export type FormEditorData = PreviewData;
|
||||
export type PageBasicInfo = PreviewData;
|
||||
export type PageEditorData = PreviewData;
|
||||
export type PagePublishInfo = PreviewData;
|
||||
export type SystemSummaryData = PreviewData;
|
||||
export { default as DesignEditorPanel } from './DesignEditorPanel.vue';
|
||||
export type { DesignData, DesignType } from './DesignEditorPanel.vue';
|
||||
|
||||
export const AppDesignPanel = DesignPreviewDialog;
|
||||
export const AppSettingsPanel = DesignPreviewDialog;
|
||||
export const DashboardBasicInfoConfirmPanel = DesignPreviewDialog;
|
||||
export const DashboardDesignConfirmPanel = DesignPreviewDialog;
|
||||
export const DashboardPublishConfirmPanel = DesignPreviewDialog;
|
||||
export const DesignEditorPanel = DesignPreviewDialog;
|
||||
export const SystemSummaryConfirmPanel = DesignPreviewDialog;
|
||||
export { default as FormEditorContent } from './FormEditorContent.vue';
|
||||
export type { FormEditorData, BasicFormData as FormBasicData } from './FormEditorContent.vue';
|
||||
|
||||
export { default as AppDesignPanel } from './AppDesignPanel.vue';
|
||||
export type { AppDesignData } from './AppDesignPanel.vue';
|
||||
|
||||
export { default as AppSettingsPanel } from './AppSettingsPanel.vue';
|
||||
export type { AppSettingsData } from './AppSettingsPanel.vue';
|
||||
|
||||
export { default as DashboardBasicInfoConfirmPanel } from './DashboardBasicInfoConfirmPanel.vue';
|
||||
export type { DashboardBasicInfoData } from './DashboardBasicInfoConfirmPanel.vue';
|
||||
|
||||
export { default as DashboardDesignConfirmPanel } from './DashboardDesignConfirmPanel.vue';
|
||||
export type { DashboardDesignData } from './DashboardDesignConfirmPanel.vue';
|
||||
|
||||
export { default as PageBasicInfoEditor } from './PageBasicInfoEditor.vue';
|
||||
export type { PageBasicInfo } from './PageBasicInfoEditor.vue';
|
||||
|
||||
export { default as PagePublishInfoEditor } from './PagePublishInfoEditor.vue';
|
||||
export type { PagePublishInfo } from './PagePublishInfoEditor.vue';
|
||||
|
||||
export { default as PageEditorContent } from './PageEditorContent.vue';
|
||||
export type { PageEditorData } from './PageEditorContent.vue';
|
||||
|
||||
export { default as DashboardPublishConfirmPanel } from './DashboardPublishConfirmPanel.vue';
|
||||
export type { DashboardPublishData } from './DashboardPublishConfirmPanel.vue';
|
||||
|
||||
export { default as SystemSummaryConfirmPanel } from './SystemSummaryConfirmPanel.vue';
|
||||
export type { SystemSummaryData } from './SystemSummaryConfirmPanel.vue';
|
||||
|
||||
@@ -24,10 +24,6 @@ export default defineConfig(async () => {
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
exactAlias(
|
||||
'#/components/form-editor',
|
||||
'./src/components/ai-chat-panel/form-editor-lite.ts',
|
||||
),
|
||||
exactAlias(
|
||||
'@vben-core/design',
|
||||
'../../packages/@core/base/design/src/index.ts',
|
||||
|
||||
Reference in New Issue
Block a user