新增钉钉 Stream SDK 依赖,支持无 HTTP 上下文的后台线程显式传入 token 进行审批回调。同时,完善了 MES 审批台账功能,新增审批记录同步、批量发起审批时的门禁与台账写入逻辑,增强了系统的审批流管理能力。

This commit is contained in:
geht
2026-06-05 10:44:30 +08:00
parent 4785c55e52
commit fc4e3211ad
73 changed files with 5225 additions and 240 deletions

View File

@@ -0,0 +1,157 @@
import { reactive, ref, type Ref } from 'vue';
const STORAGE_KEY = 'mes_ding_tpl_launch_pos';
export interface FloatPosition {
left: number;
top: number;
}
function readAllPositions(): Record<string, FloatPosition> {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}') || {};
} catch {
return {};
}
}
/** 查询条件区域BasicTable 搜索表单)右侧的默认坐标 */
export function calcDefaultPosition(btnWidth: number, btnHeight: number): FloatPosition {
const formEl =
document.querySelector<HTMLElement>('.jeecg-basic-table-form-container .ant-form') ||
document.querySelector<HTMLElement>('.jeecg-basic-table-form-container');
if (!formEl) {
return {
left: Math.max(8, window.innerWidth - btnWidth - 24),
top: 100,
};
}
const rect = formEl.getBoundingClientRect();
return {
left: Math.max(8, rect.right - btnWidth - 12),
top: Math.max(8, rect.top + (rect.height - btnHeight) / 2),
};
}
/**
* 可拖拽悬浮按钮位置:默认对齐查询区右侧,拖拽后按路由持久化。
*/
export function useDraggablePosition(routePath: Ref<string>) {
const pos = reactive<FloatPosition>({ left: 0, top: 0 });
const isDragging = ref(false);
let moved = false;
let pointerId: number | null = null;
let startX = 0;
let startY = 0;
let startLeft = 0;
let startTop = 0;
let btnWidth = 120;
let btnHeight = 36;
function setButtonSize(width: number, height: number) {
btnWidth = width;
btnHeight = height;
}
function loadPosition(path: string): boolean {
const saved = readAllPositions()[path];
if (saved && typeof saved.left === 'number' && typeof saved.top === 'number') {
pos.left = saved.left;
pos.top = saved.top;
return true;
}
return false;
}
function savePosition(path: string) {
const all = readAllPositions();
all[path] = { left: pos.left, top: pos.top };
localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
}
function applyDefaultPosition() {
const p = calcDefaultPosition(btnWidth, btnHeight);
pos.left = p.left;
pos.top = p.top;
}
function initPosition(path: string, preferDefault = false) {
if (!path) return;
if (!preferDefault && loadPosition(path)) return;
applyDefaultPosition();
}
function clampPosition() {
const maxLeft = window.innerWidth - btnWidth - 8;
const maxTop = window.innerHeight - btnHeight - 8;
pos.left = Math.min(Math.max(8, pos.left), maxLeft);
pos.top = Math.min(Math.max(8, pos.top), maxTop);
}
function onPointerMove(e: PointerEvent) {
if (pointerId !== e.pointerId) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
if (!moved && (Math.abs(dx) > 4 || Math.abs(dy) > 4)) {
moved = true;
isDragging.value = true;
}
if (!moved) return;
pos.left = startLeft + dx;
pos.top = startTop + dy;
clampPosition();
}
function endDrag(path: string) {
document.removeEventListener('pointermove', onPointerMove);
document.removeEventListener('pointerup', onPointerUp);
document.removeEventListener('pointercancel', onPointerUp);
if (moved && path) {
savePosition(path);
}
pointerId = null;
setTimeout(() => {
isDragging.value = false;
moved = false;
}, 0);
}
function onPointerUp(e: PointerEvent) {
if (pointerId !== e.pointerId) return;
endDrag(routePath.value);
}
function onPointerDown(e: PointerEvent, el: HTMLElement | null) {
if (e.button !== 0) return;
if (el) {
const rect = el.getBoundingClientRect();
setButtonSize(rect.width, rect.height);
}
moved = false;
isDragging.value = false;
pointerId = e.pointerId;
startX = e.clientX;
startY = e.clientY;
startLeft = pos.left;
startTop = pos.top;
(e.currentTarget as HTMLElement)?.setPointerCapture?.(e.pointerId);
document.addEventListener('pointermove', onPointerMove);
document.addEventListener('pointerup', onPointerUp);
document.addEventListener('pointercancel', onPointerUp);
}
function wasDragged() {
return moved;
}
return {
pos,
isDragging,
setButtonSize,
initPosition,
applyDefaultPosition,
onPointerDown,
wasDragged,
clampPosition,
};
}