上传头像 部分卡片加入模型选择

This commit is contained in:
X1627315083@163.com
2026-03-18 10:59:12 +08:00
parent 61034e71d1
commit 68a6be6468
16 changed files with 235 additions and 41 deletions

View File

@@ -9,10 +9,11 @@ import request from '@/utils/request'
export interface getSketchFlowCanvasData { export interface getSketchFlowCanvasData {
id: string id: string
} }
export const getSketchFlowCanvas = (data:getSketchFlowCanvasData) => { export const getSketchFlowCanvas = (data:getSketchFlowCanvasData,loading?:boolean) => {
return request({ return request({
url: `/api/canvas/detail/${data.id}`, url: `/api/canvas/detail/${data.id}`,
method: 'get', method: 'get',
loading
}) })
} }
@@ -27,14 +28,15 @@ export interface saveSketchFlowCanvasData {
id?: string id?: string
canvasData: string canvasData: string
} }
export const putSketchFlowCanvas = (data:saveSketchFlowCanvasData) => { export const putSketchFlowCanvas = (data:saveSketchFlowCanvasData,loading?:boolean) => {
return request({ return request({
url: `/api/canvas/detail/${data.id}`, url: `/api/canvas/detail/${data.id}`,
method: 'put', method: 'put',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
data: data.canvasData data: data.canvasData,
loading
}) })
} }
@@ -112,6 +114,7 @@ export const toRealStyleApi = (data:toRealStyleData) => {
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以 * @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
* @param data.variantCount 生成图片的数量 * @param data.variantCount 生成图片的数量
* @param data.colors 生成上色的图片颜色列表 * @param data.colors 生成上色的图片颜色列表
* @param data.mode 选择的模型
* @returns 线稿图上色 * @returns 线稿图上色
*/ */
export interface toColorPaletteData { export interface toColorPaletteData {
@@ -119,6 +122,7 @@ export interface toColorPaletteData {
imageUrl?: string imageUrl?: string
variantCount?: string variantCount?: string
colors?: Array<string> colors?: Array<string>
mode?: string
} }
export const toColorPaletteApi = (data:toColorPaletteData) => { export const toColorPaletteApi = (data:toColorPaletteData) => {
return request({ return request({
@@ -128,7 +132,8 @@ export const toColorPaletteApi = (data:toColorPaletteData) => {
sketchId: data.sketchId, sketchId: data.sketchId,
imageUrl: data.imageUrl, imageUrl: data.imageUrl,
variantCount: data.variantCount, variantCount: data.variantCount,
colors: data.colors colors: data.colors,
mode: data.mode,
} }
}) })
} }
@@ -140,6 +145,7 @@ export const toColorPaletteApi = (data:toColorPaletteData) => {
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以 * @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
* @param data.styles 生成上色的图片颜色列表 * @param data.styles 生成上色的图片颜色列表
* @param data.userPrompt 生成上色的图片颜色列表 * @param data.userPrompt 生成上色的图片颜色列表
* @param data.mode 选择的模型
* @returns 场景构图 * @returns 场景构图
*/ */
export interface toSceneCompositionData { export interface toSceneCompositionData {
@@ -147,6 +153,7 @@ export interface toSceneCompositionData {
imageUrl?: string imageUrl?: string
userPrompt?: string userPrompt?: string
styles?: Array<string> styles?: Array<string>
mode?: string
} }
export const toSceneCompositionApi = (data:toSceneCompositionData) => { export const toSceneCompositionApi = (data:toSceneCompositionData) => {
return request({ return request({
@@ -156,8 +163,83 @@ export const toSceneCompositionApi = (data:toSceneCompositionData) => {
sketchId: data.sketchId, sketchId: data.sketchId,
imageUrl: data.imageUrl, imageUrl: data.imageUrl,
userPrompt: data.userPrompt, userPrompt: data.userPrompt,
styles: data.styles styles: data.styles,
mode: data.mode,
} }
}) })
} }
/**
* 线稿贴印花
* @param data 线稿贴印花的参数
* @param data.sketchId sketch id
* @param data.imageUrl 进行生成的图片。minio地址和正常地址都可以
* @param data.styles 生成上色的图片颜色列表
* @param data.userPrompt 生成上色的图片颜色列表
* @param data.mode 选择的模型
* @returns 线稿贴印花
*/
export interface sketchAddPrintData {
sketchId?: string
imageUrl?: string
surFaceUrl?: string
userPrompt?: string
mode?: string
}
export const sketchAddPrintApi = (data:sketchAddPrintData) => {
return request({
url: `/api/image/surface-edit`,
method: 'post',
data:{
sketchId: data.sketchId,
imageUrl: data.imageUrl,
userPrompt: data.userPrompt,
surFaceUrl: data.surFaceUrl,
mode: data.mode,
}
})
}
/**
* 图片转3d
* @param data 图片转3d的参数
* @param data.sketchId sketch id
* @param data.imageUrls 进行生成的图片。minio地址和正常地址都可以
* @returns 图片转3d
*/
export interface sketchToThreeData {
sketchId?: string
imageUrls?: Array<string>
}
export const sketchToThreeApi = (data:sketchToThreeData) => {
return request({
url: `/api/image/img-to-3d`,
method: 'post',
data:{
sketchId: data.sketchId,
imageUrls: data.imageUrls,
}
})
}
/**
* 3d模型转3视图
* @param data 3d模型转3视图的参数
* @param data.sketchId sketch id
* @param data.glbUrl 生成的3d模型地址
* @returns 3d模型转3视图
*/
export interface threeToThreeViewsData {
sketchId?: string
glbUrl?: string
}
export const threeToThreeViewsApi = (data:threeToThreeViewsData) => {
return request({
url: `/api/image/glb-to-3views`,
method: 'post',
data:{
sketchId: data.sketchId,
glbUrl: data.glbUrl,
}
})
}

View File

@@ -1,9 +1,10 @@
import request from '@/utils/request' import request from '@/utils/request'
export const uploadImage = (data: FormData) => { export const uploadImage = (data: FormData, loading = false) => {
return request({ return request({
url: '/api/file/upload', url: '/api/file/upload',
method: 'POST', method: 'POST',
loading,
data data
}) })
} }

View File

@@ -109,3 +109,27 @@ export const UpdateUserProfile = (data, loading = false) => {
}) })
} }
/**
* 上传头像
* @param data 上传头像的参数
* @param data.avatar 头像文件
* @returns 上传头像成功的响应
*/
export const UpdateUserAvatar = (data) => {
return request({
url: '/api/user/avatar',
method: 'post',
data
})
}
/**
* 获取上传头像次数
*/
export const getAvatarLimit = () => {
return request({
url: '/api/user/avatar/limit',
method: 'get',
})
}

