diff --git a/src/App.vue b/src/App.vue index bc85f84..161fbfe 100644 --- a/src/App.vue +++ b/src/App.vue @@ -132,382 +132,409 @@ const categories = ref([{ icon: 'trash', text: '媒体' }, { icon: 'trash', text categories.value.unshift({ icon: 'trash', text: '基础', - modules: [{ - vid: hash(`${++nextId}`), - mid: hash(`${++nextId}`), - title: '轮播图', - maxReferenceCount: -1, - referenceCount: 0, - image: undefined, - configs: [ - { - type: 'list', - field: 'items', - label: '轮播图设置', - help: '最多可添加10张图片,建议宽度750px;鼠标拖拽左侧圆点可调整图片顺序', - addable: true, - configs: [{ - type: 'image', - field: 'image', - label: '图片', - required: true, - // 表示内联数据 - inlines: [{ - type: 'text', - field: 'title', - label: '标题', - help: '选填,不超过 4 个字', + modules: [ + { + vid: hash(`${++nextId}`), + mid: hash(`${++nextId}`), + title: '轮播图', + maxReferenceCount: -1, + referenceCount: 0, + image: undefined, + configs: [ + { + type: 'list', + field: 'items', + label: '轮播图设置', + help: '最多可添加10张图片,建议宽度750px;鼠标拖拽左侧圆点可调整图片顺序', + addable: true, + configs: [{ + type: 'image', + field: 'image', + label: '图片', + required: true, + // 表示内联数据 + inlines: [{ + type: 'text', + field: 'title', + label: '标题', + help: '选填,不超过 4 个字', + }, { + type: 'text', + field: 'link', + label: '链接', + help: '请输入链接', // 自动生成:"请输入${label}" + }], + }], + }, + { + type: 'object', + field: 'indicator', + label: '指示器', + configs: [{ + type: 'mark', + field: 'style', + label: '指示器样式', + values: [ + { label: '圆形', value: '#circle' }, + { label: '直线', value: '#line' }, + { label: '数字', value: '#number' }, + ], }, { - type: 'text', - field: 'link', - label: '链接', - help: '请输入链接', // 自动生成:"请输入${label}" + type: 'mark', + field: 'position', + label: '指示器位置', + values: [ + { label: '居左', value: 'start' }, + { label: '居中', value: 'center' }, + { label: '居右', value: 'end' }, + ], + }, + { + type: 'color', + field: 'color', + label: '指示器颜色', }], - }], - }, - { - type: 'object', - field: 'indicator', - label: '指示器', - configs: [{ - type: 'mark', - field: 'style', - label: '指示器样式', - values: [ - { label: '圆形', value: '#circle' }, - { label: '直线', value: '#line' }, - { label: '数字', value: '#number' }, - ], - }, { - type: 'mark', - field: 'position', - label: '指示器位置', - values: [ - { label: '居左', value: 'start' }, - { label: '居中', value: 'center' }, - { label: '居右', value: 'end' }, - ], }, { - type: 'color', - field: 'color', - label: '指示器颜色', - }], - }, - { - type: 'object', - field: 'background', - label: '背景', - configs: [{ - type: 'boolean', - field: 'enabled', - label: '是否显示背景色', - }, { - type: 'background', - field: 'value', - // 背景的不同实现方式,可以在里面添加 image 选项来支持图片 - // 这里只能支持颜色和渐变, - features: ['color', 'gradient'], + type: 'object', + field: 'background', label: '背景', - }], - } - ], - init: { - items: [ - { - image: '', - title: '123', - link: '123', + configs: [{ + type: 'boolean', + field: 'enabled', + label: '是否显示背景色', + }, { + type: 'background', + field: 'value', + // 背景的不同实现方式,可以在里面添加 image 选项来支持图片 + // 这里只能支持颜色和渐变, + features: ['color', 'gradient'], + label: '背景', + }], } ], - indicator: { - style: '#circle', - color: 'green', - position: 'start', - }, - background: { - enabled: true, - value: "#ffffff", - }, - theme: { - color: 'white', - radius: 24 - } - }, - templates: { - '#circle': { - theme: { - gap: 20, - }, - children: { - type: 'each', - key: 'items', - handle: { - theme: { - width: 20, - height: 20, - radius: 10, - color: '@@indicator.color', - }, + init: { + items: [ + { + image: '', + title: '123', + link: '123', } + ], + indicator: { + style: '#circle', + color: 'green', + position: 'start', + }, + background: { + enabled: true, + value: "#ffffff", + }, + theme: { + color: 'white', + radius: 24 } }, - '#line': { - theme: { - gap: 20, + templates: { + '#circle': { + theme: { + gap: 20, + }, + children: { + type: 'each', + key: 'items', + handle: { + theme: { + width: 20, + height: 20, + radius: 10, + color: '@@indicator.color', + }, + } + } }, - children: { - type: 'each', - key: 'items', - handle: { - theme: { - width: 40, - height: 20, - color: '@@indicator.color', - }, + '#line': { + theme: { + gap: 20, + }, + children: { + type: 'each', + key: 'items', + handle: { + theme: { + width: 40, + height: 20, + color: '@@indicator.color', + }, + } } + }, + "#number": { + theme: { + color: 'rgba(0,0,0,0.4)', + textColor: '#fff' + }, + children: '1/len(items)' } }, - "#number": { - theme: { - color: 'rgba(0,0,0,0.4)', - textColor: '#fff' + theme: { + position: 'relative', + height: 200, + padding: { + horizontal: 12.0, + vertical: 12, }, - children: '1/len(items)' - } - }, - theme: { - position: 'relative', - height: 200, - padding: { - horizontal: 12.0, - vertical: 12, + color: 'cyan', }, - color: 'cyan', + children: [ + { + vid: hash(`${++nextId}`), + theme: { + width: '100%', + height: '100%', + color: '@@theme.color', + radius: "@@theme.radius", + textAlign: 'center', + }, + children: '设置轮播图', + }, + { + theme: { + position: 'absolute', + flexible: true, + mainAlign: '@@indicator.position', + bottom: 20, + left: 20, + right: 20, + height: 20, + color: 'red', + }, + children: { + type: 'template', + key: 'indicator.style' + } + }], }, - children: [{ + { + title: '产品组', vid: hash(`${++nextId}`), - theme: { - width: '100%', - height: '100%', - color: '@@theme.color', - radius: "@@theme.radius", - textAlign: 'center', - }, - children: '设置轮播图', - }, { - theme: { - position: 'absolute', - flexible: true, - mainAlign: '@@indicator.position', - bottom: 20, - left: 20, - right: 20, - height: 20, - color: 'red', - }, - // children: { - // type: 'each', - // key: 'indicator.position', - // handle: { - // theme: { - // width: 20, - // height: 20, - // radius: 10, - // color: 'black', - // } - // }, - // } - children: { - type: 'template', - key: 'indicator.style' - } - }], - }, - { - vid: hash(`${++nextId}`), - mid: hash(`${++nextId}`), - title: '产品组', - maxReferenceCount: -1, - referenceCount: 0, - image: undefined, - configs: [ - { - type: 'object', - field: 'titleAndDesc', - label: '产品标题及说明', - configs: [ - { - type: 'text', - label: '产品标题', - field: 'mainTitle' - }, - { - type: 'text', - label: '说明', - field: 'content', - }, - ] - }, - { - help: '最多可添加10张图片,建议宽度750px;鼠标拖拽左侧圆点可调整图片顺序', - addable: true, - type: 'list', - field: 'groups', - label: '产品组列表', - configs: [ - { - type: 'image', - label: '产品图片', - field: 'productPhoto', - // 表示内联数据 - inlines: [{ + mid: hash(`${++nextId}`), + maxReferenceCount: -1, + referenceCount: 0, + image: undefined, + configs: [ + { + type: 'object', + field: 'titleAndDesc', + label: '产品标题及说明', + configs: [ + { type: 'text', - field: 'link', - label: '链接', - help: '请输入链接', // 自动生成:"请输入${label}" + label: '产品标题', + field: 'mainTitle' }, { type: 'text', - label: '自定义标题', - field: 'productTitle', + label: '说明', + field: 'content', }, { type: 'text', - label: '自定义说明', - field: 'productDesc', + label: '提示', + field: 'more', }, - ] - }, - - ] - }, - ], - init: { - titleAndDesc: { - mainTitle: '标题', - content: '说明', - }, - groups: [ + ] + }, { + help: '帮助', + addable: true, + type: 'list', + field: 'groups', + label: '产品组列表', + configs: [ + { + type: 'image', + label: '产品图片', + field: 'productPhoto', + // 表示内联数据 + inlines: [{ + type: 'text', + field: 'link', + label: '链接', + help: '请输入链接', // 自动生成:"请输入${label}" + }, + { + type: 'text', + label: '自定义标题', + field: 'productTitle', + }, + { + type: 'text', + label: '自定义说明', + field: 'productDesc', + }, + ] + }, - productTitle: '111', - } + ] + }, ], - - }, - theme: { - height: 200, - padding: { - horizontal: 12.0, + init: { + titleAndDesc: { + mainTitle: '标题123', + content: '说明123', + more: '查看更多', + }, + groups: [ + { + productTitle: '111', + } + ], + theme: { + color: 'blue', + }, + children:{ + theme: { + radius: 12 + }, + } }, - color: 'pink', - }, - children: { - vid: hash(`${++nextId}`), theme: { - width: '100%', - height: '100%', - color: 'white', - radius: 12.0, - textAlign: 'center', + height: 200, + padding: 12, + color: '@@theme.color', }, - children: [ - { - theme: { - flexible: true, - gap: 12, - crossAlign: 'center', - }, - children: [ - { - theme: { - padding: { - horizontal: 5 - }, - }, - children: [ - { - theme: { - flexible: true, - gap: 5, - crossAlign: 'center', + children: { + vid: hash(`${++nextId}`), + theme: { + width: '100%', + height: '100%', + color: 'white', + clip: 'hidden', + radius: '@@children.theme.radius', + textAlign: 'center', + }, + children: [ + { + theme: { + flexible: true, + gap: 12, + crossAlign: 'center', + }, + children: [ + { + theme: { + padding: { + horizontal: 5 }, - children: [ - { - theme: { - fontSize: 16, - }, - children: '左侧标题', + }, + children: [ + { + theme: { + flexible: true, + gap: 5, + crossAlign: 'center', }, - { - theme: { - fontSize: 16, + children: [ + { + theme: { + fontSize: 16, + }, + children: { + type: 'text', + key: 'titleAndDesc.mainTitle' + }, }, - children: '左侧说明', - } - ] - }, - ] - }, - { - theme: { - grow: 1, - } - }, - { - vid: hash(`${++nextId}`), - theme: { - width: 'auto', - margin: { - horizontal: 5, + { + theme: { + fontSize: 16, + }, + children: { + type: 'text', + key: 'titleAndDesc.content' + }, + } + ] + }, + ] + }, + { + theme: { + grow: 1, + } + }, + { + vid: hash(`${++nextId}`), + theme: { + width: 'auto', + margin: { + horizontal: 5, + }, + fontSize: 12, + padding: { + vertical: 1, + horizontal: 5, + }, + color: '@@children.children.children.children.children.2.color', + textColor: '#fff', + radius: 16, }, - fontSize: 12, - padding: { - vertical: 1, - horizontal: 5, + children: { + type: 'text', + key: 'titleAndDesc.more' }, - color: 'pink', - radius: 16, }, - children: '查看更多', - }, - ], - }, - { - vid: hash(`${++nextId}`), - theme: { - width: "94%", - height: "70%", - color: 'pink', - radius: 12.0, - margin: { - top: '1%', - left: '3%', - right: '3%', - bottom: '2%', - }, - padding: { - horizontal: 3, - } + ], }, - children: [{ + { + vid: hash(`${++nextId}`), theme: { - width: '35%', - height: '100%', - mainAlign: "center", - color: '#fff', - crossAlign: "center" - + width: "94%", + height: "70%", + color: 'pink', + radius: 12.0, + margin: { + top: '1%', + left: '3%', + right: '3%', + bottom: '2%', + }, + padding: { + horizontal: 3, + }, + gap: 3, + clip: 'autoX', }, - children: '产品' + children: + { + type: 'each', + key: 'groups', + handle: { + theme: { + width: '100px', + height: '100%', + radius: 10, + color: '#fff', + }, + } + }, + // [{ + // theme: { + // width: '35%', + // height: '100%', + // mainAlign: "center", + // color: '#fff', + // crossAlign: "center" + // + // }, + // children: { } + // }, + // ] }, - ] - }, - ] + ] + } } - } ], }) diff --git a/src/engineer/components/ConfigCurrentView.vue b/src/engineer/components/ConfigCurrentView.vue index b025cdc..2e672fc 100644 --- a/src/engineer/components/ConfigCurrentView.vue +++ b/src/engineer/components/ConfigCurrentView.vue @@ -3,6 +3,7 @@ import type { View } from '../types'; import { computed } from 'vue'; import { useContext } from '../context'; import ColorConfig from '../configs/ColorConfig.vue'; +import NumberStyle from '../styles/NumberStyle.vue' defineOptions({ name: 'ConfigCurrentView', @@ -17,17 +18,30 @@ const styles = computed(() => { return undefined } return Object.entries(view.value.theme).filter(([, value]) => { - return typeof value === 'string' && value.startsWith('@@theme.') + return typeof value === 'string' && value.startsWith('@@') }) as Array<[string, string]> }) +const label = computed(()=>{ + if(!view.value?.title) { + return undefined + } + return view.value.title +}) + + diff --git a/src/engineer/context.ts b/src/engineer/context.ts index bb7c459..a0f8207 100644 --- a/src/engineer/context.ts +++ b/src/engineer/context.ts @@ -51,7 +51,7 @@ export interface EngineContextBase { nextId: number /** 数据源 */ sources: Record> - /** 到处数据 */ + /** 导出数据 */ export: () => Exported /** 获取动态数据 */ value: (blockId: string, key: string) => T | undefined @@ -62,7 +62,7 @@ export type EngineContext = UnwrapNestedRefs export const contextKey: InjectionKey = Symbol.for('ddd:engine') const must = (v: T | undefined, error: string): T => { - if (v == null) { + if(v==null){ throw new Error(error) } return v @@ -74,8 +74,8 @@ export function useContext(): EngineContext { export const parentViewIdKey: InjectionKey = Symbol.for('ddd:view:parent:id') -export function useParentViewId(): string | undefined { - return inject(parentViewIdKey) +export function useParentViewId(): string { + return must(inject(parentViewIdKey), 'no parentViewId no found') } export function provideParentViewId(id: string | undefined): void { @@ -86,7 +86,7 @@ export function provideParentViewId(id: string | undefined): void { const blockIdKey: InjectionKey = Symbol.for('ddd:block:id') -export function useBlockId(): string { +export const useBlockId = (): string => { return must(inject(blockIdKey), "no block found") } @@ -94,8 +94,9 @@ export function provideBlockId(id: string): void { provide(blockIdKey, id) } -export const currentViewDOMRectKey: InjectionKey>> = Symbol.for('ddd:view:current:domrect') -export const parentViewDOMRectKey: InjectionKey>> = Symbol.for('ddd:view:parent:domrect') +type ReactingDomRect = InjectionKey>> +export const currentViewDOMRectKey: ReactingDomRect = Symbol.for('ddd:view:current:domrect') +export const parentViewDOMRectKey: ReactingDomRect = Symbol.for('ddd:view:parent:domrect') export function useCurrentViewDOMRect(): UnwrapNestedRefs> { return must(inject(currentViewDOMRectKey), 'no DOMReact found') @@ -106,9 +107,9 @@ export function useParentViewDOMRect(): UnwrapNestedRefs> { } export function getModule(ctx: UnwrapNestedRefs, mid: string): Module | undefined { - for (const category of ctx.categories) { - for (const module of category.modules) { - if (module.mid === mid) { + for(const category of ctx.categories){ + for(const module of category.modules){ + if(module.mid === mid) { return module } } @@ -116,15 +117,15 @@ export function getModule(ctx: UnwrapNestedRefs, mid: string) return undefined } -export function useModule() { +export function useModule(){ const ctx = useContext() return computed((): Module | undefined => { - for (const block of ctx.blocks) { - if (block.vid !== ctx.activeBlockId) { + for(const block of ctx.blocks){ + if(block.vid !== ctx.activeBlockId){ continue } - const module = getModule(ctx, block.mid) + const module = getModule(ctx,block.mid) if (module != null) { return module } @@ -163,7 +164,9 @@ export interface TreeData { value: any } -const treeDataKey: InjectionKey|WritableComputedRef | null> = Symbol.for('ddd:view:tree:data') +type RefTreeData = InjectionKey|WritableComputedRef|null> + +const treeDataKey: RefTreeData = Symbol.for('ddd:view:tree:data') export function provideTreeData(data: UnwrapNestedRefs | WritableComputedRef | null) { provide(treeDataKey, data) @@ -175,7 +178,9 @@ export function useSource(source: string | undefined, fallback?: T) const blockId = useBlockId() return computed({ + get(): T | undefined { + console.log('get',source,'source',blockId || ctx.activeBlockId,'id') if (!source) { return fallback } @@ -193,6 +198,7 @@ export function useSource(source: string | undefined, fallback?: T) return valueOf(ctx.sources[id], source) ?? fallback }, set(value: T | undefined) { + console.log('set',value,'value',source,'source',blockId || ctx.activeBlockId,'id') if (!source || source.startsWith("$")) { return } diff --git a/src/engineer/styles/NumberStyle.vue b/src/engineer/styles/NumberStyle.vue new file mode 100644 index 0000000..483bc18 --- /dev/null +++ b/src/engineer/styles/NumberStyle.vue @@ -0,0 +1,18 @@ + + diff --git a/src/engineer/types.ts b/src/engineer/types.ts index bdf4d40..8f57685 100644 --- a/src/engineer/types.ts +++ b/src/engineer/types.ts @@ -274,7 +274,7 @@ export type ClipBehavior = | 'visible' | 'auto' | 'autoX' // overflow-x: auto; overflow-y: hidden - | 'autoY' // overflow-x: hidden; overflow-y: auto + | 'autoY' // overflow-y: hidden; overflow-y: auto /** * 视图数据 @@ -405,6 +405,7 @@ export interface View { */ // theme?: Theme | Widget theme?: DynamicTheme + title?: string /** * 关联数据源 */ diff --git a/src/engineer/views/RenderTemplate.vue b/src/engineer/views/RenderTemplate.vue index 4e51616..8221b45 100644 --- a/src/engineer/views/RenderTemplate.vue +++ b/src/engineer/views/RenderTemplate.vue @@ -9,11 +9,12 @@ const props = defineProps<{ const mod = useModule() const name = useSource(props.source) -const view = computed(() => { +const view = computed(()=>{ const ts = mod.value?.templates const key = name.value return ts && key ? ts[key] : undefined }) +