fix(dc/tool-c): Fix special character handling and improve UX
Major fixes: - Fix pivot transformation with special characters in column names - Fix compute column validation for Chinese punctuation - Fix recode dialog to fetch unique values from full dataset via new API - Add column mapping mechanism to handle special characters Database migration: - Add column_mapping field to dc_tool_c_sessions table - Migration file: 20251208_add_column_mapping UX improvements: - Darken table grid lines for better visibility - Reduce column width by 40% with tooltip support - Insert new columns next to source columns - Preserve original row order after operations - Add notice about 50-row preview limit Modified files: - Backend: SessionService, SessionController, QuickActionService, routes - Python: pivot.py, compute.py, recode.py, binning.py, conditional.py - Frontend: DataGrid, RecodeDialog, index.tsx, ag-grid-custom.css - Database: schema.prisma, migration SQL Status: Code complete, database migrated, ready for testing
This commit is contained in:
@@ -516,3 +516,4 @@ export default FulltextDetailDrawer;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -115,3 +115,4 @@ export function useFulltextResults({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -78,3 +78,4 @@ export function useFulltextTask({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -469,3 +469,4 @@ export default FulltextResults;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -109,3 +109,4 @@ export const useAssets = (activeTab: AssetTabType) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -99,3 +99,4 @@ export const useRecentTasks = () => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -335,3 +335,4 @@ const BinningDialog: React.FC<BinningDialogProps> = ({
|
||||
|
||||
export default BinningDialog;
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ const DataGrid: React.FC<DataGridProps> = ({ data, columns, onCellValueChanged }
|
||||
// ✅ 修复:使用安全的field名(索引),通过valueGetter获取实际数据
|
||||
field: `col_${index}`,
|
||||
headerName: col.name,
|
||||
// ✅ 优化:添加tooltip显示完整列名
|
||||
headerTooltip: col.name,
|
||||
// ✅ 关键修复:使用valueGetter直接从原始数据中获取值
|
||||
valueGetter: (params: any) => {
|
||||
return params.data?.[col.id];
|
||||
@@ -49,8 +51,8 @@ const DataGrid: React.FC<DataGridProps> = ({ data, columns, onCellValueChanged }
|
||||
filter: true,
|
||||
resizable: true,
|
||||
editable: false, // MVP阶段暂不支持手动编辑
|
||||
width: 150, // ✅ 增加默认宽度,适应长列名
|
||||
minWidth: 100,
|
||||
width: 90, // ✅ 优化:减小40%(原150 -> 90)
|
||||
minWidth: 60,
|
||||
|
||||
// ✅ 优化1.3:缺失值高亮(新CSS类名)
|
||||
cellClass: (params) => {
|
||||
|
||||
@@ -298,3 +298,4 @@ const DropnaDialog: React.FC<Props> = ({
|
||||
|
||||
export default DropnaDialog;
|
||||
|
||||
|
||||
|
||||
@@ -260,3 +260,4 @@ const PivotDialog: React.FC<Props> = ({
|
||||
|
||||
export default PivotDialog;
|
||||
|
||||
|
||||
|
||||
@@ -41,35 +41,55 @@ const RecodeDialog: React.FC<RecodeDialogProps> = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [extracting, setExtracting] = useState(false);
|
||||
|
||||
// 当选择列时,提取唯一值
|
||||
// 当选择列时,从后端获取唯一值
|
||||
useEffect(() => {
|
||||
if (!selectedColumn || !data || data.length === 0) {
|
||||
if (!selectedColumn || !sessionId) {
|
||||
setUniqueValues([]);
|
||||
setMappingTable([]);
|
||||
return;
|
||||
}
|
||||
|
||||
setExtracting(true);
|
||||
const fetchUniqueValues = async () => {
|
||||
setExtracting(true);
|
||||
|
||||
try {
|
||||
// ✨ 调用后端API获取唯一值(从完整数据中提取,不受前端50行限制)
|
||||
const response = await fetch(
|
||||
`/api/v1/dc/tool-c/sessions/${sessionId}/unique-values?column=${encodeURIComponent(selectedColumn)}`
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || '获取唯一值失败');
|
||||
}
|
||||
|
||||
const unique = result.data.uniqueValues;
|
||||
|
||||
setUniqueValues(unique);
|
||||
|
||||
// 初始化映射表
|
||||
const initialMapping = unique.map((val: any) => ({
|
||||
originalValue: val,
|
||||
newValue: '',
|
||||
}));
|
||||
|
||||
setMappingTable(initialMapping);
|
||||
|
||||
// 生成默认新列名
|
||||
setNewColumnName(`${selectedColumn}_编码`);
|
||||
} catch (error: any) {
|
||||
console.error('[RecodeDialog] 获取唯一值失败:', error);
|
||||
message.error(error.message || '获取唯一值失败');
|
||||
setUniqueValues([]);
|
||||
setMappingTable([]);
|
||||
} finally {
|
||||
setExtracting(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 提取唯一值
|
||||
const values = data.map((row) => row[selectedColumn]);
|
||||
const unique = Array.from(new Set(values)).filter(v => v !== null && v !== undefined && v !== '');
|
||||
|
||||
setUniqueValues(unique);
|
||||
|
||||
// 初始化映射表
|
||||
const initialMapping = unique.map((val) => ({
|
||||
originalValue: val,
|
||||
newValue: '',
|
||||
}));
|
||||
|
||||
setMappingTable(initialMapping);
|
||||
|
||||
// 生成默认新列名
|
||||
setNewColumnName(`${selectedColumn}_编码`);
|
||||
|
||||
setExtracting(false);
|
||||
}, [selectedColumn, data]);
|
||||
fetchUniqueValues();
|
||||
}, [selectedColumn, sessionId, message]);
|
||||
|
||||
// 更新映射值
|
||||
const updateMapping = (originalValue: any, newValue: string) => {
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
--ag-foreground-color: #1e293b;
|
||||
|
||||
/* ==================== 边框(关键优化)==================== */
|
||||
--ag-border-color: #e5e7eb; /* ✅ 优化:边框颜色统一 */
|
||||
--ag-row-border-color: #f1f5f9; /* ✅ 优化:极淡的横向分割线 */
|
||||
--ag-border-color: #d1d5db; /* ✅ 优化:边框颜色加深(原#e5e7eb -> #d1d5db) */
|
||||
--ag-row-border-color: #e5e7eb; /* ✅ 优化:横向分割线加深(原#f1f5f9 -> #e5e7eb) */
|
||||
--ag-row-border-width: 1px;
|
||||
--ag-borders: none; /* ✅ 优化:去除所有边框 */
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
border-right: none !important; /* ✅ 优化1.1:去除纵向边框 */
|
||||
border-bottom: 2px solid #e5e7eb; /* ✅ 优化:只保留底部边框 */
|
||||
border-bottom: 2px solid #d1d5db; /* ✅ 优化:底部边框加深(原#e5e7eb -> #d1d5db) */
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-header-cell:hover {
|
||||
|
||||
@@ -251,8 +251,19 @@ const ToolC = () => {
|
||||
onComputeClick={() => updateState({ computeDialogVisible: true })}
|
||||
onPivotClick={() => updateState({ pivotDialogVisible: true })}
|
||||
/>
|
||||
<div className="flex-1 p-4 overflow-hidden">
|
||||
<DataGrid data={state.data} columns={state.columns} />
|
||||
<div className="flex-1 p-4 overflow-hidden flex flex-col">
|
||||
{/* ✨ 优化:提示只显示前50行 */}
|
||||
{state.data.length > 0 && (
|
||||
<div className="mb-2 px-3 py-2 bg-blue-50 border border-blue-200 rounded-lg flex items-center gap-2 text-sm">
|
||||
<span className="text-blue-600">ℹ️</span>
|
||||
<span className="text-blue-700">
|
||||
<strong>提示:</strong>表格仅展示前 <strong>50行</strong> 数据预览,导出功能将包含 <strong>全部</strong> 处理结果
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<DataGrid data={state.data} columns={state.columns} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,3 +61,4 @@ export interface DataStats {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,3 +57,4 @@ export type AssetTabType = 'all' | 'processed' | 'raw';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user