Files
YschcmsFront/src/api/module/cms.ts
T

251 lines
7.4 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.
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<T>(url: string, params?: IObj): Method {
return alovaInstance.Get<T>(url, { params });
}
/** POST JSON(@RequestBody);params 为追加到 URL 的 query 参数(alova Post config 同 Get) */
function postJSON<T>(url: string, data?: unknown, params?: IObj): Method {
return alovaInstance.Post<T>(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<AuthUserInfo>(`${AUTH}/user`);
}
}
/** 平台代理域(Notice BaseApiController 同款契约) */
class BaseApi {
/** 当前登录用户信息(CAS 会话注入,头像/姓名用于 layout) */
getUserInfo() {
return get<any>(`${BASE}/GetUserInfo`);
}
/** 当前用户已授权的模块列表(左侧菜单树) */
getUserMod() {
return get<any[]>(`${BASE}/GetUserMod`);
}
}
class SchoolApi {
/** 学校工作台列表(学校卡片 = 顶层站点聚合,docs/09 §3.3) */
list() {
return get<SchoolCard[]>(`${SCHOOL}/list`);
}
/** UserBase 学校选项(添加学校绑定用,仅超管;平台不可达为空数组) */
options() {
return get<SchoolOption[]>(`${SCHOOL}/options`);
}
}
class SiteApi {
list() {
return get<SiteVo[]>(`${SITE}/list`);
}
/** 入参对齐 SiteSaveRequest(themeParams 服务端合并进 config_json.theme_params,方案 §5.3) */
save(data: Partial<SiteVo> & { themeParams?: Record<string, string> }) {
return postJSON<SiteVo>(`${SITE}/save`, data);
}
}
class CategoryApi {
tree(siteId: number) {
return get<CategoryTreeNode[]>(`${CATEGORY}/tree`, { siteId });
}
save(data: Partial<CategoryTreeNode>) {
return postJSON<CategoryTreeNode>(`${CATEGORY}/save`, data);
}
delete(data: DeleteRequest) {
return postJSON<void>(`${CATEGORY}/delete`, data);
}
/** 子栏目排序(UserBase SortChildren 同语义:按传入顺序落库 sortNo=1..n) */
sort(data: { siteId: number; parentId: number; ids: number[] }) {
return postJSON<void>(`${CATEGORY}/sort`, data);
}
}
class ArticleApi {
page(query: ArticlePageQuery) {
return get<PageResult<ArticlePageItem>>(`${ARTICLE}/page`, { ...query });
}
/** 编辑回显 */
get(siteId: number, id: number) {
return get<ArticleEditVo>(`${ARTICLE}/get`, { siteId, id });
}
/** 保存草稿(content 服务端 jsoup 清洗) */
save(data: ArticleSaveRequest) {
return postJSON<number>(`${ARTICLE}/save`, data);
}
/** 发布(publishTime 为未来时间即定时发布) */
publish(siteId: number, id: number) {
return postJSON<void>(`${ARTICLE}/publish/${id}`, undefined, { siteId });
}
offline(siteId: number, id: number) {
return postJSON<void>(`${ARTICLE}/offline/${id}`, undefined, { siteId });
}
topmost(siteId: number, id: number, enable: boolean) {
return postJSON<void>(`${ARTICLE}/topmost/${id}`, undefined, { siteId, enable });
}
delete(data: DeleteRequest) {
return postJSON<void>(`${ARTICLE}/delete`, data);
}
}
class AttachmentApi {
page(siteId: number, orphanOnly: boolean, page: number, pageSize: number) {
return get<PageResult<AttachmentVo> & { unusedCount: number }>(`${ATTACHMENT}/page`, {
siteId,
orphanOnly,
page,
pageSize,
});
}
delete(data: DeleteRequest) {
return postJSON<void>(`${ATTACHMENT}/delete`, data);
}
}
class BannerApi {
list(siteId: number, position?: string) {
return get<BannerVo[]>(`${BANNER}/list`, { siteId, position });
}
save(data: Partial<BannerVo>) {
return postJSON<BannerVo>(`${BANNER}/save`, data);
}
delete(data: DeleteRequest) {
return postJSON<void>(`${BANNER}/delete`, data);
}
}
class LinkApi {
list(siteId: number) {
return get<LinkVo[]>(`${LINK}/list`, { siteId });
}
save(data: Partial<LinkVo>) {
return postJSON<LinkVo>(`${LINK}/save`, data);
}
delete(data: DeleteRequest) {
return postJSON<void>(`${LINK}/delete`, data);
}
}
class ThemeApi {
/** 可用主题列表(含 params schema,建站向导/站点编辑共用,方案 §5.2) */
list() {
return get<ThemeVo[]>(`${THEME}/list`);
}
}
/** 主题预览 iframe URL(方案 §5.5:/api/theme/preview,CAS 会话同源,参数 p_ 前缀)。
* W4:内容源站点参数 sitePath(嵌套路径语义,后端 siteSnapshotByPath 解析)。 */
export function themePreviewUrl(sitePath: string, theme: string, themeParams: Record<string, string> = {}): 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,
};