Files
Docker/uniapp/youjiala/src/composables/useAdminPick.ts
T
2026-10-13 14:23:10 +08:00

109 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 管理端"选择页"回传协议
*
* 旧 WebVue 的做法:Operation 页面里塞一个 `<dragModal>`,内嵌 Enquiry 组件,
* 用 provide/inject(operationToEnquiryMethodKey + isModalUser/selectionTypeUser)
* 双向通信,确定时读 `enquiry.value.keys`。
* 移动端不能照搬:屏幕容不下"表单 + 弹窗 + 表格"三层,而且 uni 的页面之间
* 没有 provide/inject(每个页面是独立实例)。所以改成"真的跳一个选择页,
* 选完 emit + navigateBack",通道名由调用方给定。
*
* 参数一律不做 encodeURIComponent:H5 侧 uni/vue-router 拼 URL 用的是 encodeURI,
* 预编码会把 % 再编成 %25(这个坑在 nav-guard 的 ?from= 上踩过一次,
* 见 src/md/page-routing.md)。因此 channel 与 params 的值只允许
* 字母、数字、下划线、连字符、点、斜杠这类安全字符。
*/
import type { AdminRow } from "@/admin/admin-types";
import type { PageName } from "@/router/routes";
import { onUnmounted, ref } from "vue";
import { pageUrl } from "@/router/routes";
/** 事件名前缀:uni.$emit 是全局总线,加前缀避免与业务事件撞名 */
const EVENT_PREFIX = "admin:pick:";
/** 打开发起方要的东西:目标页 + 透传给它的 query */
export interface AdminPickRequest {
page: PageName;
/** 简单标量参数,如 { multiple: "1" };不允许放 JSON 串或含特殊字符的值 */
params?: Record<string, string>;
}
function buildQuery(params: Record<string, string>): string {
const parts: string[] = [];
for (const [key, value] of Object.entries(params)) {
parts.push(`${key}=${value}`);
}
return parts.join("&");
}
/**
* 调用方侧:打开选择页并登记"选好了怎么办"。
* @param channel 通道名,同一页面的多个选择器用不同通道(如 actor、manager)
*/
export function useAdminPick<T extends AdminRow = AdminRow>(channel: string) {
const eventName = `${EVENT_PREFIX}${channel}`;
let handler: ((rows: T[]) => void) | null = null;
function detach(): void {
if (handler) {
uni.$off(eventName, handler);
handler = null;
}
}
function open(
request: AdminPickRequest,
onPicked: (rows: T[]) => void,
): void {
// 先退订再订阅:同一通道重复打开时若留着旧闭包,上一次的选择逻辑会被再执行一遍
detach();
handler = (rows: T[]) => {
onPicked(rows ?? []);
};
uni.$on(eventName, handler);
const query = buildQuery({ channel, ...(request.params ?? {}) });
uni.navigateTo({ url: `${pageUrl(request.page)}?${query}` });
}
onUnmounted(detach);
return { channel, open, detach };
}
/**
* 选择页侧:onLoad 里把 channel 存下来,确定时回传。
* 没带 channel 参数时按 "default" 处理——手敲地址进来也不会报错,
* 只是没人接收这次选择。
*/
export function useAdminPickTarget() {
const channel = ref("default");
const multiple = ref(false);
/** 从 onLoad 的 options 里读协议参数 */
function readOptions(options?: Record<string, string | undefined>): void {
const raw = String(options?.channel ?? "").trim();
if (raw) {
channel.value = raw;
}
multiple.value = String(options?.multiple ?? "") === "1";
}
/** 回传并退回上一页 */
function deliver(rows: AdminRow[]): void {
uni.$emit(`${EVENT_PREFIX}${channel.value}`, rows);
uni.navigateBack();
}
/** 单选语义:只认第一条 */
function deliverOne(row: AdminRow): void {
deliver([row]);
}
function cancel(): void {
uni.navigateBack();
}
return { channel, multiple, readOptions, deliver, deliverOne, cancel };
}