fix: scope User operations to visible data only in CleanerPage

Fix critical bug where non-Admin users could affect other users' data
when using "取消" (Uncheck All) and "确认删除" (Confirm Deletion) buttons.

Problem:
- User users see only their filtered materials in table (filteredResults)
- "取消" button was unchecking ALL materials in validationResults
- "确认删除" was processing ALL materials, not just visible ones
- This caused User A to delete/modify User B's invisible data

Solution:
1. Modified "取消" button to only uncheck visible filteredResults
   - Now removes selectedItems only for visible material codes
   - Preserves selections for other users' invisible data

2. Modified handleConfirmDeletion to process only visible items for non-Admin users
   - Admin: processes all validationResults (unchanged behavior)
   - User: processes only filteredResults (scoped to their data)

Security Impact:
- Prevents cross-user data interference
- Ensures User scope isolation
- Maintains Admin full access to all data

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-03-03 11:13:39 +08:00
parent 77311edce0
commit 6ecf03ce11
2 changed files with 289 additions and 3 deletions

View File

@@ -156,13 +156,17 @@ const CleanerPage: React.FC = () => {
}
const handleConfirmDeletion = async () => {
if (validationResults.length === 0) return alert('没有可处理的数据')
// For non-admin users, only process visible filtered results
// For admin users, process all validation results
const resultsToProcess = isAdmin ? validationResults : filteredResults
if (resultsToProcess.length === 0) return alert('没有可处理的数据')
const materialsToUpsert: { materialCode: string; managerName: string }[] = []
const materialsToDelete: string[] = []
const missingManager: string[] = []
for (const result of validationResults) {
for (const result of resultsToProcess) {
if (!result.materialCode?.trim()) continue
const code = result.materialCode.trim()
@@ -417,7 +421,17 @@ const CleanerPage: React.FC = () => {
<CheckSquare size={14} className="text-blue-600" />
</button>
<button
onClick={() => setSelectedItems(new Set())}
onClick={() => {
// Only uncheck items that are visible in filteredResults
const visibleCodes = new Set(filteredResults.map((r) => r.materialCode))
setSelectedItems((prev) => {
const newSet = new Set(prev)
for (const code of visibleCodes) {
newSet.delete(code)
}
return newSet
})
}}
className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
>
<Square size={14} className="text-slate-400" />