@truenewx/tnxvue3
Version:
互联网技术解决方案:Vue3扩展支持
276 lines (244 loc) • 9.67 kB
text/typescript
/**
* 基于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);
}
}