Browse Source

fix(nc-gui): add encoding option in csv export

nc-feat/export-csv-delimiters
Ramesh Mane 2 days ago
parent
commit
2da47e476d
  1. 93
      packages/nc-gui/extensions/data-exporter/index.vue
  2. 1
      packages/nocodb/src/interface/Jobs.ts
  3. 3
      packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts
  4. 5
      packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts

93
packages/nc-gui/extensions/data-exporter/index.vue

@ -94,8 +94,10 @@ const exportPayload = ref<{
tableId?: string tableId?: string
viewId?: string viewId?: string
delimiter?: string delimiter?: string
encoding?: BufferEncoding
}>({ }>({
delimiter: ',', delimiter: ',',
encoding: 'utf8',
}) })
const tableList = computed(() => { const tableList = computed(() => {
@ -166,6 +168,7 @@ async function exportDataAsync() {
const jobData = await $api.export.data(exportPayload.value.viewId, 'csv', { const jobData = await $api.export.data(exportPayload.value.viewId, 'csv', {
extension_id: extension.value.id, extension_id: extension.value.id,
delimiter: exportPayload.value.delimiter, delimiter: exportPayload.value.delimiter,
encoding: exportPayload.value.encoding,
}) })
jobList.value.unshift(jobData) jobList.value.unshift(jobData)
@ -279,6 +282,7 @@ eventBus.on(async (event, payload) => {
onMounted(async () => { onMounted(async () => {
exportPayload.value = extension.value.kvStore.get('exportPayload') || {} exportPayload.value = extension.value.kvStore.get('exportPayload') || {}
exportPayload.value.delimiter = exportPayload.value.delimiter || ',' exportPayload.value.delimiter = exportPayload.value.delimiter || ','
exportPayload.value.encoding = exportPayload.value.encoding || 'utf8'
deletedExports.value = extension.value.kvStore.get('deletedExports') || [] deletedExports.value = extension.value.kvStore.get('deletedExports') || []
@ -411,33 +415,68 @@ onMounted(async () => {
</div> </div>
</div> </div>
<div v-if="fullscreen" class="flex items-center gap-2"> <div
<div class="min-w-[65px]">Separator</div> v-if="fullscreen"
<a-form-item class="!my-0 flex-1 max-w-[237px]"> class="flex items-center gap-3 flex-wrap"
<NcSelect :class="{
v-model:value="exportPayload.delimiter" 'max-w-[474px]': fullscreen,
placeholder="-select separator-" }"
:disabled="isExporting" >
class="nc-data-exporter-separator nc-select-shadow !w-[120px]" <div class="flex items-center gap-2 flex-1">
dropdown-class-name="w-[180px]" <div class="min-w-[65px]">Separator</div>
@change="saveChanges" <a-form-item class="!my-0 flex-1">
> <NcSelect
<a-select-option v-for="delimiter of delimiters" :key="delimiter.value" :value="delimiter.value"> v-model:value="exportPayload.delimiter"
<div class="w-full flex items-center gap-2"> placeholder="-select separator-"
<NcTooltip class="flex-1 truncate" show-on-truncate-only> :disabled="isExporting"
<template #title>{{ delimiter.label }}</template> class="nc-data-exporter-separator nc-select-shadow"
<span>{{ delimiter.label }}</span> dropdown-class-name="w-[180px]"
</NcTooltip> @change="saveChanges"
<component >
:is="iconMap.check" <a-select-option v-for="delimiter of delimiters" :key="delimiter.value" :value="delimiter.value">
v-if="exportPayload.delimiter === delimiter.value" <div class="w-full flex items-center gap-2">
id="nc-selected-item-icon" <NcTooltip class="flex-1 truncate" show-on-truncate-only>
class="flex-none text-primary w-4 h-4" <template #title>{{ delimiter.label }}</template>
/> <span>{{ delimiter.label }}</span>
</div> </NcTooltip>
</a-select-option> <component
</NcSelect> :is="iconMap.check"
</a-form-item> v-if="exportPayload.delimiter === delimiter.value"
id="nc-selected-item-icon"
class="flex-none text-primary w-4 h-4"
/>
</div>
</a-select-option>
</NcSelect>
</a-form-item>
</div>
<div class="flex items-center gap-2 flex-1">
<div class="min-w-[65px]">Encoding</div>
<a-form-item class="!my-0 flex-1">
<NcSelect
v-model:value="exportPayload.encoding"
placeholder="-select separator-"
class="nc-csv-import-separator nc-select-shadow"
dropdown-class-name="w-[190px]"
@change="saveChanges"
>
<a-select-option v-for="encoding of encodings" :key="encoding.value" :value="encoding.value">
<div class="w-full flex items-center gap-2">
<NcTooltip class="flex-1 truncate" show-on-truncate-only>
<template #title>{{ encoding.label }}</template>
<span>{{ encoding.label }}</span>
</NcTooltip>
<component
:is="iconMap.check"
v-if="exportPayload.encoding === encoding.value"
id="nc-selected-item-icon"
class="flex-none text-primary w-4 h-4"
/>
</div>
</a-select-option>
</NcSelect>
</a-form-item>
</div>
</div> </div>
</div> </div>
<div <div

1
packages/nocodb/src/interface/Jobs.ts

@ -152,6 +152,7 @@ export interface DataExportJobData extends JobData {
options?: { options?: {
delimiter?: string; delimiter?: string;
extension_id?: string; extension_id?: string;
encoding?: BufferEncoding;
}; };
modelId: string; modelId: string;
viewId: string; viewId: string;

3
packages/nocodb/src/modules/jobs/jobs/data-export/data-export.processor.ts

@ -59,7 +59,7 @@ export class DataExportProcessor {
read() {}, read() {},
}); });
dataStream.setEncoding('utf8'); dataStream.setEncoding(options.encoding || 'utf8');
let error = null; let error = null;
@ -79,6 +79,7 @@ export class DataExportProcessor {
viewId: view.id, viewId: view.id,
ncSiteUrl: ncSiteUrl, ncSiteUrl: ncSiteUrl,
delimiter: options?.delimiter, delimiter: options?.delimiter,
encoding: options?.encoding || 'utf8',
}) })
.catch((e) => { .catch((e) => {
this.logger.debug(e); this.logger.debug(e);

5
packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts

@ -461,6 +461,7 @@ export class ExportService {
_fieldIds?: string[]; _fieldIds?: string[];
ncSiteUrl?: string; ncSiteUrl?: string;
delimiter?: string; delimiter?: string;
encoding?: BufferEncoding;
}, },
) { ) {
const { dataStream, linkStream, handledMmList } = param; const { dataStream, linkStream, handledMmList } = param;
@ -537,7 +538,7 @@ export class ExportService {
const hasLink = !dataExportMode && mmColumns.length > 0; const hasLink = !dataExportMode && mmColumns.length > 0;
dataStream.setEncoding('utf8'); dataStream.setEncoding(param.encoding || 'utf8');
const formatData = (data: any) => { const formatData = (data: any) => {
for (const row of data) { for (const row of data) {
@ -640,7 +641,7 @@ export class ExportService {
} }
if (hasLink) { if (hasLink) {
linkStream.setEncoding('utf8'); linkStream.setEncoding(param.encoding || 'utf8');
let streamedHeaders = false; let streamedHeaders = false;

Loading…
Cancel
Save