go-view/src/views/chart/hooks/useSync.hook.ts
2023-05-28 15:59:08 +08:00

362 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { onUnmounted } from 'vue';
import html2canvas from 'html2canvas'
import { getUUID, httpErrorHandle, fetchRouteParamsLocation, base64toFile, JSONStringify, JSONParse } from '@/utils'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { EditCanvasTypeEnum, ChartEditStoreEnum, ProjectInfoEnum, ChartEditStorage } from '@/store/modules/chartEditStore/chartEditStore.d'
import { useChartHistoryStore } from '@/store/modules/chartHistoryStore/chartHistoryStore'
import { StylesSetting } from '@/components/Pages/ChartItemSetting'
import { useSystemStore } from '@/store/modules/systemStore/systemStore'
import { useChartLayoutStore } from '@/store/modules/chartLayoutStore/chartLayoutStore'
import { ChartLayoutStoreEnum } from '@/store/modules/chartLayoutStore/chartLayoutStore.d'
import { fetchChartComponent, fetchConfigComponent, createComponent } from '@/packages/index'
import { saveInterval } from '@/settings/designSetting'
import throttle from 'lodash/throttle'
// 接口状态
import { ResultEnum } from '@/enums/httpEnum'
// 接口
import { saveProjectApi, fetchProjectApi, uploadFile, updateProjectApi } from '@/api/path'
// 画布枚举
import { SyncEnum } from '@/enums/editPageEnum'
import { CreateComponentType, CreateComponentGroupType, ConfigType } from '@/packages/index.d'
import { BaseEvent, EventLife } from '@/enums/eventEnum'
import { PublicGroupConfigClass } from '@/packages/public/publicConfig'
import merge from 'lodash/merge'
/**
* * 画布-版本升级对旧数据无法兼容的补丁
* @param object
*/
const canvasVersionUpdatePolyfill = (object: any) => {
return object
}
/**
* * 组件-版本升级对旧数据无法兼容的补丁
* @param newObject
* @param sources
*/
const componentVersionUpdatePolyfill = (newObject: any, sources: any) => {
try {
// 判断是否是组件
if (sources.id) {
// 处理事件补丁
const hasVnodeBeforeMount = 'vnodeBeforeMount' in sources.events
const hasVnodeMounted = 'vnodeMounted' in sources.events
if (hasVnodeBeforeMount) {
newObject.events.advancedEvents.vnodeBeforeMount = sources?.events.vnodeBeforeMount
}
if (hasVnodeMounted) {
newObject.events.advancedEvents.vnodeMounted = sources?.events.vnodeMounted
}
if (hasVnodeBeforeMount || hasVnodeMounted) {
sources.events = {
baseEvent: {
[BaseEvent.ON_CLICK]: undefined,
[BaseEvent.ON_DBL_CLICK]: undefined,
[BaseEvent.ON_MOUSE_ENTER]: undefined,
[BaseEvent.ON_MOUSE_LEAVE]: undefined
},
advancedEvents: {
[EventLife.VNODE_MOUNTED]: undefined,
[EventLife.VNODE_BEFORE_MOUNT]: undefined
},
interactEvents: []
}
}
return newObject
}
} catch (error) {
return newObject
}
}
/**
* * 合并处理
* @param newObject 新的模板数据
* @param sources 新拿到的数据
* @returns object
*/
const componentMerge = (newObject: any, sources: any, notComponent = false) => {
// 处理组件补丁
componentVersionUpdatePolyfill(newObject, sources)
// 非组件不处理
if (notComponent) return merge(newObject, sources)
// 组件排除 newObject
const option = sources.option
if (!option) return merge(newObject, sources)
// 为 undefined 的 sources 来源对象属性将被跳过详见 https://www.lodashjs.com/docs/lodash.merge
sources.option = undefined
if (option) {
return {
...merge(newObject, sources),
option: option
}
}
}
// 请求处理
export const useSync = () => {
const chartEditStore = useChartEditStore()
const chartHistoryStore = useChartHistoryStore()
const systemStore = useSystemStore()
const chartLayoutStore = useChartLayoutStore()
/**
* * 组件动态注册
* @param projectData 项目数据
* @param isReplace 是否替换数据
* @returns
*/
const updateComponent = async (projectData: ChartEditStorage, isReplace = false, changeId = false) => {
if (isReplace) {
// 清除列表
chartEditStore.componentList = []
// 清除历史记录
chartHistoryStore.clearBackStack()
chartHistoryStore.clearForwardStack()
}
// 画布补丁处理
projectData.editCanvasConfig = canvasVersionUpdatePolyfill(projectData.editCanvasConfig)
// 列表组件注册
projectData.componentList.forEach(async (e: CreateComponentType | CreateComponentGroupType) => {
const intComponent = (target: CreateComponentType) => {
if (!window['$vue'].component(target.chartConfig.chartKey)) {
window['$vue'].component(target.chartConfig.chartKey, fetchChartComponent(target.chartConfig))
window['$vue'].component(target.chartConfig.conKey, fetchConfigComponent(target.chartConfig))
}
}
if (e.isGroup) {
(e as CreateComponentGroupType).groupList.forEach(groupItem => {
intComponent(groupItem)
})
} else {
intComponent(e as CreateComponentType)
}
})
// 创建函数-重新创建是为了处理类种方法消失的问题
const create = async (
_componentInstance: CreateComponentType,
callBack?: (componentInstance: CreateComponentType) => void
) => {
// 补充 class 上的方法
let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig)
if (_componentInstance.chartConfig.redirectComponent) {
_componentInstance.chartConfig.dataset && (newComponent.option.dataset = _componentInstance.chartConfig.dataset)
newComponent.chartConfig.title = _componentInstance.chartConfig.title
newComponent.chartConfig.chartFrame = _componentInstance.chartConfig.chartFrame
}
if (callBack) {
if (changeId) {
callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() }))
} else {
callBack(componentMerge(newComponent, _componentInstance))
}
} else {
if (changeId) {
chartEditStore.addComponentList(
componentMerge(newComponent, { ..._componentInstance, id: getUUID() }),
false,
true
)
} else {
chartEditStore.addComponentList(componentMerge(newComponent, _componentInstance), false, true)
}
}
}
// 数据赋值
for (const key in projectData) {
// 组件
if (key === ChartEditStoreEnum.COMPONENT_LIST) {
let loadIndex = 0
const listLength = projectData[key].length
for (const comItem of projectData[key]) {
// 设置加载数量
let percentage = parseInt((parseFloat(`${++loadIndex / listLength}`) * 100).toString())
chartLayoutStore.setItemUnHandle(ChartLayoutStoreEnum.PERCENTAGE, percentage)
// 判断类型
if (comItem.isGroup) {
// 创建分组
let groupClass = new PublicGroupConfigClass()
if (changeId) {
groupClass = componentMerge(groupClass, { ...comItem, id: getUUID() })
} else {
groupClass = componentMerge(groupClass, comItem)
}
// 异步注册子应用
const targetList: CreateComponentType[] = []
for (const groupItem of (comItem as CreateComponentGroupType).groupList) {
await create(groupItem, e => {
targetList.push(e)
})
}
groupClass.groupList = targetList
// 分组插入到列表
chartEditStore.addComponentList(groupClass, false, true)
} else {
await create(comItem as CreateComponentType)
}
if (percentage === 100) {
// 清除历史记录
chartHistoryStore.clearBackStack()
chartHistoryStore.clearForwardStack()
}
}
} else {
// 非组件(顺便排除脏数据)
if (key !== 'editCanvasConfig' && key !== 'requestGlobalConfig') return
componentMerge(chartEditStore[key], projectData[key], true)
}
}
// 清除数量
chartLayoutStore.setItemUnHandle(ChartLayoutStoreEnum.PERCENTAGE, 0)
}
/**
* * 赋值全局数据
* @param projectData 项目数据
* @returns
*/
const updateStoreInfo = (projectData: {
id: string,
projectName: string,
indexImage: string,
remarks: string,
state: number
}) => {
const { id, projectName, remarks, indexImage, state } = projectData
// ID
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, id)
// 名称
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_NAME, projectName)
// 描述
chartEditStore.setProjectInfo(ProjectInfoEnum.REMARKS, remarks)
// 缩略图
chartEditStore.setProjectInfo(ProjectInfoEnum.THUMBNAIL, indexImage)
// 发布
chartEditStore.setProjectInfo(ProjectInfoEnum.RELEASE, state === 1)
}
// * 数据获取
const dataSyncFetch = async () => {
// FIX:重新执行dataSyncFetch需清空chartEditStore.componentList,否则会导致图层重复
// 切换语言等操作会导致重新执行 dataSyncFetch,此时pinia中并未清空chartEditStore.componentList导致图层重复
chartEditStore.componentList = []
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
try {
const res = await fetchProjectApi({ projectId: fetchRouteParamsLocation() })
if (res && res.code === ResultEnum.SUCCESS) {
if (res.data) {
updateStoreInfo(res.data)
// 更新全局数据
await updateComponent(JSONParse(res.data.content))
return
}else {
chartEditStore.setProjectInfo(ProjectInfoEnum.PROJECT_ID, fetchRouteParamsLocation())
}
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
} catch (error) {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
httpErrorHandle()
}
}
// * 数据保存
const dataSyncUpdate = throttle(async (updateImg = true) => {
if(!fetchRouteParamsLocation()) return
let projectId = chartEditStore.getProjectInfo[ProjectInfoEnum.PROJECT_ID];
if(projectId === null || projectId === ''){
window['$message'].error('数据初未始化成功,请刷新页面!')
return
}
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.START)
// 异常处理缩略图上传失败不影响JSON的保存
try {
if (updateImg) {
// 获取缩略图片
const range = document.querySelector('.go-edit-range') as HTMLElement
// 生成图片
const canvasImage: HTMLCanvasElement = await html2canvas(range, {
backgroundColor: null,
allowTaint: true,
useCORS: true
})
// 上传预览图
let uploadParams = new FormData()
uploadParams.append('object', base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`))
const uploadRes = await uploadFile(uploadParams)
// 保存预览图
if(uploadRes && uploadRes.code === ResultEnum.SUCCESS) {
if (uploadRes.data.fileurl) {
await updateProjectApi({
id: fetchRouteParamsLocation(),
indexImage: `${uploadRes.data.fileurl}`
})
} else {
await updateProjectApi({
id: fetchRouteParamsLocation(),
indexImage: `${systemStore.getFetchInfo.OSSUrl}${uploadRes.data.fileName}`
})
}
}
}
} catch (e) {
console.log(e)
}
// 保存数据
let params = new FormData()
params.append('projectId', projectId)
params.append('content', JSONStringify(chartEditStore.getStorageInfo || {}))
const res= await saveProjectApi(params)
if (res && res.code === ResultEnum.SUCCESS) {
// 成功状态
setTimeout(() => {
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.SUCCESS)
}, 1000)
return
}
// 失败状态
chartEditStore.setEditCanvas(EditCanvasTypeEnum.SAVE_STATUS, SyncEnum.FAILURE)
}, 3000)
// * 定时处理
const intervalDataSyncUpdate = () => {
// 定时获取数据
const syncTiming = setInterval(() => {
dataSyncUpdate()
}, saveInterval * 1000)
// 销毁
onUnmounted(() => {
clearInterval(syncTiming)
})
}
return {
updateComponent,
updateStoreInfo,
dataSyncFetch,
dataSyncUpdate,
intervalDataSyncUpdate
}
}