import { Method } from "alova"; import { alovaInstance } from "@/utils/alova/alova-iview"; import conf from "@/assets/config/host.config"; import { IObj } from "@/types/Basics"; import { ArticleEditVo, ArticlePageItem, ArticlePageQuery, ArticleSaveRequest, AttachmentVo, AuthUserInfo, BannerVo, CategoryTreeNode, DeleteRequest, LinkVo, PageResult, SchoolCard, SchoolOption, SiteVo, ThemeVo, } from "@/types/cms"; /** * CMS 管理端 API(手写 class 风格,同 Notice front 约定)。 * baseURL = conf.serviceHost(dev 为空串走 vite 代理;生产 = 源 + /yschcms 上下文)。 */ const AUTH = "/api/auth"; const BASE = "/api/base"; const SCHOOL = "/api/school"; const SITE = "/api/site"; const CATEGORY = "/api/category"; const ARTICLE = "/api/article"; const ATTACHMENT = "/api/attachment"; const BANNER = "/api/banner"; const LINK = "/api/link"; const THEME = "/api/theme"; /** GET(query 参数) */ function get(url: string, params?: IObj): Method { return alovaInstance.Get(url, { params }); } /** POST JSON(@RequestBody);params 为追加到 URL 的 query 参数(alova Post config 同 Get) */ function postJSON(url: string, data?: unknown, params?: IObj): Method { return alovaInstance.Post(url, data, { headers: { "Content-Type": "application/json;charset=UTF-8" }, params, }); } /** 上传地址(wangEditor customUpload / Upload 组件 action 直链,不经 alova) */ export function uploadUrl(): string { return `${conf.isMock ? "" : conf.serviceHost}/api/attachment/upload`; } /** 门户预览基地址({serviceHost}/portal/{siteCode}) */ export function portalBase(siteCode: string): string { return `${conf.isMock ? "" : conf.serviceHost}/portal/${siteCode}`; } /** * 后端下发的站内资产路径补 serviceHost 前缀(如主题截图 /api/theme/{name}/asset/screenshot.png)。 * 生产 context-path=/yschcms,裸 /api 绝对路径会打到网关根上 404(破图); * dev/mock 下 serviceHost 为空串,拼接后与原路径一致不受影响。 */ export function assetUrl(url: string): string { if (!url || /^(https?:|data:|blob:)/i.test(url)) return url; return `${conf.isMock ? "" : conf.serviceHost}${url}`; } class AuthApi { /** 登录态:用户 + 可管理站点 + 是否超管 */ getUser() { return get(`${AUTH}/user`); } } /** 平台代理域(Notice BaseApiController 同款契约) */ class BaseApi { /** 当前登录用户信息(CAS 会话注入,头像/姓名用于 layout) */ getUserInfo() { return get(`${BASE}/GetUserInfo`); } /** 当前用户已授权的模块列表(左侧菜单树) */ getUserMod() { return get(`${BASE}/GetUserMod`); } } class SchoolApi { /** 学校工作台列表(学校卡片 = 顶层站点聚合,docs/09 §3.3) */ list() { return get(`${SCHOOL}/list`); } /** UserBase 学校选项(添加学校绑定用,仅超管;平台不可达为空数组) */ options() { return get(`${SCHOOL}/options`); } } class SiteApi { list() { return get(`${SITE}/list`); } /** 入参对齐 SiteSaveRequest(themeParams 服务端合并进 config_json.theme_params,方案 §5.3) */ save(data: Partial & { themeParams?: Record }) { return postJSON(`${SITE}/save`, data); } } class CategoryApi { tree(siteId: number) { return get(`${CATEGORY}/tree`, { siteId }); } save(data: Partial) { return postJSON(`${CATEGORY}/save`, data); } delete(data: DeleteRequest) { return postJSON(`${CATEGORY}/delete`, data); } /** 子栏目排序(UserBase SortChildren 同语义:按传入顺序落库 sortNo=1..n) */ sort(data: { siteId: number; parentId: number; ids: number[] }) { return postJSON(`${CATEGORY}/sort`, data); } } class ArticleApi { page(query: ArticlePageQuery) { return get>(`${ARTICLE}/page`, { ...query }); } /** 编辑回显 */ get(siteId: number, id: number) { return get(`${ARTICLE}/get`, { siteId, id }); } /** 保存草稿(content 服务端 jsoup 清洗) */ save(data: ArticleSaveRequest) { return postJSON(`${ARTICLE}/save`, data); } /** 发布(publishTime 为未来时间即定时发布) */ publish(siteId: number, id: number) { return postJSON(`${ARTICLE}/publish/${id}`, undefined, { siteId }); } offline(siteId: number, id: number) { return postJSON(`${ARTICLE}/offline/${id}`, undefined, { siteId }); } topmost(siteId: number, id: number, enable: boolean) { return postJSON(`${ARTICLE}/topmost/${id}`, undefined, { siteId, enable }); } delete(data: DeleteRequest) { return postJSON(`${ARTICLE}/delete`, data); } } class AttachmentApi { page(siteId: number, orphanOnly: boolean, page: number, pageSize: number) { return get & { unusedCount: number }>(`${ATTACHMENT}/page`, { siteId, orphanOnly, page, pageSize, }); } delete(data: DeleteRequest) { return postJSON(`${ATTACHMENT}/delete`, data); } } class BannerApi { list(siteId: number, position?: string) { return get(`${BANNER}/list`, { siteId, position }); } save(data: Partial) { return postJSON(`${BANNER}/save`, data); } delete(data: DeleteRequest) { return postJSON(`${BANNER}/delete`, data); } } class LinkApi { list(siteId: number) { return get(`${LINK}/list`, { siteId }); } save(data: Partial) { return postJSON(`${LINK}/save`, data); } delete(data: DeleteRequest) { return postJSON(`${LINK}/delete`, data); } } class ThemeApi { /** 可用主题列表(含 params schema,建站向导/站点编辑共用,方案 §5.2) */ list() { return get(`${THEME}/list`); } /** W1:上传合规 zip(multipart,超管;免重启立即注册) */ upload(file: File) { const fd = new FormData(); fd.append("file", file); return alovaInstance.Post(`${THEME}/upload`, fd); } /** W1:从现有包派生新皮肤(复制包体+改名) */ derive(source: string, name: string) { return postJSON(`${THEME}/derive`, { source, name }); } /** W1:编辑外部包 theme.json 的 title/参数 schema */ updateMeta(name: string, data: { title?: string; params?: unknown[] }) { return alovaInstance.Put(`${THEME}/${name}/meta`, data, { headers: { "Content-Type": "application/json;charset=UTF-8" }, }); } /** W1:删除外部包(被用站点拒绝;内置不可删) */ remove(name: string) { return alovaInstance.Delete<{ ok: boolean }>(`${THEME}/${name}`); } /** W1:热重载(运维直放目录后生效) */ reload() { return postJSON(`${THEME}/reload`); } } /** 主题预览 iframe URL(方案 §5.5:/api/theme/preview,CAS 会话同源,参数 p_ 前缀)。 * W4:内容源站点参数 sitePath(嵌套路径语义,后端 siteSnapshotByPath 解析)。 */ export function themePreviewUrl(sitePath: string, theme: string, themeParams: Record = {}): string { const params = new URLSearchParams({ sitePath, theme, page: "home" }); Object.keys(themeParams).forEach(k => params.set(`p_${k}`, themeParams[k])); return `${conf.isMock ? "" : conf.serviceHost}/api/theme/preview?${params.toString()}`; } export const authApi = new AuthApi(); export const baseApi = new BaseApi(); export const schoolApi = new SchoolApi(); export const siteApi = new SiteApi(); export const categoryApi = new CategoryApi(); export const articleApi = new ArticleApi(); export const attachmentApi = new AttachmentApi(); export const bannerApi = new BannerApi(); export const linkApi = new LinkApi(); export const themeApi = new ThemeApi(); export default { authApi, baseApi, schoolApi, siteApi, categoryApi, articleApi, attachmentApi, bannerApi, linkApi, themeApi, uploadUrl, portalBase, themePreviewUrl, };