feat: 办事大厅用户面前端(hall)首版入库——由 app/public 改名去 app 层;build.yml 修并发撞名容exit137
build / build (push) Successful in 3m6s

This commit is contained in:
chenxing
2026-09-17 09:12:03 +08:00
parent 6511923327
commit f87523c524
89 changed files with 16000 additions and 14 deletions
+26
View File
@@ -0,0 +1,26 @@
import { Method } from "alova";
import { alovaInstance } from "@/utils/alova/alova-iview";
import type { SchoolTreeNode } from "@/types/svc";
/**
* 基础平台接口(Notice baseApi 平移):用户信息与已授权模块列表(菜单渲染数据源)。
* 模块清单在 UserBase 按本应用 appKey 配置(app_module),按登录人过滤下发。
*/
const BASE = "/api/base";
function get<T = any>(url: string, params?: Record<string, any>): Method<T> {
return new Method<T>("GET", alovaInstance, url, { params });
}
class BaseApi {
/** 当前登录用户信息 */
getUserInfo() {
return get<SchoolTreeNode>(`${BASE}/GetUserInfo`);
}
/** 当前用户已授权的模块列表(左侧菜单数据源) */
getUserMod() {
return get<SchoolTreeNode[]>(`${BASE}/GetUserMod`);
}
}
export const baseApi = new BaseApi();
+126
View File
@@ -0,0 +1,126 @@
import { Method } from "alova";
import { alovaInstance } from "@/utils/alova/alova-iview";
import config from "@/assets/config/host.config";
import type {
ApplySubmitRequest,
ApplyVo,
FlowProgressVo,
HallFilters,
HallItem,
PageResult,
} from "@/types/svc";
/**
* 公共办事大厅接口封装(全部为同源 /api/**,CAS 保护)。
*
* 纪律(2026-09-16 治理,务必遵守):
* 本文件里**不得出现任何 flow-center 地址**(无 flowHost、无直连外部 /api/flow/**)。
* 撤回/进度/流程图都是流程中心的能力,一律经办事大厅后端中转(后端用 YschFlowOpenApi 出站,
* 对端 appKey 验签 + 撤回按 submitterId 复核)。浏览器侧因此不需要跨应用票据,
* 也不存在"img 带不了签名"的问题——流程图就是一个同源 <img src>。
*/
const HALL = "/api/hall";
const APPLY = "/api/apply";
const FILE = "/api/file";
function get<T = any>(url: string, params?: Record<string, any>): Method<T> {
return new Method<T>("GET", alovaInstance, url, { params });
}
function postJSON<T = any>(url: string, data?: Record<string, any> | any[]): Method<T> {
return new Method<T>(
"POST",
alovaInstance,
url,
{ headers: { "Content-Type": "application/json;charset=UTF-8" } },
data ? JSON.stringify(data) : undefined
);
}
/** POST 表单(@RequestParam) */
function postForm<T = any>(url: string, data?: Record<string, any>): Method<T> {
const form = data
? Object.entries(data)
.filter(([, v]) => v !== undefined && v !== null)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
.join("&")
: undefined;
return new Method<T>(
"POST",
alovaInstance,
url,
{ headers: { "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" } },
form
);
}
class HallApi {
/** 已发布事项分页(关键词/分类/服务对象/部门筛选) */
items(params: Record<string, any>) {
return get<PageResult<HallItem>>(`${HALL}/items`, params);
}
/** 筛选项(服务对象/分类/部门 + 已发布计数) */
filters(siteCode: string) {
return get<HallFilters>(`${HALL}/filters`, { siteCode });
}
}
class ApplyApi {
/** 发起(后端落待提交行 + AFTER_COMMIT 异步代发起;出参见 flowEnabled 判断是否已接入) */
submit(data: ApplySubmitRequest) {
return postJSON<ApplyVo>(APPLY, data);
}
/** 待提交态重试(复用同一 bizId,流程中心 uk 幂等) */
retry(id: number) {
return postForm<ApplyVo>(`${APPLY}/${id}/retry`);
}
/** 一键重提(仅驳回/撤回态;同 bizId,流程中心 R4 重启推进) */
resubmit(id: number) {
return postForm<ApplyVo>(`${APPLY}/${id}/resubmit`);
}
/** 撤回(后端经 SDK 调流程中心托管件;仅提交人本人 + 审批中) */
withdraw(id: number) {
return postForm<void>(`${APPLY}/${id}/withdraw`);
}
/** 审批进度(结构化轨迹;与详情同可见性守卫) */
progress(id: number) {
return get<FlowProgressVo>(`${APPLY}/${id}/progress`);
}
/** 流程示意图地址(同源 <img src>;token 与字节都在后端流转)。
* img 标签不走 alova(无 baseUrl 拼接),必须显式带 serviceHost——
* 部署在 {ctx}/public/ 子路径时裸 /api 会落到网关根导致 404。 */
diagramUrl(id: number, mode = "dept") {
return `${config.serviceHost}${APPLY}/${id}/diagram.png?mode=${encodeURIComponent(mode)}`;
}
my(params?: Record<string, any>) {
return get<PageResult<ApplyVo>>(`${APPLY}/my`, params);
}
/** 发起页数据(事项要点 + 表单 schema) */
item(siteCode: string, itemCode: string) {
return get<Record<string, any>>(`${APPLY}/item`, { siteCode, itemCode });
}
detail(id: number) {
return get<ApplyVo>(`${APPLY}/${id}`);
}
}
class FileApi {
/** 上传(返回完整 URL 与附件行 id;绑定在提交动作完成) */
upload(file: File, type: "image" | "file", siteCode?: string) {
const form = new FormData();
form.append("file", file);
form.append("type", type);
if (siteCode) form.append("siteCode", siteCode);
return new Method<{ id: number; fileName: string; fileUrl: string; fileSize: number }>(
"POST",
alovaInstance,
`${FILE}/upload`,
{ headers: {} },
form
);
}
}
export const hallApi = new HallApi();
export const applyApi = new ApplyApi();
export const fileApi = new FileApi();