This commit is contained in:
lzp
2026-03-19 11:00:32 +08:00
9 changed files with 94 additions and 63 deletions

View File

@@ -11,10 +11,14 @@
<svg-icon :name="v.icon" :size="v.iconSize" />
</span>
</template>
<button class="export" @click="emit('export')">
<span class="icon"><svg-icon name="export" size="11" /></span>
<span class="text">Export</span>
</button>
<div>
<button class="export" @click="emit('export')">
<span class="icon"><svg-icon name="export" size="11" /></span>
<span class="text">Export</span>
</button>
<div v-loading="true" class="mask" v-if="downloadData.status == 'loading'"></div>
</div>
<!-- <button class="import" @click="emit('import')">
<span class="text">Import</span>
</button> -->
@@ -26,7 +30,8 @@
import { TOOLS } from '../manager/ToolManager'
const props = defineProps({
zoom: { default: 1, type: Number },
step: { default: 0.1, type: Number }
step: { default: 0.1, type: Number },
downloadData: { default: {}, type: Object }
})
const emit = defineEmits(['export', 'import'])
const stateManager = inject('stateManager') as any
@@ -109,8 +114,21 @@
cursor: not-allowed;
}
}
> button {
width: 10rem;
> div{
position: relative;
> .mask{
position: absolute !important;
top: 0;
left: 0;
height: 100%;
width: 100%;
zoom: .6;
pointer-events: none;
}
}
button {
// width: 10rem;
padding: 0 2.4rem;
height: 3rem;
border-radius: 0.4rem;
border: none;
@@ -118,12 +136,15 @@
color: #fff;
font-size: 1.1rem;
display: flex;
gap: 0.8rem;
align-items: center;
justify-content: center;
gap: 0.8rem;
position: relative;
cursor: pointer;
&:active {
opacity: 0.8;
}
}
}
</style>

View File

@@ -155,12 +155,6 @@
}
})
})
stateManager.recordState()
// } else {
// subordNode.data.data.url =
// 'https://s3-alpha-sig.figma.com/img/8ce2/f1a4/12b93da90e5f17109e7430f14837fd14?Expires=1773619200&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=kmLsTFtXJqfvuxj6husWlDkRDMOIRDjzUUjb7zh79GkBKihUHc0f59k5OAImHTPdaiEREUCCpn~8sQ-si5lenuauJpApCmAU~NsxjfQhuh9m5O~GiHenr2fKu0DIJ75-oCE3859fyxoSFXQgZ9PRmeb98kikMR6uRX9nI5TPUHgKO8ZgkhDBTW~iyaDT~1ybnoK7elPa6T2VzfO-bpIyY-MZ71VRq3RxwmZRxduqHEb3Dh-jjrHyh2SoQsHmUjSJOf-uYilfvpGUResZAjAq8ZVLEjvhzKC2bmCNZIp3RmhYO8ctU7pd5t91J6Xaa6jBLtGfMxbqIm652EC79K0RoA__'
// setTimeout(() => stateManager.recordState())
// }
}
//删除功能卡片
const onDeleteClick = ()=>{

View File

@@ -28,23 +28,23 @@
const shortcutList = ref([
{
label: 'Change the...',
value: 'Change the...'
value: 'Change the style to a realistic design. '
},
{
label: 'Bright Colors...',
value: 'Bright Colors...'
value: 'Bright colors with modern patterns, change the style to a realistic furniture design. '
},
{
label: 'Make the...',
value: 'Make the...'
value: 'Make the structure more refined and balanced, change the style to a realistic furniture style. '
},
{
label: 'Imagine...',
value: 'Imagine...'
value: 'Imagine this furniture with detailed fabric textures, change the style to a realistic design. '
},
{
label: 'Wood Materials with...',
value: 'Wood Materials with...'
value: 'Wood materials with natural oak texture and soft fabric, change the style to a realistic furniture design.'
}
])
const modeList = ref([

View File

@@ -161,6 +161,7 @@
item.scale.x = -item.scale.x
})
stateManager.recordState()
stateManager.exportFlow(stateManager.saveCanvasTimeInterval)
}
},
{
@@ -174,6 +175,7 @@
item.scale.y = -item.scale.y
})
stateManager.recordState()
stateManager.exportFlow(stateManager.saveCanvasTimeInterval)
}
}
])

