fix: 1. 修复角色权限管理报错误提示的问题
This commit is contained in:
+280
-2
@@ -9,9 +9,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { type MetaFunction } from "@remix-run/node";
|
import { type MetaFunction } from "@remix-run/node";
|
||||||
import { useState, useRef } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { DiffEditor } from "@monaco-editor/react";
|
import { DiffEditor } from "@monaco-editor/react";
|
||||||
import type { editor } from "monaco-editor";
|
import type { editor } from "monaco-editor";
|
||||||
|
import { pdfjs } from 'react-pdf';
|
||||||
|
import { toastService } from '~/components/ui/Toast';
|
||||||
|
|
||||||
|
// 设置 PDF.js worker(与 pdf-demo.tsx 相同)
|
||||||
|
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
|
||||||
|
|
||||||
export const meta: MetaFunction = () => {
|
export const meta: MetaFunction = () => {
|
||||||
return [
|
return [
|
||||||
@@ -20,6 +25,17 @@ export const meta: MetaFunction = () => {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PDF 类型枚举
|
||||||
|
type PdfType = 'text' | 'scanned' | 'unknown';
|
||||||
|
|
||||||
|
// PDF 信息接口
|
||||||
|
interface PdfInfo {
|
||||||
|
type: PdfType;
|
||||||
|
numPages: number;
|
||||||
|
textLength: number;
|
||||||
|
confidence: number; // 文本提取置信度 (0-1)
|
||||||
|
}
|
||||||
|
|
||||||
// 示例合同文本 A(原始版本)
|
// 示例合同文本 A(原始版本)
|
||||||
const CONTRACT_A = `中国烟草合同(原始版本)
|
const CONTRACT_A = `中国烟草合同(原始版本)
|
||||||
|
|
||||||
@@ -104,6 +120,107 @@ export default function MonacoDemoPage() {
|
|||||||
const [diffCount, setDiffCount] = useState<number>(0);
|
const [diffCount, setDiffCount] = useState<number>(0);
|
||||||
const [currentDiff, setCurrentDiff] = useState<number>(0);
|
const [currentDiff, setCurrentDiff] = useState<number>(0);
|
||||||
|
|
||||||
|
// PDF相关状态
|
||||||
|
const [pdf1Url, setPdf1Url] = useState<string>('');
|
||||||
|
const [pdf2Url, setPdf2Url] = useState<string>('');
|
||||||
|
const [pdf1Info, setPdf1Info] = useState<PdfInfo | null>(null);
|
||||||
|
const [pdf2Info, setPdf2Info] = useState<PdfInfo | null>(null);
|
||||||
|
const [isLoadingPdf1, setIsLoadingPdf1] = useState(false);
|
||||||
|
const [isLoadingPdf2, setIsLoadingPdf2] = useState(false);
|
||||||
|
const [useExample, setUseExample] = useState(true);
|
||||||
|
|
||||||
|
// PDF类型检测函数
|
||||||
|
const detectPdfType = async (pdfUrl: string): Promise<PdfInfo> => {
|
||||||
|
const loadingTask = pdfjs.getDocument(pdfUrl);
|
||||||
|
const pdf = await loadingTask.promise;
|
||||||
|
|
||||||
|
let totalTextLength = 0;
|
||||||
|
const pagesToCheck = Math.min(pdf.numPages, 3); // 检查前3页
|
||||||
|
|
||||||
|
for (let i = 1; i <= pagesToCheck; i++) {
|
||||||
|
const page = await pdf.getPage(i);
|
||||||
|
const textContent = await page.getTextContent();
|
||||||
|
const pageText = textContent.items
|
||||||
|
.map((item: any) => item.str)
|
||||||
|
.join('');
|
||||||
|
totalTextLength += pageText.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均每页文字数量
|
||||||
|
const avgTextPerPage = totalTextLength / pagesToCheck;
|
||||||
|
|
||||||
|
// 计算置信度(0-1)
|
||||||
|
const confidence = Math.min(avgTextPerPage / 500, 1);
|
||||||
|
|
||||||
|
// 判断PDF类型
|
||||||
|
let type: PdfType;
|
||||||
|
if (avgTextPerPage > 100) {
|
||||||
|
type = 'text';
|
||||||
|
} else if (avgTextPerPage > 10) {
|
||||||
|
type = 'scanned';
|
||||||
|
} else {
|
||||||
|
type = 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
numPages: pdf.numPages,
|
||||||
|
textLength: totalTextLength,
|
||||||
|
confidence
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// PDF文本提取函数
|
||||||
|
const extractTextFromPdf = async (pdfUrl: string): Promise<string> => {
|
||||||
|
const loadingTask = pdfjs.getDocument(pdfUrl);
|
||||||
|
const pdf = await loadingTask.promise;
|
||||||
|
|
||||||
|
let fullText = '';
|
||||||
|
|
||||||
|
for (let i = 1; i <= pdf.numPages; i++) {
|
||||||
|
const page = await pdf.getPage(i);
|
||||||
|
const textContent = await page.getTextContent();
|
||||||
|
const pageText = textContent.items
|
||||||
|
.map((item: any) => item.str)
|
||||||
|
.join(' ');
|
||||||
|
fullText += `\n========== 第 ${i} 页 ==========\n${pageText}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullText;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载PDF并提取文本
|
||||||
|
const loadPdfAndExtractText = async (pdfUrl: string, setPdfInfo: (info: PdfInfo | null) => void, setLoading: (loading: boolean) => void, setTextContent: (text: string) => void) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
// 1. 检测PDF类型
|
||||||
|
const pdfInfo = await detectPdfType(pdfUrl);
|
||||||
|
setPdfInfo(pdfInfo);
|
||||||
|
|
||||||
|
// 2. 提取文本
|
||||||
|
if (pdfInfo.type === 'text') {
|
||||||
|
const text = await extractTextFromPdf(pdfUrl);
|
||||||
|
setTextContent(text);
|
||||||
|
toastService.success(`PDF加载成功!共 ${pdfInfo.numPages} 页,提取了 ${pdfInfo.textLength} 个字符`);
|
||||||
|
} else if (pdfInfo.type === 'scanned') {
|
||||||
|
toastService.warning('检测到扫描版PDF,文本提取质量可能较低');
|
||||||
|
const text = await extractTextFromPdf(pdfUrl);
|
||||||
|
setTextContent(text);
|
||||||
|
} else {
|
||||||
|
toastService.error('无法识别PDF类型,可能是图片PDF');
|
||||||
|
setTextContent('');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('PDF加载失败:', error);
|
||||||
|
toastService.error('PDF加载失败,请检查文件路径');
|
||||||
|
setPdfInfo(null);
|
||||||
|
setTextContent('');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Monaco Editor 挂载后的回调
|
// Monaco Editor 挂载后的回调
|
||||||
const handleEditorDidMount = (editor: editor.IStandaloneDiffEditor) => {
|
const handleEditorDidMount = (editor: editor.IStandaloneDiffEditor) => {
|
||||||
diffEditorRef.current = editor;
|
diffEditorRef.current = editor;
|
||||||
@@ -163,6 +280,9 @@ export default function MonacoDemoPage() {
|
|||||||
setOriginalText(CONTRACT_A);
|
setOriginalText(CONTRACT_A);
|
||||||
setModifiedText(CONTRACT_B);
|
setModifiedText(CONTRACT_B);
|
||||||
setCurrentDiff(0);
|
setCurrentDiff(0);
|
||||||
|
setUseExample(true);
|
||||||
|
setPdf1Info(null);
|
||||||
|
setPdf2Info(null);
|
||||||
|
|
||||||
// 重新计算差异数量
|
// 重新计算差异数量
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -175,6 +295,37 @@ export default function MonacoDemoPage() {
|
|||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 从URL参数加载PDF
|
||||||
|
const loadPdfsFromUrl = () => {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
const pdf1Path = searchParams.get('pdf1');
|
||||||
|
const pdf2Path = searchParams.get('pdf2');
|
||||||
|
|
||||||
|
if (pdf1Path || pdf2Path) {
|
||||||
|
setUseExample(false);
|
||||||
|
|
||||||
|
if (pdf1Path) {
|
||||||
|
const fullUrl = `/api/pdf-proxy?path=${encodeURIComponent(pdf1Path)}`;
|
||||||
|
setPdf1Url(fullUrl);
|
||||||
|
loadPdfAndExtractText(fullUrl, setPdf1Info, setIsLoadingPdf1, setOriginalText);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdf2Path) {
|
||||||
|
const fullUrl = `/api/pdf-proxy?path=${encodeURIComponent(pdf2Path)}`;
|
||||||
|
setPdf2Url(fullUrl);
|
||||||
|
loadPdfAndExtractText(fullUrl, setPdf2Info, setIsLoadingPdf2, setModifiedText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组件挂载时读取URL参数
|
||||||
|
useEffect(() => {
|
||||||
|
loadPdfsFromUrl();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="monaco-demo-page" style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
<div className="monaco-demo-page" style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||||
{/* 页面头部 */}
|
{/* 页面头部 */}
|
||||||
@@ -301,6 +452,69 @@ export default function MonacoDemoPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* PDF加载信息 */}
|
||||||
|
{!useExample && (pdf1Info || pdf2Info || isLoadingPdf1 || isLoadingPdf2) && (
|
||||||
|
<div style={{
|
||||||
|
padding: '12px 24px',
|
||||||
|
backgroundColor: '#fff3cd',
|
||||||
|
borderBottom: '1px solid #ffc107',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#856404'
|
||||||
|
}}>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '24px' }}>
|
||||||
|
<i className="ri-file-pdf-line" style={{ fontSize: '18px', marginTop: '2px' }}></i>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<strong>PDF文档信息:</strong>
|
||||||
|
<div style={{ display: 'flex', gap: '24px', marginTop: '8px' }}>
|
||||||
|
{/* PDF 1 信息 */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>📄 文档1(左侧/原始)</div>
|
||||||
|
{isLoadingPdf1 ? (
|
||||||
|
<div style={{ color: '#666' }}>⏳ 加载中...</div>
|
||||||
|
) : pdf1Info ? (
|
||||||
|
<div>
|
||||||
|
<div>类型: <span style={{
|
||||||
|
color: pdf1Info.type === 'text' ? '#28a745' : pdf1Info.type === 'scanned' ? '#ffc107' : '#dc3545',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>
|
||||||
|
{pdf1Info.type === 'text' ? '✅ 文本PDF' : pdf1Info.type === 'scanned' ? '⚠️ 扫描PDF' : '❌ 未知类型'}
|
||||||
|
</span></div>
|
||||||
|
<div>页数: {pdf1Info.numPages} 页</div>
|
||||||
|
<div>字符数: {pdf1Info.textLength} 个</div>
|
||||||
|
<div>置信度: {(pdf1Info.confidence * 100).toFixed(0)}%</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ color: '#999' }}>未加载</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PDF 2 信息 */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>📄 文档2(右侧/修改)</div>
|
||||||
|
{isLoadingPdf2 ? (
|
||||||
|
<div style={{ color: '#666' }}>⏳ 加载中...</div>
|
||||||
|
) : pdf2Info ? (
|
||||||
|
<div>
|
||||||
|
<div>类型: <span style={{
|
||||||
|
color: pdf2Info.type === 'text' ? '#28a745' : pdf2Info.type === 'scanned' ? '#ffc107' : '#dc3545',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>
|
||||||
|
{pdf2Info.type === 'text' ? '✅ 文本PDF' : pdf2Info.type === 'scanned' ? '⚠️ 扫描PDF' : '❌ 未知类型'}
|
||||||
|
</span></div>
|
||||||
|
<div>页数: {pdf2Info.numPages} 页</div>
|
||||||
|
<div>字符数: {pdf2Info.textLength} 个</div>
|
||||||
|
<div>置信度: {(pdf2Info.confidence * 100).toFixed(0)}%</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ color: '#999' }}>未加载</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 说明信息 */}
|
{/* 说明信息 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '12px 24px',
|
padding: '12px 24px',
|
||||||
@@ -311,13 +525,36 @@ export default function MonacoDemoPage() {
|
|||||||
}}>
|
}}>
|
||||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
|
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '8px' }}>
|
||||||
<i className="ri-information-line" style={{ fontSize: '18px', marginTop: '2px' }}></i>
|
<i className="ri-information-line" style={{ fontSize: '18px', marginTop: '2px' }}></i>
|
||||||
<div>
|
<div style={{ flex: 1 }}>
|
||||||
<strong>差异高亮说明:</strong>
|
<strong>差异高亮说明:</strong>
|
||||||
<ul style={{ margin: '4px 0 0 0', paddingLeft: '20px' }}>
|
<ul style={{ margin: '4px 0 0 0', paddingLeft: '20px' }}>
|
||||||
<li><span style={{ color: '#28a745', fontWeight: 'bold' }}>绿色</span>:新增的内容</li>
|
<li><span style={{ color: '#28a745', fontWeight: 'bold' }}>绿色</span>:新增的内容</li>
|
||||||
<li><span style={{ color: '#dc3545', fontWeight: 'bold' }}>红色</span>:删除的内容</li>
|
<li><span style={{ color: '#dc3545', fontWeight: 'bold' }}>红色</span>:删除的内容</li>
|
||||||
<li><span style={{ color: '#ffc107', fontWeight: 'bold' }}>黄色背景</span>:修改的行内差异</li>
|
<li><span style={{ color: '#ffc107', fontWeight: 'bold' }}>黄色背景</span>:修改的行内差异</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{useExample && (
|
||||||
|
<div style={{ marginTop: '12px', paddingTop: '12px', borderTop: '1px solid #b3d9ff' }}>
|
||||||
|
<strong>💡 使用提示:</strong>
|
||||||
|
<div style={{ marginTop: '4px' }}>
|
||||||
|
您可以通过URL参数加载PDF文档进行对比:
|
||||||
|
<code style={{
|
||||||
|
display: 'block',
|
||||||
|
marginTop: '4px',
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.05)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontSize: '12px',
|
||||||
|
wordBreak: 'break-all'
|
||||||
|
}}>
|
||||||
|
/monaco-demo?pdf1=路径1&pdf2=路径2
|
||||||
|
</code>
|
||||||
|
<div style={{ marginTop: '4px', fontSize: '12px' }}>
|
||||||
|
示例: <code>/monaco-demo?pdf1=documents/contract_v1.pdf&pdf2=documents/contract_v2.pdf</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -353,8 +590,49 @@ export default function MonacoDemoPage() {
|
|||||||
diffAlgorithm: 'advanced', // 使用高级差异算法
|
diffAlgorithm: 'advanced', // 使用高级差异算法
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* PDF加载中的遮罩层 */}
|
||||||
|
{(isLoadingPdf1 || isLoadingPdf2) && (
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
zIndex: 1000
|
||||||
|
}}>
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<div style={{
|
||||||
|
width: '48px',
|
||||||
|
height: '48px',
|
||||||
|
border: '4px solid #f3f3f3',
|
||||||
|
borderTop: '4px solid #00684a',
|
||||||
|
borderRadius: '50%',
|
||||||
|
animation: 'spin 1s linear infinite',
|
||||||
|
margin: '0 auto 16px'
|
||||||
|
}}></div>
|
||||||
|
<div style={{ fontSize: '16px', color: '#333' }}>
|
||||||
|
正在加载PDF文档并提取文本...
|
||||||
|
</div>
|
||||||
|
{isLoadingPdf1 && <div style={{ fontSize: '14px', color: '#666', marginTop: '8px' }}>📄 加载文档1</div>}
|
||||||
|
{isLoadingPdf2 && <div style={{ fontSize: '14px', color: '#666', marginTop: '8px' }}>📄 加载文档2</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 添加旋转动画 */}
|
||||||
|
<style>{`
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
|
||||||
{/* 页面底部信息 */}
|
{/* 页面底部信息 */}
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '8px 24px',
|
padding: '8px 24px',
|
||||||
|
|||||||
@@ -55,41 +55,14 @@ function getDataScopeLabel(scope: string): string {
|
|||||||
// ClientLoader - 加载初始数据
|
// ClientLoader - 加载初始数据
|
||||||
export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
||||||
// ==================== 权限校验 ====================
|
// ==================== 权限校验 ====================
|
||||||
// 检查用户是否有provincial_admin权限
|
// 不在这里做权限检查,因为路由权限已经在 root.tsx 中检查过了
|
||||||
try {
|
// 如果用户能访问到这个页面,说明已经通过了路由权限检查
|
||||||
const userInfo = localStorage.getItem('user_info');
|
//
|
||||||
|
// 原有的客户端权限检查逻辑已移除,统一使用 root.tsx 的路由权限控制
|
||||||
if (!userInfo) {
|
// 这样可以避免:
|
||||||
// 未登录,重定向到登录页
|
// 1. 路由权限通过但页面权限不通过的冲突
|
||||||
window.location.href = '/login';
|
// 2. localStorage.user_info 数据不完整导致的误判
|
||||||
throw new Error('未登录');
|
// 3. 重复的权限检查逻辑
|
||||||
}
|
|
||||||
|
|
||||||
const user = JSON.parse(userInfo);
|
|
||||||
|
|
||||||
// 检查角色或权限
|
|
||||||
// provincial_admin 角色拥有完整的RBAC管理权限
|
|
||||||
const hasPermission =
|
|
||||||
user.role === 'provincial_admin' ||
|
|
||||||
user.role_key === 'provincial_admin' ||
|
|
||||||
(user.permissions && Array.isArray(user.permissions) && user.permissions.includes('system:rbac:manage'));
|
|
||||||
|
|
||||||
if (!hasPermission) {
|
|
||||||
// 无权限,显示错误提示
|
|
||||||
console.warn('⚠️ 权限不足:需要省级管理员权限或system:rbac:manage权限');
|
|
||||||
toastService.error('权限不足,需要省级管理员权限');
|
|
||||||
|
|
||||||
// 返回空数据,但不阻止页面渲染(可以显示友好的无权限提示)
|
|
||||||
return {
|
|
||||||
roles: [],
|
|
||||||
routes: [],
|
|
||||||
users: [],
|
|
||||||
noPermission: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('权限检查失败:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== 加载数据 ====================
|
// ==================== 加载数据 ====================
|
||||||
try {
|
try {
|
||||||
@@ -102,16 +75,14 @@ export async function clientLoader({ request }: ClientLoaderFunctionArgs) {
|
|||||||
return {
|
return {
|
||||||
roles,
|
roles,
|
||||||
routes,
|
routes,
|
||||||
users,
|
users
|
||||||
noPermission: false
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载数据失败:", error);
|
console.error("加载数据失败:", error);
|
||||||
return {
|
return {
|
||||||
roles: [],
|
roles: [],
|
||||||
routes: [],
|
routes: [],
|
||||||
users: [],
|
users: []
|
||||||
noPermission: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1105,22 +1076,26 @@ export default function RolePermissions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有权限(通过角色列表是否为空判断)
|
// ==================== 权限检查移除说明 ====================
|
||||||
// 注意:这是一个简单的权限检查,实际应该从loader返回的noPermission字段判断
|
// 原有的客户端权限检查已移除,统一使用 root.tsx 的路由权限控制
|
||||||
const noPermission = roles.length === 0 && routes.length === 0 && users.length === 0 && !loading;
|
// 如果用户能访问到这个页面,说明已经通过了路由权限检查
|
||||||
|
// 不再显示"无权限"提示页面
|
||||||
|
|
||||||
// 无权限提示页面
|
// 如果数据为空,可能是数据加载失败,显示友好的空状态提示
|
||||||
if (noPermission) {
|
const hasNoData = roles.length === 0 && routes.length === 0 && users.length === 0 && !loading;
|
||||||
|
|
||||||
|
// 数据加载失败提示(不是权限问题)
|
||||||
|
if (hasNoData) {
|
||||||
return (
|
return (
|
||||||
<div className="role-permissions-page">
|
<div className="role-permissions-page">
|
||||||
<Card className="no-permission-card">
|
<Card className="no-data-card">
|
||||||
<div className="empty-state" style={{ padding: '80px 40px' }}>
|
<div className="empty-state" style={{ padding: '80px 40px' }}>
|
||||||
<i className="ri-shield-cross-line" style={{ fontSize: '96px', color: '#dcdfe6' }}></i>
|
<i className="ri-database-2-line" style={{ fontSize: '96px', color: '#dcdfe6' }}></i>
|
||||||
<h2 style={{ fontSize: '24px', fontWeight: 600, color: '#303133', marginTop: '24px', marginBottom: '12px' }}>
|
<h2 style={{ fontSize: '24px', fontWeight: 600, color: '#303133', marginTop: '24px', marginBottom: '12px' }}>
|
||||||
权限不足
|
暂无数据
|
||||||
</h2>
|
</h2>
|
||||||
<p style={{ fontSize: '16px', color: '#606266', marginBottom: '32px' }}>
|
<p style={{ fontSize: '16px', color: '#606266', marginBottom: '32px' }}>
|
||||||
您没有访问角色权限管理的权限,需要 <strong>省级管理员</strong> 角色或 <strong>system:rbac:manage</strong> 权限
|
系统中暂无角色、路由或用户数据,请稍后重试或联系管理员
|
||||||
</p>
|
</p>
|
||||||
<div style={{ display: 'flex', gap: '12px', justifyContent: 'center' }}>
|
<div style={{ display: 'flex', gap: '12px', justifyContent: 'center' }}>
|
||||||
<Button type="default" icon="ri-arrow-left-line" onClick={() => window.history.back()}>
|
<Button type="default" icon="ri-arrow-left-line" onClick={() => window.history.back()}>
|
||||||
|
|||||||
Reference in New Issue
Block a user