feat(import): allow empty string as null for Excel import

This commit is contained in:
lxk955
2026-10-11 08:46:09 +08:00
committed by GitHub
parent 39505896a6
commit c744598425
14 changed files with 54 additions and 0 deletions
@@ -1698,6 +1698,13 @@ watch(rawProgressPercent, (percent) => {
<Label class="text-xs">{{ t("tableImport.lastDataRow") }}</Label>
<Input v-model.number="lastDataRow" type="number" min="0" class="h-8 text-xs" />
</div>
<div class="col-span-4 space-y-1">
<label class="flex items-center gap-2 text-xs">
<input v-model="emptyStringAsNull" type="checkbox" class="h-3.5 w-3.5 accent-primary" />
{{ t("tableImport.emptyStringAsNull") }}
</label>
<p class="text-[11px] text-muted-foreground">{{ t("tableImport.emptyStringAsNullExcelHint") }}</p>
</div>
</div>
<div v-if="supportsImportConflictPolicy" class="space-y-1.5 rounded-md border p-3">
@@ -650,4 +650,39 @@ describe("TableImportDialog batch selection", () => {
expect(document.body.textContent).not.toContain("Title row must be before the data start row");
expect(button("Next").disabled).toBe(false);
});
it("renders empty string as null option for Excel source and passes it to parse options (#11608)", async () => {
let capturedOptions: any = null;
mocks.previewTableImportFile.mockImplementation((_source, options) => {
capturedOptions = options;
return Promise.resolve({
fileName: "rows.xlsx",
filePath: "/tmp/rows.xlsx",
sourceRef: "workbook-source",
fileType: "excel",
sizeBytes: 10,
columns: ["id", "name"],
rows: [[1, "test"]],
totalRows: 1,
totalRowsExact: true,
sourceFingerprint: "rows-Sheet1",
sheets: ["Sheet1"],
});
});
await mountDialog([new File(["workbook"], "rows.xlsx")]);
expect(button("Next").disabled).toBe(false);
const emptyStringCheckbox = [...document.body.querySelectorAll<HTMLInputElement>('input[type="checkbox"]')].find((el) => el.parentElement?.textContent?.includes("Empty string as NULL"));
expect(emptyStringCheckbox).toBeTruthy();
expect(emptyStringCheckbox!.checked).toBe(false);
emptyStringCheckbox!.click();
await new Promise((resolve) => setTimeout(resolve, 300));
await flushAsyncUpdates();
expect(capturedOptions?.parseOptions?.emptyStringAsNull).toBe(true);
await startImport();
expect(mocks.importTableFile.mock.calls.at(-1)?.[0].parseOptions.emptyStringAsNull).toBe(true);
});
});
+1
View File
@@ -6282,6 +6282,7 @@ export default withEnglishFallback({
trimValues: "Dəyərlərin əvvəlindəki və sonundakı boşluqları sil",
emptyStringAsNull: "Boş sətri NULL kimi qəbul et",
emptyStringAsNullHint: "İşarələndikdə boş sahələr NULL kimi idxal olunur və \\N adi mətn olur; əks halda boş sahələr boş sətir qalır və \\N NULL kimi idxal olunur.",
emptyStringAsNullExcelHint: "İşarələndikdə boş sətirlər NULL kimi idxal olunur; əks halda boş sətir kimi saxlanılır.",
jsonShape: "JSON sətirləri",
jsonShapeAuto: "Avtomatik müəyyən et",
jsonShapeObjects: "Obyektlər",
+1
View File
@@ -7181,6 +7181,7 @@ export default {
trimValues: "Trim values",
emptyStringAsNull: "Empty string as NULL",
emptyStringAsNullHint: "When checked, empty fields import as NULL and \\N becomes plain text; otherwise empty fields stay as empty strings and \\N imports as NULL.",
emptyStringAsNullExcelHint: "When checked, empty strings are imported as NULL; otherwise, they are preserved as empty strings.",
conflictPolicy: "On primary-key conflict",
conflictError: "Stop with an error",
skipDuplicateRows: "Skip duplicate rows",
+1
View File
@@ -6545,6 +6545,7 @@ export default withEnglishFallback({
trimValues: "Trim values",
emptyStringAsNull: "Empty string as NULL",
emptyStringAsNullHint: "Si se activa, los campos vacíos se importan como NULL y \\N pasa a ser texto; si no, los campos vacíos se mantienen como cadenas vacías y \\N se importa como NULL.",
emptyStringAsNullExcelHint: "Si se activa, las cadenas vacías se importan como NULL; si no, se mantienen como cadenas vacías.",
conflictPolicy: "Ante conflicto de clave primaria",
conflictError: "Detener con error",
skipDuplicateRows: "Omitir filas duplicadas",
+1
View File
@@ -6936,6 +6936,7 @@ export default withEnglishFallback({
trimValues: "Pangkas nilai",
emptyStringAsNull: "String kosong sebagai NULL",
emptyStringAsNullHint: "Jika dicentang, bidang kosong diimpor sebagai NULL dan \\N menjadi teks biasa; jika tidak, bidang kosong tetap berupa string kosong dan \\N diimpor sebagai NULL.",
emptyStringAsNullExcelHint: "Jika dicentang, string kosong diimpor sebagai NULL; jika tidak, tetap berupa string kosong.",
conflictPolicy: "Saat terjadi konflik kunci utama",
conflictError: "Hentikan dengan galat",
skipDuplicateRows: "Lewati baris duplikat",
+1
View File
@@ -6347,6 +6347,7 @@ export default withEnglishFallback({
trimValues: "Trim values",
emptyStringAsNull: "Empty string as NULL",
emptyStringAsNullHint: "Se attivo, i campi vuoti vengono importati come NULL e \\N diventa testo normale; altrimenti restano stringhe vuote e \\N viene importato come NULL.",
emptyStringAsNullExcelHint: "Se attivo, le stringhe vuote vengono importate come NULL; altrimenti rimangono stringhe vuote.",
jsonShape: "JSON rows",
jsonShapeAuto: "Auto-detect",
jsonShapeObjects: "Objects",
+1
View File
@@ -6363,6 +6363,7 @@ export default withEnglishFallback({
trimValues: "Trim values",
emptyStringAsNull: "Empty string as NULL",
emptyStringAsNullHint: "チェックすると空フィールドを NULL として取り込み、\\N は通常のテキストになります。チェックしない場合は空フィールドは空文字列のままとなり、\\N を NULL として取り込みます。",
emptyStringAsNullExcelHint: "チェックすると空文字列を NULL として取り込みます。チェックしない場合は空文字列のまま保持します。",
jsonShape: "JSON rows",
jsonShapeAuto: "Auto-detect",
jsonShapeObjects: "Objects",
+1
View File
@@ -6144,6 +6144,7 @@ export default withEnglishFallback({
trimValues: "값 양끝 공백 제거",
emptyStringAsNull: "빈 문자열을 NULL로",
emptyStringAsNullHint: "체크하면 빈 필드를 NULL로 가져오고 \\N은 일반 텍스트가 됩니다. 해제하면 빈 필드는 빈 문자열로 유지되고 \\N은 NULL로 가져옵니다.",
emptyStringAsNullExcelHint: "체크하면 빈 문자열을 NULL로 가져옵니다. 해제하면 빈 문자열로 유지됩니다.",
jsonShape: "JSON 행",
jsonShapeAuto: "자동 감지",
jsonShapeObjects: "객체",
+1
View File
@@ -6346,6 +6346,7 @@ export default withEnglishFallback({
trimValues: "Trim values",
emptyStringAsNull: "Empty string as NULL",
emptyStringAsNullHint: "Quando marcado, campos vazios são importados como NULL e \\N passa a ser texto; caso contrário, permanecem como strings vazias e \\N é importado como NULL.",
emptyStringAsNullExcelHint: "Quando marcado, strings vazias são importadas como NULL; caso contrário, permanecem como strings vazias.",
jsonShape: "JSON rows",
jsonShapeAuto: "Auto-detect",
jsonShapeObjects: "Objects",
+1
View File
@@ -7537,6 +7537,7 @@ export default withEnglishFallback({
trimValues: "Обрезать значения",
emptyStringAsNull: "Пустая строка как NULL",
emptyStringAsNullHint: "Если включено, пустые поля импортируются как NULL, а \\N считается обычным текстом; иначе пустые поля остаются пустыми строками, а \\N импортируется как NULL.",
emptyStringAsNullExcelHint: "Если включено, пустые строки импортируются как NULL; иначе сохраняются как пустые строки.",
jsonShape: "Строки JSON",
jsonShapeAuto: "Автоопределение",
jsonShapeObjects: "Объекты",
+1
View File
@@ -6163,6 +6163,7 @@ export default withEnglishFallback({
trimValues: "Değerleri kırp",
emptyStringAsNull: "Boş dizeyi NULL say",
emptyStringAsNullHint: "İşaretlendiğinde boş alanlar NULL olarak içe aktarılır ve \\N düz metin olur; aksi halde boş alanlar boş dize kalır ve \\N NULL olarak içe aktarılır.",
emptyStringAsNullExcelHint: "İşaretlendiğinde boş dizeler NULL olarak içe aktarılır; aksi halde boş dize olarak korunur.",
jsonShape: "JSON satırları",
jsonShapeAuto: "Otomatik algıla",
jsonShapeObjects: "Nesneler",
+1
View File
@@ -7166,6 +7166,7 @@ export default withEnglishFallback({
trimValues: "裁剪空白",
emptyStringAsNull: "空字符串作为 NULL",
emptyStringAsNullHint: "勾选后空字段一律按 NULL 导入(此时文件里的 \\N 按普通文本导入);不勾选则空字段保留为空字符串,并把 \\N 按 NULL 导入。",
emptyStringAsNullExcelHint: "勾选后空字符串识别为 NULL;不勾选则保留为空字符串。",
conflictPolicy: "主键冲突时",
conflictError: "停止并报错",
skipDuplicateRows: "跳过重复行",
+1
View File
@@ -5681,6 +5681,7 @@ export default withEnglishFallback({
trimValues: "裁剪空白",
emptyStringAsNull: "空字串作為 NULL",
emptyStringAsNullHint: "勾選後空欄位一律以 NULL 匯入(此時檔案裡的 \\N 以一般文字匯入);不勾選則空欄位保留為空字串,並把 \\N 以 NULL 匯入。",
emptyStringAsNullExcelHint: "勾選後空字串識別為 NULL;不勾選則保留為空字串。",
conflictPolicy: "主鍵衝突時",
conflictError: "停止並回報錯誤",
skipDuplicateRows: "略過重複列",