View File

@@ -47,7 +47,7 @@
</template>
</VueFlow>
</div>
<header-tools @export="exportFlow" @import="importFlow" />
<header-tools @export="exportFlow" @import="importFlow" :downloadData="downloadData" />
<zoom
:zoom="stateManager.zoom.value"
:step="0.1"
@@ -107,9 +107,8 @@
const vueFlow = ref<any>()
const nodeTypes = ref([NODE_TYPE.INPUT, NODE_TYPE.SECONDARY, NODE_TYPE.OUTPUT, NODE_TYPE.ALONE])
// 状态管理器
const stateManager = new StateManager({ vueFlow })
const stateManager = new StateManager({ vueFlow,sketchId:props.config.imgId })
provide('stateManager', stateManager)
// 事件管理器
@@ -191,29 +190,34 @@
}
// 导出流程
const getFlowJson = () => {
if(!stateManager.isSave.value)return ''
return JSON.stringify(stateManager.nodes.value)
}
const exportFlow = () => {
const downloadData = ref<any>({
amount: 0,
progress: 0,
status: 'success',//success
})
const exportFlow = async () => {
// console.log(vueFlow.value)
// console.log(vueFlow.value.toImage)
let arr = stateManager.nodes.value.filter((v) => v.data.type === NODE_COMPONENT.RESULT_IMAGE)
let imgList = []
arr.forEach((v) => {
arr.forEach((v,i) => {
v.data.data.imageProcessTasks.forEach((item,index) => {
let url = item.url
let name = url?.split(".").pop().split("?").shift();
imgList.push({url:url,name:`${v.data.type}${index == 0?'':index}.${name}`})
imgList.push({url:url,name:`${v.data.type}${i}-${index == 0?'':index}.${name}`})
})
})
downImgListToZip(imgList)
console.log(imgList)
downloadData.value.amount = imgList.length
downloadData.value.status = 'loading'
await downImgListToZip(imgList,(progress)=>{
downloadData.value.progress = progress
if(progress == downloadData.value.amount){
downloadData.value.status = 'success'
}
})
return
// flowManager.exportFlow()
const str = getFlowJson()
stateManager.isSave.value = false
emit('exportFlow', str)
// localStorage.setItem('flow_json', str)
}
// 导入流程
const importFlow = async (json) => {

View File

@@ -1,6 +1,6 @@
<template>
<fullscreen-dialog v-model="dialogVisible" @close="close" hide-destroy>
<flow-canvas ref="flowCanvasRef" :config="config" @exportFlow="exportFlow" />
<flow-canvas ref="flowCanvasRef" :config="config" />
</fullscreen-dialog>
</template>
@@ -31,22 +31,7 @@
config.value.json = json
dialogVisible.value = true
}
const exportFlow = async (str) => {
if(!config.value.imgId || !str)return
await new Promise((resolve) => {
putSketchFlowCanvas({
id: config.value.imgId,
canvasData: str },true).then(() => {
resolve(true)
}).catch(() => {
resolve(true)
})
})
}
const close = async () => {
const str = flowCanvasRef.value?.getFlowJson()
await exportFlow(str)
dialogVisible.value = false
}
const handleBeforeUnload = (event) => {
@@ -67,7 +52,6 @@
defineExpose({
open,
close,
exportFlow
})
</script>
<style lang="less" scoped>

View File

@@ -38,6 +38,7 @@ export class EventManager {
}
})
this.stateManager.recordState()
this.stateManager.exportFlow(this.stateManager.saveCanvasTimeInterval)
}
/** 处理点击 */
handleClick(event: any) {

View File

@@ -2,6 +2,8 @@ import { ref, computed } from "vue";
import { NODE_TYPE, NODE_DATATYPE } from '../tools/index.d'
import { ElMessageBox } from 'element-plus'
import i18n from '@/lang'
import { putSketchFlowCanvas } from '@/api/flow-canvas'
const t = i18n.global.t
export interface NodesItem {
@@ -37,8 +39,12 @@ export class StateManager {
toolManager: any
generateManager: any
// 是否有数据没保存
isSave: any
// 保存画布数据定时器
saveCanvasTime: any
// 保存画布数据定时器时间间隔
saveCanvasTimeInterval: any
// 打开画布线稿id
sketchId: any
// 设置管理器
setManager(options) {
options.eventManager && (this.eventManager = options.eventManager)
@@ -57,7 +63,10 @@ export class StateManager {
this.mxHistory = ref(50)
this.historyList = ref([])
this.historyIndex = ref(0)
this.isSave = ref(false)
this.sketchId = options.sketchId
this.saveCanvasTimeInterval = 6000
this.saveCanvasTime = null
this.activeNodeID = ref("")
this.nodes = ref<NodesItem[]>([]);
@@ -100,8 +109,6 @@ export class StateManager {
})
return arr
})
window.nodes = this.nodes
window.aaa = this
}
/** 设置激活节点 */
@@ -110,6 +117,7 @@ export class StateManager {
addNode(node: NodesItem) {
this.nodes.value.push(node);
this.recordState()
this.exportFlow()
}
/** 删除节点 */
async deleteNode(id: string, { isElMessageBox } = { isElMessageBox: false }) {
@@ -136,6 +144,7 @@ export class StateManager {
if (!deletePromise) return console.log('删除操作被取消')
this.nodes.value = this.nodes.value.filter((node: NodesItem) => node.id !== id)
this.recordState()
this.exportFlow()
}
/** 获取节点 */
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
@@ -176,8 +185,22 @@ export class StateManager {
const size = this.historyList.value.length - this.mxHistory.value
if (size > 0) this.historyList.value.splice(0, size)
this.historyIndex.value = this.historyList.value.length - 1
this.isSave.value = true
}
/** 画布数据存储 */
async exportFlow (time:number = 0){
if(!this.sketchId)return
clearTimeout(this.saveCanvasTime)
await new Promise((resolve) => {
this.saveCanvasTime = setTimeout(()=>{
putSketchFlowCanvas({
id: this.sketchId,
canvasData: JSON.stringify(this.nodes.value) }).then(() => {
resolve(true)
}).catch(() => {
resolve(true)
})
},time)
})
}
/** 撤回状态 */
undoState() {
@@ -186,6 +209,7 @@ export class StateManager {
if (!state) return
this.historyIndex.value = index
this.nodes.value = JSON.parse(state.nodes)
this.exportFlow(this.saveCanvasTimeInterval)
}
/** 重做状态 */
redoState() {
@@ -194,6 +218,7 @@ export class StateManager {
if (!state) return
this.historyIndex.value = index
this.nodes.value = JSON.parse(state.nodes)
this.exportFlow(this.saveCanvasTimeInterval)
}
/** 显示指定子节点和父节点连接线,隐藏父节点和其他子节点链接线, */
@@ -211,7 +236,7 @@ export class StateManager {
}
dispose() {
this.isSave.value = false
clearTimeout(this.saveCanvasTime)
this.historyList.value = []
this.historyIndex.value = 0
}

View File

@@ -19,10 +19,11 @@ export const downloadImage = (url: string, name: string) => {
}
/** 批量下载图片 */
export const downImgListToZip = async (imagesParams) => {
export const downImgListToZip = async (imagesParams,callback) => {
const zip = new JSZip()
const promises = []
// 遍历下载每个图片
let progress = 0
imagesParams.forEach((img, index) => {
const promise = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
@@ -32,18 +33,17 @@ export const downImgListToZip = async (imagesParams) => {
if (xhr.status === 200) {
const fileName = img.name
zip.file(fileName, xhr.response)
progress++
callback(progress)
resolve('')
} else {
reject(new Error(`下载失败: ${img.url}`))
}
}
xhr.onerror = () => reject(new Error(`网络错误: ${img.url}`))
xhr.send()
})
promises.push(promise)
console.log(promises,zip)
})
// 等待所有图片下载完成
@@ -56,7 +56,7 @@ export const downImgListToZip = async (imagesParams) => {
link.href = URL.createObjectURL(content)
link.download = 'DesignFiles'
link.click()
URL.revokeObjectURL(link.href)
// URL.revokeObjectURL(link.href)
})
.catch((error) => console.error('下载失败:', error))
}