add ai drawing

This commit is contained in:
XIE7654
2025-07-22 12:09:37 +08:00
parent 71d5053b9c
commit 04ee6bf0e0
8 changed files with 240 additions and 112 deletions

View File

@@ -9,7 +9,7 @@ export interface CreateImageTaskParams {
n?: number;
}
export async function createImageTask(params: CreateImageTaskParams) {
export async function createDrawing(params: CreateImageTaskParams) {
const res = await fetchWithAuth('drawing/', {
method: 'POST',
body: JSON.stringify(params),
@@ -20,7 +20,7 @@ export async function createImageTask(params: CreateImageTaskParams) {
return await res.json();
}
export async function fetchImageTaskStatus(id: number) {
export async function getDrawingDetail(id: number) {
const res = await fetchWithAuth(`drawing/${id}/`);
if (!res.ok) {
throw new Error('查询图片任务状态失败');
@@ -33,12 +33,12 @@ export interface GetImagePageParams {
page_size?: number;
}
export async function getImagePage(params: GetImagePageParams = {}) {
export async function getDrawingPage(params: GetImagePageParams = {}) {
const query = new URLSearchParams();
if (params.page) query.append('page', String(params.page));
if (params.page_size) query.append('page_size', String(params.page_size));
const res = await fetchWithAuth(
`drawing${query.toString() ? `?${query.toString()}` : ''}`,
`drawing/${query.toString() ? `?${query.toString()}` : ''}`,
);
if (!res.ok) {
throw new Error('获取图片分页失败');

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, h, reactive, ref } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
@@ -13,9 +13,22 @@ import {
Pagination,
Row,
Select,
Spin,
} from 'ant-design-vue';
import { createImageTask } from '#/api/ai/drawing';
import {
createDrawing,
getDrawingDetail,
getDrawingPage,
} from '#/api/ai/drawing';
// 定义图片对象类型
interface DrawingImage {
id: number;
status: string;
pic_url?: string;
// 其他属性
}
// 表单选项
const platforms = [
@@ -24,14 +37,14 @@ const platforms = [
// { label: 'OpenAI', value: 'openai' },
// { label: 'Google GenAI', value: 'google-genai' },
];
const models = {
const models: Record<string, { label: string; value: string }[]> = {
tongyi: [{ label: 'wanx_v1', value: 'wanx_v1' }],
// deepseek: [{ label: 'deepseek-img', value: 'deepseek-img' }],
// openai: [{ label: 'dall-e-3', value: 'dall-e-3' }],
// 'google-genai': [{ label: 'imagen', value: 'imagen' }],
// 其他平台...
};
const sizes = [
{ label: '1024x1024', value: '1024x1024' },
{ label: '1024*1024', value: '1024*1024' },
{ label: '720*1280', value: '720*1280' },
{ label: '768*1152', value: '768*1152' },
{ label: '1280*720', value: '1280*720' },
@@ -51,26 +64,24 @@ const styles = [
// 表单数据
const form = reactive({
prompt: '近景镜头18岁的中国女孩古代服饰圆脸正面看着镜头民族优雅的服装商业摄影室外电影级光照半身特写精致的淡妆锐利的边缘。',
prompt:
'近景镜头18岁的中国女孩古代服饰圆脸正面看着镜头民族优雅的服装商业摄影室外电影级光照半身特写精致的淡妆锐利的边缘。',
platform: 'tongyi',
model: 'wanx_v1',
size: '1024x1024',
model: 'wanx-v1',
size: '1024*1024',
style: 'watercolor',
});
// 图片数据与分页
const images = ref<string[]>([]);
const images = ref<DrawingImage[]>([]);
const loading = ref(false);
const page = ref(1);
const pageSize = 12;
const total = computed(() => images.value.length);
const pagedImages = computed(() =>
images.value.slice((page.value - 1) * pageSize, page.value * pageSize),
);
const pageSize = ref(9);
const total = ref(0);
// 平台切换时自动切换模型
const onPlatformChange = (val: string) => {
form.model = models[val][0].value;
const onPlatformChange = (value: number | string) => {
form.model = models[value as string]?.[0]?.value ?? '';
};
// 提交表单调用AI画图API
@@ -78,14 +89,15 @@ async function handleDraw() {
loading.value = true;
try {
// 这里调用你的AI画图API返回图片url数组
const res = await createImageTask(form);
const data = await createDrawing(form);
if (data.code !== 0) {
message.error(data.message || '生成失败');
return;
}
page.value = 1;
await fetchDrawingList(page.value, pageSize.value); // 刷新第一页图片列表
// images.value = res.data.images;
// DEMO用假数据
// images.value = Array.from(
// { length: 30 },
// (_, i) => `https://picsum.photos/seed/${form.prompt}-${i}/300/300`,
// );
page.value = 1;
message.success('生成成功');
} catch {
message.error('生成失败');
@@ -93,6 +105,59 @@ async function handleDraw() {
loading.value = false;
}
}
// 轮询获取图片详情
const pollDrawingDetail = async (id: number) => {
fetchDrawingDetail(id).then((res) => {
if (res && res.status === 'RUNNING') {
setTimeout(() => pollDrawingDetail(id), 5000);
}
});
};
// 获取图片分页列表
async function fetchDrawingList(pageNum = 1, pageSize = 9) {
try {
const res = await getDrawingPage({ page: pageNum, page_size: pageSize });
images.value = res.items;
// images.value = res.items.map(item => item.pic_url);
total.value = res.total;
// 检查每个 item 的状态
for (const item of res.items) {
if (item.status === 'PENDING') {
fetchDrawingDetail(item.id);
} else if (item.status === 'RUNNING') {
pollDrawingDetail(item.id);
}
}
return res;
} catch {
message.error('获取图片列表失败');
return null;
}
}
// 获取图片详情
const fetchDrawingDetail = async (id: number) => {
try {
const res = await getDrawingDetail(id);
// 更新 images 中对应项
const idx = images.value.findIndex((item) => item.id === id);
if (idx !== -1) {
images.value[idx] = { ...images.value[idx], ...res.data };
}
// 处理详情数据
return res;
} catch {
message.error('获取图片详情失败');
return null;
}
};
// 页面加载时调用获取图片列表
onMounted(() => {
fetchDrawingList();
});
</script>
<template>
@@ -166,28 +231,46 @@ async function handleDraw() {
<Card title="生成结果" bordered>
<Row :gutter="16">
<Col
v-for="(img, idx) in pagedImages"
:key="img"
:span="4"
v-for="(img, idx) in images"
:key="idx"
:span="8"
style="margin-bottom: 16px"
>
<Card
hoverable
:cover="
h('img', {
src: img,
style: 'width:100%;height:180px;object-fit:cover;',
})
"
/>
<Card hoverable>
<template #cover>
<div
v-if="img.status === 'PENDING' || img.status === 'RUNNING'"
style="
width: 100%;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
"
>
<Spin size="large" />
</div>
<img
v-else
:src="img.pic_url"
style="width: 100%; height: 180px; object-fit: cover"
/>
</template>
</Card>
</Col>
</Row>
<Pagination
v-if="total > pageSize"
v-model:current="page"
:total="total"
:page-size="pageSize"
style="margin-top: 16px; text-align: right"
@change="
(p, ps) => {
page = p;
pageSize = ps;
fetchDrawingList(p, ps);
}
"
/>
</Card>
</Col>