Compare commits
No commits in common. "d62dcd2f82f9c2d2e5bd2a3060b40b45ace657fb" and "7ac52a8790be4b3d0c7daab108e83df0e6ad77d4" have entirely different histories.
d62dcd2f82
...
7ac52a8790
|
|
@ -42,9 +42,6 @@ shared/
|
||||||
├── Table.tsx # 表格(styled thead,tbody 由调用方提供)
|
├── Table.tsx # 表格(styled thead,tbody 由调用方提供)
|
||||||
├── Toast.tsx # 通知条(右下角固定定位)
|
├── Toast.tsx # 通知条(右下角固定定位)
|
||||||
├── Pagination.tsx # 分页(中文标签:"上一页"/"下一页")
|
├── Pagination.tsx # 分页(中文标签:"上一页"/"下一页")
|
||||||
├── hooks/ # React Hooks
|
|
||||||
│ ├── useDirtyTracker.ts # 脏状态追踪("修改后才显示保存按钮"模式)
|
|
||||||
│ └── index.ts # Barrel 导出
|
|
||||||
└── login-page.tsx # 统一登录页(SSO + LDAP 切换,不在 barrel 中导出)
|
└── login-page.tsx # 统一登录页(SSO + LDAP 切换,不在 barrel 中导出)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -353,7 +350,6 @@ import LoginPage from '@shared/ui/login-page' // 单独导入,不在 barrel
|
||||||
| Table | `headers: string[]`, `children: ReactNode` | 样式化 thead,tbody 由调用方提供 |
|
| Table | `headers: string[]`, `children: ReactNode` | 样式化 thead,tbody 由调用方提供 |
|
||||||
| Toast | `message`, `type: 'success'|'error'|'info'`, `onClose` | 右下角固定通知条 |
|
| Toast | `message`, `type: 'success'|'error'|'info'`, `onClose` | 右下角固定通知条 |
|
||||||
| Pagination | `page`, `totalPages`, `onPageChange` | 中文标签,totalPages <= 1 时隐藏 |
|
| Pagination | `page`, `totalPages`, `onPageChange` | 中文标签,totalPages <= 1 时隐藏 |
|
||||||
| useDirtyTracker | `isDirty(key, item): boolean`, `markClean(key, item): void`, `removeItem(key): void` | 脏状态追踪 Hook,"修改后才显示保存按钮"模式 |
|
|
||||||
| LoginPage | `siteName: string`, `title?: string` | 完整登录页,支持 SSO 和 LDAP 切换 |
|
| LoginPage | `siteName: string`, `title?: string` | 完整登录页,支持 SSO 和 LDAP 切换 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { useDirtyTracker } from './useDirtyTracker'
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
'use client'
|
|
||||||
import { useState, useRef } from 'react'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 脏状态追踪 Hook — 用于"修改后才显示保存按钮"模式。
|
|
||||||
*
|
|
||||||
* @example 数组场景(每个子项独立保存)
|
|
||||||
* const { isDirty, markClean, removeItem } = useDirtyTracker()
|
|
||||||
* {items.map(item => (
|
|
||||||
* <div key={item.id}>
|
|
||||||
* <input onChange={...} />
|
|
||||||
* {isDirty(item.id, item) && <button onClick={save}>保存</button>}
|
|
||||||
* </div>
|
|
||||||
* ))}
|
|
||||||
* // 保存成功后: markClean(id, savedItem)
|
|
||||||
* // 删除时: removeItem(id)
|
|
||||||
*/
|
|
||||||
export function useDirtyTracker() {
|
|
||||||
const savedMap = useRef<Map<string, string>>(new Map())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断指定 key 的当前数据是否相对于上次 markClean 有修改。
|
|
||||||
* 从未 markClean 过的 key 视为 dirty(适用于新增项)。
|
|
||||||
*/
|
|
||||||
const isDirty = (key: string, current: unknown): boolean => {
|
|
||||||
const saved = savedMap.current.get(key)
|
|
||||||
if (saved === undefined) return true // 从未保存过 → dirty
|
|
||||||
return saved !== JSON.stringify(current)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将指定 key 的当前数据标记为"已保存"(重置 dirty 状态)。
|
|
||||||
*/
|
|
||||||
const markClean = (key: string, item: unknown): void => {
|
|
||||||
savedMap.current.set(key, JSON.stringify(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除指定 key 的追踪记录(用于列表项被删除时清理)。
|
|
||||||
*/
|
|
||||||
const removeItem = (key: string): void => {
|
|
||||||
savedMap.current.delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isDirty, markClean, removeItem }
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue