u
This commit is contained in:
@@ -1,343 +0,0 @@
|
|||||||
import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
||||||
import { MinioProgressCallbacks, UploadResult } from "@/share/types";
|
|
||||||
import { filesClient } from "../api";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface UploadPart {
|
|
||||||
ETag: string;
|
|
||||||
PartNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UploadProgressDetails {
|
|
||||||
partNumber: number;
|
|
||||||
totalParts: number;
|
|
||||||
partSize: number;
|
|
||||||
totalSize: number;
|
|
||||||
partProgress?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type MinioMultipartUploadPolicy = InferResponseType<typeof filesClient["multipart-policy"]['$post'],200>
|
|
||||||
type MinioUploadPolicy = InferResponseType<typeof filesClient["policy"]['$post'],200>
|
|
||||||
|
|
||||||
|
|
||||||
const PART_SIZE = 5 * 1024 * 1024; // 每部分5MB
|
|
||||||
|
|
||||||
|
|
||||||
export class MinIOXHRMultipartUploader {
|
|
||||||
/**
|
|
||||||
* 使用XHR分段上传文件到MinIO
|
|
||||||
*/
|
|
||||||
static async upload(
|
|
||||||
policy: MinioMultipartUploadPolicy,
|
|
||||||
file: File | Blob,
|
|
||||||
key: string,
|
|
||||||
callbacks?: MinioProgressCallbacks
|
|
||||||
): Promise<UploadResult> {
|
|
||||||
const partSize = PART_SIZE;
|
|
||||||
const totalSize = file.size;
|
|
||||||
const totalParts = Math.ceil(totalSize / partSize);
|
|
||||||
const uploadedParts: UploadPart[] = [];
|
|
||||||
|
|
||||||
callbacks?.onProgress?.({
|
|
||||||
stage: 'uploading',
|
|
||||||
message: '准备上传文件...',
|
|
||||||
progress: 0,
|
|
||||||
details: {
|
|
||||||
loaded: 0,
|
|
||||||
total: totalSize
|
|
||||||
},
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
|
|
||||||
// 分段上传
|
|
||||||
for (let i = 0; i < totalParts; i++) {
|
|
||||||
const start = i * partSize;
|
|
||||||
const end = Math.min(start + partSize, totalSize);
|
|
||||||
const partBlob = file.slice(start, end);
|
|
||||||
const partNumber = i + 1;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const etag = await this.uploadPart(
|
|
||||||
policy.partUrls[i],
|
|
||||||
partBlob,
|
|
||||||
callbacks,
|
|
||||||
{
|
|
||||||
partNumber,
|
|
||||||
totalParts,
|
|
||||||
partSize: partBlob.size,
|
|
||||||
totalSize
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
uploadedParts.push({
|
|
||||||
ETag: etag,
|
|
||||||
PartNumber: partNumber
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新进度
|
|
||||||
const progress = Math.round((end / totalSize) * 100);
|
|
||||||
callbacks?.onProgress?.({
|
|
||||||
stage: 'uploading',
|
|
||||||
message: `上传文件片段 ${partNumber}/${totalParts}`,
|
|
||||||
progress,
|
|
||||||
details: {
|
|
||||||
loaded: end,
|
|
||||||
total: totalSize,
|
|
||||||
},
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 完成上传
|
|
||||||
try {
|
|
||||||
await this.completeMultipartUpload(policy, key, uploadedParts);
|
|
||||||
|
|
||||||
callbacks?.onProgress?.({
|
|
||||||
stage: 'complete',
|
|
||||||
message: '文件上传完成',
|
|
||||||
progress: 100,
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
|
|
||||||
callbacks?.onComplete?.();
|
|
||||||
return {
|
|
||||||
fileUrl: `${policy.host}/${key}`,
|
|
||||||
fileKey: key,
|
|
||||||
bucketName: policy.bucket
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
callbacks?.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 上传单个片段
|
|
||||||
private static uploadPart(
|
|
||||||
uploadUrl: string,
|
|
||||||
partBlob: Blob,
|
|
||||||
callbacks?: MinioProgressCallbacks,
|
|
||||||
progressDetails?: UploadProgressDetails
|
|
||||||
): Promise<string> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
xhr.upload.onprogress = (event) => {
|
|
||||||
if (event.lengthComputable && callbacks?.onProgress) {
|
|
||||||
const partProgress = Math.round((event.loaded / event.total) * 100);
|
|
||||||
callbacks.onProgress({
|
|
||||||
stage: 'uploading',
|
|
||||||
message: `上传文件片段 ${progressDetails?.partNumber}/${progressDetails?.totalParts} (${partProgress}%)`,
|
|
||||||
progress: Math.round((
|
|
||||||
(progressDetails?.partNumber ? (progressDetails.partNumber - 1) * (progressDetails.partSize || 0) : 0) + event.loaded
|
|
||||||
) / (progressDetails?.totalSize || 1) * 100),
|
|
||||||
details: {
|
|
||||||
...progressDetails,
|
|
||||||
loaded: event.loaded,
|
|
||||||
total: event.total
|
|
||||||
},
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onload = () => {
|
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
|
||||||
// 获取ETag(MinIO返回的标识)
|
|
||||||
const etag = xhr.getResponseHeader('ETag')?.replace(/"/g, '') || '';
|
|
||||||
resolve(etag);
|
|
||||||
} else {
|
|
||||||
reject(new Error(`上传片段失败: ${xhr.status} ${xhr.statusText}`));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onerror = () => reject(new Error('上传片段失败'));
|
|
||||||
|
|
||||||
xhr.open('PUT', uploadUrl);
|
|
||||||
xhr.send(partBlob);
|
|
||||||
|
|
||||||
if (callbacks?.signal) {
|
|
||||||
callbacks.signal.addEventListener('abort', () => {
|
|
||||||
xhr.abort();
|
|
||||||
reject(new Error('上传已取消'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 完成分段上传
|
|
||||||
private static async completeMultipartUpload(
|
|
||||||
policy: MinioMultipartUploadPolicy,
|
|
||||||
key: string,
|
|
||||||
uploadedParts: UploadPart[]
|
|
||||||
): Promise<void> {
|
|
||||||
const response = await filesClient["complete-multipart-upload"].$post({
|
|
||||||
json:{
|
|
||||||
bucket: policy.bucket,
|
|
||||||
key,
|
|
||||||
uploadId: policy.uploadId,
|
|
||||||
parts: uploadedParts
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`完成分段上传失败: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MinIOXHRUploader {
|
|
||||||
/**
|
|
||||||
* 使用XHR上传文件到MinIO
|
|
||||||
*/
|
|
||||||
static upload(
|
|
||||||
policy: MinioUploadPolicy,
|
|
||||||
file: File | Blob,
|
|
||||||
key: string,
|
|
||||||
callbacks?: MinioProgressCallbacks
|
|
||||||
): Promise<UploadResult> {
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
// 添加 MinIO 需要的字段
|
|
||||||
Object.entries(policy).forEach(([k, value]) => {
|
|
||||||
// 排除 policy 中的 key、host、prefix、ossType 字段
|
|
||||||
if (k !== 'key' && k !== 'host' && k !== 'prefix' && k !== 'ossType' && typeof value === 'string') {
|
|
||||||
formData.append(k, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 添加 自定义 key 字段
|
|
||||||
formData.append('key', key);
|
|
||||||
formData.append('file', file);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
// 上传进度处理
|
|
||||||
if (callbacks?.onProgress) {
|
|
||||||
xhr.upload.onprogress = (event) => {
|
|
||||||
if (event.lengthComputable) {
|
|
||||||
callbacks.onProgress?.({
|
|
||||||
stage: 'uploading',
|
|
||||||
message: '正在上传文件...',
|
|
||||||
progress: Math.round((event.loaded * 100) / event.total),
|
|
||||||
details: {
|
|
||||||
loaded: event.loaded,
|
|
||||||
total: event.total
|
|
||||||
},
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 完成处理
|
|
||||||
xhr.onload = () => {
|
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
|
||||||
if (callbacks?.onProgress) {
|
|
||||||
callbacks.onProgress({
|
|
||||||
stage: 'complete',
|
|
||||||
message: '文件上传完成',
|
|
||||||
progress: 100,
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
callbacks?.onComplete?.();
|
|
||||||
resolve({
|
|
||||||
fileUrl:`${policy.host}/${key}`,
|
|
||||||
fileKey: key,
|
|
||||||
bucketName: policy.bucket
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const error = new Error(`上传失败: ${xhr.status} ${xhr.statusText}`);
|
|
||||||
callbacks?.onError?.(error);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 错误处理
|
|
||||||
xhr.onerror = () => {
|
|
||||||
const error = new Error('上传失败');
|
|
||||||
if (callbacks?.onProgress) {
|
|
||||||
callbacks.onProgress({
|
|
||||||
stage: 'error',
|
|
||||||
message: '文件上传失败',
|
|
||||||
progress: 0,
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
callbacks?.onError?.(error);
|
|
||||||
reject(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 根据当前页面协议和 host 配置决定最终的上传地址
|
|
||||||
const currentProtocol = typeof window !== 'undefined' ? window.location.protocol : 'https:';
|
|
||||||
const host = policy.host?.startsWith('http')
|
|
||||||
? policy.host
|
|
||||||
: `${currentProtocol}//${policy.host}`;
|
|
||||||
// 开始上传
|
|
||||||
xhr.open('POST', host);
|
|
||||||
xhr.send(formData);
|
|
||||||
|
|
||||||
// 处理取消
|
|
||||||
if (callbacks?.signal) {
|
|
||||||
callbacks.signal.addEventListener('abort', () => {
|
|
||||||
xhr.abort();
|
|
||||||
reject(new Error('上传已取消'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUploadPolicy(key: string): Promise<MinioUploadPolicy> {
|
|
||||||
const policyResponse = await filesClient.policy.$post({
|
|
||||||
json: { key }
|
|
||||||
});
|
|
||||||
if (!policyResponse.ok) {
|
|
||||||
throw new Error('获取上传策略失败');
|
|
||||||
}
|
|
||||||
return policyResponse.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMultipartUploadPolicy(totalSize: number,fileKey: string) {
|
|
||||||
const policyResponse = await filesClient["multipart-policy"].$post({
|
|
||||||
json: { totalSize, partSize: PART_SIZE, fileKey}
|
|
||||||
});
|
|
||||||
if (!policyResponse.ok) {
|
|
||||||
throw new Error('获取分段上传策略失败');
|
|
||||||
}
|
|
||||||
return await policyResponse.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function uploadMinIOWithPolicy(
|
|
||||||
uploadPath: string,
|
|
||||||
file: File | Blob,
|
|
||||||
fileKey: string,
|
|
||||||
callbacks?: MinioProgressCallbacks
|
|
||||||
): Promise<UploadResult> {
|
|
||||||
if(uploadPath === '/') uploadPath = '';
|
|
||||||
else{
|
|
||||||
if(!uploadPath.endsWith('/')) uploadPath = `${uploadPath}/`
|
|
||||||
// 去掉开头的 /
|
|
||||||
if(uploadPath.startsWith('/')) uploadPath = uploadPath.replace(/^\//, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const key = `${uploadPath}${fileKey}`
|
|
||||||
if( file.size > PART_SIZE ){
|
|
||||||
const policy = await getMultipartUploadPolicy(file.size, key)
|
|
||||||
return MinIOXHRMultipartUploader.upload(
|
|
||||||
policy,
|
|
||||||
file,
|
|
||||||
key,
|
|
||||||
callbacks
|
|
||||||
);
|
|
||||||
}else{
|
|
||||||
const policy = await getUploadPolicy(key);
|
|
||||||
return MinIOXHRUploader.upload(policy, file, key, callbacks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,587 +1,15 @@
|
|||||||
export enum OssType {
|
|
||||||
ALIYUN = 'aliyun',
|
|
||||||
MINIO = 'minio',
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全局配置常量
|
// 全局配置常量
|
||||||
export interface GlobalConfig {
|
export interface GlobalConfig {
|
||||||
OSS_BASE_URL: string;
|
OSS_BASE_URL: string;
|
||||||
OSS_TYPE: OssType;
|
|
||||||
API_BASE_URL: string;
|
|
||||||
APP_NAME: string;
|
APP_NAME: string;
|
||||||
ENV: string;
|
|
||||||
DEFAULT_THEME: string;
|
|
||||||
MAP_CONFIG: {
|
|
||||||
KEY: string;
|
|
||||||
VERSION: string;
|
|
||||||
PLUGINS: string[];
|
|
||||||
MAP_MODE: MapMode;
|
|
||||||
};
|
|
||||||
CHART_THEME: string;
|
|
||||||
ENABLE_THEME_CONFIG: boolean;
|
|
||||||
THEME: ThemeSettings | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface Role {
|
|
||||||
id: number,
|
|
||||||
name: string,
|
|
||||||
description?: string | null,
|
|
||||||
permissions: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 定义类型
|
|
||||||
export interface User {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
nickname: string | null;
|
|
||||||
email: string | null;
|
|
||||||
phone: string | null;
|
|
||||||
roles?: Role[];
|
|
||||||
avatar?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MenuItem {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
icon: string;
|
|
||||||
component: string;
|
|
||||||
parent_id?: number;
|
|
||||||
children?: MenuItem[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 认证上下文类型
|
// 认证上下文类型
|
||||||
export interface AuthContextType {
|
export interface AuthContextType<T> {
|
||||||
user: User | null;
|
user: T | null;
|
||||||
token: string | null;
|
token: string | null;
|
||||||
login: (username: string, password: string, latitude?: number, longitude?: number) => Promise<void>;
|
login: (username: string, password: string, latitude?: number, longitude?: number) => Promise<void>;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主题上下文类型
|
|
||||||
export interface ThemeContextType {
|
|
||||||
isDark: boolean;
|
|
||||||
currentTheme: ThemeSettings;
|
|
||||||
updateTheme: (theme: Partial<ThemeSettings>) => void; // 实时预览
|
|
||||||
saveTheme: (theme: Partial<ThemeSettings>) => Promise<ThemeSettings>; // 保存到后端
|
|
||||||
resetTheme: () => Promise<ThemeSettings>;
|
|
||||||
toggleTheme: () => void; // 切换主题模式
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主题模式枚举
|
|
||||||
export enum ThemeMode {
|
|
||||||
LIGHT = 'light',
|
|
||||||
DARK = 'dark'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字体大小枚举
|
|
||||||
export enum FontSize {
|
|
||||||
SMALL = 'small',
|
|
||||||
MEDIUM = 'medium',
|
|
||||||
LARGE = 'large'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 紧凑模式枚举
|
|
||||||
export enum CompactMode {
|
|
||||||
NORMAL = 0, // 正常模式
|
|
||||||
COMPACT = 1 // 紧凑模式
|
|
||||||
}
|
|
||||||
|
|
||||||
// 颜色方案类型
|
|
||||||
export interface ColorScheme {
|
|
||||||
name: string;
|
|
||||||
primary: string;
|
|
||||||
background: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主题设置类型
|
|
||||||
export interface ThemeSettings {
|
|
||||||
/** 主键ID */
|
|
||||||
// id?: number;
|
|
||||||
|
|
||||||
/** 用户ID */
|
|
||||||
// user_id: number;
|
|
||||||
|
|
||||||
/** 主题模式(light/dark) */
|
|
||||||
theme_mode: ThemeMode;
|
|
||||||
|
|
||||||
/** 主题方案名称 */
|
|
||||||
scheme_name?: string;
|
|
||||||
|
|
||||||
/** 主题主色 */
|
|
||||||
primary_color: string;
|
|
||||||
|
|
||||||
/** 背景色 */
|
|
||||||
background_color?: string;
|
|
||||||
|
|
||||||
/** 文字颜色 */
|
|
||||||
text_color?: string;
|
|
||||||
|
|
||||||
/** 边框圆角 */
|
|
||||||
border_radius?: number;
|
|
||||||
|
|
||||||
/** 字体大小(small/medium/large) */
|
|
||||||
font_size: FontSize;
|
|
||||||
|
|
||||||
/** 是否紧凑模式(0否 1是) */
|
|
||||||
is_compact: CompactMode;
|
|
||||||
|
|
||||||
/** 创建时间 */
|
|
||||||
// created_at?: string;
|
|
||||||
|
|
||||||
// /** 更新时间 */
|
|
||||||
// updated_at?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启用/禁用状态枚举
|
|
||||||
export enum EnableStatus {
|
|
||||||
DISABLED = 0, // 禁用
|
|
||||||
ENABLED = 1 // 启用
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启用/禁用状态中文映射
|
|
||||||
export const EnableStatusNameMap: Record<EnableStatus, string> = {
|
|
||||||
[EnableStatus.DISABLED]: '禁用',
|
|
||||||
[EnableStatus.ENABLED]: '启用'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除状态枚举
|
|
||||||
export enum DeleteStatus {
|
|
||||||
NOT_DELETED = 0, // 未删除
|
|
||||||
DELETED = 1 // 已删除
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除状态中文映射
|
|
||||||
export const DeleteStatusNameMap: Record<DeleteStatus, string> = {
|
|
||||||
[DeleteStatus.NOT_DELETED]: '未删除',
|
|
||||||
[DeleteStatus.DELETED]: '已删除'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 地图类型
|
|
||||||
export enum MapMode {
|
|
||||||
ONLINE = 'online',
|
|
||||||
OFFLINE = 'offline'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 地图标记数据接口 - 基础定义
|
|
||||||
export interface MarkerData {
|
|
||||||
/** 标记点经度 */
|
|
||||||
longitude: number;
|
|
||||||
|
|
||||||
/** 标记点纬度 */
|
|
||||||
latitude: number;
|
|
||||||
|
|
||||||
/** 标记点ID */
|
|
||||||
id?: string | number;
|
|
||||||
|
|
||||||
/** 标记点标题 */
|
|
||||||
title?: string;
|
|
||||||
|
|
||||||
/** 标记点描述 */
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
/** 标记点图标URL */
|
|
||||||
iconUrl?: string;
|
|
||||||
|
|
||||||
/** 标记点状态 */
|
|
||||||
status?: string;
|
|
||||||
|
|
||||||
/** 标记点类型 */
|
|
||||||
type?: string;
|
|
||||||
|
|
||||||
/** 标记点额外数据 */
|
|
||||||
extraData?: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 审核状态枚举
|
|
||||||
export enum AuditStatus {
|
|
||||||
PENDING = 0, // 待审核
|
|
||||||
APPROVED = 1, // 已通过
|
|
||||||
REJECTED = 2 // 已拒绝
|
|
||||||
}
|
|
||||||
|
|
||||||
// 审核状态中文映射
|
|
||||||
export const AuditStatusNameMap: Record<AuditStatus, string> = {
|
|
||||||
[AuditStatus.PENDING]: '待审核',
|
|
||||||
[AuditStatus.APPROVED]: '已通过',
|
|
||||||
[AuditStatus.REJECTED]: '已拒绝'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 图标类型映射
|
|
||||||
type IconType = 'dashboard' | 'user' | 'setting' | 'team' | 'book' | 'calendar' | 'pie-chart' | 'database';
|
|
||||||
|
|
||||||
// 图标类型中文映射
|
|
||||||
export const IconTypeNameMap: Record<IconType, string> = {
|
|
||||||
'dashboard': '仪表盘',
|
|
||||||
'user': '用户',
|
|
||||||
'setting': '设置',
|
|
||||||
'team': '团队',
|
|
||||||
'book': '文档',
|
|
||||||
'calendar': '日历',
|
|
||||||
'pie-chart': '图表',
|
|
||||||
'database': '数据库'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 附件类型定义
|
|
||||||
export interface Attachment {
|
|
||||||
/** 附件ID */
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
/** 附件名称 */
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/** 附件访问地址 */
|
|
||||||
url: string;
|
|
||||||
|
|
||||||
/** 附件类型(如image/jpeg, application/pdf等) */
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
/** 附件大小(字节) */
|
|
||||||
size: number;
|
|
||||||
|
|
||||||
/** 上传时间 */
|
|
||||||
upload_time: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 操作日志表
|
|
||||||
export interface OperationLog {
|
|
||||||
/** 主键ID */
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
/** 操作人ID */
|
|
||||||
operator_id: number;
|
|
||||||
|
|
||||||
/** 操作类型 */
|
|
||||||
operation_type: string;
|
|
||||||
|
|
||||||
/** 操作内容 */
|
|
||||||
operation_content?: string;
|
|
||||||
|
|
||||||
/** 操作结果 */
|
|
||||||
operation_result?: string;
|
|
||||||
|
|
||||||
/** 操作IP */
|
|
||||||
ip_address?: string;
|
|
||||||
|
|
||||||
/** 是否删除 (0否 1是) */
|
|
||||||
is_deleted?: number;
|
|
||||||
|
|
||||||
/** 创建时间 */
|
|
||||||
created_at: Date;
|
|
||||||
|
|
||||||
/** 更新时间 */
|
|
||||||
updated_at: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统设置分组
|
|
||||||
export enum SystemSettingGroup {
|
|
||||||
BASIC = 'basic', // 基础设置
|
|
||||||
FEATURE = 'feature', // 功能设置
|
|
||||||
UPLOAD = 'upload', // 上传设置
|
|
||||||
NOTIFICATION = 'notify' // 通知设置
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统设置键
|
|
||||||
export enum SystemSettingKey {
|
|
||||||
// 基础设置
|
|
||||||
SITE_NAME = 'SITE_NAME', // 站点名称
|
|
||||||
SITE_DESCRIPTION = 'SITE_DESCRIPTION', // 站点描述
|
|
||||||
SITE_KEYWORDS = 'SITE_KEYWORDS', // 站点关键词
|
|
||||||
SITE_LOGO = 'SITE_LOGO', // 站点LOGO
|
|
||||||
SITE_FAVICON = 'SITE_FAVICON', // 站点图标
|
|
||||||
|
|
||||||
// 功能设置
|
|
||||||
ENABLE_REGISTER = 'ENABLE_REGISTER', // 是否开启注册
|
|
||||||
ENABLE_CAPTCHA = 'ENABLE_CAPTCHA', // 是否开启验证码
|
|
||||||
LOGIN_ATTEMPTS = 'LOGIN_ATTEMPTS', // 登录尝试次数
|
|
||||||
SESSION_TIMEOUT = 'SESSION_TIMEOUT', // 会话超时时间(分钟)
|
|
||||||
|
|
||||||
// 上传设置
|
|
||||||
UPLOAD_MAX_SIZE = 'UPLOAD_MAX_SIZE', // 最大上传大小(MB)
|
|
||||||
ALLOWED_FILE_TYPES = 'ALLOWED_FILE_TYPES', // 允许的文件类型
|
|
||||||
IMAGE_COMPRESS = 'IMAGE_COMPRESS', // 是否压缩图片
|
|
||||||
IMAGE_MAX_WIDTH = 'IMAGE_MAX_WIDTH', // 图片最大宽度
|
|
||||||
|
|
||||||
// 通知设置
|
|
||||||
NOTIFY_ON_LOGIN = 'NOTIFY_ON_LOGIN', // 登录通知
|
|
||||||
NOTIFY_ON_UPLOAD = 'NOTIFY_ON_UPLOAD', // 上传通知
|
|
||||||
NOTIFY_ON_ERROR = 'NOTIFY_ON_ERROR', // 错误通知
|
|
||||||
SMS_API_URL = 'SMS_API_URL', // SMS接口URL
|
|
||||||
SMS_API_AUTH = 'SMS_API_AUTH', // SMS接口认证头
|
|
||||||
SMS_API_TIMEOUT = 'SMS_API_TIMEOUT', // SMS接口超时时间(毫秒)
|
|
||||||
SMS_API_RETRY = 'SMS_API_RETRY', // SMS接口重试次数
|
|
||||||
|
|
||||||
// 主题设置
|
|
||||||
ENABLE_THEME_CONFIG = 'ENABLE_THEME_CONFIG' // 是否开启主题配置
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SystemSettingGroupType = SystemSettingGroup;
|
|
||||||
export type SystemSettingKeyType = SystemSettingKey;
|
|
||||||
|
|
||||||
// 系统设置值类型
|
|
||||||
export type SystemSettingValue = string | number | boolean;
|
|
||||||
|
|
||||||
// 系统设置项接口
|
|
||||||
export interface SystemSetting {
|
|
||||||
id: number;
|
|
||||||
key: SystemSettingKeyType; // 设置键
|
|
||||||
value: SystemSettingValue; // 设置值
|
|
||||||
description?: string; // 设置描述
|
|
||||||
group: SystemSettingGroupType; // 设置分组
|
|
||||||
created_at?: string;
|
|
||||||
updated_at?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统设置分组类型
|
|
||||||
export interface SystemSettingGroupData {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
settings: SystemSetting[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统设置记录类型
|
|
||||||
export type SystemSettingRecord = Record<SystemSettingKey, SystemSettingValue>;
|
|
||||||
|
|
||||||
// 允许的文件类型枚举
|
|
||||||
export enum AllowedFileType {
|
|
||||||
JPG = 'jpg',
|
|
||||||
JPEG = 'jpeg',
|
|
||||||
PNG = 'png',
|
|
||||||
GIF = 'gif',
|
|
||||||
DOC = 'doc',
|
|
||||||
DOCX = 'docx',
|
|
||||||
XLS = 'xls',
|
|
||||||
XLSX = 'xlsx',
|
|
||||||
PDF = 'pdf'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 允许的文件类型列表(用于系统设置)
|
|
||||||
export const ALLOWED_FILE_TYPES = Object.values(AllowedFileType).join(',');
|
|
||||||
|
|
||||||
// 文件库接口
|
|
||||||
export interface FileLibrary {
|
|
||||||
/** 主键ID */
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
/** 文件名称 */
|
|
||||||
file_name: string;
|
|
||||||
|
|
||||||
/** 原始文件名 */
|
|
||||||
original_filename?: string;
|
|
||||||
|
|
||||||
/** 文件路径 */
|
|
||||||
file_path: string;
|
|
||||||
|
|
||||||
/** 文件类型 */
|
|
||||||
file_type: string;
|
|
||||||
|
|
||||||
/** 文件大小(字节) */
|
|
||||||
file_size: number;
|
|
||||||
|
|
||||||
/** 上传用户ID */
|
|
||||||
uploader_id?: number;
|
|
||||||
|
|
||||||
/** 上传者名称 */
|
|
||||||
uploader_name?: string;
|
|
||||||
|
|
||||||
/** 文件分类 */
|
|
||||||
category_id?: number;
|
|
||||||
|
|
||||||
/** 文件标签 */
|
|
||||||
tags?: string;
|
|
||||||
|
|
||||||
/** 文件描述 */
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
/** 下载次数 */
|
|
||||||
download_count: number;
|
|
||||||
|
|
||||||
/** 是否禁用 (0否 1是) */
|
|
||||||
is_disabled?: EnableStatus;
|
|
||||||
|
|
||||||
/** 是否被删除 (0否 1是) */
|
|
||||||
is_deleted?: DeleteStatus;
|
|
||||||
|
|
||||||
/** 创建时间 */
|
|
||||||
created_at: string;
|
|
||||||
|
|
||||||
/** 更新时间 */
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 文件分类接口
|
|
||||||
export interface FileCategory {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
code: string;
|
|
||||||
description?: string;
|
|
||||||
is_deleted?: DeleteStatus;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 知识库表
|
|
||||||
export interface KnowInfo {
|
|
||||||
/** 主键ID */
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
/** 标题 */
|
|
||||||
title: string;
|
|
||||||
|
|
||||||
/** 内容 */
|
|
||||||
content?: string;
|
|
||||||
|
|
||||||
/** 作者 */
|
|
||||||
author?: string;
|
|
||||||
|
|
||||||
/** 分类 */
|
|
||||||
category: string;
|
|
||||||
|
|
||||||
/** 标签 */
|
|
||||||
tags?: string;
|
|
||||||
|
|
||||||
/** 封面图片URL */
|
|
||||||
cover_url?: string;
|
|
||||||
|
|
||||||
/** 审核状态 */
|
|
||||||
audit_status?: number;
|
|
||||||
|
|
||||||
/** 排序权重 */
|
|
||||||
sort_order?: number;
|
|
||||||
|
|
||||||
/** 是否删除 (0否 1是) */
|
|
||||||
is_deleted?: number;
|
|
||||||
|
|
||||||
/** 创建时间 */
|
|
||||||
created_at: string;
|
|
||||||
|
|
||||||
/** 更新时间 */
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登录位置相关类型定义
|
|
||||||
export interface LoginLocation {
|
|
||||||
id: number;
|
|
||||||
loginTime: string;
|
|
||||||
ipAddress: string;
|
|
||||||
longitude: number;
|
|
||||||
latitude: number;
|
|
||||||
location_name?: string;
|
|
||||||
user: {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
nickname: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LoginLocationDetail {
|
|
||||||
id: number;
|
|
||||||
user_id: number;
|
|
||||||
login_time: string;
|
|
||||||
ip_address: string;
|
|
||||||
longitude: number;
|
|
||||||
latitude: number;
|
|
||||||
location_name: string;
|
|
||||||
user_agent: string;
|
|
||||||
user: {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
nickname: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息类型枚举
|
|
||||||
export enum MessageType {
|
|
||||||
SYSTEM = 'system', // 系统通知
|
|
||||||
PRIVATE = 'private', // 私信
|
|
||||||
ANNOUNCE = 'announce' // 公告
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息状态枚举
|
|
||||||
export enum MessageStatus {
|
|
||||||
UNREAD = 0, // 未读
|
|
||||||
READ = 1, // 已读
|
|
||||||
DELETED = 2 // 已删除
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息状态中文映射
|
|
||||||
export const MessageStatusNameMap: Record<MessageStatus, string> = {
|
|
||||||
[MessageStatus.UNREAD]: '未读',
|
|
||||||
[MessageStatus.READ]: '已读',
|
|
||||||
[MessageStatus.DELETED]: '已删除'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 消息实体接口
|
|
||||||
export interface Message {
|
|
||||||
id: number;
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
type: MessageType;
|
|
||||||
sender_id?: number; // 发送者ID(系统消息可为空)
|
|
||||||
sender_name?: string; // 发送者名称
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户消息关联接口
|
|
||||||
export interface UserMessage {
|
|
||||||
id: number;
|
|
||||||
user_id: number;
|
|
||||||
message_id: number;
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
user_status: MessageStatus;
|
|
||||||
user_message_id: number;
|
|
||||||
is_deleted?: DeleteStatus;
|
|
||||||
read_at?: string;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
|
|
||||||
// 关联信息
|
|
||||||
message?: Message;
|
|
||||||
sender?: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MinioUploadPolicy {
|
|
||||||
'x-amz-algorithm': string;
|
|
||||||
'x-amz-credential': string;
|
|
||||||
'x-amz-date': string;
|
|
||||||
'x-amz-security-token'?: string;
|
|
||||||
policy: string;
|
|
||||||
'x-amz-signature': string;
|
|
||||||
host: string;
|
|
||||||
prefix: string;
|
|
||||||
bucket: string;
|
|
||||||
// key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MinioProgressEvent {
|
|
||||||
stage: 'uploading' | 'complete' | 'error';
|
|
||||||
message: string;
|
|
||||||
progress: number;
|
|
||||||
details?: {
|
|
||||||
loaded: number;
|
|
||||||
total: number;
|
|
||||||
};
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MinioProgressCallbacks {
|
|
||||||
onProgress?: (event: MinioProgressEvent) => void;
|
|
||||||
onComplete?: () => void;
|
|
||||||
onError?: (error: Error) => void;
|
|
||||||
signal?: AbortSignal;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UploadResult {
|
|
||||||
fileUrl:string;
|
|
||||||
fileKey:string;
|
|
||||||
bucketName:string;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user