UNPKG

@truenewx/tnxvue3

Version:

互联网技术解决方案:Vue3扩展支持

276 lines (244 loc) 9.67 kB
/** * 基于Vue 3的扩展支持 */ import Tnx, {util} from '../../tnxcore/src/tnxcore.ts'; import Text from './text/Text.vue'; import Percent from './percent/Percent.vue'; import * as Vue from 'vue'; import mitt, {Emitter, EventType} from 'mitt'; import {Router} from 'vue-router'; import {VueRouter} from './tnxvue-router.ts'; export {util}; export type EventBus = Emitter<Record<EventType, unknown>> & { once(name: EventType, handler: (event: unknown) => void): void; }; export type ButtonOptions = { text: string; click: ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined); type?: string; } export type DialogOptions = { type?: OpenType; theme?: string; click?: boolean | ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined); buttonText?: string | string[]; buttons?: ButtonOptions[]; } export type OpenType = 'alert' | 'confirm' | 'close' | 'none'; export type DrawerOptions = DialogOptions export type OpenOptions = DialogOptions & { mode?: 'dialog' | 'drawer'; title?: string; } export default class TnxVue extends Tnx { router: VueRouter; eventBus: EventBus; /** * 需要注册到Vue中的组件清单 */ components: Record<string, Vue.Component> = { Text, Percent, }; constructor(apiBaseUrl: string, id: string = 'tnxvue') { super(apiBaseUrl, id); this.libs.Vue = Vue; } install(app: Vue.App): void { for (let key of Object.keys(this.components)) { const component = this.components[key]; app.component(component.name, component); } } createVueApp(rootComponent: Vue.Component, router?: Router, rootProps?: Record<string, any>): Vue.App { let app = Vue.createApp(rootComponent, rootProps); app.use(this); if (router) { app.use(router); this.router = app.config.globalProperties.$router as VueRouter; } else if (this.router) { app.config.globalProperties.$router = this.router; } if (!this.eventBus) { this.eventBus = mitt() as EventBus; } this.eventBus.once = (name: EventType, handler: (event: unknown) => void) => { this.eventBus.all.set(name, [handler]); } return app; } /** * 深度监听指定对象,在created()中调用才能生效 * @param vm vue页面实例 * @param target 要监听的对象 * @param handler 处理函数 */ deepWatch(vm: Vue.ComponentPublicInstance, target: object, handler: (newValue: any, oldValue: any, path: string) => void): void { vm.$watch(() => { try { return JSON.stringify(target); } catch (e) { console.error(e); } return undefined; }, (newValue: string, oldValue: string): void => { try { if (newValue !== oldValue) { const newObject = JSON.parse(newValue); const oldObject = JSON.parse(oldValue); this.deepCompare(newObject, oldObject, '', handler); } } catch (e) { console.error(e); } }, {deep: true}); } deepCompare(object1: object, object2: object, path: string, handler: (newValue: any, oldValue: any, path: string) => void): void { if (object1) { const keys = Object.keys(object1); keys.forEach(key => { const fullPath = path ? `${path}.${key}` : key; if (object2) { if (Array.isArray(object1[key]) && Array.isArray(object2[key])) { object1[key].forEach((item, index) => { this.deepCompare(item, object2[key][index], `${fullPath}[${index}]`, handler); }); } else if (typeof object1[key] === 'object' && typeof object2[key] === 'object') { this.deepCompare(object1[key], object2[key], fullPath, handler); } else if (object1[key] !== object2[key]) { handler(object1[key], object2[key], fullPath); } } else { handler(object1[key], undefined, fullPath); } }); } } nextTickTimeout(vm: Vue.ComponentPublicInstance, handler: () => void, timeout?: number) { vm.$nextTick().then(() => { setTimeout(handler, timeout); }); } /** * 判断指定对象是否组件实例 * @param obj 对象 * @returns {boolean} 是否组件实例 */ isComponent(obj: any): boolean { return (typeof obj === 'object') && (typeof obj.render === 'function'); } dialog(content: string | Vue.Component, title?: string, options: DialogOptions = {}, contentProps: Record<string, any> = {}) { const buttons = this.getOptionsButtons(options); // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } protected getOptionsButtons(options: DialogOptions): ButtonOptions[] { const buttons = options.buttons || this.getDefaultDialogButtons(options.type, options.click, options.theme); if (options.buttonText) { if (!Array.isArray(options.buttonText)) { options.buttonText = [options.buttonText]; } for (let i = 0; i < buttons.length; i++) { let buttonText = options.buttonText[i]; if (buttonText) { buttons[i].text = buttonText; } } } return buttons; } protected getDefaultDialogButtons( type: OpenType, click: boolean | ((yes: boolean, close: () => void) => boolean | undefined) | ((close: () => void) => boolean | undefined), theme?: string): ButtonOptions[] { if (click !== false) { if (type === 'none') { return []; } else if (type === 'confirm') { return [{ text: '确定', type: theme || 'primary', click(close: () => void) { if (typeof click === 'function') { return click.call(this, true, close); } } }, { text: '取消', click(close: () => void) { if (typeof click === 'function') { return click.call(this, false, close); } } }]; } else if (type === 'close') { return [{ text: '关闭', type: theme, click(close: () => void) { if (typeof click === 'function') { return click.call(this, close); } } }]; } else { return [{ text: '确定', type: theme || 'primary', click(close: () => void) { if (typeof click === 'function') { return click.call(this, close); } } }]; } } return []; } closeDialog(all?: boolean): Promise<void> { // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } drawer(content: string | Vue.Component, title?: string, options: DrawerOptions = {}, contentProps: Record<string, any> = {}) { const buttons = this.getOptionsButtons(options); // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } closeDrawer(all?: boolean): Promise<void> { // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } showLoading(message?: string): Promise<void> { // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } closeLoading(): void { // 默认不实现,由UI框架扩展层实现 throw new Error('Unsupported function'); } hideLoading(): void { this.closeLoading(); } open(component: Vue.Component, props?: Record<string, any>, options: OpenOptions = {}) { const c = component as any; if (typeof c.open === 'function') { options = Object.assign({}, c.open(props), options); } else if (c.methods && typeof c.methods.open === 'function') { options = Object.assign({}, c.methods.open(props), options); } let mode = options.mode; delete options.mode; const title = options.title; delete options.title; if (mode === 'drawer') { return this.drawer(component, title, options, props); } return this.dialog(component, title, options, props); } }