diff --git a/change/@acedatacloud-nexior-fbd2872b-af79-4bd0-9e3e-46dead93994b.json b/change/@acedatacloud-nexior-fbd2872b-af79-4bd0-9e3e-46dead93994b.json new file mode 100644 index 00000000..e8a51d08 --- /dev/null +++ b/change/@acedatacloud-nexior-fbd2872b-af79-4bd0-9e3e-46dead93994b.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "add headshots ux", + "packageName": "@acedatacloud/nexior", + "email": "1348977728@qq.com", + "dependentChangeType": "patch" +} diff --git a/src/components/common/Navigator.vue b/src/components/common/Navigator.vue index fb353515..7cdd4e55 100644 --- a/src/components/common/Navigator.vue +++ b/src/components/common/Navigator.vue @@ -111,6 +111,8 @@ import { ROUTE_QRART_HISTORY, ROUTE_LUMA_INDEX, ROUTE_LUMA_HISTORY, + ROUTE_HEADSHOTS_INDEX, + ROUTE_HEADSHOTS_HISTORY, ROUTE_SUNO_INDEX, ROUTE_SUNO_HISTORY, ROUTE_SITE_INDEX @@ -147,6 +149,7 @@ export default defineComponent({ computed: { links() { const result = []; + // Add chat's leftmost icon if (this.$store?.state?.site?.features?.chat?.enabled) { result.push({ route: { @@ -157,6 +160,7 @@ export default defineComponent({ routes: [ROUTE_CHAT_CONVERSATION, ROUTE_CHAT_CONVERSATION_NEW] }); } + // Add midjourney's leftmost icon if (this.$store?.state?.site?.features?.midjourney?.enabled) { result.push({ route: { @@ -167,7 +171,7 @@ export default defineComponent({ routes: [ROUTE_MIDJOURNEY_INDEX] }); } - + // Add chatdoc's leftmost icon /* if (this.$store?.state?.site?.features?.chatdoc?.enabled) { result.push({ @@ -180,7 +184,7 @@ export default defineComponent({ }); } */ - + // Add qrart's leftmost icon if (this.$store?.state?.site?.features?.qrart?.enabled) { result.push({ route: { @@ -213,6 +217,17 @@ export default defineComponent({ routes: [ROUTE_LUMA_INDEX, ROUTE_LUMA_HISTORY] }); } + // Add headshots's leftmost icon + if (this.$store?.state?.site?.features?.headshots?.enabled) { + result.push({ + route: { + name: ROUTE_HEADSHOTS_INDEX + }, + displayName: this.$t('common.nav.headshots'), + icon: 'fa-solid fa-id-card', + routes: [ROUTE_HEADSHOTS_INDEX, ROUTE_HEADSHOTS_HISTORY] + }); + } if (this.direction === 'row') { result.push({ diff --git a/src/components/headshots/ConfigPanel.vue b/src/components/headshots/ConfigPanel.vue new file mode 100644 index 00000000..4e75e58d --- /dev/null +++ b/src/components/headshots/ConfigPanel.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/components/headshots/DetailPanel.vue b/src/components/headshots/DetailPanel.vue new file mode 100644 index 00000000..fae99a5f --- /dev/null +++ b/src/components/headshots/DetailPanel.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/headshots/ImageGallery.vue b/src/components/headshots/ImageGallery.vue new file mode 100644 index 00000000..755819a4 --- /dev/null +++ b/src/components/headshots/ImageGallery.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/components/headshots/OperationPanel.vue b/src/components/headshots/OperationPanel.vue new file mode 100644 index 00000000..a90b46dc --- /dev/null +++ b/src/components/headshots/OperationPanel.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/headshots/RecentPanel.vue b/src/components/headshots/RecentPanel.vue new file mode 100644 index 00000000..438f9a7c --- /dev/null +++ b/src/components/headshots/RecentPanel.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/components/headshots/config/ElementsSelector.vue b/src/components/headshots/config/ElementsSelector.vue new file mode 100644 index 00000000..467a3a11 --- /dev/null +++ b/src/components/headshots/config/ElementsSelector.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/src/components/headshots/config/ImageUrlsInput.vue b/src/components/headshots/config/ImageUrlsInput.vue new file mode 100644 index 00000000..733b1ed4 --- /dev/null +++ b/src/components/headshots/config/ImageUrlsInput.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/src/components/headshots/config/ModeSelector.vue b/src/components/headshots/config/ModeSelector.vue new file mode 100644 index 00000000..0fbbab59 --- /dev/null +++ b/src/components/headshots/config/ModeSelector.vue @@ -0,0 +1,83 @@ + + + + + + + diff --git a/src/components/headshots/task/Preview.vue b/src/components/headshots/task/Preview.vue new file mode 100644 index 00000000..ac952f51 --- /dev/null +++ b/src/components/headshots/task/Preview.vue @@ -0,0 +1,303 @@ + + + + + + + diff --git a/src/constants/headshots.ts b/src/constants/headshots.ts new file mode 100644 index 00000000..e9ed8392 --- /dev/null +++ b/src/constants/headshots.ts @@ -0,0 +1 @@ +export const HEADSHOTS_SERVICE_ID = '3b2ec5fb-cfcb-49f0-9393-6b8617a2d8c4'; diff --git a/src/constants/index.ts b/src/constants/index.ts index 6bfa94dc..68d36e60 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -6,6 +6,7 @@ export * from './chatdoc'; export * from './midjourney'; export * from './qrart'; export * from './luma'; +export * from './headshots'; export * from './suno'; export * from './mapping'; export * from './surface'; diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 13027b84..9737d40f 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -72,6 +72,7 @@ export const loadLocaleMessages = async (i18n: I18n, locale: string) => { 'application', 'qrart', 'luma', + 'headshots', 'suno', 'common', 'console', diff --git a/src/i18n/zh-CN/common.json b/src/i18n/zh-CN/common.json index 503d68c1..abeddf7a 100644 --- a/src/i18n/zh-CN/common.json +++ b/src/i18n/zh-CN/common.json @@ -327,6 +327,10 @@ "message": "站点配置", "description": "网站导航栏中的文本,用于显示站点配置页面" }, + "nav.headshots": { + "message": "AI证件照", + "description": "网站导航栏中的文本,用于显示站点配置页面" + }, "nav.locale": { "message": "语言设置", "description": "网站导航栏中的文本,用于更改语言" diff --git a/src/i18n/zh-CN/headshots.json b/src/i18n/zh-CN/headshots.json new file mode 100644 index 00000000..62d0372a --- /dev/null +++ b/src/i18n/zh-CN/headshots.json @@ -0,0 +1,195 @@ +{ + "name.taskId": { + "message": "任务 ID", + "description": "任务的 ID" + }, + "name.mode": { + "message": "生成速度", + "description": "速度参数的名称,用于设置生成图像的速度" + }, + "name.type": { + "message": "类型", + "description": "要生成的 QR 码的内容类型,如'链接'、'文本'等" + }, + "name.failure": { + "message": "失败", + "description": "任务的失败状态" + }, + "name.failureReason": { + "message": "失败原因", + "description": "任务失败的原因" + }, + "name.status": { + "message": "状态", + "description": "任务的状态" + }, + "name.traceId": { + "message": "追踪 ID", + "description": "任务的追踪 ID" + }, + "name.size": { + "message": "尺寸", + "description": "要生成的 QR 码的尺寸" + }, + "name.createdAt": { + "message": "创建时间", + "description": "生成 QR 码的日期和时间" + }, + "name.endImageUrls": { + "message": "人像图片", + "description": "要生成的 QR 码的种子" + }, + "description.endImageUrls": { + "message": "根据此人像图片生成对应的证件照", + "description": "要生成的 QR 码的种子" + }, + "name.content": { + "message": "内容", + "description": "要生成的 QR 码的内容" + }, + + "field.elements": { + "message": "证件照风格", + "description": "用于生成图像的绘画风格" + }, + "name.headshotsBot": { + "message": "Headshots Bot", + "description": "Headshots 证件照生成的名称" + }, + "name.contentImageUrl": { + "message": "内容图片链接", + "description": "要生成的 QR 码的内容图片 URL" + }, + "inputWay.input": { + "message": "输入内容", + "description": "输入方式" + }, + + "status.pending": { + "message": "等待中", + "description": "任务的等待状态" + }, + "status.processing": { + "message": "处理中", + "description": "任务的等待状态" + }, + "placeholder.paddingNoise": { + "message": "请选择...", + "description": "填充噪音输入的占位符" + }, + "placeholder.paddingLevel": { + "message": "请选择...", + "description": "填充级别的占位符" + }, + "placeholder.prompt": { + "message": "输入要生成的视频风格的提示词,例如:Astronauts shuttle from space to volcano", + "description": "提示字段中的占位文本" + }, + "button.fast": { + "message": "快速", + "description": "按钮文本,允许用户将'快速模式'设置为快速以生成图像,必须翻译为'快速模式'" + }, + "button.relax": { + "message": "慢速", + "description": "按钮文本,允许用户将'慢速模式'设置为放松以生成图像,必须翻译为'慢速模式'" + }, + "button.generate": { + "message": "生成", + "description": "生成Headshots证件照 的按钮文本" + }, + + "button.download": { + "message": "下载视频", + "description": "下载视频按钮文本" + }, + "button.uploadImageUrls": { + "message": "上传人像图片", + "description": "上传人像图片的按钮文本" + }, + "message.uploadReferencesExceed": { + "message": "最多可上传 5 张图片", + "description": "上传的图片数量超过限制时的错误消息" + }, + "message.uploadReferencesSuccess": { + "message": "成功上传图片", + "description": "成功上传图片时的成功消息" + }, + "message.uploadReferencesError": { + "message": "上传图片失败,请稍后重试", + "description": "图片上传失败时的错误消息" + }, + "message.startingTask": { + "message": "正在启动任务...", + "description": "绘图任务开始时的消息" + }, + "message.startTaskSuccess": { + "message": "成功发起生成任务", + "description": "成功启动任务时的成功消息" + }, + "message.startTaskFailed": { + "message": "发起任务失败,请联系管理员", + "description": "启动绘图任务失败时的错误消息" + }, + "message.usedUp": { + "message": "您的积分已用完,请购买更多积分后继续使用", + "description": "已用完时的消息" + }, + "message.generating": { + "message": "生成中...", + "description": "图像正在生成时的消息" + }, + "message.downloadVideo": { + "message": "下载此视频", + "description": "图像正在生成时的消息" + }, + + "message.generateFailed": { + "message": "生成图像失败", + "description": "图像生成失败时的消息" + }, + + "message.noOperations": { + "message": "没有可用操作", + "description": "没有可用操作时的消息" + }, + "message.noTasks": { + "message": "没有历史任务,请点击左方生成视频", + "description": "没有任务时的消息" + }, + "styleCategory.styles": { + "message": "证件照风格", + "description": "用于描述和生成图像的类别词,生成的图像应包括此标签的类别" + }, + "styleTag.malePportrait": { + "message": "男形象照", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.malePportrait2": { + "message": "男形象照2", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.kindergarten": { + "message": "幼儿园入园照", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.logoTshirt": { + "message": "企业LogoT恤照", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.wedding": { + "message": "结婚登记照", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.businessPphoto": { + "message": "商务风写真", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.bobSuit": { + "message": "黑西装波波头", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + }, + "styleTag.femalePortrait": { + "message": "女性形象照", + "description": "用于描述和生成图像的风格词,生成的图像应包括此标签的风格" + } +} diff --git a/src/layouts/Headshots.vue b/src/layouts/Headshots.vue new file mode 100644 index 00000000..176af7fd --- /dev/null +++ b/src/layouts/Headshots.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/models/headshots.ts b/src/models/headshots.ts new file mode 100644 index 00000000..bbe66fc2 --- /dev/null +++ b/src/models/headshots.ts @@ -0,0 +1,54 @@ +export interface Picture { + id?: string; + audio_url?: string; + created_at?: string; +} +// Submitted payload +export interface IHeadshotsConfig { + mode?: string; + template?: string; + image_urls?: string[]; +} +export interface IHeadshotsGenerateRequest { + mode?: string; + template?: string; + image_urls?: string[]; +} + +export interface IHeadshotsPicture { + id?: string; + image_url?: string; + template?: number; +} + +export interface IHeadshotsGenerateResponse { + success?: boolean; + task_id: string; + trace_id: string; + error?: { + code?: string; + message?: string; + }; + data?: IHeadshotsPicture[]; +} + +export interface IHeadshotsTask { + map(arg0: (song: any) => any): any; + id: string; + _id: string; + api_id?: string; + application_id?: string; + created_at?: string; + credential_id?: string; + trace_id?: string; + user_id?: string; + request?: IHeadshotsGenerateRequest; + response?: IHeadshotsGenerateResponse; +} + +export type IHeadshotsTaskResponse = IHeadshotsTask; + +export interface IHeadshotsTasksResponse { + count: number; + items: IHeadshotsTask[]; +} diff --git a/src/models/index.ts b/src/models/index.ts index 7c61cd7a..6bee3255 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -13,6 +13,7 @@ export * from './service'; export * from './credential'; export * from './qrart'; export * from './luma'; +export * from './headshots'; export * from './suno'; export * from './site'; export * from './exchange'; diff --git a/src/models/site.ts b/src/models/site.ts index 03364077..3a95baf2 100644 --- a/src/models/site.ts +++ b/src/models/site.ts @@ -4,6 +4,7 @@ export interface ISiteFeatures { chatdoc?: any; qrart?: any; luma?: any; + headshots?: any; suno?: any; support?: any; } diff --git a/src/operators/headshots.ts b/src/operators/headshots.ts new file mode 100644 index 00000000..58347b91 --- /dev/null +++ b/src/operators/headshots.ts @@ -0,0 +1,93 @@ +import axios, { AxiosResponse } from 'axios'; +import { + IHeadshotsGenerateRequest, + IHeadshotsGenerateResponse, + IHeadshotsTaskResponse, + IHeadshotsTasksResponse +} from '@/models'; +import { BASE_URL_API } from '@/constants'; + +class HeadshotsOperator { + async task(id: string, options: { token: string }): Promise> { + return await axios.post( + `/luma/tasks`, + { + action: 'retrieve', + id: id + }, + { + headers: { + accept: 'application/json', + 'content-type': 'application/json', + authorization: `Bearer ${options.token}`, + 'x-record-exempt': 'true' + }, + baseURL: BASE_URL_API + } + ); + } + + async tasks( + filter: { ids?: string[]; applicationId?: string; userId?: string; limit?: number; offset?: number }, + options: { token: string } + ): Promise> { + return await axios.post( + `/luma/tasks`, + { + action: 'retrieve_batch', + ...(filter.ids + ? { + ids: filter.ids + } + : {}), + ...(filter.applicationId + ? { + application_id: filter.applicationId + } + : {}), + ...(filter.userId + ? { + user_id: filter.userId + } + : {}), + ...(filter.limit !== undefined + ? { + limit: filter.limit + } + : {}), + ...(filter.offset !== undefined + ? { + offset: filter.offset + } + : {}) + }, + { + headers: { + accept: 'application/json', + 'content-type': 'application/json', + authorization: `Bearer ${options.token}`, + 'x-record-exempt': 'true' + }, + baseURL: BASE_URL_API + } + ); + } + + async generate( + data: IHeadshotsGenerateRequest, + options: { + token: string; + } + ): Promise> { + return await axios.post('/luma/videos', data, { + headers: { + authorization: `Bearer ${options.token}`, + 'content-type': 'application/json', + accept: 'application/x-ndjson' + }, + baseURL: BASE_URL_API + }); + } +} + +export const headshotsOperator = new HeadshotsOperator(); diff --git a/src/operators/index.ts b/src/operators/index.ts index 2c69b3ae..db63e6c3 100644 --- a/src/operators/index.ts +++ b/src/operators/index.ts @@ -13,6 +13,7 @@ export * from './service'; export * from './credential'; export * from './qrart'; export * from './luma'; +export * from './headshots'; export * from './site'; export * from './suno'; export * from './exchange'; diff --git a/src/pages/headshots/Index.vue b/src/pages/headshots/Index.vue new file mode 100644 index 00000000..0394fc31 --- /dev/null +++ b/src/pages/headshots/Index.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/src/pages/site/Index.vue b/src/pages/site/Index.vue index 4e449deb..55c7b7c4 100644 --- a/src/pages/site/Index.vue +++ b/src/pages/site/Index.vue @@ -174,7 +174,15 @@ diff --git a/src/plugins/font-awesome.ts b/src/plugins/font-awesome.ts index 52a31523..3afd54fa 100644 --- a/src/plugins/font-awesome.ts +++ b/src/plugins/font-awesome.ts @@ -11,6 +11,7 @@ import { } from '@fortawesome/free-regular-svg-icons'; import { faDiscord as faBrandsDiscord, faWeixin as faBrandsWeixin } from '@fortawesome/free-brands-svg-icons'; import { + faIdCard as faSolidIdCard, faStopCircle as faSolidStopCircle, faPlayCircle as faSolidPlayCircle, faDownload as faSolidDownload, @@ -78,6 +79,7 @@ import { faAngleDown as faSolidAngleDown } from '@fortawesome/free-solid-svg-icons'; // add icons +library.add(faSolidIdCard); library.add(faSolidStopCircle); library.add(faRegularFile); library.add(faSolidPlayCircle); diff --git a/src/router/constants.ts b/src/router/constants.ts index 3f5efa09..32790e2f 100644 --- a/src/router/constants.ts +++ b/src/router/constants.ts @@ -16,6 +16,9 @@ export const ROUTE_QRART_HISTORY = 'qrart-history'; export const ROUTE_LUMA_INDEX = 'luma-index'; export const ROUTE_LUMA_HISTORY = 'luma-history'; +export const ROUTE_HEADSHOTS_INDEX = 'headshots-index'; +export const ROUTE_HEADSHOTS_HISTORY = 'headshots-history'; + export const ROUTE_CHATDOC_INDEX = 'chatdoc-index'; export const ROUTE_CHATDOC_CONVERSATION = 'chatdoc-conversation'; export const ROUTE_CHATDOC_CONVERSATION_NEW = 'chatdoc-conversation-new'; diff --git a/src/router/headshots.ts b/src/router/headshots.ts new file mode 100644 index 00000000..81a4ad21 --- /dev/null +++ b/src/router/headshots.ts @@ -0,0 +1,16 @@ +import { ROUTE_HEADSHOTS_INDEX } from './constants'; + +export default { + path: '/headshots', + meta: { + auth: true + }, + component: () => import('@/layouts/Main.vue'), + children: [ + { + path: '', + name: ROUTE_HEADSHOTS_INDEX, + component: () => import('@/pages/headshots/Index.vue') + } + ] +}; diff --git a/src/router/index.ts b/src/router/index.ts index 23516a96..569da283 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,6 +7,7 @@ import distribution from './distribution'; import chatdoc from './chatdoc'; import qrart from './qrart'; import luma from './luma'; +import headshots from './headshots'; import suno from './suno'; import site from './site'; import profile from './profile'; @@ -33,6 +34,7 @@ const routes = [ chat, qrart, luma, + headshots, suno, midjourney, distribution, diff --git a/src/store/common/models.ts b/src/store/common/models.ts index f44a39d7..ffea51c4 100644 --- a/src/store/common/models.ts +++ b/src/store/common/models.ts @@ -4,6 +4,7 @@ import { IChatState } from '../chat/models'; import { IChatdocState } from '../chatdoc/models'; import { IQrartState } from '../qrart/models'; import { ILumaState } from '../luma/models'; +import { IHeadshotsState } from '../headshots/models'; import { ISunoState } from '../suno/models'; export interface ISetting {} @@ -31,5 +32,6 @@ export interface IRootState extends ICommonState { chatdoc: IChatdocState; qrart: IQrartState; luma: ILumaState; + headshots: IHeadshotsState; suno: ISunoState; } diff --git a/src/store/headshots/actions.ts b/src/store/headshots/actions.ts new file mode 100644 index 00000000..c8f93b17 --- /dev/null +++ b/src/store/headshots/actions.ts @@ -0,0 +1,159 @@ +import { applicationOperator, headshotsOperator, serviceOperator } from '@/operators'; +import { IHeadshotsState } from './models'; +import { ActionContext } from 'vuex'; +import { IRootState } from '../common/models'; +import { IApplication, ICredential, IHeadshotsConfig, IHeadshotsTask, IService, IApplicationType } from '@/models'; +import { Status } from '@/models/common'; +import { HEADSHOTS_SERVICE_ID } from '@/constants'; + +export const resetAll = ({ commit }: ActionContext): void => { + commit('resetAll'); +}; + +export const setCredential = async ({ commit }: any, payload: ICredential): Promise => { + console.debug('set credential', payload); + commit('setCredential', payload); +}; + +export const setConfig = ({ commit }: any, payload: IHeadshotsConfig) => { + commit('setConfig', payload); +}; + +export const setService = async ({ commit }: any, payload: IService): Promise => { + console.debug('set service', payload); + commit('setService', payload); +}; + +export const setApplication = ({ commit }: any, payload: IApplication[]) => { + commit('setApplication', payload); +}; + +export const getApplications = async ({ + commit, + state, + rootState +}: ActionContext): Promise => { + console.debug('start to get applications for headshots'); + return new Promise((resolve, reject) => { + state.status.getApplications = Status.Request; + applicationOperator + .getAll({ + user_id: rootState?.user?.id, + service_id: HEADSHOTS_SERVICE_ID + }) + .then((response) => { + console.debug('get applications success', response?.data); + state.status.getApplications = Status.Success; + // check if there is any application with 'Period' type + const application = response.data.items?.find((application) => application?.type === IApplicationType.PERIOD); + const application2 = response.data.items?.find((application) => application?.type === IApplicationType.USAGE); + if (application && application?.remaining_amount) { + console.debug('set application with Period', application); + commit('setApplication', application); + const credential = application?.credentials?.find( + (credential) => credential?.host === window.location.origin + ); + console.debug('set credential with Period', application); + commit('setCredential', credential); + } else if (application2) { + console.debug('set application with Usage', application2); + commit('setApplication', application2); + const credential = application2?.credentials?.find( + (credential) => credential?.host === window.location.origin + ); + console.debug('set credential with Usage', application); + commit('setCredential', credential); + } else { + console.debug('set application with null', response.data.items?.[0]); + commit('setApplication', response.data.items?.[0]); + } + resolve(response.data.items); + console.debug('save application success', response.data.items[0]); + }) + .catch((error) => { + state.status.getApplications = Status.Error; + reject(error); + }); + }); +}; + +export const setTasks = ({ commit }: any, payload: any) => { + commit('setTasks', payload); +}; + +export const setTasksItems = ({ commit }: any, payload: IHeadshotsTask[]) => { + commit('setTasksItems', payload); +}; + +export const setTasksTotal = ({ commit }: any, payload: number) => { + commit('setTasksTotal', payload); +}; + +export const setTasksActive = ({ commit }: any, payload: IHeadshotsTask) => { + commit('setTasksActive', payload); +}; + +export const getService = async ({ commit, state }: ActionContext): Promise => { + return new Promise((resolve, reject) => { + console.debug('start to get service for headshots'); + state.status.getService = Status.Request; + serviceOperator + .get(HEADSHOTS_SERVICE_ID) + .then((response) => { + state.status.getService = Status.Success; + commit('setService', response.data); + resolve(response.data); + }) + .catch((error) => { + state.status.getService = Status.Error; + reject(error); + }); + }); +}; + +export const getTasks = async ( + { commit, state, rootState }: ActionContext, + { offset, limit }: { offset?: number; limit?: number } +): Promise => { + return new Promise((resolve, reject) => { + console.debug('start to get tasks', offset, limit); + const credential = state.credential; + const token = credential?.token; + if (!token) { + return reject('no token'); + } + headshotsOperator + .tasks( + { + userId: rootState?.user?.id + }, + { + token + } + ) + .then((response) => { + console.debug('get imagine tasks success', response.data.items); + commit('setTasksItems', response.data.items); + commit('setTasksTotal', response.data.count); + resolve(response.data.items); + }) + .catch((error) => { + return reject(error); + }); + }); +}; + +export default { + setService, + getService, + resetAll, + setCredential, + setConfig, + setApplication, + getApplications, + setTasks, + setTasksItems, + setTasksTotal, + setTasksActive, + getTasks +}; diff --git a/src/store/headshots/index.ts b/src/store/headshots/index.ts new file mode 100644 index 00000000..c5292d63 --- /dev/null +++ b/src/store/headshots/index.ts @@ -0,0 +1,14 @@ +import { Module } from 'vuex'; +import { IHeadshotsState } from './models'; +import actions from './actions'; +import mutations from './mutations'; +import state from './state'; + +export const headshots: Module = { + namespaced: true, + state, + mutations, + actions +}; + +export default headshots; diff --git a/src/store/headshots/models.ts b/src/store/headshots/models.ts new file mode 100644 index 00000000..3bdfb55e --- /dev/null +++ b/src/store/headshots/models.ts @@ -0,0 +1,22 @@ +import { IApplication, ICredential, IService, Status } from '@/models'; +import { IHeadshotsConfig, IHeadshotsTask } from '@/models'; + +export interface IHeadshotsState { + application: IApplication | undefined; + applications: IApplication[] | undefined; + service: IService | undefined; + credential: ICredential | undefined; + config: IHeadshotsConfig | undefined; + tasks: + | { + items: IHeadshotsTask[] | undefined; + total: number | undefined; + active: IHeadshotsTask | undefined; + } + | undefined; + status: { + getService: Status; + getApplications: Status; + getTasks: Status; + }; +} diff --git a/src/store/headshots/mutations.ts b/src/store/headshots/mutations.ts new file mode 100644 index 00000000..4b631cbb --- /dev/null +++ b/src/store/headshots/mutations.ts @@ -0,0 +1,71 @@ +import { IApplication, ICredential, IHeadshotsConfig, IHeadshotsTask, IService } from '@/models'; +import { IHeadshotsState } from './models'; + +export const resetAll = (state: IHeadshotsState): void => { + state.service = undefined; + state.application = undefined; + state.config = undefined; + state.credential = undefined; + state.tasks = undefined; +}; + +export const setService = (state: IHeadshotsState, payload: IService): void => { + state.service = payload; +}; + +export const setCredential = (state: IHeadshotsState, payload: ICredential): void => { + state.credential = payload; +}; + +export const setApplication = (state: IHeadshotsState, payload: IApplication): void => { + state.application = payload; +}; + +export const setApplications = (state: IHeadshotsState, payload: IApplication[]): void => { + state.applications = payload; +}; + +export const setConfig = (state: IHeadshotsState, payload: IHeadshotsConfig): void => { + state.config = payload; +}; + +export const setTasksItems = (state: IHeadshotsState, payload: IHeadshotsTask[]): void => { + const newPayload = { + ...state.tasks, + items: payload + } as typeof state.tasks; + state.tasks = newPayload; +}; + +export const setTasksTotal = (state: IHeadshotsState, payload: number): void => { + const newPayload = { + ...state.tasks, + total: payload + } as typeof state.tasks; + state.tasks = newPayload; +}; + +export const setTasksActive = (state: IHeadshotsState, payload: IHeadshotsTask): void => { + const newPayload = { + ...state.tasks, + active: payload + } as typeof state.tasks; + state.tasks = newPayload; +}; + +export const setTasks = (state: IHeadshotsState, payload: any): void => { + state.tasks = payload; +}; + +export default { + setTasks, + setApplication, + setApplications, + setConfig, + setCredential, + setService, + setTasksActive, + setTasksItems, + setTasksTotal, + resetAll +}; diff --git a/src/store/headshots/persist.ts b/src/store/headshots/persist.ts new file mode 100644 index 00000000..47904624 --- /dev/null +++ b/src/store/headshots/persist.ts @@ -0,0 +1 @@ +export default ['headshots.credential', 'headshots.application', 'headshots.tasks']; diff --git a/src/store/headshots/state.ts b/src/store/headshots/state.ts new file mode 100644 index 00000000..206ce24b --- /dev/null +++ b/src/store/headshots/state.ts @@ -0,0 +1,18 @@ +import { IHeadshotsState } from './models'; +import { Status } from '@/models'; + +export default (): IHeadshotsState => { + return { + service: undefined, + application: undefined, + applications: undefined, + tasks: undefined, + credential: undefined, + config: undefined, + status: { + getService: Status.None, + getApplications: Status.None, + getTasks: Status.None + } + }; +}; diff --git a/src/store/index.ts b/src/store/index.ts index a02d3c97..1beab337 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -5,6 +5,7 @@ import chat from './chat'; import chatdoc from './chatdoc'; import qrart from './qrart'; import luma from './luma'; +import headshots from './headshots'; import suno from './suno'; import root from './common'; import persistChat from './chat/persist'; @@ -12,6 +13,7 @@ import persistMidjourney from './midjourney/persist'; import persistChatdoc from './chatdoc/persist'; import persistQrart from './qrart/persist'; import persistLuma from './luma/persist'; +import persistHeadshots from './headshots/persist'; import persistSuno from './suno/persist'; import persistRoot from './common/persist'; @@ -22,6 +24,7 @@ const store = createStore({ chat: chat, qrart: qrart, luma: luma, + headshots: headshots, suno: suno, chatdoc: chatdoc }, @@ -34,6 +37,7 @@ const store = createStore({ ...persistChatdoc, ...persistQrart, ...persistLuma, + ...persistHeadshots, ...persistSuno ] })