import type { DrawStyleSecondCategoryGridRef } from '@aiComponents/DrawStyleCategoryGrid/SecondGrid.tsx';
import type { UseDrawStyleCategoryConfigProps } from '@aiComponents/DrawStyleCategoryGrid/useDrawStyleCategoryConfig.ts';
import type {
FeatureLeaferFrameLayoutCommonProps,
FeatureLeaferFrameLayoutRef,
} from '@aiComponents/FeatureLeaferFrameLayout/types.ts';
import type { AiToolGenerateModalRef } from '@aiComponents/GenerateModal';
import type { QueryTagItem } from '@aiComponents/QueryTagImagesSelector/types.ts';
import type {
SemanticsSegmentImageControlRef,
SemanticsSegmentImageType,
} from '@aiComponents/SemanticsSegmentImage/types.ts';
import type { StyleConfigTextAreaRef } from '@aiComponents/StyleConfigTextArea';
import type {
UploadCropperModalProps,
UploadCropperModalRef,
} from '@aiComponents/UploadCropperModal';
import type { AiVipEquityLimitModalRef } from '@aiComponents/VipEquityLimitModal';
import type { ToolEquityCountWrapRef } from '@components/ToolEquityCountWrap';
import type { RequestDecorationDesignParams } from '@services/comfyUI.ts';
import type {
RequestJzxzDrawImageParams,
RequestTextToImageGenerateParams,
} from '@services/draw.ts';
import {
aiAddEventTrack,
combineSentenceBySplitSymbol,
getRandomNum,
getStyleDataIndexByRefs,
} from '@utils/ai.ts';
import { userAuth } from '@utils/auth.ts';
import { computeContentObjectFit, delayFun } from '@utils/common.ts';
import { decodeImageUrl } from '@utils/string.ts';
import { microGetHuoshanUploadImageUrl } from '@utils/upload.ts';
import type { UploadImageResponse } from '@wander/base-utils';
import { fileToBase64 } from '@wander/base-utils';
import { base64ToBlob, blobToFile, getImageSizeInfo, isMobile } from '@wander/base-utils';
import { useMount } from 'ahooks';
import { message } from 'antd';
import type { AxiosRequestConfig } from 'axios';
import { t } from 'i18next';
import PubSub from 'pubsub-js';
import type { ReactNode } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useLocation, useSearchParams } from 'react-router-dom';
import type { CustomUploadProps } from '@/components/CustomUpload';
import type { UploadBtnImageResponse } from '@/components/HuabanUploadBtn';
import type {
ImageSelectorModalProps,
ImageSelectorModalRef,
} from '@/components/ImageSelectorModal';
import type { ThemeSegmentedProps } from '@/components/ThemeSegmented';
import type { UploadImageType } from '@/components/UploadImageCard';
import { imgToImgSizeConfig, JZXZ_DRAW_INIT_NEGATIVE_PROMPTS } from '@/config/aiTool';
import useGetConfigValue from '@/hooks/useGetConfigValue.ts';
import {
DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY,
DECORATION_DESIGN_CHARTLET_CATEGORY_KEY,
DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY,
decorationDesignSpeedModePrompts,
decorationDesignToolKey,
TRACK_DECORATION_DESIGN_TOOL_NAME,
} from '@/pages/AiTools/DecorationDesign/config.tsx';
import { commonDrawControlNetModelMap } from '@/pages/AiTools/Draw/configs.ts';
import { useToolRouterStore } from '@/pages/AiTools/Router/store/createStore.ts';
import type { RequestDescribeRetouchingParams } from '@/services/aiTool';
import { requestDescribeRetouching } from '@/services/aiTool';
import { useStore } from '@/store/createStore.ts';
import type { AiToolProcessSuccessResponse, ControlNetEditorImageInfo } from '@/types/ai';
import type {
CandidateItem,
DrawSizeType,
DrawStyleConfigItem,
GeneratePriorityType,
LoraItem,
} from '@/types/draw.ts';
import type { ChartletCategoryItem, ChartletImageItem } from '@/types/image';
import { imageDataToBase64 } from '@/utils/image';
export type VersionValueType = 'STANDARD' | 'EXTREME_SPEED' | 'ADVANCED';
// 请求之间的间隔时长
const REQUEST_DELAY_DURATION = 2000;
// 默认重试次数
const DEFAULT_RETRY_COUNT = 2;
type VersionMobileValue = any;
export type VersionOptionItem = {
label: ReactNode;
value: VersionMobileValue;
disabled?: boolean;
};
export type CategoriesItem = {
key: string;
// 风格名称
label: string;
// 风格展示图片
thumbUrl: string;
// 提示词
promptsList: CandidateItem[];
// 大模型名称
modelName: string;
// 小模型
loraParamses?: LoraItem[];
};
export type DecorationDesignStyleItem = {
alias: string;
key: string;
categories: CategoriesItem[];
};
type PromptConfig = {
key: string;
list: CandidateItem[];
};
export const useAiDecorationDesign = () => {
const location = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
const addResultImage = useToolRouterStore((state) => state.addResultImage);
// 产品类型
const productType = useStore((state) => state.productInfo.productType);
// 不限制权益
const noLimitFrequency = userAuth(['ORGANIZATION_VIP']);
// 描述词获取
const styleConfigTextAreaRef = useRef<StyleConfigTextAreaRef>(null);
// 底图图库
const imageSelectorModalRef = useRef<ImageSelectorModalRef>(null);
// 生成弹窗
const generateModalRef = useRef<AiToolGenerateModalRef<RequestJzxzDrawImageParams>>(null);
const drawStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null);
const toolEquityCountWrapRef = useRef<ToolEquityCountWrapRef>(null);
// 画板Ref
const featureLeaferFrameLayoutRef = useRef<FeatureLeaferFrameLayoutRef>(null);
const semanticsSegmentImageControlRef = useRef<SemanticsSegmentImageControlRef>(null);
// 刷新次数
const refreshToolEquityUserUseInfo = useStore((state) => state.refreshToolEquityUserUseInfo);
// 创作模式优先级
const [generatePriorityType, setGeneratePriorityType] =
useState<GeneratePriorityType>('CONTROLNET');
// 会员权益弹窗
const DecorationDesignipLimitModalRef = useRef<AiVipEquityLimitModalRef>(null);
// 相似度
const [weight, setWeight] = useState(1);
// 最终生成图尺寸大小
const [sizeType, setSizeType] = useState<DrawSizeType>('LARGE');
// 极速版-生成失败的信息
const [errorMsg, setErrorMsg] = useState('');
// 创作弹框-极速版
const generateSpeedModalRef = useRef<AiToolGenerateModalRef>(null);
// comfyUI 创作弹框-高级版
const comfyUiGenerateModalRef =
useRef<AiToolGenerateModalRef<RequestDecorationDesignParams>>(null);
// 风格获取-高级版
const drawComfyUiStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null);
// 风格列表
const [comfyUiStyleList, setComfyUiStyleList] = useState<DecorationDesignStyleItem[]>([]);
// 高级版-风格参考图信息
const [styleReferComfyImageInfo, setStyleReferComfyImageInfo] =
useState<UploadImageResponse | null>(null);
// 风格参考图图库
const comfySourceImageSelectorModalRef = useRef<ImageSelectorModalRef>(null);
// comfyUI-XL版本参考图风格符合度
const [comfyStyleReferWeight, setComfyStyleReferWeight] = useState(0.8);
// 上传裁剪弹窗
const uploadCropperModalRef = useRef<UploadCropperModalRef>(null);
// 裁剪弹窗open
const [cropperModalOpen, setCropperModalOpen] = useState(false);
// 选中风格索引
const [currentStyleIndex, setCurrentStyleIndex] = useState(0);
// 高级版-一级类目状态获取
const [activeAlias, setActiveAlias] = useState<string>(comfyUiStyleList[0]?.alias || '');
// 高级版-获取当前激活的二级类目
const activeCategories =
comfyUiStyleList.find((item) => item.alias === activeAlias)?.categories || [];
// 版本切换状态
const [versionValue, setVersionValue] = useState<VersionValueType>(() => {
return userAuth(['USER', 'COMMON_VIP', 'TOURIST']) ? 'STANDARD' : 'EXTREME_SPEED';
});
// 标准版-该工具支持选择的类目组
const categoryRefsGroup: string[][] =
useGetConfigValue({
productKeys: {
JZXZ: DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY,
JZDS: 'jzds_decoration_design_style_categories',
},
}) || [];
// 贴图类目列表
const chartletCategoryList =
useGetConfigValue<ChartletCategoryItem[]>({ key: DECORATION_DESIGN_CHARTLET_CATEGORY_KEY }) ||
[];
// 线稿库类目列表
const queryTagList =
useGetConfigValue<QueryTagItem[]>({
key: 'jzxz_decoration_design_base_Image_query_tag_config',
}) || [];
// 高级版-该工具支持选择的类目组
const comfyUiCategoryRefsGroup: CategoriesItem[] = (
useGetConfigValue({
productKeys: {
JZXZ: DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY,
},
}) || []
).filter(
(category: CategoriesItem) =>
category.key === 'indoorHomeDecoration' || category.key === 'indoorWorkWear',
);
// 高级版-描述词
const comfyUiPrompt: PromptConfig[] = (
useGetConfigValue({
productKeys: {
JZXZ: 'jzxz_advance_draw_descriptor_config',
},
}) || []
).filter((p: PromptConfig) => p.key === 'indoorHomeDecoration' || p.key === 'indoorWorkWear');
console.log('comfyUiPrompt', comfyUiPrompt);
// 版本切换-mobile
const handleVersionMobileChange = (value: VersionValueType) => {
if (userAuth(['TOURIST'])) {
PubSub.publish('onLogin', true);
return;
}
if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) {
console.log('value', value);
DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP');
return;
}
// 否则允许切换
setVersionValue(value);
// 描述词类型获取
const promptType = styleConfigTextAreaRef.current?.getPromptType();
// 描述词自定义-版本切换判断
if (promptType === 'USER' || promptType === 'INNOVATIVE') {
if (value === 'ADVANCED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
activeCategories[currentStyleIndex].promptsList,
false,
);
return;
}
if (value === 'EXTREME_SPEED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
decorationDesignSpeedModePrompts,
false,
);
return;
}
if (value === 'STANDARD') {
const defaultRefs = categoryRefsGroup[currentStyleIndex];
// 获取对应的类目配置数据
const categories = drawStyleCategoryRef.current?.styleConfigList || [];
// 手动触发选择逻辑
const ids = getStyleDataIndexByRefs('id', categories, defaultRefs);
if (ids.length) {
styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false);
}
}
return;
}
if (value === 'ADVANCED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
activeCategories[currentStyleIndex].promptsList,
true,
);
return;
}
if (value === 'EXTREME_SPEED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true);
return;
}
if (value === 'STANDARD') {
const defaultRefs = categoryRefsGroup[currentStyleIndex];
// 获取对应的类目配置数据
const categories = drawStyleCategoryRef.current?.styleConfigList || [];
// 手动触发选择逻辑
const ids = getStyleDataIndexByRefs('id', categories, defaultRefs);
if (ids.length) {
styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true);
}
}
};
// 版本切换-pc
const handleVersionPcChange: ThemeSegmentedProps['onChange'] = (item) => {
const value = item.value;
if (userAuth(['TOURIST'])) {
PubSub.publish('onLogin', true);
return;
}
if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) {
DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP');
return;
}
// 否则允许切换
setVersionValue(value);
// 描述词类型获取
const promptType = styleConfigTextAreaRef.current?.getPromptType();
// 描述词自定义-版本切换判断
if (promptType === 'USER' || promptType === 'INNOVATIVE') {
if (value === 'ADVANCED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
activeCategories[currentStyleIndex].promptsList,
false,
);
return;
}
if (value === 'EXTREME_SPEED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
decorationDesignSpeedModePrompts,
false,
);
return;
}
if (value === 'STANDARD') {
const defaultRefs = categoryRefsGroup[currentStyleIndex];
// 获取对应的类目配置数据
const categories = drawStyleCategoryRef.current?.styleConfigList || [];
// 手动触发选择逻辑
const ids = getStyleDataIndexByRefs('id', categories, defaultRefs);
if (ids.length) {
styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false);
}
}
return;
}
if (value === 'ADVANCED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
activeCategories[currentStyleIndex].promptsList,
true,
);
return;
}
if (value === 'EXTREME_SPEED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true);
return;
}
if (value === 'STANDARD') {
const defaultRefs = categoryRefsGroup[currentStyleIndex];
// 获取对应的类目配置数据
const categories = drawStyleCategoryRef.current?.styleConfigList || [];
// 手动触发选择逻辑
const ids = getStyleDataIndexByRefs('id', categories, defaultRefs);
if (ids.length) {
styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true);
}
}
};
// 高级版-风格类目描述词对应方法
function mapPromptsToCategories(categoryGroups: any, prompts: any) {
return categoryGroups.map((categoryGroup: any) => {
const promptConfig = prompts.find((prompt: any) => prompt.key === categoryGroup.key);
if (!promptConfig) {
return null;
}
return {
alias: categoryGroup.alias,
key: categoryGroup.key,
categories: categoryGroup.categories.map((cat: any) => ({
key: cat.key,
label: cat.alias,
thumbUrl: cat.coverUrl,
promptsList: promptConfig.list.map((item: any) => ({
placeholder: item.placeholder || '',
keyword: item.keyword || '',
})),
modelName: cat.modelName,
lora: cat.loraParamses?.[0],
})),
};
});
}
// 高级版-设计领域与风格切换
const onComfyUiStyleIndexChange = (index: number) => {
setCurrentStyleIndex(index);
const currentItem = activeCategories[index];
const isUniversalCategory = activeCategories[0];
// 随机描述词
let randomPrompts: CandidateItem[] = [];
if (currentItem.promptsList?.length) {
const randomIndex = Math.floor(Math.random() * currentItem.promptsList.length);
randomPrompts = [currentItem.promptsList[randomIndex]];
}
// 更新候选配置
styleConfigTextAreaRef.current?.updateCandidateConfig(randomPrompts, true);
if (isUniversalCategory) {
styleConfigTextAreaRef.current?.updateCandidateConfig(currentItem.promptsList, true);
}
};
// 高级版-风格类目名称切换
const handleStyleAliasChange = (item: DecorationDesignStyleItem) => {
setActiveAlias(item.alias);
// 切换一级类目时重置二级类目索引为0
setCurrentStyleIndex(0);
// 获取当前激活的二级类目列表
const currentCategories =
comfyUiStyleList.find((style) => style.alias === item.alias)?.categories || [];
// 确保存在有效的二级类目
if (currentCategories.length === 0) return;
// 描述词类型获取
const promptType = styleConfigTextAreaRef.current?.getPromptType();
// 描述词自定义-版本切换判断
if (promptType === 'USER' || promptType === 'INNOVATIVE') {
if (versionValue === 'ADVANCED') {
styleConfigTextAreaRef.current?.updateCandidateConfig(
activeCategories[currentStyleIndex].promptsList,
false,
);
return;
}
}
// 合并子类的描述词
const allPrompts = currentCategories.flatMap((cat) => cat.promptsList);
if (allPrompts.length === 0) return;
// 随机选择一个描述词
const randomIndex = Math.floor(Math.random() * allPrompts.length);
const randomPrompt = allPrompts[randomIndex];
console.log('randomPrompt', randomPrompt);
// 更新到输入框
styleConfigTextAreaRef.current?.updateCandidateConfig([randomPrompt], true);
onComfyUiStyleIndexChange();
};
// 获取语意分割组件和局部绘制组件内部参考图信息并转为ControlNet所需参数
const getControlsControlNetArgs = ({
localDrawImage,
semanticsSegmentImage,
}: {
localDrawImage: ControlNetEditorImageInfo | null;
semanticsSegmentImage: SemanticsSegmentImageType | null;
}) => {
const controlNetArgs: Pick<
RequestTextToImageGenerateParams,
'localDrawImage' | 'semanticsSegmentImage'
> = {};
if (localDrawImage?.enabled) {
controlNetArgs.localDrawImage = {
...localDrawImage,
...commonDrawControlNetModelMap.inpaint,
enable: true,
// 开始迭代步数
guidanceStart: 0,
guidanceEnd: 1,
// 结束迭代步数
weight: 1,
};
}
if (semanticsSegmentImage?.enabled) {
controlNetArgs.semanticsSegmentImage = {
...semanticsSegmentImage,
...commonDrawControlNetModelMap.semanticsSegment,
enable: true,
// 开始迭代步数
guidanceStart: 0,
guidanceEnd: 1,
// 结束迭代步数
weight: 1,
};
}
return controlNetArgs;
};
// 获取画板图片
const getFrameImage = async (sizeInfo: Pick<UploadImageResponse, 'width' | 'height'>) => {
// 导出画布方法
const exportFrame = featureLeaferFrameLayoutRef.current?.exportFrame;
if (!exportFrame) {
return Promise.reject();
}
// 导出内容
const result = await exportFrame('canvas', {
size: {
width: sizeInfo.width,
height: sizeInfo.height,
},
});
const leaferCanvas = result.data;
const context = leaferCanvas.context;
// 获取画板图片
const imageData = context.getImageData(0, 0, leaferCanvas.width, leaferCanvas.height);
const base64 = imageDataToBase64(imageData);
const file = blobToFile(base64ToBlob(base64));
const imageInfo = await microGetHuoshanUploadImageUrl(file);
featureLeaferFrameLayoutRef.current?.setCompareImage(imageInfo);
return imageInfo;
};
// comfyUI-有风格参考图
const generateComfyStyleReferenceTask = async (uploadImage: UploadImageResponse) => {
comfyUiGenerateModalRef.current?.setModalOpen(true);
let imageInfo: UploadImageResponse = uploadImage;
const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || '';
// 当前尺寸的阈值
const thresholdNum = imgToImgSizeConfig[sizeType];
// 尺寸信息
const sizeParams = computeContentObjectFit({
containerWidth: thresholdNum,
containerHeight: thresholdNum,
contentWidth: uploadImage.width,
contentHeight: uploadImage.height,
type: 'contain',
});
// 风格参考图获取
const baseImage = styleReferComfyImageInfo?.largeUrl;
try {
if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) {
imageInfo = await getFrameImage(uploadImage);
}
comfyUiGenerateModalRef.current?.generateTask({
imageUrl: imageInfo.largeUrl,
seed: getRandomNum(),
...sizeParams,
// 描述词
prompt,
// 底图符合度
baseImageWeight: weight,
// 参考图片链接
styleReferImageUrl: baseImage,
// 风格参考度-权重
styleReferWeight: comfyStyleReferWeight,
});
} catch (e) {
comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR');
}
};
// comfyUI-无风格参考图
const generateComfyTask = async (uploadImage: UploadImageResponse) => {
comfyUiGenerateModalRef.current?.setModalOpen(true);
let imageInfo: UploadImageResponse = uploadImage;
const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || '';
// 当前尺寸的阈值
const thresholdNum = imgToImgSizeConfig[sizeType];
// 尺寸信息
const sizeParams = computeContentObjectFit({
containerWidth: thresholdNum,
containerHeight: thresholdNum,
contentWidth: uploadImage.width,
contentHeight: uploadImage.height,
type: 'contain',
});
try {
if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) {
imageInfo = await getFrameImage(uploadImage);
}
comfyUiGenerateModalRef.current?.generateTask({
imageUrl: imageInfo.largeUrl,
seed: getRandomNum(),
...sizeParams,
// 描述词
prompt,
// 底图符合度
baseImageWeight: weight,
});
} catch (e) {
comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR');
}
};
// 标准版-开始创作
const onSdStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({
uploadImage,
localDrawImage,
semanticsSegmentImage,
}) => {
if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) {
return;
}
if (!uploadImage) {
message.warning(t('AiTools-LocalDrawImage-yES4'));
return;
}
let imageInfo: UploadImageResponse = uploadImage;
generateModalRef.current?.setModalOpen(true);
try {
if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) {
imageInfo = await getFrameImage(uploadImage);
}
// 提取当前类目下下的参数
const { modelName = '', loraParamses = [] } =
drawStyleCategoryRef.current?.getCategoryParams() || {};
let prompt = styleConfigTextAreaRef.current?.prompts.keyword || '';
// 启用了色块 获取色块描述
if (semanticsSegmentImage?.enabled) {
// 去重描述词
const paintKeywords = [
...new Set(semanticsSegmentImage.colorBlockList.map((item) => item.englishAlias)),
];
prompt = combineSentenceBySplitSymbol([...paintKeywords, prompt]);
}
// 参考图
const styleImage = styleReferComfyImageInfo;
generateModalRef.current?.generateTask({
// 获取语意分割、局部重绘相关controlnet参数
...getControlsControlNetArgs({ localDrawImage, semanticsSegmentImage }),
prompt,
modelName,
loraParamses,
negativePrompt: JZXZ_DRAW_INIT_NEGATIVE_PROMPTS,
baseImage: {
width: imageInfo.width,
height: imageInfo.height,
url: imageInfo.largeUrl || imageInfo.thumbUrl,
// 开始迭代步数
guidanceStart: 0,
// 结束迭代步数
guidanceEnd: 1,
// 相似度
module: 'scribble_xdog',
weight,
model: 'control_v11p_sd15_scribble',
},
...(styleImage
? {
styleReferImage: {
guidanceEnd: 1,
guidanceStart: 0,
height: styleImage.height as number,
model: 'ip-adapter_sd15_plus [32cd8f7f]',
module: 'ip-adapter_clip_sd15',
url: styleImage.largeUrl,
weight: comfyStyleReferWeight,
width: styleImage.width as number,
},
}
: {}),
sizeType,
generateMode: 'REPAINT',
generatePriorityType,
generateImageType: 'DECORATION_DESIGN',
});
} catch (error) {
message.error(t('AiTools-DecorationDesign-Operate-hook-trpaP'));
}
};
// comfy-高级版-开始创作
const onComfyStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async (params) => {
if (!params?.uploadImage) {
message.warn(t('AiTools-DetailEnhance-Operate-hook-PzPPn'));
return;
}
if (styleReferComfyImageInfo) {
await generateComfyStyleReferenceTask(params.uploadImage);
} else {
await generateComfyTask(params.uploadImage);
}
};
// 极速版
const sendRequest = async (
// 请求参数
params: RequestDescribeRetouchingParams,
// 重试次数
retryCount: number,
// 请求头配置 用于减次数
config?: AxiosRequestConfig,
): Promise<UploadImageType> => {
// 没有重试次数了 直接抛出异常
if (retryCount <= 0) {
throw { errorCode: 'MAX_RETRY_TIMES', errorMsg: '生成图片异常' };
}
const response = await requestDescribeRetouching(params, config).catch((e) => {
console.error('请求失败:', e);
return null;
});
// 本次生成没有结果 重试
if (!response) {
await delayFun(REQUEST_DELAY_DURATION);
return sendRequest(params, retryCount - 1, config);
}
// 拿到了结果 正常返回
if (response.imageList[0]) {
return response.imageList[0];
}
// 拿到了结果但是没有图片返回且有文案提示
if (!response.imageList.length && response.text) {
setErrorMsg(response.text.replace(/\*/g, ''));
// 特殊错误,不重试
throw { errorCode: 'NO_IMAGE', errorMsg: response.text };
}
// 其他情况重试
await delayFun(REQUEST_DELAY_DURATION);
return sendRequest(params, retryCount - 1, config);
};
const batchRequest = async (params: RequestDescribeRetouchingParams) => {
const result: UploadImageType[] = [];
const data = await sendRequest(params, DEFAULT_RETRY_COUNT);
result.push(data);
return result;
};
const onSpeedModeGenerateSuccess = (list: UploadImageType[]) => {
const result = list[0].largeUrl;
featureLeaferFrameLayoutRef.current?.setResult(result);
// 添加进快捷使用
addResultImage({ ...list[0], url: list[0].largeUrl });
aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createSuccess');
refreshToolEquityUserUseInfo(decorationDesignToolKey);
};
// 开始创作-极速版
const onSpeedModeStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({
uploadImage,
}) => {
if (!uploadImage) {
message.warning({ content: '请先上传图片', key: 'emptyImage' });
return;
}
setErrorMsg('');
generateSpeedModalRef.current?.setModalOpen(true);
generateSpeedModalRef.current?.setRequestStatus('EXECUTING');
// 底图
const uploadImageInfo = uploadImage.largeUrl;
// 参考图
const baseImage = styleReferComfyImageInfo?.largeUrl;
const imageUrlList: string[] = [];
imageUrlList.push(uploadImageInfo);
if (baseImage) {
imageUrlList.push(baseImage);
}
// 描述词
const prompts = styleConfigTextAreaRef.current?.prompts.placeholder || '';
// 拼接描述词-有参考图
const baseImageDescription = `图一参考图二,图片要求按${uploadImage.width}:${uploadImage.height}比例生成`;
const newPrompts = `${prompts},${baseImageDescription}`;
// 拼接描述词-没有参考图
const noBaseImageDescription = `图片要求按${uploadImage.width}:${uploadImage.height}比例生成`;
const promptsText = `${prompts},${noBaseImageDescription}`;
// 根据baseImage是否存在,设置description
const description = baseImage ? newPrompts : promptsText;
try {
// 发起请求
const response = await batchRequest({
description: combineSentenceBySplitSymbol([description]),
imageUrls: imageUrlList,
});
onSpeedModeGenerateSuccess(response);
generateSpeedModalRef.current?.setRequestStatus('PENDING');
generateSpeedModalRef.current?.setModalOpen(false);
} catch (e) {
aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createError');
generateSpeedModalRef.current?.setRequestStatus('POLLING_ERROR');
}
};
// 生成结果回调
const onGenerateSuccess = async (response: AiToolProcessSuccessResponse) => {
try {
const result = response.imageList[0];
// 刷新次数
refreshToolEquityUserUseInfo(decorationDesignToolKey);
// 设置显示
featureLeaferFrameLayoutRef.current?.setResult(result);
const sizeInfo = await getImageSizeInfo(result);
// 添加生成记录
addResultImage({
url: result,
...sizeInfo,
});
} catch (e) {
console.error(e);
}
};
// 画板插入贴图
const onChartletItemClick = (imageInfo: ChartletImageItem) => {
featureLeaferFrameLayoutRef.current?.insertPicture(imageInfo);
};
// 画板插入本地上传图片
const onChartletUploadSuccess: CustomUploadProps['onUploadSuccess'] = async ({ url }) => {
try {
const size = await getImageSizeInfo(url);
featureLeaferFrameLayoutRef.current?.insertPicture({
url,
width: size.width,
height: size.height,
});
} catch (err) {
console.error(err);
message.error(t('AiTools-ImageEditorModal-hooks-mdQhK'));
}
};
// 创作优先级改变
const onGeneratePriorityTypeChange = (type: GeneratePriorityType) => {
setGeneratePriorityType(type);
setWeight(type === 'CONTROLNET' ? 1 : 1.2);
};
// 标准版-类目改变回调
const onSelectRefsChange: UseDrawStyleCategoryConfigProps['onSelectRefsChange'] = (
refs,
categories,
) => {
if (versionValue !== 'STANDARD') {
return;
}
drawStyleCategoryRef.current?.setSelectRefs(refs);
const ids = getStyleDataIndexByRefs('id', categories, refs);
if (ids.length) {
styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids);
}
};
// 标准版
const onCategoryInit = (styleConfigItems: DrawStyleConfigItem[]) => {
// 一级类目ref
const firstCategoryRefs = categoryRefsGroup.map((item) => item[0]);
// 过滤出相关的类目信息
const filterStyleConfigItems = styleConfigItems.filter((item) =>
firstCategoryRefs.includes(item.ref),
);
drawStyleCategoryRef.current?.setStyleConfigList(filterStyleConfigItems);
// 请求描述词
onSelectRefsChange(categoryRefsGroup[0], filterStyleConfigItems);
};
// 当点击继续处理时
const onContinue = async (result: string) => {
try {
const sizeInfo = await getImageSizeInfo(result);
featureLeaferFrameLayoutRef.current?.onUploadSuccess([
{ ...sizeInfo, largeUrl: result, thumbUrl: result },
]);
} catch (e) {
message.error(t('AiTools-DecorationDesign-Operate-hook-6ipKH'));
}
};
const onSelectorModalFinish: ImageSelectorModalProps['onFinish'] = async (item) => {
featureLeaferFrameLayoutRef.current?.onUploadSuccess([item]);
if (item.originFile) {
aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, isMobile() ? 'mobileUpload' : 'pcUpload');
}
};
// 风格底图选择弹窗回调
const onDecorationDesignReferImageModalFinish: ImageSelectorModalProps['onFinish'] = async ({
largeUrl,
thumbUrl,
width,
height,
originFile,
}) => {
setStyleReferComfyImageInfo({
width,
height,
largeUrl,
thumbUrl,
});
let base64;
// 原图有originFile直接转base64
if (originFile) {
try {
base64 = await fileToBase64(originFile);
} catch (e) {
console.error(e);
}
}
// base64转换成功 打开裁剪弹框 失败则默认为原图
setCropperModalOpen(true);
uploadCropperModalRef.current?.setCropperImageUrl(base64 || largeUrl);
};
// 风格底图裁剪完成图片弹框回调
const onStyleReferImageCropperSuccess: UploadCropperModalProps['onCropperSuccess'] = async (
obj,
) => {
setStyleReferComfyImageInfo(obj);
setCropperModalOpen(false);
};
const onStyleReferImageCropperError = () => {
setCropperModalOpen(false);
message.error(t('AiTools-TransferSmudge-Operate-hook-MiGY'));
};
// 开始创作版本判断
const generateTask: FeatureLeaferFrameLayoutCommonProps['onStart'] = (params) => {
if (userAuth(['TOURIST'])) {
PubSub.publish('onLogin', true);
return;
}
if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) {
return;
}
if (versionValue === 'STANDARD') {
onSdStart(params);
return;
}
if (versionValue === 'EXTREME_SPEED') {
onSpeedModeStart(params);
return;
}
if (versionValue === 'ADVANCED') {
onComfyStart(params);
}
};
useMount(() => {
// 获取极速版描述词
styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true);
// 获取高级版风格类目
const comfyUiStyle = mapPromptsToCategories(comfyUiCategoryRefsGroup, comfyUiPrompt);
setComfyUiStyleList(comfyUiStyle);
if (comfyUiStyle.length > 0) {
setActiveAlias(comfyUiStyle[0].alias);
setCurrentStyleIndex(0);
}
const image: UploadBtnImageResponse = location.state?.image;
if (image) {
featureLeaferFrameLayoutRef.current?.onUploadSuccess([image]);
return;
}
// 地址栏参数传递
const url = decodeImageUrl(searchParams.get('url') || '');
const width = searchParams.get('width');
const height = searchParams.get('height');
if (url && width && height) {
featureLeaferFrameLayoutRef.current?.onUploadSuccess([
{
largeUrl: url,
thumbUrl: url,
width: Number(width),
height: Number(height),
},
]);
setSearchParams({});
}
});
// 室内装修初始设置选中室内色块
useEffect(() => {
semanticsSegmentImageControlRef.current?.setSelectCategoryIndex(1);
}, []);
return {
noLimitFrequency,
styleConfigTextAreaRef,
imageSelectorModalRef,
generateModalRef,
featureLeaferFrameLayoutRef,
drawStyleCategoryRef,
toolEquityCountWrapRef,
semanticsSegmentImageControlRef,
generatePriorityType,
categoryRefsGroup,
weight,
setWeight,
sizeType,
setSizeType,
weightSliderMaxSize: generatePriorityType === 'PROMPTS' ? 2 : 1.3,
chartletCategoryList,
queryTagList,
onChartletItemClick,
onCategoryInit,
onChartletUploadSuccess,
onGenerateSuccess,
onContinue,
onGeneratePriorityTypeChange,
onSelectRefsChange,
onSelectorModalFinish,
versionValue,
handleVersionPcChange,
productType,
comfyUiStyleList,
setActiveAlias,
activeAlias,
activeCategories,
onComfyUiStyleIndexChange,
currentStyleIndex,
comfyUiGenerateModalRef,
drawComfyUiStyleCategoryRef,
styleReferComfyImageInfo,
setStyleReferComfyImageInfo,
comfySourceImageSelectorModalRef,
comfyStyleReferWeight,
setComfyStyleReferWeight,
generateTask,
onDecorationDesignReferImageModalFinish,
uploadCropperModalRef,
cropperModalOpen,
setCropperModalOpen,
onStyleReferImageCropperSuccess,
onStyleReferImageCropperError,
generateSpeedModalRef,
errorMsg,
DecorationDesignipLimitModalRef,
handleVersionMobileChange,
handleStyleAliasChange,
};
};
onComfyUiStyleIndexChange();需要传入对应alias的index怎么实现