|
|
@@ -8,9 +8,9 @@ import {
|
|
|
ValueFormatterParams,
|
|
|
GridReadyEvent,
|
|
|
} from 'ag-grid-community';
|
|
|
-import styles from './EstateExport.module.css'; // 改为 CSS Modules
|
|
|
+import styles from './EstateExport.module.css';
|
|
|
|
|
|
-// 1. 类型定义(补全接口,确保类型安全)
|
|
|
+// 类型定义
|
|
|
export interface IEstateExportHistory {
|
|
|
id: string;
|
|
|
exportDate: string; // yyyymmdd 格式
|
|
|
@@ -18,13 +18,13 @@ export interface IEstateExportHistory {
|
|
|
exportContent: string; // 1:物件マスタCSV, 2:分析用CSV
|
|
|
}
|
|
|
|
|
|
-// 2. AG Grid 模块注册(保持原有逻辑,补充注释)
|
|
|
+// AG Grid 模块注册
|
|
|
ModuleRegistry.registerModules([
|
|
|
- PaginationModule, // 分页模块
|
|
|
- ClientSideRowModelModule // 客户端行模型(本地数据分页)
|
|
|
+ PaginationModule,
|
|
|
+ ClientSideRowModelModule
|
|
|
]);
|
|
|
|
|
|
-// 3. 枚举与映射配置(保持原有,补充类型约束)
|
|
|
+// 枚举与映射配置
|
|
|
export enum ExportContentType {
|
|
|
PROPERTY_MASTER = '1',
|
|
|
ANALYSIS = '2'
|
|
|
@@ -35,18 +35,13 @@ const EXPORT_CONTENT_MAP: Record<ExportContentType, string> = {
|
|
|
[ExportContentType.ANALYSIS]: '分析用CSV'
|
|
|
};
|
|
|
|
|
|
-// 4. 工具函数(抽离通用逻辑,提高复用性)
|
|
|
-/**
|
|
|
- * 日期格式验证(yyyymmdd)
|
|
|
- * @param date - 待验证日期字符串
|
|
|
- * @returns 验证结果
|
|
|
- */
|
|
|
+// 工具函数
|
|
|
const validateDate = (date: string): boolean => {
|
|
|
const regex = /^\d{8}$/;
|
|
|
if (!regex.test(date)) return false;
|
|
|
|
|
|
const year = parseInt(date.substring(0, 4), 10);
|
|
|
- const month = parseInt(date.substring(4, 6), 10) - 1; // 月份从 0 开始
|
|
|
+ const month = parseInt(date.substring(4, 6), 10) - 1;
|
|
|
const day = parseInt(date.substring(6, 8), 10);
|
|
|
|
|
|
const validDate = new Date(year, month, day);
|
|
|
@@ -57,28 +52,23 @@ const validateDate = (date: string): boolean => {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
-/**
|
|
|
- * 格式化日期显示(yyyymmdd → yyyy/mm/dd)
|
|
|
- * @param date - 原始日期字符串
|
|
|
- * @returns 格式化后的日期
|
|
|
- */
|
|
|
const formatDisplayDate = (date: string): string => {
|
|
|
if (!date || date.length !== 8) return '';
|
|
|
return `${date.slice(0, 4)}/${date.slice(4, 6)}/${date.slice(6, 8)}`;
|
|
|
};
|
|
|
|
|
|
const EstateExportHistory: React.FC = () => {
|
|
|
- // 5. 状态管理(补充类型约束,初始化更合理)
|
|
|
+ // 状态管理
|
|
|
const [baseDate, setBaseDate] = useState<string>('');
|
|
|
const [selectedContent, setSelectedContent] = useState<ExportContentType>(
|
|
|
ExportContentType.ANALYSIS
|
|
|
);
|
|
|
const [historyList, setHistoryList] = useState<IEstateExportHistory[]>([]);
|
|
|
const [isExporting, setIsExporting] = useState<boolean>(false);
|
|
|
- const [gridApi, setGridApi] = useState<any>(null); // AG Grid API 实例
|
|
|
- const [isLoading, setIsLoading] = useState<boolean>(true); // 列表加载状态
|
|
|
+ const [gridApi, setGridApi] = useState<any>(null);
|
|
|
+ const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
|
|
|
|
- // 6. 样式配置(使用 CSS Modules,避免全局污染)
|
|
|
+ // 样式配置
|
|
|
const containerStyle = useMemo(() => ({
|
|
|
width: '100%',
|
|
|
height: '300px',
|
|
|
@@ -86,20 +76,20 @@ const EstateExportHistory: React.FC = () => {
|
|
|
overflow: 'hidden'
|
|
|
}), []);
|
|
|
|
|
|
- // 7. AG Grid 配置(优化默认列配置,补充缺失功能)
|
|
|
+ // AG Grid 配置
|
|
|
const defaultColDef = useMemo<ColDef>(() => ({
|
|
|
flex: 1,
|
|
|
minWidth: 120,
|
|
|
- sortable: true, // 开启排序(提升体验)
|
|
|
- filter: true, // 开启筛选(提升体验)
|
|
|
+ sortable: true,
|
|
|
+ filter: true,
|
|
|
resizable: true,
|
|
|
- menuTabs: ['filterMenuTab'], // 只显示筛选菜单(简化操作)
|
|
|
- cellStyle: { display: 'flex', alignItems: 'center' } // 单元格垂直居中
|
|
|
+ menuTabs: ['filterMenuTab'],
|
|
|
+ cellStyle: { display: 'flex', alignItems: 'center' }
|
|
|
}), []);
|
|
|
|
|
|
- const paginationPageSize = useMemo(() => 5, []); // 调整为每页 5 条(更合理)
|
|
|
+ const paginationPageSize = useMemo(() => 5, []);
|
|
|
|
|
|
- // 8. 列定义(优化格式化逻辑,抽离为独立函数)
|
|
|
+ // 列定义
|
|
|
const columnDefs = useMemo<ColDef<IEstateExportHistory>[]>(() => [
|
|
|
{
|
|
|
headerName: '出力日付',
|
|
|
@@ -107,14 +97,14 @@ const EstateExportHistory: React.FC = () => {
|
|
|
flex: 1.2,
|
|
|
valueFormatter: (params: ValueFormatterParams<IEstateExportHistory>) =>
|
|
|
formatDisplayDate(params.value),
|
|
|
- sort: 'desc' // 默认按日期降序排序
|
|
|
+ sort: 'desc'
|
|
|
},
|
|
|
{
|
|
|
headerName: '担当者',
|
|
|
field: 'exporter',
|
|
|
flex: 1,
|
|
|
filter: 'agTextColumnFilter',
|
|
|
- filterParams: { matchContains: true } // 包含匹配(更灵活)
|
|
|
+ filterParams: { matchContains: true }
|
|
|
},
|
|
|
{
|
|
|
headerName: '出力内容',
|
|
|
@@ -122,7 +112,7 @@ const EstateExportHistory: React.FC = () => {
|
|
|
flex: 1.5,
|
|
|
valueFormatter: (params: ValueFormatterParams<IEstateExportHistory>) =>
|
|
|
EXPORT_CONTENT_MAP[params.value as ExportContentType] || '不明',
|
|
|
- filter: 'agSetColumnFilter', // 下拉筛选(更直观)
|
|
|
+ filter: 'agSetColumnFilter',
|
|
|
filterParams: {
|
|
|
values: Object.values(ExportContentType).map(key =>
|
|
|
EXPORT_CONTENT_MAP[key as ExportContentType]
|
|
|
@@ -133,11 +123,10 @@ const EstateExportHistory: React.FC = () => {
|
|
|
}
|
|
|
], []);
|
|
|
|
|
|
- // 9. 初始化数据(优化加载状态,模拟真实接口延迟)
|
|
|
+ // 初始化数据
|
|
|
useEffect(() => {
|
|
|
const fetchHistory = async () => {
|
|
|
try {
|
|
|
- // 模拟接口请求延迟
|
|
|
await new Promise(resolve => setTimeout(resolve, 600));
|
|
|
|
|
|
const mockBackendData: IEstateExportHistory[] = [
|
|
|
@@ -165,16 +154,15 @@ const EstateExportHistory: React.FC = () => {
|
|
|
fetchHistory();
|
|
|
}, []);
|
|
|
|
|
|
- // 10. Grid 初始化回调(保存 API 实例,用于后续操作)
|
|
|
+ // Grid 初始化回调
|
|
|
const onGridReady = useCallback((params: GridReadyEvent<IEstateExportHistory>) => {
|
|
|
setGridApi(params.api);
|
|
|
}, []);
|
|
|
|
|
|
- // 11. 导出处理逻辑(优化异步流程,增强用户反馈)
|
|
|
+ // 导出处理逻辑
|
|
|
const handleExport = useCallback(async () => {
|
|
|
if (isExporting) return;
|
|
|
|
|
|
- // 验证基準日
|
|
|
if (!baseDate) {
|
|
|
alert('基準日を入力してください');
|
|
|
return;
|
|
|
@@ -188,7 +176,6 @@ const EstateExportHistory: React.FC = () => {
|
|
|
setIsExporting(true);
|
|
|
|
|
|
try {
|
|
|
- // 模拟接口请求(实际项目中替换为 axios/fetch)
|
|
|
await new Promise(resolve => setTimeout(resolve, 800));
|
|
|
|
|
|
const newHistory: IEstateExportHistory = {
|
|
|
@@ -198,12 +185,10 @@ const EstateExportHistory: React.FC = () => {
|
|
|
exportContent: selectedContent
|
|
|
};
|
|
|
|
|
|
- // 新增数据添加到列表顶部,并保持排序
|
|
|
setHistoryList(prev => [newHistory, ...prev].sort((a, b) =>
|
|
|
b.exportDate.localeCompare(a.exportDate)
|
|
|
));
|
|
|
|
|
|
- // 刷新 Grid 视图
|
|
|
gridApi?.refreshCells();
|
|
|
alert('エクスポートが完了しました');
|
|
|
} catch (error) {
|
|
|
@@ -215,107 +200,111 @@ const EstateExportHistory: React.FC = () => {
|
|
|
}
|
|
|
}, [baseDate, selectedContent, isExporting, gridApi]);
|
|
|
|
|
|
- // 12. 日期输入处理(限制输入格式,增强用户体验)
|
|
|
+ // 日期输入处理
|
|
|
const handleDateChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
- // 只允许输入数字,且长度不超过 8
|
|
|
const value = e.target.value.replace(/\D/g, '').slice(0, 8);
|
|
|
setBaseDate(value);
|
|
|
}, []);
|
|
|
|
|
|
return (
|
|
|
- <div className={styles.container}>
|
|
|
- <h2 className={styles.title}>エクスポート</h2>
|
|
|
-
|
|
|
- {/* 导出操作区 */}
|
|
|
- <div className={styles.exportSection}>
|
|
|
- <h3 className={styles.subtitle}>エクスポート操作</h3>
|
|
|
- <div className={styles.formGroup}>
|
|
|
- <div className={styles.formItem}>
|
|
|
- <label className={styles.label}>基準日</label>
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- className={styles.input}
|
|
|
- placeholder="yyyymmdd"
|
|
|
- value={baseDate}
|
|
|
- onChange={handleDateChange}
|
|
|
- maxLength={8}
|
|
|
- disabled={isExporting}
|
|
|
- aria-label="基準日入力"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div className={styles.formItem}>
|
|
|
- <label className={styles.label}>出力内容</label>
|
|
|
- <div className={styles.radioGroup}>
|
|
|
- <label className={styles.radioItem}>
|
|
|
+ <div className={styles.container}>
|
|
|
+ <h2 className={styles.title}>エクスポート</h2>
|
|
|
+
|
|
|
+ <div className={styles.exportSection}>
|
|
|
+ <h3 className={styles.subtitle}>エクスポート操作</h3>
|
|
|
+ {/* 网格布局容器 */}
|
|
|
+ <div className={styles.formGroup}>
|
|
|
+ {/* 基准日行:标签 + 输入框 */}
|
|
|
+ <div className={styles.formRow}>
|
|
|
+ <label className={styles.label}>基準日</label>
|
|
|
+ <div className={styles.control}>
|
|
|
<input
|
|
|
- type="radio"
|
|
|
- name="exportContent"
|
|
|
- value={ExportContentType.PROPERTY_MASTER}
|
|
|
- checked={selectedContent === ExportContentType.PROPERTY_MASTER}
|
|
|
- onChange={() => setSelectedContent(ExportContentType.PROPERTY_MASTER)}
|
|
|
- disabled={isExporting}
|
|
|
+ type="text"
|
|
|
+ className={styles.input}
|
|
|
+ placeholder="yyyymmdd"
|
|
|
+ value={baseDate}
|
|
|
+ onChange={handleDateChange}
|
|
|
+ maxLength={8}
|
|
|
+ disabled={isExporting}
|
|
|
/>
|
|
|
- <span className={styles.radioLabel}>物件マスタCSV</span>
|
|
|
- </label>
|
|
|
- <label className={styles.radioItem}>
|
|
|
- <input
|
|
|
- type="radio"
|
|
|
- name="exportContent"
|
|
|
- value={ExportContentType.ANALYSIS}
|
|
|
- checked={selectedContent === ExportContentType.ANALYSIS}
|
|
|
- onChange={() => setSelectedContent(ExportContentType.ANALYSIS)}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 出力内容行:标签 + radio组 */}
|
|
|
+ <div className={styles.formRow}>
|
|
|
+ <label className={styles.label}>出力内容</label>
|
|
|
+ <div className={styles.control}>
|
|
|
+ <div className={styles.radioGroup}>
|
|
|
+ <label className={styles.radioItem}>
|
|
|
+ <input
|
|
|
+ type="radio"
|
|
|
+ name="exportContent"
|
|
|
+ value={ExportContentType.PROPERTY_MASTER}
|
|
|
+ checked={selectedContent === ExportContentType.PROPERTY_MASTER}
|
|
|
+ onChange={() => setSelectedContent(ExportContentType.PROPERTY_MASTER)}
|
|
|
+ disabled={isExporting}
|
|
|
+ />
|
|
|
+ <span className={styles.radioLabel}>物件マスタCSV</span>
|
|
|
+ </label>
|
|
|
+ <label className={styles.radioItem}>
|
|
|
+ <input
|
|
|
+ type="radio"
|
|
|
+ name="exportContent"
|
|
|
+ value={ExportContentType.ANALYSIS}
|
|
|
+ checked={selectedContent === ExportContentType.ANALYSIS}
|
|
|
+ onChange={() => setSelectedContent(ExportContentType.ANALYSIS)}
|
|
|
+ disabled={isExporting}
|
|
|
+ />
|
|
|
+ <span className={styles.radioLabel}>分析用CSV</span>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 按钮行 */}
|
|
|
+ <div className={styles.buttonRow}>
|
|
|
+ <button
|
|
|
+ className={`${styles.exportBtn} ${isExporting ? styles.disabledBtn : ''}`}
|
|
|
+ onClick={handleExport}
|
|
|
disabled={isExporting}
|
|
|
- />
|
|
|
- <span className={styles.radioLabel}>分析用CSV</span>
|
|
|
- </label>
|
|
|
+ >
|
|
|
+ {isExporting ? '処理中...' : '出力'}
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <button
|
|
|
- className={`${styles.exportBtn} ${isExporting ? styles.disabledBtn : ''}`}
|
|
|
- onClick={handleExport}
|
|
|
- disabled={isExporting}
|
|
|
- >
|
|
|
- {isExporting ? '処理中...' : '出力'}
|
|
|
- </button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- {/* 导出历史区 */}
|
|
|
- <div className={styles.historySection}>
|
|
|
- <h3 className={styles.subtitle}>出力履歴</h3>
|
|
|
- <div className={styles.underline}></div>
|
|
|
-
|
|
|
- {isLoading ? (
|
|
|
- // 加载状态
|
|
|
- <div className={styles.loading}>
|
|
|
- <span>履歴データを読み込んでいます...</span>
|
|
|
- </div>
|
|
|
- ) : historyList.length === 0 ? (
|
|
|
- // 空状态
|
|
|
- <div className={styles.emptyState}>
|
|
|
- <span>出力履歴がありません</span>
|
|
|
- </div>
|
|
|
- ) : (
|
|
|
- // AG Grid 表格
|
|
|
- <div className={`ag-theme-alpine ${styles.gridContainer}`} style={containerStyle}>
|
|
|
- <AgGridReact<IEstateExportHistory>
|
|
|
- rowData={historyList}
|
|
|
- columnDefs={columnDefs}
|
|
|
- defaultColDef={defaultColDef}
|
|
|
- pagination={true}
|
|
|
- paginationPageSize={paginationPageSize}
|
|
|
- paginationPageSizeSelector={[5, 10, 20]}
|
|
|
- onGridReady={onGridReady}
|
|
|
- animateRows={true}
|
|
|
- suppressDragLeaveHidesColumns={true}
|
|
|
- enableCellTextSelection={true}
|
|
|
- />
|
|
|
- </div>
|
|
|
- )}
|
|
|
+ {/* 导出历史区 */}
|
|
|
+ <div className={styles.historySection}>
|
|
|
+ <h3 className={styles.subtitle}>出力履歴</h3>
|
|
|
+ <div className={styles.underline}></div>
|
|
|
+
|
|
|
+ {isLoading ? (
|
|
|
+ <div className={styles.loading}>
|
|
|
+ <span>履歴データを読み込んでいます...</span>
|
|
|
+ </div>
|
|
|
+ ) : historyList.length === 0 ? (
|
|
|
+ <div className={styles.emptyState}>
|
|
|
+ <span>出力履歴がありません</span>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div className={`ag-theme-alpine ${styles.gridContainer}`} style={containerStyle}>
|
|
|
+ <AgGridReact<IEstateExportHistory>
|
|
|
+ rowData={historyList}
|
|
|
+ columnDefs={columnDefs}
|
|
|
+ defaultColDef={defaultColDef}
|
|
|
+ pagination={true}
|
|
|
+ paginationPageSize={paginationPageSize}
|
|
|
+ paginationPageSizeSelector={[5, 10, 20]}
|
|
|
+ onGridReady={onGridReady}
|
|
|
+ animateRows={true}
|
|
|
+ suppressDragLeaveHidesColumns={true}
|
|
|
+ enableCellTextSelection={true}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
);
|
|
|
};
|
|
|
|