feat: 1. 将大部分的请求从fetch改成axios方便管理。
2. 给文档类型添加入口模块和相关数据的渲染。并且给文档类型进行功能上的角色权限区分 3. 新增角色权限管理页面
This commit is contained in:
@@ -607,6 +607,13 @@ function convertIcon(elementIcon: string | null): string {
|
||||
if (!elementIcon) {
|
||||
return 'ri-file-line'; // 默认图标
|
||||
}
|
||||
|
||||
// 如果已经是 RemixIcon 格式(以 ri- 开头),直接返回
|
||||
if (elementIcon.startsWith('ri-')) {
|
||||
return elementIcon;
|
||||
}
|
||||
|
||||
// 否则尝试从 Element UI 映射表中查找
|
||||
return ICON_MAPPING[elementIcon] || 'ri-file-line';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { postgrestGet, postgrestPut } from "../postgrest-client";
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* 从不同格式的 API 响应中提取数据
|
||||
@@ -134,26 +135,18 @@ export async function submitCrossCheckingOpinion(
|
||||
evaluation_result_id: opinionData.reviewPointResultId
|
||||
};
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/admin/cross_review/proposals`, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/cross_review/proposals`, requestData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || '提交失败');
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
success: true,
|
||||
message: '意见提交成功',
|
||||
data: data
|
||||
data: response.data
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -190,23 +183,19 @@ export async function getCrossCheckingOpinions(
|
||||
// 如果没传userId,默认用1
|
||||
const realUserId = userId ?? 1;
|
||||
// 实际后端API调用,拼接API_BASE_URL
|
||||
const response = await fetch(`${API_BASE_URL}/admin/cross_review/proposals/document`, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/cross_review/proposals/document`, {
|
||||
user_id: realUserId,
|
||||
document_id: documentId, // 如果后端需要document_id可以加上
|
||||
page,
|
||||
page_size: pageSize
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
user_id: realUserId,
|
||||
document_id: documentId, // 如果后端需要document_id可以加上
|
||||
page,
|
||||
page_size: pageSize
|
||||
})
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('获取意见列表失败');
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
const data = response.data;
|
||||
console.log('最原始的返回data', data);
|
||||
// 处理新的数据结构,支持分页
|
||||
const responseData = data.data || data;
|
||||
@@ -328,23 +317,24 @@ export async function performOpinionAction(
|
||||
throw new Error('无效的操作类型');
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: actionData.action === 'withdraw_opinion' ? 'DELETE' : 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
const response = actionData.action === 'withdraw_opinion'
|
||||
? await axios.delete(endpoint, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
: await axios.post(endpoint, requestBody, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const data = response.data;
|
||||
|
||||
console.log('返回的意见列表数据',data);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || data.error || '操作失败');
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
success: true,
|
||||
@@ -417,20 +407,15 @@ export async function checkProposalVotes(
|
||||
document_id: documentId
|
||||
};
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/admin/cross_review/proposals/document/check_pending_votes`, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/cross_review/proposals/document/check_pending_votes`, requestData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const data = response.data;
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.message || '检查失败');
|
||||
}
|
||||
console.log("检查投票数据",data);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UPLOAD_URL } from '../../config/api-config';
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* 从不同格式的 API 响应中提取数据
|
||||
@@ -146,8 +147,8 @@ export async function uploadCrossCheckingDocument(
|
||||
|
||||
// 发送请求
|
||||
try {
|
||||
console.log('【交叉评查上传】开始fetch请求...');
|
||||
const headers: HeadersInit = {
|
||||
console.log('【交叉评查上传】开始axios请求...');
|
||||
const headers: Record<string, string> = {
|
||||
'X-File-Name': encodeURIComponent(fileName),
|
||||
};
|
||||
|
||||
@@ -155,50 +156,35 @@ export async function uploadCrossCheckingDocument(
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData
|
||||
const response = await axios.post(uploadUrl, formData, {
|
||||
headers
|
||||
});
|
||||
|
||||
|
||||
console.log('【交叉评查上传】收到服务器响应:', { status: response.status, statusText: response.statusText });
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`【交叉评查上传】上传失败 (${response.status}): ${errorText}`);
|
||||
return {
|
||||
error: `上传失败: ${response.status} ${response.statusText} - ${errorText}`,
|
||||
status: response.status
|
||||
};
|
||||
}
|
||||
|
||||
console.log('【交叉评查上传】开始解析JSON响应');
|
||||
let responseData;
|
||||
try {
|
||||
responseData = await response.json();
|
||||
console.log('【交叉评查上传】JSON响应解析成功:', responseData);
|
||||
} catch (jsonError) {
|
||||
console.error('【交叉评查上传】JSON解析失败:', jsonError);
|
||||
return {
|
||||
error: `解析响应JSON失败: ${jsonError instanceof Error ? jsonError.message : '未知错误'}`,
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
|
||||
const extractedData = extractApiData<CrossCheckingFileUploadResponse>(responseData);
|
||||
|
||||
console.log('【交叉评查上传】JSON响应解析成功:', response.data);
|
||||
|
||||
const extractedData = extractApiData<CrossCheckingFileUploadResponse>(response.data);
|
||||
console.log('【交叉评查上传】提取的数据:', extractedData);
|
||||
|
||||
|
||||
if (!extractedData) {
|
||||
console.error('【交叉评查上传】无法提取数据');
|
||||
return { error: '处理上传响应失败', status: 500 };
|
||||
}
|
||||
|
||||
|
||||
console.log('【交叉评查上传】上传成功,返回数据');
|
||||
return { data: extractedData as CrossCheckingFileUploadResponse };
|
||||
} catch (fetchError) {
|
||||
console.error('【交叉评查上传】fetch请求失败:', fetchError);
|
||||
return {
|
||||
error: `fetch请求错误: ${fetchError instanceof Error ? fetchError.message : '未知错误'}`,
|
||||
} catch (axiosError) {
|
||||
console.error('【交叉评查上传】axios请求失败:', axiosError);
|
||||
if (axios.isAxiosError(axiosError)) {
|
||||
const errorText = axiosError.response?.data || axiosError.message;
|
||||
return {
|
||||
error: `上传失败: ${axiosError.response?.status || 500} ${axiosError.response?.statusText || ''} - ${errorText}`,
|
||||
status: axiosError.response?.status || 500
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: `axios请求错误: ${axiosError instanceof Error ? axiosError.message : '未知错误'}`,
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
@@ -258,14 +244,12 @@ export async function batchUploadAndAssignCrossCheckingFiles(
|
||||
};
|
||||
formData.append('upload_info', JSON.stringify(uploadInfo));
|
||||
formData.append('assign_user_ids', JSON.stringify(assignUserIds));
|
||||
const headers: HeadersInit = {};
|
||||
const headers: Record<string, string> = {};
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData
|
||||
const response = await axios.post(uploadUrl, formData, {
|
||||
headers
|
||||
});
|
||||
const result = await response.json();
|
||||
const result = response.data;
|
||||
if (result && result.success) {
|
||||
successes.push({ file: fileInfo, result });
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { API_BASE_URL } from '../../config/api-config';
|
||||
import { postgrestPut } from '../postgrest-client';
|
||||
import axios from 'axios';
|
||||
|
||||
// 交叉评查任务状态枚举
|
||||
export enum CrossCheckingTaskStatus {
|
||||
@@ -393,33 +394,28 @@ export async function getUserTaskDocuments(page: number = 1, pageSize: number =
|
||||
// 拼接绝对路径,去除多余斜杠
|
||||
const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;
|
||||
const url = `${base}/admin/cross_review/tasks/user_tasks`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${jwtToken || ''}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
page: page,
|
||||
page_size: pageSize
|
||||
})
|
||||
|
||||
const response = await axios.post(url, {
|
||||
page: page,
|
||||
page_size: pageSize
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${jwtToken || ''}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: `HTTP ${response.status}: ${response.statusText}`
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result
|
||||
data: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}`
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : '获取用户任务列表失败'
|
||||
@@ -441,33 +437,28 @@ export async function getTaskDocuments(taskId: number, page: number = 1, pageSiz
|
||||
const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;
|
||||
const url = `${base}/admin/cross_review/tasks/${taskId}/documents`;
|
||||
// console.log('最终请求URL:', url);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${jwtToken || ''}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
page: page,
|
||||
page_size: pageSize
|
||||
})
|
||||
|
||||
const response = await axios.post(url, {
|
||||
page: page,
|
||||
page_size: pageSize
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${jwtToken || ''}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: `HTTP ${response.status}: ${response.statusText}`
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result
|
||||
data: response.data
|
||||
};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
return {
|
||||
success: false,
|
||||
error: `HTTP ${error.response?.status}: ${error.response?.statusText || error.message}`
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : '获取任务文档列表失败'
|
||||
|
||||
@@ -25,6 +25,10 @@ export interface DocumentTypeUI {
|
||||
name: string;
|
||||
description: string;
|
||||
groups: DocumentTypeGroup[];
|
||||
entry_module?: {
|
||||
id: number;
|
||||
name: string;
|
||||
} | null;
|
||||
llm_extraction_template_id?: number | null;
|
||||
vlm_extraction_template_id?: number | null;
|
||||
evaluation_template_id?: number | null;
|
||||
@@ -39,6 +43,7 @@ export interface DocumentTypeCreateDTO {
|
||||
name: string;
|
||||
description?: string;
|
||||
group_ids: string[];
|
||||
entry_module_id?: number | null;
|
||||
llm_extraction_template_id?: number | null;
|
||||
vlm_extraction_template_id?: number | null;
|
||||
evaluation_template_id?: number | null;
|
||||
@@ -108,27 +113,27 @@ export async function getAllEvaluationPointGroups(token?: string): Promise<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>('evaluation_point_groups', params);
|
||||
|
||||
|
||||
if (response.error) {
|
||||
return { error: response.error, status: response.status };
|
||||
}
|
||||
|
||||
|
||||
// 使用extractApiData提取数据
|
||||
const extractedData = extractApiData<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>(response.data);
|
||||
|
||||
|
||||
if (!extractedData) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
|
||||
// 转换为DocumentTypeGroup格式
|
||||
const groups: DocumentTypeGroup[] = extractedData.map(item => ({
|
||||
id: item.id.toString(),
|
||||
name: item.name
|
||||
}));
|
||||
|
||||
|
||||
return { data: groups };
|
||||
} catch (error) {
|
||||
console.error('获取所有评查点分组失败:', error);
|
||||
@@ -136,6 +141,101 @@ export async function getAllEvaluationPointGroups(token?: string): Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父级评查分组(pid=0的分组)
|
||||
* @param token JWT token (可选)
|
||||
* @returns 父级评查点分组列表
|
||||
*/
|
||||
export async function getParentEvaluationPointGroups(token?: string): Promise<{
|
||||
data?: DocumentTypeGroup[];
|
||||
error?: string;
|
||||
status?: number;
|
||||
}> {
|
||||
try {
|
||||
const params: PostgrestParams = {
|
||||
select: 'id, name',
|
||||
filter: {
|
||||
'pid': 'eq.0'
|
||||
},
|
||||
order: 'id.asc',
|
||||
token
|
||||
};
|
||||
|
||||
const response = await postgrestGet<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>('evaluation_point_groups', params);
|
||||
|
||||
if (response.error) {
|
||||
return { error: response.error, status: response.status };
|
||||
}
|
||||
|
||||
// 使用extractApiData提取数据
|
||||
const extractedData = extractApiData<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>(response.data);
|
||||
|
||||
if (!extractedData) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
// 转换为DocumentTypeGroup格式
|
||||
const groups: DocumentTypeGroup[] = extractedData.map(item => ({
|
||||
id: item.id.toString(),
|
||||
name: item.name
|
||||
}));
|
||||
|
||||
return { data: groups };
|
||||
} catch (error) {
|
||||
console.error('获取父级评查点分组失败:', error);
|
||||
return { error: error instanceof Error ? error.message : '获取父级评查点分组失败' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有入口模块
|
||||
* @param token JWT token (可选)
|
||||
* @returns 入口模块列表
|
||||
*/
|
||||
export async function getEntryModules(token?: string): Promise<{
|
||||
data?: Array<{ id: number; name: string }>;
|
||||
error?: string;
|
||||
status?: number;
|
||||
}> {
|
||||
try {
|
||||
const params: PostgrestParams = {
|
||||
select: 'id, name',
|
||||
order: 'id.asc',
|
||||
token
|
||||
};
|
||||
|
||||
const response = await postgrestGet<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>('entry_modules', params);
|
||||
|
||||
if (response.error) {
|
||||
return { error: response.error, status: response.status };
|
||||
}
|
||||
|
||||
// 使用extractApiData提取数据
|
||||
const extractedData = extractApiData<Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
}>>(response.data);
|
||||
|
||||
if (!extractedData) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
return { data: extractedData };
|
||||
} catch (error) {
|
||||
console.error('获取入口模块失败:', error);
|
||||
return { error: error instanceof Error ? error.message : '获取入口模块失败' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取评查点分组信息
|
||||
* @param ids 评查点分组ID数组
|
||||
@@ -216,19 +316,22 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
|
||||
try {
|
||||
const page = searchParams.page || 1;
|
||||
const pageSize = searchParams.pageSize || 10;
|
||||
|
||||
// 构建查询参数
|
||||
|
||||
// 构建查询参数,使用 PostgREST 的资源嵌入语法来关联查询
|
||||
// 使用外键约束名称进行关联:entry_modules!fk_document_types_entry_module
|
||||
const params: PostgrestParams = {
|
||||
select: `
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
evaluation_point_groups_ids,
|
||||
entry_module_id,
|
||||
entry_modules!fk_document_types_entry_module(id, name),
|
||||
prompt_config,
|
||||
created_at,
|
||||
updated_at,
|
||||
code
|
||||
`,
|
||||
`.replace(/\s+/g,' ').trim(),
|
||||
order: 'updated_at.desc',
|
||||
headers: {
|
||||
'Prefer': 'count=exact'
|
||||
@@ -238,13 +341,13 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
|
||||
filter: {} as Record<string, string>,
|
||||
token: frontendJWT
|
||||
};
|
||||
|
||||
|
||||
// 添加筛选条件
|
||||
const filter: Record<string, string> = {};
|
||||
if (searchParams.name) {
|
||||
filter['name'] = `ilike.%${searchParams.name}%`;
|
||||
}
|
||||
|
||||
|
||||
// 如果有分组ID筛选条件
|
||||
if (searchParams.ruleType) {
|
||||
filter['evaluation_point_groups_ids'] = `cs.[${searchParams.ruleType}]`;
|
||||
@@ -264,32 +367,70 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
|
||||
}
|
||||
|
||||
params.filter = filter;
|
||||
|
||||
|
||||
// console.log('获取文档类型列表,参数:', params);
|
||||
const response = await postgrestGet<DocumentType[]>('document_types', params);
|
||||
|
||||
const response = await postgrestGet<(DocumentType & {
|
||||
entry_modules: { id: number; name: string } | null;
|
||||
})[]>('document_types', params);
|
||||
|
||||
if (response.error) {
|
||||
return { error: response.error, status: response.status };
|
||||
}
|
||||
|
||||
// 使用extractApiData提取数据
|
||||
const extractedData = extractApiData<DocumentType[]>(response.data);
|
||||
const documentTypes = extractedData || [];
|
||||
|
||||
// console.log('提取的文档类型数据:', documentTypes);
|
||||
|
||||
// 🔧 优化:移除评查点分组查询(文档列表UI不需要此数据)
|
||||
// 直接转换为UI类型,不查询关联的分组信息
|
||||
const uiTypes = documentTypes.map(type => ({
|
||||
...convertToUIDocumentType(type),
|
||||
groups: [] // 保持接口兼容性,但不填充数据
|
||||
}));
|
||||
|
||||
// console.log('提取的文档类型数据:', JSON.stringify(response));
|
||||
|
||||
// 使用extractApiData提取数据
|
||||
const extractedData = extractApiData<(DocumentType & {
|
||||
entry_modules: { id: number; name: string } | null;
|
||||
})[]>(response.data);
|
||||
const documentTypes = extractedData || [];
|
||||
|
||||
|
||||
// 并发查询所有需要的评查点分组信息
|
||||
const allGroupIds = new Set<number>();
|
||||
documentTypes.forEach(type => {
|
||||
if (type.evaluation_point_groups_ids) {
|
||||
const ids = Array.isArray(type.evaluation_point_groups_ids)
|
||||
? type.evaluation_point_groups_ids
|
||||
: [type.evaluation_point_groups_ids as unknown as number];
|
||||
ids.forEach(id => allGroupIds.add(id));
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有分组ID,查询所有分组信息
|
||||
let groupsMap: Map<number, DocumentTypeGroup> = new Map();
|
||||
if (allGroupIds.size > 0) {
|
||||
const groupsResponse = await getEvaluationPointGroupsByIds(Array.from(allGroupIds), frontendJWT);
|
||||
if (groupsResponse.data) {
|
||||
groupsResponse.data.forEach(group => {
|
||||
groupsMap.set(parseInt(group.id, 10), group);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为UI类型,包含entry_module和groups信息
|
||||
const uiTypes = documentTypes.map(type => {
|
||||
// 获取该文档类型关联的分组
|
||||
let typeGroups: DocumentTypeGroup[] = [];
|
||||
if (type.evaluation_point_groups_ids) {
|
||||
const ids = Array.isArray(type.evaluation_point_groups_ids)
|
||||
? type.evaluation_point_groups_ids
|
||||
: [type.evaluation_point_groups_ids as unknown as number];
|
||||
typeGroups = ids.map(id => groupsMap.get(id)).filter(Boolean) as DocumentTypeGroup[];
|
||||
}
|
||||
|
||||
return {
|
||||
...convertToUIDocumentType({ ...type, groups: typeGroups }),
|
||||
entry_module: type.entry_modules || null,
|
||||
groups: typeGroups
|
||||
};
|
||||
});
|
||||
|
||||
// 获取总数
|
||||
let totalCount = 0;
|
||||
const responseWithHeaders = response as {
|
||||
data: unknown;
|
||||
headers: Record<string, string>
|
||||
const responseWithHeaders = response as {
|
||||
data: unknown;
|
||||
headers: Record<string, string>
|
||||
};
|
||||
if (responseWithHeaders.headers) {
|
||||
const rangeHeader = responseWithHeaders.headers['content-range'];
|
||||
@@ -300,7 +441,7 @@ export async function getDocumentTypes(searchParams: DocumentTypeSearchParams =
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
data: {
|
||||
types: uiTypes,
|
||||
@@ -360,7 +501,10 @@ export async function deleteDocumentType(id: string, frontendJWT?: string): Prom
|
||||
/**
|
||||
* 将API返回的文档类型转换为UI文档类型
|
||||
*/
|
||||
function convertToUIDocumentType(type: DocumentType & { groups: DocumentTypeGroup[] }): DocumentTypeUI {
|
||||
function convertToUIDocumentType(type: DocumentType & {
|
||||
groups: DocumentTypeGroup[];
|
||||
entry_modules?: { id: number; name: string } | null;
|
||||
}): DocumentTypeUI {
|
||||
// 提取提示词模板ID,确保安全处理以避免控制台警告
|
||||
let llmExtractionTemplateId: number | null = null;
|
||||
let vlmExtractionTemplateId: number | null = null;
|
||||
@@ -396,6 +540,7 @@ function convertToUIDocumentType(type: DocumentType & { groups: DocumentTypeGrou
|
||||
name: type.name,
|
||||
description: type.description || '',
|
||||
groups: type.groups || [],
|
||||
entry_module: type.entry_modules || null,
|
||||
llm_extraction_template_id: llmExtractionTemplateId,
|
||||
vlm_extraction_template_id: vlmExtractionTemplateId,
|
||||
evaluation_template_id: evaluationTemplateId,
|
||||
@@ -428,18 +573,22 @@ export async function getDocumentType(id: string, frontendJWT?: string): Promise
|
||||
name,
|
||||
description,
|
||||
evaluation_point_groups_ids,
|
||||
entry_module_id,
|
||||
entry_modules!fk_document_types_entry_module(id, name),
|
||||
prompt_config,
|
||||
created_at,
|
||||
updated_at,
|
||||
code
|
||||
`,
|
||||
`.replace(/\s+/g,' ').trim(),
|
||||
filter: {
|
||||
'id': `eq.${id}`
|
||||
},
|
||||
token: frontendJWT
|
||||
};
|
||||
|
||||
const response = await postgrestGet<DocumentType[]>('document_types', params);
|
||||
const response = await postgrestGet<(DocumentType & {
|
||||
entry_modules: { id: number; name: string } | null;
|
||||
})[]>('document_types', params);
|
||||
|
||||
if (response.error) {
|
||||
return { error: response.error, status: response.status };
|
||||
@@ -573,6 +722,7 @@ export async function createDocumentType(documentType: DocumentTypeCreateDTO, fr
|
||||
name: documentType.name.trim(),
|
||||
description: documentType.description || '',
|
||||
evaluation_point_groups_ids: groupIds,
|
||||
entry_module_id: documentType.entry_module_id || null,
|
||||
prompt_config: promptConfig,
|
||||
// code: documentType.code || null
|
||||
};
|
||||
@@ -698,6 +848,7 @@ export async function updateDocumentType(id: string, documentType: DocumentTypeU
|
||||
name: documentType.name.trim(),
|
||||
description: documentType.description || '',
|
||||
evaluation_point_groups_ids: groupIds,
|
||||
entry_module_id: documentType.entry_module_id || null,
|
||||
prompt_config: promptConfig
|
||||
};
|
||||
|
||||
|
||||
@@ -802,19 +802,10 @@ export interface RuleGroup {
|
||||
*/
|
||||
export async function getRuleTypes(documentTypeIds?: number[], token?: string): Promise<{data: RuleType[]; error?: never} | {data?: never; error: string; status?: number}> {
|
||||
try {
|
||||
// 如果没有传入 documentTypeIds,返回空数组
|
||||
if (!documentTypeIds || documentTypeIds.length === 0) {
|
||||
console.warn('getRuleTypes: 未提供 documentTypeIds');
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
// 1️⃣ 根据 documentTypeIds 查询 document_types 表
|
||||
const typeIdsStr = documentTypeIds.join(',');
|
||||
const documentTypesParams: PostgrestParams = {
|
||||
select: 'id, name, evaluation_point_groups_ids',
|
||||
filter: {
|
||||
'id': `in.(${typeIdsStr})`
|
||||
},
|
||||
filter: {},
|
||||
token
|
||||
};
|
||||
|
||||
|
||||
@@ -384,42 +384,6 @@ export async function getDocumentWithNoUserId(id: string, frontendJWT?: string):
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件下载链接
|
||||
* @param filePath 文件路径
|
||||
* @returns 下载链接
|
||||
*/
|
||||
export async function getFileDownloadUrl(filePath: string): Promise<{
|
||||
data?: { downloadUrl: string };
|
||||
error?: string;
|
||||
status?: number;
|
||||
}> {
|
||||
try {
|
||||
if (!filePath) {
|
||||
return { error: '文件路径不能为空', status: 400 };
|
||||
}
|
||||
|
||||
// 这里应该调用获取文件下载链接的API
|
||||
// 假设后端有这样的端点:/api/files/generate-download-url?path=xxx
|
||||
// 实际项目中需要根据你的后端API调整
|
||||
|
||||
// 临时解决方案:返回Remix路由路径
|
||||
// 这将通过Remix服务器代理对文件的访问
|
||||
return {
|
||||
data: {
|
||||
downloadUrl: `/documents/download?path=${encodeURIComponent(filePath)}`
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取文件下载链接失败:', error);
|
||||
return {
|
||||
error: error instanceof Error ? error.message : '获取文件下载链接失败',
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档信息
|
||||
* @param id 文档ID
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { postgrestGet, type PostgrestParams } from '../postgrest-client';
|
||||
import dayjs from 'dayjs';
|
||||
import { UPLOAD_URL } from '../../config/api-config';
|
||||
import axios from 'axios';
|
||||
// import { API_BASE_URL } from '../client';
|
||||
|
||||
/**
|
||||
@@ -213,26 +214,15 @@ export async function uploadContractTemplate(
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData
|
||||
const response = await axios.post(uploadUrl, formData, {
|
||||
headers
|
||||
});
|
||||
|
||||
|
||||
console.log('【合同模板上传】服务器响应状态:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('【合同模板上传】服务器返回错误:', errorText);
|
||||
return {
|
||||
error: `服务器错误: ${response.status} ${response.statusText}`,
|
||||
status: response.status
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
const result = response.data;
|
||||
console.log('【合同模板上传】服务器返回结果:', result);
|
||||
|
||||
|
||||
if (result.success) {
|
||||
return { data: result.result };
|
||||
} else {
|
||||
@@ -299,26 +289,15 @@ export async function appendContractAttachments(
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData
|
||||
const response = await axios.post(uploadUrl, formData, {
|
||||
headers
|
||||
});
|
||||
|
||||
|
||||
console.log('【合同附件追加】服务器响应状态:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('【合同附件追加】服务器返回错误:', errorText);
|
||||
return {
|
||||
error: `服务器错误: ${response.status} ${response.statusText}`,
|
||||
status: response.status
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
const result = response.data;
|
||||
console.log('【合同附件追加】服务器返回结果:', result);
|
||||
|
||||
|
||||
if (result.success) {
|
||||
return { data: result.result };
|
||||
} else {
|
||||
@@ -388,12 +367,12 @@ export async function uploadDocumentToServer(
|
||||
// console.log('【调试】准备发送请求到服务器:', uploadUrl);
|
||||
|
||||
// 发送请求
|
||||
// const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, {
|
||||
// const response = await axios.post(`${API_BASE_URL}/admin/documents/upload`, ...
|
||||
try {
|
||||
// console.log('【调试】开始fetch请求...');
|
||||
// console.log('【调试】开始axios请求...');
|
||||
|
||||
// 构建请求头,只在有JWT token时添加Authorization
|
||||
const headers: HeadersInit = {
|
||||
const headers: Record<string, string> = {
|
||||
'X-File-Name': encodeURIComponent(fileName)
|
||||
};
|
||||
|
||||
@@ -401,37 +380,16 @@ export async function uploadDocumentToServer(
|
||||
headers['Authorization'] = `Bearer ${jwtToken}`;
|
||||
}
|
||||
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData
|
||||
const response = await axios.post(uploadUrl, formData, {
|
||||
headers
|
||||
});
|
||||
|
||||
|
||||
// console.log('【调试】收到服务器响应:', { status: response.status, statusText: response.statusText });
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`【调试】上传失败 (${response.status}): ${errorText}`);
|
||||
return {
|
||||
error: `上传失败: ${response.status} ${response.statusText} - ${errorText}`,
|
||||
status: response.status
|
||||
};
|
||||
}
|
||||
|
||||
// console.log('【调试】开始解析JSON响应');
|
||||
let responseData;
|
||||
try {
|
||||
responseData = await response.json();
|
||||
// console.log('【上传调试】服务器原始JSON响应:', responseData);
|
||||
// console.log('【上传调试】响应类型:', typeof responseData);
|
||||
// console.log('【上传调试】响应keys:', Object.keys(responseData));
|
||||
} catch (jsonError) {
|
||||
console.error('【调试】JSON解析失败:', jsonError);
|
||||
return {
|
||||
error: `解析响应JSON失败: ${jsonError instanceof Error ? jsonError.message : '未知错误'}`,
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
|
||||
const responseData = response.data;
|
||||
// console.log('【上传调试】服务器原始JSON响应:', responseData);
|
||||
// console.log('【上传调试】响应类型:', typeof responseData);
|
||||
// console.log('【上传调试】响应keys:', Object.keys(responseData));
|
||||
|
||||
const extractedData = extractApiData<FileUploadResponse>(responseData);
|
||||
// console.log('【上传调试】提取后的数据:', extractedData);
|
||||
@@ -449,10 +407,17 @@ export async function uploadDocumentToServer(
|
||||
|
||||
// console.log('【调试】上传成功,返回数据');
|
||||
return { data: extractedData };
|
||||
} catch (fetchError) {
|
||||
console.error('【调试】fetch请求失败:', fetchError);
|
||||
return {
|
||||
error: `fetch请求错误: ${fetchError instanceof Error ? fetchError.message : '未知错误'}`,
|
||||
} catch (axiosError) {
|
||||
console.error('【调试】axios请求失败:', axiosError);
|
||||
if (axios.isAxiosError(axiosError)) {
|
||||
const errorText = axiosError.response?.data || axiosError.message;
|
||||
return {
|
||||
error: `上传失败: ${axiosError.response?.status || 500} ${axiosError.response?.statusText || ''} - ${errorText}`,
|
||||
status: axiosError.response?.status || 500
|
||||
};
|
||||
}
|
||||
return {
|
||||
error: `axios请求错误: ${axiosError instanceof Error ? axiosError.message : '未知错误'}`,
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { createCookieSessionStorage } from "@remix-run/node";
|
||||
import { postgrestGet, postgrestPost, postgrestPut } from "../postgrest-client";
|
||||
import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt";
|
||||
import { OAUTH_CONFIG, API_BASE_URL } from "~/config/api-config";
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* 用户角色类型定义
|
||||
@@ -95,7 +96,7 @@ export const sessionStorage = createCookieSessionStorage({
|
||||
path: "/", // Cookie 作用域为整个应用
|
||||
sameSite: "lax", // CSRF 保护,允许顶级导航
|
||||
secrets: ["s3cr3t"], // TODO: 应该从环境变量读取
|
||||
maxAge: 60 * 60 * 2, // 2小时,与 OAuth Token 同步
|
||||
maxAge: 60 * 60 * 8, // 8小时,确保大于等于JWT token最大有效期(通常为6小时)
|
||||
secure: false, // 开发环境中禁用 HTTPS 要求
|
||||
},
|
||||
});
|
||||
@@ -369,12 +370,16 @@ export async function createUserSession(params: {
|
||||
if (params.frontendJWT) {
|
||||
session.set("frontendJWT", params.frontendJWT);
|
||||
}
|
||||
|
||||
const cookie = await sessionStorage.commitSession(session);
|
||||
// console.log("创建完整会话 - 设置Cookie:", !!cookie);
|
||||
// console.log("创建完整会话 - 用户角色:", params.userRole);
|
||||
// console.log("创建完整会话 - 重定向到:", params.redirectTo);
|
||||
|
||||
|
||||
// 🔑 根据 tokenExpiresIn 动态设置 Cookie 的 maxAge
|
||||
// 如果有 tokenExpiresIn,使用它作为 Cookie 有效期;否则使用默认值(8小时)
|
||||
const cookieMaxAge = params.tokenExpiresIn || (60 * 60 * 8); // 默认8小时
|
||||
// console.log("🍪 [createUserSession] Cookie maxAge:", cookieMaxAge, "秒 (", (cookieMaxAge / 3600).toFixed(2), "小时)");
|
||||
|
||||
const cookie = await sessionStorage.commitSession(session, {
|
||||
maxAge: cookieMaxAge // 🔑 动态设置 Cookie 有效期
|
||||
});
|
||||
|
||||
return new Response(null, {
|
||||
status: 302, // HTTP 重定向状态码
|
||||
headers: {
|
||||
@@ -487,20 +492,18 @@ async function callIDaaSLogout(accessToken: string, appId: string): Promise<void
|
||||
formData.append('redirect_url', encodeURIComponent(redirectUri));
|
||||
|
||||
try {
|
||||
const response = await fetch(logoutUrl, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(logoutUrl, formData.toString(), {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`IDaaS登出失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
console.log("IDaaS单点登出请求成功");
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
console.error("调用IDaaS登出接口失败:", error.response?.status, error.response?.statusText);
|
||||
throw new Error(`IDaaS登出失败: ${error.response?.status} ${error.response?.statusText}`);
|
||||
}
|
||||
console.error("调用IDaaS登出接口失败:", error);
|
||||
throw error;
|
||||
}
|
||||
@@ -775,18 +778,16 @@ export async function simpleRootLogin(
|
||||
}
|
||||
|
||||
// 调用登录接口
|
||||
const loginResponse = await fetch(`${API_BASE_URL}/password_login`, {
|
||||
method: 'POST',
|
||||
const loginResponse = await axios.post(`${API_BASE_URL}/password_login`, {
|
||||
sub: username.trim(),
|
||||
password: password.trim()
|
||||
}, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sub: username.trim(),
|
||||
password: password.trim()
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const loginResult = await loginResponse.json();
|
||||
const loginResult = loginResponse.data;
|
||||
console.log('登录接口返回', loginResult);
|
||||
|
||||
// 检查重试次数
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import { API_BASE_URL } from "~/config/api-config";
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* 登录请求参数(OAuth 方式)
|
||||
@@ -64,30 +65,26 @@ export async function loginWithOAuth(loginData: LoginRequest): Promise<LoginResp
|
||||
console.log("📝 [Login Client] 调用后端 OAuth 登录接口:", loginUrl);
|
||||
|
||||
try {
|
||||
const response = await fetch(loginUrl, {
|
||||
method: "POST",
|
||||
const response = await axios.post(loginUrl, loginData, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
},
|
||||
body: JSON.stringify(loginData)
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error("❌ [Login Client] OAuth 登录请求失败:", response.status, errorData);
|
||||
console.log("✅ [Login Client] OAuth 登录成功");
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const errorData = error.response?.data || {};
|
||||
console.error("❌ [Login Client] OAuth 登录请求失败:", error.response?.status, errorData);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorData.error || errorData.message || `登录失败: ${response.status}`
|
||||
error: errorData.error || errorData.message || `登录失败: ${error.response?.status || 'Unknown'}`
|
||||
};
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("✅ [Login Client] OAuth 登录成功");
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("❌ [Login Client] OAuth 登录请求异常:", error);
|
||||
return {
|
||||
success: false,
|
||||
@@ -120,33 +117,29 @@ export async function loginWithPassword(
|
||||
console.log("📝 [Login Client] 调用后端密码登录接口:", loginUrl);
|
||||
|
||||
try {
|
||||
const response = await fetch(loginUrl, {
|
||||
method: "POST",
|
||||
const response = await axios.post(loginUrl, {
|
||||
username,
|
||||
password
|
||||
}, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.error("❌ [Login Client] 密码登录请求失败:", response.status, errorData);
|
||||
console.log("✅ [Login Client] 密码登录成功");
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const errorData = error.response?.data || {};
|
||||
console.error("❌ [Login Client] 密码登录请求失败:", error.response?.status, errorData);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorData.error || errorData.message || `登录失败: ${response.status}`
|
||||
error: errorData.error || errorData.message || `登录失败: ${error.response?.status || 'Unknown'}`
|
||||
};
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("✅ [Login Client] 密码登录成功");
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("❌ [Login Client] 密码登录请求异常:", error);
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* 2. 如果需要新的网络请求,在 `OAuthClient` 中添加
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
interface OAuthConfig {
|
||||
serverUrl: string;
|
||||
clientId: string;
|
||||
@@ -114,46 +116,38 @@ export class OAuthClient {
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(url, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
timeout: 60000 // 60秒超时
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
console.log('🔧 Token响应状态:', response.status, response.statusText);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('❌ 获取访问令牌失败:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
errorData: errorData
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
const tokenResponse = await response.json() as TokenResponse;
|
||||
const tokenResponse = response.data as TokenResponse;
|
||||
console.log('✅ 获取访问令牌成功:', {
|
||||
token_type: tokenResponse.token_type,
|
||||
expires_in: tokenResponse.expires_in,
|
||||
scope: tokenResponse.scope
|
||||
});
|
||||
|
||||
|
||||
return tokenResponse;
|
||||
} catch (error) {
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 获取访问令牌超时(15秒):', error.message);
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
console.error('❌ 获取访问令牌超时(60秒):', error.message);
|
||||
} else if (error.response) {
|
||||
console.error('❌ 获取访问令牌失败:', {
|
||||
status: error.response.status,
|
||||
statusText: error.response.statusText,
|
||||
errorData: error.response.data
|
||||
});
|
||||
} else {
|
||||
console.error('❌ 获取访问令牌网络错误:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.error('❌ 获取访问令牌网络错误:', error);
|
||||
console.error('❌ 获取访问令牌错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -168,31 +162,25 @@ export class OAuthClient {
|
||||
const url = `${this.config.serverUrl}/api/bff/v1.2/oauth2/userinfo`;
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
const response = await axios.get(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
signal: controller.signal
|
||||
timeout: 60000 // 60秒超时
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('获取用户信息失败:', response.status, response.statusText);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await response.json() as UserInfoResponse;
|
||||
return response.data as UserInfoResponse;
|
||||
} catch (error) {
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 获取用户信息超时(15秒):', error.message);
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
console.error('❌ 获取用户信息超时(60秒):', error.message);
|
||||
} else if (error.response) {
|
||||
console.error('获取用户信息失败:', error.response.status, error.response.statusText);
|
||||
} else {
|
||||
console.error('❌ 获取用户信息网络错误:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.error('❌ 获取用户信息网络错误:', error);
|
||||
console.error('❌ 获取用户信息错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -219,34 +207,25 @@ export class OAuthClient {
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(url, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
timeout: 60000 // 60秒超时
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('刷新访问令牌失败:', errorData);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await response.json() as TokenResponse;
|
||||
return response.data as TokenResponse;
|
||||
} catch (error) {
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 刷新访问令牌超时(15秒):', error.message);
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
console.error('❌ 刷新访问令牌超时(60秒):', error.message);
|
||||
} else if (error.response) {
|
||||
console.error('刷新访问令牌失败:', error.response.data);
|
||||
} else {
|
||||
console.error('❌ 刷新访问令牌网络错误:', error.message);
|
||||
}
|
||||
} else {
|
||||
console.error('❌ 刷新访问令牌网络错误:', error);
|
||||
console.error('❌ 刷新访问令牌错误:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -266,25 +245,21 @@ export class OAuthClient {
|
||||
});
|
||||
|
||||
try {
|
||||
// 创建 AbortController 用于超时控制
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15秒超时
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
const response = await axios.post(url, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: data,
|
||||
signal: controller.signal
|
||||
timeout: 60000 // 60秒超时
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
return response.ok;
|
||||
return response.status >= 200 && response.status < 300;
|
||||
} catch (error) {
|
||||
// 判断是否为超时错误
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.error('❌ 登出超时(15秒):', error.message);
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
console.error('❌ 登出超时(60秒):', error.message);
|
||||
} else {
|
||||
console.error('❌ 登出失败:', error);
|
||||
}
|
||||
} else {
|
||||
console.error('❌ 登出失败:', error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/**
|
||||
* 角色权限管理 API
|
||||
* 用于角色、路由权限、用户角色的管理
|
||||
*/
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
/**
|
||||
* 路由信息
|
||||
*/
|
||||
export interface RouteInfo {
|
||||
id: number;
|
||||
route_path: string;
|
||||
route_name: string;
|
||||
route_title: string;
|
||||
component?: string;
|
||||
parent_id?: number | null;
|
||||
icon?: string;
|
||||
sort_order: number;
|
||||
is_hidden: boolean;
|
||||
is_cache: boolean;
|
||||
status: number;
|
||||
children?: RouteInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
*/
|
||||
export interface RoleInfo {
|
||||
id: number;
|
||||
role_key: string;
|
||||
role_name: string;
|
||||
data_scope: string;
|
||||
description: string;
|
||||
parent_role_id?: number | null;
|
||||
priority: number;
|
||||
is_system_role: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色-路由权限关联
|
||||
*/
|
||||
export interface RoleRoutePermission {
|
||||
id: number;
|
||||
role_id: number;
|
||||
route_id: number;
|
||||
permission: string; // 'R' | 'RW' | 'NONE'
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
export interface UserInfo {
|
||||
id: number;
|
||||
username: string;
|
||||
nick_name: string;
|
||||
phone_number?: string;
|
||||
email?: string;
|
||||
ou_name: string;
|
||||
status: number;
|
||||
is_leader: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户-角色关联
|
||||
*/
|
||||
export interface UserRoleRelation {
|
||||
id: number;
|
||||
user_id: number;
|
||||
role_id: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// ==================== 模拟数据 ====================
|
||||
|
||||
/**
|
||||
* 模拟路由数据(树形结构)
|
||||
*/
|
||||
const mockRoutes: RouteInfo[] = [
|
||||
{
|
||||
id: 1,
|
||||
route_path: '/documents',
|
||||
route_name: 'documents',
|
||||
route_title: '文档管理',
|
||||
icon: 'ri-file-text-line',
|
||||
sort_order: 1,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: null,
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
route_path: '/documents/list',
|
||||
route_name: 'documents-list',
|
||||
route_title: '文档列表',
|
||||
icon: 'ri-list-check',
|
||||
sort_order: 1,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 1
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
route_path: '/documents/upload',
|
||||
route_name: 'documents-upload',
|
||||
route_title: '文档上传',
|
||||
icon: 'ri-upload-line',
|
||||
sort_order: 2,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
route_path: '/cross-checking',
|
||||
route_name: 'cross-checking',
|
||||
route_title: '交叉评查',
|
||||
icon: 'ri-exchange-line',
|
||||
sort_order: 2,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: null,
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
route_path: '/cross-checking/tasks',
|
||||
route_name: 'cross-checking-tasks',
|
||||
route_title: '评查任务',
|
||||
icon: 'ri-task-line',
|
||||
sort_order: 1,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
route_path: '/settings',
|
||||
route_name: 'settings',
|
||||
route_title: '系统设置',
|
||||
icon: 'ri-settings-3-line',
|
||||
sort_order: 3,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: null,
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
route_path: '/settings/document-types',
|
||||
route_name: 'document-types',
|
||||
route_title: '文档类型管理',
|
||||
icon: 'ri-file-list-line',
|
||||
sort_order: 1,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 3
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
route_path: '/settings/rule-groups',
|
||||
route_name: 'rule-groups',
|
||||
route_title: '评查点分组',
|
||||
icon: 'ri-folder-line',
|
||||
sort_order: 2,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 3
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
route_path: '/settings/prompts',
|
||||
route_name: 'prompts',
|
||||
route_title: '提示词管理',
|
||||
icon: 'ri-message-line',
|
||||
sort_order: 3,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
route_path: '/role-permissions',
|
||||
route_name: 'role-permissions',
|
||||
route_title: '角色权限管理',
|
||||
icon: 'ri-shield-user-line',
|
||||
sort_order: 4,
|
||||
is_hidden: false,
|
||||
is_cache: true,
|
||||
status: 1,
|
||||
parent_id: null
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* 模拟角色数据
|
||||
*/
|
||||
const mockRoles: RoleInfo[] = [
|
||||
{
|
||||
id: 1,
|
||||
role_key: 'admin',
|
||||
role_name: '系统管理员',
|
||||
data_scope: 'ALL',
|
||||
description: '拥有系统所有权限',
|
||||
priority: 1,
|
||||
is_system_role: true,
|
||||
created_at: '2024-01-01 10:00:00',
|
||||
updated_at: '2024-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
role_key: 'provincial',
|
||||
role_name: '省级管理员',
|
||||
data_scope: 'PROVINCE',
|
||||
description: '省级权限,可管理文档类型和评查点',
|
||||
priority: 2,
|
||||
is_system_role: false,
|
||||
created_at: '2024-01-02 10:00:00',
|
||||
updated_at: '2024-01-02 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
role_key: 'city_admin',
|
||||
role_name: '市级管理员',
|
||||
data_scope: 'CITY',
|
||||
description: '市级权限,可管理本市文档',
|
||||
priority: 3,
|
||||
is_system_role: false,
|
||||
created_at: '2024-01-03 10:00:00',
|
||||
updated_at: '2024-01-03 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
role_key: 'common_user',
|
||||
role_name: '普通用户',
|
||||
data_scope: 'SELF',
|
||||
description: '普通用户,只能查看自己的文档',
|
||||
priority: 4,
|
||||
is_system_role: false,
|
||||
created_at: '2024-01-04 10:00:00',
|
||||
updated_at: '2024-01-04 10:00:00'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
role_key: 'reviewer',
|
||||
role_name: '评审员',
|
||||
data_scope: 'DEPARTMENT',
|
||||
description: '负责文档评审工作',
|
||||
priority: 5,
|
||||
is_system_role: false,
|
||||
created_at: '2024-01-05 10:00:00',
|
||||
updated_at: '2024-01-05 10:00:00'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* 模拟角色-路由权限关联数据
|
||||
*/
|
||||
const mockRoleRoutePermissions: RoleRoutePermission[] = [
|
||||
// 系统管理员拥有所有权限
|
||||
{ id: 1, role_id: 1, route_id: 1, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 2, role_id: 1, route_id: 11, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 3, role_id: 1, route_id: 12, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 4, role_id: 1, route_id: 2, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 5, role_id: 1, route_id: 21, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 6, role_id: 1, route_id: 3, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 7, role_id: 1, route_id: 31, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 8, role_id: 1, route_id: 32, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 9, role_id: 1, route_id: 33, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 10, role_id: 1, route_id: 4, permission: 'RW', created_at: '2024-01-01 10:00:00' },
|
||||
|
||||
// 省级管理员
|
||||
{ id: 11, role_id: 2, route_id: 1, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 12, role_id: 2, route_id: 11, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 13, role_id: 2, route_id: 12, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 14, role_id: 2, route_id: 3, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 15, role_id: 2, route_id: 31, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 16, role_id: 2, route_id: 32, permission: 'RW', created_at: '2024-01-02 10:00:00' },
|
||||
|
||||
// 普通用户
|
||||
{ id: 17, role_id: 4, route_id: 1, permission: 'R', created_at: '2024-01-04 10:00:00' },
|
||||
{ id: 18, role_id: 4, route_id: 11, permission: 'R', created_at: '2024-01-04 10:00:00' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 模拟用户数据
|
||||
*/
|
||||
const mockUsers: UserInfo[] = [
|
||||
{
|
||||
id: 1,
|
||||
username: 'admin',
|
||||
nick_name: '系统管理员',
|
||||
phone_number: '13800138000',
|
||||
email: 'admin@example.com',
|
||||
ou_name: '系统管理部',
|
||||
status: 1,
|
||||
is_leader: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: 'zhangsan',
|
||||
nick_name: '张三',
|
||||
phone_number: '13800138001',
|
||||
email: 'zhangsan@example.com',
|
||||
ou_name: '广东省局',
|
||||
status: 1,
|
||||
is_leader: true
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: 'lisi',
|
||||
nick_name: '李四',
|
||||
phone_number: '13800138002',
|
||||
email: 'lisi@example.com',
|
||||
ou_name: '梅州市局',
|
||||
status: 1,
|
||||
is_leader: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
username: 'wangwu',
|
||||
nick_name: '王五',
|
||||
phone_number: '13800138003',
|
||||
email: 'wangwu@example.com',
|
||||
ou_name: '云浮市局',
|
||||
status: 1,
|
||||
is_leader: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
username: 'zhaoliu',
|
||||
nick_name: '赵六',
|
||||
phone_number: '13800138004',
|
||||
email: 'zhaoliu@example.com',
|
||||
ou_name: '揭阳市局',
|
||||
status: 1,
|
||||
is_leader: false
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* 模拟用户-角色关联数据
|
||||
*/
|
||||
const mockUserRoles: UserRoleRelation[] = [
|
||||
{ id: 1, user_id: 1, role_id: 1, created_at: '2024-01-01 10:00:00' },
|
||||
{ id: 2, user_id: 2, role_id: 2, created_at: '2024-01-02 10:00:00' },
|
||||
{ id: 3, user_id: 3, role_id: 3, created_at: '2024-01-03 10:00:00' },
|
||||
{ id: 4, user_id: 4, role_id: 4, created_at: '2024-01-04 10:00:00' },
|
||||
{ id: 5, user_id: 5, role_id: 5, created_at: '2024-01-05 10:00:00' }
|
||||
];
|
||||
|
||||
// ==================== API 函数 ====================
|
||||
|
||||
/**
|
||||
* 获取所有角色列表
|
||||
*/
|
||||
export async function getRoles(): Promise<RoleInfo[]> {
|
||||
// 模拟网络延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockRoles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有路由(树形结构)
|
||||
*/
|
||||
export async function getRoutes(): Promise<RouteInfo[]> {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定角色的路由权限
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
export async function getRoleRoutePermissions(roleId: number): Promise<RoleRoutePermission[]> {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
return mockRoleRoutePermissions.filter(p => p.role_id === roleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色的路由权限
|
||||
* @param roleId 角色ID
|
||||
* @param routeIds 路由ID数组
|
||||
*/
|
||||
export async function updateRoleRoutePermissions(
|
||||
roleId: number,
|
||||
routeIds: number[]
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
// 在实际应用中,这里会调用后端API
|
||||
console.log('更新角色权限:', { roleId, routeIds });
|
||||
|
||||
// 模拟更新本地数据
|
||||
// 删除该角色的旧权限
|
||||
const oldPermissions = mockRoleRoutePermissions.filter(p => p.role_id === roleId);
|
||||
oldPermissions.forEach(p => {
|
||||
const index = mockRoleRoutePermissions.indexOf(p);
|
||||
if (index > -1) {
|
||||
mockRoleRoutePermissions.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加新权限
|
||||
routeIds.forEach((routeId, index) => {
|
||||
mockRoleRoutePermissions.push({
|
||||
id: Date.now() + index,
|
||||
role_id: roleId,
|
||||
route_id: routeId,
|
||||
permission: 'RW',
|
||||
created_at: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
return { success: true, message: '角色权限更新成功' };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定角色的用户列表
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
export async function getRoleUsers(roleId: number): Promise<UserInfo[]> {
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
// 查找具有该角色的用户ID
|
||||
const userIds = mockUserRoles
|
||||
.filter(ur => ur.role_id === roleId)
|
||||
.map(ur => ur.user_id);
|
||||
|
||||
// 返回用户详细信息
|
||||
return mockUsers.filter(u => userIds.includes(u.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有用户列表
|
||||
*/
|
||||
export async function getAllUsers(): Promise<UserInfo[]> {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为用户分配角色
|
||||
* @param userId 用户ID
|
||||
* @param roleIds 角色ID数组
|
||||
*/
|
||||
export async function assignUserRoles(
|
||||
userId: number,
|
||||
roleIds: number[]
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
console.log('为用户分配角色:', { userId, roleIds });
|
||||
|
||||
// 模拟更新本地数据
|
||||
// 删除该用户的旧角色
|
||||
const oldRoles = mockUserRoles.filter(ur => ur.user_id === userId);
|
||||
oldRoles.forEach(ur => {
|
||||
const index = mockUserRoles.indexOf(ur);
|
||||
if (index > -1) {
|
||||
mockUserRoles.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加新角色
|
||||
roleIds.forEach((roleId, index) => {
|
||||
mockUserRoles.push({
|
||||
id: Date.now() + index,
|
||||
user_id: userId,
|
||||
role_id: roleId,
|
||||
created_at: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
return { success: true, message: '用户角色分配成功' };
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新角色
|
||||
* @param roleData 角色数据
|
||||
*/
|
||||
export async function createRole(
|
||||
roleData: Omit<RoleInfo, 'id' | 'created_at' | 'updated_at'>
|
||||
): Promise<{ success: boolean; message: string; data?: RoleInfo }> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
const newRole: RoleInfo = {
|
||||
...roleData,
|
||||
id: Math.max(...mockRoles.map(r => r.id)) + 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
mockRoles.push(newRole);
|
||||
|
||||
return { success: true, message: '角色创建成功', data: newRole };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色信息
|
||||
* @param roleId 角色ID
|
||||
* @param roleData 角色数据
|
||||
*/
|
||||
export async function updateRole(
|
||||
roleId: number,
|
||||
roleData: Partial<Omit<RoleInfo, 'id' | 'created_at' | 'updated_at'>>
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
const roleIndex = mockRoles.findIndex(r => r.id === roleId);
|
||||
if (roleIndex === -1) {
|
||||
return { success: false, message: '角色不存在' };
|
||||
}
|
||||
|
||||
mockRoles[roleIndex] = {
|
||||
...mockRoles[roleIndex],
|
||||
...roleData,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
return { success: true, message: '角色更新成功' };
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
export async function deleteRole(roleId: number): Promise<{ success: boolean; message: string }> {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
const role = mockRoles.find(r => r.id === roleId);
|
||||
if (!role) {
|
||||
return { success: false, message: '角色不存在' };
|
||||
}
|
||||
|
||||
if (role.is_system_role) {
|
||||
return { success: false, message: '系统角色不能删除' };
|
||||
}
|
||||
|
||||
const roleIndex = mockRoles.indexOf(role);
|
||||
mockRoles.splice(roleIndex, 1);
|
||||
|
||||
return { success: true, message: '角色删除成功' };
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { get } from '../axios-client';
|
||||
import { API_BASE_URL } from '../../config/api-config';
|
||||
import axios from 'axios';
|
||||
|
||||
// 用户信息接口
|
||||
export interface UserInfo {
|
||||
@@ -56,24 +57,16 @@ export async function getOrganizationTree(includeUsers: boolean = true, jwtToken
|
||||
let responseData: OrganizationResponse;
|
||||
|
||||
if (jwtToken) {
|
||||
// 如果提供了JWT Token,则使用fetch并携带Authorization头
|
||||
// 如果提供了JWT Token,则使用axios并携带Authorization头
|
||||
const url = `${API_BASE_URL}/admin/users/organizations?include_users=${includeUsers}`;
|
||||
const response = await fetch(url, {
|
||||
const response = await axios.get(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${jwtToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('获取组织架构失败 (fetch):', errorText);
|
||||
return {
|
||||
success: false,
|
||||
error: `HTTP error! status: ${response.status}, ${errorText}`
|
||||
};
|
||||
}
|
||||
responseData = await response.json();
|
||||
responseData = response.data;
|
||||
} else {
|
||||
// 否则,使用原有的get方法
|
||||
const response = await get<OrganizationResponse>(
|
||||
|
||||
Reference in New Issue
Block a user