feat(ui): 修改useApp实现,与ui-react中保持一致

This commit is contained in:
roymondchen 2023-04-10 19:59:53 +08:00
parent abcac71826
commit befaf67ba7
11 changed files with 213 additions and 492 deletions

View File

@ -35,18 +35,18 @@ export default ({ config, methods }: UseAppOptions) => {
const node = app?.page?.getNode(config.id); const node = app?.page?.getNode(config.id);
const [created, setCreated] = useState(false); const [created, setCreated] = useState(false);
const emitData = {
config,
...methods,
};
if (!created) { if (!created) {
// 只需要触发一次 created // 只需要触发一次 created
setCreated(true); setCreated(true);
node?.emit('created', { methods }); node?.emit('created', emitData);
} }
useEffect(() => { useEffect(() => {
const emitData = {
config,
...methods,
};
node?.emit('mounted', emitData); node?.emit('mounted', emitData);
return () => { return () => {

View File

@ -1,6 +1,7 @@
<template> <template>
<component <component
v-if="display()" v-if="display()"
ref="component"
:is="tagName" :is="tagName"
:id="config.id" :id="config.id"
:class="`magic-ui-component${config.className ? ` ${config.className}` : ''}`" :class="`magic-ui-component${config.className ? ` ${config.className}` : ''}`"
@ -9,39 +10,34 @@
></component> ></component>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, getCurrentInstance, inject, provide } from 'vue'; import { computed, inject } from 'vue';
import Core from '@tmagic/core'; import Core from '@tmagic/core';
import { toLine } from '@tmagic/utils'; import { toLine } from '@tmagic/utils';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: Record<string, any>;
type: Object, model: any;
default: () => ({}), }>(),
}, {
config: () => ({}),
model: () => ({}),
}, },
);
setup(props) { const app: Core | undefined = inject('app');
const vm = getCurrentInstance()?.proxy;
const app: Core | undefined = inject('app');
provide('hoc', vm); const tagName = computed(() => `magic-ui-${toLine(props.config.type)}`);
const style = computed(() => app?.transformStyle(props.config.style));
return { const display = () => {
tagName: computed(() => `magic-ui-${toLine(props.config.type)}`), const displayCfg = props.config?.display;
style: computed(() => app?.transformStyle(props.config.style)),
display: () => { if (typeof displayCfg === 'function') {
const displayCfg = props.config?.display; return displayCfg(app);
}
if (typeof displayCfg === 'function') { return displayCfg !== false;
return displayCfg(app); };
}
return displayCfg !== false;
},
};
},
});
</script> </script>

View File

@ -1,63 +1,34 @@
<template> <template>
<button class="magic-ui-button" @click="clickHandler"> <button class="magic-ui-button">
<slot> <slot>
<magic-ui-text :config="textConfig"></magic-ui-text> <magic-ui-text :config="textConfig"></magic-ui-text>
</slot> </slot>
</button> </button>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, getCurrentInstance, PropType, reactive } from 'vue'; import { computed } from 'vue';
import { MComponent } from '@tmagic/schema';
import { MButton, MButtonInstance, MText } from '../../../src/types';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: MComponent;
type: Object as PropType<MButton>, model: any;
default: () => ({}), }>(),
}, {
model: () => ({}),
model: {
type: Object,
default: () => ({}),
},
}, },
setup(props) { );
useApp(props);
const vm: MButtonInstance = getCurrentInstance()?.proxy as MButtonInstance;
const actions = reactive<Function[]>([]);
const actualActions = computed(() => [
typeof props.config.preAction === 'function' ? props.config.preAction : () => true,
...actions,
typeof props.config.postAction === 'function' ? props.config.postAction : () => true,
]);
function pushAction(action: Function): void {
actions.push(action);
}
async function clickHandler(): Promise<void> {
for (const fn of actualActions.value) {
if (typeof fn === 'function') {
const ret = await fn(vm, { model: props.model });
if (ret === false) {
break;
}
}
}
}
const textConfig = computed<MText>(() => ({ const textConfig = computed(() => ({
type: 'text', type: 'text',
text: props.config?.text || '', text: props.config?.text || '',
disabledText: props.config?.disabledText || '', }));
html: props.config?.html || '',
}));
return { useApp({
pushAction, config: props.config,
clickHandler, methods: {},
textConfig,
};
},
}); });
</script> </script>

View File

