types: add types to useBlock, useDataSource, useTranslate, useReource, useBreadcrumb, Modal (#1262)

This commit is contained in:
Gene 2025-04-02 09:40:33 +08:00 committed by GitHub
parent f3ed92531c
commit f53ea7ae6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 362 additions and 133 deletions

View File

@ -592,7 +592,7 @@ const exportSchema = () => {
return JSON.stringify(pageState.pageSchema) return JSON.stringify(pageState.pageSchema)
} }
const getSchema = () => { const getSchema = (): RootNode | object => {
return pageState.pageSchema || {} return pageState.pageSchema || {}
} }

View File

@ -945,7 +945,7 @@ export const addScript = (src: string) => appendScript(src, getDocument())
* @param {*} messages * @param {*} messages
* @param {*} merge * @param {*} merge
*/ */
export const setLocales = (messages: any, merge: any) => { export const setLocales = (messages: any, merge?: boolean) => {
const i18n = getRenderer().getI18n() const i18n = getRenderer().getI18n()
Object.keys(messages).forEach((lang) => { Object.keys(messages).forEach((lang) => {

View File

@ -8,7 +8,7 @@ export interface Node {
export type RootNode = Omit<Node, 'id'> & { export type RootNode = Omit<Node, 'id'> & {
id?: string id?: string
css?: string css?: string
fileName: string fileName?: string
methods?: Record<string, any> methods?: Record<string, any>
state?: Record<string, any> state?: Record<string, any>
lifeCycles?: Record<string, any> lifeCycles?: Record<string, any>
@ -16,4 +16,5 @@ export type RootNode = Omit<Node, 'id'> & {
bridge?: any bridge?: any
inputs?: any[] inputs?: any[]
outputs?: any[] outputs?: any[]
schema?: any
} }

View File

@ -1,7 +1,18 @@
import { h, render } from 'vue' import { h, render } from 'vue'
import { Modal } from '@opentiny/vue' import { Modal } from '@opentiny/vue'
const confirm = ({ title, status, message, exec, cancel, showFooter = true }) => { export interface ModalOptions {
title: string
status?: string
message: string | ((...args: any[]) => any)
exec?: (...args: any[]) => any
cancel?: (...args: any[]) => any
showFooter?: boolean
}
export type ConfirmOptions = ModalOptions
const confirm = ({ title, status, message, exec, cancel, showFooter = true }: ConfirmOptions) => {
Modal.confirm({ Modal.confirm({
title, title,
status, status,
@ -13,7 +24,7 @@ const confirm = ({ title, status, message, exec, cancel, showFooter = true }) =>
</div> </div>
) )
} }
}).then((res) => { }).then((res: string) => {
if (res === 'confirm' && typeof exec === 'function') { if (res === 'confirm' && typeof exec === 'function') {
exec() exec()
} else if (typeof cancel === 'function') { } else if (typeof cancel === 'function') {
@ -22,7 +33,9 @@ const confirm = ({ title, status, message, exec, cancel, showFooter = true }) =>
}) })
} }
const message = ({ title, status, message, exec, width = '400' }) => { export type MessageOptions = Pick<ModalOptions, 'title' | 'status' | 'message' | 'exec'> & { width?: string }
const message = ({ title, status, message, exec, width = '400' }: MessageOptions) => {
Modal.alert({ Modal.alert({
title, title,
status, status,
@ -42,13 +55,13 @@ const message = ({ title, status, message, exec, width = '400' }) => {
}) })
} }
const topbox = (options) => { const topbox = (options: ModalOptions) => {
const props = { ...options, modelValue: true } const props = { ...options, modelValue: true }
let TopBox = h(Modal, props) let TopBox = h(Modal, props)
const modalEl = document.createElement('div') const modalEl = document.createElement('div')
const close = () => { const close = () => {
TopBox.el.remove() TopBox.el?.remove()
TopBox = null TopBox = null
} }
@ -60,6 +73,13 @@ const topbox = (options) => {
} }
} }
declare global {
interface Window {
topbox?: (options: ModalOptions) => { TopBox: any; close: () => void }
message?: (options: MessageOptions) => void
}
}
window.topbox = topbox window.topbox = topbox
window.message = message window.message = message

View File

@ -9,10 +9,11 @@ const durationMap = {
export interface NotifyOptions { export interface NotifyOptions {
[key: string]: any [key: string]: any
customClass: string title?: string
title: string message: string
type: keyof typeof durationMap type: keyof typeof durationMap
position: string customClass?: string
position?: string
} }
const useNotify = (config: NotifyOptions) => { const useNotify = (config: NotifyOptions) => {

View File

@ -59,7 +59,10 @@ const layoutState = reactive({
toolbars: { toolbars: {
visiblePopover: false visiblePopover: false
}, },
pageStatus: '' pageStatus: {
state: '',
data: {}
}
}) })
const getMoveDragBarState = () => { const getMoveDragBarState = () => {
return layoutState.isMoveDragBar return layoutState.isMoveDragBar

View File

@ -0,0 +1,107 @@
export interface Property {
label: {
zh_CN?: string
}
description: {
zh_CN?: string
}
collapse: {
number: number
text: {
zh_CN?: string
}
}
content?: BlockProperty[]
}
export interface BlockContent {
componentName: string
blockName?: string
fileName: string
css?: string
props: Record<string, any>
children: any[]
schema: {
properties?: Property[]
events?: Record<string, any>
}
state?: Record<string, any>
methods: Record<string, any>
dataSource?: Record<string, any>
i18n?: any
}
export interface BlockOccupier {
id: number
username: string
resetPasswordToken: string
}
export interface Block {
id?: string | number
name_cn?: string
label: string
path?: string
categories: string[]
public: number
is_published?: number
framework: string
content: BlockContent
occupier?: BlockOccupier | null
created_at?: string | Date
updated_at?: string | Date
histories?: any[]
assets?: any
}
export interface BlockGroup {
id: string
name: string
desc: string
app: {
id: string | number
name: string
}
blocks: { data: Block }[]
groupId: string
groupName: string
}
export interface BlockProperty {
linked?: { property: any; blockProperty: any } | null
property: any
defaultValue: any
widget?: any
}
export interface SchemaData {
langs: Record<string, any>
methods: Record<string, any>
state: Record<string, any>
classNameList: string[]
contentList: any[]
}
export type ParsePropToDataOptons = Pick<SchemaData, 'langs' | 'methods' | 'state'> & {
prop: {
type: string
key: string
value: any
}
}
export type ParseChildPropsOptions = Pick<SchemaData, 'langs' | 'methods' | 'state'> & {
child: {
props: Record<string, any>
[x: string]: any
}
}
export interface CreateBlockOptions {
name_cn: string
label: string
path?: string
categories: string[]
}
export type CreateEmptyBlockOptions = Pick<Block, 'name_cn' | 'label' | 'path' | 'categories'>

View File

@ -10,7 +10,7 @@
* *
*/ */
import { ref, reactive, readonly } from 'vue' import { ref, reactive, readonly, type DeepReadonly } from 'vue'
import { hyphenate } from '@vue/shared' import { hyphenate } from '@vue/shared'
import { extend, copyArray } from '@opentiny/vue-renderless/common/object' import { extend, copyArray } from '@opentiny/vue-renderless/common/object'
import { format } from '@opentiny/vue-renderless/common/date' import { format } from '@opentiny/vue-renderless/common/date'
@ -32,12 +32,24 @@ import {
META_SERVICE META_SERVICE
} from '@opentiny/tiny-engine-meta-register' } from '@opentiny/tiny-engine-meta-register'
import meta from '../../meta' import meta from '../../meta'
import type {
Block,
BlockContent,
BlockGroup,
BlockProperty,
CreateBlockOptions,
CreateEmptyBlockOptions,
ParseChildPropsOptions,
ParsePropToDataOptons,
Property,
SchemaData
} from './types'
const { SORT_TYPE, SCHEMA_DATA_TYPE, BLOCK_OPENNESS } = constants const { SORT_TYPE, SCHEMA_DATA_TYPE, BLOCK_OPENNESS } = constants
const NODE_TYPE_PAGE = 'Page' const NODE_TYPE_PAGE = 'Page'
const nameCn = 'name_cn' const nameCn = 'name_cn'
const DEFAULT_PROPERTIES = readonly([ const DEFAULT_PROPERTIES = readonly<Property[]>([
{ {
label: { label: {
zh_CN: '基础信息' zh_CN: '基础信息'
@ -55,7 +67,7 @@ const DEFAULT_PROPERTIES = readonly([
} }
]) ])
const DEFAULT_BLOCK = readonly({ const DEFAULT_BLOCK = readonly<DeepReadonly<BlockContent>>({
componentName: 'Block', componentName: 'Block',
fileName: '', fileName: '',
css: '', css: '',
@ -70,63 +82,63 @@ const DEFAULT_BLOCK = readonly({
dataSource: {} dataSource: {}
}) })
const blockState = reactive({ const blockState = reactive<{ list: Block[]; current: Block | null }>({
list: [], list: [],
current: null // 当前画布中正在渲染的区块数据 current: null // 当前画布中正在渲染的区块数据
}) })
// 区块分组信息 // 区块分组信息
const groupState = reactive({ const groupState = reactive<{ list: BlockGroup[]; selected: BlockGroup | object }>({
list: [], list: [],
selected: {} selected: {}
}) })
// 区块分类 // 区块分类
const categoryState = reactive({ const categoryState = reactive<{ list: BlockGroup[] }>({
list: [] list: []
}) })
const getBlockList = () => blockState.list const getBlockList = () => blockState.list
const setBlockList = (list) => { const setBlockList = (list: Block[]) => {
blockState.list = list blockState.list = list
} }
const addBlock = (block) => { const addBlock = (block: Block) => {
const blockList = getBlockList() const blockList = getBlockList()
blockList.unshift(block) blockList.unshift(block)
} }
const delBlock = (block) => { const delBlock = (block: Block) => {
remove(getBlockList(), block) remove(getBlockList(), block)
} }
// 获取当前画布中的区块信息 // 获取当前画布中的区块信息
const getCurrentBlock = () => blockState.current const getCurrentBlock = () => blockState.current
const setCurrentBlock = (block) => { const setCurrentBlock = (block: Block) => {
blockState.current = block blockState.current = block
} }
const getGroupList = () => groupState.list const getGroupList = () => groupState.list
const setGroupList = (list) => { const setGroupList = (list: BlockGroup[]) => {
groupState.list = list groupState.list = list
} }
const getCategoryList = () => categoryState.list const getCategoryList = () => categoryState.list
const setCategoryList = (list) => { const setCategoryList = (list: BlockGroup[]) => {
categoryState.list = list categoryState.list = list
} }
const getSelectedGroup = () => groupState.selected const getSelectedGroup = () => groupState.selected
const setSelectedGroup = (selected) => { const setSelectedGroup = (selected: BlockGroup) => {
groupState.selected = selected groupState.selected = selected
} }
const copyCss = (css, classNameList) => { const copyCss = (css: string, classNameList: string[]) => {
classNameList = Array.from(new Set(classNameList)).map((item) => '.' + item) classNameList = Array.from(new Set(classNameList)).map((item) => '.' + item)
const cssObject = getCssObjectFromStyleStr(css) const cssObject = getCssObjectFromStyleStr(css)
let styleStr = '' let styleStr = ''
@ -141,10 +153,10 @@ const copyCss = (css, classNameList) => {
return styleStr return styleStr
} }
const copySchema = (schema, contentList, methods) => { const copySchema = (schema: Partial<BlockContent['schema']>, contentList: string[], methods: Record<string, any>) => {
const content = schema?.properties?.[0]?.content || [] const content = schema?.properties?.[0]?.content || []
let emitList = [] let emitList: string[] = []
const emitListCopies = {} const emitListCopies: Record<string, any> = {}
Object.keys(methods).forEach((key) => { Object.keys(methods).forEach((key) => {
const item = JSON.stringify(methods[key].value).match(/emit..*?\)/g) const item = JSON.stringify(methods[key].value).match(/emit..*?\)/g)
@ -154,10 +166,16 @@ const copySchema = (schema, contentList, methods) => {
}) })
emitList.forEach((e) => { emitList.forEach((e) => {
let key = e.match(/'.*?'/g)[0].replace(/'/g, '') const matches = e.match(/'.*?'/g)
if (!matches || !matches.length) {
return
}
let key = matches[0].replace(/'/g, '')
key = `on${key[0].toLocaleUpperCase() + key.slice(1, key.length)}` key = `on${key[0].toLocaleUpperCase() + key.slice(1, key.length)}`
if (schema?.events[key]) { if (schema?.events?.[key]) {
emitListCopies[key] = schema?.events[key] emitListCopies[key] = schema?.events[key]
} }
}) })
@ -174,12 +192,12 @@ const copySchema = (schema, contentList, methods) => {
return schemaCopies return schemaCopies
} }
const copyMethods = (schema) => { const copyMethods = (schema: Record<string, any>) => {
const methodsListCopies = {} const methodsListCopies: Record<string, any> = {}
// 因为methods方法里面大部分是用户的业务代码无法复用,所以只需要拷贝一个空方法即可 // 因为methods方法里面大部分是用户的业务代码无法复用,所以只需要拷贝一个空方法即可
Object.entries(schema).forEach(([key, value]) => { Object.entries(schema).forEach(([key, value]) => {
const ast = parseExpression(value.value) const ast: any = parseExpression(value.value)
// 清空函数体 // 清空函数体
if (ast.body?.body) { if (ast.body?.body) {
@ -194,8 +212,8 @@ const copyMethods = (schema) => {
return methodsListCopies return methodsListCopies
} }
const copyState = (stateObj = {}, methodsObj = {}) => { const copyState = (stateObj: Record<string, any> = {}, methodsObj: Record<string, any> = {}) => {
const stateCopies = {} const stateCopies: Record<string, any> = {}
const stateKey = Object.keys(stateObj).map((e) => `state.${e} `) const stateKey = Object.keys(stateObj).map((e) => `state.${e} `)
stateKey.forEach((e) => { stateKey.forEach((e) => {
@ -210,7 +228,7 @@ const copyState = (stateObj = {}, methodsObj = {}) => {
return stateCopies return stateCopies
} }
const parsePropToData = (data, { prop, langs, state, methods }) => { const parsePropToData = (data: SchemaData, { prop, langs, state, methods }: ParsePropToDataOptons) => {
if (prop.type === SCHEMA_DATA_TYPE.I18n) { if (prop.type === SCHEMA_DATA_TYPE.I18n) {
data.langs[prop.key] = langs[prop.key] data.langs[prop.key] = langs[prop.key]
} else if (prop.type === SCHEMA_DATA_TYPE.JSExpression) { } else if (prop.type === SCHEMA_DATA_TYPE.JSExpression) {
@ -228,9 +246,9 @@ const parsePropToData = (data, { prop, langs, state, methods }) => {
} }
const filterDataFn = const filterDataFn =
(parseChildProps) => (parseChildProps: (...args: any[]) => any) =>
({ children = [], langs = {}, methods = {}, state = {} }) => { ({ children = [] as any[], langs = {}, methods = {}, state = {} }) => {
const data = { const data: SchemaData = {
langs: {}, langs: {},
methods: {}, methods: {},
state: {}, state: {},
@ -247,14 +265,14 @@ const filterDataFn =
return data return data
} }
const parseChildProps = (data, { child, langs, state, methods }) => { const parseChildProps = (data: SchemaData, { child, langs, state, methods }: ParseChildPropsOptions) => {
if (child.props) { if (child.props) {
Object.entries(child.props).forEach(([propKey, prop]) => { Object.entries(child.props).forEach(([propKey, prop]) => {
if (typeof prop === 'object') { if (typeof prop === 'object') {
parsePropToData(data, { prop, langs, state, methods }) parsePropToData(data, { prop, langs, state, methods })
} else { } else {
if (propKey === 'className' && prop) { if (propKey === 'className' && prop) {
data.classNameList.push(...prop.split(' ').filter((item) => item)) data.classNameList.push(...prop.split(' ').filter((item: string) => item))
} }
} }
}) })
@ -271,14 +289,14 @@ const parseChildProps = (data, { child, langs, state, methods }) => {
} }
} }
const getBlockPageSchema = (block) => { const getBlockPageSchema = (block: Block) => {
const content = block?.content || {} const content = block?.content || {}
content.componentName = content.componentName || content.blockName content.componentName = content.componentName || content.blockName || ''
return content return content
} }
const initBlock = async (block = {}, _langs = {}, isEdit) => { const initBlock = async (block: any = {}, _langs = {}, isEdit?: boolean) => {
const { resetBlockCanvasState, setSaved, getSchema } = useCanvas() const { resetBlockCanvasState, setSaved, getSchema } = useCanvas()
const { setBreadcrumbBlock } = useBreadcrumb() const { setBreadcrumbBlock } = useBreadcrumb()
@ -305,7 +323,7 @@ const initBlock = async (block = {}, _langs = {}, isEdit) => {
}) })
} }
const createBlock = ({ name_cn, label, path, categories }) => { const createBlock = ({ name_cn, label, path, categories }: CreateBlockOptions) => {
const { pageState } = useCanvas() const { pageState } = useCanvas()
const schema = extend(true, {}, pageState.currentSchema) const schema = extend(true, {}, pageState.currentSchema)
// 选中 body 节点创建区块时需传递子节点数据 // 选中 body 节点创建区块时需传递子节点数据
@ -320,20 +338,20 @@ const createBlock = ({ name_cn, label, path, categories }) => {
filterData({ filterData({
children, children,
langs: getLangs(), langs: getLangs(),
methods: pageState.pageSchema.methods, methods: pageState.pageSchema?.methods,
state: pageState.pageSchema.state state: pageState.pageSchema?.state
}) })
) )
const css = copyCss(pageState.pageSchema.css, classNameList) const css = copyCss(pageState.pageSchema?.css || '', classNameList)
const methodsCopies = copyMethods(methods) const methodsCopies = copyMethods(methods)
Object.assign(methods, methodsCopies) Object.assign(methods, methodsCopies)
const schemaCopies = copySchema(pageState.pageSchema.schema, contentList, methods) const schemaCopies = copySchema(pageState.pageSchema?.schema, contentList, methods)
const stateCopies = copyState(pageState.pageSchema.state, methods) const stateCopies = copyState(pageState.pageSchema?.state, methods)
Object.assign(state, stateCopies) Object.assign(state, stateCopies)
const block = { const block: Block = {
path, path,
[nameCn]: name_cn, [nameCn]: name_cn,
label, label,
@ -355,8 +373,8 @@ const createBlock = ({ name_cn, label, path, categories }) => {
initBlock(block, langs) initBlock(block, langs)
} }
const createEmptyBlock = ({ name_cn, label, path, categories }) => { const createEmptyBlock = ({ name_cn, label, path, categories }: CreateEmptyBlockOptions) => {
const block = { const block: Block = {
path, path,
[nameCn]: name_cn, [nameCn]: name_cn,
label, label,
@ -372,8 +390,8 @@ const createEmptyBlock = ({ name_cn, label, path, categories }) => {
initBlock(block) initBlock(block)
} }
const setComponentLinkedValue = ({ propertyName, value }) => { const setComponentLinkedValue = ({ propertyName, value }: { propertyName: string; value: any }) => {
const { schema } = useCanvas().canvasApi.value?.getCurrent() || {} const { schema } = useCanvas().canvasApi.value?.getCurrent?.() || {}
if (!propertyName || !schema) { if (!propertyName || !schema) {
return return
@ -383,28 +401,28 @@ const setComponentLinkedValue = ({ propertyName, value }) => {
schema.props[propertyName] = value schema.props[propertyName] = value
} }
const getBlockI18n = (block) => block?.content?.i18n || {} const getBlockI18n = (block: Block) => block?.content?.i18n || {}
const getBlockProperties = (block) => block?.content?.schema?.properties?.[0]?.content || [] const getBlockProperties = (block: Block) => block?.content?.schema?.properties?.[0]?.content || []
const addBlockProperty = (property, block) => { const addBlockProperty = (property: BlockProperty, block: Block) => {
if (!block) { if (!block) {
return return
} }
if (!block.content) { if (!block.content) {
block.content = {} block.content = {} as BlockContent
} }
if (!block.content.schema) { if (!block.content.schema) {
block.content.schema = {} block.content.schema = {} as BlockContent['schema']
} }
if (!block.content.schema.properties) { if (!block.content.schema.properties) {
block.content.schema.properties = copyArray(DEFAULT_PROPERTIES) block.content.schema.properties = copyArray(DEFAULT_PROPERTIES)
} }
block.content.schema.properties[0].content.push(property) block.content.schema.properties?.[0].content?.push(property)
if (property.linked) { if (property.linked) {
setComponentLinkedValue({ setComponentLinkedValue({
@ -417,7 +435,7 @@ const addBlockProperty = (property, block) => {
} }
} }
const editBlockProperty = (property, data) => { const editBlockProperty = (property: BlockProperty, data: any) => {
if (property.linked) { if (property.linked) {
const value = { const value = {
type: SCHEMA_DATA_TYPE.JSExpression, type: SCHEMA_DATA_TYPE.JSExpression,
@ -431,13 +449,13 @@ const editBlockProperty = (property, data) => {
} }
} }
const removePropertyLink = ({ componentProperty }) => { const removePropertyLink = ({ componentProperty }: { componentProperty: BlockProperty }) => {
const linked = componentProperty.linked const linked = componentProperty.linked
componentProperty.linked = null componentProperty.linked = null
const properties = getBlockProperties(getCurrentBlock()) const properties = getBlockProperties(getCurrentBlock()!)
properties.forEach((property) => { properties.forEach((property) => {
if (property.linked && property.property === linked.blockProperty) { if (property.linked && property.property === linked?.blockProperty) {
if (componentProperty.widget?.props?.modelValue) { if (componentProperty.widget?.props?.modelValue) {
componentProperty.widget.props.modelValue = property.defaultValue componentProperty.widget.props.modelValue = property.defaultValue
} }
@ -452,19 +470,19 @@ const removePropertyLink = ({ componentProperty }) => {
}) })
} }
const getBlockEvents = (block = {}) => block?.content?.schema?.events || {} const getBlockEvents = (block = {} as Block) => block?.content?.schema?.events || {}
const addBlockEvent = ({ name, event }, block) => { const addBlockEvent = ({ name, event }: { name: string; event: any }, block: Block) => {
if (!block) { if (!block) {
return return
} }
if (!block.content) { if (!block.content) {
block.content = {} block.content = {} as BlockContent
} }
if (!block.content.schema) { if (!block.content.schema) {
block.content.schema = {} block.content.schema = {} as BlockContent['schema']
} }
if (!block.content.schema.events) { if (!block.content.schema.events) {
@ -474,8 +492,8 @@ const addBlockEvent = ({ name, event }, block) => {
block.content.schema.events[name] = event block.content.schema.events[name] = event
} }
const removeEventLink = (linkedEventName) => { const removeEventLink = (linkedEventName: string) => {
const events = getBlockEvents(getCurrentBlock()) const events = getBlockEvents(getCurrentBlock()!)
Object.entries(events).forEach(([name, event]) => { Object.entries(events).forEach(([name, event]) => {
if (linkedEventName === name) { if (linkedEventName === name) {
@ -484,7 +502,7 @@ const removeEventLink = (linkedEventName) => {
}) })
} }
const appendEventEmit = ({ eventName, functionName } = {}) => { const appendEventEmit = ({ eventName, functionName }: { eventName?: string; functionName?: string } = {}) => {
if (!eventName || !functionName) { if (!eventName || !functionName) {
return return
} }
@ -495,8 +513,8 @@ const appendEventEmit = ({ eventName, functionName } = {}) => {
const method = getMethods()?.[functionName] const method = getMethods()?.[functionName]
if (method?.type === SCHEMA_DATA_TYPE.JSFunction) { if (method?.type === SCHEMA_DATA_TYPE.JSFunction) {
const ast = parseExpression(method.value) const ast: any = parseExpression(method.value)
const params = ast.params.map((param) => param.name) const params = ast.params.map((param: { name: string }) => param.name)
const emitContent = `this.emit('${hyphenate(eventName.replace(/^on/i, ''))}', ${params.join(',')})` const emitContent = `this.emit('${hyphenate(eventName.replace(/^on/i, ''))}', ${params.join(',')})`
// 如果方法里面已经有了相同的emit语句就不添加了 // 如果方法里面已经有了相同的emit语句就不添加了
@ -534,13 +552,13 @@ const selectedGroup = ref({ ...DEFAULT_GROUPS[0] })
const selectedBlock = ref('') const selectedBlock = ref('')
// 已选择的区块数组,用于在当前分组里添加区块 // 已选择的区块数组,用于在当前分组里添加区块
const selectedBlockArray = ref([]) const selectedBlockArray = ref<Block[]>([])
// 是否刷新区块列表,在当前分组里添加/删除区块后通知刷新区块列表 // 是否刷新区块列表,在当前分组里添加/删除区块后通知刷新区块列表
const isRefresh = ref(false) const isRefresh = ref(false)
// 切换分组时调用 // 切换分组时调用
const groupChange = (group) => { const groupChange = (group?: BlockGroup) => {
if (!group) return if (!group) return
// 需要改变selectedGroup的引用地址才能触发tiny-select组件的watch事件 // 需要改变selectedGroup的引用地址才能触发tiny-select组件的watch事件
@ -551,7 +569,7 @@ const groupChange = (group) => {
} }
// 添加设计器默认区块分组 // 添加设计器默认区块分组
const addDefaultGroup = (groups) => { const addDefaultGroup = (groups: BlockGroup[]) => {
const result = DEFAULT_GROUPS.map((group) => ({ const result = DEFAULT_GROUPS.map((group) => ({
label: group.groupName, label: group.groupName,
value: group value: group
@ -573,15 +591,23 @@ const addDefaultGroup = (groups) => {
} }
// 是否是设计器默认区块分组 // 是否是设计器默认区块分组
const isDefaultGroupId = (groupId) => groupId === DEFAULT_GROUP_ID const isDefaultGroupId = (groupId: string) => groupId === DEFAULT_GROUP_ID
const isAllGroupId = (groupId) => groupId === DEFAULT_GROUPS[0].groupId const isAllGroupId = (groupId: string) => groupId === DEFAULT_GROUPS[0].groupId
// 获取今天的开始时间 // 获取今天的开始时间
const getCurrentDate = () => new Date().setHours(0, 0, 0, 0) const getCurrentDate = () => new Date().setHours(0, 0, 0, 0)
interface DateInfo {
nowDayOfWeek: number
nowDay: number
nowMonth: number
nowYear: number
lastMonth: number
}
// 获取本周的开始时间 // 获取本周的开始时间
const getCurrentWeek = (date) => { const getCurrentWeek = (date: DateInfo) => {
const { nowDayOfWeek, nowDay, nowMonth, nowYear } = date const { nowDayOfWeek, nowDay, nowMonth, nowYear } = date
const weekStartDate = new Date(nowYear, nowMonth, nowDay - nowDayOfWeek + 1) const weekStartDate = new Date(nowYear, nowMonth, nowDay - nowDayOfWeek + 1)
@ -589,7 +615,7 @@ const getCurrentWeek = (date) => {
} }
// 获取本月的开始时间 // 获取本月的开始时间
const getCurrentMonth = (date) => { const getCurrentMonth = (date: DateInfo) => {
const { nowMonth, nowYear } = date const { nowMonth, nowYear } = date
const monthStartDate = new Date(nowYear, nowMonth, 1) const monthStartDate = new Date(nowYear, nowMonth, 1)
@ -597,7 +623,7 @@ const getCurrentMonth = (date) => {
} }
// 获取上月的开始时间 // 获取上月的开始时间
const getLastMonth = (date) => { const getLastMonth = (date: DateInfo) => {
const { nowYear, lastMonth } = date const { nowYear, lastMonth } = date
const lastMonthStartDate = new Date(nowYear, lastMonth, 1) const lastMonthStartDate = new Date(nowYear, lastMonth, 1)
@ -605,7 +631,7 @@ const getLastMonth = (date) => {
} }
// 判断时间戳属于今天/本周/本月/上月/更久以前 // 判断时间戳属于今天/本周/本月/上月/更久以前
const getDateFromNow = (timeStamp) => { const getDateFromNow = (timeStamp: number = 0) => {
// 当前日期 // 当前日期
const now = new Date() const now = new Date()
const nowDay = now.getDate() const nowDay = now.getDate()
@ -621,7 +647,7 @@ const getDateFromNow = (timeStamp) => {
lastMonthDate.setMonth(lastMonthDate.getMonth() - 1) lastMonthDate.setMonth(lastMonthDate.getMonth() - 1)
const lastMonth = lastMonthDate.getMonth() const lastMonth = lastMonthDate.getMonth()
const date = { nowDayOfWeek, nowDay, nowMonth, nowYear, lastMonth } const date: DateInfo = { nowDayOfWeek, nowDay, nowMonth, nowYear, lastMonth }
// 存在currentDateStart与currentWeekStart相同的情况故不可以用currentDateStart作key // 存在currentDateStart与currentWeekStart相同的情况故不可以用currentDateStart作key
const dateMap = new Map([ const dateMap = new Map([
@ -629,7 +655,7 @@ const getDateFromNow = (timeStamp) => {
['本周', () => getCurrentWeek(date)], ['本周', () => getCurrentWeek(date)],
['本月', () => getCurrentMonth(date)], ['本月', () => getCurrentMonth(date)],
['上月', () => getLastMonth(date)], ['上月', () => getLastMonth(date)],
['更久以前', () => ''] ['更久以前', () => 0]
]) ])
for (const [key, value] of dateMap) { for (const [key, value] of dateMap) {
@ -642,14 +668,14 @@ const getDateFromNow = (timeStamp) => {
} }
// 将历史记录分组 // 将历史记录分组
const splitBackupGroups = (data) => { const splitBackupGroups = (data: { updated_at: string | number; message: string; id: string }[]) => {
const backupList = {} const backupList: Record<string, any> = {}
if (!data || !data.length) return backupList if (!data || !data.length) return backupList
data.sort((backup1, backup2) => new Date(backup2.updated_at) - new Date(backup1.updated_at)) data.sort((backup1, backup2) => new Date(backup2.updated_at).getTime() - new Date(backup1.updated_at).getTime())
data.forEach((item) => { data.forEach((item) => {
const updateTime = item.updated_at && new Date(item.updated_at) const updateTime = item.updated_at ? new Date(item.updated_at) : null
const title = getDateFromNow(updateTime?.getTime()) || '' const title = getDateFromNow(updateTime?.getTime()) || ''
backupList[title] = backupList[title] || [] backupList[title] = backupList[title] || []
backupList[title].push({ backupList[title].push({
@ -663,24 +689,28 @@ const splitBackupGroups = (data) => {
} }
const sortTypeHandlerMap = { const sortTypeHandlerMap = {
[SORT_TYPE.timeAsc]: (blockList) => { [SORT_TYPE.timeAsc]: (blockList: Block[]) => {
blockList.sort((block1, block2) => new Date(block1.updated_at) - new Date(block2.updated_at)) blockList.sort(
(block1, block2) => new Date(block1.updated_at || '').getTime() - new Date(block2.updated_at || '').getTime()
)
}, },
[SORT_TYPE.timeDesc]: (blockList) => { [SORT_TYPE.timeDesc]: (blockList: Block[]) => {
blockList.sort((block1, block2) => new Date(block2.updated_at) - new Date(block1.updated_at)) blockList.sort(
(block1, block2) => new Date(block2.updated_at || '').getTime() - new Date(block1.updated_at || '').getTime()
)
}, },
[SORT_TYPE.alphabetDesc]: (blockList) => { [SORT_TYPE.alphabetDesc]: (blockList: Block[]) => {
// name_cn 包含中文,需要用 localeCompare // name_cn 包含中文,需要用 localeCompare
blockList.sort((block1, block2) => (block2.name_cn || block2.label).localeCompare(block1.name_cn || block1.label)) blockList.sort((block1, block2) => (block2.name_cn || block2.label).localeCompare(block1.name_cn || block1.label))
}, },
[SORT_TYPE.alphabetAsc]: (blockList) => { [SORT_TYPE.alphabetAsc]: (blockList: Block[]) => {
// name_cn 包含中文,需要用 localeCompare // name_cn 包含中文,需要用 localeCompare
blockList.sort((block1, block2) => (block1.name_cn || block1.label).localeCompare(block2.name_cn || block2.label)) blockList.sort((block1, block2) => (block1.name_cn || block1.label).localeCompare(block2.name_cn || block2.label))
} }
} }
// 排序 // 排序
const sort = (blockList, type) => { const sort = (blockList: Block[], type: string) => {
if (blockList.length === 0) return blockList if (blockList.length === 0) return blockList
if (sortTypeHandlerMap[type]) { if (sortTypeHandlerMap[type]) {
@ -694,7 +724,7 @@ const sort = (blockList, type) => {
} }
// 在可选区块列表里选择区块 // 在可选区块列表里选择区块
const check = (block) => { const check = (block: Block) => {
if (selectedBlockArray.value.some((item) => item.id === block.id)) { if (selectedBlockArray.value.some((item) => item.id === block.id)) {
return return
} }
@ -703,11 +733,11 @@ const check = (block) => {
} }
// 取消选择区块 // 取消选择区块
const cancelCheck = (block) => { const cancelCheck = (block: Block) => {
selectedBlockArray.value = selectedBlockArray.value.filter((item) => item.id !== block.id) selectedBlockArray.value = selectedBlockArray.value.filter((item) => item.id !== block.id)
} }
const checkAll = (blockList) => { const checkAll = (blockList: Block[]) => {
selectedBlockArray.value = blockList selectedBlockArray.value = blockList
} }
@ -715,11 +745,11 @@ const cancelCheckAll = () => {
selectedBlockArray.value = [] selectedBlockArray.value = []
} }
const getBlockAssetsByVersion = (block, version) => { const getBlockAssetsByVersion = (block: Block, version?: string) => {
let assets = block.assets let assets = block.assets
if (version) { if (version) {
const replaceUri = (uri) => uri.replace(/@\d{1,3}(\.\d{1,3}){0,2}\//, `@${version}/`) const replaceUri = (uri: string) => uri.replace(/@\d{1,3}(\.\d{1,3}){0,2}\//, `@${version}/`)
assets = { assets = {
...block.assets, ...block.assets,

View File

@ -45,7 +45,27 @@ const compareData = () => {
return { isRecordSame, isDataSourceSame, isRemoteDataSame } return { isRecordSame, isDataSourceSame, isRemoteDataSame }
} }
const handleConfirmSave = (dataSourceState, isRecordSame, resolve, isDataSourceSame, callback) => { interface DataSourceState {
dataSource: Record<string, any>
record: Record<string, any>
recordCopies: Record<string, any>
dataSourceColumn: Record<string, any>
dataSourceColumnCopies: Record<string, any>
remoteData: Record<string, any>
remoteDataCopies: Record<string, any>
currentRecordId: string
isRecordValidate: boolean
disCard: boolean
remoteConfig: Record<string, any>
}
const handleConfirmSave = (
dataSourceState: DataSourceState,
isRecordSame: boolean,
resolve: (value: unknown) => void,
isDataSourceSame: boolean,
callback: (...args: any[]) => any
) => {
let { let {
name, name,
data: { data, columns } data: { data, columns }
@ -63,7 +83,7 @@ const handleConfirmSave = (dataSourceState, isRecordSame, resolve, isDataSourceS
// 数据源数据修改,新增,数据源数据做修改 // 数据源数据修改,新增,数据源数据做修改
if (dataSourceState.currentRecordId) { if (dataSourceState.currentRecordId) {
data = data || [] data = data || []
const index = data.findIndex((item) => item.id === dataSourceState.currentRecordId) const index = data.findIndex((item: { id: string }) => item.id === dataSourceState.currentRecordId)
data[index] = Object.assign(data[index], dataSourceState.record) data[index] = Object.assign(data[index], dataSourceState.record)
} else { } else {
@ -85,7 +105,7 @@ const handleConfirmSave = (dataSourceState, isRecordSame, resolve, isDataSourceS
const requestData = { name, data: { columns, data, type } } const requestData = { name, data: { columns, data, type } }
callback(id, requestData).then((data) => { callback(id, requestData).then((data: any) => {
if (data) { if (data) {
dataSourceState.record = {} dataSourceState.record = {}
dataSourceState.recordCopies = {} dataSourceState.recordCopies = {}
@ -100,7 +120,7 @@ const handleConfirmSave = (dataSourceState, isRecordSame, resolve, isDataSourceS
return undefined return undefined
} }
const saveDataSource = (callback) => { const saveDataSource = (callback: (...args: any[]) => any) => {
const { isRecordSame, isDataSourceSame } = compareData() const { isRecordSame, isDataSourceSame } = compareData()
const { confirm } = useModal() const { confirm } = useModal()

View File

@ -19,12 +19,19 @@ import { PROP_DATA_TYPE } from '@opentiny/tiny-engine-common/js/constants'
import { useResource, useCanvas, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register' import { useResource, useCanvas, getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
const { HOST_TYPE } = constants const { HOST_TYPE } = constants
const state = reactive({ const state = reactive<{ langs: Record<string, any> }>({
langs: {} langs: {}
}) })
const currentLanguage = ref('zh_CN') const currentLanguage = ref('zh_CN')
const i18nResource = reactive({ messages: {}, locales: [] }) const i18nResource = reactive<{
messages: Record<string, any>
locales: any[]
[x: string]: any
}>({
messages: {},
locales: []
})
const i18nApi = '/app-center/api/i18n/entries' const i18nApi = '/app-center/api/i18n/entries'
const globalParams = { const globalParams = {
host: '', host: '',
@ -60,7 +67,7 @@ const removeI18n = (key = []) => {
* @returns * @returns
*/ */
const ensureI18n = (obj, send) => { const ensureI18n = (obj: { [x: string]: any; key: string }, send?: boolean) => {
const { locales } = i18nResource const { locales } = i18nResource
const contents = Object.fromEntries(locales.map(({ lang }) => [lang, obj[lang]])) const contents = Object.fromEntries(locales.map(({ lang }) => [lang, obj[lang]]))
const langs = getLangs() const langs = getLangs()
@ -93,16 +100,16 @@ const ensureI18n = (obj, send) => {
} }
try { try {
const messages = {} const messages: Record<string, any> = {}
Object.entries(contents).forEach(([locale, message]) => { Object.entries(contents).forEach(([locale, message]) => {
messages[locale] = { messages[locale] = {
[key]: message [key]: message
} }
}) })
useCanvas().canvasApi.value?.setLocales(messages, true) useCanvas().canvasApi.value?.setLocales?.(messages, true)
} catch (e) { } catch (e) {
throw new Error(e) throw new Error(String(e))
// 不需要处理有报错的词条会在画布初始化的时候统一调setLocales这个方法 // 不需要处理有报错的词条会在画布初始化的时候统一调setLocales这个方法
} }
@ -117,12 +124,19 @@ const getI18nData = () => {
}) })
} }
const getI18n = async ({ init, local }) => { export interface I18nOptions {
init?: boolean
local?: any
host?: string
hostType?: string
}
const getI18n = async ({ init, local }: I18nOptions): Promise<typeof appSchemaState.langs> => {
const { appSchemaState } = useResource() const { appSchemaState } = useResource()
if (local) { if (local) {
const locales = appSchemaState?.langs?.locales || [] const locales = appSchemaState?.langs?.locales || []
const messages = {} const messages: Record<string, any> = {}
const langs = getLangs() const langs = getLangs()
if (Array.isArray(locales)) { if (Array.isArray(locales)) {
@ -143,8 +157,8 @@ const getI18n = async ({ init, local }) => {
} }
} }
const initI18n = async ({ host, hostType, init, local }) => { const initI18n = async ({ host, hostType, init, local }: I18nOptions) => {
globalParams.host = host globalParams.host = host || ''
const hostTypeVar = 'host_type' const hostTypeVar = 'host_type'
globalParams[hostTypeVar] = hostType || HOST_TYPE.App globalParams[hostTypeVar] = hostType || HOST_TYPE.App
@ -165,23 +179,23 @@ const initI18n = async ({ host, hostType, init, local }) => {
}) })
} }
const initAppI18n = async (appId) => { const initAppI18n = async (appId: string) => {
if (appId) { if (appId) {
await initI18n({ await initI18n({
host: appId, host: appId,
hostType: HOST_TYPE.App hostType: HOST_TYPE.App
}) })
useCanvas().canvasApi.value?.setLocales(i18nResource.messages) useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
} }
} }
const initBlockI18n = async (blockId) => { const initBlockI18n = async (blockId: string) => {
if (blockId) { if (blockId) {
await initI18n({ await initI18n({
host: blockId, host: blockId,
hostType: HOST_TYPE.Block hostType: HOST_TYPE.Block
}) })
useCanvas().canvasApi.value?.setLocales(i18nResource.messages) useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
} }
} }
@ -192,12 +206,13 @@ const initBlockLocalI18n = async (langs = {}) => {
hostType: HOST_TYPE.Block, hostType: HOST_TYPE.Block,
local: true local: true
}) })
useCanvas().canvasApi.value?.setLocales(i18nResource.messages) useCanvas().canvasApi.value?.setLocales?.(i18nResource.messages)
} }
const format = (str = '', params = {}) => str.replace(/\$\{(.+?)\}/g, (substr, key) => params[key] || '') const format = (str = '', params: Record<string, any> = {}) =>
str.replace(/\$\{(.+?)\}/g, (_substr, key: string) => params[key] || '')
const translate = (obj) => { const translate = (obj: { [x: string]: any }) => {
const { type, key = utils.guid() } = obj || {} const { type, key = utils.guid() } = obj || {}
if (type === PROP_DATA_TYPE.I18N) { if (type === PROP_DATA_TYPE.I18N) {
@ -213,13 +228,13 @@ const translate = (obj) => {
const getData = () => i18nResource.messages const getData = () => i18nResource.messages
const batchCreateI18n = ({ host, hostType }) => { const batchCreateI18n = ({ host, hostType }: Pick<I18nOptions, 'host' | 'hostType'>) => {
if (!host) { if (!host) {
return return
} }
globalParams.host = host globalParams.host = host
globalParams.host_type = hostType globalParams.host_type = hostType || ''
const { locales } = i18nResource const { locales } = i18nResource
const langs = getLangs() const langs = getLangs()

View File

@ -28,16 +28,39 @@ import {
const { COMPONENT_NAME, DEFAULT_INTERCEPTOR } = constants const { COMPONENT_NAME, DEFAULT_INTERCEPTOR } = constants
const appSchemaState = reactive({ interface AppSchemaState {
dataSource: any[]
pageTree: any[]
langs: {
locales: {
lang: string
}[]
messages: any
}
utils: { [x: string]: any; type: string }[]
globalState: any[]
materialsDeps: {
scripts: any[]
styles: Set<unknown>
}
componentsMap?: any
dataHandler?: any
willFetch?: any
errorHandler?: any
bridge?: any
isDemo?: boolean
}
const appSchemaState = reactive<AppSchemaState>({
dataSource: [], dataSource: [],
pageTree: [], pageTree: [],
langs: {}, langs: { locales: [], messages: {} },
utils: {}, utils: [],
globalState: [], globalState: [],
materialsDeps: { scripts: [], styles: new Set() } materialsDeps: { scripts: [], styles: new Set() }
}) })
function goPage(pageId) { function goPage(pageId: string) {
if (!pageId) { if (!pageId) {
return return
} }
@ -45,7 +68,16 @@ function goPage(pageId) {
getMetaApi(META_SERVICE.GlobalService).updatePageId(pageId) getMetaApi(META_SERVICE.GlobalService).updatePageId(pageId)
} }
const initPage = (pageInfo) => { interface PageInfo {
[x: string]: any
meta: any
id: string
fileName: string
componentName: string
props: any
}
const initPage = (pageInfo: PageInfo) => {
try { try {
if (pageInfo.meta) { if (pageInfo.meta) {
const { occupier } = pageInfo.meta const { occupier } = pageInfo.meta
@ -77,12 +109,12 @@ const initPage = (pageInfo) => {
* id * id
* @param {string} blockId id * @param {string} blockId id
*/ */
const initBlock = async (blockId) => { const initBlock = async (blockId: string) => {
const blockApi = getMetaApi(META_APP.BlockManage) const blockApi = getMetaApi(META_APP.BlockManage)
const blockContent = await blockApi.getBlockById(blockId) const blockContent = await blockApi.getBlockById(blockId)
if (blockContent.public_scope_tenants.length) { if (blockContent.public_scope_tenants.length) {
blockContent.public_scope_tenants = blockContent.public_scope_tenants.map((e) => e.id) blockContent.public_scope_tenants = blockContent.public_scope_tenants.map((e: { id: string }) => e.id)
} }
useLayout().layoutState.pageStatus = getCanvasStatus(blockContent?.occupier) useLayout().layoutState.pageStatus = getCanvasStatus(blockContent?.occupier)

View File

@ -18,7 +18,7 @@ const CONSTANTS = {
BLOCKTEXT: '区块' BLOCKTEXT: '区块'
} }
const setBreadcrumbPage = (value: string) => { const setBreadcrumbPage = (value: any) => {
breadcrumbData.value = [CONSTANTS.PAGETEXT, ...value] breadcrumbData.value = [CONSTANTS.PAGETEXT, ...value]
sessionStorage.setItem('pageInfo', value) sessionStorage.setItem('pageInfo', value)
} }