mirror of https://github.com/nocodb/nocodb
Browse Source
# Conflicts: # packages/nocodb/package-lock.json # packages/nocodb/package.jsonpull/341/head
Pranav C
3 years ago
128 changed files with 6020 additions and 2516 deletions
@ -1,76 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-menu> |
|
||||||
<template v-slot:activator="{on}"> |
|
||||||
<div class="value" v-on="on">{{ localState }}</div> |
|
||||||
</template> |
|
||||||
<v-date-picker v-on="parentListeners" v-model="localState"></v-date-picker> |
|
||||||
</v-menu> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "date-picker-cell", props: { |
|
||||||
value: [String, Date] |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
if (this.$el && this.$el.$el) { |
|
||||||
this.$el.$el.focus(); |
|
||||||
} |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value; |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', new Date(val).toJSON().slice(0, 10)); |
|
||||||
} |
|
||||||
}, |
|
||||||
parentListeners() { |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if (this.$listeners.blur) { |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if (this.$listeners.focus) { |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
|
|
||||||
.value { |
|
||||||
width: 100%; |
|
||||||
height: 100%; |
|
||||||
min-height:20px; |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,93 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-datetime-picker |
|
||||||
v-on="parentListeners" |
|
||||||
class="caption xc-date-time-picker" |
|
||||||
ref="picker" |
|
||||||
:text-field-props="{ |
|
||||||
class:'caption mt-n1 pt-0' |
|
||||||
}" |
|
||||||
:time-picker-props="{ |
|
||||||
format:'24hr' |
|
||||||
}" |
|
||||||
v-model="localState" |
|
||||||
></v-datetime-picker> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "date-time-picker-cell", |
|
||||||
props: ['value', 'ignoreFocus'], |
|
||||||
mounted() { |
|
||||||
if (!this.ignoreFocus) { |
|
||||||
this.$refs.picker.display = true; |
|
||||||
} |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
if(/^\d{6,}$/.test(this.value)){ |
|
||||||
return new Date(+this.value); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
return /\dT\d/.test(this.value) ? new Date(this.value.replace(/(\d)T(?=\d)/, '$1 ')) : this.value; |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
// if(/^\d{6,}$/.test(this.value)){ |
|
||||||
// return this.$emit('input', new Date(this.value).getTime()); |
|
||||||
// } |
|
||||||
|
|
||||||
|
|
||||||
const uVal = new Date(val).toISOString().slice(0, 19).replace('T', ' ').replace(/(\d{1,2}:\d{1,2}):\d{1,2}$/,'$1'); |
|
||||||
console.log(val, uVal) |
|
||||||
this.$emit('input', uVal); |
|
||||||
} |
|
||||||
}, |
|
||||||
parentListeners(){ |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if(this.$listeners.blur){ |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if(this.$listeners.focus){ |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
/deep/ .v-input, /deep/ .v-text-field { |
|
||||||
margin-top: 0 !important; |
|
||||||
padding-top: 0 !important; |
|
||||||
font-size: inherit !important; |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,89 +0,0 @@ |
|||||||
<template> |
|
||||||
<select v-on="parentListeners" v-model="localState"> |
|
||||||
<option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option> |
|
||||||
</select> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "enum-list-cell", |
|
||||||
props: { |
|
||||||
value: String, |
|
||||||
column: Object |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
this.$el.focus(); |
|
||||||
let event; |
|
||||||
event = document.createEvent('MouseEvents'); |
|
||||||
event.initMouseEvent('mousedown', true, true, window); |
|
||||||
this.$el.dispatchEvent(event); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return this.value |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', val); |
|
||||||
this.$emit('update'); |
|
||||||
} |
|
||||||
}, |
|
||||||
enumValues() { |
|
||||||
if (this.column && this.column.dtxp) { |
|
||||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
|
||||||
} |
|
||||||
return []; |
|
||||||
}, |
|
||||||
parentListeners(){ |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if(this.$listeners.blur){ |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if(this.$listeners.focus){ |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
select { |
|
||||||
width: 100%; |
|
||||||
height: 100%; |
|
||||||
color: var(--v-textColor-base); |
|
||||||
-webkit-appearance: menulist; |
|
||||||
/*webkit browsers */ |
|
||||||
-moz-appearance: menulist; |
|
||||||
/*Firefox */ |
|
||||||
appearance: menulist; |
|
||||||
} |
|
||||||
|
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,100 +0,0 @@ |
|||||||
<template> |
|
||||||
<div class="d-flex align-center"> |
|
||||||
|
|
||||||
<div> |
|
||||||
<div class="" v-for="(val,i) of setValues" :key="val"> |
|
||||||
<input type="checkbox" :id="`key-check-box-${val}`" class="orange--text" v-model="localState" :value="val"> |
|
||||||
<label class="py-1 px-3 d-inline-block my-1 label" :for="`key-check-box-${val}`" |
|
||||||
:style="{ |
|
||||||
background:colors[i % colors.length ] |
|
||||||
}" |
|
||||||
>{{ val }}</label> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import colors from "@/components/project/spreadsheet/helpers/colors"; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "set-list-checkbox-cell", |
|
||||||
props: { |
|
||||||
value: String, |
|
||||||
column: Object |
|
||||||
}, |
|
||||||
data() { |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
this.$el.focus(); |
|
||||||
let event; |
|
||||||
event = document.createEvent('MouseEvents'); |
|
||||||
event.initMouseEvent('mousedown', true, true, window); |
|
||||||
this.$el.dispatchEvent(event); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
colors() { |
|
||||||
return this.$store.state.windows.darkTheme ? colors.dark : colors.light; |
|
||||||
}, |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return this.value && this.value.split(',') |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', val.join(',')); |
|
||||||
this.$emit('update'); |
|
||||||
} |
|
||||||
}, |
|
||||||
setValues() { |
|
||||||
if (this.column && this.column.dtxp) { |
|
||||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
|
||||||
} |
|
||||||
return []; |
|
||||||
}, |
|
||||||
parentListeners() { |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if (this.$listeners.blur) { |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if (this.$listeners.focus) { |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
|
|
||||||
.label { |
|
||||||
border-radius: 25px; |
|
||||||
} |
|
||||||
|
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,91 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<select v-on="parentListeners" v-model="localState" multiple> |
|
||||||
<option v-for="val of setValues" :key="val" :value="val">{{ val }}</option> |
|
||||||
</select> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "set-list-cell", |
|
||||||
props: { |
|
||||||
value: String, |
|
||||||
column: Object |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
this.$el.focus(); |
|
||||||
let event; |
|
||||||
event = document.createEvent('MouseEvents'); |
|
||||||
event.initMouseEvent('mousedown', true, true, window); |
|
||||||
this.$el.dispatchEvent(event); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return this.value && this.value.split(',') |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', val.join(',')); |
|
||||||
this.$emit('update'); |
|
||||||
} |
|
||||||
}, |
|
||||||
setValues() { |
|
||||||
if (this.column && this.column.dtxp) { |
|
||||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
|
||||||
} |
|
||||||
return []; |
|
||||||
}, |
|
||||||
parentListeners(){ |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if(this.$listeners.blur){ |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if(this.$listeners.focus){ |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
select { |
|
||||||
width: 100%; |
|
||||||
height: 100%; |
|
||||||
color: var(--v-textColor-base); |
|
||||||
-webkit-appearance: menulist; |
|
||||||
/*webkit browsers */ |
|
||||||
-moz-appearance: menulist; |
|
||||||
/*Firefox */ |
|
||||||
appearance: menulist; |
|
||||||
} |
|
||||||
|
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,78 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-menu> |
|
||||||
<template v-slot:activator="{on}"> |
|
||||||
<div class="value" v-on="on">{{ localState }}</div> |
|
||||||
</template> |
|
||||||
<v-time-picker v-on="parentListeners" v-model="localState"></v-time-picker> |
|
||||||
</v-menu> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "time-picker-cell", |
|
||||||
props: { |
|
||||||
value: [String, Date] |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
if (this.$el && this.$el.$el) { |
|
||||||
this.$el.$el.focus(); |
|
||||||
} |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return typeof this.value === 'string' ? this.value.replace(/(\d)T(?=\d)/, '$1 ') : this.value; |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', (new Date(val).toJSON() || '').slice(0, 10)); |
|
||||||
} |
|
||||||
}, |
|
||||||
parentListeners() { |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if (this.$listeners.blur) { |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if (this.$listeners.focus) { |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
if (this.$listeners.cancel) { |
|
||||||
$listeners.cancel = this.$listeners.cancel; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
.value { |
|
||||||
min-height: 20px; |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,26 +1,28 @@ |
|||||||
<template> |
<template> |
||||||
<editable-attachment-cell |
<v-lazy> |
||||||
:isLocked="isLocked" |
<editable-attachment-cell |
||||||
:db-alias="dbAlias" |
:isLocked="isLocked" |
||||||
@click.stop="$emit('enableedit')" v-if="isAttachment" :value="value" :column="column"></editable-attachment-cell> |
:db-alias="dbAlias" |
||||||
<set-list-cell @click.stop="$emit('enableedit')" v-else-if="isSet" :value="value" :column="column"></set-list-cell> |
@click.stop="$emit('enableedit')" v-if="isAttachment" :value="value" :column="column"></editable-attachment-cell> |
||||||
<!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>--> |
<set-list-cell @click.stop="$emit('enableedit')" v-else-if="isSet" :value="value" :column="column"></set-list-cell> |
||||||
<enum-cell @click.stop="$emit('enableedit')" v-else-if="isEnum" :value="value" :column="column"></enum-cell> |
<!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>--> |
||||||
<span v-else>{{ value }}</span> |
<enum-cell @click.stop="$emit('enableedit')" v-else-if="isEnum" :value="value" :column="column"></enum-cell> |
||||||
|
<span v-else>{{ value }}</span> |
||||||
|
</v-lazy> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script> |
<script> |
||||||
import cell from "@/components/project/spreadsheet/mixins/cell"; |
import cell from "@/components/project/spreadsheet/mixins/cell"; |
||||||
import SetListCell from "@/components/project/spreadsheet/cell/setListCell"; |
import SetListCell from "@/components/project/spreadsheet/components/cell/setListCell"; |
||||||
import EnumCell from "@/components/project/spreadsheet/cell/enumCell"; |
import EnumCell from "@/components/project/spreadsheet/components/cell/enumCell"; |
||||||
import AttachmentCell from "@/components/project/spreadsheet/cell/attachmentCell"; |
import AttachmentCell from "@/components/project/spreadsheet/components/cell/attachmentCell"; |
||||||
import EditableAttachmentCell from "@/components/project/spreadsheet/editableCell/editableAttachmentCell"; |
import EditableAttachmentCell from "@/components/project/spreadsheet/components/editableCell/editableAttachmentCell"; |
||||||
import EnumListEditableCell from "@/components/project/spreadsheet/editableCell/enumListEditableCell"; |
import EnumListEditableCell from "@/components/project/spreadsheet/components/editableCell/enumListEditableCell"; |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: "tableCell", |
name: "tableCell", |
||||||
components: {EnumListEditableCell, EditableAttachmentCell, AttachmentCell, EnumCell, SetListCell}, |
components: {EnumListEditableCell, EditableAttachmentCell, AttachmentCell, EnumCell, SetListCell}, |
||||||
props: ['value','dbAlias','isLocked','selected'], |
props: ['value', 'dbAlias', 'isLocked', 'selected'], |
||||||
mixins: [cell], |
mixins: [cell], |
||||||
computed: {} |
computed: {} |
||||||
} |
} |
@ -1,38 +1,43 @@ |
|||||||
<template> |
<template> |
||||||
<div> |
<div> |
||||||
<span v-for="v in (value || '').split(',')" :key="v" :style="{ |
<v-chip |
||||||
background:colors[v] |
small |
||||||
}" class="set-item ma-1 py-1 px-3">{{ v }}</span> |
v-for="v in (value || '').split(',')" |
||||||
|
:key="v" |
||||||
|
:color="colors[setValues.indexOf(v) % colors.length]" |
||||||
|
class="set-item ma-1 py-1 px-3" |
||||||
|
> |
||||||
|
{{ v }} |
||||||
|
</v-chip> |
||||||
</div> |
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script> |
<script> |
||||||
import colors from "@/components/project/spreadsheet/helpers/colors"; |
import colors from "@/mixins/colors"; |
||||||
|
|
||||||
export default { |
export default { |
||||||
props: ['value', 'column'], |
props: ['value', 'column'], |
||||||
name: "setListCell", |
name: "setListCell", |
||||||
|
mixins: [colors], |
||||||
computed: { |
computed: { |
||||||
colors() { |
|
||||||
const col = this.$store.state.windows.darkTheme ? colors.dark : colors.light; |
setValues() { |
||||||
if (this.column && this.column.dtxp) { |
if (this.column && this.column.dtxp) { |
||||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')).reduce((obj, v, i) => ({ |
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
||||||
...obj, |
|
||||||
[v]: col[i] |
|
||||||
}), {}) |
|
||||||
} |
} |
||||||
return {}; |
return []; |
||||||
} |
}, |
||||||
} |
} |
||||||
} |
} |
||||||
</script> |
</script> |
||||||
|
|
||||||
<style scoped> |
<style scoped> |
||||||
|
/* |
||||||
.set-item { |
.set-item { |
||||||
display: inline-block; |
display: inline-block; |
||||||
border-radius: 25px; |
border-radius: 25px; |
||||||
white-space: nowrap; |
white-space: nowrap; |
||||||
} |
}*/ |
||||||
</style> |
</style> |
||||||
<!-- |
<!-- |
||||||
/** |
/** |
@ -0,0 +1,302 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<v-container fluid class="wrapper mb-3"> |
||||||
|
<v-row> |
||||||
|
<v-col> |
||||||
|
<v-radio-group row hide-details dense v-model="type" @change="$refs.input.validate()" class="pt-0 mt-0"> |
||||||
|
<v-radio value="hm" label="Has Many"></v-radio> |
||||||
|
<v-radio value="mm" label="Many To Many"></v-radio> |
||||||
|
<v-radio disabled value="oo" label="One To One"></v-radio> |
||||||
|
</v-radio-group> |
||||||
|
</v-col> |
||||||
|
</v-row> |
||||||
|
</v-container> |
||||||
|
|
||||||
|
<v-container fluid class="wrapper"> |
||||||
|
<v-row> |
||||||
|
<v-col cols="12"> |
||||||
|
<v-autocomplete |
||||||
|
ref="input" |
||||||
|
outlined |
||||||
|
class="caption" |
||||||
|
hide-details="auto" |
||||||
|
:loading="isRefTablesLoading" |
||||||
|
label="Child Table" |
||||||
|
:full-width="false" |
||||||
|
v-model="relation.childTable" |
||||||
|
:items="refTables" |
||||||
|
item-text="_tn" |
||||||
|
item-value="tn" |
||||||
|
required |
||||||
|
dense |
||||||
|
:rules="tableRules" |
||||||
|
></v-autocomplete> |
||||||
|
</v-col |
||||||
|
> |
||||||
|
<!-- <v-col cols="6"> |
||||||
|
<v-text-field |
||||||
|
outlined |
||||||
|
class="caption" |
||||||
|
hide-details |
||||||
|
label="Child Column" |
||||||
|
:full-width="false" |
||||||
|
v-model="relation.childColumn" |
||||||
|
required |
||||||
|
dense |
||||||
|
ref="childColumnRef" |
||||||
|
@change="onColumnSelect" |
||||||
|
></v-text-field> |
||||||
|
</v-col |
||||||
|
>--> |
||||||
|
</v-row> |
||||||
|
<template v-if="!isSQLite"> |
||||||
|
<v-row> |
||||||
|
<v-col cols="6"> |
||||||
|
<v-autocomplete |
||||||
|
outlined |
||||||
|
class="caption" |
||||||
|
hide-details |
||||||
|
label="On Update" |
||||||
|
:full-width="false" |
||||||
|
v-model="relation.onUpdate" |
||||||
|
:items="onUpdateDeleteOptions" |
||||||
|
required |
||||||
|
dense |
||||||
|
:disabled="relation.type !== 'real'" |
||||||
|
></v-autocomplete> |
||||||
|
</v-col> |
||||||
|
<v-col cols="6"> |
||||||
|
<v-autocomplete |
||||||
|
outlined |
||||||
|
class="caption" |
||||||
|
hide-details |
||||||
|
label="On Delete" |
||||||
|
:full-width="false" |
||||||
|
v-model="relation.onDelete" |
||||||
|
:items="onUpdateDeleteOptions" |
||||||
|
required |
||||||
|
dense |
||||||
|
:disabled="relation.type !== 'real'" |
||||||
|
></v-autocomplete> |
||||||
|
</v-col> |
||||||
|
</v-row> |
||||||
|
|
||||||
|
|
||||||
|
<v-row> |
||||||
|
|
||||||
|
<v-col> |
||||||
|
<v-checkbox |
||||||
|
false-value="real" |
||||||
|
true-value="virtual" |
||||||
|
label="Virtual Relation" |
||||||
|
:full-width="false" |
||||||
|
v-model="relation.type" |
||||||
|
required |
||||||
|
class="mt-0" |
||||||
|
dense |
||||||
|
></v-checkbox> |
||||||
|
</v-col> |
||||||
|
</v-row> |
||||||
|
</template> |
||||||
|
</v-container> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: "linked-to-another-options", |
||||||
|
props: ['nodes', 'column', 'meta', 'isSQLite', 'alias'], |
||||||
|
data: () => ({ |
||||||
|
type: 'hm', |
||||||
|
refTables: [], |
||||||
|
refColumns: [], |
||||||
|
relation: {}, |
||||||
|
isRefTablesLoading: false, |
||||||
|
isRefColumnsLoading: false, |
||||||
|
}), |
||||||
|
async created() { |
||||||
|
await this.loadTablesList(); |
||||||
|
this.relation = { |
||||||
|
childColumn: `${this.meta.tn}_id`, |
||||||
|
childTable: this.nodes.tn, |
||||||
|
parentTable: this.column.rtn || "", |
||||||
|
parentColumn: this.column.rcn || "", |
||||||
|
onDelete: "NO ACTION", |
||||||
|
onUpdate: "NO ACTION", |
||||||
|
updateRelation: this.column.rtn ? true : false, |
||||||
|
type: 'real' |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
onUpdateDeleteOptions() { |
||||||
|
if (this.isMSSQL) { |
||||||
|
return ["NO ACTION"] |
||||||
|
} |
||||||
|
return [ |
||||||
|
"NO ACTION", |
||||||
|
"CASCADE", |
||||||
|
"RESTRICT", |
||||||
|
"SET NULL", |
||||||
|
"SET DEFAULT" |
||||||
|
]; |
||||||
|
}, |
||||||
|
tableRules() { |
||||||
|
return [ |
||||||
|
v => !!v || 'Required', |
||||||
|
v => { |
||||||
|
if (this.type === 'mm') |
||||||
|
return !(this.meta.manyToMany || []) |
||||||
|
.some(mm => mm.tn === v && mm.rtn === this.meta.tn || mm.rtn === v && mm.tn === this.meta.tn) |
||||||
|
|| 'Duplicate many to many relation is not allowed at the moment'; |
||||||
|
if (this.type === 'hm') |
||||||
|
return !(this.meta.hasMany || []) |
||||||
|
.some(hm => hm.tn === v) |
||||||
|
|| 'Duplicate has many relation is not allowed at the moment'; |
||||||
|
}, |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadColumnList() { |
||||||
|
this.isRefColumnsLoading = true; |
||||||
|
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, 'columnList', {tn: this.meta.tn}]) |
||||||
|
|
||||||
|
|
||||||
|
const columns = result.data.list; |
||||||
|
this.refColumns = JSON.parse(JSON.stringify(columns)); |
||||||
|
|
||||||
|
if (this.relation.updateRelation && !this.relationColumnChanged) { |
||||||
|
//only first time when editing add defaault value to this field |
||||||
|
this.relation.parentColumn = this.column.rcn; |
||||||
|
this.relationColumnChanged = true; |
||||||
|
} else { |
||||||
|
//find pk column and assign to parentColumn |
||||||
|
const pkKeyColumns = this.refColumns.filter(el => el.pk); |
||||||
|
this.relation.parentColumn = (pkKeyColumns[0] || {}).cn || ""; |
||||||
|
} |
||||||
|
this.onColumnSelect(); |
||||||
|
|
||||||
|
this.isRefColumnsLoading = false; |
||||||
|
}, |
||||||
|
async loadTablesList() { |
||||||
|
this.isRefTablesLoading = true; |
||||||
|
|
||||||
|
|
||||||
|
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, 'tableList']); |
||||||
|
|
||||||
|
|
||||||
|
this.refTables = result.data.list.map(({tn, _tn}) => ({tn, _tn})) |
||||||
|
this.isRefTablesLoading = false; |
||||||
|
}, |
||||||
|
async saveManyToMany() { |
||||||
|
try { |
||||||
|
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [ |
||||||
|
{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, |
||||||
|
'xcM2MRelationCreate', |
||||||
|
{ |
||||||
|
_cn: this.alias, |
||||||
|
...this.relation, |
||||||
|
type: this.isSQLite || this.relation.type === 'virtual' ? 'virtual' : 'real', |
||||||
|
parentTable: this.meta.tn, |
||||||
|
updateRelation: this.column.rtn ? true : false |
||||||
|
} |
||||||
|
]); |
||||||
|
} catch (e) { |
||||||
|
throw e |
||||||
|
} |
||||||
|
}, |
||||||
|
async saveRelation() { |
||||||
|
if (this.type === 'mm') { |
||||||
|
await this.saveManyToMany(); |
||||||
|
return; |
||||||
|
} |
||||||
|
try { |
||||||
|
const parentPK = this.meta.columns.find(c => c.pk); |
||||||
|
|
||||||
|
|
||||||
|
const childTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, 'tableXcModelGet', { |
||||||
|
tn: this.relation.childTable |
||||||
|
}]); |
||||||
|
|
||||||
|
const childMeta = JSON.parse(childTableData.meta) |
||||||
|
|
||||||
|
const newChildColumn = {}; |
||||||
|
|
||||||
|
|
||||||
|
Object.assign(newChildColumn, { |
||||||
|
cn: this.relation.childColumn, |
||||||
|
_cn: this.relation.childColumn, |
||||||
|
rqd: false, |
||||||
|
pk: false, |
||||||
|
ai: false, |
||||||
|
cdf: null, |
||||||
|
dt: parentPK.dt, |
||||||
|
dtxp: parentPK.dtxp, |
||||||
|
dtxs: parentPK.dtxs, |
||||||
|
un: parentPK.un, |
||||||
|
altered: 1 |
||||||
|
}); |
||||||
|
|
||||||
|
const columns = [...childMeta.columns, newChildColumn]; |
||||||
|
|
||||||
|
let result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, "tableUpdate", { |
||||||
|
tn: childMeta.tn, |
||||||
|
_tn: childMeta._tn, |
||||||
|
originalColumns: childMeta.columns, |
||||||
|
columns |
||||||
|
}]); |
||||||
|
|
||||||
|
|
||||||
|
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [ |
||||||
|
{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, |
||||||
|
this.relation.type === 'real' && !this.isSQLite ? "relationCreate" : 'xcVirtualRelationCreate', |
||||||
|
{ |
||||||
|
...this.relation, |
||||||
|
parentTable: this.meta.tn, |
||||||
|
parentColumn: parentPK.cn, |
||||||
|
updateRelation: this.column.rtn ? true : false, |
||||||
|
type: 'real' |
||||||
|
} |
||||||
|
]); |
||||||
|
} catch (e) { |
||||||
|
throw e |
||||||
|
} |
||||||
|
}, |
||||||
|
onColumnSelect() { |
||||||
|
const col = this.refColumns.find(c => this.relation.parentColumn === c.cn); |
||||||
|
this.$emit('onColumnSelect', col) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
.wrapper { |
||||||
|
border: solid 2px #7f828b33; |
||||||
|
border-radius: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
/deep/ .v-input__append-inner { |
||||||
|
margin-top: 4px !important; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,131 @@ |
|||||||
|
<template> |
||||||
|
<v-card min-width="300px" max-width="400px" max-height="95vh" style="overflow: auto" |
||||||
|
class="elevation-0 card"> |
||||||
|
<v-form v-model="valid"> |
||||||
|
<v-container fluid @click.stop.prevent> |
||||||
|
<v-row> |
||||||
|
<v-col cols="12" class="d-flex pb-0"> |
||||||
|
|
||||||
|
<v-spacer></v-spacer> |
||||||
|
<v-btn x-small outlined @click="close">Cancel</v-btn> |
||||||
|
<v-btn x-small color="primary" @click="save" :disabled="!valid">Save</v-btn> |
||||||
|
</v-col> |
||||||
|
<v-col cols="12"> |
||||||
|
<v-text-field |
||||||
|
ref="column" |
||||||
|
hide-details="auto" |
||||||
|
color="primary" |
||||||
|
v-model="newColumn._cn" |
||||||
|
class="caption" |
||||||
|
label="Column name" |
||||||
|
:rules="[ |
||||||
|
v => !!v || 'Required', |
||||||
|
v => !meta || !meta.columns || !column ||meta.columns.every(c => v !== c.cn ) && meta.v.every(c => column && c._cn === column._cn || v !== c._cn ) || 'Duplicate column name' |
||||||
|
]" |
||||||
|
dense outlined></v-text-field> |
||||||
|
</v-col> |
||||||
|
|
||||||
|
|
||||||
|
</v-row> |
||||||
|
</v-container> |
||||||
|
</v-form> |
||||||
|
|
||||||
|
|
||||||
|
</v-card> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
name: "editVirtualColumn", |
||||||
|
components: {}, |
||||||
|
props: { |
||||||
|
nodes: Object, |
||||||
|
meta: Object, |
||||||
|
value: Boolean, |
||||||
|
column: Object |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
valid: false, |
||||||
|
newColumn: {} |
||||||
|
}), |
||||||
|
async created() { |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
close() { |
||||||
|
this.$emit('input', false); |
||||||
|
this.newColumn = {}; |
||||||
|
}, |
||||||
|
async save() { |
||||||
|
try { |
||||||
|
await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, 'xcUpdateVirtualKeyAlias', { |
||||||
|
tn: this.nodes.tn, |
||||||
|
oldAlias: this.column._cn, |
||||||
|
newAlias: this.newColumn._cn, |
||||||
|
}]); |
||||||
|
|
||||||
|
this.$toast.success('Successfully updated alias').goAway(3000); |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
this.$toast.error('Failed to update column alias').goAway(3000); |
||||||
|
} |
||||||
|
this.$emit('saved'); |
||||||
|
this.$emit('input', false); |
||||||
|
}, |
||||||
|
|
||||||
|
focusInput() { |
||||||
|
setTimeout(() => { |
||||||
|
if (this.$refs.column && this.$refs.column.$el) { |
||||||
|
this.$refs.column.$el.querySelector('input').focus() |
||||||
|
} |
||||||
|
}, 100); |
||||||
|
}, |
||||||
|
|
||||||
|
}, mounted() { |
||||||
|
this.newColumn = {...this.column} |
||||||
|
}, watch: { |
||||||
|
column(c) { |
||||||
|
this.newColumn = {...c} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
|
||||||
|
|
||||||
|
::v-deep { |
||||||
|
|
||||||
|
|
||||||
|
.v-input__slot { |
||||||
|
min-height: auto !important; |
||||||
|
} |
||||||
|
|
||||||
|
.v-input:not(.v-input--is-focused) fieldset { |
||||||
|
border-color: #7f828b33 !important; |
||||||
|
} |
||||||
|
|
||||||
|
.ui-type input { |
||||||
|
height: 24px; |
||||||
|
} |
||||||
|
|
||||||
|
.v-input--selection-controls__input > i { |
||||||
|
transform: scale(.83); |
||||||
|
} |
||||||
|
|
||||||
|
label { |
||||||
|
font-size: 0.75rem !important |
||||||
|
} |
||||||
|
|
||||||
|
.v-text-field--outlined.v-input--dense .v-label:not(.v-label--active) { |
||||||
|
top: 6px; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.card { |
||||||
|
border: solid 2px #7f828b33; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
@ -1,11 +1,11 @@ |
|||||||
<template> |
<template> |
||||||
<v-select v-on="parentListeners" v-model="localState" dense flat :items="enumValues" hide-details class="mt-0" :clearable="!column.rqd"> |
<v-select solo v-on="parentListeners" v-model="localState" dense flat :items="enumValues" hide-details class="mt-0" :clearable="!column.rqd"> |
||||||
<!-- <option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>--> |
<!-- <option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>--> |
||||||
<template v-slot:selection="{item}"> |
<template v-slot:selection="{item}"> |
||||||
<div class="d-100 pl-4" :class="{ |
<div class="d-100" :class="{ |
||||||
'text-center' : !isForm |
'text-center' : !isForm |
||||||
}"> |
}"> |
||||||
<v-chip small :color="colors[enumValues.indexOf(item) % colors.length]">{{ item }}</v-chip> |
<v-chip small :color="colors[enumValues.indexOf(item) % colors.length]" class="ma-1">{{ item }}</v-chip> |
||||||
</div> |
</div> |
||||||
</template> |
</template> |
||||||
<template v-slot:item="{item}"> |
<template v-slot:item="{item}"> |
@ -0,0 +1,54 @@ |
|||||||
|
<template> |
||||||
|
<v-pagination |
||||||
|
v-if="count !== Infinity" |
||||||
|
style="max-width: 100%" |
||||||
|
v-model="page" |
||||||
|
:length="Math.ceil(count / size)" |
||||||
|
:total-visible="8" |
||||||
|
@input="$emit('input',page)" |
||||||
|
color="primary lighten-2" |
||||||
|
></v-pagination> |
||||||
|
<div v-else class="mx-auto d-flex align-center mt-n1 " style="max-width:250px"> |
||||||
|
<span class="caption" style="white-space: nowrap"> Change page:</span> |
||||||
|
<v-text-field |
||||||
|
class="ml-1 caption" |
||||||
|
:full-width="false" |
||||||
|
outlined |
||||||
|
dense |
||||||
|
hide-details |
||||||
|
v-model="page" |
||||||
|
@keydown.enter="$emit('input',page)" |
||||||
|
type="number" |
||||||
|
> |
||||||
|
<template #append> |
||||||
|
<x-icon tooltip="Change page" small icon.class="mt-1" @click="$emit('input',page)">mdi-keyboard-return |
||||||
|
</x-icon> |
||||||
|
</template> |
||||||
|
</v-text-field> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
props: { |
||||||
|
count: Number, |
||||||
|
value: Number, |
||||||
|
size: Number, |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
page: 1 |
||||||
|
}), |
||||||
|
mounted() { |
||||||
|
this.page = this.value; |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
value(v) { |
||||||
|
this.page = v; |
||||||
|
} |
||||||
|
}, |
||||||
|
name: "pagination" |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
</style> |
@ -0,0 +1,144 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<v-lazy> |
||||||
|
<has-many-cell |
||||||
|
ref="cell" |
||||||
|
v-if="hm" |
||||||
|
:row="row" |
||||||
|
:value="row[`${hm._tn}List`]" |
||||||
|
:meta="meta" |
||||||
|
:hm="hm" |
||||||
|
:nodes="nodes" |
||||||
|
:active="active" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:is-new="isNew" |
||||||
|
:is-form="isForm" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
v-on="$listeners" |
||||||
|
/> |
||||||
|
<many-to-many-cell |
||||||
|
ref="cell" |
||||||
|
v-else-if="mm" |
||||||
|
:row="row" |
||||||
|
:value="row[`${mm._rtn}MMList`]" |
||||||
|
:meta="meta" |
||||||
|
:mm="mm" |
||||||
|
:nodes="nodes" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:active="active" |
||||||
|
:is-new="isNew" |
||||||
|
:api="api" |
||||||
|
:is-form="isForm" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
v-on="$listeners" |
||||||
|
/> |
||||||
|
<belongs-to-cell |
||||||
|
ref="cell" |
||||||
|
:disabled-columns="disabledColumns" |
||||||
|
v-else-if="bt" |
||||||
|
:active="active" |
||||||
|
:row="row" |
||||||
|
:value="row[`${bt._rtn}Read`]" |
||||||
|
:meta="meta" |
||||||
|
:bt="bt" |
||||||
|
:nodes="nodes" |
||||||
|
:api="api" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:is-new="isNew" |
||||||
|
:is-form="isForm" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
v-on="$listeners" |
||||||
|
/> |
||||||
|
</v-lazy> |
||||||
|
</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"; |
||||||
|
|
||||||
|
// todo: optimize parent/child meta extraction |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "virtual-cell", |
||||||
|
components: { |
||||||
|
belongsToCell, |
||||||
|
manyToManyCell, |
||||||
|
hasManyCell |
||||||
|
}, |
||||||
|
props: { |
||||||
|
breadcrumbs: { |
||||||
|
type: Array, |
||||||
|
default() { |
||||||
|
return []; |
||||||
|
} |
||||||
|
}, |
||||||
|
column: [Object], |
||||||
|
row: [Object], |
||||||
|
nodes: [Object], |
||||||
|
meta: [Object], |
||||||
|
api: [Object, Function], |
||||||
|
active: Boolean, |
||||||
|
sqlUi: [Object, Function], |
||||||
|
isNew: { |
||||||
|
type: Boolean, |
||||||
|
default: false |
||||||
|
}, |
||||||
|
isForm: { |
||||||
|
type: Boolean, |
||||||
|
default: false |
||||||
|
}, |
||||||
|
disabledColumns: Object |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
hm() { |
||||||
|
return this.column && this.column.hm; |
||||||
|
}, |
||||||
|
bt() { |
||||||
|
return this.column && this.column.bt; |
||||||
|
}, |
||||||
|
mm() { |
||||||
|
return this.column && this.column.mm; |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async save(row) { |
||||||
|
if (row && this.$refs.cell && this.$refs.cell.saveLocalState) { |
||||||
|
try { |
||||||
|
await this.$refs.cell.saveLocalState(row); |
||||||
|
} catch (e) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,399 @@ |
|||||||
|
<template> |
||||||
|
<div class="d-flex d-100 chips-wrapper" :class="{active}"> |
||||||
|
<template v-if="!isForm"> |
||||||
|
<div class="chips d-flex align-center img-container flex-grow-1 hm-items"> |
||||||
|
<template v-if="value || localState"> |
||||||
|
<item-chip |
||||||
|
:active="active" |
||||||
|
:item="value" |
||||||
|
:value="cellValue" |
||||||
|
@edit="editParent" |
||||||
|
@unlink="unlink" |
||||||
|
></item-chip> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
<div class="action align-center justify-center px-1 flex-shrink-1" |
||||||
|
:class="{'d-none': !active, 'd-flex':active }"> |
||||||
|
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">{{ |
||||||
|
value ? 'mdi-arrow-expand' : 'mdi-plus' |
||||||
|
}} |
||||||
|
</x-icon> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<list-items |
||||||
|
:key="parentId" |
||||||
|
v-if="newRecordModal" |
||||||
|
:size="10" |
||||||
|
:meta="parentMeta" |
||||||
|
:primary-col="parentPrimaryCol" |
||||||
|
:primary-key="parentPrimaryKey" |
||||||
|
v-model="newRecordModal" |
||||||
|
:api="parentApi" |
||||||
|
@add-new-record="insertAndMapNewParentRecord" |
||||||
|
@add="addChildToParent" |
||||||
|
:query-params="parentQueryParams" |
||||||
|
/> |
||||||
|
|
||||||
|
<list-child-items |
||||||
|
ref="childList" |
||||||
|
v-if="parentMeta && isForm" |
||||||
|
:isForm="isForm" |
||||||
|
:local-state="localState? [localState] : []" |
||||||
|
:is-new="isNew" |
||||||
|
:size="10" |
||||||
|
:parent-meta="parentMeta" |
||||||
|
:meta="parentMeta" |
||||||
|
:primary-col="parentPrimaryCol" |
||||||
|
:primary-key="parentPrimaryKey" |
||||||
|
:api="parentApi" |
||||||
|
:query-params="{ |
||||||
|
...parentQueryParams, |
||||||
|
where: `(${parentPrimaryKey},eq,${parentId})` |
||||||
|
}" |
||||||
|
@new-record="showNewRecordModal" |
||||||
|
@edit="editParent" |
||||||
|
@unlink="unlink" |
||||||
|
:bt="value" |
||||||
|
/> |
||||||
|
|
||||||
|
<v-dialog |
||||||
|
:overlay-opacity="0.8" |
||||||
|
v-if="selectedParent" |
||||||
|
width="1000px" |
||||||
|
max-width="100%" |
||||||
|
class=" mx-auto" |
||||||
|
v-model="expandFormModal"> |
||||||
|
<component |
||||||
|
v-if="selectedParent" |
||||||
|
:is="form" |
||||||
|
:db-alias="nodes.dbAlias" |
||||||
|
:has-many="parentMeta.hasMany" |
||||||
|
:belongs-to="parentMeta.belongsTo" |
||||||
|
:table="parentMeta.tn" |
||||||
|
:old-row="{...selectedParent}" |
||||||
|
:meta="parentMeta" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:primary-value-column="parentPrimaryCol" |
||||||
|
:api="parentApi" |
||||||
|
:available-columns="parentAvailableColumns" |
||||||
|
:nodes="nodes" |
||||||
|
:query-params="parentQueryParams" |
||||||
|
:is-new.sync="isNewParent" |
||||||
|
icon-color="warning" |
||||||
|
ref="expandedForm" |
||||||
|
v-model="selectedParent" |
||||||
|
@cancel="selectedParent = null" |
||||||
|
@input="onParentSave" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
></component> |
||||||
|
|
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory"; |
||||||
|
import ListItems from "@/components/project/spreadsheet/components/virtualCell/components/listItems"; |
||||||
|
import ItemChip from "@/components/project/spreadsheet/components/virtualCell/components/item-chip"; |
||||||
|
import ListChildItems from "@/components/project/spreadsheet/components/virtualCell/components/listChildItems"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "belongs-to-cell", |
||||||
|
components: {ListChildItems, ItemChip, ListItems}, |
||||||
|
props: { |
||||||
|
breadcrumbs: { |
||||||
|
type: Array, |
||||||
|
default() { |
||||||
|
return []; |
||||||
|
} |
||||||
|
}, |
||||||
|
isForm: Boolean, |
||||||
|
value: [Object, Array], |
||||||
|
meta: [Object], |
||||||
|
bt: Object, |
||||||
|
nodes: [Object], |
||||||
|
row: [Object], |
||||||
|
api: [Object, Function], |
||||||
|
sqlUi: [Object, Function], |
||||||
|
active: Boolean, |
||||||
|
isNew: Boolean, |
||||||
|
disabledColumns: Object |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
newRecordModal: false, |
||||||
|
parentListModal: false, |
||||||
|
// parentMeta: null, |
||||||
|
list: null, |
||||||
|
childList: null, |
||||||
|
dialogShow: false, |
||||||
|
confirmAction: null, |
||||||
|
confirmMessage: '', |
||||||
|
selectedParent: null, |
||||||
|
isNewParent: false, |
||||||
|
expandFormModal: false, |
||||||
|
localState: null, |
||||||
|
pid: null |
||||||
|
}), |
||||||
|
async mounted() { |
||||||
|
if (this.isForm) { |
||||||
|
await this.loadParentMeta() |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async onParentSave(parent) { |
||||||
|
if (this.isNewParent) { |
||||||
|
await this.addChildToParent(parent) |
||||||
|
} else { |
||||||
|
this.$emit('loadTableData') |
||||||
|
} |
||||||
|
}, |
||||||
|
async insertAndMapNewParentRecord() { |
||||||
|
await this.loadParentMeta(); |
||||||
|
this.newRecordModal = false; |
||||||
|
this.isNewParent = true; |
||||||
|
this.selectedParent = {}; |
||||||
|
this.expandFormModal = true; |
||||||
|
}, |
||||||
|
|
||||||
|
async unlink() { |
||||||
|
const column = this.meta.columns.find(c => c.cn === this.bt.cn); |
||||||
|
const _cn = column._cn; |
||||||
|
if (this.isNew) { |
||||||
|
this.$emit('updateCol', this.row, _cn, null); |
||||||
|
this.localState = null; |
||||||
|
return |
||||||
|
} |
||||||
|
if (column.rqd) { |
||||||
|
this.$toast.info('Unlink is not possible, instead map to another parent.').goAway(3000) |
||||||
|
return |
||||||
|
} |
||||||
|
const id = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___'); |
||||||
|
await this.api.update(id, {[_cn]: null}, this.row) |
||||||
|
this.$emit('loadTableData') |
||||||
|
if (this.isForm && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
}, |
||||||
|
async showParentListModal() { |
||||||
|
this.parentListModal = true; |
||||||
|
await this.loadParentMeta(); |
||||||
|
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; |
||||||
|
this.childList = await this.parentApi.paginatedList({ |
||||||
|
where: `(${_cn},eq,${pid})` |
||||||
|
}) |
||||||
|
}, |
||||||
|
async removeChild(child) { |
||||||
|
this.dialogShow = true; |
||||||
|
this.confirmMessage = |
||||||
|
'Do you want to delete the record?'; |
||||||
|
this.confirmAction = async act => { |
||||||
|
if (act === 'hideDialog') { |
||||||
|
this.dialogShow = false; |
||||||
|
} else { |
||||||
|
const id = this.parentMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
await this.parentApi.delete(id) |
||||||
|
this.pid = null; |
||||||
|
this.dialogShow = false; |
||||||
|
this.$emit('loadTableData') |
||||||
|
if (this.isForm && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
async loadParentMeta() { |
||||||
|
// todo: optimize |
||||||
|
if (!this.parentMeta) { |
||||||
|
await this.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias, |
||||||
|
tn: this.bt.rtn |
||||||
|
}) |
||||||
|
// const parentTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
// env: this.nodes.env, |
||||||
|
// dbAlias: this.nodes.dbAlias |
||||||
|
// }, 'tableXcModelGet', { |
||||||
|
// tn: this.bt.rtn |
||||||
|
// }]); |
||||||
|
// this.parentMeta = JSON.parse(parentTableData.meta) |
||||||
|
} |
||||||
|
}, |
||||||
|
async showNewRecordModal() { |
||||||
|
await this.loadParentMeta(); |
||||||
|
this.newRecordModal = true; |
||||||
|
}, |
||||||
|
async addChildToParent(parent) { |
||||||
|
|
||||||
|
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 _cn = this.meta.columns.find(c => c.cn === this.bt.cn)._cn; |
||||||
|
|
||||||
|
if (this.isNew) { |
||||||
|
this.localState = parent; |
||||||
|
this.$emit('updateCol', this.row, _cn, +pid || pid) |
||||||
|
this.newRecordModal = false; |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
await this.api.update(id, { |
||||||
|
[_cn]: +pid |
||||||
|
}, { |
||||||
|
[_cn]: this.value && this.value[this.parentPrimaryKey] |
||||||
|
}); |
||||||
|
this.pid = pid; |
||||||
|
|
||||||
|
this.newRecordModal = false; |
||||||
|
|
||||||
|
this.$emit('loadTableData') |
||||||
|
if (this.isForm && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
}, |
||||||
|
async editParent(parent) { |
||||||
|
await this.loadParentMeta(); |
||||||
|
this.isNewParent = false; |
||||||
|
this.selectedParent = parent; |
||||||
|
this.expandFormModal = true; |
||||||
|
setTimeout(() => { |
||||||
|
this.$refs.expandedForm && this.$refs.expandedForm.reload() |
||||||
|
}, 500) |
||||||
|
}, |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
parentMeta() { |
||||||
|
return this.$store.state.meta.metas[this.bt.rtn]; |
||||||
|
}, |
||||||
|
parentApi() { |
||||||
|
return this.parentMeta && this.parentMeta._tn ? |
||||||
|
ApiFactory.create(this.$store.getters['project/GtrProjectType'], |
||||||
|
this.parentMeta && this.parentMeta._tn, this.parentMeta && this.parentMeta.columns, this, this.parentMeta) : null; |
||||||
|
}, |
||||||
|
parentId() { |
||||||
|
return this.pid ?? (this.value && this.parentMeta && this.parentMeta.columns.filter((c) => c.pk).map(c => this.value[c._cn]).join('___')) |
||||||
|
}, |
||||||
|
parentPrimaryCol() { |
||||||
|
return this.parentMeta && (this.parentMeta.columns.find(c => c.pv) || {})._cn |
||||||
|
}, |
||||||
|
parentPrimaryKey() { |
||||||
|
return this.parentMeta && (this.parentMeta.columns.find(c => c.pk) || {})._cn |
||||||
|
}, |
||||||
|
parentQueryParams() { |
||||||
|
if (!this.parentMeta) return {} |
||||||
|
return { |
||||||
|
childs: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '', |
||||||
|
parents: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.bt).map(({bt}) => bt.rtn).join()) || '', |
||||||
|
many: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.mm).map(({mm}) => mm.rtn).join()) || '' |
||||||
|
} |
||||||
|
}, |
||||||
|
parentAvailableColumns() { |
||||||
|
const hideCols = ['created_at', 'updated_at']; |
||||||
|
if (!this.parentMeta) return []; |
||||||
|
|
||||||
|
const columns = []; |
||||||
|
if (this.parentMeta.columns) { |
||||||
|
columns.push(...this.parentMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) && !((this.parentMeta.v || []).some(v => v.bt && v.bt.cn === c.cn)))) |
||||||
|
} |
||||||
|
if (this.parentMeta.v) { |
||||||
|
columns.push(...this.parentMeta.v.map(v => ({...v, virtual: 1}))); |
||||||
|
} |
||||||
|
return columns; |
||||||
|
}, |
||||||
|
// todo: |
||||||
|
form() { |
||||||
|
return this.selectedParent ? () => import("@/components/project/spreadsheet/components/expandedForm") : 'span'; |
||||||
|
}, |
||||||
|
cellValue() { |
||||||
|
if (this.value || this.localState) { |
||||||
|
if (this.parentMeta && this.parentPrimaryCol) { |
||||||
|
return (this.value || this.localState)[this.parentPrimaryCol] |
||||||
|
} |
||||||
|
return Object.values(this.value || this.localState)[1] |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
isNew(n, o) { |
||||||
|
if (!n && o) { |
||||||
|
this.localState = null |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
created() { |
||||||
|
this.loadParentMeta(); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.items-container { |
||||||
|
overflow-x: visible; |
||||||
|
max-height: min(500px, 60vh); |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-value { |
||||||
|
.primary-key { |
||||||
|
display: none; |
||||||
|
margin-left: .5em; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .primary-key { |
||||||
|
display: inline; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.child-card { |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
box-shadow: 0 0 .2em var(--v-textColor-lighten5) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.hm-items { |
||||||
|
flex-wrap: wrap; |
||||||
|
row-gap: 3px; |
||||||
|
gap: 3px; |
||||||
|
margin: 3px auto; |
||||||
|
} |
||||||
|
|
||||||
|
.chips-wrapper { |
||||||
|
.chips { |
||||||
|
max-width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
&.active { |
||||||
|
.chips { |
||||||
|
max-width: calc(100% - 22px); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,43 @@ |
|||||||
|
<template> |
||||||
|
<v-chip |
||||||
|
class="chip" |
||||||
|
:class="{active}" |
||||||
|
small |
||||||
|
text-color="textColor" |
||||||
|
:color="isDark ? '' : 'primary lighten-5'" |
||||||
|
@click="active && $emit('edit',item)" |
||||||
|
> |
||||||
|
<span class="name" :title="value">{{ value }}</span> |
||||||
|
<div v-show="active" class="mr-n1 ml-2"> |
||||||
|
<x-icon |
||||||
|
:color="['text' , 'textLight']" |
||||||
|
x-small |
||||||
|
icon.class="unlink-icon" |
||||||
|
@click.stop="$emit('unlink',item)" |
||||||
|
>mdi-close-thick |
||||||
|
</x-icon> |
||||||
|
</div> |
||||||
|
</v-chip> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
props: { |
||||||
|
value: String, |
||||||
|
active: Boolean, |
||||||
|
item: Object |
||||||
|
}, |
||||||
|
name: "item-chip" |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.chip { |
||||||
|
max-width: max(100%, 60px); |
||||||
|
|
||||||
|
.name { |
||||||
|
text-overflow: ellipsis; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,220 @@ |
|||||||
|
<template> |
||||||
|
<!-- <v-dialog v-model="show" width="600">--> |
||||||
|
<v-card width="600" color=""> |
||||||
|
<v-card-title v-if="!isForm" class="textColor--text mx-2" :class="{'py-2':isForm}"> |
||||||
|
<span v-if="!isForm">{{ meta ? meta._tn : 'Children' }}</span> |
||||||
|
<v-spacer> |
||||||
|
</v-spacer> |
||||||
|
<v-icon small class="mr-1" @click="loadData()">mdi-reload</v-icon> |
||||||
|
<v-btn |
||||||
|
small |
||||||
|
class="caption" |
||||||
|
color="primary" |
||||||
|
@click="$emit('new-record')" |
||||||
|
> |
||||||
|
<v-icon |
||||||
|
small>mdi-link |
||||||
|
</v-icon> |
||||||
|
Link to '{{ meta._tn }}' |
||||||
|
</v-btn> |
||||||
|
</v-card-title> |
||||||
|
<v-card-text> |
||||||
|
<div class="items-container pt-2 mb-n4" :class="{'mx-n2' : isForm}"> |
||||||
|
<div class="text-right mb-2 mt-n2 mx-2"> |
||||||
|
<v-btn |
||||||
|
v-if="isForm" |
||||||
|
x-small |
||||||
|
class="caption" |
||||||
|
color="primary" |
||||||
|
outlined |
||||||
|
@click="$emit('new-record')" |
||||||
|
> |
||||||
|
<v-icon |
||||||
|
x-small>mdi-link |
||||||
|
</v-icon> |
||||||
|
Link to '{{ meta._tn }}' |
||||||
|
</v-btn> |
||||||
|
</div> |
||||||
|
<template v-if="isDataAvail"> |
||||||
|
<v-card |
||||||
|
v-for="(ch,i) in ((data && data.list) || localState)" |
||||||
|
class="mx-2 mb-2 child-list-modal child-card" |
||||||
|
outlined |
||||||
|
:key="i" |
||||||
|
@click="$emit('edit',ch)" |
||||||
|
> |
||||||
|
<div class="remove-child-icon d-flex align-center"> |
||||||
|
<x-icon |
||||||
|
:tooltip="`Unlink this '${meta._tn}' from '${parentMeta._tn}'`" |
||||||
|
:color="['error','grey']" |
||||||
|
small |
||||||
|
@click.stop="$emit('unlink',ch,i)" |
||||||
|
icon.class="mr-1 mt-n1" |
||||||
|
>mdi-link-variant-remove |
||||||
|
</x-icon> |
||||||
|
<x-icon |
||||||
|
v-if="!mm && !bt" |
||||||
|
:tooltip="`Delete row in '${meta._tn}'`" |
||||||
|
:color="['error','grey']" |
||||||
|
small |
||||||
|
@click.stop="$emit('delete',ch,i)" |
||||||
|
>mdi-delete-outline |
||||||
|
</x-icon> |
||||||
|
</div> |
||||||
|
|
||||||
|
<v-card-title class="primary-value textColor--text text--lighten-2">{{ ch[primaryCol] }} |
||||||
|
<span class="grey--text caption primary-key ml-1" |
||||||
|
v-if="primaryKey"> (Primary Key : {{ ch[primaryKey] }})</span> |
||||||
|
</v-card-title> |
||||||
|
</v-card> |
||||||
|
</template> |
||||||
|
|
||||||
|
<div v-else-if="data || localState" class="text-center textLight--text" |
||||||
|
:class="{'pt-6 pb-4' : !isForm , 'pt-4 pb-3':isForm}"> |
||||||
|
No item{{ bt ? '' : 's' }} found |
||||||
|
</div> |
||||||
|
|
||||||
|
<div v-if="isForm" class="mb-2 d-flex align-center justify-center"> |
||||||
|
<pagination |
||||||
|
v-if="!bt && data && data.count > 1" |
||||||
|
:size="size" |
||||||
|
:count="data && data.count" |
||||||
|
v-model="page" |
||||||
|
@input="loadData" |
||||||
|
></pagination> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-card-text> |
||||||
|
<v-card-actions v-if="!isForm" class="justify-center flex-column" :class="{'py-0':isForm}"> |
||||||
|
<pagination |
||||||
|
v-if="!bt && data && data.count > 1" |
||||||
|
:size="size" |
||||||
|
:count="data && data.count" |
||||||
|
v-model="page" |
||||||
|
@input="loadData" |
||||||
|
class="mb-3" |
||||||
|
></pagination> |
||||||
|
</v-card-actions> |
||||||
|
</v-card> |
||||||
|
<!-- </v-dialog>--> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import Pagination from "@/components/project/spreadsheet/components/pagination"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "listChildItems", |
||||||
|
components: {Pagination}, |
||||||
|
props: { |
||||||
|
isForm: Boolean, |
||||||
|
bt: Object, |
||||||
|
localState: [Array], |
||||||
|
isNew: Boolean, |
||||||
|
value: Boolean, |
||||||
|
title: { |
||||||
|
type: String, |
||||||
|
default: 'Link Record' |
||||||
|
}, |
||||||
|
queryParams: { |
||||||
|
type: Object, |
||||||
|
default() { |
||||||
|
return {}; |
||||||
|
} |
||||||
|
}, |
||||||
|
primaryKey: String, |
||||||
|
primaryCol: String, |
||||||
|
meta: Object, |
||||||
|
parentMeta: Object, |
||||||
|
size: Number, |
||||||
|
api: [Object, Function], |
||||||
|
mm: [Object, Boolean] |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
data: null, |
||||||
|
page: 1 |
||||||
|
}), |
||||||
|
mounted() { |
||||||
|
this.loadData(); |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadData() { |
||||||
|
if (!this.api || this.isNew) return; |
||||||
|
this.data = await this.api.paginatedList({ |
||||||
|
limit: this.size, |
||||||
|
offset: this.size * (this.page - 1), |
||||||
|
...this.queryParams |
||||||
|
}) |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
isDataAvail() { |
||||||
|
return (this.data && this.data.list && this.data.list.length) || (this.localState && this.localState.length); |
||||||
|
}, |
||||||
|
show: { |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
}, get() { |
||||||
|
return this.value; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
queryParams() { |
||||||
|
this.loadData(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
|
||||||
|
.child-list-modal { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.remove-child-icon { |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
top: 10px; |
||||||
|
bottom: 10px; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .remove-child-icon { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.items-container { |
||||||
|
overflow-x: visible; |
||||||
|
max-height: min(500px, 60vh); |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,118 @@ |
|||||||
|
<template> |
||||||
|
<v-dialog v-model="show" width="600" content-class="dialog"> |
||||||
|
<v-icon small class="close-icon" @click="$emit('input',false)">mdi-close</v-icon> |
||||||
|
<list-child-items |
||||||
|
v-if="show" |
||||||
|
ref="child" |
||||||
|
:local-state="localState" |
||||||
|
:is-new="isNew" |
||||||
|
:size="10" |
||||||
|
:meta="meta" |
||||||
|
:parent-meta="meta" |
||||||
|
:primary-col="primaryCol" |
||||||
|
:primary-key="primaryKey" |
||||||
|
:api="api" |
||||||
|
:query-params="queryParams" |
||||||
|
v-bind="$attrs" |
||||||
|
v-on="$listeners" |
||||||
|
|
||||||
|
/> |
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import Pagination from "@/components/project/spreadsheet/components/pagination"; |
||||||
|
import ListChildItems from "@/components/project/spreadsheet/components/virtualCell/components/listChildItems"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "listChildItemsModal", |
||||||
|
components: {ListChildItems, Pagination}, |
||||||
|
props: { |
||||||
|
localState: Array, |
||||||
|
isNew: Boolean, |
||||||
|
value: Boolean, |
||||||
|
title: { |
||||||
|
type: String, |
||||||
|
default: 'Link Record' |
||||||
|
}, |
||||||
|
queryParams: { |
||||||
|
type: Object, |
||||||
|
default() { |
||||||
|
return {}; |
||||||
|
} |
||||||
|
}, |
||||||
|
primaryKey: String, |
||||||
|
primaryCol: String, |
||||||
|
meta: Object, |
||||||
|
parentMeta: Object, |
||||||
|
size: Number, |
||||||
|
api: [Object, Function], |
||||||
|
mm: [Object, Boolean] |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
data: null, |
||||||
|
page: 1 |
||||||
|
}), |
||||||
|
mounted() { |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadData() { |
||||||
|
if (this.$refs && this.$refs.child) { |
||||||
|
this.$refs.child.loadData(); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
show: { |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
}, get() { |
||||||
|
return this.value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
::v-deep { |
||||||
|
.dialog { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.close-icon { |
||||||
|
width: auto; |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
top: 10px; |
||||||
|
z-index: 9; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,225 @@ |
|||||||
|
<template> |
||||||
|
<v-dialog v-model="show" width="600" content-class="dialog"> |
||||||
|
<v-icon small class="close-icon" @click="$emit('input',false)">mdi-close</v-icon> |
||||||
|
<v-card width="600" > |
||||||
|
<v-card-title class="textColor--text mx-2 justify-center">{{ title }} |
||||||
|
|
||||||
|
</v-card-title> |
||||||
|
|
||||||
|
<v-card-title> |
||||||
|
<v-text-field |
||||||
|
hide-details |
||||||
|
dense |
||||||
|
outlined |
||||||
|
placeholder="Search records" |
||||||
|
class=" caption search-field ml-2" |
||||||
|
/> |
||||||
|
<v-spacer></v-spacer> |
||||||
|
|
||||||
|
<v-icon small class="mr-1" @click="loadData()">mdi-reload</v-icon> |
||||||
|
<v-btn small class="caption mr-2" color="primary" @click="$emit('add-new-record')"> |
||||||
|
<v-icon small>mdi-plus</v-icon> |
||||||
|
New Record |
||||||
|
</v-btn> |
||||||
|
|
||||||
|
</v-card-title> |
||||||
|
|
||||||
|
<v-card-text> |
||||||
|
<div class="items-container"> |
||||||
|
<template v-if="data && data.list && data.list.length"> |
||||||
|
<v-card |
||||||
|
v-for="(ch,i) in data.list" |
||||||
|
class="ma-2 child-card" |
||||||
|
outlined |
||||||
|
v-ripple |
||||||
|
@click="$emit('add',ch)" |
||||||
|
:key="i" |
||||||
|
> |
||||||
|
<v-card-text class="primary-value textColor--text text--lighten-2 d-flex"> |
||||||
|
<span class="font-weight-bold"> {{ ch[primaryCol] }} </span> |
||||||
|
<span class="grey--text caption primary-key " |
||||||
|
v-if="primaryKey">(Primary Key : {{ ch[primaryKey] }})</span> |
||||||
|
<v-spacer/> |
||||||
|
<v-chip v-if="hm && ch[`${hm._rtn}Read`] && ch[`${hm._rtn}Read`][hmParentPrimaryValCol]" x-small> |
||||||
|
{{ ch[`${hm._rtn}Read`][hmParentPrimaryValCol] }} |
||||||
|
</v-chip> |
||||||
|
</v-card-text> |
||||||
|
</v-card> |
||||||
|
|
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<div v-else-if="data" class="text-center py-15 textLight--text"> |
||||||
|
No items found |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</v-card-text> |
||||||
|
<v-card-actions class="justify-center py-2 flex-column"> |
||||||
|
<pagination |
||||||
|
v-if="data && data.list && data.list.length" |
||||||
|
:size="size" |
||||||
|
:count="data.count" |
||||||
|
v-model="page" |
||||||
|
@input="loadData" |
||||||
|
class="mb-3" |
||||||
|
></pagination> |
||||||
|
</v-card-actions> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import Pagination from "@/components/project/spreadsheet/components/pagination"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "listItems", |
||||||
|
components: {Pagination}, |
||||||
|
props: { |
||||||
|
value: Boolean, |
||||||
|
hm: [Object, Function], |
||||||
|
title: { |
||||||
|
type: String, |
||||||
|
default: 'Link Record' |
||||||
|
}, |
||||||
|
queryParams: { |
||||||
|
type: Object, |
||||||
|
default() { |
||||||
|
return {}; |
||||||
|
} |
||||||
|
}, |
||||||
|
primaryKey: String, |
||||||
|
primaryCol: String, |
||||||
|
meta: Object, |
||||||
|
size: Number, |
||||||
|
api: [Object, Function], |
||||||
|
mm: [Object, Function], |
||||||
|
parentId: [String, Number], |
||||||
|
parentMeta:[Object] |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
data: null, |
||||||
|
page: 1 |
||||||
|
}), |
||||||
|
mounted() { |
||||||
|
this.loadData(); |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async loadData() { |
||||||
|
if (!this.api) return; |
||||||
|
|
||||||
|
if (this.mm) { |
||||||
|
this.data = await this.api.paginatedM2mNotChildrenList({ |
||||||
|
limit: this.size, |
||||||
|
offset: this.size * (this.page - 1), |
||||||
|
...this.queryParams |
||||||
|
}, this.mm.vtn,this.parentId) |
||||||
|
|
||||||
|
} else { |
||||||
|
this.data = await this.api.paginatedList({ |
||||||
|
limit: this.size, |
||||||
|
offset: this.size * (this.page - 1), |
||||||
|
...this.queryParams |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
show: { |
||||||
|
set(v) { |
||||||
|
this.$emit('input', v) |
||||||
|
}, get() { |
||||||
|
return this.value; |
||||||
|
} |
||||||
|
}, |
||||||
|
hmParentPrimaryValCol(){ |
||||||
|
return this.hm && |
||||||
|
this.parentMeta && |
||||||
|
this.parentMeta.columns.find(v => v.pv)._cn |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.child-list-modal { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.remove-child-icon { |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
top: 10px; |
||||||
|
bottom: 10px; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .remove-child-icon { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.child-card { |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
box-shadow: 0 0 .2em var(--v-textColor-lighten5) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.primary-value { |
||||||
|
.primary-key { |
||||||
|
display: none; |
||||||
|
margin-left: .5em; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .primary-key { |
||||||
|
display: inline; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.items-container { |
||||||
|
overflow-x: visible; |
||||||
|
max-height: min(500px, 60vh); |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep { |
||||||
|
.dialog { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.close-icon { |
||||||
|
width: auto; |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
top: 10px; |
||||||
|
z-index: 9; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,467 @@ |
|||||||
|
<template> |
||||||
|
<div class="d-flex d-100 chips-wrapper" :class="{active}"> |
||||||
|
<template v-if="!isForm"> |
||||||
|
<div class="chips d-flex align-center img-container flex-grow-1 hm-items"> |
||||||
|
<template v-if="value||localState"> |
||||||
|
<item-chip |
||||||
|
v-for="(ch,i) in (value|| localState)" |
||||||
|
:active="active" |
||||||
|
:item="ch" |
||||||
|
:value="getCellValue(ch)" |
||||||
|
:key="i" |
||||||
|
@edit="editChild" |
||||||
|
@unlink="unlinkChild" |
||||||
|
></item-chip> |
||||||
|
|
||||||
|
<span v-if="value && value.length === 10" class="caption pointer ml-1 grey--text" |
||||||
|
@click="showChildListModal">more... |
||||||
|
</span> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
<div class="actions 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 x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<list-items |
||||||
|
v-if="newRecordModal" |
||||||
|
:hm="hm" |
||||||
|
:size="10" |
||||||
|
:meta="childMeta" |
||||||
|
:primary-col="childPrimaryCol" |
||||||
|
:primary-key="childPrimaryKey" |
||||||
|
v-model="newRecordModal" |
||||||
|
:api="childApi" |
||||||
|
:parent-meta="meta" |
||||||
|
@add-new-record="insertAndAddNewChildRecord" |
||||||
|
@add="addChildToParent" |
||||||
|
:query-params="{ |
||||||
|
...childQueryParams, |
||||||
|
where: isNew ? null :`~not(${childForeignKey},eq,${parentId})~or(${childForeignKey},is,null)`, |
||||||
|
}"/> |
||||||
|
|
||||||
|
<list-child-items |
||||||
|
:is="isForm ? 'list-child-items' : 'list-child-items-modal'" |
||||||
|
:isForm="isForm" |
||||||
|
ref="childList" |
||||||
|
v-if="childMeta && (childListModal || isForm)" |
||||||
|
v-model="childListModal" |
||||||
|
:local-state.sync="localState" |
||||||
|
:is-new="isNew" |
||||||
|
:size="10" |
||||||
|
:meta="childMeta" |
||||||
|
:parent-meta="meta" |
||||||
|
:primary-col="childPrimaryCol" |
||||||
|
:primary-key="childPrimaryKey" |
||||||
|
:api="childApi" |
||||||
|
:query-params="{ |
||||||
|
...childQueryParams, |
||||||
|
where: `(${childForeignKey},eq,${parentId})` |
||||||
|
}" |
||||||
|
@new-record="showNewRecordModal" |
||||||
|
@edit="editChild" |
||||||
|
@unlink="unlinkChild" |
||||||
|
@delete="deleteChild" |
||||||
|
/> |
||||||
|
|
||||||
|
<dlg-label-submit-cancel |
||||||
|
type="primary" |
||||||
|
v-if="dialogShow" |
||||||
|
:actionsMtd="confirmAction" |
||||||
|
:dialogShow="dialogShow" |
||||||
|
:heading="confirmMessage" |
||||||
|
> |
||||||
|
</dlg-label-submit-cancel> |
||||||
|
|
||||||
|
<v-dialog |
||||||
|
:overlay-opacity="0.8" |
||||||
|
v-if="selectedChild" |
||||||
|
width="1000px" |
||||||
|
max-width="100%" |
||||||
|
class=" mx-auto" |
||||||
|
v-model="expandFormModal"> |
||||||
|
<component |
||||||
|
v-if="selectedChild" |
||||||
|
:is="form" |
||||||
|
:db-alias="nodes.dbAlias" |
||||||
|
:has-many="childMeta.hasMany" |
||||||
|
:belongs-to="childMeta.belongsTo" |
||||||
|
@cancel="selectedChild = null" |
||||||
|
@input="$emit('loadTableData')" |
||||||
|
:table="childMeta.tn" |
||||||
|
v-model="selectedChild" |
||||||
|
:old-row="{...selectedChild}" |
||||||
|
:meta="childMeta" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:primary-value-column="childPrimaryCol" |
||||||
|
:api="childApi" |
||||||
|
:available-columns="childAvailableColumns" |
||||||
|
icon-color="warning" |
||||||
|
:nodes="nodes" |
||||||
|
:query-params="childQueryParams" |
||||||
|
ref="expandedForm" |
||||||
|
:is-new.sync="isNewChild" |
||||||
|
:disabled-columns="disabledChildColumns" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
></component> |
||||||
|
|
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory"; |
||||||
|
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel"; |
||||||
|
import Pagination from "@/components/project/spreadsheet/components/pagination"; |
||||||
|
import ListItems from "@/components/project/spreadsheet/components/virtualCell/components/listItems"; |
||||||
|
import ItemChip from "@/components/project/spreadsheet/components/virtualCell/components/item-chip"; |
||||||
|
import ListChildItems from "@/components/project/spreadsheet/components/virtualCell/components/listChildItems"; |
||||||
|
import listChildItemsModal |
||||||
|
from "@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal"; |
||||||
|
import {parseIfInteger} from "@/helpers"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "has-many-cell", |
||||||
|
components: { |
||||||
|
ListChildItems, |
||||||
|
ItemChip, |
||||||
|
ListItems, |
||||||
|
Pagination, |
||||||
|
DlgLabelSubmitCancel, |
||||||
|
listChildItemsModal |
||||||
|
}, |
||||||
|
props: { |
||||||
|
breadcrumbs: { |
||||||
|
type: Array, |
||||||
|
default() { |
||||||
|
return []; |
||||||
|
} |
||||||
|
}, |
||||||
|
value: [Object, Array], |
||||||
|
meta: [Object], |
||||||
|
hm: Object, |
||||||
|
nodes: [Object], |
||||||
|
row: [Object], |
||||||
|
sqlUi: [Object, Function], |
||||||
|
active: Boolean, |
||||||
|
isNew: Boolean, |
||||||
|
isForm: Boolean, |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
newRecordModal: false, |
||||||
|
childListModal: false, |
||||||
|
// childMeta: null, |
||||||
|
dialogShow: false, |
||||||
|
confirmAction: null, |
||||||
|
confirmMessage: '', |
||||||
|
selectedChild: null, |
||||||
|
expandFormModal: false, |
||||||
|
isNewChild: false, |
||||||
|
localState: [] |
||||||
|
}), |
||||||
|
async mounted() { |
||||||
|
await this.loadChildMeta() |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async showChildListModal() { |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.childListModal = true; |
||||||
|
}, |
||||||
|
async deleteChild(child) { |
||||||
|
this.dialogShow = true; |
||||||
|
this.confirmMessage = |
||||||
|
'Do you want to delete the record?'; |
||||||
|
this.confirmAction = async act => { |
||||||
|
if (act === 'hideDialog') { |
||||||
|
this.dialogShow = false; |
||||||
|
} else { |
||||||
|
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
try { |
||||||
|
await this.childApi.delete(id) |
||||||
|
this.dialogShow = false; |
||||||
|
this.$emit('loadTableData') |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
this.$toast.error(e.message) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
async unlinkChild(child) { |
||||||
|
if (this.isNew) { |
||||||
|
this.localState.splice(this.localState.indexOf(child), 1) |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await this.loadChildMeta(); |
||||||
|
const column = this.childMeta.columns.find(c => c.cn === this.hm.cn); |
||||||
|
if (column.rqd) { |
||||||
|
this.$toast.info('Unlink is not possible, instead add to another record.').goAway(3000) |
||||||
|
return |
||||||
|
} |
||||||
|
const _cn = column._cn; |
||||||
|
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
await this.childApi.update(id, {[_cn]: null}, child) |
||||||
|
this.$emit('loadTableData') |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
// } |
||||||
|
// } |
||||||
|
}, |
||||||
|
async loadChildMeta() { |
||||||
|
// todo: optimize |
||||||
|
if (!this.childMeta) { |
||||||
|
|
||||||
|
await this.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias, |
||||||
|
tn: this.hm.tn |
||||||
|
}) |
||||||
|
// const childTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
// env: this.nodes.env, |
||||||
|
// dbAlias: this.nodes.dbAlias |
||||||
|
// }, 'tableXcModelGet', { |
||||||
|
// tn: this.hm.tn |
||||||
|
// }]); |
||||||
|
// this.childMeta = JSON.parse(childTableData.meta); |
||||||
|
// this.childQueryParams = JSON.parse(childTableData.query_params); |
||||||
|
} |
||||||
|
}, |
||||||
|
async showNewRecordModal() { |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.newRecordModal = true; |
||||||
|
}, |
||||||
|
async addChildToParent(child) { |
||||||
|
if (this.isNew) { |
||||||
|
this.localState.push(child); |
||||||
|
this.newRecordModal = false; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
const _cn = this.childForeignKey; |
||||||
|
this.newRecordModal = false; |
||||||
|
|
||||||
|
await this.childApi.update(id, { |
||||||
|
[_cn]: parseIfInteger(this.parentId) |
||||||
|
}, { |
||||||
|
[_cn]: child[this.childForeignKey] |
||||||
|
}); |
||||||
|
|
||||||
|
this.$emit('loadTableData') |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
}, |
||||||
|
async editChild(child) { |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.isNewChild = false; |
||||||
|
this.selectedChild = child; |
||||||
|
this.expandFormModal = true; |
||||||
|
setTimeout(() => { |
||||||
|
this.$refs.expandedForm && this.$refs.expandedForm.reload() |
||||||
|
}, 500) |
||||||
|
}, |
||||||
|
async insertAndAddNewChildRecord() { |
||||||
|
this.newRecordModal = false; |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.isNewChild = true; |
||||||
|
this.selectedChild = { |
||||||
|
[this.childForeignKey]: parseIfInteger(this.parentId) |
||||||
|
}; |
||||||
|
this.expandFormModal = true; |
||||||
|
setTimeout(() => { |
||||||
|
this.$refs.expandedForm && this.$refs.expandedForm.$set(this.$refs.expandedForm.changedColumns, this.childForeignKey, true) |
||||||
|
}, 500) |
||||||
|
}, |
||||||
|
getCellValue(cellObj) { |
||||||
|
if (cellObj) { |
||||||
|
if (this.parentMeta && this.childPrimaryCol) { |
||||||
|
return cellObj[this.childPrimaryCol] |
||||||
|
} |
||||||
|
return Object.values(cellObj)[1] |
||||||
|
} |
||||||
|
}, |
||||||
|
async saveLocalState(row) { |
||||||
|
let child; |
||||||
|
while (child = this.localState.pop()) { |
||||||
|
if (row) { |
||||||
|
// todo: use common method |
||||||
|
const pid = this.meta.columns.filter((c) => c.pk).map(c => row[c._cn]).join('___') |
||||||
|
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
const _cn = this.childForeignKey; |
||||||
|
await this.childApi.update(id, { |
||||||
|
[_cn]: parseIfInteger(pid) |
||||||
|
}, { |
||||||
|
[_cn]: child[this.childForeignKey] |
||||||
|
}); |
||||||
|
} else { |
||||||
|
await this.addChildToParent(child) |
||||||
|
} |
||||||
|
} |
||||||
|
this.$emit('newRecordsSaved'); |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
childMeta() { |
||||||
|
return this.$store.state.meta.metas[this.hm.tn] |
||||||
|
}, |
||||||
|
childApi() { |
||||||
|
return this.childMeta && this.childMeta._tn ? |
||||||
|
ApiFactory.create(this.$store.getters['project/GtrProjectType'], |
||||||
|
this.childMeta && this.childMeta._tn, this.childMeta && this.childMeta.columns, this, this.childMeta) : null; |
||||||
|
}, |
||||||
|
childPrimaryCol() { |
||||||
|
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {})._cn |
||||||
|
}, |
||||||
|
primaryCol() { |
||||||
|
return this.meta && (this.meta.columns.find(c => c.pv) || {})._cn |
||||||
|
}, |
||||||
|
childPrimaryKey() { |
||||||
|
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn |
||||||
|
}, |
||||||
|
childForeignKey() { |
||||||
|
return this.childMeta && (this.childMeta.columns.find(c => c.cn === this.hm.cn) || {})._cn |
||||||
|
}, |
||||||
|
disabledChildColumns() { |
||||||
|
return {[this.childForeignKey]: true} |
||||||
|
}, |
||||||
|
// todo: |
||||||
|
form() { |
||||||
|
return this.selectedChild ? () => import("@/components/project/spreadsheet/components/expandedForm") : 'span'; |
||||||
|
}, |
||||||
|
childAvailableColumns() { |
||||||
|
const hideCols = ['created_at', 'updated_at']; |
||||||
|
if (!this.childMeta) return []; |
||||||
|
|
||||||
|
const columns = []; |
||||||
|
if (this.childMeta.columns) { |
||||||
|
columns.push(...this.childMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) && !((this.childMeta.v || []).some(v => v.bt && v.bt.cn === c.cn)))) |
||||||
|
} |
||||||
|
if (this.childMeta.v) { |
||||||
|
columns.push(...this.childMeta.v.map(v => ({...v, virtual: 1}))); |
||||||
|
} |
||||||
|
return columns; |
||||||
|
}, |
||||||
|
childQueryParams() { |
||||||
|
if (!this.childMeta) return {} |
||||||
|
return { |
||||||
|
childs: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '', |
||||||
|
parents: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.bt).map(({bt}) => bt.rtn).join()) || '', |
||||||
|
many: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.mm).map(({mm}) => mm.rtn).join()) || '' |
||||||
|
} |
||||||
|
}, |
||||||
|
parentId() { |
||||||
|
return this.meta && this.meta.columns ? this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___') : ''; |
||||||
|
} |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
isNew(n, o) { |
||||||
|
if (!n && o) { |
||||||
|
this.saveLocalState(); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
created() { |
||||||
|
this.loadChildMeta(); |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.items-container { |
||||||
|
overflow-x: visible; |
||||||
|
max-height: min(500px, 60vh); |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-value { |
||||||
|
.primary-key { |
||||||
|
display: none; |
||||||
|
margin-left: .5em; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .primary-key { |
||||||
|
display: inline; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.child-card { |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
box-shadow: 0 0 .2em var(--v-textColor-lighten5) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.hm-items { |
||||||
|
//min-width: 200px; |
||||||
|
//max-width: 400px; |
||||||
|
flex-wrap: wrap; |
||||||
|
row-gap: 3px; |
||||||
|
gap: 3px; |
||||||
|
margin: 3px auto; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep { |
||||||
|
.unlink-icon { |
||||||
|
padding: 0px 1px 2px 1px; |
||||||
|
margin-top: 2px; |
||||||
|
margin-right: -2px; |
||||||
|
} |
||||||
|
|
||||||
|
.search-field { |
||||||
|
input { |
||||||
|
max-height: 28px !important; |
||||||
|
} |
||||||
|
|
||||||
|
.v-input__slot { |
||||||
|
min-height: auto !important; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.chips-wrapper { |
||||||
|
.chips { |
||||||
|
max-width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
&.active { |
||||||
|
.chips { |
||||||
|
max-width: calc(100% - 44px); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,505 @@ |
|||||||
|
<template> |
||||||
|
<div class="d-flex d-100 chips-wrapper" :class="{active}"> |
||||||
|
<template v-if="!isForm"> |
||||||
|
<div class="chips d-flex align-center img-container flex-grow-1 hm-items"> |
||||||
|
<template v-if="(value || localState)"> |
||||||
|
<item-chip v-for="(v,j) in (value || localState)" |
||||||
|
:active="active" |
||||||
|
:item="v" |
||||||
|
:value="getCellValue(v)" |
||||||
|
:key="j" |
||||||
|
@edit="editChild" |
||||||
|
@unlink="unlinkChild" |
||||||
|
></item-chip> |
||||||
|
|
||||||
|
</template> |
||||||
|
<span v-if="value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal">more...</span> |
||||||
|
</div> |
||||||
|
<div class="actions 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 x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
|
||||||
|
<list-items |
||||||
|
v-if="newRecordModal" |
||||||
|
:hm="true" |
||||||
|
:size="10" |
||||||
|
:meta="childMeta" |
||||||
|
:primary-col="childPrimaryCol" |
||||||
|
:primary-key="childPrimaryKey" |
||||||
|
v-model="newRecordModal" |
||||||
|
:api="api" |
||||||
|
:mm="mm" |
||||||
|
:parent-id="row && row[parentPrimaryKey]" |
||||||
|
@add-new-record="insertAndAddNewChildRecord" |
||||||
|
@add="addChildToParent" |
||||||
|
:query-params="childQueryParams"/> |
||||||
|
|
||||||
|
<list-child-items |
||||||
|
:is="isForm ? 'list-child-items' : 'list-child-items-modal'" |
||||||
|
:isForm="isForm" |
||||||
|
ref="childList" |
||||||
|
v-if="childMeta && assocMeta && (isForm || childListModal)" |
||||||
|
v-model="childListModal" |
||||||
|
:is-new="isNew" |
||||||
|
:size="10" |
||||||
|
:meta="childMeta" |
||||||
|
:parent-meta="meta" |
||||||
|
:primary-col="childPrimaryCol" |
||||||
|
:primary-key="childPrimaryKey" |
||||||
|
:api="childApi" |
||||||
|
:mm="mm" |
||||||
|
:parent-id="row && row[parentPrimaryKey]" |
||||||
|
:query-params="{...childQueryParams, conditionGraph }" |
||||||
|
:local-state="localState" |
||||||
|
@new-record="showNewRecordModal" |
||||||
|
@edit="editChild" |
||||||
|
@unlink="unlinkChild" |
||||||
|
/> |
||||||
|
<dlg-label-submit-cancel |
||||||
|
type="primary" |
||||||
|
v-if="dialogShow" |
||||||
|
:actionsMtd="confirmAction" |
||||||
|
:dialogShow="dialogShow" |
||||||
|
:heading="confirmMessage" |
||||||
|
> |
||||||
|
</dlg-label-submit-cancel> |
||||||
|
|
||||||
|
<!-- todo : move to listitem component --> |
||||||
|
<v-dialog |
||||||
|
:overlay-opacity="0.8" |
||||||
|
v-if="selectedChild" |
||||||
|
width="1000px" |
||||||
|
max-width="100%" |
||||||
|
class=" mx-auto" |
||||||
|
v-model="expandFormModal"> |
||||||
|
<component |
||||||
|
v-if="selectedChild" |
||||||
|
:is="form" |
||||||
|
:db-alias="nodes.dbAlias" |
||||||
|
:has-many="childMeta.hasMany" |
||||||
|
:belongs-to="childMeta.belongsTo" |
||||||
|
:table="childMeta.tn" |
||||||
|
:old-row="{...selectedChild}" |
||||||
|
:meta="childMeta" |
||||||
|
:sql-ui="sqlUi" |
||||||
|
:primary-value-column="childPrimaryCol" |
||||||
|
:api="childApi" |
||||||
|
:available-columns="childAvailableColumns" |
||||||
|
icon-color="warning" |
||||||
|
:nodes="nodes" |
||||||
|
:query-params="childQueryParams" |
||||||
|
ref="expandedForm" |
||||||
|
:is-new.sync="isNewChild" |
||||||
|
v-model="selectedChild" |
||||||
|
:breadcrumbs="breadcrumbs" |
||||||
|
@cancel="selectedChild = null" |
||||||
|
@input="onChildSave" |
||||||
|
></component> |
||||||
|
|
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import ApiFactory from "@/components/project/spreadsheet/apis/apiFactory"; |
||||||
|
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel"; |
||||||
|
import ListItems from "@/components/project/spreadsheet/components/virtualCell/components/listItems"; |
||||||
|
import ItemChip from "@/components/project/spreadsheet/components/virtualCell/components/item-chip"; |
||||||
|
import ListChildItems from "@/components/project/spreadsheet/components/virtualCell/components/listChildItems"; |
||||||
|
import listChildItemsModal |
||||||
|
from "@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal"; |
||||||
|
import {parseIfInteger} from "@/helpers"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "many-to-many-cell", |
||||||
|
components: {ListChildItems, ItemChip, ListItems, DlgLabelSubmitCancel, listChildItemsModal}, |
||||||
|
props: { |
||||||
|
breadcrumbs: { |
||||||
|
type: Array, |
||||||
|
default() { |
||||||
|
return []; |
||||||
|
} |
||||||
|
}, |
||||||
|
value: [Object, Array], |
||||||
|
meta: [Object], |
||||||
|
mm: Object, |
||||||
|
nodes: [Object], |
||||||
|
row: [Object], |
||||||
|
api: [Object, Function], |
||||||
|
sqlUi: [Object, Function], |
||||||
|
active: Boolean, |
||||||
|
isNew: Boolean, |
||||||
|
isForm: Boolean, |
||||||
|
}, |
||||||
|
data: () => ({ |
||||||
|
isNewChild: false, |
||||||
|
newRecordModal: false, |
||||||
|
childListModal: false, |
||||||
|
// childMeta: null, |
||||||
|
// assocMeta: null, |
||||||
|
childList: null, |
||||||
|
dialogShow: false, |
||||||
|
confirmAction: null, |
||||||
|
confirmMessage: '', |
||||||
|
selectedChild: null, |
||||||
|
expandFormModal: false, |
||||||
|
localState: [] |
||||||
|
}), |
||||||
|
async mounted() { |
||||||
|
if (this.isForm) { |
||||||
|
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()]); |
||||||
|
} |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
async onChildSave(child) { |
||||||
|
if (this.isNewChild) { |
||||||
|
this.isNewChild = false; |
||||||
|
await this.addChildToParent(child); |
||||||
|
} else { |
||||||
|
this.$emit('loadTableData') |
||||||
|
} |
||||||
|
}, |
||||||
|
async showChildListModal() { |
||||||
|
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()]); |
||||||
|
this.childListModal = true; |
||||||
|
}, async unlinkChild(child) { |
||||||
|
if (this.isNew) { |
||||||
|
this.localState.splice(this.localState.indexOf(child), 1) |
||||||
|
return; |
||||||
|
} |
||||||
|
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()]); |
||||||
|
|
||||||
|
const _pcn = this.meta.columns.find(c => c.cn === this.mm.cn)._cn; |
||||||
|
const _ccn = this.childMeta.columns.find(c => c.cn === this.mm.rcn)._cn; |
||||||
|
|
||||||
|
const apcn = this.assocMeta.columns.find(c => c.cn === this.mm.vcn).cn; |
||||||
|
const accn = this.assocMeta.columns.find(c => c.cn === this.mm.vrcn).cn; |
||||||
|
|
||||||
|
const id = this.assocMeta.columns.filter((c) => c.cn === apcn || c.cn === accn).map(c => c.cn === apcn ? this.row[_pcn] : child[_ccn]).join('___'); |
||||||
|
await this.assocApi.delete(id) |
||||||
|
this.$emit('loadTableData') |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
}, |
||||||
|
async removeChild(child) { |
||||||
|
this.dialogShow = true; |
||||||
|
this.confirmMessage = |
||||||
|
'Do you want to delete the record?'; |
||||||
|
this.confirmAction = async act => { |
||||||
|
if (act === 'hideDialog') { |
||||||
|
this.dialogShow = false; |
||||||
|
} else { |
||||||
|
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); |
||||||
|
await this.childApi.delete(id) |
||||||
|
this.dialogShow = false; |
||||||
|
this.$emit('loadTableData') |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
async loadChildMeta() { |
||||||
|
// todo: optimize |
||||||
|
if (!this.childMeta) { |
||||||
|
await this.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias, |
||||||
|
tn: this.mm.rtn |
||||||
|
}) |
||||||
|
// const parentTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
// env: this.nodes.env, |
||||||
|
// dbAlias: this.nodes.dbAlias |
||||||
|
// }, 'tableXcModelGet', { |
||||||
|
// tn: this.mm.rtn |
||||||
|
// }]); |
||||||
|
// this.childMeta = JSON.parse(parentTableData.meta) |
||||||
|
} |
||||||
|
}, |
||||||
|
async loadAssociateTableMeta() { |
||||||
|
// todo: optimize |
||||||
|
if (!this.assocMeta) { |
||||||
|
await this.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias, |
||||||
|
tn: this.mm.vtn |
||||||
|
}) |
||||||
|
// const assocTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
||||||
|
// env: this.nodes.env, |
||||||
|
// dbAlias: this.nodes.dbAlias |
||||||
|
// }, 'tableXcModelGet', { |
||||||
|
// tn: this.mm.vtn |
||||||
|
// }]); |
||||||
|
// this.assocMeta = JSON.parse(assocTableData.meta) |
||||||
|
} |
||||||
|
}, |
||||||
|
async showNewRecordModal() { |
||||||
|
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()]); |
||||||
|
this.newRecordModal = true; |
||||||
|
// this.list = await this.c hildApi.paginatedList({}) |
||||||
|
}, |
||||||
|
async addChildToParent(child) { |
||||||
|
if (this.isNew) { |
||||||
|
this.localState.push(child) |
||||||
|
this.newRecordModal = false; |
||||||
|
return |
||||||
|
} |
||||||
|
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 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; |
||||||
|
try { |
||||||
|
await this.assocApi.insert({ |
||||||
|
[vcidCol]: parseIfInteger(cid), |
||||||
|
[vpidCol]: parseIfInteger(pid) |
||||||
|
}); |
||||||
|
|
||||||
|
this.$emit('loadTableData') |
||||||
|
} catch (e) { |
||||||
|
// todo: handle |
||||||
|
console.log(e) |
||||||
|
} |
||||||
|
this.newRecordModal = false; |
||||||
|
if ((this.childListModal || this.isForm) && this.$refs.childList) { |
||||||
|
this.$refs.childList.loadData(); |
||||||
|
} |
||||||
|
|
||||||
|
}, |
||||||
|
|
||||||
|
|
||||||
|
async insertAndAddNewChildRecord() { |
||||||
|
this.newRecordModal = false; |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.isNewChild = true; |
||||||
|
this.selectedChild = { |
||||||
|
[this.childForeignKey]: this.parentId |
||||||
|
}; |
||||||
|
this.expandFormModal = true; |
||||||
|
setTimeout(() => { |
||||||
|
this.$refs.expandedForm && this.$refs.expandedForm.$set(this.$refs.expandedForm.changedColumns, this.childForeignKey, true) |
||||||
|
}, 500) |
||||||
|
}, async editChild(child) { |
||||||
|
await this.loadChildMeta(); |
||||||
|
this.isNewChild = false; |
||||||
|
this.selectedChild = child; |
||||||
|
this.expandFormModal = true; |
||||||
|
setTimeout(() => { |
||||||
|
this.$refs.expandedForm && this.$refs.expandedForm.reload() |
||||||
|
}, 500) |
||||||
|
}, |
||||||
|
async saveLocalState(row) { |
||||||
|
let child; |
||||||
|
while (child = this.localState.pop()) { |
||||||
|
if (row) { |
||||||
|
// todo: use common method |
||||||
|
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 => row[c._cn]).join('___'); |
||||||
|
|
||||||
|
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; |
||||||
|
await this.assocApi.insert({ |
||||||
|
[vcidCol]: parseIfInteger(cid), |
||||||
|
[vpidCol]: parseIfInteger(pid) |
||||||
|
}); |
||||||
|
} else { |
||||||
|
await this.addChildToParent(child) |
||||||
|
} |
||||||
|
} |
||||||
|
this.$emit('newRecordsSaved'); |
||||||
|
} |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
getCellValue() { |
||||||
|
return cellObj => { |
||||||
|
if (cellObj) { |
||||||
|
if (this.childPrimaryCol) { |
||||||
|
return cellObj[this.childPrimaryCol] |
||||||
|
} |
||||||
|
return Object.values(cellObj)[1] |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
childMeta() { |
||||||
|
return this.$store.state.meta.metas[this.mm.rtn] |
||||||
|
}, |
||||||
|
assocMeta() { |
||||||
|
return this.$store.state.meta.metas[this.mm.vtn] |
||||||
|
}, |
||||||
|
childApi() { |
||||||
|
return this.childMeta && this.childMeta._tn ? |
||||||
|
ApiFactory.create( |
||||||
|
this.$store.getters['project/GtrProjectType'], |
||||||
|
this.childMeta._tn, |
||||||
|
this.childMeta.columns, |
||||||
|
this, |
||||||
|
this.childMeta |
||||||
|
) : null; |
||||||
|
}, |
||||||
|
assocApi() { |
||||||
|
return this.assocMeta && this.assocMeta._tn ? |
||||||
|
ApiFactory.create( |
||||||
|
this.$store.getters['project/GtrProjectType'], |
||||||
|
this.assocMeta._tn, |
||||||
|
this.assocMeta.columns, |
||||||
|
this, |
||||||
|
this.assocMeta |
||||||
|
) : null; |
||||||
|
}, |
||||||
|
childPrimaryCol() { |
||||||
|
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {})._cn |
||||||
|
}, |
||||||
|
childPrimaryKey() { |
||||||
|
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn |
||||||
|
}, |
||||||
|
parentPrimaryKey() { |
||||||
|
return this.meta && (this.meta.columns.find(c => c.pk) || {})._cn |
||||||
|
}, |
||||||
|
childQueryParams() { |
||||||
|
if (!this.childMeta) return {} |
||||||
|
return { |
||||||
|
childs: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '', |
||||||
|
parents: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.bt).map(({bt}) => bt.rtn).join()) || '', |
||||||
|
many: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.mm).map(({mm}) => mm.rtn).join()) || '' |
||||||
|
} |
||||||
|
}, |
||||||
|
conditionGraph() { |
||||||
|
if (!this.childMeta || !this.assocMeta) return null; |
||||||
|
return { |
||||||
|
[this.assocMeta.tn]: { |
||||||
|
"relationType": "hm", |
||||||
|
[this.assocMeta.columns.find(c => c.cn === this.mm.vcn).cn]: { |
||||||
|
"eq": this.row[this.parentPrimaryKey] |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
childAvailableColumns() { |
||||||
|
const hideCols = ['created_at', 'updated_at']; |
||||||
|
if (!this.childMeta) return []; |
||||||
|
|
||||||
|
const columns = []; |
||||||
|
if (this.childMeta.columns) { |
||||||
|
columns.push(...this.childMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) && !((this.childMeta.v || []).some(v => v.bt && v.bt.cn === c.cn)))) |
||||||
|
} |
||||||
|
if (this.childMeta.v) { |
||||||
|
columns.push(...this.childMeta.v.map(v => ({...v, virtual: 1}))); |
||||||
|
} |
||||||
|
return columns; |
||||||
|
}, |
||||||
|
// todo: |
||||||
|
form() { |
||||||
|
return this.selectedChild ? () => import("@/components/project/spreadsheet/components/expandedForm") : 'span'; |
||||||
|
}, |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
async isNew(n, o) { |
||||||
|
if (!n && o) { |
||||||
|
await this.saveLocalState(); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
created() { |
||||||
|
this.loadChildMeta(); |
||||||
|
this.loadAssociateTableMeta() |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.items-container { |
||||||
|
overflow-x: visible; |
||||||
|
max-height: min(500px, 60vh); |
||||||
|
overflow-y: auto; |
||||||
|
} |
||||||
|
|
||||||
|
.primary-value { |
||||||
|
.primary-key { |
||||||
|
display: none; |
||||||
|
margin-left: .5em; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .primary-key { |
||||||
|
display: inline; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
.child-list-modal { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.remove-child-icon { |
||||||
|
position: absolute; |
||||||
|
right: 10px; |
||||||
|
top: 10px; |
||||||
|
bottom: 10px; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
|
||||||
|
&:hover .remove-child-icon { |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.child-card { |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
&:hover { |
||||||
|
box-shadow: 0 0 .2em var(--v-textColor-lighten5) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.hm-items { |
||||||
|
//min-width: 200px; |
||||||
|
//max-width: 400px; |
||||||
|
flex-wrap: wrap; |
||||||
|
row-gap: 3px; |
||||||
|
gap: 3px; |
||||||
|
margin: 3px auto; |
||||||
|
} |
||||||
|
|
||||||
|
.chips-wrapper { |
||||||
|
.chips { |
||||||
|
max-width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
&.active { |
||||||
|
.chips { |
||||||
|
max-width: calc(100% - 44px); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -0,0 +1,181 @@ |
|||||||
|
<template> |
||||||
|
<div class="d-flex align-center"> |
||||||
|
|
||||||
|
<v-tooltip bottom> |
||||||
|
<template #activator="{on}"> |
||||||
|
<v-icon v-if="column.hm" color="warning" x-small class="mr-1" v-on="on">mdi-table-arrow-right</v-icon> |
||||||
|
<v-icon v-else-if="column.bt" color="info" x-small class="mr-1" v-on="on">mdi-table-arrow-left</v-icon> |
||||||
|
<v-icon v-else-if="column.mm" color="pink" x-small class="mr-1" v-on="on">mdi-table-network</v-icon> |
||||||
|
|
||||||
|
<span v-on="on" class="name flex-grow-1" :title="column._cn">{{ column._cn }}</span> |
||||||
|
|
||||||
|
<span v-if="column.rqd" v-on="on" class="error--text text--lighten-1"> *</span> |
||||||
|
</template> |
||||||
|
<span class="caption" v-html="tooltipMsg"></span> |
||||||
|
</v-tooltip> |
||||||
|
<v-spacer> |
||||||
|
</v-spacer> |
||||||
|
|
||||||
|
<v-menu offset-y open-on-hover left> |
||||||
|
<template v-slot:activator="{on}"> |
||||||
|
<v-icon v-if="!isForm" v-on="on" small>mdi-menu-down</v-icon> |
||||||
|
</template> |
||||||
|
<v-list dense> |
||||||
|
<v-list-item dense @click="editColumnMenu = true"> |
||||||
|
<x-icon small class="mr-1" color="primary">mdi-pencil</x-icon> |
||||||
|
<span class="caption">Edit</span> |
||||||
|
</v-list-item> |
||||||
|
<!-- <v-list-item dense @click="setAsPrimaryValue"> |
||||||
|
<x-icon small class="mr-1" color="primary">mdi-key-star</x-icon> |
||||||
|
<v-tooltip bottom> |
||||||
|
<template v-slot:activator="{on}"> |
||||||
|
<span class="caption" v-on="on">Set as Primary value</span> |
||||||
|
</template> |
||||||
|
<span class="caption font-weight-bold">Primary value will be shown in place of primary key</span> |
||||||
|
</v-tooltip> |
||||||
|
</v-list-item> --> |
||||||
|
<v-list-item @click="columnDeleteDialog = true"> |
||||||
|
<x-icon small class="mr-1" color="error">mdi-delete-outline</x-icon> |
||||||
|
<span class="caption">Delete</span> |
||||||
|
</v-list-item> |
||||||
|
</v-list> |
||||||
|
</v-menu> |
||||||
|
|
||||||
|
<v-dialog v-model="columnDeleteDialog" max-width="500" |
||||||
|
persistent> |
||||||
|
<v-card> |
||||||
|
<v-card-title class="grey darken-2 subheading white--text">Confirm</v-card-title> |
||||||
|
<v-divider></v-divider> |
||||||
|
<v-card-text class="mt-4 title">Do you want to delete <span class="font-weight-bold">'{{ |
||||||
|
column.cn |
||||||
|
}}'</span> column ? |
||||||
|
</v-card-text> |
||||||
|
<v-divider></v-divider> |
||||||
|
<v-card-actions class="d-flex pa-4"> |
||||||
|
<v-spacer></v-spacer> |
||||||
|
<v-btn small @click="columnDeleteDialog = false">Cancel</v-btn> |
||||||
|
<v-btn small color="error" @click="deleteColumn">Confirm</v-btn> |
||||||
|
</v-card-actions> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
|
||||||
|
|
||||||
|
<v-menu offset-y v-model="editColumnMenu" content-class="elevation-0" left> |
||||||
|
<template v-slot:activator="{on}"> |
||||||
|
<span v-on="on"></span> |
||||||
|
</template> |
||||||
|
<edit-virtual-column |
||||||
|
v-if="editColumnMenu" |
||||||
|
v-model="editColumnMenu" |
||||||
|
:nodes="nodes" |
||||||
|
:edit-column="true" |
||||||
|
:column="column" |
||||||
|
:meta="meta" |
||||||
|
v-on="$listeners" |
||||||
|
></edit-virtual-column> |
||||||
|
</v-menu> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<script> |
||||||
|
import EditVirtualColumn from "@/components/project/spreadsheet/components/editVirtualColumn"; |
||||||
|
|
||||||
|
export default { |
||||||
|
components: {EditVirtualColumn}, |
||||||
|
props: ['column', 'nodes', 'meta', 'isForm'], |
||||||
|
name: "virtualHeaderCell", |
||||||
|
data: () => ({ |
||||||
|
columnDeleteDialog: false, |
||||||
|
editColumnMenu: false |
||||||
|
}), |
||||||
|
computed: { |
||||||
|
type() { |
||||||
|
if (this.column.bt) return 'bt' |
||||||
|
if (this.column.hm) return 'hm' |
||||||
|
if (this.column.mm) return 'mm' |
||||||
|
}, |
||||||
|
childColumn() { |
||||||
|
if (this.column.bt) return this.column.bt.cn |
||||||
|
if (this.column.hm) return this.column.hm.cn |
||||||
|
if (this.column.mm) return this.column.mm.rcn |
||||||
|
}, |
||||||
|
childTable() { |
||||||
|
if (this.column.bt) return this.column.bt.tn |
||||||
|
if (this.column.hm) return this.column.hm.tn |
||||||
|
if (this.column.mm) return this.column.mm.rtn |
||||||
|
}, |
||||||
|
parentTable() { |
||||||
|
if (this.column.bt) return this.column.bt.rtn |
||||||
|
if (this.column.hm) return this.column.hm.rtn |
||||||
|
if (this.column.mm) return this.column.mm.tn |
||||||
|
}, |
||||||
|
parentColumn() { |
||||||
|
if (this.column.bt) return this.column.bt.rcn |
||||||
|
if (this.column.hm) return this.column.hm.rcn |
||||||
|
if (this.column.mm) return this.column.mm.cn |
||||||
|
}, |
||||||
|
tooltipMsg() { |
||||||
|
if (!this.column) return ''; |
||||||
|
if (this.column.hm) { |
||||||
|
return `'${this.column.hm._rtn}' has many '${this.column.hm._tn}'` |
||||||
|
} else if (this.column.mm) { |
||||||
|
return `'${this.column.mm._tn}' & '${this.column.mm._rtn}' have <br>many to many relation` |
||||||
|
} else if (this.column.bt) { |
||||||
|
return `'${this.column.bt._tn}' belongs to '${this.column.bt._rtn}'` |
||||||
|
} |
||||||
|
} |
||||||
|
}, methods: { |
||||||
|
async deleteColumn() { |
||||||
|
try { |
||||||
|
const column = {...this.column, cno: this.column.cn}; |
||||||
|
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [{ |
||||||
|
env: this.nodes.env, |
||||||
|
dbAlias: this.nodes.dbAlias |
||||||
|
}, "xcRelationColumnDelete", { |
||||||
|
type: this.type, |
||||||
|
childColumn: this.childColumn, |
||||||
|
childTable: this.childTable, |
||||||
|
parentTable: this.parentTable, |
||||||
|
parentColumn: this.parentColumn, |
||||||
|
assocTable: this.column.mm && this.column.mm.vtn |
||||||
|
}]); |
||||||
|
this.$emit('saved'); |
||||||
|
this.columnDeleteDialog = false; |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.name { |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
</style> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
--> |
@ -1,203 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-container fluid class="wrapper"> |
|
||||||
<v-row> |
|
||||||
<v-col cols="6"> |
|
||||||
<v-autocomplete |
|
||||||
outlined |
|
||||||
class="caption" |
|
||||||
hide-details |
|
||||||
:loading="isRefTablesLoading" |
|
||||||
label="Reference Table" |
|
||||||
:full-width="false" |
|
||||||
v-model="relation.parentTable" |
|
||||||
:items="refTables" |
|
||||||
item-text="_tn" |
|
||||||
item-value="tn" |
|
||||||
required |
|
||||||
dense |
|
||||||
@change="loadColumnList" |
|
||||||
></v-autocomplete> |
|
||||||
</v-col |
|
||||||
> |
|
||||||
<v-col cols="6"> |
|
||||||
<v-autocomplete |
|
||||||
outlined |
|
||||||
class="caption" |
|
||||||
hide-details |
|
||||||
:loading="isRefColumnsLoading" |
|
||||||
label="Reference Column" |
|
||||||
:full-width="false" |
|
||||||
v-model="relation.parentColumn" |
|
||||||
:items="refColumns" |
|
||||||
item-text="_cn" |
|
||||||
item-value="cn" |
|
||||||
required |
|
||||||
dense |
|
||||||
ref="parentColumnRef" |
|
||||||
@change="onColumnSelect" |
|
||||||
></v-autocomplete> |
|
||||||
</v-col |
|
||||||
> |
|
||||||
</v-row> |
|
||||||
|
|
||||||
<v-row> |
|
||||||
<v-col cols="6"> |
|
||||||
<v-autocomplete |
|
||||||
outlined |
|
||||||
class="caption" |
|
||||||
hide-details |
|
||||||
label="On Update" |
|
||||||
:full-width="false" |
|
||||||
v-model="relation.onUpdate" |
|
||||||
:items="onUpdateDeleteOptions" |
|
||||||
required |
|
||||||
dense |
|
||||||
:disabled="relation.type !== 'real'" |
|
||||||
></v-autocomplete> |
|
||||||
</v-col> |
|
||||||
<v-col cols="6"> |
|
||||||
<v-autocomplete |
|
||||||
outlined |
|
||||||
class="caption" |
|
||||||
hide-details |
|
||||||
label="On Delete" |
|
||||||
:full-width="false" |
|
||||||
v-model="relation.onDelete" |
|
||||||
:items="onUpdateDeleteOptions" |
|
||||||
required |
|
||||||
dense |
|
||||||
:disabled="relation.type !== 'real'" |
|
||||||
></v-autocomplete> |
|
||||||
</v-col> |
|
||||||
</v-row> |
|
||||||
|
|
||||||
|
|
||||||
<v-row> |
|
||||||
|
|
||||||
<v-col> |
|
||||||
<v-checkbox |
|
||||||
false-value="real" |
|
||||||
true-value="virtual" |
|
||||||
label="Virtual Relation" |
|
||||||
:full-width="false" |
|
||||||
v-model="relation.type" |
|
||||||
required |
|
||||||
class="mt-0" |
|
||||||
dense |
|
||||||
></v-checkbox> |
|
||||||
</v-col> |
|
||||||
</v-row> |
|
||||||
|
|
||||||
</v-container> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "linked-to-another-options", |
|
||||||
props: ['nodes', 'column'], |
|
||||||
data: () => ({ |
|
||||||
refTables: [], |
|
||||||
refColumns: [], |
|
||||||
relation: {}, |
|
||||||
isRefTablesLoading: false, |
|
||||||
isRefColumnsLoading: false, |
|
||||||
onUpdateDeleteOptions: [ |
|
||||||
"NO ACTION", |
|
||||||
"CASCADE", |
|
||||||
"RESTRICT", |
|
||||||
"SET NULL", |
|
||||||
"SET DEFAULT" |
|
||||||
], |
|
||||||
}), |
|
||||||
async created() { |
|
||||||
await this.loadTablesList(); |
|
||||||
this.relation = { |
|
||||||
childColumn: this.column.cn, |
|
||||||
childTable: this.nodes.tn, |
|
||||||
parentTable: this.column.rtn || "", |
|
||||||
parentColumn: this.column.rcn || "", |
|
||||||
onDelete: "CASCADE", |
|
||||||
onUpdate: "CASCADE", |
|
||||||
updateRelation: this.column.rtn ? true : false, |
|
||||||
type: 'real' |
|
||||||
} |
|
||||||
}, |
|
||||||
methods: { |
|
||||||
async loadColumnList() { |
|
||||||
if (!this.relation.parentTable) return; |
|
||||||
this.isRefColumnsLoading = true; |
|
||||||
|
|
||||||
|
|
||||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
|
||||||
env: this.nodes.env, |
|
||||||
dbAlias: this.nodes.dbAlias |
|
||||||
}, 'columnList', {tn: this.relation.parentTable}]) |
|
||||||
|
|
||||||
|
|
||||||
const columns = result.data.list; |
|
||||||
this.refColumns = JSON.parse(JSON.stringify(columns)); |
|
||||||
|
|
||||||
if (this.relation.updateRelation && !this.relationColumnChanged) { |
|
||||||
//only first time when editing add defaault value to this field |
|
||||||
this.relation.parentColumn = this.column.rcn; |
|
||||||
this.relationColumnChanged = true; |
|
||||||
} else { |
|
||||||
//find pk column and assign to parentColumn |
|
||||||
const pkKeyColumns = this.refColumns.filter(el => el.pk); |
|
||||||
this.relation.parentColumn = (pkKeyColumns[0] || {}).cn || ""; |
|
||||||
} |
|
||||||
this.onColumnSelect(); |
|
||||||
|
|
||||||
this.isRefColumnsLoading = false; |
|
||||||
}, |
|
||||||
async loadTablesList() { |
|
||||||
this.isRefTablesLoading = true; |
|
||||||
|
|
||||||
|
|
||||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ |
|
||||||
env: this.nodes.env, |
|
||||||
dbAlias: this.nodes.dbAlias |
|
||||||
}, 'tableList']); |
|
||||||
|
|
||||||
|
|
||||||
this.refTables = result.data.list.map(({tn,_tn}) => ({tn,_tn})) |
|
||||||
this.isRefTablesLoading = false; |
|
||||||
}, |
|
||||||
async saveRelation() { |
|
||||||
try { |
|
||||||
let result = await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [ |
|
||||||
{ |
|
||||||
env: this.nodes.env, |
|
||||||
dbAlias: this.nodes.dbAlias |
|
||||||
}, |
|
||||||
this.relation.type === 'real' ? "relationCreate" : 'xcVirtualRelationCreate', |
|
||||||
this.relation |
|
||||||
]); |
|
||||||
} catch (e) { |
|
||||||
throw e; |
|
||||||
} |
|
||||||
}, |
|
||||||
onColumnSelect() { |
|
||||||
const col = this.refColumns.find(c => this.relation.parentColumn === c.cn); |
|
||||||
this.$emit('onColumnSelect', col) |
|
||||||
} |
|
||||||
}, |
|
||||||
watch: { |
|
||||||
'column.cn': (c) => { |
|
||||||
this.$set(this.relation, 'childColumn', c); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
|
|
||||||
.wrapper { |
|
||||||
border: solid 2px #7f828b33; |
|
||||||
border-radius: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
/deep/ .v-input__append-inner { |
|
||||||
margin-top: 4px !important; |
|
||||||
} |
|
||||||
</style> |
|
@ -1,101 +0,0 @@ |
|||||||
<template> |
|
||||||
|
|
||||||
<div class="d-flex align-center"> |
|
||||||
|
|
||||||
<div> |
|
||||||
<div class="item" v-for="(val,i) of enumValues" :key="val"> |
|
||||||
<input type="radio" :id="`key-radio-${val}`" class="orange--text" v-model="localState" :value="val"> |
|
||||||
<label class="py-1 px-3 d-inline-block my-1 label" :for="`key-radio-${val}`" |
|
||||||
:style="{ |
|
||||||
background:colors[i % colors.length ] |
|
||||||
}" |
|
||||||
>{{ val }}</label> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import {enumColor as colors} from "@/components/project/spreadsheet/helpers/colors"; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "enum-radio-editable-cell", |
|
||||||
props: { |
|
||||||
value: String, |
|
||||||
column: Object |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
// this.$el.focus(); |
|
||||||
// let event; |
|
||||||
// event = document.createEvent('MouseEvents'); |
|
||||||
// event.initMouseEvent('mousedown', true, true, window); |
|
||||||
// this.$el.dispatchEvent(event); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
colors() { |
|
||||||
return this.$store.state.windows.darkTheme ? colors.dark : colors.light; |
|
||||||
}, |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return this.value |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', val); |
|
||||||
this.$emit('update'); |
|
||||||
} |
|
||||||
}, |
|
||||||
enumValues() { |
|
||||||
if (this.column && this.column.dtxp) { |
|
||||||
return this.column.dtxp.split(',').map(v => v.replace(/^'|'$/g, '')) |
|
||||||
} |
|
||||||
return []; |
|
||||||
}, |
|
||||||
parentListeners() { |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if (this.$listeners.blur) { |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if (this.$listeners.focus) { |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
|
|
||||||
.label { |
|
||||||
border-radius: 25px; |
|
||||||
} |
|
||||||
|
|
||||||
.item { |
|
||||||
white-space: nowrap; |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,69 +0,0 @@ |
|||||||
<template> |
|
||||||
<input v-on="parentListeners" v-model="localState" type="number"> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: "integerCell", |
|
||||||
props: { |
|
||||||
value: [String, Number] |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
this.$el.focus(); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
localState: { |
|
||||||
get() { |
|
||||||
return this.value |
|
||||||
}, |
|
||||||
set(val) { |
|
||||||
this.$emit('input', parseInt(val, 10)); |
|
||||||
} |
|
||||||
}, |
|
||||||
parentListeners(){ |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if(this.$listeners.blur){ |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if(this.$listeners.focus){ |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
input { |
|
||||||
width: 100%; |
|
||||||
height: 100%; |
|
||||||
color: var(--v-textColor-base); |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -1,97 +0,0 @@ |
|||||||
<template> |
|
||||||
<div @keydown.stop.enter class="cell-container"> |
|
||||||
<div class="d-flex ma-1" v-if="!isForm"> |
|
||||||
<v-spacer> |
|
||||||
</v-spacer> |
|
||||||
<v-btn outlined x-small class="mr-1" @click="$emit('cancel')">Cancel</v-btn> |
|
||||||
<v-btn x-small color="primary" @click="save">Save</v-btn> |
|
||||||
</div> |
|
||||||
<monaco-json-object-editor v-model="localState" |
|
||||||
style="width: 300px;min-height: 200px;min-width:100%"></monaco-json-object-editor> |
|
||||||
|
|
||||||
|
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import MonacoJsonEditor from "@/components/monaco/MonacoJsonEditor"; |
|
||||||
import MonacoJsonObjectEditor from "@/components/monaco/MonacoJsonObjectEditor"; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: "json-cell", |
|
||||||
components: {MonacoJsonObjectEditor, MonacoJsonEditor}, |
|
||||||
props: { |
|
||||||
value: String, |
|
||||||
isForm:Boolean |
|
||||||
}, |
|
||||||
data: () => ({ |
|
||||||
localState: '' |
|
||||||
}), |
|
||||||
created() { |
|
||||||
this.localState = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; |
|
||||||
}, |
|
||||||
mounted() { |
|
||||||
}, watch: { |
|
||||||
value(val) { |
|
||||||
this.localState = typeof val === 'string' ? JSON.parse(val) : val; |
|
||||||
}, |
|
||||||
localState(val){ |
|
||||||
if(this.isForm){ |
|
||||||
this.$emit('input', JSON.stringify(val)) |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
methods: { |
|
||||||
save() { |
|
||||||
this.$emit('input', JSON.stringify(this.localState)) |
|
||||||
} |
|
||||||
}, |
|
||||||
computed:{ |
|
||||||
|
|
||||||
parentListeners(){ |
|
||||||
const $listeners = {}; |
|
||||||
|
|
||||||
if(this.$listeners.blur){ |
|
||||||
$listeners.blur = this.$listeners.blur; |
|
||||||
} |
|
||||||
if(this.$listeners.focus){ |
|
||||||
$listeners.focus = this.$listeners.focus; |
|
||||||
} |
|
||||||
|
|
||||||
return $listeners; |
|
||||||
}, |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped> |
|
||||||
.cell-container { |
|
||||||
/*margin: 0 -5px;*/ |
|
||||||
/*position: relative;*/ |
|
||||||
width: 100% |
|
||||||
} |
|
||||||
</style> |
|
||||||
<!-- |
|
||||||
/** |
|
||||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
|
||||||
* |
|
||||||
* @author Naveen MR <oof1lab@gmail.com> |
|
||||||
* @author Pranav C Balan <pranavxc@gmail.com> |
|
||||||
* |
|
||||||
* @license GNU AGPL version 3 or any later version |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU Affero General Public License as |
|
||||||
* published by the Free Software Foundation, either version 3 of the |
|
||||||
* License, or (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU Affero General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU Affero General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
* |
|
||||||
*/ |
|
||||||
--> |
|
@ -0,0 +1,73 @@ |
|||||||
|
export const state = () => ({ |
||||||
|
metas: {}, |
||||||
|
loading: {} |
||||||
|
}); |
||||||
|
|
||||||
|
export const mutations = { |
||||||
|
MutMeta(state, {key, value}) { |
||||||
|
state.metas = {...state.metas, [key]: value}; |
||||||
|
}, |
||||||
|
MutLoading(state, {key, value}) { |
||||||
|
state.loading = {...state.loading, [key]: value}; |
||||||
|
}, |
||||||
|
MutClear(state) { |
||||||
|
state.metas = {}; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
export const actions = { |
||||||
|
async ActLoadMeta({state, commit, dispatch}, {tn, env, dbAlias, force}) { |
||||||
|
if (!force && state.loading[tn]) { |
||||||
|
return await new Promise(resolve => { |
||||||
|
const unsubscribe = this.app.store.subscribe(s => { |
||||||
|
if (s.type === 'meta/MutLoading' && s.payload.key === tn && !s.payload.value) { |
||||||
|
unsubscribe(); |
||||||
|
resolve(state.metas[tn]) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
if (!force && state.metas[tn]) { |
||||||
|
return state.metas[tn]; |
||||||
|
} |
||||||
|
commit('MutLoading', { |
||||||
|
key: tn, |
||||||
|
value: true |
||||||
|
}) |
||||||
|
const model = await dispatch('sqlMgr/ActSqlOp', [{env, dbAlias}, 'tableXcModelGet', {tn}], {root: true}); |
||||||
|
const meta = JSON.parse(model.meta); |
||||||
|
commit('MutMeta', { |
||||||
|
key: tn, |
||||||
|
value: meta |
||||||
|
}) |
||||||
|
commit('MutLoading', { |
||||||
|
key: tn, |
||||||
|
value: undefined |
||||||
|
}) |
||||||
|
return force ? model : meta; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue