Browse Source

feat: Add child edit option

Signed-off-by: Pranav C Balan <pranavxc@gmail.com>
pull/341/head
Pranav C Balan 3 years ago committed by Pranav C
parent
commit
adc4282d8a
  1. 12
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  2. 73
      packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue
  3. 25
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue
  4. 64
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  5. 93
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  6. 18
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  7. 8
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  8. 110
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  9. 3
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  10. 11
      packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts

12
packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue

@ -2,7 +2,9 @@
<v-card width="1000" max-width="100%"> <v-card width="1000" max-width="100%">
<v-toolbar height="55" class="elevation-1"> <v-toolbar height="55" class="elevation-1">
<div class="d-100 d-flex "> <div class="d-100 d-flex ">
<h5 class="title text-center">{{ table }} : {{ localState[primaryValueColumn] }}</h5> <h5 class="title text-center">
<v-icon :color="iconColor">mdi-table-arrow-right</v-icon>
{{ table }} : {{ localState[primaryValueColumn] }}</h5>
<v-spacer> <v-spacer>
</v-spacer> </v-spacer>
<v-btn small text @click="reload"> <v-btn small text @click="reload">
@ -186,9 +188,13 @@ export default {
hasMany: Object, hasMany: Object,
belongsTo: Object, belongsTo: Object,
isNew: Boolean, isNew: Boolean,
oldRow: Object oldRow: Object,
iconColor:{
type:String,
default: 'primary'
}
}, },
name: "expandedForm", name: "expanded-form",
data: () => ({ data: () => ({
showborder: false, showborder: false,
loadingLogs: true, loadingLogs: true,

73
packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue

@ -0,0 +1,73 @@
<template>
<div>
<has-many-cell
v-if="hm"
:row="row"
:value="row[hm._tn]"
:meta="meta"
:hm="hm"
:nodes="nodes"
:active="active"
:sql-ui="sqlUi"
v-on="$listeners"
/>
<many-to-many-cell
v-else-if="mm"
:row="row"
:value="row[mm._rtn]"
:meta="meta"
:mm="mm"
:nodes="nodes"
:sql-ui="sqlUi"
:active="active"
v-on="$listeners"
/>
<belongs-to-cell
v-else-if="bt"
:active="active"
:row="row"
:value="row[bt._rtn]"
:meta="meta"
:bt="bt"
:nodes="nodes"
:api="api"
:sql-ui="sqlUi"
v-on="$listeners"
/>
</div>
</template>
<script>
import HasManyCell from "@/components/project/spreadsheet/components/virtualCell/hasManyCell";
import ManyToManyCell from "@/components/project/spreadsheet/components/virtualCell/manyToManyCell";
import BelongsToCell from "@/components/project/spreadsheet/components/virtualCell/belogsToCell";
export default {
name: "virtual-cell",
components: {BelongsToCell, ManyToManyCell, HasManyCell},
props: {
column: [Object],
row: [Object],
nodes: [Object],
meta: [Object],
api: [Object, Function],
active: Boolean,
sqlUi: [Object, Function],
},
computed: {
hm() {
return this.column && this.column.hm;
},
bt() {
return this.column && this.column.bt;
},
mm() {
return this.column && this.column.mm;
}
}
}
</script>
<style scoped>
</style>

25
packages/nc-gui/components/project/spreadsheet/components/editableCell/belogsToCell.vue → packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue

@ -8,8 +8,11 @@
</v-chip> </v-chip>
</template> </template>
</div> </div>
<div class="d-flex align-center justify-center px-1 flex-shrink-1"> <div class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon> <x-icon small :color="['primary','grey']" @click="showNewRecordModal">{{
value ? 'mdi-pencil' : 'mdi-plus'
}}
</x-icon>
</div> </div>
@ -53,8 +56,6 @@
</v-dialog> </v-dialog>
</div> </div>
</template> </template>
@ -73,6 +74,8 @@ export default {
nodes: [Object], nodes: [Object],
row: [Object], row: [Object],
api: [Object, Function], api: [Object, Function],
sqlUi: [Object, Function],
active: Boolean
}, },
data: () => ({ data: () => ({
newRecordModal: false, newRecordModal: false,
@ -89,7 +92,7 @@ export default {
async showParentListModal() { async showParentListModal() {
this.parentListModal = true; this.parentListModal = true;
await this.getParentMeta(); await this.getParentMeta();
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.parentMeta.columns.find(c => c.cn === this.hm.cn)._cn; const _cn = this.parentMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.childList = await this.parentApi.paginatedList({ this.childList = await this.parentApi.paginatedList({
where: `(${_cn},eq,${pid})` where: `(${_cn},eq,${pid})`
@ -103,7 +106,7 @@ export default {
if (act === 'hideDialog') { if (act === 'hideDialog') {
this.dialogShow = false; this.dialogShow = false;
} else { } else {
const id = this.parentMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join(','); const id = this.parentMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.parentApi.delete(id) await this.parentApi.delete(id)
this.showParentListModal(); this.showParentListModal();
this.dialogShow = false; this.dialogShow = false;
@ -129,8 +132,8 @@ export default {
this.list = await this.parentApi.paginatedList({}) this.list = await this.parentApi.paginatedList({})
}, },
async addParentToChild(parent) { async addParentToChild(parent) {
const pid = this.parentMeta.columns.filter((c) => c.pk).map(c => parent[c._cn]).join(','); const pid = this.parentMeta.columns.filter((c) => c.pk).map(c => parent[c._cn]).join('___');
const id = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const id = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.meta.columns.find(c => c.cn === this.bt.cn)._cn; const _cn = this.meta.columns.find(c => c.cn === this.bt.cn)._cn;
await this.api.update(id, { await this.api.update(id, {
@ -204,11 +207,11 @@ export default {
} }
.hm-items { .hm-items {
min-width: 200px; //min-width: 200px;
max-width: 400px; //max-width: 400px;
flex-wrap: wrap; flex-wrap: wrap;
row-gap: 3px; row-gap: 3px;
gap:3px; gap: 3px;
margin: 3px auto; margin: 3px auto;
} }

64
packages/nc-gui/components/project/spreadsheet/components/editableCell/hasManyCell.vue → packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -5,12 +5,12 @@
<v-chip <v-chip
small small
v-for="(v,i) in value.map(v=>Object.values(v)[1])" v-for="(v,i) in value.map(v=>Object.values(v)[1])"
:color="colors[i%colors.length]" :key="j"> :color="colors[i%colors.length]" :key="i">
{{ v }} {{ v }}
</v-chip> </v-chip>
</template> </template>
</div> </div>
<div class="d-flex align-center justify-center px-1 flex-shrink-1"> <div class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon> <x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon>
<x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon> <x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon>
</div> </div>
@ -68,12 +68,13 @@
class="ma-2 child-list-modal child-card" class="ma-2 child-list-modal child-card"
outlined outlined
:key="i" :key="i"
@click="editChild(ch)"
> >
<x-icon <x-icon
class="remove-child-icon" class="remove-child-icon"
:color="['error','grey']" :color="['error','grey']"
small small
@click="removeChild(ch,i)" @click.stop="removeChild(ch,i)"
>mdi-delete-outline >mdi-delete-outline
</x-icon> </x-icon>
@ -104,24 +105,55 @@
> >
</dlg-label-submit-cancel> </dlg-label-submit-cancel>
<v-dialog
:overlay-opacity="0.8"
v-if="selectedChild"
width="1000px"
max-width="100%"
class=" mx-auto"
v-model="showExpandModal">
<expanded-form
v-if="selectedChild"
:db-alias="nodes.dbAlias"
:has-many="childMeta.hasMany"
:belongs-to="childMeta.belongsTo"
@cancel="selectedChild = null"
@input="$emit('loadTableData');showChildListModal();"
:table="childMeta.tn"
v-model="selectedChild"
:old-row="{...selectedChild}"
:meta="childMeta"
:sql-ui="sqlUi"
:primary-value-column="childPrimaryCol"
:api="childApi"
icon-color="warning"
></expanded-form>
</v-dialog>
</div> </div>
</template> </template>
<script> <script>
import colors from "@/mixins/colors"; import colors from "@/mixins/colors";
import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory"; import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory";
import expandedForm from "@/components/project/spreadsheet/components/expandedForm";
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel"; import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel";
export default { export default {
name: "has-many-cell", name: "has-many-cell",
components: {DlgLabelSubmitCancel}, components: {
DlgLabelSubmitCancel,
expandedForm
},
mixins: [colors], mixins: [colors],
props: { props: {
value: [Object, Array], value: [Object, Array],
meta: [Object], meta: [Object],
hm: Object, hm: Object,
nodes: [Object], nodes: [Object],
row: [Object] row: [Object],
sqlUi: [Object, Function],
active: Boolean
}, },
data: () => ({ data: () => ({
newRecordModal: false, newRecordModal: false,
@ -131,14 +163,16 @@ export default {
childList: null, childList: null,
dialogShow: false, dialogShow: false,
confirmAction: null, confirmAction: null,
confirmMessage: '' confirmMessage: '',
selectedChild: null,
showExpandModal: false
}), }),
methods: { methods: {
async showChildListModal() { async showChildListModal() {
this.childListModal = true; this.childListModal = true;
await this.getChildMeta(); await this.getChildMeta();
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn; const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.childList = await this.childApi.paginatedList({ this.childList = await this.childApi.paginatedList({
where: `(${_cn},eq,${pid})` where: `(${_cn},eq,${pid})`
@ -152,7 +186,7 @@ export default {
if (act === 'hideDialog') { if (act === 'hideDialog') {
this.dialogShow = false; this.dialogShow = false;
} else { } else {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join(','); const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.childApi.delete(id) await this.childApi.delete(id)
this.showChildListModal(); this.showChildListModal();
this.dialogShow = false; this.dialogShow = false;
@ -178,8 +212,8 @@ export default {
this.list = await this.childApi.paginatedList({}) this.list = await this.childApi.paginatedList({})
}, },
async addChildToParent(child) { async addChildToParent(child) {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join(','); const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn; const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.newRecordModal = false; this.newRecordModal = false;
@ -190,6 +224,10 @@ export default {
}); });
this.$emit('loadTableData') this.$emit('loadTableData')
},
editChild(child) {
this.selectedChild = child;
this.showExpandModal = true;
} }
}, },
computed: { computed: {
@ -253,11 +291,11 @@ export default {
} }
.hm-items { .hm-items {
min-width: 200px; //min-width: 200px;
max-width: 400px; //max-width: 400px;
flex-wrap: wrap; flex-wrap: wrap;
row-gap: 3px; row-gap: 3px;
gap:3px; gap: 3px;
margin: 3px auto; margin: 3px auto;
} }

93
packages/nc-gui/components/project/spreadsheet/components/editableCell/manyToMany.vue → packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -11,8 +11,9 @@
</v-chip> </v-chip>
</template> </template>
</div> </div>
<div class="d-flex align-center justify-center px-1 flex-shrink-1"> <div class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon> <x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon>
<!-- <x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon>-->
</div> </div>
@ -56,6 +57,79 @@
</v-dialog> </v-dialog>
<v-dialog v-if="childListModal" v-model="childListModal" width="600">
<v-card width="600" color="backgroundColor">
<v-card-title class="textColor--text mx-2">{{ childMeta ? childMeta._tn : 'Children' }}</v-card-title>
<v-card-text>
<div class="items-container">
<template v-if="childList">
<v-card
v-for="(ch,i) in childList.list"
class="ma-2 child-list-modal child-card"
outlined
:key="i"
@click="editChild(ch)"
>
<x-icon
class="remove-child-icon"
:color="['error','grey']"
small
@click.stop="removeChild(ch,i)"
>mdi-delete-outline
</x-icon>
<v-card-title class="primary-value textColor--text text--lighten-2">{{ ch[childPrimaryCol] }}
<span class="grey--text caption primary-key"
v-if="childPrimaryKey">(Primary Key : {{ ch[childPrimaryKey] }})</span>
</v-card-title>
</v-card>
</template>
</div>
</v-card-text>
<v-card-actions class="justify-center pb-6">
<v-btn small outlined class="caption" color="primary" @click="showNewRecordModal">
<v-icon>mdi-plus</v-icon>
Add Record
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
:overlay-opacity="0.8"
v-if="selectedChild"
width="1000px"
max-width="100%"
class=" mx-auto"
v-model="showExpandModal">
<expanded-form
v-if="selectedChild"
:db-alias="nodes.dbAlias"
:has-many="childMeta.hasMany"
:belongs-to="childMeta.belongsTo"
@cancel="selectedChild = null"
@input="$emit('loadTableData');showChildListModal();"
:table="childMeta.tn"
v-model="selectedChild"
:old-row="{...selectedChild}"
:meta="childMeta"
:sql-ui="sqlUi"
:primary-value-column="childPrimaryCol"
:api="childApi"
></expanded-form>
</v-dialog>
<dlg-label-submit-cancel
type="primary"
v-if="dialogShow"
:actionsMtd="confirmAction"
:dialogShow="dialogShow"
:heading="confirmMessage"
>
</dlg-label-submit-cancel>
</div> </div>
</template> </template>
@ -65,8 +139,9 @@ import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory";
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel"; import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel";
export default { export default {
name: "many-to-many", name: "many-to-many-cell",
mixins: [colors], mixins: [colors],
components: {DlgLabelSubmitCancel},
props: { props: {
value: [Object, Array], value: [Object, Array],
meta: [Object], meta: [Object],
@ -74,6 +149,8 @@ export default {
nodes: [Object], nodes: [Object],
row: [Object], row: [Object],
api: [Object, Function], api: [Object, Function],
sqlUi: [Object, Function],
active: Boolean
}, },
data: () => ({ data: () => ({
newRecordModal: false, newRecordModal: false,
@ -91,7 +168,7 @@ export default {
async showChildListModal() { async showChildListModal() {
this.childListModal = true; this.childListModal = true;
await this.getChildMeta(); await this.getChildMeta();
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn; const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.childList = await this.childApi.paginatedList({ this.childList = await this.childApi.paginatedList({
where: `(${_cn},eq,${pid})` where: `(${_cn},eq,${pid})`
@ -105,7 +182,7 @@ export default {
if (act === 'hideDialog') { if (act === 'hideDialog') {
this.dialogShow = false; this.dialogShow = false;
} else { } else {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join(','); const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.childApi.delete(id) await this.childApi.delete(id)
this.showChildListModal(); this.showChildListModal();
this.dialogShow = false; this.dialogShow = false;
@ -143,8 +220,8 @@ export default {
this.list = await this.childApi.paginatedList({}) this.list = await this.childApi.paginatedList({})
}, },
async addChildToParent(child) { async addChildToParent(child) {
const cid = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join(','); const cid = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join(','); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const vcidCol = this.assocMeta.columns.find(c => c.cn === this.mm.vrcn)._cn; const vcidCol = this.assocMeta.columns.find(c => c.cn === this.mm.vrcn)._cn;
const vpidCol = this.assocMeta.columns.find(c => c.cn === this.mm.vcn)._cn; const vpidCol = this.assocMeta.columns.find(c => c.cn === this.mm.vcn)._cn;
@ -235,8 +312,8 @@ export default {
} }
.hm-items { .hm-items {
min-width: 200px; //min-width: 200px;
max-width: 400px; //max-width: 400px;
flex-wrap: wrap; flex-wrap: wrap;
row-gap: 3px; row-gap: 3px;
gap: 3px; gap: 3px;

18
packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js

@ -59,18 +59,26 @@ export default {
}, },
availableColumns() { availableColumns() {
let columns = []; let columns = [];
// todo: generate hideCols based on default values // todo: generate hideCols based on default values
const hideCols = ['created_at', 'updated_at']; const hideCols = ['created_at', 'updated_at'];
if (this.showSystemFields) { if (this.showSystemFields) {
columns = this.meta.columns || []; columns = this.meta.columns || [];
} else if (this.data && this.data.length) { } else if (this.data && this.data.length) {
// c._cn in this.data[0].row && columns = (this.meta.columns.filter(c => !(c.pk && c.ai)
columns = (this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || []; && !(this.meta.v.some(v => v.bt && v.bt.cn === c.cn))
&& !hideCols.includes(c.cn))) || [];
} else { } else {
columns = (this.meta && this.meta.columns && this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || []; columns = (this.meta && this.meta.columns && this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || [];
} }
if (this.meta && this.meta.v) {
columns = [...columns, ...this.meta.v.map(v => ({...v, virtual: 1}))];
}
if (this.fieldsOrder.length) { if (this.fieldsOrder.length) {
return [...columns].sort((c1, c2) => { return [...columns].sort((c1, c2) => {
const i1 = this.fieldsOrder.indexOf(c1._cn); const i1 = this.fieldsOrder.indexOf(c1._cn);
@ -103,9 +111,9 @@ export default {
offset: this.size * (this.page - 1), offset: this.size * (this.page - 1),
where: this.concatenatedXWhere, where: this.concatenatedXWhere,
sort: this.sort, sort: this.sort,
childs: (this.meta && this.meta.hasMany && this.meta.hasMany.map(hm => hm.tn).join())||'', childs: (this.meta && this.meta.hasMany && this.meta.hasMany.map(hm => hm.tn).join()) || '',
parents: (this.meta && this.meta.belongsTo && this.meta.belongsTo.map(hm => hm.rtn).join())||'', parents: (this.meta && this.meta.belongsTo && this.meta.belongsTo.map(hm => hm.rtn).join()) || '',
many: (this.meta && this.meta.manyToMany && this.meta.manyToMany.map(mm => mm.rtn).join())||'' many: (this.meta && this.meta.manyToMany && this.meta.manyToMany.map(mm => mm.rtn).join()) || ''
} }
}, colLength() { }, colLength() {
return (this.availableColumns && this.availableColumns.length) || 0 return (this.availableColumns && this.availableColumns.length) || 0

8
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -67,12 +67,15 @@
</x-btn> </x-btn>
<fields v-model="showFields" :field-list="fieldList" <fields
v-model="showFields"
:field-list="fieldList"
:meta="meta" :meta="meta"
:is-locked="isLocked" :is-locked="isLocked"
:fieldsOrder.sync="fieldsOrder" :fieldsOrder.sync="fieldsOrder"
:sqlUi="sqlUi" :sqlUi="sqlUi"
:showSystemFields.sync="showSystemFields"></fields> :showSystemFields.sync="showSystemFields"
/>
<sort-list <sort-list
:is-locked="isLocked" :is-locked="isLocked"
@ -576,7 +579,6 @@ export default {
console.log(e) console.log(e)
} }
this.searchField = this.primaryValueColumn; this.searchField = this.primaryValueColumn;
this.dataLoaded = true; this.dataLoaded = true;
// await this.loadViews(); // await this.loadViews();

110
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -23,7 +23,11 @@
> >
<!-- :style="columnsWidth[col._cn] ? `min-width:${columnsWidth[col._cn]}; max-width:${columnsWidth[col._cn]}` : ''" <!-- :style="columnsWidth[col._cn] ? `min-width:${columnsWidth[col._cn]}; max-width:${columnsWidth[col._cn]}` : ''"
--> -->
<template v-if="col.virtual">{{ col._cn }}</template>
<header-cell <header-cell
v-else
:isPublicView="isPublicView" :isPublicView="isPublicView"
@onRelationDelete="$emit('onRelationDelete')" @onRelationDelete="$emit('onRelationDelete')"
:nodes="nodes" :nodes="nodes"
@ -37,35 +41,8 @@
@saved="onNewColCreation" @saved="onNewColCreation"
></header-cell> ></header-cell>
<!--{{ col.cn }}-->
</th>
<th v-for="(bt,i) in meta.belongsTo"
class="grey-border caption font-wight-regular"
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
:key="i"
>
{{ bt._rtn }} (Belongs To)
</th>
<th v-for="(hm,i) in meta.hasMany"
class="grey-border caption font-wight-regular"
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
:key="i"
>
{{ hm._tn }} (Has Many)
</th> </th>
<th v-for="(mm,i) in meta.manyToMany"
class="grey-border caption font-wight-regular"
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
:key="i"
>
{{ mm.rtn }} (Many To Many)
</th>
<th <th
v-if="!isLocked && !isVirtual && !isPublicView && _isUIAllowed('add-column')" v-if="!isLocked && !isVirtual && !isPublicView && _isUIAllowed('add-column')"
@click="addNewColMenu = true" @click="addNewColMenu = true"
@ -142,17 +119,29 @@
v-show="showFields[columnObj._cn]" v-show="showFields[columnObj._cn]"
:data-col="columnObj._cn" :data-col="columnObj._cn"
> >
<virtual-cell
v-if="columnObj.virtual"
:column="columnObj"
:row="rowObj"
:nodes="nodes"
:meta="meta"
:api="api"
:active="selected.col === col && selected.row === row"
:sql-ui="sqlUi"
v-on="$listeners"
></virtual-cell>
<editable-cell <editable-cell
v-if="!isLocked v-else-if="
!isLocked
&& !isPublicView && !isPublicView
&& ( && (editEnabled.col === col && editEnabled.row === row)
editEnabled.col === col || enableEditable(columnObj)
&& editEnabled.row === row "
)
|| enableEditable(columnObj)"
:column="columnObj" :column="columnObj"
:meta="meta" :meta="meta"
:active="(selected.col === col && selected.row === row)" :active="selected.col === col && selected.row === row"
v-model="rowObj[columnObj._cn]" v-model="rowObj[columnObj._cn]"
@save="editEnabled = {}" @save="editEnabled = {}"
@cancel="editEnabled = {}" @cancel="editEnabled = {}"
@ -288,50 +277,6 @@
:sql-ui="sqlUi" :sql-ui="sqlUi"
></table-cell> ></table-cell>
</td> </td>
<td v-for="(bt,i) in meta.belongsTo" class="caption" :key="i">
<belongs-to-cell
:row="rowObj"
:value="rowObj[bt._rtn]"
:meta="meta"
:bt="bt"
:nodes="nodes"
@loadTableData="$emit('loadTableData')"
:api="api"
/>
</td>
<td v-for="(hm,i) in meta.hasMany" class="caption" :key="i">
<template v-if="rowObj[hm._tn]">
<has-many-cell
:row="rowObj"
:value="rowObj[hm._tn]"
:meta="meta"
:hm="hm"
:nodes="nodes"
@loadTableData="$emit('loadTableData')"
/>
</template>
</td>
<td v-for="(mm,i) in meta.manyToMany" class="caption" :key="i">
<many-to-many
:row="rowObj"
:value="rowObj[mm._rtn]"
:meta="meta"
:mm="mm"
:nodes="nodes"
@loadTableData="$emit('loadTableData')"
/>
</td>
</tr> </tr>
<tr v-if="!isLocked && !isPublicView && isEditable && relationType !== 'bt'"> <tr v-if="!isLocked && !isPublicView && isEditable && relationType !== 'bt'">
<td :colspan="visibleColLength + 1" class="text-left pointer" @click="insertNewRow(true)"> <td :colspan="visibleColLength + 1" class="text-left pointer" @click="insertNewRow(true)">
@ -359,12 +304,13 @@ import EditColumn from "@/components/project/spreadsheet/components/editColumn";
import TableCell from "@/components/project/spreadsheet/components/tableCell"; import TableCell from "@/components/project/spreadsheet/components/tableCell";
import colors from "@/mixins/colors"; import colors from "@/mixins/colors";
import columnStyling from "@/components/project/spreadsheet/helpers/columnStyling"; import columnStyling from "@/components/project/spreadsheet/helpers/columnStyling";
import HasManyCell from "@/components/project/spreadsheet/components/editableCell/hasManyCell"; import HasManyCell from "@/components/project/spreadsheet/components/virtualCell/hasManyCell";
import BelongsToCell from "@/components/project/spreadsheet/components/editableCell/belogsToCell"; import BelongsToCell from "@/components/project/spreadsheet/components/virtualCell/belogsToCell";
import ManyToMany from "@/components/project/spreadsheet/components/editableCell/manyToMany"; import ManyToMany from "@/components/project/spreadsheet/components/virtualCell/manyToManyCell";
import VirtualCell from "@/components/project/spreadsheet/components/virtualCell";
export default { export default {
components: {ManyToMany, BelongsToCell, HasManyCell, TableCell, EditColumn, EditableCell, HeaderCell}, components: {VirtualCell, ManyToMany, BelongsToCell, HasManyCell, TableCell, EditColumn, EditableCell, HeaderCell},
mixins: [colors], mixins: [colors],
props: { props: {
relationType: String, relationType: String,

3
packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

@ -1509,7 +1509,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
const tableMetaA = this.metas[meta.belongsTo[0].rtn]; const tableMetaA = this.metas[meta.belongsTo[0].rtn];
const tableMetaB = this.metas[meta.belongsTo[1].rtn]; const tableMetaB = this.metas[meta.belongsTo[1].rtn];
/* // remove hasmany relation with associate table from tables /* // remove hasmany relation with associate table from tables
tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1) tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1)
tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/ tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/
@ -1551,6 +1551,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// Update metadata of tables which have manytomany relation // Update metadata of tables which have manytomany relation
// and recreate basemodel with new meta information // and recreate basemodel with new meta information
for (const meta of metas) { for (const meta of metas) {
meta.v = [...meta.v, ...meta.manyToMany.map(mm => ({mm, _cn:`${mm._tn} <=> ${mm._rtn}`}))]
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
meta: JSON.stringify(meta) meta: JSON.stringify(meta)
}, {title: meta.tn}) }, {title: meta.tn})

11
packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts

@ -615,6 +615,17 @@ class ModelXcMetaMysql extends BaseRender {
belongsTo: this.ctx.belongsTo, belongsTo: this.ctx.belongsTo,
db_type: this.ctx.db_type, db_type: this.ctx.db_type,
type: this.ctx.type, type: this.ctx.type,
v: [
...(this.ctx.hasMany || []).map(hm => ({
hm,
_cn:`${hm._rtn} => ${hm._tn}`
})),
...(this.ctx.belongsTo || []).map(bt => ({
bt,
_cn:`${bt._rtn} <= ${bt._tn}`
})),
]
} }
} }

Loading…
Cancel
Save