mirror of https://github.com/nocodb/nocodb
աɨռɢӄաօռɢ
2 years ago
committed by
GitHub
9 changed files with 770 additions and 7 deletions
@ -0,0 +1,484 @@ |
|||||||
|
<template> |
||||||
|
<div :class="{'pt-10':!hideLabel}"> |
||||||
|
<v-dialog v-model="dropOrUpload" max-width="600"> |
||||||
|
<v-card max-width="600"> |
||||||
|
<v-tabs height="30"> |
||||||
|
<v-tab> |
||||||
|
<v-icon small class="mr-1"> |
||||||
|
mdi-file-upload-outline |
||||||
|
</v-icon> |
||||||
|
<span class="caption text-capitalize">Upload</span> |
||||||
|
</v-tab> |
||||||
|
<!-- <v-tab>--> |
||||||
|
<!-- <v-icon small class="mr-1"> |
||||||
|
mdi-link-variant |
||||||
|
</v-icon> |
||||||
|
<span class="caption text-capitalize">URL</span> |
||||||
|
</v-tab>--> |
||||||
|
<v-tab> |
||||||
|
<v-icon small class="mr-1"> |
||||||
|
mdi-link-variant |
||||||
|
</v-icon> |
||||||
|
<span class="caption text-capitalize">String</span> |
||||||
|
</v-tab> |
||||||
|
|
||||||
|
<v-tab-item> |
||||||
|
<div class="nc-json-import-tab-item "> |
||||||
|
<div |
||||||
|
class="nc-droppable d-flex align-center justify-center flex-column" |
||||||
|
:style="{ |
||||||
|
background : dragOver ? '#7772' : '' |
||||||
|
}" |
||||||
|
@click="$refs.file.click()" |
||||||
|
@drop.prevent="dropHandler" |
||||||
|
@dragover.prevent="dragOver = true" |
||||||
|
@dragenter.prevent="dragOver = true" |
||||||
|
@dragexit="dragOver = false" |
||||||
|
@dragleave="dragOver = false" |
||||||
|
@dragend="dragOver = false" |
||||||
|
> |
||||||
|
<x-icon :color="['primary','grey']" size="50"> |
||||||
|
mdi-file-plus-outline |
||||||
|
</x-icon> |
||||||
|
<p class="title mb-1 mt-2"> |
||||||
|
<!-- Select File to Upload--> |
||||||
|
{{ $t('msg.info.upload') }} |
||||||
|
</p> |
||||||
|
<p class="grey--text mb-1"> |
||||||
|
<!-- or drag and drop file--> |
||||||
|
{{ $t('msg.info.upload_sub') }} |
||||||
|
</p> |
||||||
|
|
||||||
|
<p v-if="quickImportType == 'excel'" class="caption grey--text"> |
||||||
|
<!-- Supported: .xls, .xlsx, .xlsm, .ods, .ots --> |
||||||
|
{{ $t('msg.info.excelSupport') }} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-tab-item> |
||||||
|
<!-- <v-tab-item> |
||||||
|
<div class="nc-json-import-tab-item align-center"> |
||||||
|
<div class="pa-4 d-100 h-100"> |
||||||
|
<v-form ref="form" v-model="valid"> |
||||||
|
<div class="d-flex"> |
||||||
|
<!– todo: i18n label–> |
||||||
|
<v-text-field |
||||||
|
v-model="url" |
||||||
|
hide-details="auto" |
||||||
|
type="url" |
||||||
|
label="Enter JSON file url" |
||||||
|
class="caption" |
||||||
|
outlined |
||||||
|
dense |
||||||
|
:rules=" |
||||||
|
[ |
||||||
|
v => !!v || $t('general.required'), |
||||||
|
v => !(/(10)(\.([2]([0-5][0-5]|[01234][6-9])|[1][0-9][0-9]|[1-9][0-9]|[0-9])){3}|(172)\.(1[6-9]|2[0-9]|3[0-1])(\.(2[0-4][0-9]|25[0-5]|[1][0-9][0-9]|[1-9][0-9]|[0-9])){2}|(192)\.(168)(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){2}|(0.0.0.0)|localhost?/g).test(v) || errorMessages.ipBlockList |
||||||
|
]" |
||||||
|
/> |
||||||
|
<v-btn v-t="['c:project:create:json:load-url']" class="ml-3" color="primary" @click="loadUrl"> |
||||||
|
<!–Load–> |
||||||
|
{{ $t('general.load') }} |
||||||
|
</v-btn> |
||||||
|
</div> |
||||||
|
</v-form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-tab-item>--> |
||||||
|
<v-tab-item> |
||||||
|
<div class="nc-json-import-tab-item align-center"> |
||||||
|
<div class="pa-4 d-100 h-100"> |
||||||
|
<v-form ref="form" v-model="valid"> |
||||||
|
<div class="nc-json-editor-wrapper"> |
||||||
|
<v-btn small class="nc-json-format-btn" @click="formatJson"> |
||||||
|
Format |
||||||
|
</v-btn> |
||||||
|
|
||||||
|
<!--label="Enter excel file url"--> |
||||||
|
<monaco-json-editor |
||||||
|
ref="editor" |
||||||
|
v-model="jsonString" |
||||||
|
style="height:320px" |
||||||
|
/> |
||||||
|
<div class="text-center mt-4"> |
||||||
|
<v-btn v-t="['c:project:create:excel:load-url']" class="ml-3" color="primary" @click="loadJsonString"> |
||||||
|
<!--Load--> |
||||||
|
{{ $t('general.load') }} |
||||||
|
</v-btn> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-form> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-tab-item> |
||||||
|
</v-tabs> |
||||||
|
|
||||||
|
<div class="px-4 pb-2"> |
||||||
|
<div class="d-flex"> |
||||||
|
<v-spacer /> |
||||||
|
<span class="caption pointer grey--text" @click="showMore = !showMore"> |
||||||
|
{{ showMore ? $t('general.hideAll') : $t('general.showMore') }} |
||||||
|
<v-icon small color="grey lighten-1">mdi-menu-{{ showMore ? 'up' : 'down' }}</v-icon> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div class="mb-2 pt-2 nc-json-import-options" :style="{ maxHeight: showMore ? '200px' : '0'}"> |
||||||
|
<p /> |
||||||
|
<!--hint="# of rows to parse to infer data type"--> |
||||||
|
<v-text-field |
||||||
|
v-model="parserConfig.maxRowsToParse" |
||||||
|
style="max-width: 250px" |
||||||
|
class="caption mx-auto" |
||||||
|
dense |
||||||
|
persistent-hint |
||||||
|
:hint="$t('msg.info.footMsg')" |
||||||
|
outlined |
||||||
|
type="number" |
||||||
|
/> |
||||||
|
|
||||||
|
<v-checkbox |
||||||
|
v-model="parserConfig.normalizeNested" |
||||||
|
style="width: 250px" |
||||||
|
class="mx-auto mb-2" |
||||||
|
dense |
||||||
|
hide-details |
||||||
|
> |
||||||
|
<template #label> |
||||||
|
<span class="caption">Flatten nested</span> |
||||||
|
<v-tooltip bottom position-y=""> |
||||||
|
<template #activator="{ on }"> |
||||||
|
<v-icon small class="ml-1" v-on="on"> |
||||||
|
mdi-information-outline |
||||||
|
</v-icon> |
||||||
|
</template> |
||||||
|
<div class="caption" style="width: 260px"> |
||||||
|
If flatten nested option is set it will flatten nested object as root level property. In normal case nested object will treat as JSON column. |
||||||
|
<br> |
||||||
|
<br> |
||||||
|
For example the following input: <code class="caption font-weight-bold">{ |
||||||
|
"prop1": { |
||||||
|
"prop2": "value" |
||||||
|
}, |
||||||
|
"prop3": "value", |
||||||
|
"prop4": 1 |
||||||
|
}</code> will treat as: |
||||||
|
<code class="caption font-weight-bold">{ |
||||||
|
"prop1_prop2": "value", |
||||||
|
"prop3": "value", |
||||||
|
"prop4": 1 |
||||||
|
}</code> |
||||||
|
</div> |
||||||
|
</v-tooltip> |
||||||
|
</template> |
||||||
|
</v-checkbox> |
||||||
|
<v-checkbox |
||||||
|
v-model="parserConfig.importData" |
||||||
|
style="width: 250px" |
||||||
|
class="mx-auto mb-2" |
||||||
|
dense |
||||||
|
hide-details |
||||||
|
> |
||||||
|
<template #label> |
||||||
|
<span class="caption">Import data</span> |
||||||
|
</template> |
||||||
|
</v-checkbox> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
<v-tooltip bottom> |
||||||
|
<template #activator="{on}"> |
||||||
|
<input |
||||||
|
ref="file" |
||||||
|
class="nc-json-import-input" |
||||||
|
type="file" |
||||||
|
style="display: none" |
||||||
|
accept=".json" |
||||||
|
@change="_change($event)" |
||||||
|
> |
||||||
|
<v-btn |
||||||
|
|
||||||
|
v-if="!hideLabel" |
||||||
|
small |
||||||
|
outlined |
||||||
|
v-on="on" |
||||||
|
@click="$refs.file.click()" |
||||||
|
> |
||||||
|
<v-icon small class="mr-1"> |
||||||
|
mdi-file-excel-outline |
||||||
|
</v-icon> |
||||||
|
<!--Import--> |
||||||
|
{{ $t('activity.import') }} |
||||||
|
</v-btn> |
||||||
|
</template> |
||||||
|
<span class="caption">Create template from JSON</span> |
||||||
|
</v-tooltip> |
||||||
|
|
||||||
|
<v-dialog v-if="templateData" v-model="templateEditorModal" max-width="1000"> |
||||||
|
<v-card class="pa-6" min-width="500"> |
||||||
|
<template-editor :project-template.sync="templateData" json-import :quick-import-type="quickImportType"> |
||||||
|
<template #toolbar="{valid}"> |
||||||
|
<h3 class="mt-2 grey--text"> |
||||||
|
<span> |
||||||
|
JSON Import |
||||||
|
</span> |
||||||
|
</h3> |
||||||
|
<v-spacer /> |
||||||
|
<v-spacer /> |
||||||
|
<create-project-from-template-btn |
||||||
|
:template-data="templateData" |
||||||
|
:import-data="importData" |
||||||
|
:import-to-project="importToProject" |
||||||
|
json-import |
||||||
|
:valid="valid" |
||||||
|
create-gql-text="Import as GQL Project" |
||||||
|
create-rest-text="Import as REST Project" |
||||||
|
@closeModal="$emit('closeModal'),templateEditorModal = false" |
||||||
|
> |
||||||
|
<!--Import Excel--> |
||||||
|
<span v-if="quickImportType === 'excel'"> |
||||||
|
{{ $t('activity.importExcel') }} |
||||||
|
</span> |
||||||
|
<!--Import CSV--> |
||||||
|
<span v-if="quickImportType === 'csv'"> |
||||||
|
{{ $t('activity.importCSV') }} |
||||||
|
</span> |
||||||
|
</create-project-from-template-btn> |
||||||
|
</template> |
||||||
|
</template-editor> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
|
||||||
|
import TemplateEditor from '~/components/templates/Editor' |
||||||
|
import CreateProjectFromTemplateBtn from '~/components/templates/CreateProjectFromTemplateBtn' |
||||||
|
import MonacoJsonEditor from '~/components/monaco/MonacoJsonEditor' |
||||||
|
import JSONTemplateAdapter from '~/components/import/templateParsers/JSONTemplateAdapter' |
||||||
|
import JSONUrlTemplateAdapter from '~/components/import/templateParsers/JSONUrlTemplateAdapter' |
||||||
|
|
||||||
|
export default { |
||||||
|
name: 'JsonImport', |
||||||
|
components: { MonacoJsonEditor, CreateProjectFromTemplateBtn, TemplateEditor }, |
||||||
|
props: { |
||||||
|
hideLabel: Boolean, |
||||||
|
value: Boolean, |
||||||
|
importToProject: Boolean, |
||||||
|
quickImportType: String |
||||||
|
}, |
||||||
|
data() { |
||||||
|
return { |
||||||
|
templateEditorModal: false, |
||||||
|
valid: null, |
||||||
|
templateData: null, |
||||||
|
importData: null, |
||||||
|
dragOver: false, |
||||||
|
url: '', |
||||||
|
showMore: false, |
||||||
|
parserConfig: { |
||||||
|
maxRowsToParse: 500, |
||||||
|
normalizeNested: true, |
||||||
|
importData: true |
||||||
|
}, |
||||||
|
filename: '', |
||||||
|
jsonString: '', |
||||||
|
errorMessages: { |
||||||
|
ipBlockList: 'IP Not allowed!', |
||||||
|
importJSON: 'Target file is not an accepted file type. The accepted file type is .json!' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
dropOrUpload: { |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
}, |
||||||
|
get() { |
||||||
|
return this.value |
||||||
|
} |
||||||
|
}, |
||||||
|
tables() { |
||||||
|
return this.$store.state.project.tables || [] |
||||||
|
} |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
if (this.$route && this.$route.query && this.$route.query.excelUrl) { |
||||||
|
this.url = this.$route.query.excelUrl |
||||||
|
this.loadUrl() |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
formatJson() { |
||||||
|
console.log(this.$refs.editor) |
||||||
|
this.$refs.editor.format() |
||||||
|
}, |
||||||
|
|
||||||
|
selectFile() { |
||||||
|
this.$refs.file.files = null |
||||||
|
this.$refs.file.click() |
||||||
|
}, |
||||||
|
|
||||||
|
_change(event) { |
||||||
|
const files = event.target.files |
||||||
|
if (files && files[0]) { |
||||||
|
this._file(files[0]) |
||||||
|
event.target.value = '' |
||||||
|
} |
||||||
|
}, |
||||||
|
async _file(file) { |
||||||
|
this.templateData = null |
||||||
|
this.importData = null |
||||||
|
this.$store.commit('loader/MutMessage', 'Loading excel file') |
||||||
|
let i = 0 |
||||||
|
const int = setInterval(() => { |
||||||
|
this.$store.commit('loader/MutMessage', `Loading excel file${'.'.repeat(++i % 4)}`) |
||||||
|
}, 1000) |
||||||
|
this.dropOrUpload = false |
||||||
|
const reader = new FileReader() |
||||||
|
this.filename = file.name |
||||||
|
|
||||||
|
reader.onload = async(e) => { |
||||||
|
const ab = e.target.result |
||||||
|
await this.parseAndExtractData('file', ab, file.name) |
||||||
|
this.$store.commit('loader/MutMessage', null) |
||||||
|
|
||||||
|
clearInterval(int) |
||||||
|
} |
||||||
|
|
||||||
|
const handleEvent = (event) => { |
||||||
|
this.$store.commit('loader/MutMessage', `${event.type}: ${event.loaded} bytes transferred`) |
||||||
|
} |
||||||
|
|
||||||
|
reader.addEventListener('progress', handleEvent) |
||||||
|
reader.onerror = (e) => { |
||||||
|
console.log('error', e) |
||||||
|
this.$store.commit('loader/MutClear') |
||||||
|
} |
||||||
|
reader.readAsText(file) |
||||||
|
}, |
||||||
|
|
||||||
|
async parseAndExtractData(type, val, name) { |
||||||
|
try { |
||||||
|
let templateGenerator |
||||||
|
this.templateData = null |
||||||
|
this.importData = null |
||||||
|
switch (type) { |
||||||
|
case 'file': |
||||||
|
templateGenerator = new JSONTemplateAdapter(name, val, this.parserConfig) |
||||||
|
break |
||||||
|
case 'url': |
||||||
|
templateGenerator = new JSONUrlTemplateAdapter(val, this.$store, this.parserConfig, this.$api) |
||||||
|
break |
||||||
|
case 'string': |
||||||
|
templateGenerator = new JSONTemplateAdapter(name, val, this.parserConfig) |
||||||
|
break |
||||||
|
} |
||||||
|
await templateGenerator.init() |
||||||
|
templateGenerator.parse() |
||||||
|
this.templateData = templateGenerator.getTemplate() |
||||||
|
|
||||||
|
this.templateData.tables[0].table_name = this.populateUniqueTableName() |
||||||
|
|
||||||
|
this.importData = templateGenerator.getData() |
||||||
|
this.templateEditorModal = true |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
this.$toast |
||||||
|
.error(await this._extractSdkResponseErrorMsg(e)) |
||||||
|
.goAway(3000) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
dropHandler(ev) { |
||||||
|
this.dragOver = false |
||||||
|
let file |
||||||
|
if (ev.dataTransfer.items) { |
||||||
|
// Use DataTransferItemList interface to access the file(s) |
||||||
|
if (ev.dataTransfer.items.length && ev.dataTransfer.items[0].kind === 'file') { |
||||||
|
file = ev.dataTransfer.items[0].getAsFile() |
||||||
|
} |
||||||
|
} else if (ev.dataTransfer.files.length) { |
||||||
|
file = ev.dataTransfer.files[0] |
||||||
|
} |
||||||
|
|
||||||
|
if (!file) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (!/.*\.json/.test(file.name)) { |
||||||
|
return this.$toast.error(this.errorMessages.importJSON).goAway(3000) |
||||||
|
} |
||||||
|
|
||||||
|
this._file(file) |
||||||
|
}, |
||||||
|
dragOverHandler(ev) { |
||||||
|
// Prevent default behavior (Prevent file from being opened) |
||||||
|
ev.preventDefault() |
||||||
|
}, |
||||||
|
populateUniqueTableName() { |
||||||
|
let c = 1 |
||||||
|
while (this.tables.some(t => t.title === `Sheet${c}`)) { c++ } |
||||||
|
return `Sheet${c}` |
||||||
|
}, |
||||||
|
async loadUrl() { |
||||||
|
if ((this.$refs.form && !this.$refs.form.validate()) || !this.url) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
this.$store.commit('loader/MutMessage', 'Loading json file from url') |
||||||
|
|
||||||
|
let i = 0 |
||||||
|
const int = setInterval(() => { |
||||||
|
this.$store.commit('loader/MutMessage', `Loading json file${'.'.repeat(++i % 4)}`) |
||||||
|
}, 1000) |
||||||
|
|
||||||
|
this.dropOrUpload = false |
||||||
|
|
||||||
|
await this.parseAndExtractData('url', this.url, '') |
||||||
|
clearInterval(int) |
||||||
|
this.$store.commit('loader/MutClear') |
||||||
|
}, |
||||||
|
|
||||||
|
async loadJsonString() { |
||||||
|
await this.parseAndExtractData('string', this.jsonString) |
||||||
|
this.$store.commit('loader/MutClear') |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.nc-droppable { |
||||||
|
width: 100%; |
||||||
|
min-height: 200px; |
||||||
|
border-radius: 4px; |
||||||
|
border: 2px dashed #ddd; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-json-import-tab-item { |
||||||
|
min-height: 400px; |
||||||
|
padding: 20px; |
||||||
|
display: flex; |
||||||
|
align-items: stretch; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-json-import-options { |
||||||
|
transition: .4s max-height; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-json-editor-wrapper{ |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.nc-json-format-btn{ |
||||||
|
position:absolute; |
||||||
|
right:4px; |
||||||
|
top:4px; |
||||||
|
z-index:9; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,150 @@ |
|||||||
|
import { TemplateGenerator, UITypes } from 'nocodb-sdk' |
||||||
|
import { |
||||||
|
extractMultiOrSingleSelectProps, |
||||||
|
getCheckboxValue, |
||||||
|
isCheckboxType, isDecimalType, isEmailType, |
||||||
|
isMultiLineTextType, isUrlType |
||||||
|
} from '~/components/import/templateParsers/parserHelpers' |
||||||
|
|
||||||
|
const jsonTypeToUidt = { |
||||||
|
number: UITypes.Number, |
||||||
|
string: UITypes.SingleLineText, |
||||||
|
date: UITypes.DateTime, |
||||||
|
boolean: UITypes.Checkbox, |
||||||
|
object: UITypes.JSON |
||||||
|
} |
||||||
|
|
||||||
|
const extractNestedData = (obj, path) => path.reduce((val, key) => val && val[key], obj) |
||||||
|
|
||||||
|
export default class JSONTemplateAdapter extends TemplateGenerator { |
||||||
|
constructor(name = 'test', data, parserConfig = {}) { |
||||||
|
super() |
||||||
|
this.config = { |
||||||
|
maxRowsToParse: 500, |
||||||
|
...parserConfig |
||||||
|
} |
||||||
|
this.name = name |
||||||
|
this._jsonData = typeof data === 'string' ? JSON.parse(data) : data |
||||||
|
this.project = { |
||||||
|
title: this.name, |
||||||
|
tables: [] |
||||||
|
} |
||||||
|
this.data = {} |
||||||
|
} |
||||||
|
|
||||||
|
async init() { |
||||||
|
} |
||||||
|
|
||||||
|
parseData() { |
||||||
|
this.columns = this.csv.meta.fields |
||||||
|
this.data = this.csv.data |
||||||
|
} |
||||||
|
|
||||||
|
getColumns() { |
||||||
|
return this.columns |
||||||
|
} |
||||||
|
|
||||||
|
getData() { |
||||||
|
return this.data |
||||||
|
} |
||||||
|
|
||||||
|
get jsonData() { |
||||||
|
return Array.isArray(this._jsonData) ? this._jsonData : [this._jsonData] |
||||||
|
} |
||||||
|
|
||||||
|
parse() { |
||||||
|
const jsonData = this.jsonData |
||||||
|
const tn = 'table' |
||||||
|
const table = { table_name: tn, ref_table_name: tn, columns: [] } |
||||||
|
|
||||||
|
this.data[tn] = [] |
||||||
|
|
||||||
|
for (const col of Object.keys(jsonData[0])) { |
||||||
|
const columns = this._parseColumn([col], jsonData) |
||||||
|
table.columns.push(...columns) |
||||||
|
} |
||||||
|
|
||||||
|
if (this.config.importData) { this._parseTableData(table) } |
||||||
|
|
||||||
|
this.project.tables.push(table) |
||||||
|
} |
||||||
|
|
||||||
|
getTemplate() { |
||||||
|
return this.project |
||||||
|
} |
||||||
|
|
||||||
|
_parseColumn(path = [], jsonData = this.jsonData, firstRowVal = path.reduce((val, k) => val && val[k], this.jsonData[0])) { |
||||||
|
const columns = [] |
||||||
|
// parse nested
|
||||||
|
if (firstRowVal && typeof firstRowVal === 'object' && !Array.isArray(firstRowVal) && this.config.normalizeNested) { |
||||||
|
for (const key of Object.keys(firstRowVal)) { |
||||||
|
const normalizedNestedColumns = this._parseColumn([...path, key], this.jsonData, firstRowVal[key]) |
||||||
|
columns.push(...normalizedNestedColumns) |
||||||
|
} |
||||||
|
} else { |
||||||
|
const cn = path.join('_').replace(/\W/g, '_').trim() |
||||||
|
|
||||||
|
const column = { |
||||||
|
column_name: cn, |
||||||
|
ref_column_name: cn, |
||||||
|
path |
||||||
|
} |
||||||
|
|
||||||
|
column.uidt = jsonTypeToUidt[typeof firstRowVal] || UITypes.SingleLineText |
||||||
|
|
||||||
|
const colData = jsonData.map(r => extractNestedData(r, path)) |
||||||
|
Object.assign(column, this._getColumnUIDTAndMetas(colData, column.uidt)) |
||||||
|
columns.push(column) |
||||||
|
} |
||||||
|
|
||||||
|
return columns |
||||||
|
} |
||||||
|
|
||||||
|
_getColumnUIDTAndMetas(colData, defaultType) { |
||||||
|
const colProps = { uidt: defaultType } |
||||||
|
// todo: optimize
|
||||||
|
if (colProps.uidt === UITypes.SingleLineText) { |
||||||
|
// check for long text
|
||||||
|
if (isMultiLineTextType(colData)) { |
||||||
|
colProps.uidt = UITypes.LongText |
||||||
|
} if (isEmailType(colData)) { |
||||||
|
colProps.uidt = UITypes.Email |
||||||
|
} if (isUrlType(colData)) { |
||||||
|
colProps.uidt = UITypes.URL |
||||||
|
} else { |
||||||
|
const checkboxType = isCheckboxType(colData) |
||||||
|
if (checkboxType.length === 1) { |
||||||
|
colProps.uidt = UITypes.Checkbox |
||||||
|
} else { |
||||||
|
Object.assign(colProps, extractMultiOrSingleSelectProps(colData)) |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (colProps.uidt === UITypes.Number) { |
||||||
|
if (isDecimalType(colData)) { |
||||||
|
colProps.uidt = UITypes.Decimal |
||||||
|
} |
||||||
|
} |
||||||
|
return colProps |
||||||
|
} |
||||||
|
|
||||||
|
_parseTableData(tableMeta) { |
||||||
|
for (const row of this.jsonData) { |
||||||
|
const rowData = {} |
||||||
|
for (let i = 0; i < tableMeta.columns.length; i++) { |
||||||
|
const value = extractNestedData(row, tableMeta.columns[i].path || []) |
||||||
|
if (tableMeta.columns[i].uidt === UITypes.Checkbox) { |
||||||
|
rowData[tableMeta.columns[i].ref_column_name] = getCheckboxValue(value) |
||||||
|
} else if (tableMeta.columns[i].uidt === UITypes.SingleSelect || tableMeta.columns[i].uidt === UITypes.MultiSelect) { |
||||||
|
rowData[tableMeta.columns[i].ref_column_name] = (value || '').toString().trim() || null |
||||||
|
} else if (tableMeta.columns[i].uidt === UITypes.JSON) { |
||||||
|
rowData[tableMeta.columns[i].ref_column_name] = JSON.stringify(value) |
||||||
|
} else { |
||||||
|
// toto: do parsing if necessary based on type
|
||||||
|
rowData[tableMeta.columns[i].column_name] = value |
||||||
|
} |
||||||
|
} |
||||||
|
this.data[tableMeta.ref_table_name].push(rowData) |
||||||
|
// rowIndex++
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
import JSONTemplateAdapter from '~/components/import/templateParsers/JSONTemplateAdapter' |
||||||
|
|
||||||
|
export default class JSONUrlTemplateAdapter extends JSONTemplateAdapter { |
||||||
|
constructor(url, $store, parserConfig, $api) { |
||||||
|
const name = url.split('/').pop() |
||||||
|
super(name, null, parserConfig) |
||||||
|
this.url = url |
||||||
|
this.$api = $api |
||||||
|
this.$store = $store |
||||||
|
} |
||||||
|
|
||||||
|
async init() { |
||||||
|
const data = await this.$api.utils.axiosRequestMake({ |
||||||
|
apiMeta: { |
||||||
|
url: this.url |
||||||
|
} |
||||||
|
}) |
||||||
|
this._jsonData = data |
||||||
|
await super.init() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import UITypes from './UITypes'; |
||||||
|
|
||||||
|
export interface Column { |
||||||
|
column_name: string; |
||||||
|
ref_column_name: string; |
||||||
|
uidt?: UITypes; |
||||||
|
dtxp?: any; |
||||||
|
dt?: any; |
||||||
|
} |
||||||
|
export interface Table { |
||||||
|
table_name: string; |
||||||
|
ref_table_name: string; |
||||||
|
columns: Array<Column>; |
||||||
|
} |
||||||
|
export interface Template { |
||||||
|
title: string; |
||||||
|
tables: Array<Table>; |
||||||
|
} |
||||||
|
|
||||||
|
export default abstract class TemplateGenerator { |
||||||
|
abstract parse(): Promise<any>; |
||||||
|
abstract parseTemplate(): Promise<Template>; |
||||||
|
abstract getColumns(): Promise<any>; |
||||||
|
abstract parseData(): Promise<any>; |
||||||
|
abstract getData(): Promise<{ |
||||||
|
[table_name: string]: Array<{ |
||||||
|
[key: string]: any; |
||||||
|
}>; |
||||||
|
}>; |
||||||
|
} |
Loading…
Reference in new issue