import { logger } from '@motajs/common'; import { ElementLocator } from '@motajs/render'; import { GameUI, IUIController, SetupComponentOptions } from '@motajs/system'; import { computed, defineComponent, ref, SetupContext } from 'vue'; import { Background, BackgroundProps } from './background'; import { TextContent, TextContentProps, IGameUIPropsBase } from '@user/client-base'; export interface WaitBoxProps extends Partial, Partial { loc: ElementLocator; width: number; promise?: Promise; text?: string; pad?: number; } export type WaitBoxEmits = { resolve: (data: T) => void; }; export interface WaitBoxExpose { /** * 手动将此组件兑现,注意调用时如果传入的 Promise 还没有兑现, * 当 Promise 兑现后将不会再次触发 resolve 事件,即 resolve 事件只会被触发一次 * @param data 兑现值 */ resolve(data: T): void; } const waitBoxProps = { props: ['promise', 'loc', 'winskin', 'color', 'border', 'width'], emits: ['resolve'] } satisfies SetupComponentOptions< WaitBoxProps, WaitBoxEmits, keyof WaitBoxEmits >; /** * 等待框,可以等待某个异步操作 (Promise),操作完毕后触发兑现事件,单次调用参考 {@link waitbox}。 * 参数参考 {@link WaitBoxProps},事件参考 {@link WaitBoxEmits},函数接口参考 {@link WaitBoxExpose}。用例如下: * ```tsx * // 创建一个等待 1000ms 的 Promise,兑现值是等待完毕时的当前时间刻 * const promise = new Promise(res => window.setTimeout(() => res(Date.now()), 1000)); * * console.log(time)} * /> * ``` */ export const WaitBox = defineComponent< WaitBoxProps, WaitBoxEmits, keyof WaitBoxEmits >( ( props: WaitBoxProps, { emit, expose, attrs }: SetupContext> ) => { const contentHeight = ref(200); const text = computed(() => props.text ?? '请等待 ...'); const pad = computed(() => props.pad ?? 24); const containerLoc = computed(() => { const [x = 0, y = 0, , , ax = 0, ay = 0] = props.loc; return [x, y, props.width, contentHeight.value, ax, ay]; }); const backLoc = computed(() => { return [1, 1, props.width - 2, contentHeight.value - 2]; }); const contentLoc = computed(() => { return [ pad.value, pad.value, props.width - pad.value * 2, contentHeight.value - pad.value * 2 ]; }); let resolved: boolean = false; props.promise?.then( value => { resolve(value); }, reason => { logger.warn(63, reason); } ); const resolve = (data: T) => { if (resolved) return; resolved = true; emit('resolve', data); }; const onContentHeight = (height: number) => { contentHeight.value = height + pad.value * 2; }; expose>({ resolve }); return () => ( ); }, waitBoxProps ); /** * 打开一个等待框,等待传入的 Promise 兑现后,关闭等待框,并将兑现值返回。 * 示例,等待 1000ms: * ```ts * // 创建一个等待 1000ms 的 Promise,兑现值是等待完毕时的当前时间刻 * const promise = new Promise(res => window.setTimeout(() => res(Date.now()), 1000)); * const value = await waitbox( * // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可 * props.controller, * // 确认框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效 * [240, 240, void 0, void 0, 0.5, 0.5], * // 确认框的宽度 * 240, * // 要等待的 Promise * promise, * // 额外的 props,例如填写等待文本,此项可选,参考 WaitBoxProps * { text: '请等待 1000ms' } * ); * console.log(value); // 输出时间刻 * ``` * @param controller UI 控制器 * @param loc 等待框的位置 * @param width 等待框的宽度 * @param promise 要等待的 Promise * @param props 额外的 props,参考 {@link WaitBoxProps} */ export function waitbox( controller: IUIController, loc: ElementLocator, width: number, promise: Promise, props?: Partial> ): Promise { return new Promise(res => { const instance = controller.open(WaitBoxUI, { ...(props ?? {}), loc, width, promise, onResolve: (data: unknown) => { controller.close(instance); res(data as T); } }); }); } export const WaitBoxUI = new GameUI('wait-box', WaitBox);