typescriptvuenocodevue3drag-and-dropvitelowcodetsxlow-codevuex4vue-router4designdraggableelement-pluselement-uimonaco-editortsvant-uidemo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
6.9 KiB
264 lines
6.9 KiB
/**
|
|
* @name: useVisualData
|
|
* @author: 卜启缘
|
|
* @date: 2021/5/6 11:59
|
|
* @description:useVisualData
|
|
* @update: 2021/5/6 11:59
|
|
*/
|
|
import { reactive, inject, readonly, computed, watch, InjectionKey } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import type {
|
|
VisualEditorModelValue,
|
|
VisualEditorBlockData,
|
|
VisualEditorPage,
|
|
FetchApiItem,
|
|
VisualEditorModel
|
|
} from '@/visual-editor/visual-editor.utils'
|
|
|
|
import { visualConfig } from '@/visual.config'
|
|
import { CacheEnum } from '@/enums'
|
|
|
|
// 保存到本地JSON数据的key
|
|
export const localKey = CacheEnum.PAGE_DATA_KEY
|
|
|
|
// 注入jsonData的key
|
|
export const injectKey: InjectionKey<string> = Symbol()
|
|
|
|
interface IState {
|
|
currentBlock: VisualEditorBlockData // 当前正在操作的组件
|
|
currentPage: VisualEditorPage // 当前正在操作的页面
|
|
jsonData: VisualEditorModelValue // 整棵JSON树
|
|
}
|
|
|
|
/**
|
|
* @description 创建空的新页面
|
|
*/
|
|
export const createNewPage = ({ title = '新页面', path = '/' }) => ({
|
|
title,
|
|
path,
|
|
config: {
|
|
bgColor: '',
|
|
bgImage: ''
|
|
},
|
|
blocks: []
|
|
})
|
|
|
|
const defaultValue: VisualEditorModelValue = {
|
|
pages: {
|
|
// 页面
|
|
'/': createNewPage({ title: '首页' })
|
|
},
|
|
models: [], // 模型实体集合
|
|
actions: {
|
|
// 动作集合
|
|
fetch: {
|
|
name: '接口请求',
|
|
apis: []
|
|
},
|
|
dialog: {
|
|
name: '对话框',
|
|
handlers: []
|
|
}
|
|
}
|
|
}
|
|
|
|
export const initVisualData = () => {
|
|
const localData = JSON.parse(sessionStorage.getItem(localKey) as string)
|
|
const jsonData: VisualEditorModelValue = Object.keys(localData?.pages || {}).length
|
|
? localData
|
|
: defaultValue
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
console.log('jsonData:', jsonData)
|
|
// 所有页面的path都必须以 / 开发
|
|
const getPrefixPath = (path: string) => (path.startsWith('/') ? path : `/${path}`)
|
|
|
|
const currentPage = jsonData.pages[route.path]
|
|
|
|
const state: IState = reactive({
|
|
jsonData,
|
|
currentPage,
|
|
currentBlock: currentPage?.blocks?.find((item) => item.focus) ?? ({} as VisualEditorBlockData)
|
|
})
|
|
const paths = Object.keys(jsonData.pages)
|
|
|
|
const isExistPath = paths.some((path) => route.path == path)
|
|
// 当前页面是否存在
|
|
if (!isExistPath) {
|
|
router.replace(paths[0] || '/')
|
|
state.currentPage = jsonData.pages[paths[0]] ?? defaultValue.pages['/']
|
|
}
|
|
|
|
// 路由变化时更新当前操作的页面
|
|
watch(
|
|
() => route.path,
|
|
(url) => setCurrentPage(url)
|
|
)
|
|
|
|
// 更新page
|
|
const updatePage = ({ newPath, oldPath, page }) => {
|
|
console.log(state.jsonData.pages[oldPath], page)
|
|
if (newPath && newPath != oldPath) {
|
|
page.path = newPath
|
|
// 如果传了新的路径,则认为是修改页面路由
|
|
state.jsonData.pages[getPrefixPath(newPath)] = { ...state.jsonData.pages[oldPath], ...page }
|
|
deletePage(oldPath, getPrefixPath(newPath))
|
|
} else {
|
|
Object.assign(state.jsonData.pages[oldPath], page)
|
|
}
|
|
}
|
|
// 添加page
|
|
const incrementPage = (path = '', page: VisualEditorPage) => {
|
|
state.jsonData.pages[getPrefixPath(path)] ??= page ?? createNewPage({ path })
|
|
}
|
|
// 删除page
|
|
const deletePage = (path = '', redirectPath = '') => {
|
|
delete state.jsonData.pages[path]
|
|
if (redirectPath) {
|
|
setCurrentPage(redirectPath)
|
|
}
|
|
}
|
|
// 设置当前页面
|
|
const setCurrentPage = (path = '/') => {
|
|
state.currentPage = jsonData.pages[path]
|
|
if (!state.currentPage) {
|
|
state.currentPage = jsonData.pages['/']
|
|
router.replace('/')
|
|
}
|
|
const currentFocusBlock = state.currentPage.blocks.find((item) => item.focus)
|
|
setCurrentBlock(currentFocusBlock ?? ({} as VisualEditorBlockData))
|
|
}
|
|
|
|
// 设置当前被操作的组件
|
|
const setCurrentBlock = (block: VisualEditorBlockData) => {
|
|
state.currentBlock = block
|
|
}
|
|
|
|
// 更新pages下面的blocks
|
|
const updatePageBlock = (path = '', blocks: VisualEditorBlockData[] = []) => {
|
|
state.jsonData.pages[path].blocks = blocks
|
|
}
|
|
|
|
/**
|
|
* @description 新建API接口请求
|
|
*/
|
|
const incrementFetchApi = (api: FetchApiItem) => {
|
|
state.jsonData.actions.fetch.apis.push(api)
|
|
}
|
|
|
|
/**
|
|
* @description 删除某个API接口
|
|
*/
|
|
const deleteFetchApi = (key: string) => {
|
|
const index = state.jsonData.actions.fetch.apis.findIndex((item) => item.key == key)
|
|
if (index !== -1) {
|
|
state.jsonData.actions.fetch.apis.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description 更新某个接口或者批量更新接口
|
|
* @param {FetchApiItem | FetchApiItem[]} api 接口
|
|
* @param {boolean} isCover 是否覆盖全部接口
|
|
*/
|
|
const updateFetchApi = (api: FetchApiItem | FetchApiItem[], isCover = false) => {
|
|
const fetch = state.jsonData.actions.fetch
|
|
const apis = Array.isArray(api) ? api : [api]
|
|
if (isCover) {
|
|
fetch.apis = apis
|
|
} else {
|
|
apis.forEach((apiItem) => {
|
|
const target = fetch.apis.find((item) => item.key == apiItem.key)
|
|
Object.assign(target, api)
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description 新增模型
|
|
*/
|
|
const incrementModel = (model: VisualEditorModel) => {
|
|
state.jsonData.models.push(model)
|
|
}
|
|
|
|
/**
|
|
* @description 删除某个模型
|
|
*/
|
|
const deleteModel = (key: string) => {
|
|
const index = state.jsonData.models.findIndex((item) => item.key == key)
|
|
if (index !== -1) {
|
|
state.jsonData.models.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param { VisualEditorModel | VisualEditorModel[]} model 模型项或模型数组
|
|
* @param {boolean} isCover 是否覆盖所有模型
|
|
* @description 更新某个模型
|
|
*/
|
|
const updateModel = (model: VisualEditorModel | VisualEditorModel[], isCover = false) => {
|
|
const jsonData = state.jsonData
|
|
const models = Array.isArray(model) ? model : [model]
|
|
if (isCover) {
|
|
jsonData.models = models
|
|
} else {
|
|
models.forEach((modelItem) => {
|
|
const index = jsonData.models.findIndex((item) => item.key == modelItem.key)
|
|
if (index !== -1) {
|
|
state.jsonData.models.splice(index, 1, modelItem)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 使用自定义JSON覆盖整个项目
|
|
const overrideProject = (jsonData) => {
|
|
state.jsonData = typeof jsonData === 'string' ? JSON.parse(jsonData) : jsonData
|
|
}
|
|
|
|
return {
|
|
visualConfig: visualConfig,
|
|
jsonData: readonly(state.jsonData), // 保护JSONData避免直接修改
|
|
currentPage: computed(() => state.currentPage),
|
|
currentBlock: computed(() => state.currentBlock),
|
|
overrideProject,
|
|
incrementFetchApi,
|
|
deleteFetchApi,
|
|
updateFetchApi,
|
|
incrementModel,
|
|
deleteModel,
|
|
updateModel,
|
|
setCurrentPage,
|
|
setCurrentBlock,
|
|
updatePage,
|
|
incrementPage,
|
|
deletePage,
|
|
updatePageBlock
|
|
}
|
|
}
|
|
|
|
export const useVisualData = () => inject<ReturnType<typeof initVisualData>>(injectKey)!
|
|
|
|
/**
|
|
* 实体的字段数据类型
|
|
*/
|
|
export const fieldTypes = [
|
|
{
|
|
label: '字符串',
|
|
value: 'string'
|
|
},
|
|
{
|
|
label: '数字',
|
|
value: 'number'
|
|
},
|
|
{
|
|
label: '数组',
|
|
value: 'array'
|
|
},
|
|
{
|
|
label: '布尔值',
|
|
value: 'boolean'
|
|
}
|
|
]
|
|
|