@ -6,47 +6,47 @@
:style="style" :style="style"
> >
<slot></slot> <slot></slot>
<magic-ui-component v-for="item in config.items" :key="item.id" :config="item"></magic-ui-component> <MComponent v-for="item in config.items" :key="item.id" :config="item"></MComponent>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, PropType } from 'vue'; import { computed, inject } from 'vue';
import Core from '@tmagic/core';
import type { MContainer } from '@tmagic/schema'; import type { MContainer } from '@tmagic/schema';
import Component from '../../Component.vue'; import MComponent from '../../Component.vue';
import useApp from '../../useApp'; import useApp from '../../useApp';
import useCommonMethod from '../../useCommonMethod'; import useCommonMethod from '../../useCommonMethod';
export default defineComponent({ const props = withDefaults(
components: { defineProps<{
'magic-ui-component': Component, config: MContainer;
model: any;
}>(),
{
model: () => ({}),
}, },
);
props: { const app: Core | undefined = inject('app');
config: {
type: Object as PropType<MContainer>,
default: () => ({}),
},
},
setup(props) { const style = computed(() => app?.transformStyle(props.config.style || {}));
const app = useApp(props);
return { const display = () => {
style: computed(() => app?.transformStyle(props.config.style || {})), const displayCfg = props.config?.display;
display: () => { if (typeof displayCfg === 'function') {
const displayCfg = props.config?.display; return displayCfg(app);
}
return displayCfg !== false;
};
if (typeof displayCfg === 'function') { useApp({
return displayCfg(app); config: props.config,
} methods: {
return displayCfg !== false; ...useCommonMethod(props),
},
...useCommonMethod(props),
};
}, },
}); });
</script> </script>

View File

@ -1,32 +1,27 @@
<template> <template>
<img class="magic-ui-img" :src="config.src" @click="clickHandler" /> <img class="magic-ui-img" :src="config.src" @click="clickHandler" />
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, PropType } from 'vue'; import { MComponent } from '@tmagic/schema';
import { MImg } from '../../types';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: MComponent;
type: Object as PropType<MImg>, model: any;
default: () => ({}), }>(),
}, {
model: () => ({}),
model: {
type: Object,
default: () => ({}),
},
}, },
setup(props) { );
useApp(props);
return { const clickHandler = () => {
clickHandler() { if (props.config.url) window.location.href = props.config.url;
if (props.config.url) window.location.href = props.config.url; };
},
}; useApp({
}, config: props.config,
methods: {},
}); });
</script> </script>

View File

@ -3,60 +3,55 @@
<slot></slot> <slot></slot>
</magic-ui-container> </magic-ui-container>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref } from 'vue'; import { inject, ref } from 'vue';
import Core from '@tmagic/core'; import Core from '@tmagic/core';
import type { MNode } from '@tmagic/schema'; import type { MComponent, MNode } from '@tmagic/schema';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: MComponent;
type: Object, model: any;
default: () => ({}), }>(),
}, {
model: () => ({}),
model: {
type: Object,
default: () => ({}),
},
}, },
);
setup(props) { const visible = ref(false);
const visible = ref(false); const app: Core | undefined = inject('app');
const app: Core | undefined = useApp(props); const node = app?.page?.getNode(props.config.id);
const node = app?.page?.getNode(props.config.id);
const openOverlay = () => { const openOverlay = () => {
visible.value = true; visible.value = true;
if (app) { if (app) {
app.emit('overlay:open', node); app.emit('overlay:open', node);
} }
}; };
const closeOverlay = () => { const closeOverlay = () => {
visible.value = false; visible.value = false;
if (app) { if (app) {
app.emit('overlay:close', node); app.emit('overlay:close', node);
} }
}; };
app?.page?.on('editor:select', (info, path) => { app?.page?.on('editor:select', (info, path) => {
if (path.find((node: MNode) => node.id === props.config.id)) { if (path.find((node: MNode) => node.id === props.config.id)) {
openOverlay(); openOverlay();
} else { } else {
closeOverlay(); closeOverlay();
} }
}); });
return { useApp({
visible, config: props.config,
methods: {
openOverlay, openOverlay,
closeOverlay, closeOverlay,
};
}, },
}); });
</script> </script>

View File