View File

@@ -1,6 +1,8 @@
<template> <template>
<!-- 颜色调色板 --> <!-- 颜色调色板 -->
<div class="color-palette"> <div class="color-palette">
<p class="label">Mode</p>
<my-select v-model="data.mode" :list="modeList" />
<p class="label">Choose Color</p> <p class="label">Choose Color</p>
<div class="color-list"> <div class="color-list">
<div <div
@@ -24,7 +26,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, onMounted, ref } from 'vue' import { reactive, onMounted, ref } from 'vue'
const data = reactive({ const data = reactive({
colors: [] colors: [],
mode: 'Advanced',
}) })
const emit = defineEmits(['update-data']) const emit = defineEmits(['update-data'])
const maxColor = 5 const maxColor = 5
@@ -41,11 +44,15 @@
const target = e.target as HTMLInputElement const target = e.target as HTMLInputElement
data.colors.push(target.value) data.colors.push(target.value)
} }
const modeList = ref([
{ value: 'Advanced', label: 'Advanced' },
{ value: 'Normal', label: 'Normal' }
])
const getApiData = ()=>{ const getApiData = ()=>{
return { return {
variantCount: '2', variantCount: '2',
colors: data.colors colors: data.colors,
mode: data.mode,
} }
} }

View File

@@ -36,7 +36,7 @@
import To3View from './to-3view.vue' import To3View from './to-3view.vue'
import To3DModel from './to-3d-model.vue' import To3DModel from './to-3d-model.vue'
import { toRealStyleApi, toColorPaletteApi, toSceneCompositionApi } from '@/api/flow-canvas' import { toRealStyleApi, toColorPaletteApi, toSceneCompositionApi, sketchAddPrintApi, sketchToThreeApi, threeToThreeViewsApi } from '@/api/flow-canvas'
// import ToVideo from './to-video.vue' // import ToVideo from './to-video.vue'
// import AddPrint from './add-print.vue' // import AddPrint from './add-print.vue'
@@ -62,6 +62,7 @@
type: NODE_DATATYPE.SURFACE_EDIT, type: NODE_DATATYPE.SURFACE_EDIT,
title: 'Surface Edit', title: 'Surface Edit',
component: SurfaceEdit, component: SurfaceEdit,
api: sketchAddPrintApi
}, },
{ {
tier: NODE_DATATIER.SCENE_COMPOSITION, tier: NODE_DATATIER.SCENE_COMPOSITION,
@@ -81,13 +82,15 @@
tier: NODE_DATATIER.TO_3D_MODEL, tier: NODE_DATATIER.TO_3D_MODEL,
type: NODE_DATATYPE.TO_3D_MODEL, type: NODE_DATATYPE.TO_3D_MODEL,
title: 'To 3D Model', title: 'To 3D Model',
component: To3DModel component: To3DModel,
api:sketchToThreeApi
}, },
{ {
tier: NODE_DATATIER.TO_3VIEW, tier: NODE_DATATIER.TO_3VIEW,
type: NODE_DATATYPE.TO_3VIEW, type: NODE_DATATYPE.TO_3VIEW,
title: 'To 3-View', title: 'To 3-View',
component: To3View component: To3View,
api:threeToThreeViewsApi
} }
] ]
const nodeManager = inject('nodeManager') as any const nodeManager = inject('nodeManager') as any
@@ -133,7 +136,7 @@
...data, ...data,
} }
const taskList = await currentComponent.value.api(apiData).then((rv)=>{ const taskList = await currentComponent.value.api(apiData).then((rv)=>{
return rv return [rv]
}) || [] }) || []
// const taskList = [{taskId:'123'}] // const taskList = [{taskId:'123'}]
// if (!subordNode) { // if (!subordNode) {

