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 {
|
||||
OSS_BASE_URL: string;
|
||||
OSS_TYPE: OssType;
|
||||
API_BASE_URL: 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 {
|
||||
user: User | null;
|
||||
export interface AuthContextType<T> {
|
||||
user: T | null;
|
||||
token: string | null;
|
||||
login: (username: string, password: string, latitude?: number, longitude?: number) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
isAuthenticated: 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