@ -7,40 +7,39 @@
:style="style" :style="style"
> >
<slot></slot> <slot></slot>
<magic-ui-component v-for="item in config.items" :key="item.id" :config="item"></magic-ui-component> <MComponent v-for="item in config.items" :key="item.id" :config="item"></MComponent>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { computed, defineComponent, PropType } from 'vue'; import { computed, inject } from 'vue';
import Core from '@tmagic/core';
import type { MPage } from '@tmagic/schema'; import type { MPage } from '@tmagic/schema';
import Component from '../../Component.vue'; import MComponent from '../../Component.vue';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
components: { defineProps<{
'magic-ui-component': Component, config: MPage;
model: any;
}>(),
{
model: () => ({}),
}, },
);
props: { const app: Core | undefined = inject('app');
config: {
type: Object as PropType<MPage>,
default: () => ({}),
},
},
setup(props) { const style = computed(() => app?.transformStyle(props.config.style || {}));
const app = useApp(props);
return { const refresh = () => {
style: computed(() => app?.transformStyle(props.config.style || {})), window.location.reload();
};
refresh() { useApp({
window.location.reload(); config: props.config,
}, methods: { refresh },
};
},
}); });
</script> </script>

View File

@ -2,46 +2,41 @@
<img class="magic-ui-qrcode" :src="imgUrl" /> <img class="magic-ui-qrcode" :src="imgUrl" />
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, PropType, ref, watch } from 'vue'; import { ref, watch } from 'vue';
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import { MQrcode } from '../../types'; import type { MComponent } from '@tmagic/schema';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: MComponent;
type: Object as PropType<MQrcode>, model: any;
default: () => ({}), }>(),
}, {
model: () => ({}),
model: {
type: Object,
default: () => ({}),
},
}, },
);
setup(props) { const imgUrl = ref();
useApp(props);
const imgUrl = ref();
watch( watch(
() => props.config.url, () => props.config.url,
(url = '') => { (url = '') => {
QRCode.toDataURL(url, (e: any, url: string) => { QRCode.toDataURL(url, (e: any, url: string) => {
if (e) console.error(e); if (e) console.error(e);
imgUrl.value = url; imgUrl.value = url;
}); });
},
{
immediate: true,
},
);
return {
imgUrl,
};
}, },
{
immediate: true,
},
);
useApp({
config: props.config,
methods: {},
}); });
</script> </script>

View File

@ -1,63 +1,24 @@
<script lang="ts"> <template>
import { computed, defineComponent, getCurrentInstance, h, inject, PropType } from 'vue'; <span>{{ config.text }}</span>
</template>
<script lang="ts" setup>
import { MComponent } from '@tmagic/schema';
import { MComponentInstance, MText, MTextInstance } from '../../../src/types';
import useApp from '../../useApp'; import useApp from '../../useApp';
export default defineComponent({ const props = withDefaults(
props: { defineProps<{
config: { config: MComponent;
type: Object as PropType<MText>, model: any;
default: () => ({}), }>(),
}, {
model: () => ({}),
model: {
type: Object,
default: () => ({}),
},
vars: {
type: Object,
default: () => ({}),
},
}, },
setup(props) { );
useApp(props);
const vm: MTextInstance = getCurrentInstance()?.proxy as MTextInstance;
const hoc: MComponentInstance = inject('hoc');
const displayText = computed(() => {
let text = props.config?.text || '';
const { vars } = props;
if (hoc?.disabled && props.config?.disabledText) {
text = props.config.disabledText;
}
if (typeof text === 'function') {
return text.bind(vm)(vm, { model: props.model });
}
if (Object.prototype.toString.call(vars) === '[object Object]') {
let tmp: string = text;
Object.entries(vars).forEach(([key, value]) => {
tmp = tmp.replace(new RegExp(`{{${key}}}`, 'g'), value);
});
return tmp;
}
return text || '';
});
return { useApp({
displayText, config: props.config,
}; methods: {},
},
render() {
const className = this.config?.multiple ? 'magic-ui-text' : 'magic-ui-text magic-ui-text--single-line';
if (typeof this.$slots?.default === 'function') {
return h('div', { class: className }, [this.$slots?.default?.() || '']);
}
return h('div', {
class: className,
...(this.displayText ? { innerHTML: this.displayText } : {}),
});
},
}); });
</script> </script>

View File