View File

@@ -3,6 +3,8 @@
<div class="scene-composition"> <div class="scene-composition">
<p class="label">Prompt</p> <p class="label">Prompt</p>
<my-textarea v-model="data.prompt" /> <my-textarea v-model="data.prompt" />
<p class="label">Mode</p>
<my-select v-model="data.mode" :list="modeList" />
<p class="label">Choose Style</p> <p class="label">Choose Style</p>
<div class="style-list"> <div class="style-list">
<div <div
@@ -47,8 +49,13 @@
]) ])
const data = reactive({ const data = reactive({
prompt: '', prompt: '',
styles: ['Colorful', 'Modernist'] styles: ['Colorful', 'Modernist'],
mode: 'Advanced',
}) })
const modeList = ref([
{ value: 'Advanced', label: 'Advanced' },
{ value: 'Normal', label: 'Normal' }
])
const onClickStyle = (value: string) => { const onClickStyle = (value: string) => {
if (data.styles.includes(value)) { if (data.styles.includes(value)) {
data.styles = data.styles.filter((v) => v !== value) data.styles = data.styles.filter((v) => v !== value)
@@ -60,6 +67,7 @@
return { return {
styles: data.styles, styles: data.styles,
userPrompt: data.prompt, userPrompt: data.prompt,
mode: data.mode,
} }
} }
defineExpose({ data, getApiData }) defineExpose({ data, getApiData })

View File

@@ -3,21 +3,35 @@
<div class="surface-edit"> <div class="surface-edit">
<p class="label">Image</p> <p class="label">Image</p>
<upload-file v-model="data.file" /> <upload-file v-model="data.file" />
<p class="label">Mode</p>
<my-select v-model="data.mode" :list="modeList" />
<p class="label">Prompt</p> <p class="label">Prompt</p>
<my-textarea v-model="data.prompt" /> <my-textarea v-model="data.prompt" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, onMounted } from 'vue' import { reactive, ref, onMounted } from 'vue'
import myTextarea from '../../tools/my-textarea.vue' import myTextarea from '../../tools/my-textarea.vue'
import uploadFile from '../../tools/upload-file.vue' import uploadFile from '../../tools/upload-file.vue'
import mySelect from '../../tools/my-select.vue'
const data = reactive({ const data = reactive({
prompt: '', prompt: '',
file: null file: null,
mode: 'Advanced',
}) })
const modeList = ref([
defineExpose({ data }) { value: 'Advanced', label: 'Advanced' },
{ value: 'Normal', label: 'Normal' }
])
const getApiData = ()=>{
return {
mode: data.mode,
surFaceUrl: data.file,
userPrompt: data.prompt,
}
}
defineExpose({ data,getApiData })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -3,30 +3,38 @@
<div class="to-3d-model"> <div class="to-3d-model">
<p class="label">Image</p> <p class="label">Image</p>
<div class="image"> <div class="image">
<img src="https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__" alt=""> <img :src="data.url" alt="">
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, onMounted } from 'vue' import { reactive, onMounted, useAttrs } from 'vue'
import uploadFile from '../../tools/upload-file.vue' import uploadFile from '../../tools/upload-file.vue'
const data = reactive({ const attrs = useAttrs()
file: null
})
defineExpose({ data }) const data = reactive({
url: attrs.node?.data?.originalImage,
})
const getApiData = ()=>{
return {
imageUrls: [data.url],
}
}
defineExpose({ data,getApiData })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.to-3d-model { .to-3d-model {
> .image { > .image {
padding: 18px; padding: 6px;
border-radius: 10px; border-radius: 10px;
background-color: #f0f0f0; background-color: #f0f0f0;
>img{ >img{
width: 100%; width: 100%;
height: auto; height: auto;
max-height: 87px;
object-fit: contain;
} }
} }
} }

