生成历史
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled

This commit is contained in:
2025-11-28 14:04:25 +08:00
parent 626351d33c
commit 04d84573a5
2 changed files with 368 additions and 2 deletions

View File

@@ -24,6 +24,13 @@ const prefix = 'code/';
// return requestClient.get<any>(`${prefix}detail`, { params: { id } });
// }
/**
* 代码生成记录
* @param data
*/
export async function getCodeGenerationListApi(data: Record<string, any> = {}) {
return requestClient.get<any>(`${prefix}list`, { params: data });
}
/**
* 代码生成
* @param data

View File

@@ -1,14 +1,17 @@
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import { defineAsyncComponent, onMounted, ref } from 'vue';
import { confirm, Page } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { Button, Drawer, Empty, List, message, Modal, Tag } from 'ant-design-vue';
import { JsonViewer } from '@vben/common-ui';
import { downloadByData } from '#/util/tool';
import {
codeGenerationDownloadApi,
generationApi,
getCodeGenerationListApi,
} from '#/views/code-generation/api';
import type { CodeGenData } from './utils/data-transformer';
@@ -19,6 +22,25 @@ import JsonImport from './components/json-import.vue';
import ManualForm from './components/manual-form.vue';
import ModeSelector from './components/mode-selector.vue';
// 生成历史相关
interface GenerationHistory {
id: number;
title: string;
file_name: string;
path: string;
params: any; // JSON对象
created_at: string; // 已格式化的时间字符串
updated_at: string;
}
const historyDrawerVisible = ref(false);
const historyList = ref<GenerationHistory[]>([]);
const historyLoading = ref(false);
// JSON详情查看
const jsonViewerVisible = ref(false);
const currentJsonData = ref<any>(null);
// 懒加载组件
const ModeSelectorComponent = defineAsyncComponent(() =>
Promise.resolve(ModeSelector),
@@ -72,10 +94,57 @@ const handleGenerate = async (data: CodeGenData) => {
downloadByData(blob, res.file_name, 'application/zip');
});
});
// 生成成功后刷新历史记录
if (historyDrawerVisible.value) {
loadHistoryList();
}
} catch (error: any) {
message.error(error.message || '代码生成失败');
}
};
// 加载生成历史列表
const loadHistoryList = async () => {
historyLoading.value = true;
try {
getCodeGenerationListApi({}).then((res) => {
historyList.value = res.items;
});
} catch (error: any) {
message.error(error.message || '加载历史记录失败');
historyList.value = [];
} finally {
historyLoading.value = false;
}
};
// 打开历史记录抽屉
const handleOpenHistory = () => {
historyDrawerVisible.value = true;
loadHistoryList();
};
// 下载历史记录
const handleDownloadHistory = async (item: GenerationHistory) => {
try {
// return console.log(item);
codeGenerationDownloadApi(item.id).then((blob) => {
downloadByData(blob, item.file_name, 'application/zip');
message.success('下载成功');
});
// const blob = await codeGenerationDownloadApi(item.id);
// downloadByData(blob, item.file_name, 'application/zip');
} catch (error: any) {
message.error(error.message || '下载失败');
}
};
// 查看JSON详情
const handleViewJson = (item: GenerationHistory) => {
currentJsonData.value = item.params;
jsonViewerVisible.value = true;
};
</script>
<template>
@@ -83,6 +152,15 @@ const handleGenerate = async (data: CodeGenData) => {
:title="currentMode ? modeTitles[currentMode] : '代码生成器'"
auto-content-height
>
<template #extra>
<Button
v-if="!currentMode"
type="primary"
@click="handleOpenHistory"
>
<i class="fa-solid fa-clock-rotate-left mr-2"></i>生成历史
</Button>
</template>
<Transition name="fade-slide" mode="out-in">
<!-- 模式选择界面 -->
<div v-if="!currentMode" key="selector" class="mode-selector-container">
@@ -131,6 +209,123 @@ const handleGenerate = async (data: CodeGenData) => {
@back="handleBack"
/>
</Transition>
<!-- 生成历史抽屉 -->
<Drawer
v-model:open="historyDrawerVisible"
title="生成历史"
:width="700"
placement="right"
class="history-drawer"
>
<div v-if="historyLoading" class="flex h-64 items-center justify-center">
<div class="text-center">
<i class="fa-solid fa-spinner fa-spin text-2xl text-primary mb-2"></i>
<p class="text-sm text-gray-500 dark:text-gray-400">加载中...</p>
</div>
</div>
<div v-else-if="historyList.length === 0" class="flex h-64 items-center justify-center">
<Empty description="暂无生成记录" />
</div>
<div v-else class="history-list-clean">
<div
v-for="item in historyList"
:key="item.id"
class="history-item-clean"
>
<div class="history-item-wrapper">
<!-- 左侧内容 -->
<div class="history-item-left">
<div class="history-item-header">
<h4 class="history-item-title">{{ item.title || '未命名' }}</h4>
</div>
<div class="history-item-file-row">
<svg
class="history-file-icon"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V5.41421C14 5.149 13.8946 4.89464 13.7071 4.70711L11.2929 2.29289C11.1054 2.10536 10.851 2 10.5858 2H3Z"
fill="currentColor"
fill-opacity="0.1"
/>
<path
d="M3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V5.41421C14 5.149 13.8946 4.89464 13.7071 4.70711L11.2929 2.29289C11.1054 2.10536 10.851 2 10.5858 2H3Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M10 2V5H13"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 8H11M5 10H9"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg>
<span class="history-item-file">{{ item.file_name || '未命名文件' }}</span>
</div>
<div class="history-item-time-row">
<span class="history-item-time-label">生成时间</span>
<span class="history-item-time">{{ item.created_at || '-' }}</span>
</div>
</div>
<!-- 右侧操作 -->
<div class="history-item-right">
<Button
v-if="item.params"
type="link"
size="small"
class="history-btn-link"
@click="handleViewJson(item)"
>
<i class="fa-solid fa-code mr-1"></i>
查看
</Button>
<Button
type="primary"
size="small"
class="history-btn-primary"
@click="handleDownloadHistory(item)"
>
<i class="fa-solid fa-download mr-1"></i>
下载
</Button>
</div>
</div>
</div>
</div>
</Drawer>
<!-- JSON详情查看Modal -->
<Modal
v-model:open="jsonViewerVisible"
title="生成参数详情"
:width="800"
:footer="null"
class="json-viewer-modal"
>
<div class="json-viewer-container">
<JsonViewer
:value="currentJsonData"
:expand-depth="3"
copyable
boxed
/>
</div>
</Modal>
</Page>
</template>
@@ -153,4 +348,168 @@ const handleGenerate = async (data: CodeGenData) => {
opacity: 0;
transform: translateX(20px);
}
/* 历史记录列表样式 - 简洁美观风格 */
.history-list-clean {
display: flex;
flex-direction: column;
gap: 12px;
}
.history-item-clean {
background: #ffffff;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 10px;
transition: all 0.2s ease;
overflow: hidden;
}
.dark .history-item-clean {
background: #1f2937;
border-color: rgba(255, 255, 255, 0.1);
}
.history-item-clean:hover {
border-color: rgba(24, 144, 255, 0.3);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.dark .history-item-clean:hover {
border-color: rgba(24, 144, 255, 0.5);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.history-item-wrapper {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 18px 20px;
gap: 24px;
}
.history-item-left {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.history-item-header {
margin-bottom: 0;
}
.history-item-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin: 0;
line-height: 1.4;
word-break: break-word;
}
.dark .history-item-title {
color: #f9fafb;
}
.history-item-file-row {
display: flex;
align-items: center;
gap: 8px;
}
.history-file-icon {
color: #1890ff;
width: 16px;
height: 16px;
flex-shrink: 0;
margin-top: 2px;
}
.history-item-file {
font-size: 14px;
color: #6b7280;
word-break: break-all;
line-height: 1.5;
}
.dark .history-item-file {
color: #9ca3af;
}
.history-item-time-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 2px;
}
.history-item-time-label {
font-size: 13px;
color: #9ca3af;
flex-shrink: 0;
}
.dark .history-item-time-label {
color: #6b7280;
}
.history-item-time {
font-size: 13px;
color: #6b7280;
word-break: break-word;
}
.dark .history-item-time {
color: #9ca3af;
}
.history-item-right {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
padding-top: 2px;
}
.history-btn-link {
height: auto;
padding: 0;
font-size: 13px;
display: flex;
align-items: center;
transition: all 0.2s ease;
}
.history-btn-primary {
height: 32px;
padding: 0 16px;
border-radius: 6px;
font-size: 13px;
display: flex;
align-items: center;
transition: all 0.2s ease;
}
.history-btn-primary:hover {
transform: translateY(-1px);
}
/* JSON查看器样式 */
.json-viewer-container {
max-height: 600px;
overflow: auto;
padding: 8px;
background: #fafafa;
border-radius: 8px;
}
.dark .json-viewer-container {
background: #1f2937;
}
/* 抽屉样式优化 */
:deep(.history-drawer .ant-drawer-body) {
padding: 20px;
}
</style>