@ -16,8 +16,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { ComponentPublicInstance } from 'vue';
/* style */ /* style */
export type PartCSSStyle = { export type PartCSSStyle = {
[key in keyof CSSStyleDeclaration]?: string | number; [key in keyof CSSStyleDeclaration]?: string | number;
@ -45,203 +43,3 @@ export interface MEventBus {
$once: (...args: any) => void; $once: (...args: any) => void;
$emit: (...args: any) => void; $emit: (...args: any) => void;
} }
/* component */
export interface MComponent {
type: string;
id?: number | string;
name?: string;
style?: StyleCfg;
disabledStyle?: StyleCfg;
className?: string | ((p1: any, p2: any) => string);
display?: boolean | ((p1: any, p2: any) => boolean);
html?: string;
created?: (p1: any, p2: any) => Promise<any>;
mounted?: (p1: any, p2: any) => Promise<any>;
renderType?: number;
events?: MEvent[];
}
export interface MComponentProps {
config: MComponent;
model: Object;
}
export type MComponentInstance =
| ComponentPublicInstance<
MComponentProps,
{},
{
[propName: string]: any;
disabled: boolean;
}
>
| null
| undefined;
/* container */
export interface MContainer extends MComponent {
items?: MComponent[] | MContainer[];
}
export interface MContainerProps {
config: MContainer;
model: Object;
}
export type MContainerInstance =
| ComponentPublicInstance<
MContainerProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
/* page */
export interface MPage extends MContainer {
title?: string;
cssFile?: string;
}
export interface MPageProps {
config: MPage;
}
export type MPageInstance =
| ComponentPublicInstance<
MPageProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
/* pop */
export interface MPop extends MContainer {
activate: () => void;
maskClose: boolean;
}
export interface MPopProps {
config: MPop;
model: Object;
fillWithSlot: boolean;
beforeOpen: (p1: MPopInstance, p2: any) => boolean;
beforeClose: (p1: MPopInstance) => boolean;
}
export interface MPopObj {
name: string;
options: object;
}
export type MPopInstance =
| ComponentPublicInstance<
MPopProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
/* app */
export interface MApp extends MComponent {
items: MPage[];
}
export interface MAppProps {
config: [MApp];
pageConfig: MPage;
}
export enum MAppElementType {
pages = 'pages',
containers = 'containers',
components = 'components',
pops = 'pops',
}
export type MAppInstance =
| ComponentPublicInstance<
MAppProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
export type MCommonInstance = MContainerInstance | MPageInstance | MComponentInstance | MPopInstance;
/* tabs */
export type MTabs = MContainer;
export interface MTabsProps {
config: MTabs;
model: Object;
}
export type MTabsInstance =
| ComponentPublicInstance<
MTabsProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
/* text */
export interface MText extends MComponent {
text?: string | ((p1: any, p2: any) => string);
disabledText?: string | ((p1: any, p2: any) => string);
multiple?: boolean;
}
export interface MTextProps {
config: MText;
model: Object;
vars: Object;
}
export type MTextInstance =
| ComponentPublicInstance<
MTextProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
/* button */
export interface MButton extends MComponent {
preAction?: (p1: any, p2: any) => string;
postAction?: (p1: any, p2: any) => string;
text?: string | ((p1: any, p2: any) => string);
disabledText?: string | ((p1: any, p2: any) => string);
}
export interface MButtonProps {
config: MButton;
model: Object;
}
export type MButtonInstance =
| ComponentPublicInstance<
MButtonProps,
{},
{
[propName: string]: any;
}
>
| null
| undefined;
export type ArrayOneOrMore = { 0: string } & string[];
export interface MImg {
src: string;
url: string;
}
export interface MQrcode {
url: string;
}
export interface MPop extends MComponent {
items?: MComponent[] | MContainer[];
closeButtonStyle?: any;
closeButton?: boolean;
}

View File

@ -16,24 +16,35 @@
* limitations under the License. * limitations under the License.
*/ */
import { getCurrentInstance, inject, onMounted, onUnmounted } from 'vue'; import { inject, onMounted, onUnmounted } from 'vue';
import Core from '@tmagic/core'; import Core from '@tmagic/core';
import type { MComponent } from '@tmagic/schema';
export default (props: any) => { interface UseAppOptions {
config: MComponent;
methods?: {
[key: string]: Function;
};
}
export default ({ config, methods }: UseAppOptions) => {
const app: Core | undefined = inject('app'); const app: Core | undefined = inject('app');
const node = app?.page?.getNode(props.config.id); const node = app?.page?.getNode(config.id);
const vm = getCurrentInstance()?.proxy; const emitData = {
config,
...methods,
};
node?.emit('created', vm); node?.emit('created', emitData);
onMounted(() => { onMounted(() => {
node?.emit('mounted', vm); node?.emit('mounted', emitData);
}); });
onUnmounted(() => { onUnmounted(() => {
node?.emit('destroy', vm); node?.emit('destroy', emitData);
}); });
return app; return app;