View File

@@ -48,13 +48,13 @@
} }
]) ])
const modeList = ref([ const modeList = ref([
{ value: 'Fast', label: 'Fast' }, { value: 'Advanced', label: 'Advanced' },
{ value: 'Normal', label: 'Normal' } { value: 'Normal', label: 'Normal' }
]) ])
const data = reactive({ const data = reactive({
prompt: '', prompt: '',
pixelRatio: '1:1', pixelRatio: '1:1',
mode: 'Normal', mode: 'Advanced',
}) })
const getApiData = ()=>{ const getApiData = ()=>{

View File

@@ -16,6 +16,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, computed } from 'vue' import { reactive, computed } from 'vue'
import { uploadImage } from '@/api/upload'
const emit = defineEmits(['update:modelValue', 'change']) const emit = defineEmits(['update:modelValue', 'change'])
const props = defineProps({ const props = defineProps({
modelValue: { type: [File, Object, String, null] }, modelValue: { type: [File, Object, String, null] },
@@ -44,7 +45,12 @@
input.accept = 'image/png, image/jpeg, image/jpg' input.accept = 'image/png, image/jpeg, image/jpg'
input.addEventListener('change', (e) => { input.addEventListener('change', (e) => {
const file = e.target.files[0] const file = e.target.files[0]
if (file) onChange(file) const formData = new FormData()
formData.append('file', file)
uploadImage(formData).then((res) => {
if (res) onChange(res)
})
}) })
input.click() input.click()
} }

View File

@@ -16,7 +16,7 @@
const open = async (options) => { const open = async (options) => {
let json = [] let json = []
await new Promise((resolve) => { await new Promise((resolve) => {
getSketchFlowCanvas({ id: options.imgId }).then((res:any) => { getSketchFlowCanvas({ id: options.imgId },true).then((res:any) => {
if (res) { if (res) {
json = JSON.parse(res) json = JSON.parse(res)
} }
@@ -34,7 +34,7 @@
await new Promise((resolve) => { await new Promise((resolve) => {
putSketchFlowCanvas({ putSketchFlowCanvas({
id: config.value.imgId, id: config.value.imgId,
canvasData: str }).then(() => { canvasData: str },true).then(() => {
resolve(true) resolve(true)
}).catch(() => { }).catch(() => {
resolve(true) resolve(true)

View File

@@ -13,7 +13,7 @@
popper-style="width:auto; min-width: 22rem; padding: 0.8rem; border-radius: 1.4rem;" popper-style="width:auto; min-width: 22rem; padding: 0.8rem; border-radius: 1.4rem;"
> >
<template #reference> <template #reference>
<img class="pic" src="@/assets/images/pic.jpg" /> <img class="pic" :src="userInfoStore.state.userInfo?.avatar || '@/assets/images/pic.jpg'" />
</template> </template>
<div class="menu-box"> <div class="menu-box">
<div> <div>
@@ -97,6 +97,7 @@
width: 4.65rem; width: 4.65rem;
height: 4.65rem; height: 4.65rem;
border-radius: 50%; border-radius: 50%;
object-fit: cover;
} }
} }
.menu-box { .menu-box {

View File

@@ -81,6 +81,7 @@ export default {
userAgreement: 'User Agreement', userAgreement: 'User Agreement',
privacyPolicy: 'Privacy Policy', privacyPolicy: 'Privacy Policy',
view: 'View', view: 'View',
remainingNum: 'Remaining number of times to upload profile picture:',
}, },
Country:{ Country:{
unitedStates: 'United States', unitedStates: 'United States',

View File

@@ -81,7 +81,8 @@ export default {
timesPerHour: '{time} 次/小时', timesPerHour: '{time} 次/小时',
userAgreement: '用户协议', userAgreement: '用户协议',
privacyPolicy: '隐私政策', privacyPolicy: '隐私政策',
view: '查看' view: '查看',
remainingNum: '剩余上传头像次数:',
}, },
Country: { Country: {
unitedStates: '美国', unitedStates: '美国',

View File

@@ -21,6 +21,10 @@ export const useUserInfoStore = defineStore('userInfo', () => {
} }
} }
const updateUserInfo = (data: any) => {
state.value.userInfo = { ...state.value.userInfo, ...data }
}
// actions // actions
const setUserInfo = (data: any) => { const setUserInfo = (data: any) => {
state.value.userInfo = data state.value.userInfo = data
@@ -49,6 +53,7 @@ export const useUserInfoStore = defineStore('userInfo', () => {
getUserInfo, getUserInfo,
setToken, setToken,
setUserInfo, setUserInfo,
logOut logOut,
updateUserInfo
} }
}) })

View File

@@ -1,12 +1,19 @@
<template> <template>
<div> <div>
<div class="label">{{ $t('Home.userProfile') }}</div> <div class="label">{{ $t('Home.userProfile') }}</div>
<div class="profile"> <el-tooltip
<img :src="userInfo?.profile" /> class="box-item"
<span class="edit" @click="onProfileEdit"> effect="dark"
<svg-icon name="profile-edit" size="11" /> :content="`${$t('Home.remainingNum')} ${remainingNum}`"
</span> placement="top"
</div> >
<div class="profile">
<img :src="userInfo?.avatar" />
<span class="edit" @click="onProfileEdit">
<svg-icon name="profile-edit" size="11" />
</span>
</div>
</el-tooltip>
</div> </div>
<div> <div>
<div class="label">{{ $t('Home.userName') }}</div> <div class="label">{{ $t('Home.userName') }}</div>
@@ -27,11 +34,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, nextTick, inject } from 'vue' import { computed, ref, nextTick, inject, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import dropdownMenu from '@/components/dropdown-menu.vue' import dropdownMenu from '@/components/dropdown-menu.vue'
import { useUserInfoStore } from '@/stores' import { useUserInfoStore } from '@/stores'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { uploadImage } from '@/api/upload'
import { UpdateUserAvatar, getAvatarLimit } from '@/api/user'
const router = useRouter() const router = useRouter()
const { locale } = useI18n() const { locale } = useI18n()
const userInfoStore = useUserInfoStore() const userInfoStore = useUserInfoStore()
@@ -40,6 +49,7 @@
{ label: 'English', value: 'ENGLISH' }, { label: 'English', value: 'ENGLISH' },
{ label: '中文', value: 'CHINESE_SIMPLIFIED' } { label: '中文', value: 'CHINESE_SIMPLIFIED' }
]) ])
const remainingNum = ref(0)
const changeLang = (value: string) => { const changeLang = (value: string) => {
locale.value = value locale.value = value
localStorage.setItem('language', value) localStorage.setItem('language', value)
@@ -50,10 +60,32 @@
const logout = () => { const logout = () => {
userInfoStore.logOut() userInfoStore.logOut()
} }
const getAvatarLimitNum = ()=>{
getAvatarLimit().then((res:any) => {
if (res) {
remainingNum.value = res
}
})
}
const onProfileEdit = () => { const onProfileEdit = () => {
// MyEvent.emit('openProfileEditDialog') // MyEvent.emit('openProfileEditDialog')
console.log('openProfileEditDialog') const input = document.createElement('input')
input.type = 'file'
input.accept = 'image/png, image/jpeg, image/jpg'
input.addEventListener('change', (e) => {
const file = e.target.files[0]
const formData = new FormData()
formData.append('avatar', file)
UpdateUserAvatar(formData).then((res) => {
userInfoStore.updateUserInfo({avatar: res})
getAvatarLimitNum()
})
})
input.click()
} }
onMounted(()=>{
getAvatarLimitNum()
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@@ -75,6 +107,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 50%; border-radius: 50%;
object-fit: cover;
} }
> .edit { > .edit {
position: absolute; position: absolute;