删除文件

This commit is contained in:
zlwu committed 2025-08-10 09:56:04 +08:00
1 parent 0a2321e897
commit 620f6fce80
4 files changed
-835

No files matched your search

@@ -1,33 +0,0 @@
-- 修复奖学金申请表缺失的create_dept字段
-- 问题: MyBatis-Plus插入数据时报错 Unknown column 'create_dept' in 'field list'
-- 原因: 数据库表缺少BaseEntity中的create_dept字段
-- 解决方案: 为jcbt_scholarship_application表添加create_dept字段
-- 检查字段是否存在,不存在则添加
SET @sql = '';
SELECT COUNT(*) INTO @col_exists
FROM information_schema.columns
WHERE table_schema = 'hzdyjs_xg'
AND table_name = 'jcbt_scholarship_application'
AND column_name = 'create_dept';
-- 如果字段不存在,则添加
SET @sql = IF(@col_exists = 0,
'ALTER TABLE jcbt_scholarship_application ADD COLUMN create_dept bigint(20) DEFAULT NULL COMMENT ''创建部门'' AFTER submit_time;',
'SELECT ''字段 create_dept 已存在,跳过添加'' AS message;');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 验证字段是否添加成功
SELECT
COLUMN_NAME as '字段名',
DATA_TYPE as '数据类型',
IS_NULLABLE as '允许空值',
COLUMN_DEFAULT as '默认值',
COLUMN_COMMENT as '注释'
FROM information_schema.columns
WHERE table_schema = 'hzdyjs_xg'
AND table_name = 'jcbt_scholarship_application'
AND column_name = 'create_dept';
-318
View File
@@ -1,318 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>权限诊断工具</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background: #f5f5f5;
}
.container {
max-width: 1000px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.debug-section {
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
background: #f9f9f9;
}
.debug-title {
font-weight: bold;
color: #333;
margin-bottom: 10px;
font-size: 16px;
}
.permission-item {
padding: 5px;
margin: 3px 0;
background: #e8f5e8;
border-radius: 3px;
font-family: monospace;
}
.error {
color: red;
background: #ffe6e6;
}
.success {
color: green;
background: #e6ffe6;
}
.warning {
color: orange;
background: #fff3e0;
}
.code-block {
background: #f0f0f0;
padding: 10px;
border-radius: 4px;
font-family: monospace;
margin: 10px 0;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
button:hover {
background: #0056b3;
}
.highlight {
background: yellow;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>🔍 奖学金申请权限诊断工具</h1>
<p><strong>问题描述</strong>:jwc010用户看不到"班主任批量申请"按钮</p>
<p><strong>预期权限</strong>:<code>jcbt:application:add</code></p>
<button onclick="startDiagnosis()">开始诊断</button>
<button onclick="clearResults()">清空结果</button>
<div id="results"></div>
</div>
<script>
function startDiagnosis() {
const results = document.getElementById('results');
results.innerHTML = '';
addSection('🚀 开始权限诊断...', 'info');
// 1. 检查当前URL和页面
checkCurrentPage();
// 2. 检查本地存储
checkLocalStorage();
// 3. 检查全局变量
checkGlobalVariables();
// 4. 检查Vue Store
checkVueStore();
// 5. 检查DOM元素
checkDOMElements();
// 6. 提供调试命令
provideDebugCommands();
addSection('✅ 诊断完成!请检查上述结果。', 'success');
}
function checkCurrentPage() {
addSection('📍 当前页面信息', 'info');
addResult('当前URL', window.location.href);
addResult('页面标题', document.title);
addResult('用户代理', navigator.userAgent);
}
function checkLocalStorage() {
addSection('💾 本地存储检查', 'info');
// 检查Token
const token = localStorage.getItem('token') || sessionStorage.getItem('token');
addResult('Token存在', token ? '✅ 是' : '❌ 否');
if (token) {
addResult('Token长度', token.length);
addResult('Token前20字符', token.substring(0, 20) + '...');
}
// 检查用户信息
const userInfo = localStorage.getItem('userInfo') || sessionStorage.getItem('userInfo');
if (userInfo) {
try {
const user = JSON.parse(userInfo);
addResult('用户信息', JSON.stringify(user, null, 2));
} catch (e) {
addResult('用户信息解析失败', e.message, 'error');
}
}
// 检查权限信息
const permissions = localStorage.getItem('permissions') || sessionStorage.getItem('permissions');
if (permissions) {
try {
const perms = JSON.parse(permissions);
addResult('本地权限数量', Array.isArray(perms) ? perms.length : 'N/A');
if (Array.isArray(perms)) {
const targetPerm = 'jcbt:application:add';
const hasTargetPerm = perms.includes(targetPerm);
addResult(`目标权限 ${targetPerm}`, hasTargetPerm ? '✅ 存在' : '❌ 不存在', hasTargetPerm ? 'success' : 'error');
// 显示所有jcbt相关权限
const jcbtPerms = perms.filter(p => p.includes('jcbt'));
addResult('JCBT相关权限', jcbtPerms.length > 0 ? jcbtPerms.join(', ') : '无');
}
} catch (e) {
addResult('权限信息解析失败', e.message, 'error');
}
} else {
addResult('本地权限信息', '❌ 不存在', 'error');
}
}
function checkGlobalVariables() {
addSection('🌍 全局变量检查', 'info');
// 检查Vue应用
if (window.Vue) {
addResult('Vue版本', window.Vue.version || 'Vue 3.x');
} else {
addResult('Vue', '❌ 未找到');
}
// 检查Pinia
if (window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
addResult('Vue Devtools', '✅ 已安装');
}
}
function checkVueStore() {
addSection('🗄️ Vue Store检查', 'info');
// 这里需要在Vue应用中执行
addResult('注意', '以下命令需要在浏览器控制台的Vue应用上下文中执行:', 'warning');
const vueCommands = [
'// 检查用户权限',
'const userStore = useUserStore();',
'console.log("用户权限:", userStore.permissions);',
'console.log("用户角色:", userStore.roles);',
'console.log("用户ID:", userStore.userId);',
'console.log("用户名:", userStore.name);',
'',
'// 检查目标权限',
'const targetPerm = "jcbt:application:add";',
'const hasPermission = userStore.permissions.includes(targetPerm);',
'console.log(`是否有权限 ${targetPerm}:`, hasPermission);',
'',
'// 检查权限指令逻辑',
'import { checkPermi } from "@/utils/permission";',
'const result = checkPermi(["jcbt:application:add"]);',
'console.log("权限检查结果:", result);'
];
vueCommands.forEach(cmd => {
addCodeBlock(cmd);
});
}
function checkDOMElements() {
addSection('🔍 DOM元素检查', 'info');
// 查找奖学金申请相关的按钮
const buttons = document.querySelectorAll('button');
let foundButton = false;
buttons.forEach((btn, index) => {
const text = btn.textContent.trim();
if (text.includes('申请') || text.includes('新增') || text.includes('批量')) {
foundButton = true;
addResult(`按钮 ${index + 1}`, `"${text}" - 可见: ${btn.style.display !== 'none'}`, 'info');
}
});
if (!foundButton) {
addResult('相关按钮', '❌ 未找到申请/新增/批量相关按钮', 'error');
}
// 检查是否在正确的页面
const url = window.location.href;
if (url.includes('jcbt') && url.includes('application')) {
addResult('页面匹配', '✅ 在奖学金申请页面', 'success');
} else {
addResult('页面匹配', '❌ 不在奖学金申请页面', 'error');
}
}
function provideDebugCommands() {
addSection('🛠️ 调试命令', 'info');
addResult('在浏览器控制台执行以下命令进行深度调试:', '', 'info');
const commands = [
'// 1. 检查用户完整信息',
'fetch("/getInfo", { headers: { "Authorization": "Bearer " + localStorage.getItem("token") } })',
' .then(res => res.json())',
' .then(data => console.log("用户信息:", data));',
'',
'// 2. 手动检查权限',
'console.log("当前用户权限:", JSON.parse(localStorage.getItem("permissions") || "[]"));',
'',
'// 3. 检查Vue实例',
'const app = document.querySelector("#app").__vueParentComponent;',
'console.log("Vue应用:", app);',
'',
'// 4. 强制显示按钮(测试用)',
'document.querySelectorAll("button").forEach(btn => {',
' if (btn.textContent.includes("申请")) {',
' btn.style.display = "inline-block";',
' btn.style.opacity = "1";',
' console.log("强制显示按钮:", btn.textContent);',
' }',
'});'
];
commands.forEach(cmd => {
addCodeBlock(cmd);
});
}
function addSection(title, type = 'info') {
const results = document.getElementById('results');
const section = document.createElement('div');
section.className = `debug-section ${type}`;
section.innerHTML = `<div class="debug-title">${title}</div>`;
results.appendChild(section);
return section;
}
function addResult(label, value, type = 'info') {
const results = document.getElementById('results');
const lastSection = results.lastElementChild;
const item = document.createElement('div');
item.className = `permission-item ${type}`;
item.innerHTML = `<strong>${label}:</strong> ${value}`;
lastSection.appendChild(item);
}
function addCodeBlock(code) {
const results = document.getElementById('results');
const lastSection = results.lastElementChild;
const block = document.createElement('div');
block.className = 'code-block';
block.textContent = code;
lastSection.appendChild(block);
}
function clearResults() {
document.getElementById('results').innerHTML = '';
}
// 页面加载时自动检查基础信息
window.onload = function() {
addSection('💡 使用说明', 'info');
addResult('步骤1', '确保您在奖学金申请管理页面(http://localhost/jcbt/scholarship/application)');
addResult('步骤2', '点击"开始诊断"按钮');
addResult('步骤3', '查看诊断结果,特别关注权限相关信息');
addResult('步骤4', '按照提供的调试命令在浏览器控制台进一步检查');
};
</script>
</body>
</html>
-364
View File
@@ -1,364 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>班级选择功能测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
.form-container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
select, input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
select:focus, input:focus {
outline: none;
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.btn {
background-color: #409eff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn:hover {
background-color: #337ecc;
}
.btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.result {
margin-top: 20px;
padding: 15px;
background-color: #f0f9ff;
border: 1px solid #b3d8ff;
border-radius: 4px;
}
.error {
background-color: #fef0f0;
border-color: #fbc4c4;
color: #c53030;
}
.success {
background-color: #f0fff4;
border-color: #9ae6b4;
color: #2f855a;
}
.student-list {
margin-top: 15px;
}
.student-item {
padding: 8px;
margin: 5px 0;
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
cursor: pointer;
}
.student-item:hover {
background-color: #e9ecef;
}
.student-item.selected {
background-color: #e3f2fd;
border-color: #409eff;
}
</style>
</head>
<body>
<div class="form-container">
<h1>🎓 贫困生申请 - 班级选择功能测试</h1>
<p>测试班级选择 → 学生搜索的级联功能</p>
<form id="testForm">
<div class="form-group">
<label for="academicYear">学年</label>
<select id="academicYear" name="academicYear">
<option value="2024-2025">2024-2025</option>
<option value="2023-2024">2023-2024</option>
<option value="2022-2023">2022-2023</option>
</select>
</div>
<div class="form-group">
<label for="classId">班级</label>
<select id="classId" name="classId" disabled>
<option value="">请先获取班级列表</option>
</select>
</div>
<div class="form-group">
<label for="studentSearch">学号搜索</label>
<input type="text" id="studentSearch" name="studentSearch"
placeholder="请先选择班级,然后输入学号进行搜索" disabled>
</div>
<div class="form-group">
<label for="studentName">学生姓名</label>
<input type="text" id="studentName" name="studentName"
placeholder="选择学生后自动填充" readonly>
</div>
<button type="button" class="btn" onclick="loadClasses()">获取班级列表</button>
<button type="button" class="btn" onclick="testSubmit()" disabled id="submitBtn">提交测试</button>
</form>
<div id="result" class="result" style="display: none;"></div>
<div id="studentList" class="student-list" style="display: none;"></div>
</div>
<script>
// 模拟API基础URL
const API_BASE_URL = 'http://localhost:8080';
let classOptions = [];
let studentOptions = [];
let selectedStudent = null;
// 获取班级列表
async function loadClasses() {
try {
showResult('正在获取班级列表...', 'info');
// 模拟API调用(实际应该调用真实API)
const response = await mockGetCurrentUserClasses();
classOptions = response.data || [];
populateClassSelect();
showResult(`成功获取 ${classOptions.length} 个班级`, 'success');
document.getElementById('classId').disabled = false;
} catch (error) {
console.error('获取班级列表失败:', error);
showResult('获取班级列表失败: ' + error.message, 'error');
}
}
// 填充班级下拉选项
function populateClassSelect() {
const classSelect = document.getElementById('classId');
classSelect.innerHTML = '<option value="">请选择班级</option>';
classOptions.forEach(classItem => {
const option = document.createElement('option');
option.value = classItem.classId;
option.textContent = `${classItem.className} - ${classItem.majorName || ''}`;
classSelect.appendChild(option);
});
// 绑定班级选择事件
classSelect.onchange = handleClassChange;
}
// 班级选择变化处理
async function handleClassChange() {
const classId = document.getElementById('classId').value;
const studentSearchInput = document.getElementById('studentSearch');
const studentNameInput = document.getElementById('studentName');
// 清空学生相关字段
studentSearchInput.value = '';
studentNameInput.value = '';
selectedStudent = null;
document.getElementById('studentList').style.display = 'none';
if (classId) {
// 启用学生搜索
studentSearchInput.disabled = false;
studentSearchInput.placeholder = '请输入学号进行搜索';
// 获取班级学生列表
try {
showResult('正在获取班级学生列表...', 'info');
const response = await mockGetClassStudents(classId);
studentOptions = response.data || [];
showResult(`班级内有 ${studentOptions.length} 名学生`, 'success');
// 绑定学生搜索事件
studentSearchInput.oninput = handleStudentSearch;
} catch (error) {
console.error('获取学生列表失败:', error);
showResult('获取学生列表失败: ' + error.message, 'error');
}
} else {
// 禁用学生搜索
studentSearchInput.disabled = true;
studentSearchInput.placeholder = '请先选择班级';
}
}
// 学生搜索处理
function handleStudentSearch() {
const keyword = document.getElementById('studentSearch').value.toLowerCase();
const studentListDiv = document.getElementById('studentList');
if (!keyword) {
studentListDiv.style.display = 'none';
return;
}
// 过滤学生
const filteredStudents = studentOptions.filter(student =>
student.studentNo && student.studentNo.toLowerCase().includes(keyword)
);
// 显示搜索结果
if (filteredStudents.length > 0) {
displayStudentList(filteredStudents);
studentListDiv.style.display = 'block';
} else {
studentListDiv.innerHTML = '<p>未找到匹配的学生</p>';
studentListDiv.style.display = 'block';
}
}
// 显示学生列表
function displayStudentList(students) {
const studentListDiv = document.getElementById('studentList');
studentListDiv.innerHTML = '<h4>搜索结果:</h4>';
students.forEach(student => {
const studentDiv = document.createElement('div');
studentDiv.className = 'student-item';
studentDiv.textContent = `${student.studentNo} - ${student.studentName}`;
studentDiv.onclick = () => selectStudent(student);
studentListDiv.appendChild(studentDiv);
});
}
// 选择学生
function selectStudent(student) {
selectedStudent = student;
document.getElementById('studentSearch').value = student.studentNo;
document.getElementById('studentName').value = student.studentName;
document.getElementById('studentList').style.display = 'none';
document.getElementById('submitBtn').disabled = false;
showResult(`已选择学生: ${student.studentNo} - ${student.studentName}`, 'success');
}
// 提交测试
function testSubmit() {
const formData = {
academicYear: document.getElementById('academicYear').value,
classId: document.getElementById('classId').value,
studentNo: document.getElementById('studentSearch').value,
studentName: document.getElementById('studentName').value,
selectedStudent: selectedStudent
};
showResult('测试数据: ' + JSON.stringify(formData, null, 2), 'success');
}
// 显示结果
function showResult(message, type = 'info') {
const resultDiv = document.getElementById('result');
resultDiv.style.display = 'block';
resultDiv.className = `result ${type}`;
resultDiv.innerHTML = `<pre>${message}</pre>`;
}
// 模拟API - 获取当前用户班级
async function mockGetCurrentUserClasses() {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 500));
return {
code: 200,
msg: '操作成功',
data: [
{
classId: 1,
className: '计算机科学与技术2021级1班',
grade: '2021',
majorName: '计算机科学与技术',
collegeName: '计算机学院'
},
{
classId: 2,
className: '软件工程2021级1班',
grade: '2021',
majorName: '软件工程',
collegeName: '计算机学院'
},
{
classId: 3,
className: '数据科学与大数据技术2022级1班',
grade: '2022',
majorName: '数据科学与大数据技术',
collegeName: '计算机学院'
}
]
};
}
// 模拟API - 获取班级学生
async function mockGetClassStudents(classId) {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 300));
const studentData = {
1: [
{ studentId: 101, studentNo: '20210101', studentName: '张三', gender: '男' },
{ studentId: 102, studentNo: '20210102', studentName: '李四', gender: '女' },
{ studentId: 103, studentNo: '20210103', studentName: '王五', gender: '男' },
{ studentId: 104, studentNo: '20210104', studentName: '赵六', gender: '女' }
],
2: [
{ studentId: 201, studentNo: '20210201', studentName: '钱七', gender: '男' },
{ studentId: 202, studentNo: '20210202', studentName: '孙八', gender: '女' },
{ studentId: 203, studentNo: '20210203', studentName: '周九', gender: '男' }
],
3: [
{ studentId: 301, studentNo: '20220301', studentName: '吴十', gender: '女' },
{ studentId: 302, studentNo: '20220302', studentName: '郑十一', gender: '男' },
{ studentId: 303, studentNo: '20220303', studentName: '王十二', gender: '女' },
{ studentId: 304, studentNo: '20220304', studentName: '李十三', gender: '男' },
{ studentId: 305, studentNo: '20220305', studentName: '刘十四', gender: '女' }
]
};
return {
code: 200,
msg: '操作成功',
data: studentData[classId] || []
};
}
// 页面加载完成后的初始化
document.addEventListener('DOMContentLoaded', function() {
showResult('页面加载完成,请点击"获取班级列表"开始测试', 'info');
});
</script>
</body>
</html>
-120
View File
@@ -1,120 +0,0 @@
# 奖学金审核页面测试说明
## 📋 修改内容总结
### 🔄 页面改造完成
1. **搜索表单**:基于申请表字段重新设计
- 申请编号、学生姓名、学号、申请状态
- 奖学金类型、申请批次、申请类型、提交时间
2. **表格列**:调整为申请表字段结构
- 学号、学生姓名、班级、奖学金类型
- 申请类型、申请状态、最终评分、提交时间、创建时间
3. **操作按钮**:
- 详情:查看申请详细信息
- 审核:仅对"已提交"状态的申请显示
- 修改:仅对"草稿"状态的申请显示
4. **API调用**:
- 改为调用 `listScholarshipApplication` 获取申请列表
- 使用 `updateScholarshipApplication` 进行审核操作
## 🧪 测试步骤
### 1. 页面加载测试
```bash
# 访问页面
http://localhost/jcbt/scholarship/review
# 预期结果:
- 页面正常加载
- 显示申请统计卡片
- 搜索表单显示正确字段
- 表格显示申请记录
```
### 2. 搜索功能测试
```bash
# 测试各个搜索条件
- 输入申请编号搜索
- 输入学生姓名搜索
- 选择申请状态筛选
- 选择奖学金类型筛选
- 设置提交时间范围
# 预期结果:
- 搜索结果正确过滤
- 重置按钮清空所有条件
```
### 3. 审核功能测试
```bash
# 点击"已提交"状态的申请的"审核"按钮
# 预期结果:
- 弹出审核对话框
- 显示申请详细信息
- 可选择审核结果:通过/拒绝/转入审核中
- 可输入审核意见
- 提交后更新申请状态
```
### 4. 统计功能测试
```bash
# 检查统计卡片
# 预期结果:
- 待我审核:显示"已提交"状态的申请数量
- 已通过:显示"已通过"状态的申请数量
- 已拒绝:显示"已拒绝"状态的申请数量
- 总审核数:显示总申请数量
```
## 🐛 已知问题
### Vue Linter 错误
```bash
# 当前存在的错误(不影响功能):
Line 174: [vue/no-v-model-argument] 'v-model' directives require no argument.
Line 277: [vue/no-v-model-argument] 'v-model' directives require no argument.
Line 278: [vue/no-v-model-argument] 'v-model' directives require no argument.
```
这些是分页组件的 v-model 参数问题,不影响页面功能。
## 📝 后续工作
1. **后端API优化**:
- 确保 `listScholarshipApplication` 支持审核页面的查询参数
- 添加审核相关的统计接口
2. **权限控制**:
- 根据用户角色控制可见的申请记录
- 审核权限验证
3. **数据库优化**:
- 考虑是否需要保留 `jcbt_scholarship_review` 表
- 或者完全基于申请表进行状态管理
## 🎯 优势
1. **简化架构**:直接基于申请表,减少复杂关联
2. **数据一致性**:避免申请表和审核表状态不同步
3. **用户体验**:审核界面直观,操作流程清晰
4. **维护便利**:减少代码复杂度,易于维护
## 📊 测试数据需求
```sql
-- 需要在申请表中有不同状态的测试数据
INSERT INTO jcbt_scholarship_application
(application_no, student_name, student_no, current_status, submit_time, ...)
VALUES
('JXPJ2025001', '张三', '20230001', 'SUBMITTED', NOW(), ...),
('JXPJ2025002', '李四', '20230002', 'DRAFT', NOW(), ...),
('JXPJ2025003', '王五', '20230003', 'APPROVED', NOW(), ...),
('JXPJ2025004', '赵六', '20230004', 'REJECTED', NOW(), ...);
```
测试完成后,请反馈页面的实际表现!