ow
This commit is contained in:
1 parent
3c0d389fcd
commit
32f237c4bf
22 files changed
+3351
-24
No files matched your search
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 管理端"选择页"回传协议
|
||||
*
|
||||
* 旧 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 };
|
||||
}
|
||||
Reference in new issue
Block a user