mirror of
https://github.com/nocodb/nocodb.git
synced 2026-04-30 15:26:48 +00:00
1
.gitignore
vendored
1
.gitignore
vendored
@@ -84,3 +84,4 @@ mongod
|
||||
#=========
|
||||
shared.json
|
||||
/scripts/Cypress/screenshots
|
||||
/scripts/exp/
|
||||
|
||||
42
Run.md
Normal file
42
Run.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Setup
|
||||
|
||||
#### Setting up dev environment
|
||||
|
||||
- Clone `nocodb/nocodb` GitHub repo and checkout to `feat/v2` branch
|
||||
```sh
|
||||
git clone https://github.com/nocodb/nc
|
||||
cd nocodb
|
||||
```
|
||||
|
||||
- Navigate to `nocodb-sdk` package folder, install and build the package
|
||||
```sh
|
||||
cd packages/nocodb-sdk
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### Running backend
|
||||
|
||||
```sh
|
||||
# Navigate to `nocodb` package and install dependencies
|
||||
cd packages/nocodb
|
||||
npm install
|
||||
|
||||
# requires sqlite3
|
||||
|
||||
npm run watch:run
|
||||
|
||||
# if you have mysql on localhost (set its password as password)
|
||||
# npm run watch:run:mysql
|
||||
```
|
||||
|
||||
#### Running frontend
|
||||
|
||||
```sh
|
||||
# Navigate to `nc-gui` package and install dependencies
|
||||
cd packages/nc-gui
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
|
||||
15114
package-lock.json
generated
15114
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,11 @@
|
||||
"xlsx": "^0.17.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start:api": "cd ./packages/nocodb; npm install; npm run watch:run",
|
||||
"start:xcdb-api": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run",
|
||||
"build:common": "cd ./packages/nocodb-sdk; npm install; npm run build",
|
||||
"start:api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true npm run watch:run:cypress",
|
||||
"start:xcdb-api": "cd ./packages/nocodb; npm install; NC_DISABLE_CACHE=true NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
|
||||
"start:api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true npm run watch:run:cypress",
|
||||
"start:xcdb-api:cache": "cd ./packages/nocodb; npm install; NC_DISABLE_TELE=true NC_INFLECTION=camelize DATABASE_URL=sqlite:../../../scripts/cypress/fixtures/sqlite-sakila/sakila.db npm run watch:run:cypress",
|
||||
"start:web": "cd ./packages/nc-gui; npm install; npm run dev",
|
||||
"cypress:run": "cypress run --config-file ./scripts/cypress/cypress.json",
|
||||
"cypress:open": "cypress open --config-file ./scripts/cypress/cypress.json",
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": { "project": "./tsconfig.json" },
|
||||
"env": { "es6": true },
|
||||
"ignorePatterns": ["node_modules", "build", "coverage"],
|
||||
"plugins": ["import", "eslint-comments", "functional"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:eslint-comments/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:functional/lite",
|
||||
"prettier",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"globals": { "BigInt": true, "console": true, "WebAssembly": true },
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"eslint-comments/disable-enable-pair": [
|
||||
"error",
|
||||
{ "allowWholeFile": true }
|
||||
],
|
||||
"eslint-comments/no-unused-disable": "error",
|
||||
"import/order": [
|
||||
"error",
|
||||
{ "newlines-between": "always", "alphabetize": { "order": "asc" } }
|
||||
],
|
||||
"sort-imports": [
|
||||
"error",
|
||||
{ "ignoreDeclarationSort": true, "ignoreCase": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
9587
packages/nc-common/package-lock.json
generated
9587
packages/nc-common/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
export * from './lib/XcUIBuilder';
|
||||
export * from './lib/XcNotification';
|
||||
@@ -25,7 +25,10 @@
|
||||
"ignoreI18nBlock": false
|
||||
}
|
||||
],
|
||||
"@intlify/vue-i18n/no-missing-keys": "error"
|
||||
"@intlify/vue-i18n/no-missing-keys": "error",
|
||||
"max-len": ["warn", {
|
||||
"code": 120
|
||||
}]
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,6 @@
|
||||
<div>
|
||||
<v-card dark style="width: 100%; max-height:100%;overflow: auto">
|
||||
<v-container v-if="databaseCount" fluid style="min-height:200px;">
|
||||
<!-- <v-row class="text-center">-->
|
||||
<!-- <v-col cols="12">-->
|
||||
<v-card class="pa-2 text-center elevation-10" dark>
|
||||
<h3 class="title mb-3 mt-4">
|
||||
APIs Generated
|
||||
@@ -19,8 +17,6 @@
|
||||
<span class="subtitle grey--text text--lighten-1">within {{ timeTaken }} seconds</span>
|
||||
</p>
|
||||
</v-card>
|
||||
<!-- </v-col>-->
|
||||
<!-- </v-row>-->
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card dark class=" elevation-10">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-toolbar flat height="38" class="toolbar-border-bottom">
|
||||
<v-toolbar flat height="38" class="mt-5">
|
||||
<v-spacer />
|
||||
|
||||
<x-btn
|
||||
@@ -27,7 +27,7 @@
|
||||
color="primary"
|
||||
small
|
||||
:disabled="loading"
|
||||
@click="newTokenDialog = true"
|
||||
@click="showNewTokenDlg"
|
||||
>
|
||||
<v-icon small left>
|
||||
mdi-plus
|
||||
@@ -55,6 +55,17 @@
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr v-if="!tokens.length">
|
||||
<td colspan="3">
|
||||
<div
|
||||
class="text-center caption grey--text"
|
||||
>
|
||||
No tokens available
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="(token,i) in tokens" :key="i">
|
||||
<td class="caption text-center">
|
||||
{{ token.description }}
|
||||
@@ -90,15 +101,15 @@
|
||||
</x-icon>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- <tr>
|
||||
<td colspan="3" class="text-center">
|
||||
<x-btn tooltip="Generate new api token" outlined x-small color="primary" @click="newTokenDialog = true">
|
||||
<x-btn tooltip="Generate new api token" outlined x-small color="primary" @click="showNewTokenDlg">
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
<!--Add New Token-->
|
||||
<!–Add New Token–>
|
||||
{{ $t('activity.newToken') }}
|
||||
</x-btn>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>-->
|
||||
</v-simple-table>
|
||||
</v-container>
|
||||
|
||||
@@ -138,6 +149,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { copyTextToClipboard } from '~/helpers/xutils'
|
||||
|
||||
export default {
|
||||
name: 'ApiTokens',
|
||||
data: () => ({
|
||||
@@ -150,32 +163,45 @@ export default {
|
||||
this.loadApiTokens()
|
||||
},
|
||||
methods: {
|
||||
showNewTokenDlg() {
|
||||
this.newTokenDialog = true
|
||||
this.$tele.emit('api-mgmt:token:generate:trigger')
|
||||
},
|
||||
copyToken(token) {
|
||||
this.$clipboard(token)
|
||||
copyTextToClipboard(token)
|
||||
this.$toast.info('Copied to clipboard').goAway(1000)
|
||||
|
||||
this.$tele.emit('api-mgmt:token:copy')
|
||||
},
|
||||
async loadApiTokens() {
|
||||
this.tokens = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcApiTokenList'])
|
||||
this.tokens = (await this.$api.apiToken.list(this.$store.state.project.projectId))
|
||||
},
|
||||
async generateToken() {
|
||||
try {
|
||||
this.newTokenDialog = false
|
||||
this.tokens = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcApiTokenCreate', this.tokenObj])
|
||||
await this.$api.apiToken.create(this.$store.state.project.projectId, this.tokenObj)
|
||||
this.$toast.success('Token generated successfully').goAway(3000)
|
||||
this.tokenObj = {}
|
||||
await this.loadApiTokens()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('api-mgmt:token:generate:submit')
|
||||
},
|
||||
async deleteToken(item) {
|
||||
try {
|
||||
this.tokens = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcApiTokenDelete', { id: item.id }])
|
||||
await this.$api.apiToken.delete(this.$store.state.project.projectId, item.token)
|
||||
// this.tokens = //await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcApiTokenDelete', { id: item.id }])
|
||||
this.$toast.success('Token deleted successfully').goAway(3000)
|
||||
await this.loadApiTokens()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('api-mgmt:token:delete')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export default {
|
||||
}, 'xcAuthHookSet', this.data])
|
||||
this.$toast.success('Auth hook details updated successfully').goAway(3000)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.$toast.error('Some error occurred').goAway(3000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<!-- href: '#'-->
|
||||
<!-- },-->
|
||||
<!-- {-->
|
||||
<!-- text: nodes.tn + ' (Logic)',-->
|
||||
<!-- text: nodes.table_name + ' (Logic)',-->
|
||||
<!-- disabled: true,-->
|
||||
<!-- href: '#'-->
|
||||
<!-- }]" divider=">" small>-->
|
||||
|
||||
285
packages/nc-gui/components/auth/shareOrInviteModal.vue
Normal file
285
packages/nc-gui/components/auth/shareOrInviteModal.vue
Normal file
@@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<v-dialog v-model="userEditDialog" :width="invite_token ? 700 :700" @close="invite_token = null">
|
||||
<v-card v-if="selectedUser" style="min-height: 100%" class="elevation-0">
|
||||
<v-card-title>
|
||||
{{ $t('activity.share') }} : {{ $store.getters['project/GtrProjectName'] }}
|
||||
|
||||
<div class="nc-header-border" />
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<div>
|
||||
<v-icon small>
|
||||
mdi-account-outline
|
||||
</v-icon>
|
||||
<template v-if="invite_token">
|
||||
Copy Invite Token
|
||||
</template>
|
||||
<template v-else-if="selectedUser.id">
|
||||
Edit User
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- Invite Team -->
|
||||
{{ $t('activity.inviteTeam') }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="pa-4 nc-invite-container">
|
||||
<div v-if="invite_token" class="mt-6 align-center">
|
||||
<v-alert
|
||||
v-ripple
|
||||
type="success"
|
||||
outlined
|
||||
|
||||
class="pointer"
|
||||
@click="clipboard(inviteUrl); $toast.success('Copied invite url to clipboard').goAway(3000)"
|
||||
>
|
||||
<template #append>
|
||||
<v-icon color="green" class="ml-2">
|
||||
mdi-content-copy
|
||||
</v-icon>
|
||||
</template>
|
||||
<div class="ellipsis d-100">
|
||||
{{ inviteUrl }}
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<p class="caption grey--text mt-3">
|
||||
{{ $t('msg.info.userInviteNoSMTP') }}
|
||||
<!-- Looks like you have not configured mailer yet! <br>Please copy above -->
|
||||
<!-- invite -->
|
||||
<!-- link and send it to -->
|
||||
{{ invite_token && (invite_token.email || invite_token.emails && invite_token.emails.join(', ')) }}.
|
||||
</p>
|
||||
|
||||
<div class="text-right">
|
||||
<!--tooltip="Invite more users"-->
|
||||
<x-btn
|
||||
:tooltip="$t('tooltip.inviteMore')"
|
||||
small
|
||||
outlined
|
||||
btn.class="grey--text"
|
||||
@click="clickInviteMore"
|
||||
>
|
||||
<v-icon small color="grey" class="mr-1">
|
||||
mdi-account-multiple-plus-outline
|
||||
</v-icon>
|
||||
<!--Invite more-->
|
||||
{{ $t('activity.inviteMore') }}
|
||||
</x-btn>
|
||||
</div>
|
||||
|
||||
<!-- todo: show error message if failed-->
|
||||
</div>
|
||||
<template v-else>
|
||||
<v-form ref="form" v-model="valid" @submit.prevent="saveUser">
|
||||
<v-row class="my-0">
|
||||
<v-col cols="8" class="py-0">
|
||||
<!--hint="You can add multiple comma(,) separated emails"-->
|
||||
<v-text-field
|
||||
ref="email"
|
||||
v-model="selectedUser.email"
|
||||
:disabled="!!selectedUser.id"
|
||||
dense
|
||||
validate-on-blur
|
||||
outlined
|
||||
:rules="emailRules"
|
||||
class="caption"
|
||||
:hint="$t('msg.info.addMultipleUsers')"
|
||||
label="Email"
|
||||
@input="edited=true"
|
||||
>
|
||||
<template #label>
|
||||
<span class="caption">
|
||||
<!-- Email -->
|
||||
{{ $t('labels.email') }}
|
||||
</span>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="4" class="py-0">
|
||||
<!--label="Select User Role"-->
|
||||
<v-combobox
|
||||
v-model="selectedRoles"
|
||||
outlined
|
||||
:rules="roleRules"
|
||||
class="role-select caption"
|
||||
hide-details="auto"
|
||||
:items="roles"
|
||||
:label="$t('labels.selectUserRole')"
|
||||
dense
|
||||
deletable-chips
|
||||
@change="edited = true"
|
||||
>
|
||||
<template #selection="{item}">
|
||||
<v-chip small :color="rolesColors[item]">
|
||||
{{ item }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<template #item="{item}">
|
||||
<div>
|
||||
<div>{{ item }}</div>
|
||||
<div class="mb-2 caption grey--text">
|
||||
{{ roleDescriptions[item] }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</v-combobox>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
<div class="text-center mt-0">
|
||||
<x-btn
|
||||
v-ge="['rows','save']"
|
||||
:tooltip="$t('tooltip.saveChanges')"
|
||||
color="primary"
|
||||
|
||||
btn.class="nc-invite-or-save-btn"
|
||||
@click="saveUser"
|
||||
>
|
||||
<v-icon small left>
|
||||
{{ selectedUser.id ? 'save' : 'mdi-send' }}
|
||||
</v-icon>
|
||||
{{ selectedUser.id ? $t('general.save') : $t('activity.invite') }}
|
||||
</x-btn>
|
||||
</div>
|
||||
</template>
|
||||
<!-- </v-card-actions>-->
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<share-base />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isEmail } from '~/helpers'
|
||||
import { enumColor } from '~/components/project/spreadsheet/helpers/colors'
|
||||
import ShareBase from '~/components/base/shareBase'
|
||||
|
||||
export default {
|
||||
name: 'ShareOrInviteModal',
|
||||
components: { ShareBase },
|
||||
props: {
|
||||
value: Boolean
|
||||
},
|
||||
data: () => ({
|
||||
roles: ['creator', 'editor', 'commenter', 'viewer'],
|
||||
selectedUser: {},
|
||||
invite_token: null,
|
||||
valid: null,
|
||||
emailRules: [
|
||||
v => !!v || 'E-mail is required',
|
||||
(v) => {
|
||||
const invalidEmails = (v || '').split(/\s*,\s*/).filter(e => !isEmail(e))
|
||||
return !invalidEmails.length || `"${invalidEmails.join(', ')}" - invalid email`
|
||||
}
|
||||
],
|
||||
roleRules: [
|
||||
v => !!v || 'User Role is required',
|
||||
v => ['creator', 'editor', 'commenter', 'viewer'].includes(v) || 'invalid user role'
|
||||
],
|
||||
userList: [],
|
||||
roleDescriptions: {},
|
||||
deleteUserType: ''
|
||||
}),
|
||||
computed: {
|
||||
userEditDialog: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(v) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
},
|
||||
inviteUrl() {
|
||||
return this.invite_token ? `${location.origin}${location.pathname}#/user/authentication/signup/${this.invite_token.invite_token}` : null
|
||||
},
|
||||
rolesColors() {
|
||||
const colors = this.$store.state.windows.darkTheme ? enumColor.dark : enumColor.light
|
||||
return this.roles.reduce((o, r, i) => {
|
||||
o[r] = colors[i % colors.length]
|
||||
return o
|
||||
}, {})
|
||||
},
|
||||
|
||||
selectedRoles: {
|
||||
get() {
|
||||
return (this.selectedUser && this.selectedUser.roles ? this.selectedUser.roles.split(',') : []).sort((a, b) => this.roleNames.indexOf(a) - this.roleNames.indexOf(a))[0]
|
||||
},
|
||||
set(roles) {
|
||||
if (this.selectedUser) {
|
||||
this.selectedUser.roles = roles // .filter(Boolean).join(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
async saveUser() {
|
||||
this.validate = true
|
||||
await this.$nextTick()
|
||||
if (this.loading || !this.$refs.form.validate() || !this.selectedUser) {
|
||||
return
|
||||
}
|
||||
this.$tele.emit(`user-mgmt:add:${this.selectedUser.roles}`)
|
||||
|
||||
if (!this.edited) {
|
||||
this.userEditDialog = false
|
||||
}
|
||||
|
||||
try {
|
||||
let data
|
||||
if (this.selectedUser.id) {
|
||||
await this.$api.auth.projectUserUpdate(this.$route.params.project_id, this.selectedUser.id, {
|
||||
roles: this.selectedUser.roles,
|
||||
email: this.selectedUser.email,
|
||||
project_id: this.$route.params.project_id,
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
})
|
||||
} else {
|
||||
data = (await this.$api.auth.projectUserAdd(this.$route.params.project_id, {
|
||||
...this.selectedUser,
|
||||
project_id: this.$route.params.project_id,
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
}))
|
||||
}
|
||||
this.$toast.success('Successfully updated the user details').goAway(3000)
|
||||
this.$emit('saved')
|
||||
if (data && data.invite_token) {
|
||||
this.invite_token = data
|
||||
// todo: bring anim
|
||||
// this.simpleAnim()
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
|
||||
this.userEditDialog = false
|
||||
},
|
||||
|
||||
clickInviteMore() {
|
||||
this.$tele.emit('user-mgmt:invite-more')
|
||||
this.invite_token = null
|
||||
this.selectedUser = { roles: 'editor' }
|
||||
},
|
||||
clipboard(str) {
|
||||
const el = document.createElement('textarea')
|
||||
el.addEventListener('focusin', e => e.stopPropagation())
|
||||
el.value = str
|
||||
document.body.appendChild(el)
|
||||
el.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
|
||||
this.$tele.emit('user-mgmt:copy-url')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div class="h-100">
|
||||
<v-toolbar flat height="38" class="toolbar-border-bottom">
|
||||
<v-toolbar flat height="38" class="mt-5">
|
||||
<v-text-field
|
||||
v-model="query"
|
||||
style="max-width: 300px"
|
||||
dense
|
||||
solo
|
||||
flat
|
||||
solo
|
||||
class="search-field caption"
|
||||
hide-details
|
||||
placeholder="Filter by email"
|
||||
@keypress.enter="loadUsers"
|
||||
>
|
||||
<template #prepend-inner>
|
||||
<v-icon class="mt-1" small>
|
||||
<v-icon small class="mt-1">
|
||||
search
|
||||
</v-icon>
|
||||
</template>
|
||||
@@ -28,7 +28,7 @@
|
||||
color="primary"
|
||||
small
|
||||
:disabled="loading"
|
||||
@click="loadUsers"
|
||||
@click="clickReload"
|
||||
@click.prevent
|
||||
>
|
||||
<v-icon small left>
|
||||
@@ -40,6 +40,7 @@
|
||||
<!-- tooltip="Add new role" -->
|
||||
<x-btn
|
||||
v-if="_isUIAllowed('newUser')"
|
||||
class="nc-new-user"
|
||||
v-ge="['roles','add new']"
|
||||
outlined
|
||||
:tooltip="$t('tooltip.addRole')"
|
||||
@@ -56,26 +57,13 @@
|
||||
</x-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card style="height:calc(100% - 38px)">
|
||||
<v-card style="height:calc(100% - 38px)" class="elevation-0">
|
||||
<v-container style="height: 100%" fluid>
|
||||
<!-- <div class="d-flex d-100 justify-center">-->
|
||||
|
||||
<v-row style="height:100%">
|
||||
<v-col cols="12" class="h-100">
|
||||
<v-card class="h-100 elevation-0">
|
||||
<v-row style="height:100%">
|
||||
<v-col offset="2" :cols="8" class="h-100" style="overflow-y: auto">
|
||||
<!-- <v-card class="h-100 px-4 py-2">-->
|
||||
|
||||
<!-- <v-row>
|
||||
<v-col>
|
||||
|
||||
</v-col>
|
||||
<v-col class="flex-shrink-1 flex-grow-0"><h4 class="text-center text-capitalize mt-2 d-100"
|
||||
style="min-width:100px">User List</h4></v-col>
|
||||
<v-col></v-col>
|
||||
|
||||
</v-row>-->
|
||||
<v-data-table
|
||||
v-if="users"
|
||||
dense
|
||||
@@ -116,12 +104,6 @@
|
||||
|
||||
<template #item="{item}">
|
||||
<tr @click="selectedUser = item">
|
||||
<!-- <td>
|
||||
<v-radio-group dense hide-details v-model="selectedUserIndex" class="mt-n2">
|
||||
<v-radio :value="index"
|
||||
></v-radio>
|
||||
</v-radio-group>
|
||||
</td>-->
|
||||
<td>{{ item.email }}</td>
|
||||
<td>
|
||||
<!-- {{ item.roles }}-->
|
||||
@@ -133,23 +115,6 @@
|
||||
>
|
||||
{{ getRole(item.roles) }}
|
||||
</v-chip>
|
||||
|
||||
<!-- <v-edit-dialog-->
|
||||
<!-- >-->
|
||||
<!-- <div-->
|
||||
<!-- :title="item.roles"-->
|
||||
<!-- style="width:180px;overflow:hidden;white-space: nowrap;text-overflow:ellipsis"> {{-->
|
||||
<!-- item.roles-->
|
||||
<!-- }}-->
|
||||
<!-- </div>-->
|
||||
<!-- <template v-slot:input>-->
|
||||
<!-- <v-text-field-->
|
||||
<!-- v-model="item.roles"-->
|
||||
<!-- label="Edit"-->
|
||||
<!-- single-line-->
|
||||
<!-- ></v-text-field>-->
|
||||
<!-- </template>-->
|
||||
<!-- </v-edit-dialog>-->
|
||||
</td>
|
||||
<td>
|
||||
<!-- tooltip="Edit User" -->
|
||||
@@ -168,28 +133,18 @@
|
||||
tooltip="Add user to project"
|
||||
color="primary"
|
||||
small
|
||||
@click="inviteUser(item.email)"
|
||||
@click="inviteUser(item)"
|
||||
>
|
||||
mdi-plus
|
||||
</x-icon>
|
||||
<!-- <x-icon
|
||||
tooltip="Remove user from NocoDB"
|
||||
class="ml-2"
|
||||
color="error"
|
||||
small
|
||||
@click.prevent.stop="deleteId = item.id; deleteItem = item.id;showConfirmDlg = true;deleteUserType='DELETE_FROM_NOCODB'"
|
||||
>
|
||||
mdi-delete-forever-outline
|
||||
</x-icon> -->
|
||||
</span>
|
||||
<!-- tooltip="Remove user from project" -->
|
||||
<x-icon
|
||||
v-else
|
||||
:tooltip="$t('activity.deleteUser')"
|
||||
class="ml-2"
|
||||
color="error"
|
||||
small
|
||||
@click.prevent.stop="deleteId = item.id; deleteItem = item.id;showConfirmDlg = true;deleteUserType='DELETE_FROM_PROJECT'"
|
||||
@click.prevent.stop="clickDeleteUser(item.id)"
|
||||
>
|
||||
mdi-delete-outline
|
||||
</x-icon>
|
||||
@@ -201,7 +156,7 @@
|
||||
icon-class="mt-n1"
|
||||
color="primary"
|
||||
small
|
||||
@click.prevent.stop="rensendInvite(item.id)"
|
||||
@click.prevent.stop="resendInvite(item.id)"
|
||||
>
|
||||
mdi-email-send-outline
|
||||
</x-icon>
|
||||
@@ -222,7 +177,7 @@
|
||||
</template>
|
||||
</v-data-table>
|
||||
<!-- tooltip="Add new user" -->
|
||||
<div class="mt-10 text-center">
|
||||
<!-- <div class="mt-10 text-center">
|
||||
<x-btn
|
||||
v-if="_isUIAllowed('newUser')"
|
||||
v-ge="['roles','add new']"
|
||||
@@ -236,10 +191,10 @@
|
||||
<v-icon small left>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
<!-- New User -->
|
||||
<!– New User –>
|
||||
{{ $t('activity.newUser') }}
|
||||
</x-btn>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<feedback-form class="mx-auto mt-6" />
|
||||
</v-col>
|
||||
@@ -324,21 +279,9 @@
|
||||
|
||||
<!-- todo: move to a separate component-->
|
||||
<v-dialog v-model="userEditDialog" :width="invite_token ? 700 :700" @close="invite_token = null">
|
||||
<v-card v-if="selectedUser" style="min-height: 100%">
|
||||
<v-card v-if="selectedUser" style="min-height: 100%" class="elevation-0">
|
||||
<v-card-title>
|
||||
{{ $t('activity.share') }} : {{ $store.getters['project/GtrProjectName'] }}
|
||||
<!--
|
||||
<h4 class="text-center text-capitalize mt-2 d-100 display-1">
|
||||
<template v-if="invite_token">
|
||||
Copy Invite Token
|
||||
</template>
|
||||
<template v-else-if="selectedUser.id">
|
||||
Edit User
|
||||
</template>
|
||||
<template v-else>
|
||||
Invite
|
||||
</template>
|
||||
</h4>-->
|
||||
|
||||
<div class="nc-header-border" />
|
||||
</v-card-title>
|
||||
@@ -394,7 +337,7 @@
|
||||
small
|
||||
outlined
|
||||
btn.class="grey--text"
|
||||
@click="invite_token = null, selectedUser = {}"
|
||||
@click="clickInviteMore"
|
||||
>
|
||||
<v-icon small color="grey" class="mr-1">
|
||||
mdi-account-multiple-plus-outline
|
||||
@@ -418,7 +361,7 @@
|
||||
dense
|
||||
validate-on-blur
|
||||
outlined
|
||||
:rules="validate && emailRules"
|
||||
:rules="emailRules"
|
||||
class="caption"
|
||||
:hint="$t('msg.info.addMultipleUsers')"
|
||||
label="Email"
|
||||
@@ -437,6 +380,7 @@
|
||||
<v-combobox
|
||||
v-model="selectedRoles"
|
||||
outlined
|
||||
:rules="roleRules"
|
||||
class="role-select caption"
|
||||
hide-details="auto"
|
||||
:items="roles"
|
||||
@@ -462,10 +406,6 @@
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
<!-- </v-card-text>
|
||||
<v-card-actions class="justify-center">-->
|
||||
|
||||
<!-- tooltip="Save Changes" -->
|
||||
<div class="text-center mt-0">
|
||||
<x-btn
|
||||
v-ge="['rows','save']"
|
||||
@@ -529,6 +469,10 @@ export default {
|
||||
return !invalidEmails.length || `"${invalidEmails.join(', ')}" - invalid email`
|
||||
}
|
||||
],
|
||||
roleRules: [
|
||||
v => !!v || 'User Role is required',
|
||||
v => ['creator', 'editor', 'commenter', 'viewer'].includes(v) || 'invalid user role'
|
||||
],
|
||||
userList: [],
|
||||
roleDescriptions: {},
|
||||
deleteUserType: '' // [DELETE_FROM_PROJECT, DELETE_FROM_NOCODB]
|
||||
@@ -599,6 +543,22 @@ export default {
|
||||
this.$eventBus.$off('show-add-user', this.addUser)
|
||||
},
|
||||
methods: {
|
||||
clickReload() {
|
||||
this.loadUsers()
|
||||
this.$tele.emit('user-mgmt:reload')
|
||||
},
|
||||
clickDeleteUser(id) {
|
||||
this.$tele.emit('user-mgmt:delete:trigger')
|
||||
this.deleteId = id
|
||||
this.deleteItem = id
|
||||
this.showConfirmDlg = true
|
||||
this.deleteUserType = 'DELETE_FROM_PROJECT'
|
||||
},
|
||||
clickInviteMore() {
|
||||
this.$tele.emit('user-mgmt:invite-more')
|
||||
this.invite_token = null
|
||||
this.selectedUser = { roles: 'editor' }
|
||||
},
|
||||
getRole(roles) {
|
||||
return (roles ? roles.split(',') : []).sort((a, b) => this.roleNames.indexOf(a) - this.roleNames.indexOf(a))[0]
|
||||
},
|
||||
@@ -650,8 +610,10 @@ export default {
|
||||
el.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(el)
|
||||
|
||||
this.$tele.emit('user-mgmt:copy-url')
|
||||
},
|
||||
async rensendInvite(id) {
|
||||
async resendInvite(id) {
|
||||
try {
|
||||
await this.$axios.post('/admin/resendInvite/' + id, {
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
@@ -668,23 +630,34 @@ export default {
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('user-mgmt:resend-invite')
|
||||
},
|
||||
async loadUsers() {
|
||||
try {
|
||||
const { page = 1, itemsPerPage = 20 } = this.options
|
||||
const data = (await this.$axios.get('/admin', {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
},
|
||||
params: {
|
||||
// const data = (await this.$axios.get('/admin', {
|
||||
// headers: {
|
||||
// 'xc-auth': this.$store.state.users.token
|
||||
// },
|
||||
// params: {
|
||||
// limit: itemsPerPage,
|
||||
// offset: (page - 1) * itemsPerPage,
|
||||
// query: this.query,
|
||||
// project_id: this.$route.params.project_id
|
||||
// }
|
||||
// })).data
|
||||
|
||||
const userData = (await this.$api.auth.projectUserList(this.$store.state.project.projectId, {
|
||||
query: {
|
||||
limit: itemsPerPage,
|
||||
offset: (page - 1) * itemsPerPage,
|
||||
query: this.query,
|
||||
project_id: this.$route.params.project_id
|
||||
query: this.query
|
||||
}
|
||||
})).data
|
||||
this.count = data.count
|
||||
this.users = data.list
|
||||
}))
|
||||
|
||||
this.count = userData.users.pageInfo.totalRows
|
||||
this.users = userData.users.list
|
||||
if (!this.selectedUser && this.users && this.users[0]) {
|
||||
this.selectedUserIndex = 0
|
||||
}
|
||||
@@ -694,33 +667,27 @@ export default {
|
||||
},
|
||||
async loadRoles() {
|
||||
try {
|
||||
this.roles = (await this.$axios.get('/admin/roles', {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
},
|
||||
params: {
|
||||
project_id: this.$route.params.project_id
|
||||
}
|
||||
})).data.map((role) => {
|
||||
this.roleDescriptions[role.title] = role.description
|
||||
return role.title
|
||||
}).filter(role => role !== 'guest')
|
||||
this.roles = ['creator', 'editor', 'commenter', 'viewer']
|
||||
|
||||
// todo:
|
||||
// (await this.$axios.get('/admin/roles', {
|
||||
// headers: {
|
||||
// 'xc-auth': this.$store.state.users.token
|
||||
// },
|
||||
// params: {
|
||||
// project_id: this.$route.params.project_id
|
||||
// }
|
||||
// })).data.map((role) => {
|
||||
// this.roleDescriptions[role.title] = role.description
|
||||
// return role.title
|
||||
// }).filter(role => role !== 'guest')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async deleteUser(id, type) {
|
||||
try {
|
||||
await this.$axios.delete('/admin/' + id, {
|
||||
params: {
|
||||
project_id: this.$route.params.project_id,
|
||||
email: this.deleteItem.email,
|
||||
type
|
||||
},
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
})
|
||||
await this.$api.auth.projectUserRemove(this.$route.params.project_id, id)
|
||||
this.$toast.success(`Successfully removed the user from ${type === 'DELETE_FROM_PROJECT' ? 'project' : 'NocoDB'}`).goAway(3000)
|
||||
await this.loadUsers()
|
||||
} catch (e) {
|
||||
@@ -734,6 +701,8 @@ export default {
|
||||
}
|
||||
await this.deleteUser(this.deleteId, this.deleteUserType)
|
||||
this.showConfirmDlg = false
|
||||
|
||||
this.$tele.emit('user-mgmt:delete:submit')
|
||||
},
|
||||
addUser() {
|
||||
this.invite_token = null
|
||||
@@ -741,23 +710,19 @@ export default {
|
||||
roles: 'editor'
|
||||
}
|
||||
this.userEditDialog = true
|
||||
|
||||
this.$tele.emit('user-mgmt:add-user:trigger')
|
||||
},
|
||||
async inviteUser(email) {
|
||||
async inviteUser(item) {
|
||||
try {
|
||||
await this.$axios.post('/admin', {
|
||||
email,
|
||||
project_id: this.$route.params.project_id,
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
})
|
||||
await this.$api.auth.projectUserAdd(this.$route.params.project_id, item)
|
||||
this.$toast.success('Successfully added user to project').goAway(3000)
|
||||
await this.loadUsers()
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('user-mgmt:invite-user')
|
||||
},
|
||||
async saveUser() {
|
||||
this.validate = true
|
||||
@@ -765,6 +730,7 @@ export default {
|
||||
if (this.loading || !this.$refs.form.validate() || !this.selectedUser) {
|
||||
return
|
||||
}
|
||||
this.$tele.emit(`user-mgmt:add:${this.selectedUser.roles}`)
|
||||
|
||||
if (!this.edited) {
|
||||
this.userEditDialog = false
|
||||
@@ -773,31 +739,23 @@ export default {
|
||||
try {
|
||||
let data
|
||||
if (this.selectedUser.id) {
|
||||
await this.$axios.put('/admin/' + this.selectedUser.id, {
|
||||
await this.$api.auth.projectUserUpdate(this.$route.params.project_id, this.selectedUser.id, {
|
||||
roles: this.selectedUser.roles,
|
||||
email: this.selectedUser.email,
|
||||
project_id: this.$route.params.project_id,
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
})
|
||||
} else {
|
||||
data = await this.$axios.post('/admin', {
|
||||
data = (await this.$api.auth.projectUserAdd(this.$route.params.project_id, {
|
||||
...this.selectedUser,
|
||||
project_id: this.$route.params.project_id,
|
||||
projectName: this.$store.getters['project/GtrProjectName']
|
||||
}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
this.$toast.success('Successfully updated the user details').goAway(3000)
|
||||
await this.loadUsers()
|
||||
if (data && data.data && data.data.invite_token) {
|
||||
this.invite_token = data.data
|
||||
if (data && data.invite_token) {
|
||||
this.invite_token = data
|
||||
this.simpleAnim()
|
||||
return
|
||||
}
|
||||
@@ -805,7 +763,6 @@ export default {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000)
|
||||
}
|
||||
|
||||
this.userEditDialog = false
|
||||
await this.loadUsers()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="h-100 nc-auth-tab">
|
||||
<div class="h-100" style="width: 100%">
|
||||
<v-tabs height="30" color="x-active">
|
||||
<v-tabs height="40" color="x-active">
|
||||
<v-tab>
|
||||
<span class="caption text-capitalize">
|
||||
<!-- Users Management -->
|
||||
@@ -23,19 +23,6 @@
|
||||
<api-tokens :nodes="nodes" />
|
||||
</v-tab-item>
|
||||
</template>
|
||||
<v-tab>
|
||||
<span class="caption text-capitalize">
|
||||
<!-- Roles Management -->
|
||||
{{ $t('title.rolesMgmt') }}
|
||||
</span>
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<roles :nodes="nodes" />
|
||||
</v-tab-item>
|
||||
<!-- <v-tab><span class="caption text-capitalize">Auth Management</span></v-tab>
|
||||
<v-tab-item>
|
||||
<auth-hooks :nodes="nodes"></auth-hooks>
|
||||
</v-tab-item>-->
|
||||
</v-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{{ $t('activity.shareBase.link') }}
|
||||
</span>
|
||||
<div class="nc-container">
|
||||
<v-chip v-if="base.enabled" :color="colors[4]" style="" class="rounded pl-1 pr-0 d-100 nc-url-chip pr-3">
|
||||
<v-chip v-if="base.uuid" :color="colors[4]" style="" class="rounded pl-1 pr-0 d-100 nc-url-chip pr-3">
|
||||
<div class="nc-url-wrapper d-flex mx-1 align-center d-100">
|
||||
<span class="nc-url flex-grow-1 caption ">{{ url }}</span>
|
||||
<v-spacer />
|
||||
@@ -54,7 +54,7 @@
|
||||
<template #activator="{on}">
|
||||
<div class="my-2" v-on="on">
|
||||
<div class="font-weight-bold nc-disable-shared-base">
|
||||
<span v-if="base.enabled">
|
||||
<span v-if="base.uuid">
|
||||
<!-- Anyone with the link -->
|
||||
{{ $t('activity.shareBase.enable') }}
|
||||
</span>
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item dense @click="createSharedBase('viewer')">
|
||||
<v-list-item v-if="!base.uuid" dense @click="createSharedBase('viewer')">
|
||||
<v-list-item-title>
|
||||
<v-icon small class="mr-1">
|
||||
mdi-link-variant
|
||||
@@ -80,14 +80,14 @@
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item dense @click="disableSharedBase">
|
||||
<v-list-item v-if="base.uuid" dense @click="disableSharedBase">
|
||||
<v-list-item-title>
|
||||
<v-icon small class="mr-1">
|
||||
mdi-link-variant-off
|
||||
</v-icon>
|
||||
<span class="caption">
|
||||
<!-- Disable shared base -->
|
||||
{{ $t('activity.shareBase.link') }}
|
||||
{{ $t('activity.shareBase.disable') }}
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
@@ -112,9 +112,12 @@
|
||||
</div>
|
||||
<v-spacer />
|
||||
<div class="d-flex justify-center" style="width:120px">
|
||||
<v-menu v-if="base.enabled" offset-y>
|
||||
<v-menu v-if="base.uuid" offset-y>
|
||||
<template #activator="{on}">
|
||||
<div class="text-capitalize my-2 font-weight-bold backgroundColorDefault py-2 px-4 rounded nc-shared-base-role" v-on="on">
|
||||
<div
|
||||
class="text-capitalize my-2 font-weight-bold backgroundColorDefault py-2 px-4 rounded nc-shared-base-role"
|
||||
v-on="on"
|
||||
>
|
||||
{{ base.roles || 'Viewer' }}
|
||||
|
||||
<v-icon small>
|
||||
@@ -158,7 +161,7 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
url() {
|
||||
return this.base && this.base.shared_base_id ? `${this.dashboardUrl}#/nc/base/${this.base.shared_base_id}` : null
|
||||
return this.base && this.base.uuid ? `${this.dashboardUrl}#/nc/base/${this.base.uuid}` : null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -167,8 +170,10 @@ export default {
|
||||
methods: {
|
||||
async loadSharedBase() {
|
||||
try {
|
||||
const sharedBase = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{ dbAlias: 'db' }, 'getSharedBaseLink'])
|
||||
// const sharedBase = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
// { dbAlias: 'db' }, 'getSharedBaseLink'])
|
||||
const sharedBase = (await this.$api.project.sharedBaseGet(this.$store.state.project.projectId))
|
||||
|
||||
this.base = sharedBase || {}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
@@ -176,35 +181,46 @@ export default {
|
||||
},
|
||||
async createSharedBase(roles = 'viewer') {
|
||||
try {
|
||||
const sharedBase = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'createSharedBaseLink', { roles }])
|
||||
// const sharedBase = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'createSharedBaseLink', { roles }])
|
||||
const sharedBase = (await this.$api.project.sharedBaseUpdate(this.$store.state.project.projectId, { roles }))
|
||||
|
||||
this.base = sharedBase || {}
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit(`shared-base:enable:${roles}`)
|
||||
},
|
||||
async disableSharedBase() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'disableSharedBaseLink'])
|
||||
await this.$api.project.sharedBaseDisable(this.$store.state.project.projectId)
|
||||
this.base = {}
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('shared-base:disable')
|
||||
},
|
||||
async recreate() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'disableSharedBaseLink'])
|
||||
const sharedBase = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'createSharedBaseLink'])
|
||||
const sharedBase = (await this.$api.project.sharedBaseCreate(this.$store.state.project.projectId, { roles: this.base.roles || 'viewer' }))
|
||||
this.base = sharedBase || {}
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('shared-base:recreate')
|
||||
},
|
||||
copyUrl() {
|
||||
copyTextToClipboard(this.url)
|
||||
this.$toast.success('Copied shareable base url to clipboard!').goAway(3000)
|
||||
|
||||
this.$tele.emit('shared-base:copy-url')
|
||||
},
|
||||
navigateToSharedBase() {
|
||||
window.open(this.url, '_blank')
|
||||
|
||||
this.$tele.emit('shared-base:open-url')
|
||||
},
|
||||
generateEmbeddableIframe() {
|
||||
copyTextToClipboard(`<iframe
|
||||
@@ -215,6 +231,8 @@ width="100%"
|
||||
height="700"
|
||||
style="background: transparent; border: 1px solid #ddd"></iframe>`)
|
||||
this.$toast.success('Copied embeddable html code!').goAway(3000)
|
||||
|
||||
this.$tele.emit('shared-base:copy-embed-frame')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,5 +257,8 @@ style="background: transparent; border: 1px solid #ddd"></iframe>`)
|
||||
background: var(--v-backgroundColor-base);
|
||||
padding: 20px 20px;
|
||||
}
|
||||
/deep/ .nc-url-chip .v-chip__content{width: 100%}
|
||||
|
||||
/deep/ .nc-url-chip .v-chip__content {
|
||||
width: 100%
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -74,81 +74,6 @@
|
||||
|
||||
<div ref="panelContainer" style="">
|
||||
<api-overlay v-show="projectReloading" :project-created="projectCreated" />
|
||||
<!-- <v-overlay absolute color="grey" opacity="0.4"-->
|
||||
<!-- class="create-project-overlay">-->
|
||||
|
||||
<!--<div>
|
||||
<v-card dark style="width: 100%; max-height:100%;overflow: auto">
|
||||
<v-container fluid style="min-height:200px;">
|
||||
<!– <v-row class="text-center">–>
|
||||
<!– <v-col cols="12">–>
|
||||
<v-card class="pa-2 text-center elevation-10" dark>
|
||||
<h3 class="title mb-3 mt-4">APIs Generated</h3>
|
||||
<p><span class="display-2 font-weight-bold">1,200,000</span><br>
|
||||
<span class="subtitle grey--text text--lighten-1">within 60 seconds</span></p>
|
||||
</v-card>
|
||||
<!– </v-col>–>
|
||||
<!– </v-row>–>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-card dark class=" elevation-10">
|
||||
<v-card-text class="pb-0 font-weight-bold"># Databases</v-card-text>
|
||||
<v-card-text class="title white--text">
|
||||
<v-icon class="mt-n2 mr-1" color="info">mdi-database-sync</v-icon>
|
||||
|
||||
180/190
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-card dark class=" elevation-10">
|
||||
<v-card-text class="pb-0 font-weight-bold"># Tables</v-card-text>
|
||||
<v-card-text class="title white--text">
|
||||
<v-icon class="mr-1 mt-n1 " color="info">mdi-table-large</v-icon>
|
||||
50000
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-card dark class=" elevation-10">
|
||||
<v-card-text class="pb-0 font-weight-bold">Time Saved</v-card-text>
|
||||
<v-card-text class="title white--text">
|
||||
<v-icon class="mr-1 mt-n1" color="secondary">mdi-clock-fast</v-icon>
|
||||
10hrs
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-card dark class=" elevation-10">
|
||||
<v-card-text class="pb-0 font-weight-bold">CostSaved</v-card-text>
|
||||
<v-card-text class="title white--text">
|
||||
<v-icon color="success" class="mt-n2 mr-1">mdi-currency-usd-circle</v-icon>
|
||||
100$
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<!– <v-divider class="my-3 "></v-divider>–>
|
||||
<v-card dark class=" elevation-10">
|
||||
<p class="title text-center pt-2">List of Database</p>
|
||||
<v-simple-table style="width: 100%">
|
||||
<tr v-for="i in 40">
|
||||
<td v-for="j in 5" class="py-2 px-3">
|
||||
<div class="d-flex">
|
||||
<v-icon color="green" x-small class="mr-2">mdi-moon-full</v-icon>
|
||||
<span class="caption">Database {{ i }} {{ j }}</span>
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</v-simple-table>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</div>-->
|
||||
<!-- </v-overlay>-->
|
||||
|
||||
<v-container fluid>
|
||||
<v-row>
|
||||
<v-col cols="12" class="mb-0 pb-0">
|
||||
@@ -163,52 +88,11 @@
|
||||
:label="$t('placeholder.projName')"
|
||||
autofocus
|
||||
>
|
||||
<!-- <v-icon color="info" class="blink_me mt-n1" slot="prepend">-->
|
||||
<!-- mdi-lightbulb-on-->
|
||||
<!-- </v-icon>-->
|
||||
</v-text-field>
|
||||
|
||||
<!-- Access Project via -->
|
||||
<label class="caption"> {{ $t('msg.info.apiOptions') }}</label>
|
||||
<v-radio-group
|
||||
v-model="project.projectType"
|
||||
row
|
||||
hide-details
|
||||
dense
|
||||
class="mb-0 mt-0"
|
||||
>
|
||||
<v-radio
|
||||
v-for="(type, i) in projectTypes"
|
||||
:key="type.value"
|
||||
:color="type.iconColor"
|
||||
:value="type.value"
|
||||
>
|
||||
<template #label>
|
||||
<v-chip :color="i ? colors[3] : colors[7]">
|
||||
<v-icon small class="mr-1">
|
||||
{{ type.icon }}
|
||||
</v-icon>
|
||||
<span class="caption">{{ type.text }}</span>
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-radio>
|
||||
</v-radio-group>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <v-select
|
||||
v-model="project.projectType" hint="Access via API type" persistent-hint dense
|
||||
:items="projectTypes">
|
||||
<template v-slot:prepend>
|
||||
<img v-if="typeIcon.type === 'img'" :src="typeIcon.icon" style="width: 32px">
|
||||
<v-icon v-else :color="typeIcon.iconColor">{{ typeIcon.icon }}</v-icon>
|
||||
</template>
|
||||
<template v-slot:item="{item}">
|
||||
<span class="caption d-flex align-center">
|
||||
<img v-if="item.type === 'img'" :src="item.icon" style="width: 30px">
|
||||
<v-icon v-else :color="item.iconColor">{{ item.icon }}</v-icon> {{ item.text }}</span>
|
||||
</template>
|
||||
|
||||
</v-select>-->
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
@@ -217,10 +101,6 @@
|
||||
offset="1"
|
||||
:class="{ 'mt-0 pt-0': !edit, 'mt-3 pt-3': edit }"
|
||||
>
|
||||
<!-- <h2 :class="{'text-center mb-2':!edit,'text-center mb-2 grey--text':edit}">
|
||||
{{ project.title && project.title.toUpperCase() }}'s
|
||||
Environments</h2> -->
|
||||
|
||||
<p
|
||||
:class="{
|
||||
'text-center mb-2 mt-3': !edit,
|
||||
@@ -244,8 +124,6 @@
|
||||
>
|
||||
<v-expansion-panel-header disable-icon-rotate>
|
||||
<p class="pa-0 ma-0">
|
||||
<!-- <v-icon>mdi-test-tube</v-icon> -->
|
||||
<!-- <span class="title"> <b>'{{ envKey }}'</b> environment : </span>-->
|
||||
<v-tooltip v-for="(db, tabIndex) in envData.db" :key="tabIndex" bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-icon
|
||||
@@ -321,21 +199,6 @@
|
||||
db.connection.database
|
||||
}}</span>
|
||||
</v-tab>
|
||||
<!-- <v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<x-btn tooltip="Add New Database to Environment" text small class="ma-2" v-on="on"
|
||||
@click.prevent.stop="addNewDB(envKey,panelIndex)"
|
||||
v-ge="['project','env-db-add']"
|
||||
>
|
||||
<v-hover v-slot:default="{ hover }">
|
||||
<v-icon :color="hover ? 'primary' : 'grey'">mdi-database-plus
|
||||
</v-icon>
|
||||
</v-hover>
|
||||
</x-btn>
|
||||
</template>
|
||||
<span>Add new database to '{{ envKey }}' environment</span>
|
||||
</v-tooltip>
|
||||
-->
|
||||
<v-tabs-items v-model="databases[panelIndex]">
|
||||
<v-tab-item
|
||||
v-for="(db, dbIndex) in project.envs[envKey].db"
|
||||
@@ -422,14 +285,6 @@
|
||||
>
|
||||
{{ data.item }}
|
||||
</v-chip>
|
||||
<!-- <div class="d-flex flex-column mx-auto "-->
|
||||
<!-- style="width:100%;border-bottom: 1px solid #ddd">-->
|
||||
<!-- <img class="mx-auto py-3" width="80" :src="dbIcons[data.item]"/>-->
|
||||
<!-- <!– {{ data.item }}–>-->
|
||||
|
||||
<!-- <p v-if="!databaseNames[data.item]" class="text-center grey--text">-->
|
||||
<!-- Coming soon</p>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
@@ -523,6 +378,7 @@
|
||||
:label="$t('labels.dbCreateIfNotExists')"
|
||||
/>
|
||||
</v-col>
|
||||
<!-- todo : ssl & inflection -->
|
||||
<v-col v-if="db.client !== 'sqlite3'" class="">
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel style="border: 1px solid wheat">
|
||||
@@ -649,7 +505,7 @@
|
||||
<v-col>
|
||||
<!-- Inflection - Table name -->
|
||||
<v-select
|
||||
v-model="db.meta.inflection.tn"
|
||||
v-model="db.meta.inflection.table_name"
|
||||
:disabled="edit"
|
||||
class="caption"
|
||||
:label="
|
||||
@@ -667,7 +523,7 @@
|
||||
<v-col>
|
||||
<!-- Inflection - Column name -->
|
||||
<v-select
|
||||
v-model="db.meta.inflection.cn"
|
||||
v-model="db.meta.inflection.column_name"
|
||||
:disabled="edit"
|
||||
class="caption"
|
||||
:label="
|
||||
@@ -776,157 +632,10 @@
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
|
||||
<!-- <v-expansion-panel>
|
||||
<v-expansion-panel-header disable-icon-rotate>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<x-btn tooltip="Add New Environment to Project" color="grey" block v-on="on" outlined
|
||||
v-ge="['project','env-add']"
|
||||
@click.stop="addNewEnvironment">
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
Add Another Environment
|
||||
</x-btn>
|
||||
</template>
|
||||
<span>Add new environment to {{ project.title }} project</span>
|
||||
</v-tooltip>
|
||||
<template v-slot:actions>
|
||||
<i></i>
|
||||
</template>
|
||||
</v-expansion-panel-header>
|
||||
</v-expansion-panel>-->
|
||||
|
||||
</v-expansion-panels>
|
||||
</v-col>
|
||||
|
||||
<!-- <v-col cols="10" offset="1" v-show="isTitle"
|
||||
:class="{'mt-0 pt-0':!edit,'mt-3 pt-3':edit}">
|
||||
<!– <h2 :class="{'text-center mb-2':!edit,'text-center mb-2 grey--text':edit}">–>
|
||||
<!– Advanced Configuration</h2>–>
|
||||
<v-expansion-panels focusable accordion="" class="elevation-20"
|
||||
style="border: 1px solid grey">
|
||||
<v-expansion-panel
|
||||
@change="onAdvancePanelToggle"
|
||||
>
|
||||
<v-expansion-panel-header disable-icon-rotate>
|
||||
|
||||
<p class="pa-0 ma-0">
|
||||
<v-icon class="mt-n2 " color="grey darken-1">mdi-cog</v-icon>
|
||||
<span class="grey--text text--darken-1">Advance Configuration</span>
|
||||
</p>
|
||||
|
||||
</v-expansion-panel-header>
|
||||
<v-expansion-panel-content eager>
|
||||
<v-card class="mt-3">
|
||||
<v-card-title>
|
||||
<v-icon class="mr-2">mdi-shield-account-outline</v-icon>
|
||||
Authentication Configuration
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-container class="justify-center">
|
||||
<v-row>
|
||||
<v-col cols="12" class="py-0">
|
||||
<v-select
|
||||
v-model="auth.authType" hint="Choose Authentication type"
|
||||
persistent-hint dense
|
||||
:items="authTypes">
|
||||
|
||||
<template v-slot:item="{item}">
|
||||
<span class="caption">
|
||||
{{ item.text }}</span>
|
||||
</template>
|
||||
|
||||
</v-select>
|
||||
|
||||
</v-col>
|
||||
<v-col cols="12" class="py-0" v-if="auth.authType && auth.authType !== 'none'">
|
||||
<v-text-field
|
||||
|
||||
v-if="auth.authType !== 'middleware'"
|
||||
v-model="auth.authSecret"
|
||||
label="Enter Auth Secret (Randomly generated)"
|
||||
:type="showSecret ? 'text' : 'password'"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<v-icon small
|
||||
@click="showSecret = !showSecret"
|
||||
>{{
|
||||
showSecret ? 'visibility_off' :
|
||||
'visibility'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-text-field
|
||||
v-else
|
||||
|
||||
v-model="auth.webhook"
|
||||
label="Webhook url"
|
||||
|
||||
>
|
||||
</v-text-field>
|
||||
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<!– </v-expansion-panel-content>–>
|
||||
<!– </v-expansion-panel>–>
|
||||
<!– </v-expansion-panels>–>
|
||||
<!– </v-col>–>
|
||||
<!– <v-col cols="10" offset="1" v-show="project.title.trim().length"–>
|
||||
<!– :class="{'mt-0 pt-0':!edit,'mt-3 pt-3':edit}">–>
|
||||
|
||||
<!– <v-expansion-panels v-model="smtpPanel" focusable accordion="" class="elevation-20"–>
|
||||
<!– style="border: 1px solid grey">–>
|
||||
|
||||
<!– <v-expansion-panel>–>
|
||||
<!– <v-expansion-panel-header disable-icon-rotate>–>
|
||||
|
||||
<!– <p class="pa-0 ma-0">–>
|
||||
<!– <v-icon class="mt-n2 " color="grey darken-1">mdi-email-edit</v-icon> –>
|
||||
<!– <span class="grey--text text--darken-1">SMTP Configuration</span>–>
|
||||
<!– </p>–>
|
||||
|
||||
<!– </v-expansion-panel-header>–>
|
||||
<!– <v-expansion-panel-content :eager="false">–>
|
||||
|
||||
<v-card class="mt-3">
|
||||
<v-card-title>
|
||||
<v-icon class="mr-2">mdi-email-edit</v-icon>
|
||||
SMTP Configuration
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
|
||||
<v-text-field
|
||||
v-model="smtpConfiguration.from"
|
||||
label="From address"
|
||||
placeholder="Company<noreply@company.com>"
|
||||
|
||||
>
|
||||
</v-text-field>
|
||||
|
||||
<label>Mailer Config Options<span class="caption"> (For connection example visit: <a
|
||||
href="https://nodemailer.com/smtp/#examples">https://nodemailer.com/smtp/#examples</a>)</span></label>
|
||||
<monaco-json-editor
|
||||
ref="monacoEditor"
|
||||
v-model="smtpConfiguration.options"
|
||||
style="height: 300px; width:100% "></monaco-json-editor>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
|
||||
</v-expansion-panels>
|
||||
|
||||
</v-col>
|
||||
|
||||
<v-col cols="10" offset="1" v-show="isTitle"
|
||||
:class="{'mt-0 pt-0':!edit,'mt-3 pt-3':edit}">
|
||||
|
||||
<create-project-coming-soon></create-project-coming-soon>
|
||||
</v-col>-->
|
||||
</v-row>
|
||||
</v-container>
|
||||
</div>
|
||||
@@ -941,8 +650,8 @@
|
||||
:type="dialog.type"
|
||||
/>
|
||||
|
||||
<!-- heading="Connection was successful" -->
|
||||
<!-- ok-label="Ok & Save Project" -->
|
||||
<!-- heading="Connection was successful" -->
|
||||
<!-- ok-label="Ok & Save Project" -->
|
||||
<dlg-ok-new
|
||||
v-model="testSuccess"
|
||||
:heading="$t('msg.info.dbConnected')"
|
||||
@@ -999,7 +708,12 @@ import colors from '@/mixins/colors'
|
||||
import DlgOkNew from '@/components/utils/dlgOkNew'
|
||||
import readFile from '@/helpers/fileReader'
|
||||
|
||||
const { uniqueNamesGenerator, starWars, adjectives, animals } = require('unique-names-generator')
|
||||
const {
|
||||
uniqueNamesGenerator,
|
||||
starWars,
|
||||
adjectives,
|
||||
animals
|
||||
} = require('unique-names-generator')
|
||||
|
||||
const homeDir = ''
|
||||
|
||||
@@ -1052,14 +766,36 @@ export default {
|
||||
projectReloading: false,
|
||||
enableDbEdit: 0,
|
||||
authTypes: [
|
||||
{ text: 'JWT', value: 'jwt' },
|
||||
{ text: 'Master Key', value: 'masterKey' },
|
||||
{ text: 'Middleware', value: 'middleware' },
|
||||
{ text: 'Disabled', value: 'none' }
|
||||
{
|
||||
text: 'JWT',
|
||||
value: 'jwt'
|
||||
},
|
||||
{
|
||||
text: 'Master Key',
|
||||
value: 'masterKey'
|
||||
},
|
||||
{
|
||||
text: 'Middleware',
|
||||
value: 'middleware'
|
||||
},
|
||||
{
|
||||
text: 'Disabled',
|
||||
value: 'none'
|
||||
}
|
||||
],
|
||||
projectTypes: [
|
||||
{ text: 'REST APIs', value: 'rest', icon: 'mdi-code-json', iconColor: 'green' },
|
||||
{ text: 'GRAPHQL APIs', value: 'graphql', icon: 'mdi-graphql', iconColor: 'pink' }
|
||||
{
|
||||
text: 'REST APIs',
|
||||
value: 'rest',
|
||||
icon: 'mdi-code-json',
|
||||
iconColor: 'green'
|
||||
},
|
||||
{
|
||||
text: 'GRAPHQL APIs',
|
||||
value: 'graphql',
|
||||
icon: 'mdi-graphql',
|
||||
iconColor: 'pink'
|
||||
}
|
||||
],
|
||||
|
||||
showPass: {},
|
||||
@@ -1115,8 +851,8 @@ export default {
|
||||
graphqlDepthLimit: 10
|
||||
},
|
||||
inflection: {
|
||||
tn: ['camelize'],
|
||||
cn: ['camelize']
|
||||
table_name: 'camelize',
|
||||
column_name: 'camelize'
|
||||
}
|
||||
},
|
||||
ui: {
|
||||
@@ -1374,7 +1110,10 @@ export default {
|
||||
if (this.project.projectType) {
|
||||
return this.projectTypes.find(({ value }) => value === this.project.projectType)
|
||||
} else {
|
||||
return { icon: 'mdi-server', iconColor: 'primary' }
|
||||
return {
|
||||
icon: 'mdi-server',
|
||||
iconColor: 'primary'
|
||||
}
|
||||
}
|
||||
},
|
||||
databaseNamesReverse() {
|
||||
@@ -1416,18 +1155,6 @@ export default {
|
||||
selectFile(db, obj, key, index) {
|
||||
this.$refs[key][index].click()
|
||||
|
||||
// console.log(obj, key);
|
||||
// const file = dialog.showOpenDialog({
|
||||
// properties: ["openFile"]
|
||||
// });
|
||||
// console.log(typeof file, file, typeof file[0]);
|
||||
// if (file && file[0]) {
|
||||
// let fileName = path.basename(file[0]);
|
||||
// db.ui[obj][key] = fileName;
|
||||
// Vue.set(db.ui[obj], key, fileName)
|
||||
// //db.connection[obj][key] = file[0].toString();
|
||||
// Vue.set(db.connection[obj], key, file[0].toString())
|
||||
// }
|
||||
},
|
||||
onPanelToggle(panelIndex, envKey) {
|
||||
this.$nextTick(() => {
|
||||
@@ -1455,20 +1182,15 @@ export default {
|
||||
Vue.set(this.databases, panelIndex, tabIndex)
|
||||
},
|
||||
getProjectJson() {
|
||||
console.log('Project json before creating', this.project)
|
||||
|
||||
/**
|
||||
* remove UI keys within project
|
||||
*/
|
||||
const xcConfig = JSON.parse(JSON.stringify(this.project))
|
||||
console.log(JSON.stringify(this.project))
|
||||
console.log('Project json after parsing', xcConfig)
|
||||
delete xcConfig.ui
|
||||
|
||||
for (const env in xcConfig.envs) {
|
||||
for (let i = 0; i < xcConfig.envs[env].db.length; ++i) {
|
||||
xcConfig.envs[env].db[i].meta.api.type = this.project.projectType
|
||||
console.log('getProjectJson:', env, i, xcConfig.envs[env].db[i])
|
||||
if (
|
||||
xcConfig.envs[env].db[i].client === 'mysql' ||
|
||||
xcConfig.envs[env].db[i].client === 'mysql2'
|
||||
@@ -1495,15 +1217,15 @@ export default {
|
||||
const inflectionObj = xcConfig.envs[env].db[i].meta.inflection
|
||||
|
||||
if (inflectionObj) {
|
||||
if (Array.isArray(inflectionObj.tn)) {
|
||||
inflectionObj.tn = inflectionObj.tn.join(',')
|
||||
if (Array.isArray(inflectionObj.table_name)) {
|
||||
inflectionObj.table_name = inflectionObj.table_name.join(',')
|
||||
}
|
||||
if (Array.isArray(inflectionObj.cn)) {
|
||||
inflectionObj.cn = inflectionObj.cn.join(',')
|
||||
if (Array.isArray(inflectionObj.column_name)) {
|
||||
inflectionObj.column_name = inflectionObj.column_name.join(',')
|
||||
}
|
||||
|
||||
inflectionObj.tn = inflectionObj.tn || 'none'
|
||||
inflectionObj.cn = inflectionObj.cn || 'none'
|
||||
inflectionObj.table_name = inflectionObj.table_name || 'none'
|
||||
inflectionObj.column_name = inflectionObj.column_name || 'none'
|
||||
}
|
||||
|
||||
if (this.allSchemas) {
|
||||
@@ -1563,14 +1285,11 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Project json : after', xcConfig)
|
||||
return xcConfig
|
||||
},
|
||||
|
||||
constructProjectJsonFromProject(project) {
|
||||
// const {projectJson: envs, ...rest} = project;
|
||||
|
||||
// let p = {...rest, ...envs};
|
||||
const p = project // JSON.parse(JSON.stringify(project.projectJson));
|
||||
|
||||
p.ui = {
|
||||
@@ -1651,50 +1370,48 @@ export default {
|
||||
|
||||
this.projectReloading = true
|
||||
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{
|
||||
query: {
|
||||
skipProjectHasDb: 1
|
||||
}
|
||||
},
|
||||
this.edit ? 'projectUpdateByWeb' : 'projectCreateByWeb',
|
||||
{
|
||||
project: {
|
||||
title: projectJson.title,
|
||||
folder: 'config.xc.json',
|
||||
type: 'pg'
|
||||
},
|
||||
projectJson
|
||||
const con = projectJson.envs._noco.db[0]
|
||||
const inflection = (con.meta && con.meta.inflection) || {}
|
||||
try {
|
||||
const result = (await this.$api.project.create({
|
||||
title: projectJson.title,
|
||||
bases: [{
|
||||
type: con.client,
|
||||
config: con,
|
||||
inflection_column: inflection.column_name,
|
||||
inflection_table: inflection.table_name
|
||||
}],
|
||||
external: true
|
||||
}))
|
||||
|
||||
clearInterval(interv)
|
||||
toast.goAway(100)
|
||||
|
||||
await this.$store.dispatch('project/ActLoadProjectInfo')
|
||||
|
||||
this.projectReloading = false
|
||||
|
||||
if (!this.edit && !this.allSchemas) {
|
||||
this.$router.push({
|
||||
path: `/nc/${result.id}`,
|
||||
query: {
|
||||
new: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
])
|
||||
|
||||
clearInterval(interv)
|
||||
toast.goAway(100)
|
||||
console.log('project created redirect to project page', projectJson, result)
|
||||
|
||||
await this.$store.dispatch('project/ActLoadProjectInfo')
|
||||
|
||||
this.projectReloading = false
|
||||
|
||||
if (!this.edit && !this.allSchemas) {
|
||||
this.$router.push({
|
||||
path: `/nc/${result.id}`,
|
||||
query: {
|
||||
new: 1
|
||||
}
|
||||
})
|
||||
this.projectCreated = true
|
||||
} catch (e) {
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
|
||||
this.projectCreated = true
|
||||
|
||||
this.projectReloading = false
|
||||
this.$tele.emit('project:create:extdb:submit')
|
||||
},
|
||||
|
||||
mtdDialogGetEnvNameSubmit(envName, cookie) {
|
||||
console.log(envName)
|
||||
this.dialogGetEnvName.dialogShow = false
|
||||
if (envName in this.project.envs) {
|
||||
console.log('Environment exists')
|
||||
} else {
|
||||
Vue.set(this.project.envs, envName, {
|
||||
db: [
|
||||
@@ -1711,8 +1428,8 @@ export default {
|
||||
tn: 'nc_evolutions',
|
||||
dbAlias: 'db',
|
||||
inflection: {
|
||||
tn: 'camelize',
|
||||
cn: 'camelize'
|
||||
table_name: 'camelize',
|
||||
column_name: 'camelize'
|
||||
},
|
||||
api: {
|
||||
type: ''
|
||||
@@ -1734,7 +1451,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mtdDialogGetEnvNameCancel() {
|
||||
console.log('mtdDialogGetTableNameCancel cancelled')
|
||||
this.dialogGetEnvName.dialogShow = false
|
||||
},
|
||||
|
||||
@@ -1761,8 +1477,8 @@ export default {
|
||||
tn: 'nc_evolutions',
|
||||
dbAlias,
|
||||
inflection: {
|
||||
tn: 'camelize',
|
||||
cn: 'camelize'
|
||||
table_name: 'camelize',
|
||||
column_name: 'camelize'
|
||||
},
|
||||
api: {
|
||||
type: ''
|
||||
@@ -1786,24 +1502,8 @@ export default {
|
||||
this.dialog.show = false
|
||||
},
|
||||
selectDir(ev) {
|
||||
// console.log(ev)
|
||||
// const file = dialog.showOpenDialog({
|
||||
// properties: ['openDirectory']
|
||||
// })
|
||||
// if (file && file[0]) {
|
||||
// this.baseFolder = file[0]
|
||||
// this.project.folder = file[0]
|
||||
// this.userSelectedDir = true
|
||||
// }
|
||||
},
|
||||
selectSqliteFile(db) {
|
||||
// console.log(ev)
|
||||
// const file = dialog.showOpenDialog({
|
||||
// properties: ["openFile"]
|
||||
// });
|
||||
// if (file && file[0]) {
|
||||
// db.connection.connection.filename = file[0];
|
||||
// }
|
||||
},
|
||||
|
||||
getDbStatusColor(db) {
|
||||
@@ -1838,7 +1538,6 @@ export default {
|
||||
}
|
||||
},
|
||||
async newTestConnection(db, env, panelIndex) {
|
||||
console.log(this.project.envs[env][0])
|
||||
if (
|
||||
db.connection.host === 'localhost' &&
|
||||
!this.edit &&
|
||||
@@ -1869,7 +1568,6 @@ export default {
|
||||
'testConnection',
|
||||
c1
|
||||
])
|
||||
console.log('test connection result', result)
|
||||
|
||||
if (result.code === 0) {
|
||||
db.ui.setup = 1
|
||||
@@ -1884,10 +1582,11 @@ export default {
|
||||
if (e === env) {
|
||||
// ignore
|
||||
} else {
|
||||
console.log(this.project.envs[e])
|
||||
|
||||
const c2 = {
|
||||
connection: { ...this.project.envs[e].db[0].connection, database: undefined },
|
||||
connection: {
|
||||
...this.project.envs[e].db[0].connection,
|
||||
database: undefined
|
||||
},
|
||||
client: this.project.envs[e].db[0].client
|
||||
}
|
||||
|
||||
@@ -1934,46 +1633,31 @@ export default {
|
||||
}
|
||||
let sendAdvancedConfig = false
|
||||
const sslOptions = Object.values(connection.ssl).filter(el => !!el)
|
||||
console.log('sslOptions:', sslOptions)
|
||||
if (sslOptions[0]) {
|
||||
sendAdvancedConfig = true
|
||||
} else {
|
||||
console.log('no ssl options')
|
||||
}
|
||||
return sendAdvancedConfig
|
||||
},
|
||||
|
||||
handleSSL(db, creating = true) {
|
||||
console.log('handleSSL', db)
|
||||
const sendAdvancedConfig = this.sendAdvancedConfig(db.connection)
|
||||
if (!sendAdvancedConfig) {
|
||||
// args.ssl = undefined;
|
||||
db.connection.ssl = undefined
|
||||
}
|
||||
|
||||
if (db.connection.ssl) {
|
||||
// db.connection.ssl.caFilePath = db.connection.ssl.ca;
|
||||
// db.connection.ssl.keyFilePath = db.connection.ssl.key;
|
||||
// db.connection.ssl.certFilePath = db.connection.ssl.cert;
|
||||
// if(creating) {
|
||||
// delete db.connection.ssl.ca;
|
||||
// delete db.connection.ssl.key;
|
||||
// delete db.connection.ssl.cert;
|
||||
// }
|
||||
}
|
||||
},
|
||||
getDatabaseForTestConnection(dbType) {
|
||||
},
|
||||
async testConnection(db, env, panelIndex) {
|
||||
this.$tele.emit('project:create:extdb:test-connection')
|
||||
this.$store.commit('notification/MutToggleProgressBar', true)
|
||||
try {
|
||||
if (!(await this.newTestConnection(db, env, panelIndex))) {
|
||||
// this.activeDbNode.testConnectionStatus = false;
|
||||
//
|
||||
|
||||
this.handleSSL(db)
|
||||
|
||||
console.log('testconnection params', db)
|
||||
if (db.client === 'sqlite3') {
|
||||
db.ui.setup = 1
|
||||
} else {
|
||||
@@ -1985,18 +1669,8 @@ export default {
|
||||
client: db.client
|
||||
}
|
||||
|
||||
// const result = await this.sqlMgr.testConnection(c1);
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{
|
||||
query: {
|
||||
skipProjectHasDb: 1
|
||||
}
|
||||
},
|
||||
'testConnection',
|
||||
c1
|
||||
])
|
||||
const result = (await this.$api.utils.testConnection(c1))
|
||||
|
||||
console.log('test connection result', result)
|
||||
if (result.code === 0) {
|
||||
db.ui.setup = 1
|
||||
// this.dialog.heading = "Connection was successful"
|
||||
@@ -2010,8 +1684,6 @@ export default {
|
||||
this.dialog.type = 'error'
|
||||
this.dialog.show = true
|
||||
}
|
||||
|
||||
console.log('testconnection params : after', db)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -2038,7 +1710,10 @@ export default {
|
||||
const db = this.project.envs[env].db[index]
|
||||
Vue.set(db, 'client', this.databaseNames[client])
|
||||
if (client !== 'Sqlite') {
|
||||
const { ssl, ...connectionDet } = this.sampleConnectionData[client]
|
||||
const {
|
||||
ssl,
|
||||
...connectionDet
|
||||
} = this.sampleConnectionData[client]
|
||||
|
||||
Vue.set(db, 'connection', {
|
||||
...connectionDet,
|
||||
@@ -2064,11 +1739,8 @@ export default {
|
||||
database: [this.project.folder, `${this.project.title}_${env}_${index + 1}`].join(
|
||||
'/'
|
||||
),
|
||||
// database: path.join(this.project.folder, `${this.project.title}_${env}_${index + 1}`),
|
||||
useNullAsDefault: true
|
||||
})
|
||||
// Vue.set(db.connection, 'connection', {filename: `${this.project.folder}/${this.project.title}_${env}_${index + 1}`})
|
||||
// Vue.set(db.connection, 'database', `${this.project.folder}/${this.project.title}_${env}_${index + 1}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2082,8 +1754,6 @@ export default {
|
||||
db.ui.setup = status
|
||||
},
|
||||
removeDBFromEnv(db, env, panelIndex, dbIndex) {
|
||||
console.log(db, env, panelIndex, dbIndex)
|
||||
|
||||
for (const env in this.project.envs) {
|
||||
if (this.project.envs[env].db.length > dbIndex) {
|
||||
this.project.envs[env].db.splice(dbIndex, 1)
|
||||
@@ -2095,7 +1765,10 @@ export default {
|
||||
Vue.set(this.project, 'envs', { ...this.project.envs })
|
||||
}
|
||||
},
|
||||
fetch({ store, params }) {
|
||||
fetch({
|
||||
store,
|
||||
params
|
||||
}) {
|
||||
},
|
||||
beforeCreated() {
|
||||
},
|
||||
@@ -2115,12 +1788,6 @@ export default {
|
||||
if (db.client !== 'sqlite3') {
|
||||
Vue.set(db.connection, 'database', `${this.project.title}_${env}_${index + 1}`)
|
||||
} else {
|
||||
// Vue.set(db.connection, 'connection', {
|
||||
// filename: path.join(
|
||||
// this.project.folder,
|
||||
// `${this.project.title}_${env}_${index + 1}`
|
||||
// )
|
||||
// })
|
||||
Vue.set(db.connection, 'database', `${this.project.title}_${env}_${index + 1}`)
|
||||
}
|
||||
}
|
||||
@@ -2168,12 +1835,9 @@ export default {
|
||||
this.compErrorMessages[Math.floor(Math.random() * this.compErrorMessages.length)]
|
||||
|
||||
if (this.edit) {
|
||||
// this.edit = true;
|
||||
// await this.$store.dispatch('sqlMgr/instantiateSqlMgr');
|
||||
try {
|
||||
let data = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcProjectGetConfig'])
|
||||
data = JSON.parse(data.config)
|
||||
console.log('created:', data)
|
||||
this.constructProjectJsonFromProject(data)
|
||||
this.$set(this.project, 'folder', data.folder)
|
||||
} catch (e) {
|
||||
@@ -2215,17 +1879,10 @@ export default {
|
||||
...this.sampleConnectionData[dbsAvailable[0]],
|
||||
ssl: { ...this.sampleConnectionData[dbsAvailable[0]].ssl }
|
||||
}
|
||||
// db.ui.setup = await PortScanner.isAlive(db.connection.host, parseInt(db.connection.port)) ? 0 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for (let env in this.project.envs) {
|
||||
// for (let db of this.project.envs[env]) {
|
||||
// db.ui.setup = await PortScanner.isAlive(db.connection.host, parseInt(db.connection.port)) ? 0 : -1;
|
||||
// console.log('testing port', env, db.connection.database, db.ui.setup);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
@@ -31,21 +31,6 @@
|
||||
<v-card-text class="align-self-end" v-text="item.description" />
|
||||
</v-card>
|
||||
</v-col>
|
||||
<!-- <v-col cols="3">-->
|
||||
<!-- <v-card>-->
|
||||
<!-- <v-img-->
|
||||
|
||||
<!-- class="white--text align-end"-->
|
||||
<!-- gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"-->
|
||||
<!-- height="200px"-->
|
||||
<!-- >-->
|
||||
<!-- <v-card-title>Push Notification</v-card-title>-->
|
||||
<!-- </v-img>-->
|
||||
<!-- <v-card-text>-->
|
||||
<!-- Google Firebase based cloud messaging service-->
|
||||
<!-- </v-card-text>-->
|
||||
<!-- </v-card>-->
|
||||
<!-- </v-col>-->
|
||||
</v-row>
|
||||
</v-expansion-panel-content>
|
||||
</v-expansion-panel>
|
||||
|
||||
@@ -168,10 +168,6 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import draggable from 'vuedraggable'
|
||||
//
|
||||
// const {promisify, fs, path, config, Handlebars} = require("electron").remote.require(
|
||||
// "./libs"
|
||||
// );
|
||||
export default {
|
||||
name: 'Environment',
|
||||
directives: {},
|
||||
@@ -293,7 +289,6 @@ export default {
|
||||
'utf-8')
|
||||
},
|
||||
async saveEnvironment (env) {
|
||||
console.log(env, this.envValues[env])
|
||||
try {
|
||||
let projectJsonPath, freshProjectObj
|
||||
|
||||
|
||||
46
packages/nc-gui/components/githubStarBtn.vue
Normal file
46
packages/nc-gui/components/githubStarBtn.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<gh-btns-star
|
||||
icon="mark-github"
|
||||
slug="nocodb/nocodb"
|
||||
show-count
|
||||
:class="{'dark' : isDark}"
|
||||
>
|
||||
{{ ghStarText }}
|
||||
</gh-btns-star>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GithubStarBtn',
|
||||
data: () => ({ ghStarText: 'Star' }),
|
||||
mounted() {
|
||||
setInterval(() => this.ghStarText = this.ghStarText === 'Star' ? 'Fork' : 'Star', 60000)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .gh-button-container{
|
||||
background: #fff2;
|
||||
border-radius: 4px;
|
||||
margin:0
|
||||
}
|
||||
/deep/ .gh-button-container:not(.dark) > a {
|
||||
background: transparent !important;
|
||||
color: #cdcdcd !important;
|
||||
}
|
||||
/deep/ .gh-button-container > a:first-child{
|
||||
border-left-color: transparent;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
/deep/ .gh-button-container > a:last-child{
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/deep/ .gh-button, /deep/ .social-count{
|
||||
padding:1px 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -225,64 +225,6 @@
|
||||
</template>
|
||||
</tbody>
|
||||
</v-simple-table>
|
||||
|
||||
<!-- <v-data-table-->
|
||||
<!-- :headers="headers"-->
|
||||
<!-- :items="acls"-->
|
||||
<!-- item-key="name"-->
|
||||
<!-- show-expand-->
|
||||
<!-- class="elevation-1"-->
|
||||
<!-- >-->
|
||||
|
||||
<!-- <template v-slot:expanded-item="{ headers, item }">-->
|
||||
<!-- <td :colspan="headers.length">More info about {{ item }}</td>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- <!– <template v-slot:item="{ headers, item }">–>-->
|
||||
<!-- <!– <tr>–>-->
|
||||
<!-- <!– <td>test</td>–>-->
|
||||
<!-- <!– </tr>–>-->
|
||||
<!-- <!– </template>–>-->
|
||||
|
||||
<!-- <!– <template v-slot:header="{ headers, item }">–>-->
|
||||
<!-- <!– <tr>–>-->
|
||||
<!-- <!– <th>test</th>–>-->
|
||||
<!-- <!– </tr>–>-->
|
||||
<!-- <!– </template>–>-->
|
||||
|
||||
<!-- </v-data-table>-->
|
||||
|
||||
<!-- <v-virtual-scroll-->
|
||||
<!-- :items="data"-->
|
||||
<!-- :item-height="50"-->
|
||||
<!-- height="100%"-->
|
||||
<!-- >-->
|
||||
<!-- <template v-slot="{ item }">-->
|
||||
<!-- <acl-ts-file-db-child-->
|
||||
<!-- v-if="data"-->
|
||||
<!-- :nodes="nodes" :policies="data"></acl-ts-file-db-child>-->
|
||||
<!-- </template>-->
|
||||
<!-- </v-virtual-scroll>-->
|
||||
|
||||
<!-- <v-tabs-->
|
||||
<!-- v-model="aclTabs"-->
|
||||
<!-- >-->
|
||||
<!-- <template v-for="table in tables">-->
|
||||
<!-- <v-tab :key="table">{{ table }}</v-tab>-->
|
||||
<!-- <!– <v-tab-item :key="table">–>-->
|
||||
<!-- <!– <acl-ts-file-db-child–>-->
|
||||
<!-- <!– v-if="i === aclTabs"–>-->
|
||||
<!-- <!– key="acl"–>-->
|
||||
<!-- <!– :nodes="nodes" :policies="item"></acl-ts-file-db-child>–>-->
|
||||
<!-- <!– </v-tab-item>–>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- </v-tabs>-->
|
||||
|
||||
<!-- <acl-ts-file-db-child-->
|
||||
<!-- key="acl"-->
|
||||
<!-- v-if="groupedData"-->
|
||||
<!-- :nodes="nodes" :policies="acls[aclTabs]"></acl-ts-file-db-child>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -334,8 +276,8 @@ export default {
|
||||
const groupedData = {}
|
||||
|
||||
for (const item of data) {
|
||||
groupedData[item.tn] = groupedData[item.tn] || []
|
||||
groupedData[item.tn].push(item)
|
||||
groupedData[item.table_name] = groupedData[item.table_name] || []
|
||||
groupedData[item.table_name].push(item)
|
||||
}
|
||||
|
||||
this.groupedData = groupedData
|
||||
|
||||
@@ -85,8 +85,6 @@ export default {
|
||||
}
|
||||
},
|
||||
dragOverHandler(ev) {
|
||||
console.log('File(s) in drop zone')
|
||||
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
@@ -282,7 +282,6 @@ export default {
|
||||
|
||||
dropHandler(ev) {
|
||||
this.dragOver = false
|
||||
console.log('File(s) dropped')
|
||||
let file
|
||||
if (ev.dataTransfer.items) {
|
||||
// Use DataTransferItemList interface to access the file(s)
|
||||
@@ -303,8 +302,6 @@ export default {
|
||||
this._file(file)
|
||||
},
|
||||
dragOverHandler(ev) {
|
||||
console.log('File(s) in drop zone')
|
||||
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault()
|
||||
},
|
||||
|
||||
@@ -49,11 +49,11 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
|
||||
const originalRows = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, cellDates: true, defval: null })
|
||||
|
||||
// fix precision bug & timezone offset issues introduced by xlsx
|
||||
const basedate = new Date(1899, 11, 30, 0, 0, 0);
|
||||
const basedate = new Date(1899, 11, 30, 0, 0, 0)
|
||||
// number of milliseconds since base date
|
||||
const dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
|
||||
const dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000
|
||||
// number of milliseconds in a day
|
||||
const day_ms = 24 * 60 * 60 * 1000;
|
||||
const day_ms = 24 * 60 * 60 * 1000
|
||||
// handle date1904 property
|
||||
const fixImportedDate = (date) => {
|
||||
const parsed = XLSX.SSF.parse_date_code((date.getTime() - dnthresh) / day_ms, {
|
||||
@@ -62,8 +62,8 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
|
||||
return new Date(parsed.y, parsed.m, parsed.d, parsed.H, parsed.M, parsed.S)
|
||||
}
|
||||
// fix imported date
|
||||
const rows = originalRows.map((r) => r.map((v) => {
|
||||
return v instanceof Date ? fixImportedDate(v): v
|
||||
const rows = originalRows.map(r => r.map((v) => {
|
||||
return v instanceof Date ? fixImportedDate(v) : v
|
||||
}))
|
||||
|
||||
const columnNameRowExist = +rows[0].every(v => v === null || typeof v === 'string')
|
||||
@@ -166,7 +166,7 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
|
||||
const rowData = {}
|
||||
for (let i = 0; i < table.columns.length; i++) {
|
||||
if (table.columns[i].uidt === UITypes.Checkbox) {
|
||||
rowData[table.columns[i].cn] = getCheckboxValue(row[i])
|
||||
rowData[table.columns[i].column_name] = getCheckboxValue(row[i])
|
||||
} else if (table.columns[i].uidt === UITypes.Currency) {
|
||||
const cellId = XLSX.utils.encode_cell({
|
||||
c: range.s.c + i,
|
||||
@@ -174,12 +174,12 @@ export default class ExcelTemplateAdapter extends TemplateGenerator {
|
||||
})
|
||||
|
||||
const cellObj = ws[cellId]
|
||||
rowData[table.columns[i].cn] = (cellObj && cellObj.w && cellObj.w.replace(/[^\d.]+/g, '')) || row[i]
|
||||
rowData[table.columns[i].column_name] = (cellObj && cellObj.w && cellObj.w.replace(/[^\d.]+/g, '')) || row[i]
|
||||
} else if (table.columns[i].uidt === UITypes.SingleSelect || table.columns[i].uidt === UITypes.MultiSelect) {
|
||||
rowData[table.columns[i].cn] = (row[i] || '').toString().trim() || null
|
||||
rowData[table.columns[i].column_name] = (row[i] || '').toString().trim() || null
|
||||
} else {
|
||||
// toto: do parsing if necessary based on type
|
||||
rowData[table.columns[i].cn] = row[i]
|
||||
rowData[table.columns[i].column_name] = row[i]
|
||||
}
|
||||
}
|
||||
this.data[tn].push(rowData)
|
||||
|
||||
@@ -32,14 +32,6 @@
|
||||
API Changes in v0.90.0
|
||||
</span>
|
||||
</v-list-item>
|
||||
<!-- <v-list-item dense href="#" target="_blank">
|
||||
<v-icon small class="mr-2">
|
||||
mdi-rocket-launch-outline
|
||||
</v-icon>
|
||||
<span class="caption">
|
||||
Migration Guide
|
||||
</span>
|
||||
</v-list-item> -->
|
||||
<v-list-item @click="announcementAlert = false">
|
||||
<v-icon small class="mr-2">
|
||||
mdi-close
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
export default {
|
||||
name: 'Loader',
|
||||
props: {
|
||||
// message: String,
|
||||
// progress: Number
|
||||
},
|
||||
computed: {
|
||||
message() {
|
||||
|
||||
@@ -47,7 +47,6 @@ export default {
|
||||
watch: {
|
||||
codeLocal(newValue) {
|
||||
// INFO: for updating value of prop `code` in parent comp
|
||||
// console.log("update:code Event Emitted", newValue);
|
||||
this.$emit('update:code', newValue)
|
||||
},
|
||||
code(newValue) {
|
||||
@@ -65,7 +64,6 @@ export default {
|
||||
const editor = this.$refs.editor.getMonaco()
|
||||
const range = editor.getSelection()
|
||||
const selectedText = editor.getModel().getValueInRange(range)
|
||||
// console.log('getValue', editor.getModel())
|
||||
this.selection = selectedText
|
||||
this.selectionRange = range
|
||||
},
|
||||
|
||||
@@ -245,9 +245,6 @@ export default {
|
||||
...(this.$store.getters['project/GtrProjectJson'].envs ?
|
||||
Object.keys(this.$store.getters['project/GtrProjectJson'].envs[this.env].api) : [])];
|
||||
|
||||
|
||||
console.log('============', this.envValues)
|
||||
|
||||
this.tokRef = monaco.languages.setMonarchTokensProvider('mySpecialLanguage', {
|
||||
tokenizer: {
|
||||
root: [
|
||||
|
||||
@@ -189,23 +189,15 @@ export default {
|
||||
this.codeLocal = newValue
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
// console.log(MonacoEditor)
|
||||
},
|
||||
created() {
|
||||
//
|
||||
},
|
||||
methods: {
|
||||
selectionFn() {
|
||||
const editor = this.$refs.editor.getMonaco()
|
||||
const range = editor.getSelection()
|
||||
const selectedText = editor.getModel().getValueInRange(range)
|
||||
// console.log('getValue', editor.getModel())
|
||||
this.selection = selectedText
|
||||
this.selectionRange = range
|
||||
},
|
||||
pretify() {
|
||||
// console.log("this.code", this.code);
|
||||
const editor = this.$refs.editor.getMonaco()
|
||||
|
||||
if (this.selection && this.selectionRange) {
|
||||
|
||||
167
packages/nc-gui/components/previewAs.vue
Normal file
167
packages/nc-gui/components/previewAs.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-menu offset-y>
|
||||
<template #activator="{on}">
|
||||
<v-btn
|
||||
v-show="isDashboard && _isUIAllowed('previewAs')"
|
||||
small
|
||||
light
|
||||
color="#fff3"
|
||||
class="white--text nc-btn-preview"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon small class="mr-1">
|
||||
mdi-play-circle
|
||||
</v-icon>
|
||||
Preview
|
||||
<v-icon small>
|
||||
mdi-menu-down
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<template v-for="(role) in rolesList">
|
||||
<v-list-item
|
||||
:key="role.title"
|
||||
:class="`pointer nc-preview-${role.title}`"
|
||||
@click="setPreviewUser(role.title)"
|
||||
>
|
||||
<v-list-item-title>
|
||||
<v-icon
|
||||
small
|
||||
class="mr-1"
|
||||
:color="role.title === previewAs ? 'x-active' : ''"
|
||||
>
|
||||
{{ roleIcon[role.title] }}
|
||||
</v-icon>
|
||||
<span
|
||||
class="caption text-capitalize"
|
||||
:class="{ 'x-active--text': role.title === previewAs }"
|
||||
>{{ role.title }}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<template v-if="previewAs">
|
||||
<!-- <v-divider></v-divider>-->
|
||||
<v-list-item @click="setPreviewUser(null)">
|
||||
<v-icon small class="mr-1">
|
||||
mdi-close
|
||||
</v-icon>
|
||||
<!-- Reset Preview -->
|
||||
<span class="caption nc-preview-reset">{{ $t('activity.resetReview') }}</span>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-menu
|
||||
:position-x="position.x"
|
||||
:position-y="position.y"
|
||||
:value="previewAs"
|
||||
activator=""
|
||||
:close-on-click="false"
|
||||
:close-on-content-click="false"
|
||||
>
|
||||
<div class="floating-reset-btn white py-1 pr-3 caption primary lighten-2 white--text font-weight-bold d-flex align-center nc-floating-preview-btn" style="overflow-y: hidden">
|
||||
<v-icon style="cursor: move" color="white" @mousedown="mouseDown">
|
||||
mdi-drag
|
||||
</v-icon>
|
||||
|
||||
<v-divider vertical class="mr-2" />
|
||||
|
||||
<div class="d-inline pointer d-flex align-center">
|
||||
<span>Preview as :</span>
|
||||
<v-radio-group
|
||||
:value="previewAs"
|
||||
dense
|
||||
row
|
||||
class="mt-0 pt-0"
|
||||
hide-details
|
||||
@change="setPreviewUser($event)"
|
||||
>
|
||||
<v-radio
|
||||
v-for="(role) in rolesList"
|
||||
:key="role.title"
|
||||
:value="role.title"
|
||||
color="white"
|
||||
dark
|
||||
:class="`ml-1 nc-floating-preview-${role.title}`"
|
||||
>
|
||||
<template #label>
|
||||
<span class="white--text caption text-capitalize">{{ role.title }}</span>
|
||||
</template>
|
||||
</v-radio>
|
||||
</v-radio-group>
|
||||
<v-divider vertical class="mr-2" />
|
||||
<span class="pointer" @click="setPreviewUser(null)"> <v-icon small color="white">mdi-exit-to-app</v-icon> Exit</span>
|
||||
</div>
|
||||
</div>
|
||||
</v-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PreviewAs',
|
||||
data: () => ({
|
||||
roleIcon: {
|
||||
owner: 'mdi-account-star',
|
||||
creator: 'mdi-account-hard-hat',
|
||||
editor: 'mdi-account-edit',
|
||||
viewer: 'mdi-eye-outline',
|
||||
commenter: 'mdi-comment-account-outline'
|
||||
},
|
||||
rolesList: [{ title: 'editor' }, { title: 'commenter' }, { title: 'viewer' }],
|
||||
position: {
|
||||
x: 9999, y: 9999
|
||||
}
|
||||
}),
|
||||
computed: {
|
||||
previewAs: {
|
||||
get() {
|
||||
return this.$store.state.users.previewAs
|
||||
},
|
||||
set(previewAs) {
|
||||
this.$store.commit('users/MutPreviewAs', previewAs)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.position = {
|
||||
y: window.innerHeight - 100,
|
||||
x: window.innerWidth / 2 - 250
|
||||
}
|
||||
|
||||
window.addEventListener('mouseup', this.mouseUp, false)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('mousemove', this.divMove, true)
|
||||
window.removeEventListener('mouseup', this.mouseUp, false)
|
||||
},
|
||||
methods: {
|
||||
setPreviewUser(previewAs) {
|
||||
this.$tele.emit(`preview-as:${previewAs}`)
|
||||
if (!process.env.EE) {
|
||||
this.$toast.info('Available in Enterprise edition').goAway(3000)
|
||||
} else {
|
||||
this.previewAs = previewAs
|
||||
window.location.reload()
|
||||
}
|
||||
},
|
||||
mouseUp() {
|
||||
window.removeEventListener('mousemove', this.divMove, true)
|
||||
},
|
||||
mouseDown(e) {
|
||||
window.addEventListener('mousemove', this.divMove, true)
|
||||
},
|
||||
|
||||
divMove(e) {
|
||||
this.position = { y: e.clientY - 10, x: e.clientX - 18 }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -782,7 +782,6 @@ export default {
|
||||
await this.loadFileCollection(this.$store.getters['apiClientSwagger/GtrCurrentApiFilePaths'][i])
|
||||
}
|
||||
|
||||
// console.log(this.$store.getters['apiClientSwagger/GtrCurrentApiFilePaths']);
|
||||
} catch (e) {
|
||||
console.log('Failed to load previously opened query collections', e)
|
||||
}
|
||||
@@ -794,7 +793,6 @@ export default {
|
||||
|
||||
this.api.meta = {}
|
||||
|
||||
console.log(this.nodes)
|
||||
try {
|
||||
const info = (await this.$axios.get('/nc/projectApiInfo', {
|
||||
headers: {
|
||||
@@ -818,7 +816,6 @@ export default {
|
||||
console.log('Node info : ', api)
|
||||
},
|
||||
async handleKeyDown ({ metaKey, key, altKey, shiftKey, ctrlKey }) {
|
||||
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
|
||||
// cmd + s -> save
|
||||
// cmd + l -> reload
|
||||
// cmd + n -> new
|
||||
@@ -857,7 +854,6 @@ export default {
|
||||
})
|
||||
|
||||
if (userChosenPath) {
|
||||
console.log(userChosenPath)
|
||||
fs.writeFileSync(userChosenPath, '[]', 'utf-8')
|
||||
const pathObj = {
|
||||
path: userChosenPath,
|
||||
@@ -1056,7 +1052,6 @@ export default {
|
||||
},
|
||||
|
||||
async ctxMenuClickHandler (actionEvent, index) {
|
||||
console.log(actionEvent, index)
|
||||
switch (actionEvent.value) {
|
||||
case 'add-folder':
|
||||
this.tvNodeFolderAdd(index)
|
||||
@@ -1112,8 +1107,6 @@ export default {
|
||||
},
|
||||
|
||||
async apiSend () {
|
||||
console.log('apiSend')
|
||||
|
||||
if (!this.api.path.trim()) {
|
||||
this.$toast.info('Please enter http url').goAway(3000)
|
||||
return
|
||||
@@ -1154,7 +1147,6 @@ export default {
|
||||
|
||||
async fileCollectionReload () {
|
||||
const data = new Tree(await this.apiFileCollection.read())
|
||||
console.log(data)
|
||||
|
||||
this.apiTv = data
|
||||
// this.$set(this, 'apiCollections', data);
|
||||
@@ -1167,12 +1159,10 @@ export default {
|
||||
},
|
||||
|
||||
async tvNodeRename (params) {
|
||||
console.log(params)
|
||||
await this.savefileCollections(this.curApiCollectionPanel)
|
||||
},
|
||||
|
||||
async onAddNode (params) {
|
||||
console.log(params)
|
||||
await this.savefileCollections(this.curApiCollectionPanel)
|
||||
},
|
||||
|
||||
@@ -1180,7 +1170,6 @@ export default {
|
||||
const { parent, children, ...params } = node
|
||||
this.currentNode = node
|
||||
|
||||
console.log(params)
|
||||
this.apiClickedOnList(params)
|
||||
// if (params.query) ;
|
||||
// this.selectQuery(params)
|
||||
|
||||
@@ -204,8 +204,6 @@ export default {
|
||||
_nodes.type = 'apiClientDir'
|
||||
_nodes.url = url
|
||||
const tabIndex = this.tabs.findIndex(el => el.key === _nodes.key)
|
||||
console.log('apis node', this.nodes)
|
||||
console.log('apiClient node', _nodes)
|
||||
if (tabIndex !== -1) {
|
||||
this.changeActiveTab(tabIndex)
|
||||
} else {
|
||||
|
||||
@@ -1,104 +1,109 @@
|
||||
<template>
|
||||
<div class="d-flex h-100 nc-app-store-tab">
|
||||
<v-dialog v-model="pluginInstallOverlay" min-width="400px" max-width="700px" min-height="300">
|
||||
<v-card
|
||||
v-if="installPlugin && pluginInstallOverlay"
|
||||
:dark="$store.state.windows.darkTheme"
|
||||
:light="!$store.state.windows.darkTheme"
|
||||
>
|
||||
<app-install :title="installPlugin.title" :default-config="defaultConfig" @close="pluginInstallOverlay = false" @saved="saved()" />
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<div>
|
||||
<h3 class="mb-5 title grey--text">
|
||||
Configure apps
|
||||
</h3>
|
||||
<v-divider />
|
||||
<div class="d-flex h-100 nc-app-store-tab mt-5">
|
||||
<v-dialog v-model="pluginInstallOverlay" min-width="400px" max-width="700px" min-height="300">
|
||||
<v-card
|
||||
v-if="installPlugin && pluginInstallOverlay"
|
||||
:dark="$store.state.windows.darkTheme"
|
||||
:light="!$store.state.windows.darkTheme"
|
||||
>
|
||||
<app-install :id="installPlugin.id" :default-config="defaultConfig" @close="pluginInstallOverlay = false" @saved="saved()" />
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<dlg-ok-new
|
||||
v-model="pluginUninstallModal"
|
||||
:heading="`Please click on submit to reset ${resetPluginRef && resetPluginRef.title}`"
|
||||
ok-label="Submit"
|
||||
type="primary"
|
||||
@ok="confirmResetPlugin"
|
||||
/>
|
||||
<dlg-ok-new
|
||||
v-model="pluginUninstallModal"
|
||||
:heading="`Please click on submit to reset ${resetPluginRef && resetPluginRef.title}`"
|
||||
ok-label="Submit"
|
||||
type="primary"
|
||||
@ok="confirmResetPlugin"
|
||||
/>
|
||||
|
||||
<v-dialog min-width="400px" max-width="700px" min-height="300">
|
||||
<v-card
|
||||
v-if="resetPluginRef"
|
||||
:dark="$store.state.windows.darkTheme"
|
||||
:light="!$store.state.windows.darkTheme"
|
||||
>
|
||||
<v-card-text> Please confirm to reset {{ resetPluginRef.title }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" @click="confirmResetPlugin">
|
||||
Yes
|
||||
</v-btn>
|
||||
<v-btn @click="pluginUninstallModal = false">
|
||||
No
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog min-width="400px" max-width="700px" min-height="300">
|
||||
<v-card
|
||||
v-if="resetPluginRef"
|
||||
:dark="$store.state.windows.darkTheme"
|
||||
:light="!$store.state.windows.darkTheme"
|
||||
>
|
||||
<v-card-text> Please confirm to reset {{ resetPluginRef.title }}</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" @click="confirmResetPlugin">
|
||||
Yes
|
||||
</v-btn>
|
||||
<v-btn @click="pluginUninstallModal = false">
|
||||
No
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-container class="h-100 app-container">
|
||||
<v-row class="d-flex align-stretch">
|
||||
<v-col v-for="(app,i) in filteredApps" :key="i" class="" cols="6">
|
||||
<!-- @click="installApp(app)"-->
|
||||
<v-container class="h-100 app-container">
|
||||
<v-row class="d-flex align-stretch">
|
||||
<v-col v-for="(app,i) in filteredApps" :key="i" class="" cols="6">
|
||||
<!-- @click="installApp(app)"-->
|
||||
|
||||
<v-card
|
||||
height="100%"
|
||||
class="elevatio app-item-card "
|
||||
>
|
||||
<div class="install-btn ">
|
||||
<v-btn
|
||||
v-if="app.parsedInput"
|
||||
x-small
|
||||
outlined
|
||||
class=" caption text-capitalize"
|
||||
@click="installApp(app)"
|
||||
>
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-pencil
|
||||
</v-icon>
|
||||
{{ $t('general.edit') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="app.parsedInput"
|
||||
x-small
|
||||
outlined
|
||||
class="caption text-capitalize"
|
||||
@click="resetApp(app)"
|
||||
>
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-close-circle-outline
|
||||
</v-icon>
|
||||
Reset
|
||||
</v-btn>
|
||||
<v-btn v-else x-small outlined class=" caption text-capitalize" @click="installApp(app)">
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
Install
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-card
|
||||
height="100%"
|
||||
class="elevatio app-item-card "
|
||||
>
|
||||
<div class="install-btn ">
|
||||
<v-btn
|
||||
v-if="app.parsedInput"
|
||||
x-small
|
||||
outlined
|
||||
class=" caption text-capitalize"
|
||||
@click="installApp(app)"
|
||||
>
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-pencil
|
||||
</v-icon>
|
||||
{{ $t('general.edit') }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="app.parsedInput"
|
||||
x-small
|
||||
outlined
|
||||
class="caption text-capitalize"
|
||||
@click="resetApp(app)"
|
||||
>
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-close-circle-outline
|
||||
</v-icon>
|
||||
Reset
|
||||
</v-btn>
|
||||
<v-btn v-else x-small outlined class=" caption text-capitalize" @click="installApp(app)">
|
||||
<v-icon x-small class="mr-1">
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
Install
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-no-wrap">
|
||||
<v-avatar
|
||||
class="ma-3 align-self-center"
|
||||
size="50"
|
||||
tile
|
||||
:color="app.title === 'SES' ? '#242f3e' : ''"
|
||||
>
|
||||
<v-img v-if="app.logo" :src="app.logo" contain />
|
||||
<v-icon v-else-if="app.icon" color="#242f3e" size="50">
|
||||
{{ app.icon }}
|
||||
</v-icon>
|
||||
</v-avatar>
|
||||
<div class="flex-grow-1">
|
||||
<v-card-title
|
||||
class="title "
|
||||
v-text="app.title"
|
||||
/>
|
||||
<div class="d-flex flex-no-wrap">
|
||||
<v-avatar
|
||||
class="ma-3 align-self-center"
|
||||
size="50"
|
||||
tile
|
||||
:color="app.title === 'SES' ? '#242f3e' : ''"
|
||||
>
|
||||
<v-img v-if="app.logo" :src="app.logo" contain />
|
||||
<v-icon v-else-if="app.icon" color="#242f3e" size="50">
|
||||
{{ app.icon }}
|
||||
</v-icon>
|
||||
</v-avatar>
|
||||
<div class="flex-grow-1">
|
||||
<v-card-title
|
||||
class="title "
|
||||
v-text="app.title"
|
||||
/>
|
||||
|
||||
<v-card-subtitle class="pb-1" v-text="app.description" />
|
||||
<v-card-actions>
|
||||
<div class="d-flex justify-space-between d-100 align-center">
|
||||
<v-card-subtitle class="pb-1" v-text="app.description" />
|
||||
<v-card-actions>
|
||||
<div class="d-flex justify-space-between d-100 align-center">
|
||||
<!-- <v-rating-->
|
||||
<!-- full-icon="mdi-star"-->
|
||||
<!-- readonly-->
|
||||
@@ -109,8 +114,8 @@
|
||||
|
||||
<!-- <span class="subtitles" v-if="app.price && app.price !== 'Free'">${{ app.price }} / mo</span>-->
|
||||
<!-- <span class="subtitles" v-else>Free</span>-->
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
|
||||
<!-- <v-card-actions>-->
|
||||
<!-- <v-btn-->
|
||||
@@ -121,50 +126,51 @@
|
||||
<!-- Download-->
|
||||
<!-- </v-btn>-->
|
||||
<!-- </v-card-actions>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
||||
<v-navigation-drawer width="300" class="pa-1">
|
||||
<v-text-field
|
||||
v-model="query"
|
||||
dense
|
||||
hide-details
|
||||
:placeholder="$t('placeholder.searchApps')"
|
||||
color="primary"
|
||||
class="search-field caption"
|
||||
>
|
||||
<template #prepend-inner>
|
||||
<v-icon small class="mt-1">
|
||||
mdi-magnify
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<!-- <v-navigation-drawer width="300" class="pa-1">
|
||||
<v-text-field
|
||||
v-model="query"
|
||||
dense
|
||||
hide-details
|
||||
:placeholder="$t('placeholder.searchApps')"
|
||||
color="primary"
|
||||
class="search-field caption"
|
||||
>
|
||||
<template #prepend-inner>
|
||||
<v-icon small class="mt-1">
|
||||
mdi-magnify
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<v-list dense>
|
||||
<v-list-item v-for="filter of filters" :key="filter" dense>
|
||||
<v-checkbox
|
||||
v-model="selectedTags"
|
||||
class="pt-0 mt-0"
|
||||
:value="filter"
|
||||
hide-details
|
||||
dense
|
||||
:label="filter"
|
||||
>
|
||||
<template #label>
|
||||
<v-icon small class="mr-1">
|
||||
{{ icons[filter] }}
|
||||
</v-icon>
|
||||
<v-list dense>
|
||||
<v-list-item v-for="filter of filters" :key="filter" dense>
|
||||
<v-checkbox
|
||||
v-model="selectedTags"
|
||||
class="pt-0 mt-0"
|
||||
:value="filter"
|
||||
hide-details
|
||||
dense
|
||||
:label="filter"
|
||||
>
|
||||
<template #label>
|
||||
<v-icon small class="mr-1">
|
||||
{{ icons[filter] }}
|
||||
</v-icon>
|
||||
|
||||
{{ filter }}
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
{{ filter }}
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -218,26 +224,28 @@ export default {
|
||||
},
|
||||
async confirmResetPlugin() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginSet', {
|
||||
await this.$api.plugin.update(this.resetPluginRef.id, {
|
||||
input: null,
|
||||
id: this.resetPluginRef.id,
|
||||
title: this.resetPluginRef.title,
|
||||
uninstall: true
|
||||
}])
|
||||
|
||||
active: 0
|
||||
})
|
||||
this.$toast.success('Plugin uninstalled successfully').goAway(5000)
|
||||
await this.loadPluginList()
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit(`appstore:reset:${this.resetPluginRef.title}`)
|
||||
},
|
||||
async saved() {
|
||||
this.pluginInstallOverlay = false
|
||||
await this.loadPluginList()
|
||||
this.$tele.emit(`appstore:install:submit:${this.installPlugin.title}`)
|
||||
},
|
||||
async installApp(app) {
|
||||
this.pluginInstallOverlay = true
|
||||
this.installPlugin = app
|
||||
|
||||
this.$tele.emit(`appstore:install:trigger:${app.title}`)
|
||||
},
|
||||
async resetApp(app) {
|
||||
this.pluginUninstallModal = true
|
||||
@@ -245,8 +253,9 @@ export default {
|
||||
},
|
||||
async loadPluginList() {
|
||||
try {
|
||||
const plugins = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginList'])
|
||||
plugins.push(...plugins.splice(0, 3))
|
||||
// const plugins = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginList'])
|
||||
const plugins = (await this.$api.plugin.list()).list
|
||||
// plugins.push(...plugins.splice(0, 3))
|
||||
this.apps = plugins.map((p) => {
|
||||
p.tags = p.tags ? p.tags.split(',') : []
|
||||
p.parsedInput = p.input && JSON.parse(p.input)
|
||||
@@ -261,9 +270,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title {
|
||||
color: var(--v-textColor-ligten2) !important;
|
||||
}
|
||||
|
||||
.app-item-card {
|
||||
transition: .4s background-color;
|
||||
|
||||
@@ -96,7 +96,7 @@ import FormInput from '@/components/project/appStore/FormInput'
|
||||
export default {
|
||||
name: 'AppInstall',
|
||||
components: { FormInput },
|
||||
props: ['title', 'defaultConfig'],
|
||||
props: ['id', 'defaultConfig'],
|
||||
data: () => ({
|
||||
plugin: null,
|
||||
formDetails: null,
|
||||
@@ -153,34 +153,41 @@ export default {
|
||||
},
|
||||
async saveSettings() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginSet', {
|
||||
input: this.settings,
|
||||
id: this.pluginId,
|
||||
title: this.plugin.title
|
||||
}])
|
||||
await this.$api.plugin.update(this.id, {
|
||||
input: JSON.stringify(this.settings),
|
||||
active: 1
|
||||
})
|
||||
|
||||
this.$emit('saved')
|
||||
this.$toast.success(this.formDetails.msgOnInstall || 'Plugin settings saved successfully').goAway(5000)
|
||||
this.simpleAnim()
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
const e = await this._extractSdkResponseError(_e)
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
},
|
||||
async testSettings() {
|
||||
this.testing = true
|
||||
try {
|
||||
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginTest', {
|
||||
// const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginTest', {
|
||||
// input: this.settings,
|
||||
// id: this.pluginId,
|
||||
// category: this.plugin.category,
|
||||
// title: this.plugin.title
|
||||
// }])
|
||||
const res = (await this.$api.plugin.test({
|
||||
input: this.settings,
|
||||
id: this.pluginId,
|
||||
category: this.plugin.category,
|
||||
title: this.plugin.title
|
||||
}])
|
||||
}))
|
||||
if (res) {
|
||||
this.$toast.success('Successfully tested plugin settings').goAway(3000)
|
||||
} else {
|
||||
this.$toast.info('Invalid credentials').goAway(3000)
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
const e = await this._extractSdkResponseError(_e)
|
||||
this.$toast[e.message === 'Not implemented' ? 'info' : 'error'](e.message).goAway(3000)
|
||||
}
|
||||
this.testing = false
|
||||
@@ -199,9 +206,10 @@ export default {
|
||||
},
|
||||
async readPluginDetails() {
|
||||
try {
|
||||
this.plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
|
||||
title: this.title
|
||||
}])
|
||||
// this.plugin = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcPluginRead', {
|
||||
// title: this.title
|
||||
// }])
|
||||
this.plugin = (await this.$api.plugin.read(this.id))
|
||||
this.formDetails = JSON.parse(this.plugin.input_schema)
|
||||
this.pluginId = this.plugin.id
|
||||
this.settings = JSON.parse(this.plugin.input) || (this.formDetails.array ? [{}] : {})
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<audit :nodes="nodes" />
|
||||
</v-tab-item>
|
||||
|
||||
<v-tab>
|
||||
<!-- <v-tab>
|
||||
<span class="caption text-capitalize">
|
||||
<!--SQL Migrations-->
|
||||
<!–SQL Migrations–>
|
||||
{{ $t('title.sqlMigrations') }}
|
||||
</span>
|
||||
</v-tab>
|
||||
@@ -20,7 +20,7 @@
|
||||
<sql-log-and-output>
|
||||
<db :nodes="nodes" />
|
||||
</sql-log-and-output>
|
||||
</v-tab-item>
|
||||
</v-tab-item>-->
|
||||
</v-tabs>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="h-100" style="overflow: auto">
|
||||
<v-toolbar height="30">
|
||||
<v-toolbar height="30" class="elevation-0">
|
||||
<v-spacer />
|
||||
<v-btn x-small outlined @click="loadAudits">
|
||||
<v-btn small outlined @click="loadAudits">
|
||||
<v-icon small class="mr-2">
|
||||
refresh
|
||||
</v-icon>
|
||||
@@ -101,12 +101,20 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadAudits() {
|
||||
const { list, count } = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcAuditList', {
|
||||
limit: this.limit,
|
||||
offset: this.limit * (this.page - 1)
|
||||
}])
|
||||
// const { list, count } = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'xcAuditList', {
|
||||
// limit: this.limit,
|
||||
// offset: this.limit * (this.page - 1)
|
||||
// }])
|
||||
const {
|
||||
list, pageInfo
|
||||
} = (await this.$api.project.auditList(
|
||||
this.$store.state.project.projectId, {
|
||||
limit: this.limit,
|
||||
offset: this.limit * (this.page - 1)
|
||||
}))
|
||||
|
||||
this.audits = list
|
||||
this.count = count
|
||||
this.count = pageInfo.totalRows
|
||||
},
|
||||
calculateDiff(date) {
|
||||
return dayjs.utc(date).fromNow()
|
||||
|
||||
@@ -329,7 +329,6 @@ export default {
|
||||
// },
|
||||
|
||||
isMigrationButtonEnabled(name) {
|
||||
console.log('menu -- - ', name)
|
||||
return this.nodes.dbConnection.client === 'sqlite3' && name === 'Migration Down'
|
||||
},
|
||||
|
||||
@@ -337,7 +336,6 @@ export default {
|
||||
this.selectedMigration.migration = ''
|
||||
this.selectedMigration.up = ''
|
||||
this.selectedMigration.down = ''
|
||||
console.log(migration)
|
||||
this.selectedMigration.migration = migration
|
||||
// let result = await this.sqlMgr.migrator().migrationsToSql({
|
||||
// env: this.nodes.env,
|
||||
@@ -360,7 +358,6 @@ export default {
|
||||
title: migration.title,
|
||||
titleDown: migration.titleDown
|
||||
}])
|
||||
console.log(result)
|
||||
this.selectedMigration.up = result.data.object.up
|
||||
this.selectedMigration.down = result.data.object.down
|
||||
},
|
||||
@@ -377,7 +374,6 @@ export default {
|
||||
onlyList: true
|
||||
}
|
||||
|
||||
console.log('this.currentProjectFolder', this.currentProjectFolder)
|
||||
// let result = await this.sqlMgr.migrator().migrationsList(migrationArgs);
|
||||
// let result = await this.sqlMgr.sqlOp(null, 'migrationsList', migrationArgs);
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'migrationsList', migrationArgs])
|
||||
@@ -394,7 +390,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('loadEnv: ', result)
|
||||
this.tableMigrationFiles.data = result.data.object.list
|
||||
this.tableMigrationFiles.status = result.data.object.pending
|
||||
if (this.tableMigrationFiles.data[0]) {
|
||||
@@ -412,20 +407,6 @@ export default {
|
||||
},
|
||||
async migrationUp(steps = 99999999999) {
|
||||
try {
|
||||
// await this.sqlMgr.migrator().migrationsUp({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// migrationSteps: steps,
|
||||
// folder: this.currentProjectFolder,
|
||||
// sqlContentMigrate: 1
|
||||
// });
|
||||
// await this.sqlMgr.sqlOp(null, 'migrationsUp', {
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// migrationSteps: steps,
|
||||
// folder: this.currentProjectFolder,
|
||||
// sqlContentMigrate: 1
|
||||
// });
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'migrationsUp', {
|
||||
env: this.nodes.env,
|
||||
@@ -442,20 +423,6 @@ export default {
|
||||
},
|
||||
async migrationDown(steps = 99999999999) {
|
||||
try {
|
||||
// await this.sqlMgr.migrator().migrationsDown({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// migrationSteps: steps,
|
||||
// folder: this.currentProjectFolder,
|
||||
// sqlContentMigrate: 1
|
||||
// });
|
||||
// await this.sqlMgr.sqlOp(null, 'migrationsDown', {
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// migrationSteps: steps,
|
||||
// folder: this.currentProjectFolder,
|
||||
// sqlContentMigrate: 1
|
||||
// });
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'migrationsDown', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
|
||||
@@ -362,18 +362,12 @@ export default {
|
||||
return false
|
||||
})
|
||||
|
||||
// await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
|
||||
// env: this.$store.getters['project/GtrEnv']
|
||||
// }, 'xcCronSave', this.selectedItem]);
|
||||
if (!errorCrons.length) {
|
||||
for (const cron of saveList) {
|
||||
// if (cron !== this.selectedItem && !cron.id) {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
|
||||
env: this.$store.getters['project/GtrEnv']
|
||||
}, 'xcCronSave', cron])
|
||||
// }
|
||||
}
|
||||
|
||||
await this.loadCrons()
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
</template>
|
||||
<v-card class="elevation-20">
|
||||
<v-card-title class="grey darken-2 subheading" style="height:30px">
|
||||
<!-- {{ this.heading }} for {{ this.column.cn }} -->
|
||||
<!-- {{ this.heading }} for {{ this.column.column_name }} -->
|
||||
</v-card-title>
|
||||
<v-form v-model="valid">
|
||||
<v-card-text class="pt-4 pl-4">
|
||||
<p class="headline">
|
||||
{{ heading }} for {{ column.cn }}
|
||||
{{ heading }} for {{ column.column_name }}
|
||||
</p>
|
||||
<v-row
|
||||
justify="space-between"
|
||||
@@ -30,7 +30,7 @@
|
||||
label="Select Reference Table"
|
||||
:full-width="false"
|
||||
:items="refTables"
|
||||
item-text="tn"
|
||||
item-text="table_name"
|
||||
required
|
||||
dense
|
||||
/>
|
||||
@@ -43,7 +43,7 @@
|
||||
label="Select Reference Column"
|
||||
:full-width="false"
|
||||
:items="refColumns"
|
||||
item-text="cn"
|
||||
item-text="column_name"
|
||||
required
|
||||
dense
|
||||
/>
|
||||
@@ -129,8 +129,8 @@ export default {
|
||||
'SET DEFAULT'
|
||||
],
|
||||
relation: {
|
||||
childColumn: this.column.cn,
|
||||
childTable: this.nodes.tn,
|
||||
childColumn: this.column.column_name,
|
||||
childTable: this.nodes.table_name,
|
||||
parentTable: this.column.rtn || '',
|
||||
parentColumn: this.column.rcn || '',
|
||||
onDelete: 'CASCADE',
|
||||
@@ -154,18 +154,18 @@ export default {
|
||||
// dbAlias: this.nodes.dbAlias
|
||||
// });
|
||||
// const result = await client.columnList({
|
||||
// tn: this.relation.parentTable
|
||||
// table_name: this.relation.parentTable
|
||||
// });
|
||||
|
||||
// const result = await this.sqlMgr.sqlOp({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias
|
||||
// }, 'columnList', { tn: this.relation.parentTable})
|
||||
// }, 'columnList', { table_name: this.relation.parentTable})
|
||||
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'columnList', { tn: this.relation.parentTable }])
|
||||
}, 'columnList', { table_name: this.relation.parentTable }])
|
||||
|
||||
const columns = result.data.list
|
||||
this.refColumns = JSON.parse(JSON.stringify(columns))
|
||||
@@ -177,7 +177,7 @@ export default {
|
||||
} else {
|
||||
// find pk column and assign to parentColumn
|
||||
const pkKeyColumns = this.refColumns.filter(el => el.pk)
|
||||
this.relation.parentColumn = (pkKeyColumns[0] || {}).cn || ''
|
||||
this.relation.parentColumn = (pkKeyColumns[0] || {}).column_name || ''
|
||||
}
|
||||
|
||||
this.isRefColumnsLoading = false
|
||||
@@ -219,11 +219,11 @@ export default {
|
||||
await this.loadTablesList()
|
||||
|
||||
if (!this.relation.parentTable) {
|
||||
let tn = (this.refTables[0] || {}).tn || ''
|
||||
if (tn === 'nc_evolutions' || tn === '_evolutions') {
|
||||
tn = (this.refTables[1] || {}).tn || ''
|
||||
let table_name = (this.refTables[0] || {}).table_name || ''
|
||||
if (table_name === 'nc_evolutions' || table_name === '_evolutions') {
|
||||
table_name = (this.refTables[1] || {}).table_name || ''
|
||||
}
|
||||
this.relation.parentTable = tn
|
||||
this.relation.parentTable = table_name
|
||||
}
|
||||
if (this.column.rtn) {
|
||||
this.relation.parentTable = this.column.rtn
|
||||
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
triggerTimingOptions: ['BEFORE', 'AFTER'],
|
||||
triggerEventOptions: ['INSERT', 'UPDATE', 'DELETE'],
|
||||
trigger: {
|
||||
tn: this.nodes.tn,
|
||||
table_name: this.nodes.table_name,
|
||||
trigger_name: this.triggerObject.trigger || '',
|
||||
timing: this.triggerObject.timing || 'BEFORE',
|
||||
event: this.triggerObject.event || 'INSERT',
|
||||
|
||||
@@ -100,7 +100,6 @@ export default {
|
||||
}),
|
||||
|
||||
async handleKeyDown({ metaKey, key, altKey, shiftKey, ctrlKey }) {
|
||||
console.log(metaKey, key, altKey, shiftKey, ctrlKey)
|
||||
// cmd + s -> save
|
||||
// cmd + l -> reload
|
||||
// cmd + n -> new
|
||||
@@ -172,7 +171,6 @@ export default {
|
||||
...this.nodes
|
||||
}
|
||||
})
|
||||
console.log('create function result', result)
|
||||
this.newFunction = false
|
||||
this.oldCreateFunction = `${this.functionData.create_function}` + ''
|
||||
this.$toast.success('Function created successfully').goAway(3000)
|
||||
@@ -197,7 +195,6 @@ export default {
|
||||
}])
|
||||
|
||||
this.oldCreateFunction = `${this.functionData.create_function}` + ''
|
||||
console.log('update function result', result)
|
||||
this.$toast.success('Function updated successfully').goAway(3000)
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, this.isMiddleware ? 'defaultResolverMiddlewareCode' : 'defaultResolverHandlerCodeGet', {
|
||||
tn: this.nodes.tn || this.nodes.view_name,
|
||||
table_name: this.nodes.table_name || this.nodes.view_name,
|
||||
resolver: this.resolver
|
||||
}])
|
||||
if (functionCode) {
|
||||
@@ -95,7 +95,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, this.isMiddleware ? 'xcResolverMiddlewareUpdate' : 'xcResolverHandlerUpdate', {
|
||||
tn: this.nodes.tn || this.nodes.view_name,
|
||||
table_name: this.nodes.table_name || this.nodes.view_name,
|
||||
resolver: this.resolver,
|
||||
functions: [this.code]
|
||||
}])
|
||||
|
||||
@@ -73,7 +73,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'defaultRpcServiceCodeGet', {
|
||||
tn: this.nodes.tn || this.nodes.view_name,
|
||||
tn: this.nodes.table_name || this.nodes.view_name,
|
||||
service: this.service,
|
||||
relation_type: this.serviceData.relation_type,
|
||||
tnc: this.serviceData.tnc
|
||||
@@ -92,7 +92,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcRpcHandlerUpdate', {
|
||||
tn: this.nodes.tn || this.nodes.view_name,
|
||||
tn: this.nodes.table_name || this.nodes.view_name,
|
||||
service: this.service,
|
||||
functions: [this.code]
|
||||
}])
|
||||
|
||||
@@ -16,66 +16,25 @@
|
||||
</div>
|
||||
</v-tab-item>
|
||||
|
||||
<template v-for="(db,i) in dbAliasList">
|
||||
<v-tab :key="db.meta.dbAlias + i" :href="'#' + db.meta.dbAlias" class="text-capitalize caption nc-meta-mgmt-metadata-tab">
|
||||
<!-- {{ db.connection.database | extractDbName }} {{ db.meta.dbAlias }} -->
|
||||
<template v-for="(db,i) in bases">
|
||||
<v-tab :key="db.id + i" :href="'#' + db.id + 'meta'" class="text-capitalize caption nc-meta-mgmt-metadata-tab">
|
||||
<!-- {{ db.connection.database | extractDbName }} {{ db.id }} -->
|
||||
<!-- Metadata -->
|
||||
{{ $t('title.metadata') }}
|
||||
</v-tab>
|
||||
<v-tab-item :key="db.meta.dbAlias + 't' + i" :value=" db.meta.dbAlias">
|
||||
<disable-or-enable-tables
|
||||
<v-tab-item :key="db.id + 't' + i" :value="db.id + 'meta'">
|
||||
<metaDiffSync
|
||||
:nodes="nodes"
|
||||
:db="db"
|
||||
:db-alias="db.meta.dbAlias"
|
||||
:db-id="db.id"
|
||||
/>
|
||||
<!-- <v-tabs color="x-active" height="28">
|
||||
<v-tab class="text-capitalize caption">
|
||||
Tables
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<disable-or-enable-tables
|
||||
:nodes="nodes"
|
||||
:db="db"
|
||||
:db-alias="db.meta.dbAlias"
|
||||
/>
|
||||
</v-tab-item>
|
||||
<!– enable extra –>
|
||||
<v-tab class="text-capitalize caption">
|
||||
Views
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<disable-or-enable-views
|
||||
:nodes="nodes"
|
||||
:db="db"
|
||||
:db-alias="db.meta.dbAlias"
|
||||
/>
|
||||
</v-tab-item>
|
||||
<!– <v-tab class="text-capitalize caption">Functions</v-tab>
|
||||
<v-tab-item>
|
||||
<disable-or-enable-functions :nodes="nodes" :db="db"
|
||||
:db-alias="db.meta.dbAlias"></disable-or-enable-functions>
|
||||
</v-tab-item>
|
||||
<v-tab class="text-capitalize caption">Procedures</v-tab>
|
||||
<v-tab-item>
|
||||
<disable-or-enable-procedures :nodes="nodes" :db="db"
|
||||
:db-alias="db.meta.dbAlias"></disable-or-enable-procedures>
|
||||
</v-tab-item>–>
|
||||
|
||||
<v-tab class="text-capitalize caption">
|
||||
Relations
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<disable-or-enable-relations :nodes="nodes" :db-alias="db.meta.dbAlias" />
|
||||
</v-tab-item>
|
||||
</v-tabs>-->
|
||||
</v-tab-item>
|
||||
<template v-if="uiacl">
|
||||
<v-tab :key="db.meta.dbAlias + 'acl'" :href="'#' + db.meta.dbAlias + 'acl'" class="text-capitalize caption nc-ui-acl-tab">
|
||||
<!-- {{ db.connection.database | extractDbName }}-->
|
||||
<v-tab :key="db.id + 'acl'" :href="'#' + db.id + 'acl'" class="text-capitalize caption nc-ui-acl-tab">
|
||||
<!--UI Access Control-->
|
||||
{{ $t('title.uiACL') }}
|
||||
</v-tab>
|
||||
<v-tab-item :key="db.meta.dbAlias + 'aclt'" :value=" db.meta.dbAlias + 'acl'">
|
||||
<v-tab-item :key="db.id + 'aclt'" :value=" db.id + 'acl'">
|
||||
<v-tabs color="x-active" height="28">
|
||||
<v-tab class="text-capitalize caption">
|
||||
<!-- Tables -->
|
||||
@@ -85,37 +44,9 @@
|
||||
<toggle-table-ui-acl
|
||||
:nodes="nodes"
|
||||
:db="db"
|
||||
:db-alias="db.meta.dbAlias"
|
||||
:db-alias="db.id"
|
||||
/>
|
||||
</v-tab-item>
|
||||
<!-- enable extra -->
|
||||
<!-- <v-tab class="text-capitalize caption">Views</v-tab>
|
||||
<v-tab-item>
|
||||
<toggle-view-ui-acl :nodes="nodes" :db="db"
|
||||
:db-alias="db.meta.dbAlias"></toggle-view-ui-acl>
|
||||
</v-tab-item>
|
||||
<v-tab class="text-capitalize caption">Functions</v-tab>
|
||||
<v-tab-item>
|
||||
<toggle-function-ui-acl :nodes="nodes" :db="db"
|
||||
:db-alias="db.meta.dbAlias"></toggle-function-ui-acl>
|
||||
</v-tab-item>
|
||||
|
||||
<v-tab class="text-capitalize caption">Procedures</v-tab>
|
||||
<v-tab-item>
|
||||
<toggle-procedure-ui-acl :nodes="nodes" :db="db"
|
||||
:db-alias="db.meta.dbAlias"></toggle-procedure-ui-acl>
|
||||
|
||||
</v-tab-item>-->
|
||||
<!-- <v-tab class="text-capitalize caption">
|
||||
Relations
|
||||
</v-tab>
|
||||
<v-tab-item>
|
||||
<toggle-relations-ui-acl
|
||||
:nodes="nodes"
|
||||
:db="db"
|
||||
:db-alias="db.meta.dbAlias"
|
||||
/>
|
||||
</v-tab-item>-->
|
||||
</v-tabs>
|
||||
</v-tab-item>
|
||||
</template>
|
||||
@@ -127,22 +58,16 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import XcMeta from '../settings/xcMeta'
|
||||
// import DisableOrEnableRelations from './sync/disableOrEnableRelations'
|
||||
import { isMetaTable } from '@/helpers/xutils'
|
||||
import DisableOrEnableTables from '@/components/project/projectMetadata/sync/disableOrEnableTables'
|
||||
import metaDiffSync from '~/components/project/projectMetadata/sync/metaDiffSync'
|
||||
import ToggleTableUiAcl from '@/components/project/projectMetadata/uiAcl/toggleTableUIAcl'
|
||||
// import ToggleRelationsUiAcl from '@/components/project/projectMetadata/uiAcl/toggleRelationsUIAcl'
|
||||
// import DisableOrEnableViews from '~/components/project/projectMetadata/sync/disableOrEnableViews'
|
||||
|
||||
export default {
|
||||
name: 'DisableOrEnableModels',
|
||||
components: {
|
||||
// DisableOrEnableViews,
|
||||
// ToggleRelationsUiAcl,
|
||||
ToggleTableUiAcl,
|
||||
DisableOrEnableTables,
|
||||
metaDiffSync,
|
||||
XcMeta
|
||||
// DisableOrEnableRelations
|
||||
},
|
||||
props: ['nodes'],
|
||||
data: () => ({
|
||||
@@ -158,6 +83,9 @@ export default {
|
||||
},
|
||||
methods: {},
|
||||
computed: {
|
||||
bases() {
|
||||
return this.$store.state.project.project && this.$store.state.project.project.bases
|
||||
},
|
||||
dbsTab: {
|
||||
set(tab) {
|
||||
if (!tab) {
|
||||
@@ -201,7 +129,7 @@ export default {
|
||||
return 0
|
||||
}
|
||||
if (this.tables && this.models) {
|
||||
const tables = this.tables.filter(t => !isMetaTable(t.tn)).map(t => t.tn)
|
||||
const tables = this.tables.filter(t => !isMetaTable(t.table_name)).map(t => t.table_name)
|
||||
res.push(...this.models.map((m) => {
|
||||
const i = tables.indexOf(m.title)
|
||||
if (i === -1) {
|
||||
|
||||
@@ -93,10 +93,10 @@
|
||||
<template #item="{item,index}">
|
||||
<tr class="caption">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ item.relationType === 'hm' ? item.rtn : item.tn }}</td>
|
||||
<td>{{ item.relationType === 'hm' ? item.rtn : item.table_name }}</td>
|
||||
<td>{{ item.relationType === 'hm' ? 'HasMany' : 'BelongsTo' }}</td>
|
||||
<td>{{ item.rtn }}</td>
|
||||
<td>{{ item.tn }}</td>
|
||||
<td>{{ item.table_name }}</td>
|
||||
<!-- <td>
|
||||
<v-checkbox
|
||||
v-model="item.enabled"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-container v-if="dbAliasList[dbsTab]" fluid>
|
||||
<v-container fluid>
|
||||
<v-card>
|
||||
<v-row>
|
||||
<!-- <v-col cols="12">-->
|
||||
@@ -28,7 +28,7 @@
|
||||
small
|
||||
color="primary"
|
||||
icon="refresh"
|
||||
@click="loadXcDiff()"
|
||||
@click="clickReload"
|
||||
>
|
||||
<!-- Reload -->
|
||||
{{ $t('general.reload') }}
|
||||
@@ -76,18 +76,18 @@
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="model in diff"
|
||||
v-show="!filter.trim() || (model.tn || model.title || '').toLowerCase().includes(filter.toLowerCase())"
|
||||
:key="model.title"
|
||||
:class="`nc-metasync-row nc-metasync-row-${model.tn}`"
|
||||
v-show="!filter.trim() || (model.table_name || model.title || '').toLowerCase().includes(filter.toLowerCase())"
|
||||
:key="model.table_name"
|
||||
:class="`nc-metasync-row nc-metasync-row-${model.table_name}`"
|
||||
>
|
||||
<!-- v-if="model.alias.toLowerCase().indexOf(filter.toLowerCase()) > -1">-->
|
||||
<td>
|
||||
<v-icon small :color="viewIcons[model.type==='table'?'grid':'view'].color" v-on="on">
|
||||
<!-- <v-icon small :color="viewIcons[model.type==='table'?'grid':'view'].color" v-on="on">
|
||||
{{ viewIcons[model.type === 'table' ? 'grid' : 'view'].icon }}
|
||||
</v-icon>
|
||||
</v-icon>-->
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{on}">
|
||||
<span v-on="on">{{ model.tn && model.tn.slice(prefix.length) }}</span>
|
||||
<span v-on="on">{{ model.table_name && model.table_name.slice(prefix.length) }}</span>
|
||||
</template>
|
||||
<span class="caption">{{ model.title }}</span>
|
||||
</v-tooltip>
|
||||
@@ -244,6 +244,7 @@
|
||||
<div class="d-flex justify-center">
|
||||
<v-btn
|
||||
v-if="isChanged"
|
||||
v-t="['proj-meta:metadata:metasync']"
|
||||
x-large
|
||||
class="mx-auto primary nc-btn-metasync-sync-now"
|
||||
@click="syncMetaDiff"
|
||||
@@ -272,11 +273,9 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import viewIcons from '~/helpers/viewIcons'
|
||||
import XBtn from '~/components/global/xBtn'
|
||||
|
||||
export default {
|
||||
name: 'DisableOrEnableTables',
|
||||
components: { XBtn },
|
||||
props: ['nodes', 'db'],
|
||||
data: () => ({
|
||||
viewIcons,
|
||||
@@ -290,15 +289,20 @@ export default {
|
||||
}),
|
||||
async mounted() {
|
||||
await this.loadXcDiff()
|
||||
// await this.loadModels()
|
||||
// await this.loadTableList()
|
||||
// await this.loadMode// await this.loadTableList()
|
||||
},
|
||||
methods: {
|
||||
async loadXcDiff() {
|
||||
this.diff = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
dbAlias: this.db.meta.dbAlias,
|
||||
env: this.$store.getters['project/GtrEnv']
|
||||
}, 'xcMetaDiff'])
|
||||
this.diff = (await this.$api.project.metaDiffGet(this.$store.state.project.projectId, this.db.id))
|
||||
|
||||
// this.diff = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// dbAlias: this.db.meta.dbAlias,
|
||||
// env: this.$store.getters['project/GtrEnv']
|
||||
// }, 'xcMetaDiff'])
|
||||
},
|
||||
clickReload() {
|
||||
this.loadXcDiff()
|
||||
this.$tele.emit('proj-meta:metadata:reload')
|
||||
},
|
||||
/* async addTableMeta(tables) {
|
||||
try {
|
||||
@@ -345,10 +349,11 @@ export default {
|
||||
*/
|
||||
async syncMetaDiff() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
dbAlias: this.db.meta.dbAlias,
|
||||
env: this.$store.getters['project/GtrEnv']
|
||||
}, 'xcMetaDiffSync', {}])
|
||||
await this.$api.project.metaDiffSync(this.$store.state.project.projectId, this.db.id)
|
||||
// await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// dbAlias: this.db.meta.dbAlias,
|
||||
// env: this.$store.getters['project/GtrEnv']
|
||||
// }, 'xcMetaDiffSync', {}])
|
||||
this.$toast.success('Table metadata recreated successfully').goAway(3000)
|
||||
await this.loadXcDiff()
|
||||
|
||||
@@ -448,7 +453,7 @@ export default {
|
||||
return 0
|
||||
}
|
||||
if (this.tables && this.models) {
|
||||
const tables = this.tables.filter(t => !isMetaTable(t.tn)).map(t => t.tn)
|
||||
const tables = this.tables.filter(t => !isMetaTable(t.table_name)).map(t => t.table_name)
|
||||
res.push(...this.models.map((m) => {
|
||||
const i = tables.indexOf(m.title)
|
||||
if (i === -1) {
|
||||
@@ -65,10 +65,10 @@
|
||||
v-for="(relation,i) in relations"
|
||||
:key="i"
|
||||
>
|
||||
<td>{{ relation.relationType === 'hm' ? relation.rtn : relation.tn }}</td>
|
||||
<td>{{ relation.relationType === 'hm' ? relation.rtn : relation.table_name }}</td>
|
||||
<td>{{ relation.relationType === 'hm' ? 'HasMany' : 'BelongsTo' }}</td>
|
||||
<td>{{ relation.rtn }}</td>
|
||||
<td>{{ relation.tn }}</td>
|
||||
<td>{{ relation.table_name }}</td>
|
||||
|
||||
<td v-for="role in roles" :key="`${i}-${role}`">
|
||||
<v-tooltip bottom>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
dense
|
||||
hide-details
|
||||
class="my-2 mx-auto search-field"
|
||||
:placeholder="`Search '${db.connection.database}' models`"
|
||||
placeholder="Search models"
|
||||
style="max-width:300px"
|
||||
outlined
|
||||
>
|
||||
@@ -19,7 +19,6 @@
|
||||
</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<v-spacer />
|
||||
<x-btn
|
||||
outlined
|
||||
@@ -30,8 +29,7 @@
|
||||
class="nc-acl-reload"
|
||||
@click="loadTableList()"
|
||||
>
|
||||
<!-- Reload -->
|
||||
{{ $t('general.reload') }}
|
||||
Reload
|
||||
</x-btn>
|
||||
<x-btn
|
||||
outlined
|
||||
@@ -53,15 +51,15 @@
|
||||
<v-simple-table v-if="tables" dense style="min-width: 400px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="caption" bgcolor="#F5F5F5" width="100px">
|
||||
<th class="caption" width="100px">
|
||||
<!--TableName-->
|
||||
{{ $t('labels.tableName') }}
|
||||
</th>
|
||||
<th class="caption" bgcolor="#F5F5F5" width="150px">
|
||||
<th class="caption" width="150px">
|
||||
<!--ViewName-->
|
||||
{{ $t('labels.viewName') }}
|
||||
</th>
|
||||
<th v-for="role in roles" :key="role" class="caption" bgcolor="#F5F5F5" width="100px">
|
||||
<th v-for="role in roles" :key="role" class="caption" width="100px">
|
||||
{{ role.charAt(0).toUpperCase() + role.slice(1) }}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -71,27 +69,30 @@
|
||||
v-for="table in tables"
|
||||
>
|
||||
<tr
|
||||
v-if="table._tn.toLowerCase().indexOf(filter.toLowerCase()) > -1"
|
||||
:key="table.tn"
|
||||
:class="`nc-acl-table-row nc-acl-table-row-${table._tn}`"
|
||||
v-if="table.title.toLowerCase().indexOf(filter.toLowerCase()) > -1"
|
||||
:key="table.table_name"
|
||||
:class="`nc-acl-table-row nc-acl-table-row-${table.title}`"
|
||||
>
|
||||
<td>
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{on}">
|
||||
<span class="caption ml-2" v-on="on">{{ table.type === 'table' ? table._tn:table.type === 'view' ? table._tn : table.ptn.split("__")[1] }}</span>
|
||||
<span
|
||||
class="caption ml-2"
|
||||
v-on="on"
|
||||
>{{ table.ptype === 'table' ? table._ptn : table.ptype === 'view' ? table._ptn : table._ptn }}</span>
|
||||
</template>
|
||||
<span class="caption">{{ table.tn }}</span>
|
||||
<span class="caption">{{ table.ptn || table._ptn }}</span>
|
||||
</v-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<v-icon small :color="viewIcons[table.type === 'vtable' ? table.show_as : table.type].color" v-on="on">
|
||||
{{ viewIcons[table.type === 'vtable' ? table.show_as : table.type].icon }}
|
||||
<v-icon small :color="viewIcons[table.type].color" v-on="on">
|
||||
{{ viewIcons[table.type].icon }}
|
||||
</v-icon>
|
||||
<span v-if="table.ptn" class="caption">{{ table._tn }}</span>
|
||||
<span v-if="table.ptn" class="caption">{{ table.title }}</span>
|
||||
<span v-else class="caption">{{ $t('general.default') }}</span>
|
||||
<!-- {{ table.show_as || table.type }}-->
|
||||
</td>
|
||||
<td v-for="role in roles" :key="`${table.tn}-${role}`">
|
||||
<td v-for="role in roles" :key="`${table.table_name}-${role}`">
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{on}">
|
||||
<div
|
||||
@@ -99,7 +100,7 @@
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="table.disabled[role]"
|
||||
:class="`pt-0 mt-0 nc-acl-${table._tn.toLowerCase().replace('_','')}-${role}-chkbox`"
|
||||
:class="`pt-0 mt-0 nc-acl-${table.title.toLowerCase().replace('_','')}-${role}-chkbox`"
|
||||
dense
|
||||
hide-details
|
||||
:true-value="false"
|
||||
@@ -109,10 +110,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<span v-if="table.disabled[role]">Click to make '{{ table.tn }}' visible for Role:{{
|
||||
<span v-if="table.disabled[role]">Click to make '{{ table.table_name }}' visible for Role:{{
|
||||
role
|
||||
}} in UI dashboard</span>
|
||||
<span v-else>Click to hide '{{ table.tn }}' for Role:{{ role }} in UI dashboard</span>
|
||||
<span v-else>Click to hide '{{ table.table_name }}' for Role:{{ role }} in UI dashboard</span>
|
||||
</v-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -147,25 +148,26 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadTableList() {
|
||||
this.tables = (await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
dbAlias: this.db.meta.dbAlias,
|
||||
env: this.$store.getters['project/GtrEnv']
|
||||
}, 'xcVisibilityMetaGet', {
|
||||
type: 'all'
|
||||
}]))
|
||||
this.tables = (await this.$api.project.modelVisibilityList(
|
||||
this.db.project_id, {
|
||||
includeM2M: this.$store.state.windows.includeM2M || ''
|
||||
}))
|
||||
// this.tables = (await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// dbAlias: this.db.meta.dbAlias,
|
||||
// env: this.$store.getters['project/GtrEnv']
|
||||
// }, 'xcVisibilityMetaGet', {
|
||||
// type: 'all'
|
||||
// }]))
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
dbAlias: this.db.meta.dbAlias,
|
||||
env: this.$store.getters['project/GtrEnv']
|
||||
}, 'xcVisibilityMetaSetAll', {
|
||||
disableList: this.tables.filter(t => t.edited)
|
||||
}])
|
||||
await this.$api.project.modelVisibilitySet(this.db.project_id, this.tables.filter(t => t.edited))
|
||||
this.$toast.success('Updated UI ACL for tables successfully').goAway(3000)
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('proj-meta:ui-acl:update')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -176,7 +178,7 @@ export default {
|
||||
return this.tables && this.tables.length && this.tables.some(t => t.edited)
|
||||
},
|
||||
roles() {
|
||||
return this.tables && this.tables.length ? Object.keys(this.tables[0].disabled) : []
|
||||
return ['editor', 'commenter', 'viewer']// this.tables && this.tables.length ? Object.keys(this.tables[0].disabled) : []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,25 +82,25 @@ export default {
|
||||
functions = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.nodes.tn || this.nodes.view_name
|
||||
table_name: this.nodes.table_name || this.nodes.view_name
|
||||
}, 'defaultRestMiddlewareCodeGet', {
|
||||
title: this.route.title,
|
||||
relation_type: this.route.relation_type,
|
||||
tn: this.nodes.tn || this.nodes.view_name
|
||||
table_name: this.nodes.table_name || this.nodes.view_name
|
||||
}])
|
||||
} else {
|
||||
functions = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.nodes.tn || this.nodes.view_name
|
||||
table_name: this.nodes.table_name || this.nodes.view_name
|
||||
}, 'defaultRestHandlerCodeGet', {
|
||||
type: this.method,
|
||||
path: this.route[this.method].path,
|
||||
title: this.route[this.method].title,
|
||||
relation_type: this.route[this.method].relation_type,
|
||||
tnp: this.route[this.method].tnp,
|
||||
tnc: this.route[this.method].tnc,
|
||||
tn: this.nodes.tn || this.nodes.view_name
|
||||
tnp: this.route[this.method].table_namep,
|
||||
tnc: this.route[this.method].table_namec,
|
||||
table_name: this.nodes.table_name || this.nodes.view_name
|
||||
}])
|
||||
}
|
||||
if (functions && functions.length) {
|
||||
@@ -118,7 +118,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcRoutesMiddlewareUpdate', {
|
||||
tn: this.nodes.tn || this.nodes.view_name,
|
||||
table_name: this.nodes.table_name || this.nodes.view_name,
|
||||
type: this.method,
|
||||
functions: [this.code],
|
||||
title: this.route.title
|
||||
@@ -133,7 +133,7 @@ export default {
|
||||
functions: [this.code],
|
||||
path: this.route[this.method].path,
|
||||
relation_type: this.route[this.method].relation_type,
|
||||
tn: this.nodes.tn || this.nodes.view_name
|
||||
table_name: this.nodes.table_name || this.nodes.view_name
|
||||
}])
|
||||
}
|
||||
this.$toast.success('API Handler updated successfully').goAway(3000)
|
||||
|
||||
@@ -184,21 +184,11 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
// // console.log("env: this.env", this.env, this.dbAlias);
|
||||
// const client = await this.sqlMgr.projectGetSqlClient({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias
|
||||
// });
|
||||
// const result = await client.sequenceList({
|
||||
// sequence_name: this.originalNodes.sequence_name
|
||||
// });
|
||||
|
||||
const result = await this.sqlMgr.sqlOp({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'sequenceList', { sequence_name: this.originalNodes.sequence_name })
|
||||
|
||||
// console.log("sequence read", result);
|
||||
this.sequence = { ...result.data.list.find(seq => seq.sequence_name === this.originalNodes.sequence_name) }
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
@@ -223,7 +213,6 @@ export default {
|
||||
...this.nodes
|
||||
}
|
||||
})
|
||||
console.log('create sequence result', result)
|
||||
this.originalNodes.sequence_name = this.sequence.sequence_name
|
||||
this.newSequence = false
|
||||
await this.loadSequences()
|
||||
@@ -238,7 +227,6 @@ export default {
|
||||
this.sequence
|
||||
])
|
||||
|
||||
console.log('update sequence result', result)
|
||||
this.$toast.success('Sequence updated successfully').goAway(3000)
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -282,7 +270,6 @@ export default {
|
||||
}
|
||||
},
|
||||
sequenceNameChanged() {
|
||||
console.log('changed', this.sequence.sequence_name.trim())
|
||||
this.edited = this.sequence.sequence_name.trim() !== ''
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,64 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="text-center mb-5 grey--text text--darken-2">
|
||||
<!-- Metadata Operations -->
|
||||
<div class="mt-5">
|
||||
<!-- <h3 class="text-center mb-5 grey--text text--darken-2">
|
||||
<!– Metadata Operations –>
|
||||
{{ $t('title.metaOperations') }}
|
||||
</h3>
|
||||
</h3>-->
|
||||
|
||||
<v-simple-table class="ma-2 meta-table text-center mx-auto">
|
||||
<!-- <thead>-->
|
||||
<!-- <tr>-->
|
||||
<!-- <th colspan="2" class="text-center title pa-2">Metadata Operations</th>-->
|
||||
<!-- </tr>-->
|
||||
<!-- </thead>-->
|
||||
<tbody>
|
||||
<!-- <tr>-->
|
||||
<!-- <td>-->
|
||||
<!-- <!– Export all metadata from the meta tables to meta directory. –>-->
|
||||
<!-- {{ $t('tooltip.exportMetadata') }}-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <v-btn-->
|
||||
<!-- min-width="150"-->
|
||||
<!-- color="primary"-->
|
||||
<!-- small-->
|
||||
<!-- outlined-->
|
||||
<!-- :loading="loading === 'export-file'"-->
|
||||
<!-- @click="exportMeta"-->
|
||||
<!-- >-->
|
||||
<!-- <v-icon small>-->
|
||||
<!-- mdi-export-->
|
||||
<!-- </v-icon> -->
|
||||
<!-- <!– Export to file –>-->
|
||||
<!-- {{ $t('activity.exportToFile') }}-->
|
||||
<!-- </v-btn>-->
|
||||
<!-- </td>-->
|
||||
<!-- </tr>-->
|
||||
|
||||
<!-- <tr>-->
|
||||
<!-- <td>-->
|
||||
<!-- <!– Import all metadata from the meta directory to meta tables. –>-->
|
||||
<!-- {{ $t('tooltip.importMetadata') }}-->
|
||||
<!-- </td>-->
|
||||
<!-- <td>-->
|
||||
<!-- <v-btn-->
|
||||
<!-- :loading="loading === 'import-file'"-->
|
||||
<!-- min-width="150"-->
|
||||
<!-- color="info"-->
|
||||
<!-- small-->
|
||||
<!-- outlined-->
|
||||
<!-- @click="importMeta"-->
|
||||
<!-- >-->
|
||||
<!-- <v-icon small>-->
|
||||
<!-- mdi-import-->
|
||||
<!-- </v-icon> -->
|
||||
|
||||
<!-- <!– Import –>-->
|
||||
<!-- {{ $t('activity.import') }}-->
|
||||
<!-- </v-btn>-->
|
||||
<!-- </td>-->
|
||||
<!-- </tr>-->
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<!-- Export project meta to zip file and download. -->
|
||||
@@ -66,6 +14,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<v-btn
|
||||
v-t="['proj-meta:export-zip:trigger']"
|
||||
min-width="150"
|
||||
color="primary"
|
||||
small
|
||||
@@ -88,6 +37,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<v-btn
|
||||
v-t="['proj-meta:import-zip']"
|
||||
min-width="150"
|
||||
:loading="loading === 'import-zip'"
|
||||
color="info"
|
||||
@@ -232,6 +182,7 @@ export default {
|
||||
}
|
||||
this.dialogShow = false
|
||||
this.loading = null
|
||||
this.$tele.emit('proj-meta:export-zip:submit')
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -307,7 +258,7 @@ export default {
|
||||
zipFile
|
||||
])
|
||||
// this.$toast.success('Successfully imported metadata').goAway(3000)
|
||||
this.$toast.success(`${this.$t('msg.toast.importMetadata')}`).goAway(3000)
|
||||
this.$toast.success(`${this.$t('msg.toast.importMetadata')}`).goAway(3000)
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import inflection from 'inflection'
|
||||
|
||||
export default class GqlApi {
|
||||
constructor(table, columns, meta, $ctx) {
|
||||
// this.table = table;
|
||||
this.columns = columns
|
||||
this.meta = meta
|
||||
this.$ctx = $ctx
|
||||
@@ -70,19 +69,19 @@ export default class GqlApi {
|
||||
}
|
||||
|
||||
get gqlQueryListName() {
|
||||
return `${this.meta._tn}List`
|
||||
return `${this.meta.title}List`
|
||||
}
|
||||
|
||||
get gqlQueryReadName() {
|
||||
return `${this.meta._tn}Read`
|
||||
return `${this.meta.title}Read`
|
||||
}
|
||||
|
||||
get tableCamelized() {
|
||||
return `${this.meta._tn}`
|
||||
return `${this.meta.title}`
|
||||
}
|
||||
|
||||
get gqlReqBody() {
|
||||
return `\n${this.columns.map(c => c._cn).join('\n')}\n`
|
||||
return `\n${this.columns.map(c => c.title).join('\n')}\n`
|
||||
}
|
||||
|
||||
async gqlRelationReqBody(params) {
|
||||
@@ -92,11 +91,11 @@ export default class GqlApi {
|
||||
await this.$ctx.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.$ctx.nodes.dbAlias,
|
||||
env: this.$ctx.nodes.env,
|
||||
tn: child
|
||||
table_name: child
|
||||
})
|
||||
const meta = this.$ctx.$store.state.meta.metas[child]
|
||||
if (meta) {
|
||||
str += `\n${meta._tn}List{\n${meta.columns.map(c => c._cn).join('\n')}\n}`
|
||||
str += `\n${meta.title}List{\n${meta.columns.map(c => c.title).join('\n')}\n}`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,11 +104,11 @@ export default class GqlApi {
|
||||
await this.$ctx.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.$ctx.nodes.dbAlias,
|
||||
env: this.$ctx.nodes.env,
|
||||
tn: parent
|
||||
table_name: parent
|
||||
})
|
||||
const meta = this.$ctx.$store.state.meta.metas[parent]
|
||||
if (meta) {
|
||||
str += `\n${meta._tn}Read{\n${meta.columns.map(c => c._cn).join('\n')}\n}`
|
||||
str += `\n${meta.title}Read{\n${meta.columns.map(c => c.title).join('\n')}\n}`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,11 +117,11 @@ export default class GqlApi {
|
||||
await this.$ctx.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.$ctx.nodes.dbAlias,
|
||||
env: this.$ctx.nodes.env,
|
||||
tn: mm
|
||||
table_name: mm
|
||||
})
|
||||
const meta = this.$ctx.$store.state.meta.metas[mm]
|
||||
if (meta) {
|
||||
str += `\n${meta._tn}MMList{\n${meta.columns.map(c => c._cn).join('\n')}\n}`
|
||||
str += `\n${meta.title}MMList{\n${meta.columns.map(c => c.title).join('\n')}\n}`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,7 +169,7 @@ export default class GqlApi {
|
||||
|
||||
const colName = Object.keys(data)[0]
|
||||
this.$ctx.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.$ctx.nodes.dbAlias }, 'xcAuditCreate', {
|
||||
tn: this.table,
|
||||
table_name: this.table,
|
||||
cn: colName,
|
||||
pk: id,
|
||||
value: data[colName],
|
||||
@@ -216,7 +215,7 @@ export default class GqlApi {
|
||||
}
|
||||
|
||||
get table() {
|
||||
return this.meta && this.meta._tn && inflection.camelize(this.meta._tn)
|
||||
return this.meta && this.meta.title && inflection.camelize(this.meta.title)
|
||||
}
|
||||
|
||||
async paginatedM2mNotChildrenList(params, assoc, pid) {
|
||||
@@ -225,7 +224,7 @@ export default class GqlApi {
|
||||
m2mNotChildren(pid: $pid,assoc:$assoc,parent:$parent,limit:$limit, offset:$offset)
|
||||
}`,
|
||||
variables: {
|
||||
parent: this.meta.tn, assoc, pid: pid + '', ...params
|
||||
parent: this.meta.table_name, assoc, pid: pid + '', ...params
|
||||
}
|
||||
})
|
||||
const count = await this.post(`/nc/${this.$ctx.$route.params.project_id}/v1/graphql`, {
|
||||
@@ -233,7 +232,7 @@ export default class GqlApi {
|
||||
m2mNotChildrenCount(pid: $pid,assoc:$assoc,parent:$parent)
|
||||
}`,
|
||||
variables: {
|
||||
parent: this.meta.tn, assoc, pid: pid + ''
|
||||
parent: this.meta.table_name, assoc, pid: pid + ''
|
||||
}
|
||||
})
|
||||
return { list: list.data.data.m2mNotChildren, count: count.data.data.m2mNotChildrenCount.count }
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class GrpcApi {
|
||||
env: this.ctx.nodes.env,
|
||||
dbAlias: this.ctx.nodes.dbAlias
|
||||
}, 'list', {
|
||||
tn: this.table,
|
||||
table_name: this.table,
|
||||
size: params.limit,
|
||||
page: ((params.offset || 0) / (params.limit || 20)) + 1
|
||||
// orderBy:
|
||||
|
||||
@@ -6,7 +6,6 @@ export default class RestApi {
|
||||
|
||||
// todo: - get version letter and use table alias
|
||||
async list(params) {
|
||||
// const data = await this.get(`/nc/${this.$ctx.$route.params.project_id}/api/v1/${this.table}`, params)
|
||||
const data = await this.get(`/nc/${this.$ctx.$route.params.project_id}/api/v1/${this.table}`, params)
|
||||
return data.data
|
||||
}
|
||||
@@ -44,8 +43,6 @@ export default class RestApi {
|
||||
}
|
||||
|
||||
async paginatedList(params) {
|
||||
// const list = await this.list(params);
|
||||
// const count = (await this.count({where: params.where || ''})).count;
|
||||
const [list, { count }] = await Promise.all([this.list(params), this.count({
|
||||
where: params.where || '',
|
||||
conditionGraph: params.conditionGraph
|
||||
@@ -54,9 +51,6 @@ export default class RestApi {
|
||||
}
|
||||
|
||||
async paginatedM2mNotChildrenList(params, assoc, pid) {
|
||||
/// api/v1/Film/m2mNotChildren/film_actor/44
|
||||
// const list = await this.list(params);
|
||||
// const count = (await this.count({where: params.where || ''})).count;
|
||||
const { list, info: { count } } = (await this.get(`/nc/${this.$ctx.$route.params.project_id}/api/v1/${this.table}/m2mNotChildren/${assoc}/${pid}`, params)).data
|
||||
return { list, count }
|
||||
}
|
||||
|
||||
@@ -1,131 +1,167 @@
|
||||
<template>
|
||||
<div
|
||||
class="backgroundColor pa-2"
|
||||
style="width:530px"
|
||||
:style="{width:nested ? '100%' : '530px'}"
|
||||
>
|
||||
<div class="grid" @click.stop>
|
||||
<template v-for="(filter,i) in filters" dense>
|
||||
<v-icon
|
||||
v-if="!filter.readOnly"
|
||||
:key="i + '_3'"
|
||||
small
|
||||
class="nc-filter-item-remove-btn"
|
||||
@click.stop="filters.splice(i,1)"
|
||||
>
|
||||
mdi-close-box
|
||||
</v-icon>
|
||||
<span v-else :key="i + '_1'" />
|
||||
<template v-if="filter.status !== 'delete'">
|
||||
<div v-if="filter.is_group" :key="i" style="grid-column: span 4; padding:6px" class="elevation-4 ">
|
||||
<div class="d-flex" style="gap:6px; padding: 0 6px">
|
||||
<v-icon
|
||||
v-if="!filter.readOnly"
|
||||
:key="i + '_3'"
|
||||
small
|
||||
class="nc-filter-item-remove-btn"
|
||||
@click.stop="deleteFilter(filter,i)"
|
||||
>
|
||||
mdi-close-box
|
||||
</v-icon>
|
||||
<span v-else :key="i + '_1'" />
|
||||
<v-select
|
||||
v-model="filter.logical_op"
|
||||
class="flex-shrink-1 flex-grow-0 elevation-0 caption "
|
||||
:items="['and' ,'or']"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
hide-details
|
||||
placeholder="Group op"
|
||||
@click.stop
|
||||
@change="saveOrUpdate(filter, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
</div>
|
||||
<column-filter
|
||||
v-if="filter.id || shared"
|
||||
ref="nestedFilter"
|
||||
v-model="filter.children"
|
||||
:parent-id="filter.id"
|
||||
:view-id="viewId"
|
||||
nested
|
||||
:meta="meta"
|
||||
:shared="shared"
|
||||
:web-hook="webHook"
|
||||
:hook-id="hookId"
|
||||
@updated="$emit('updated')"
|
||||
@input="$emit('input', filters)"
|
||||
/>
|
||||
</div>
|
||||
<template v-else>
|
||||
<v-icon
|
||||
v-if="!filter.readOnly"
|
||||
:key="i + '_3'"
|
||||
small
|
||||
class="nc-filter-item-remove-btn"
|
||||
@click.stop="deleteFilter(filter,i)"
|
||||
>
|
||||
mdi-close-box
|
||||
</v-icon>
|
||||
<span v-else :key="i + '_1'" />
|
||||
<span
|
||||
v-if="!i"
|
||||
:key="i + '_2'"
|
||||
class="caption d-flex align-center"
|
||||
>{{ $t('labels.where') }}</span>
|
||||
|
||||
<span
|
||||
v-if="!i"
|
||||
:key="i + '_2'"
|
||||
class="caption d-flex align-center"
|
||||
>
|
||||
<!-- where -->
|
||||
{{ $t('labels.where') }}
|
||||
</span>
|
||||
<v-select
|
||||
v-else
|
||||
:key="i + '_4'"
|
||||
v-model="filter.logical_op"
|
||||
class="flex-shrink-1 flex-grow-0 elevation-0 caption "
|
||||
:items="['and' ,'or']"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
hide-details
|
||||
:disabled="filter.readOnly"
|
||||
@click.stop
|
||||
@change="filterUpdateCondition(filter, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<v-select
|
||||
v-else
|
||||
:key="i + '_4'"
|
||||
v-model="filter.logicOp"
|
||||
class="flex-shrink-1 flex-grow-0 elevation-0 caption "
|
||||
:items="['and' ,'or']"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
hide-details
|
||||
:disabled="filter.readOnly"
|
||||
@click.stop
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
<v-select
|
||||
:key="i + '_6'"
|
||||
v-model="filter.fk_column_id"
|
||||
class="caption nc-filter-field-select"
|
||||
:items="columns"
|
||||
:placeholder="$t('objects.field')"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
hide-details
|
||||
item-value="id"
|
||||
item-text="title"
|
||||
@click.stop
|
||||
@change="saveOrUpdate(filter, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span
|
||||
:class="`caption font-weight-regular nc-filter-fld-${item.title}`"
|
||||
>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select
|
||||
:key="'k' + i"
|
||||
v-model="filter.comparison_op"
|
||||
class="flex-shrink-1 flex-grow-0 caption nc-filter-operation-select"
|
||||
:items="filterComparisonOp(filter)"
|
||||
:placeholder="$t('labels.operation')"
|
||||
solo
|
||||
flat
|
||||
style="max-width:120px"
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
hide-details
|
||||
item-value="value"
|
||||
@click.stop
|
||||
@change="filterUpdateCondition(filter, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item.text }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<span v-if="['null', 'notnull', 'empty', 'notempty'].includes(filter.comparison_op)" :key="'span' + i" />
|
||||
<v-checkbox
|
||||
v-else-if="types[filter.field] === 'boolean'"
|
||||
:key="i + '_7'"
|
||||
v-model="filter.value"
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
@change="saveOrUpdate(filter, i)"
|
||||
/>
|
||||
<v-text-field
|
||||
v-else
|
||||
:key="i + '_7'"
|
||||
v-model="filter.value"
|
||||
solo
|
||||
flat
|
||||
hide-details
|
||||
dense
|
||||
class="caption nc-filter-value-select"
|
||||
:disabled="filter.readOnly"
|
||||
@click.stop
|
||||
@input="saveOrUpdate(filter, i)"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-text-field
|
||||
v-if="filter.readOnly"
|
||||
:key="i + '_5'"
|
||||
v-model="filter.field"
|
||||
class="caption "
|
||||
:placeholder="$t('objects.field')"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
disabled
|
||||
hide-details
|
||||
@click.stop
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-select
|
||||
v-else
|
||||
:key="i + '_6'"
|
||||
v-model="filter.field"
|
||||
class="caption nc-filter-field-select"
|
||||
:items="fieldList"
|
||||
:placeholder="$t('objects.field')"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
hide-details
|
||||
@click.stop
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select
|
||||
:key="'k' + i"
|
||||
v-model="filter.op"
|
||||
class="flex-shrink-1 flex-grow-0 caption nc-filter-operation-select"
|
||||
:items="opList"
|
||||
:placeholder="$t('labels.operation')"
|
||||
solo
|
||||
flat
|
||||
style="max-width:120px"
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
hide-details
|
||||
@click.stop
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<span v-if="['is null', 'is not null'].includes(filter.op)" :key="'span' + i" />
|
||||
<v-checkbox
|
||||
v-else-if="types[filter.field] === 'boolean'"
|
||||
:key="i + '_7'"
|
||||
v-model="filter.value"
|
||||
dense
|
||||
:disabled="filter.readOnly"
|
||||
/>
|
||||
<v-text-field
|
||||
v-else
|
||||
:key="i + '_7'"
|
||||
v-model="filter.value"
|
||||
solo
|
||||
flat
|
||||
hide-details
|
||||
dense
|
||||
class="caption nc-filter-value-select"
|
||||
:disabled="filter.readOnly"
|
||||
@click.stop
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- <v-list-item dense class="pt-2 list-btn">
|
||||
<v-btn @click.stop="addFilter" small class="elevation-0 grey--text">
|
||||
<v-icon small color="grey">mdi-plus</v-icon>
|
||||
Add Filter
|
||||
</v-btn>
|
||||
</v-list-item>-->
|
||||
|
||||
<v-btn small class="elevation-0 grey--text my-3" @click.stop="addFilter">
|
||||
<v-btn
|
||||
small
|
||||
class="elevation-0 grey--text my-3"
|
||||
@click.stop="addFilter"
|
||||
>
|
||||
<v-icon small color="grey">
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
@@ -141,7 +177,16 @@ import { UITypes } from '~/components/project/spreadsheet/helpers/uiTypes'
|
||||
|
||||
export default {
|
||||
name: 'ColumnFilter',
|
||||
props: ['fieldList', 'value', 'meta'],
|
||||
props: {
|
||||
fieldList: [Array],
|
||||
meta: Object,
|
||||
nested: Boolean,
|
||||
parentId: String,
|
||||
viewId: String,
|
||||
shared: Boolean,
|
||||
webHook: Boolean,
|
||||
hookId: String
|
||||
},
|
||||
data: () => ({
|
||||
filters: [],
|
||||
opList: [
|
||||
@@ -152,20 +197,85 @@ export default {
|
||||
'<',
|
||||
'>=',
|
||||
'<='
|
||||
],
|
||||
comparisonOp: [
|
||||
{
|
||||
text: 'is equal',
|
||||
value: 'eq'
|
||||
},
|
||||
{
|
||||
text: 'is not equal',
|
||||
value: 'neq'
|
||||
},
|
||||
{
|
||||
text: 'is like',
|
||||
value: 'like'
|
||||
},
|
||||
{
|
||||
text: 'is not like',
|
||||
value: 'nlike'
|
||||
},
|
||||
{
|
||||
text: 'is empty',
|
||||
value: 'empty',
|
||||
ignoreVal: true
|
||||
},
|
||||
{
|
||||
text: 'is not empty',
|
||||
value: 'notempty',
|
||||
ignoreVal: true
|
||||
},
|
||||
{
|
||||
text: 'is null',
|
||||
value: 'null',
|
||||
ignoreVal: true
|
||||
},
|
||||
{
|
||||
text: 'is not null',
|
||||
value: 'notnull',
|
||||
ignoreVal: true
|
||||
},
|
||||
{
|
||||
text: '>',
|
||||
value: 'gt'
|
||||
},
|
||||
{
|
||||
text: '<',
|
||||
value: 'lt'
|
||||
},
|
||||
{
|
||||
text: '>=',
|
||||
value: 'gte'
|
||||
},
|
||||
{
|
||||
text: '<=',
|
||||
value: 'lte'
|
||||
}
|
||||
]
|
||||
}),
|
||||
computed: {
|
||||
columnsById() {
|
||||
return (this.columns || []).reduce((o, c) => ({ ...o, [c.id]: c }), {})
|
||||
},
|
||||
autoApply() {
|
||||
return this.$store.state.windows.autoApplyFilter && !this.webHook
|
||||
},
|
||||
columns() {
|
||||
return (this.meta && this.meta.columns.filter(c => c && (!c.colOptions || !c.system)))
|
||||
},
|
||||
types() {
|
||||
if (!this.meta || !this.meta.columns || !this.meta.columns.length) { return {} }
|
||||
if (!this.meta || !this.meta.columns || !this.meta.columns.length) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return this.meta.columns.reduce((obj, col) => {
|
||||
switch (col.uidt) {
|
||||
case UITypes.Number:
|
||||
case UITypes.Decimal:
|
||||
obj[col._cn] = obj[col.cn] = 'number'
|
||||
obj[col.title] = obj[col.column_name] = 'number'
|
||||
break
|
||||
case UITypes.Checkbox:
|
||||
obj[col._cn] = obj[col.cn] = 'boolean'
|
||||
obj[col.title] = obj[col.column_name] = 'boolean'
|
||||
break
|
||||
default:
|
||||
break
|
||||
@@ -175,28 +285,161 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
async viewId(v) {
|
||||
if (v) {
|
||||
await this.loadFilter()
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
handler(v) {
|
||||
this.$emit('input', v)
|
||||
this.$emit('input', v && v.filter(f => (f.fk_column_id && f.comparison_op) || f.is_group))
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
value(v) {
|
||||
this.filters = v || []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.filters = this.value || []
|
||||
this.loadFilter()
|
||||
},
|
||||
methods: {
|
||||
filterComparisonOp(f) {
|
||||
return this.comparisonOp.filter((op) => {
|
||||
if (f && f.fk_column_id && this.columnsById[f.fk_column_id] &&
|
||||
this.columnsById[f.fk_column_id].uidt === UITypes.LinkToAnotherRecord &&
|
||||
this.columnsById[f.fk_column_id].uidt === UITypes.Lookup
|
||||
) {
|
||||
return ![
|
||||
'notempty',
|
||||
'empty',
|
||||
'notnull',
|
||||
'null'
|
||||
].includes(op.value)
|
||||
}
|
||||
return true
|
||||
})
|
||||
},
|
||||
async applyChanges(nested = false, { hookId } = {}) {
|
||||
for (const [i, filter] of Object.entries(this.filters)) {
|
||||
if (filter.status === 'delete') {
|
||||
if (this.hookId || hookId) {
|
||||
await this.$api.dbTableFilter.delete(this.hookId || hookId, filter.id)
|
||||
} else {
|
||||
await this.$api.dbTableFilter.delete(this.viewId, filter.id)
|
||||
}
|
||||
} else if (filter.status === 'update') {
|
||||
if (filter.id) {
|
||||
if (this.hookId || hookId) {
|
||||
await this.$api.dbTableFilter.update(this.hookId || hookId, filter.id, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})
|
||||
} else {
|
||||
await this.$api.dbTableFilter.update(this.viewId, filter.id, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})
|
||||
}
|
||||
} else if (this.hookId || hookId) {
|
||||
this.$set(this.filters, i, (await this.$api.dbTableFilter.create(this.hookId || hookId, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})))
|
||||
} else {
|
||||
this.$set(this.filters, i, (await this.$api.dbTableFilter.create(this.viewId, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.$refs.nestedFilter) {
|
||||
for (const nestedFilter of this.$refs.nestedFilter) {
|
||||
await nestedFilter.applyChanges(true)
|
||||
}
|
||||
}
|
||||
this.loadFilter()
|
||||
if (!nested) { this.$emit('updated') }
|
||||
},
|
||||
async loadFilter() {
|
||||
let filters = []
|
||||
if (this.viewId && this._isUIAllowed('filterSync')) {
|
||||
filters = this.parentId
|
||||
? (await this.$api.dbTableFilter.childrenRead(this.viewId, this.parentId))
|
||||
: (await this.$api.dbTableFilter.read(this.viewId))
|
||||
}
|
||||
if (this.hookId && this._isUIAllowed('filterSync')) {
|
||||
filters = this.parentId
|
||||
? (await this.$api.dbTableWebhookFilter.childrenRead(this.hookId, this.parentId))
|
||||
: (await this.$api.dbTableWebhookFilter.read(this.hookId))
|
||||
}
|
||||
|
||||
this.filters = filters
|
||||
},
|
||||
addFilter() {
|
||||
this.filters.push({
|
||||
field: '',
|
||||
op: '',
|
||||
fk_column_id: null,
|
||||
comparison_op: 'eq',
|
||||
value: '',
|
||||
logicOp: 'and'
|
||||
status: 'update',
|
||||
logical_op: 'and'
|
||||
})
|
||||
this.filters = this.filters.slice()
|
||||
this.$tele.emit(`filter:add:trigger:${this.filters.length}`)
|
||||
},
|
||||
addFilterGroup() {
|
||||
this.filters.push({
|
||||
parentId: this.parentId,
|
||||
is_group: true,
|
||||
status: 'update'
|
||||
})
|
||||
this.filters = this.filters.slice()
|
||||
const index = this.filters.length - 1
|
||||
this.saveOrUpdate(this.filters[index], index)
|
||||
},
|
||||
filterUpdateCondition(filter, i) {
|
||||
this.saveOrUpdate(filter, i)
|
||||
this.$tele.emit(`filter:condition:${filter.logical_op}:${filter.comparison_op}`)
|
||||
},
|
||||
async saveOrUpdate(filter, i) {
|
||||
if (this.shared || !this._isUIAllowed('filterSync')) {
|
||||
// this.$emit('input', this.filters.filter(f => f.fk_column_id && f.comparison_op))
|
||||
this.$emit('updated')
|
||||
} else if (!this.autoApply) {
|
||||
filter.status = 'update'
|
||||
} else if (filter.id) {
|
||||
await this.$api.dbTableFilter.update(this.viewId, filter.id, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})
|
||||
|
||||
this.$emit('updated')
|
||||
} else {
|
||||
this.$set(this.filters, i, (await this.$api.dbTableFilter.create(this.viewId, {
|
||||
...filter,
|
||||
fk_parent_id: this.parentId
|
||||
})))
|
||||
|
||||
this.$emit('updated')
|
||||
}
|
||||
},
|
||||
async deleteFilter(filter, i) {
|
||||
if (this.shared || !this._isUIAllowed('filterSync')) {
|
||||
this.filters.splice(i, 1)
|
||||
// this.$emit('input', this.filters.filter(f => f.fk_column_id && f.comparison_op))
|
||||
this.$emit('updated')
|
||||
} else if (filter.id) {
|
||||
if (!this.autoApply) {
|
||||
this.$set(filter, 'status', 'delete')
|
||||
} else {
|
||||
await this.$api.dbTableFilter.delete(this.viewId, filter.id)
|
||||
await this.loadFilter()
|
||||
this.$emit('updated')
|
||||
}
|
||||
} else {
|
||||
this.filters.splice(i, 1)
|
||||
this.$emit('updated')
|
||||
}
|
||||
// this.$emit('input', this.filters.filter(f => f.fk_column_id && f.comparison_op))
|
||||
this.$tele.emit('filter:delete')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +448,7 @@ export default {
|
||||
<style scoped>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns:22px 80px auto 110px auto;
|
||||
grid-template-columns:22px 80px auto auto auto;
|
||||
column-gap: 6px;
|
||||
row-gap: 6px
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-menu offset-y>
|
||||
<v-menu offset-y eager>
|
||||
<template #activator="{ on, }">
|
||||
<v-badge
|
||||
:value="filters.length"
|
||||
@@ -8,6 +8,7 @@
|
||||
overlap
|
||||
>
|
||||
<v-btn
|
||||
v-t="['filter:trigger']"
|
||||
class="nc-filter-menu-btn px-2 nc-remove-border"
|
||||
:disabled="isLocked"
|
||||
outlined
|
||||
@@ -27,7 +28,15 @@
|
||||
</v-btn>
|
||||
</v-badge>
|
||||
</template>
|
||||
<column-filter v-model="filters" :field-list="fieldList" :meta="meta">
|
||||
<column-filter
|
||||
ref="filter"
|
||||
v-model="filters"
|
||||
:shared="shared"
|
||||
:view-id="viewId"
|
||||
:field-list="fieldList"
|
||||
:meta="meta"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<div class="d-flex align-center mx-2" @click.stop>
|
||||
<v-checkbox
|
||||
id="col-filter-checkbox"
|
||||
@@ -42,12 +51,12 @@
|
||||
<span class="grey--text caption">
|
||||
{{ $t('msg.info.filterAutoApply') }}
|
||||
<!-- Auto apply -->
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</v-checkbox>
|
||||
|
||||
<v-spacer />
|
||||
<v-btn v-show="!autosave" color="primary" small class="caption ml-2" @click="$emit('input', filters)">
|
||||
<v-btn v-show="!autosave" color="primary" small class="caption ml-2" @click="applyChanges">
|
||||
Apply
|
||||
changes
|
||||
</v-btn>
|
||||
@@ -62,7 +71,7 @@ import ColumnFilter from '@/components/project/spreadsheet/components/columnFilt
|
||||
export default {
|
||||
name: 'ColumnFilterMenu',
|
||||
components: { ColumnFilter },
|
||||
props: ['fieldList', 'isLocked', 'value', 'meta'],
|
||||
props: ['fieldList', 'isLocked', 'value', 'meta', 'viewId', 'shared'],
|
||||
data: () => ({
|
||||
filters: []
|
||||
}),
|
||||
@@ -70,6 +79,7 @@ export default {
|
||||
autosave: {
|
||||
set(v) {
|
||||
this.$store.commit('windows/MutAutoApplyFilter', v)
|
||||
this.$tele.emit(`filter:auto-apply:${v}`)
|
||||
},
|
||||
get() {
|
||||
return this.$store.state.windows.autoApplyFilter
|
||||
@@ -97,7 +107,13 @@ export default {
|
||||
created() {
|
||||
this.filters = this.autosave ? this.value || [] : JSON.parse(JSON.stringify(this.value || []))
|
||||
},
|
||||
methods: {}
|
||||
methods: {
|
||||
applyChanges() {
|
||||
this.$emit('input', this.filters)
|
||||
if (this.$refs.filter) { this.$refs.filter.applyChanges() }
|
||||
this.$tele.emit('filter:apply-explicit')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
<v-container fluid @click.stop.prevent>
|
||||
<v-row>
|
||||
<v-col cols="12" class="mt-2">
|
||||
<!--label: Column Name-->
|
||||
<v-text-field
|
||||
ref="column"
|
||||
v-model="newColumn.cn"
|
||||
v-model="newColumn.column_name"
|
||||
hide-details="auto"
|
||||
color="primary"
|
||||
:rules="[
|
||||
v => !!v || 'Required',
|
||||
v => !meta || !meta.columns || meta.columns.every(c => column && c.cn === column.cn || v !== c.cn ) && meta.v.every(c => v !== c._cn ) || 'Duplicate column name',
|
||||
v => !meta || !meta.columns || meta.columns.every(c => column && (c.column_name || '').toLowerCase() === (column.column_name || '').toLowerCase() ||(
|
||||
(v||'').toLowerCase() !== (c.column_name||'').toLowerCase() && (v||'').toLowerCase() !== (c.title||'').toLowerCase())) || 'Duplicate column name' ,// && meta.v.every(c => v !== c.title ) || 'Duplicate column name',
|
||||
validateColumnName
|
||||
]"
|
||||
class="caption nc-column-name-input"
|
||||
@@ -105,7 +105,8 @@
|
||||
mdi-alert-outline
|
||||
</v-icon>
|
||||
</template>
|
||||
Changing MultiSelect to SingleSelect can lead to errors when there are multiple values associated with a cell
|
||||
Changing MultiSelect to SingleSelect can lead to errors when there are multiple values associated
|
||||
with a cell
|
||||
</v-alert>
|
||||
</v-col>
|
||||
|
||||
@@ -140,7 +141,7 @@
|
||||
:nodes="nodes"
|
||||
:meta="meta"
|
||||
:is-s-q-lite="isSQLite"
|
||||
:alias="newColumn.cn"
|
||||
:alias="newColumn.column_name"
|
||||
:is-m-s-s-q-l="isMSSQL"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
@@ -155,7 +156,7 @@
|
||||
:nodes="nodes"
|
||||
:meta="meta"
|
||||
:is-s-q-lite="isSQLite"
|
||||
:alias="newColumn.cn"
|
||||
:alias="newColumn.column_name"
|
||||
:is-m-s-s-q-l="isMSSQL"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
@@ -170,7 +171,7 @@
|
||||
:nodes="nodes"
|
||||
:meta="meta"
|
||||
:is-s-q-lite="isSQLite"
|
||||
:alias="newColumn.cn"
|
||||
:alias="newColumn.column_name"
|
||||
:is-m-s-s-q-l="isMSSQL"
|
||||
@onColumnSelect="onRelColumnSelect"
|
||||
/>
|
||||
@@ -190,7 +191,7 @@
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<template v-if="newColumn.cn && newColumn.uidt && !isVirtual">
|
||||
<template v-if="newColumn.column_name && newColumn.uidt && !isVirtual">
|
||||
<v-col cols="12">
|
||||
<v-container fluid class="wrapper">
|
||||
<v-row>
|
||||
@@ -369,24 +370,11 @@
|
||||
:nodes="nodes"
|
||||
:meta="meta"
|
||||
:is-s-q-lite="isSQLite"
|
||||
:alias="newColumn.cn"
|
||||
:alias="newColumn.column_name"
|
||||
:is-m-s-s-q-l="isMSSQL"
|
||||
:sql-ui="sqlUi"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
|
||||
<!-- <v-autocomplete
|
||||
label="Formula"
|
||||
hide-details
|
||||
class="caption formula-type"
|
||||
outlined
|
||||
dense
|
||||
:items="formulas"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="green--text text--darken-2 caption font-weight-regular">{{ item }}</span>
|
||||
</template>
|
||||
</v-autocomplete>-->
|
||||
</v-col>
|
||||
</template>
|
||||
</v-row>
|
||||
@@ -406,11 +394,20 @@
|
||||
</v-container>
|
||||
<v-col cols="12" class="d-flex pt-0">
|
||||
<v-spacer />
|
||||
<v-btn small outlined @click="close">
|
||||
<v-btn
|
||||
small
|
||||
outlined
|
||||
@click="close"
|
||||
>
|
||||
<!-- Cancel -->
|
||||
{{ $t('general.cancel') }}
|
||||
</v-btn>
|
||||
<v-btn small color="primary" :disabled="!valid" @click="save">
|
||||
<v-btn
|
||||
small
|
||||
color="primary"
|
||||
:disabled="!valid"
|
||||
@click="save"
|
||||
>
|
||||
<!-- Save -->
|
||||
{{ $t('general.save') }}
|
||||
</v-btn>
|
||||
@@ -429,6 +426,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { MssqlUi, SqliteUi } from 'nocodb-sdk'
|
||||
import { UITypes, uiTypes } from '../helpers/uiTypes'
|
||||
import RollupOptions from './editColumn/rollupOptions'
|
||||
import FormulaOptions from '@/components/project/spreadsheet/components/editColumn/formulaOptions'
|
||||
@@ -437,7 +435,6 @@ import CustomSelectOptions from '@/components/project/spreadsheet/components/edi
|
||||
import RelationOptions from '@/components/project/spreadsheet/components/editColumn/relationOptions'
|
||||
import DlgLabelSubmitCancel from '@/components/utils/dlgLabelSubmitCancel'
|
||||
import LinkedToAnotherOptions from '@/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions'
|
||||
import { SqliteUi, MssqlUi } from '@/helpers/sqlUi'
|
||||
import { validateColumnName } from '~/helpers'
|
||||
|
||||
export default {
|
||||
@@ -502,7 +499,7 @@ export default {
|
||||
return this.newColumn && this.newColumn.uidt === 'Rollup'
|
||||
},
|
||||
relation() {
|
||||
return this.meta && this.column && this.meta.belongsTo && this.meta.belongsTo.find(bt => bt.cn === this.column.cn)
|
||||
return this.meta && this.column && this.meta.belongsTo && this.meta.belongsTo.find(bt => bt.column_name === this.column.column_name)
|
||||
},
|
||||
isVirtual() {
|
||||
return this.isLinkToAnotherRecord || this.isLookup || this.isRollup
|
||||
@@ -515,7 +512,6 @@ export default {
|
||||
},
|
||||
async created() {
|
||||
this.genColumnData()
|
||||
// await this.loadDataTypes();
|
||||
},
|
||||
mounted() {
|
||||
this.focusInput()
|
||||
@@ -534,23 +530,8 @@ export default {
|
||||
},
|
||||
genColumnData() {
|
||||
this.newColumn = this.column ? { ...this.column } : this.sqlUi.getNewColumn([...this.meta.columns, ...(this.meta.v || [])].length + 1)
|
||||
this.newColumn.cno = this.newColumn.cn
|
||||
this.newColumn.cno = this.newColumn.column_name
|
||||
},
|
||||
/*
|
||||
async loadDataTypes() {
|
||||
try {
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'getKnexDataTypes', {}])
|
||||
|
||||
this.dataTypes = result.data.list;
|
||||
} catch (e) {
|
||||
this.$toast.error('Error loading datatypes :' + e).goAway(4000);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
*/
|
||||
close() {
|
||||
this.$emit('close')
|
||||
this.newColumn = {}
|
||||
@@ -560,9 +541,11 @@ export default {
|
||||
return
|
||||
}
|
||||
try {
|
||||
// if (this.newColumn.uidt === 'Formula') {
|
||||
// return this.$toast.info('Coming Soon...').goAway(3000)
|
||||
// }
|
||||
if (this.newColumn.uidt === 'Formula') {
|
||||
await this.$refs.formula.save()
|
||||
return this.$emit('saved')
|
||||
// return this.$toast.info('Coming Soon...').goAway(3000)
|
||||
}
|
||||
|
||||
if (this.isLinkToAnotherRecord && this.$refs.relation) {
|
||||
await this.$refs.relation.saveRelation()
|
||||
@@ -578,46 +561,28 @@ export default {
|
||||
return await this.$refs.formula.save()
|
||||
}
|
||||
|
||||
this.newColumn.tn = this.nodes.tn
|
||||
this.newColumn._cn = this.newColumn.cn
|
||||
|
||||
const columns = [...this.meta.columns]
|
||||
|
||||
if (columns.length) {
|
||||
columns[0].tn = this.nodes.tn
|
||||
}
|
||||
this.newColumn.table_name = this.nodes.table_name
|
||||
this.newColumn.title = this.newColumn.column_name
|
||||
|
||||
if (this.editColumn) {
|
||||
columns[this.columnIndex] = this.newColumn
|
||||
await this.$api.dbTableColumn.update(this.meta.id, this.column.id, this.newColumn)
|
||||
} else {
|
||||
columns.push(this.newColumn)
|
||||
await this.$api.dbTableColumn.create(this.meta.id, this.newColumn)
|
||||
}
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'tableUpdate', {
|
||||
tn: this.nodes.tn,
|
||||
_tn: this.meta._tn,
|
||||
originalColumns: this.meta.columns,
|
||||
columns
|
||||
}])
|
||||
|
||||
if (this.isRelation && this.$refs.relation) {
|
||||
await this.$refs.relation.saveRelation()
|
||||
}
|
||||
|
||||
this.$emit('saved', this.newColumn._cn, this.editColumn ? this.meta.columns[this.columnIndex]._cn : null)
|
||||
this.$emit('saved', this.newColumn.title, this.editColumn ? this.meta.columns[this.columnIndex].title : null)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
this.$emit('close')
|
||||
|
||||
this.$tele.emit(`column:edit:save:${this.newColumn.uidt}`)
|
||||
},
|
||||
onDataTypeChange() {
|
||||
this.newColumn.rqd = false
|
||||
if (this.newColumn.uidt !== UITypes.ID) {
|
||||
this.newColumn.pk = false
|
||||
this.newColumn.primaryKey = false
|
||||
}
|
||||
this.newColumn.ai = false
|
||||
this.newColumn.cdf = null
|
||||
@@ -682,16 +647,14 @@ export default {
|
||||
},
|
||||
this.relation.type === 'virtual' ? 'xcVirtualRelationDelete' : 'relationDelete',
|
||||
{
|
||||
childColumn: this.relation.cn,
|
||||
childTable: this.nodes.tn,
|
||||
childColumn: this.relation.column_name,
|
||||
childTable: this.nodes.table_name,
|
||||
parentTable: this.relation
|
||||
.rtn,
|
||||
parentColumn: this.relation
|
||||
.rcn
|
||||
}
|
||||
])
|
||||
console.log('relationDelete result ', result)
|
||||
// await this.loadColumnList();
|
||||
this.relationDeleteDlg = false
|
||||
this.relation = null
|
||||
this.$toast.success('Foreign Key deleted successfully').goAway(3000)
|
||||
|
||||
@@ -28,9 +28,6 @@
|
||||
@keydown.up.prevent="suggestionListUp"
|
||||
@keydown.enter.prevent="selectText"
|
||||
/>
|
||||
<!-- </template>-->
|
||||
<!-- <span class="caption">Example: AVG(column1, column2, column3)</span>-->
|
||||
<!-- </v-tooltip>-->
|
||||
</template>
|
||||
<v-list v-if="suggestion" ref="sugList" dense max-height="50vh" style="overflow: auto">
|
||||
<v-list-item-group
|
||||
@@ -60,11 +57,12 @@
|
||||
|
||||
<script>
|
||||
|
||||
import NcAutocompleteTree from '@/helpers/NcAutocompleteTree'
|
||||
import { getWordUntilCaret, insertAtCursor } from '@/helpers'
|
||||
import debounce from 'debounce'
|
||||
import jsep from 'jsep'
|
||||
import { UITypes } from 'nocodb-sdk'
|
||||
import formulaList, { validations } from '../../../../../helpers/formulaList'
|
||||
import { getWordUntilCaret, insertAtCursor } from '@/helpers'
|
||||
import NcAutocompleteTree from '@/helpers/NcAutocompleteTree'
|
||||
|
||||
export default {
|
||||
name: 'FormulaOptions',
|
||||
@@ -82,15 +80,21 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
suggestionsList() {
|
||||
console.log(this)
|
||||
const unsupportedFnList = this.sqlUi.getUnsupportedFnList()
|
||||
return [
|
||||
...this.availableFunctions.filter(fn => !unsupportedFnList.includes(fn)).map(fn => ({
|
||||
text: fn,
|
||||
type: 'function'
|
||||
})),
|
||||
...this.meta.columns.map(c => ({ text: c._cn, type: 'column', c })),
|
||||
...this.availableBinOps.map(op => ({ text: op, type: 'op' }))
|
||||
...this.meta.columns.filter(c => !this.column || this.column.id !== c.id).map(c => ({
|
||||
text: c.title,
|
||||
type: 'column',
|
||||
c
|
||||
})),
|
||||
...this.availableBinOps.map(op => ({
|
||||
text: op,
|
||||
type: 'op'
|
||||
}))
|
||||
]
|
||||
},
|
||||
acTree() {
|
||||
@@ -101,35 +105,32 @@ export default {
|
||||
return ref
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v, o) {
|
||||
if (v !== o) {
|
||||
this.formula = this.formula || {}
|
||||
this.formula.value = v || ''
|
||||
}
|
||||
},
|
||||
'formula.value'(v, o) {
|
||||
if (v !== o) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.formula = this.value ? { ...this.value } : {}
|
||||
this.formula = { value: this.value || '' }
|
||||
},
|
||||
methods: {
|
||||
async save() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
const formulaCol = {
|
||||
title: this.alias,
|
||||
uidt: UITypes.Formula,
|
||||
formula_raw: this.formula.value
|
||||
}
|
||||
|
||||
meta.v.push({
|
||||
_cn: this.alias,
|
||||
formula: {
|
||||
...this.formula,
|
||||
tree: jsep(this.formula.value)
|
||||
}
|
||||
})
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
const col = await this.$api.dbTableColumn.create(this.meta.id, formulaCol)
|
||||
|
||||
this.$toast.success('Formula column saved successfully').goAway(3000)
|
||||
return this.$emit('saved', this.alias)
|
||||
@@ -139,9 +140,9 @@ export default {
|
||||
},
|
||||
async update() {
|
||||
try {
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.table_name]))
|
||||
|
||||
const col = meta.v.find(c => c._cn === this.column._cn && c.formula)
|
||||
const col = meta.v.find(c => c.title === this.column.title && c.formula)
|
||||
|
||||
Object.assign(col, {
|
||||
_cn: this.alias,
|
||||
@@ -156,7 +157,7 @@ export default {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
tn: this.nodes.table_name,
|
||||
meta
|
||||
}])
|
||||
this.$toast.success('Formula column updated successfully').goAway(3000)
|
||||
@@ -194,7 +195,7 @@ export default {
|
||||
}
|
||||
pt.arguments.map(arg => this.validateAgainstMeta(arg, arr))
|
||||
} else if (pt.type === 'Identifier') {
|
||||
if (this.meta.columns.every(c => c._cn !== pt.name)) {
|
||||
if (this.meta.columns.filter(c => !this.column || this.column.id !== c.id).every(c => c.title !== pt.name)) {
|
||||
arr.push(`Column with name '${pt.name}' is not available`)
|
||||
}
|
||||
} else if (pt.type === 'BinaryExpression') {
|
||||
@@ -223,27 +224,12 @@ export default {
|
||||
},
|
||||
handleInput() {
|
||||
this.selected = 0
|
||||
// const $fakeDiv = this.$refs.fakeDiv
|
||||
this.suggestion = null
|
||||
const query = getWordUntilCaret(this.$refs.input.$el.querySelector('input')) // this.formula.value
|
||||
// if (query !== '') {
|
||||
const query = getWordUntilCaret(this.$refs.input.$el.querySelector('input'))
|
||||
const parts = query.split(/\W+/)
|
||||
|
||||
this.wordToComplete = parts.pop()
|
||||
|
||||
// if (this.wordToComplete !== '') {
|
||||
// get best match using popularity
|
||||
this.suggestion = this.acTree.complete(this.wordToComplete)
|
||||
|
||||
this.autocomplete = !!this.suggestion.length
|
||||
// } else {
|
||||
// // $span.textContent = '' // clear ghost span
|
||||
// }
|
||||
// } else {
|
||||
// this.autocomplete = false
|
||||
// // $time.textContent = ''
|
||||
// // $span.textContent = '' // clear ghost span
|
||||
// }
|
||||
},
|
||||
selectText() {
|
||||
if (this.selected > -1 && this.selected < this.suggestion.length) {
|
||||
|
||||
@@ -13,13 +13,12 @@
|
||||
>
|
||||
<v-radio value="hm" label="Has Many" />
|
||||
<v-radio value="mm" label="Many To Many" />
|
||||
<!-- <v-radio disabled value="oo" label="One To One" />-->
|
||||
</v-radio-group>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-autocomplete
|
||||
ref="input"
|
||||
v-model="relation.childTable"
|
||||
v-model="relation.childId"
|
||||
outlined
|
||||
class="caption"
|
||||
hide-details="auto"
|
||||
@@ -27,8 +26,8 @@
|
||||
:label="$t('labels.childTable')"
|
||||
:full-width="false"
|
||||
:items="refTables"
|
||||
item-text="_tn"
|
||||
item-value="tn"
|
||||
item-text="title"
|
||||
item-value="id"
|
||||
required
|
||||
dense
|
||||
:rules="tableRules"
|
||||
@@ -54,21 +53,6 @@
|
||||
|
||||
<v-container v-show="advanceOptions" fluid class="wrapper">
|
||||
<v-row>
|
||||
<!-- <v-col cols="6">
|
||||
<v-text-field
|
||||
outlined
|
||||
class="caption"
|
||||
hide-details
|
||||
:label="$t('labels.childColumn')"
|
||||
: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>
|
||||
@@ -121,6 +105,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ModelTypes, UITypes } from 'nocodb-sdk'
|
||||
|
||||
export default {
|
||||
name: 'LinkedToAnotherOptions',
|
||||
props: ['nodes', 'column', 'meta', 'isSQLite', 'alias'],
|
||||
@@ -147,159 +133,47 @@ export 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'
|
||||
}
|
||||
}
|
||||
]
|
||||
return []
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadTablesList()
|
||||
this.relation = {
|
||||
childColumn: `${this.meta.tn}_id`,
|
||||
childTable: this.nodes.tn,
|
||||
parentId: null,
|
||||
childID: null,
|
||||
childColumn: `${this.meta.table_name}_id`,
|
||||
childTable: this.nodes.table_name,
|
||||
parentTable: this.column.rtn || '',
|
||||
parentColumn: this.column.rcn || '',
|
||||
onDelete: 'NO ACTION',
|
||||
onUpdate: 'NO ACTION',
|
||||
updateRelation: !!this.column.rtn,
|
||||
type: 'real'
|
||||
relationType: 'real'
|
||||
}
|
||||
},
|
||||
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'])
|
||||
const result = (await this.$api.dbTable.list(this.$store.state.project.projectId, this.$store.state.project.project.bases[0].id))
|
||||
.list.filter(t => t.type === ModelTypes.TABLE)
|
||||
|
||||
this.refTables = result.data.list.map(({ tn, _tn }) => ({ tn, _tn }))
|
||||
this.refTables = result // .data.list.map(({ table_name, title }) => ({ table_name, title }))
|
||||
this.isRefTablesLoading = false
|
||||
},
|
||||
async saveManyToMany() {
|
||||
// try {
|
||||
// todo: toast
|
||||
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,
|
||||
alias: this.alias
|
||||
}
|
||||
])
|
||||
// } 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
|
||||
await this.$api.dbTableColumn.create(this.meta.id, {
|
||||
...this.relation,
|
||||
parentId: this.meta.id,
|
||||
uidt: UITypes.LinkToAnotherRecord,
|
||||
title: this.alias,
|
||||
type: this.type
|
||||
})
|
||||
|
||||
const columns = [...childMeta.columns, newChildColumn]
|
||||
|
||||
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,
|
||||
type: 'real',
|
||||
alias: this.alias
|
||||
}
|
||||
])
|
||||
// } catch (e) {
|
||||
// throw e
|
||||
// }
|
||||
await this.$store.dispatch('meta/ActLoadMeta', { id: this.relation.childId, force: true })
|
||||
},
|
||||
onColumnSelect() {
|
||||
const col = this.refColumns.find(c => this.relation.parentColumn === c.cn)
|
||||
const col = this.refColumns.find(c => this.relation.parentColumn === c.column_name)
|
||||
this.$emit('onColumnSelect', col)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
:label="$t('labels.childTable')"
|
||||
:full-width="false"
|
||||
:items="refTables"
|
||||
item-text="_ltn"
|
||||
item-text="title"
|
||||
:item-value="v => v"
|
||||
:rules="[v => !!v || 'Required']"
|
||||
dense
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption"><span class="font-weight-bold"> {{
|
||||
item._ltn
|
||||
}}</span> <small>({{ relationNames[item.type] }})
|
||||
item.title || item.table_name
|
||||
}}</span> <small>({{ relationNames[item.col.type] }})
|
||||
</small></span>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
@@ -35,7 +35,7 @@
|
||||
:label="$t('labels.childColumn')"
|
||||
:full-width="false"
|
||||
:items="columnList"
|
||||
item-text="_lcn"
|
||||
item-text="title"
|
||||
dense
|
||||
:loading="loadingColumns"
|
||||
:item-value="v => v"
|
||||
@@ -49,6 +49,8 @@
|
||||
|
||||
<script>
|
||||
|
||||
import { isSystemColumn, UITypes } from 'nocodb-sdk'
|
||||
|
||||
export default {
|
||||
name: 'LookupOptions',
|
||||
props: ['nodes', 'column', 'meta', 'isSQLite', 'alias'],
|
||||
@@ -64,62 +66,28 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
refTables() {
|
||||
return (this.meta
|
||||
? [
|
||||
...(this.meta.belongsTo || []).map(({ rtn, _rtn, rcn, tn, cn }) => ({
|
||||
type: 'bt',
|
||||
rtn,
|
||||
_rtn,
|
||||
rcn,
|
||||
tn,
|
||||
cn,
|
||||
ltn: rtn,
|
||||
_ltn: _rtn
|
||||
})),
|
||||
...(this.meta.hasMany || []).map(({
|
||||
tn,
|
||||
_tn,
|
||||
cn,
|
||||
rcn,
|
||||
rtn
|
||||
}) => ({
|
||||
type: 'hm',
|
||||
tn,
|
||||
_tn,
|
||||
cn,
|
||||
rcn,
|
||||
rtn,
|
||||
ltn: tn,
|
||||
_ltn: _tn
|
||||
})),
|
||||
...(this.meta.manyToMany || []).map(({ vtn, _vtn, vrcn, vcn, rtn, _rtn, rcn, tn, cn }) => ({
|
||||
type: 'mm',
|
||||
tn,
|
||||
cn,
|
||||
vtn,
|
||||
_vtn,
|
||||
vrcn,
|
||||
rcn,
|
||||
rtn,
|
||||
vcn,
|
||||
_rtn,
|
||||
ltn: rtn,
|
||||
_ltn: _rtn
|
||||
}))
|
||||
]
|
||||
: []).filter(t => this.tables.includes(t.ltn))
|
||||
if (!this.tables || !this.tables.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
const refTables = this.meta.columns.filter(c =>
|
||||
c.uidt === UITypes.LinkToAnotherRecord && !c.system
|
||||
).map(c => ({
|
||||
col: c.colOptions,
|
||||
...this.tables.find(t => t.id === c.colOptions.fk_related_model_id)
|
||||
}))
|
||||
|
||||
return refTables
|
||||
|
||||
},
|
||||
columnList() {
|
||||
return ((
|
||||
this.lookup &&
|
||||
this.lookup.table &&
|
||||
this.$store.state.meta.metas &&
|
||||
this.$store.state.meta.metas[this.lookup.table.ltn] &&
|
||||
this.$store.state.meta.metas[this.lookup.table.ltn].columns
|
||||
) || []).map(({ cn, _cn }) => ({
|
||||
lcn: cn,
|
||||
_lcn: _cn
|
||||
}))
|
||||
this.$store.state.meta.metas[this.lookup.table.id] &&
|
||||
this.$store.state.meta.metas[this.lookup.table.id].columns
|
||||
) || []).filter(c => !isSystemColumn(c))
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -127,12 +95,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadTablesList() {
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'tableList'])
|
||||
|
||||
this.tables = result.data.list.map(({ tn }) => tn)
|
||||
const result = (await this.$api.dbTable.list(this.$store.state.project.projectId, this.$store.state.project.project.bases[0].id))
|
||||
this.tables = result.list
|
||||
},
|
||||
checkLookupExist(v) {
|
||||
return (this.lookup.table && (this.meta.v || []).every(c => !(
|
||||
@@ -149,7 +113,7 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.lookup.table.ltn
|
||||
id: this.lookup.table.id
|
||||
})
|
||||
} catch (e) {
|
||||
// ignore
|
||||
@@ -160,32 +124,18 @@ export default {
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
const lookupCol = {
|
||||
title: this.alias,
|
||||
fk_relation_column_id: this.lookup.table.col.fk_column_id,
|
||||
fk_lookup_column_id: this.lookup.column.id,
|
||||
uidt: UITypes.Lookup
|
||||
}
|
||||
|
||||
meta.v.push({
|
||||
_cn: this.alias,
|
||||
lk: {
|
||||
...this.lookup.table,
|
||||
...this.lookup.column
|
||||
}
|
||||
})
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
await this.$api.dbTableColumn.create(this.meta.id, lookupCol)
|
||||
|
||||
return this.$emit('saved', this.alias)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
label="Reference Table"
|
||||
:full-width="false"
|
||||
:items="refTables"
|
||||
item-text="_tn"
|
||||
item-value="tn"
|
||||
item-text="title"
|
||||
item-value="table_name"
|
||||
required
|
||||
dense
|
||||
@change="loadColumnList"
|
||||
@@ -31,8 +31,8 @@
|
||||
label="Reference Column"
|
||||
:full-width="false"
|
||||
:items="refColumns"
|
||||
item-text="_cn"
|
||||
item-value="cn"
|
||||
item-text="title"
|
||||
item-value="column_name"
|
||||
required
|
||||
dense
|
||||
@change="onColumnSelect"
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'column.cn'(c) {
|
||||
'column.column_name'(c) {
|
||||
this.$set(this.relation, 'childColumn', c)
|
||||
},
|
||||
isSQLite(v) {
|
||||
@@ -127,8 +127,8 @@ export default {
|
||||
async created() {
|
||||
await this.loadTablesList()
|
||||
this.relation = {
|
||||
childColumn: this.column.cn,
|
||||
childTable: this.nodes.tn,
|
||||
childColumn: this.column.column_name,
|
||||
childTable: this.nodes.table_name,
|
||||
parentTable: this.column.rtn || '',
|
||||
parentColumn: this.column.rcn || '',
|
||||
onDelete: 'NO ACTION',
|
||||
@@ -148,7 +148,7 @@ export default {
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'columnList', { tn: this.relation.parentTable }])
|
||||
}, 'columnList', { table_name: this.relation.parentTable }])
|
||||
|
||||
const columns = result.data.list
|
||||
this.refColumns = JSON.parse(JSON.stringify(columns))
|
||||
@@ -160,7 +160,7 @@ export default {
|
||||
} else {
|
||||
// find pk column and assign to parentColumn
|
||||
const pkKeyColumns = this.refColumns.filter(el => el.pk)
|
||||
this.relation.parentColumn = (pkKeyColumns[0] || {}).cn || ''
|
||||
this.relation.parentColumn = (pkKeyColumns[0] || {}).column_name || ''
|
||||
}
|
||||
this.onColumnSelect()
|
||||
|
||||
@@ -174,7 +174,7 @@ export default {
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'tableList'])
|
||||
|
||||
this.refTables = result.data.list.map(({ tn, _tn }) => ({ tn, _tn }))
|
||||
this.refTables = result.data.list.map(({ table_name, title }) => ({ table_name, title }))
|
||||
this.isRefTablesLoading = false
|
||||
},
|
||||
async saveRelation() {
|
||||
@@ -187,12 +187,9 @@ export default {
|
||||
this.relation.type === 'real' && !this.isSQLite ? 'relationCreate' : 'xcVirtualRelationCreate',
|
||||
{ alias: this.alias, ...this.relation }
|
||||
])
|
||||
// } catch (e) {
|
||||
// throw e
|
||||
// }
|
||||
},
|
||||
onColumnSelect() {
|
||||
const col = this.refColumns.find(c => this.relation.parentColumn === c.cn)
|
||||
const col = this.refColumns.find(c => this.relation.parentColumn === c.column_name)
|
||||
this.$emit('onColumnSelect', col)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
:label="$t('labels.childTable')"
|
||||
:full-width="false"
|
||||
:items="refTables"
|
||||
item-text="_rltn"
|
||||
item-text="title"
|
||||
:item-value="v => v"
|
||||
:rules="[v => !!v || 'Required']"
|
||||
dense
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption"><span class="font-weight-bold"> {{
|
||||
item._rltn
|
||||
}}</span> <small>({{ relationNames[item.type] }})
|
||||
item.title || item.table_name
|
||||
}}</span> <small>({{ relationNames[item.col.type] }})
|
||||
</small></span>
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
@@ -35,7 +35,7 @@
|
||||
:label="$t('labels.childColumn')"
|
||||
:full-width="false"
|
||||
:items="columnList"
|
||||
item-text="_rlcn"
|
||||
item-text="title"
|
||||
dense
|
||||
:loading="loadingColumns"
|
||||
:item-value="v => v"
|
||||
@@ -64,6 +64,8 @@
|
||||
|
||||
<script>
|
||||
|
||||
import { isSystemColumn, UITypes } from 'nocodb-sdk'
|
||||
|
||||
export default {
|
||||
name: 'RollupOptions',
|
||||
props: ['nodes', 'column', 'meta', 'isSQLite', 'alias'],
|
||||
@@ -90,62 +92,25 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
refTables() {
|
||||
return (this.meta
|
||||
? [
|
||||
// ...(this.meta.belongsTo || []).map(({ rtn, _rtn, rcn, tn, cn }) => ({
|
||||
// type: 'bt',
|
||||
// rtn,
|
||||
// _rtn,
|
||||
// rcn,
|
||||
// tn,
|
||||
// cn,
|
||||
// ltn: rtn,
|
||||
// _ltn: _rtn
|
||||
// })),
|
||||
...(this.meta.hasMany || []).map(({
|
||||
tn,
|
||||
_tn,
|
||||
cn,
|
||||
rcn,
|
||||
rtn
|
||||
}) => ({
|
||||
type: 'hm',
|
||||
tn,
|
||||
_tn,
|
||||
cn,
|
||||
rcn,
|
||||
rtn,
|
||||
rltn: tn,
|
||||
_rltn: _tn
|
||||
})),
|
||||
...(this.meta.manyToMany || []).map(({ vtn, _vtn, vrcn, vcn, rtn, _rtn, rcn, tn, cn }) => ({
|
||||
type: 'mm',
|
||||
tn,
|
||||
cn,
|
||||
vtn,
|
||||
_vtn,
|
||||
vrcn,
|
||||
rcn,
|
||||
rtn,
|
||||
vcn,
|
||||
_rtn,
|
||||
rltn: rtn,
|
||||
_rltn: _rtn
|
||||
}))
|
||||
]
|
||||
: []).filter(t => this.tables.includes(t.rltn))
|
||||
if (!this.tables || !this.tables.length) { return [] }
|
||||
|
||||
const refTables = this.meta.columns.filter(c =>
|
||||
c.uidt === UITypes.LinkToAnotherRecord && c.colOptions.type !== 'bt' && !c.system
|
||||
).map(c => ({
|
||||
col: c.colOptions,
|
||||
...this.tables.find(t => t.id === c.colOptions.fk_related_model_id)
|
||||
}))
|
||||
|
||||
return refTables
|
||||
},
|
||||
columnList() {
|
||||
return ((
|
||||
this.rollup &&
|
||||
this.rollup.table &&
|
||||
this.$store.state.meta.metas &&
|
||||
this.$store.state.meta.metas[this.rollup.table.rltn] &&
|
||||
this.$store.state.meta.metas[this.rollup.table.rltn].columns
|
||||
) || []).map(({ cn, _cn }) => ({
|
||||
rlcn: cn,
|
||||
_rlcn: _cn
|
||||
}))
|
||||
this.$store.state.meta.metas[this.rollup.table.table_name] &&
|
||||
this.$store.state.meta.metas[this.rollup.table.table_name].columns
|
||||
) || []).filter(col => ![UITypes.Lookup, UITypes.Rollup, UITypes.LinkToAnotherRecord].includes(col.uidt) && !isSystemColumn(col))
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -153,12 +118,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadTablesList() {
|
||||
const result = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'tableList'])
|
||||
const result = (await this.$api.dbTable.list(this.$store.state.project.projectId, this.$store.state.project.project.bases[0].id))
|
||||
|
||||
this.tables = result.data.list.map(({ tn }) => tn)
|
||||
this.tables = result.list
|
||||
},
|
||||
async onTableChange() {
|
||||
this.loadingColumns = true
|
||||
@@ -167,7 +129,7 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.rollup.table.ltn
|
||||
id: this.rollup.table.id
|
||||
})
|
||||
} catch (e) {
|
||||
// ignore
|
||||
@@ -178,30 +140,15 @@ export default {
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
const rollupCol = {
|
||||
title: this.alias,
|
||||
fk_relation_column_id: this.rollup.table.col.fk_column_id,
|
||||
fk_rollup_column_id: this.rollup.column.id,
|
||||
uidt: UITypes.Rollup,
|
||||
rollup_function: this.rollup.fn
|
||||
}
|
||||
|
||||
meta.v.push({
|
||||
_cn: this.alias,
|
||||
rl: {
|
||||
...this.rollup.table,
|
||||
...this.rollup.column,
|
||||
fn: this.rollup.fn
|
||||
}
|
||||
})
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
await this.$api.dbTableColumn.create(this.meta.id, rollupCol)
|
||||
|
||||
return this.$emit('saved', this.alias)
|
||||
} catch (e) {
|
||||
|
||||
@@ -12,40 +12,49 @@
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
ref="column"
|
||||
v-model="newColumn._cn"
|
||||
v-model="newColumn.title"
|
||||
hide-details="auto"
|
||||
color="primary"
|
||||
class="caption nc-column-name-input"
|
||||
:label="$t('labels.columnName')"
|
||||
: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',
|
||||
v => !meta || !meta.columns || !column ||meta.columns.every(c => column === c || (v !== c.title)) || 'Duplicate column name',
|
||||
validateColumnName
|
||||
]"
|
||||
dense
|
||||
outlined
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="column.formula" cols="12">
|
||||
<v-col v-if="newColumn && newColumn.uidt === UITypes.Formula" cols="12">
|
||||
<formula-options
|
||||
ref="formula"
|
||||
:value="column.formula"
|
||||
v-model="newColumn.formula_raw"
|
||||
:column="column"
|
||||
:new-column="newColumn"
|
||||
:nodes="nodes"
|
||||
:meta="meta"
|
||||
:alias="newColumn._cn"
|
||||
:alias="newColumn.title"
|
||||
:sql-ui="sqlUi"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12" class="d-flex pt-0">
|
||||
<v-spacer />
|
||||
<v-btn x-small outlined @click="close">
|
||||
<v-btn
|
||||
x-small
|
||||
outlined
|
||||
@click="close"
|
||||
>
|
||||
<!-- Cancel -->
|
||||
{{ $t('general.cancel') }}
|
||||
</v-btn>
|
||||
<v-btn x-small color="primary" :disabled="!valid" @click="save">
|
||||
<v-btn
|
||||
v-t="['virtual:column:edit']"
|
||||
x-small
|
||||
color="primary"
|
||||
:disabled="!valid"
|
||||
@click="save"
|
||||
>
|
||||
<!-- Save -->
|
||||
{{ $t('general.save') }}
|
||||
</v-btn>
|
||||
@@ -57,6 +66,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UITypes, substituteColumnIdWithAliasInFormula } from 'nocodb-sdk'
|
||||
import FormulaOptions from '@/components/project/spreadsheet/components/editColumn/formulaOptions'
|
||||
import { validateColumnName } from '~/helpers'
|
||||
|
||||
@@ -72,17 +82,28 @@ export default {
|
||||
},
|
||||
data: () => ({
|
||||
valid: false,
|
||||
newColumn: {}
|
||||
newColumn: {},
|
||||
UITypes
|
||||
}),
|
||||
watch: {
|
||||
column(c) {
|
||||
this.newColumn = { ...c }
|
||||
const { colOptions, ...rest } = c
|
||||
this.newColumn = rest
|
||||
|
||||
if (rest.uidt === UITypes.Formula) {
|
||||
this.newColumn.formula_raw = substituteColumnIdWithAliasInFormula(colOptions.formula, this.meta.columns, colOptions.formula_raw)
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
},
|
||||
mounted() {
|
||||
this.newColumn = { ...this.column }
|
||||
const { colOptions, ...rest } = this.column
|
||||
this.newColumn = rest
|
||||
|
||||
if (rest.uidt === UITypes.Formula) {
|
||||
this.newColumn.formula_raw = substituteColumnIdWithAliasInFormula(colOptions.formula, this.meta.columns, colOptions.formula_raw)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
@@ -92,25 +113,12 @@ export default {
|
||||
async save() {
|
||||
// todo: rollup update
|
||||
try {
|
||||
if (this.column.formula) {
|
||||
await this.$refs.formula.update()
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
await this.$api.dbTableColumn.update(this.meta.id, this.column.id, this.newColumn)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.log(this._extractSdkResponseErrorMsg(e))
|
||||
this.$toast.error('Failed to update column alias').goAway(3000)
|
||||
}
|
||||
this.$emit('saved', this.newColumn._cn, this.column._cn)
|
||||
this.$emit('saved', this.newColumn.title, this.column.title)
|
||||
this.$emit('input', false)
|
||||
},
|
||||
|
||||
|
||||
@@ -19,6 +19,13 @@ export default {
|
||||
return { ...this.$listeners, input: this.onInput }
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
if (this.$refs.editable.innerText !== v) {
|
||||
this.$refs.editable.innerText = v
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.editable.innerText = this.value
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
:column="column"
|
||||
:is-public-grid="isPublic && !isForm"
|
||||
:is-public-form="isPublic && isForm"
|
||||
:view-id="viewId"
|
||||
:is-locked="isLocked"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
@@ -162,7 +163,8 @@ export default {
|
||||
dummy: Boolean,
|
||||
hint: String,
|
||||
isLocked: Boolean,
|
||||
isPublic: Boolean
|
||||
isPublic: Boolean,
|
||||
viewId: String
|
||||
},
|
||||
data: () => ({
|
||||
changed: false,
|
||||
|
||||
@@ -18,20 +18,11 @@ export default {
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
// this.$emit('update');
|
||||
}
|
||||
},
|
||||
|
||||
parentListeners() {
|
||||
const $listeners = {}
|
||||
|
||||
// if (this.$listeners.blur) {
|
||||
// $listeners.blur = this.$listeners.blur
|
||||
// }
|
||||
// if (this.$listeners.focus) {
|
||||
// $listeners.focus = this.$listeners.focus
|
||||
// }
|
||||
|
||||
return $listeners
|
||||
}
|
||||
},
|
||||
|
||||
@@ -41,11 +41,11 @@ export default {
|
||||
return (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value))
|
||||
.format('YYYY-MM-DD HH:mm')
|
||||
},
|
||||
set(val) {
|
||||
if(this.$parent.sqlUi.name == 'MysqlUi') {
|
||||
this.$emit('input', val && dayjs(val).format('YYYY-MM-DD HH:mm:ss'))
|
||||
set(value) {
|
||||
if (this.$parent.sqlUi.name === 'MysqlUi') {
|
||||
this.$emit('input', value && dayjs(value).format('YYYY-MM-DD HH:mm:ss'))
|
||||
} else {
|
||||
this.$emit('input', val && dayjs(val).format('YYYY-MM-DD HH:mm:ssZ'))
|
||||
this.$emit('input', value && dayjs(value).format('YYYY-MM-DD HH:mm:ssZ'))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,47 +20,46 @@
|
||||
<div class="d-flex align-center img-container">
|
||||
<div class="d-flex no-overflow">
|
||||
<div
|
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
|
||||
:key="item.url || item.title"
|
||||
class="thumbnail align-center justify-center d-flex"
|
||||
>
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{on}">
|
||||
<!-- <img alt="#" v-if="isImage(item.title)" :src="item.url" v-on="on" @click="selectImage(item.url,i)">-->
|
||||
<v-img
|
||||
v-if="isImage(item.title)"
|
||||
lazy-src="https://via.placeholder.com/60.png?text=Loading..."
|
||||
alt="#"
|
||||
max-height="33px"
|
||||
contain
|
||||
:src="item.url || item.data"
|
||||
v-on="on"
|
||||
@click="selectImage(item.url || item.data, i)"
|
||||
>
|
||||
<template #placeholder>
|
||||
<v-skeleton-loader
|
||||
type="image"
|
||||
:height="active ? 33 : 22"
|
||||
:width="active ? 33 : 22"
|
||||
/>
|
||||
</template>
|
||||
</v-img>
|
||||
<v-icon
|
||||
v-else-if="item.icon"
|
||||
:size="active ? 33 : 22"
|
||||
v-on="on"
|
||||
@click="openUrl(item.url || item.data,'_blank')"
|
||||
>
|
||||
{{
|
||||
item.icon
|
||||
}}
|
||||
</v-icon>
|
||||
<v-icon v-else :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url|| item.data,'_blank')">
|
||||
mdi-file
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>{{ item.title }}</span>
|
||||
</v-tooltip>
|
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
|
||||
:key="item.url || item.title"
|
||||
class="thumbnail align-center justify-center d-flex"
|
||||
>
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{on}">
|
||||
<v-img
|
||||
v-if="isImage(item.title)"
|
||||
lazy-src="https://via.placeholder.com/60.png?text=Loading..."
|
||||
alt="#"
|
||||
max-height="33px"
|
||||
contain
|
||||
:src="item.url || item.data"
|
||||
v-on="on"
|
||||
@click="selectImage(item.url || item.data, i)"
|
||||
>
|
||||
<template #placeholder>
|
||||
<v-skeleton-loader
|
||||
type="image"
|
||||
:height="active ? 33 : 22"
|
||||
:width="active ? 33 : 22"
|
||||
/>
|
||||
</template>
|
||||
</v-img>
|
||||
<v-icon
|
||||
v-else-if="item.icon"
|
||||
:size="active ? 33 : 22"
|
||||
v-on="on"
|
||||
@click="openUrl(item.url || item.data,'_blank')"
|
||||
>
|
||||
{{
|
||||
item.icon
|
||||
}}
|
||||
</v-icon>
|
||||
<v-icon v-else :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url|| item.data,'_blank')">
|
||||
mdi-file
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>{{ item.title }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isForm || active && !isPublicGrid && !isLocked" class="add d-flex align-center justify-center px-1 nc-attachment-add" @click="addFile">
|
||||
@@ -214,7 +213,6 @@
|
||||
v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
|
||||
:key="i"
|
||||
>
|
||||
<!-- <div class="d-flex justify-center" style="height:80px">-->
|
||||
<v-card
|
||||
:key="i"
|
||||
class="ma-2 pa-2 d-flex align-center justify-center overlay-thumbnail"
|
||||
@@ -235,12 +233,9 @@
|
||||
mdi-file
|
||||
</v-icon>
|
||||
</v-card>
|
||||
<!-- </div>-->
|
||||
</v-slide-item>
|
||||
</v-slide-group>
|
||||
</v-sheet>
|
||||
<!-- <v-img v-if="showImage && selectedImage" max-width="90vh" max-height="95vh"-->
|
||||
<!-- :src="selectedImage"></v-img>-->
|
||||
<v-icon x-large class="close-icon" @click="showImage=false">
|
||||
mdi-close-circle
|
||||
</v-icon>
|
||||
@@ -257,7 +252,7 @@ import { isImage } from '@/components/project/spreadsheet/helpers/imageExt'
|
||||
export default {
|
||||
name: 'EditableAttachmentCell',
|
||||
components: { draggable },
|
||||
props: ['dbAlias', 'value', 'active', 'isLocked', 'meta', 'column', 'isPublicGrid', 'isForm', 'isPublicForm'],
|
||||
props: ['dbAlias', 'value', 'active', 'isLocked', 'meta', 'column', 'isPublicGrid', 'isForm', 'isPublicForm', 'viewId'],
|
||||
data: () => ({
|
||||
carousel: null,
|
||||
uploading: false,
|
||||
@@ -271,20 +266,15 @@ export default {
|
||||
watch: {
|
||||
value(val, prev) {
|
||||
try {
|
||||
this.localState = (typeof val === 'string' && val !== prev ? JSON.parse(val) : val) || []
|
||||
this.localState = ((typeof val === 'string' && val !== prev ? JSON.parse(val) : val) || []).filter(Boolean)
|
||||
} catch (e) {
|
||||
this.localState = []
|
||||
}
|
||||
}
|
||||
// localState(val) {
|
||||
// if (this.isForm) {
|
||||
// this.$emit('input', JSON.stringify(val))
|
||||
// }
|
||||
// }
|
||||
},
|
||||
created() {
|
||||
try {
|
||||
this.localState = (typeof this.value === 'string' ? JSON.parse(this.value) : this.value) || []
|
||||
this.localState = ((typeof this.value === 'string' ? JSON.parse(this.value) : this.value) || []).filter(Boolean)
|
||||
} catch (e) {
|
||||
this.localState = []
|
||||
}
|
||||
@@ -343,13 +333,13 @@ export default {
|
||||
this.uploading = true
|
||||
for (const file of this.$refs.file.files) {
|
||||
try {
|
||||
const item = await this.$store.dispatch('sqlMgr/ActUploadOld', [{
|
||||
dbAlias: this.dbAlias
|
||||
}, 'xcAttachmentUpload', {
|
||||
appendPath: [this.meta.tn],
|
||||
prependName: [this.column.cn]
|
||||
}, file])
|
||||
this.localState.push(item)
|
||||
|
||||
const data = await this.$api.dbView.upload(this.$store.state.project.projectId, this.viewId, {
|
||||
files: file,
|
||||
json: '{}'
|
||||
})
|
||||
|
||||
this.localState.push(...data)
|
||||
} catch (e) {
|
||||
this.$toast.error((e.message) || 'Some internal error occurred').goAway(3000)
|
||||
this.uploading = false
|
||||
|
||||
@@ -16,7 +16,6 @@ export default {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
console.log(isValidURL(val))
|
||||
if (isValidURL(val)) { this.$emit('input', val) }
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
:clearable="!column.rqd"
|
||||
v-on="parentListeners"
|
||||
>
|
||||
<!-- <option v-for="eVal of enumValues" :key="eVal" :value="eVal">{{ eVal }}</option>-->
|
||||
<template #selection="{item}">
|
||||
<div
|
||||
class="d-100"
|
||||
|
||||
@@ -98,8 +98,6 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.cell-container {
|
||||
/*margin: 0 -5px;*/
|
||||
/*position: relative;*/
|
||||
width: 100%
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <select v-on="parentListeners" v-model="localState" multiple>
|
||||
<option v-for="val of setValues" :key="val" :value="val">{{ val }}</option>
|
||||
</select>-->
|
||||
|
||||
<v-combobox
|
||||
v-model="localState"
|
||||
@@ -18,7 +15,7 @@
|
||||
>
|
||||
<template #selection="data">
|
||||
<v-chip
|
||||
:key="data"
|
||||
:key="data.item"
|
||||
small
|
||||
class="ma-1 "
|
||||
:color="colors[setValues.indexOf(data.item) % colors.length]"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
export default {
|
||||
name: 'TextCell',
|
||||
props: {
|
||||
value: String
|
||||
value: [String, Object, Number, Boolean, Array]
|
||||
},
|
||||
computed: {
|
||||
localState: {
|
||||
|
||||
@@ -40,7 +40,6 @@ export default {
|
||||
return dateTime.format('HH:mm:ss')
|
||||
},
|
||||
set(val) {
|
||||
console.log(val)
|
||||
const dateTime = dayjs(`1999-01-01 ${val}:00`)
|
||||
if (dateTime.isValid()) { this.$emit('input', dateTime.format('YYYY-MM-DD HH:mm:ssZ')) }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</v-icon>
|
||||
|
||||
<template v-if="meta">
|
||||
{{ meta._tn }}
|
||||
{{ meta.title }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ table }}
|
||||
@@ -22,15 +22,6 @@
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<x-icon
|
||||
:tooltip="`${showSystemFields ? 'Hide' : 'Show'} system fields`"
|
||||
icon.class="mr-3 mt-n1"
|
||||
small
|
||||
@click="showSystemFields = !showSystemFields"
|
||||
>
|
||||
mdi-table-headers-eye
|
||||
</x-icon>
|
||||
|
||||
<x-icon
|
||||
v-if="!isNew && _isUIAllowed('rowComments')"
|
||||
icon-class="mr-2"
|
||||
@@ -82,18 +73,18 @@
|
||||
v-for="(col,i) in fields"
|
||||
>
|
||||
<div
|
||||
v-if="!col.lk"
|
||||
v-if="!col.lk && (!showFields || showFields[col.title])"
|
||||
:key="i"
|
||||
:class="{
|
||||
'active-row' : active === col._cn,
|
||||
'active-row' : active === col.title,
|
||||
required: isValid(col, localState)
|
||||
}"
|
||||
class="row-col my-4"
|
||||
>
|
||||
<div>
|
||||
<label :for="`data-table-form-${col._cn}`" class="body-2 text-capitalize">
|
||||
<label :for="`data-table-form-${col.title}`" class="body-2 text-capitalize">
|
||||
<virtual-header-cell
|
||||
v-if="col.virtual"
|
||||
v-if="col.colOptions"
|
||||
:column="col"
|
||||
:nodes="nodes"
|
||||
:is-form="true"
|
||||
@@ -102,15 +93,15 @@
|
||||
<header-cell
|
||||
v-else
|
||||
:is-form="true"
|
||||
:is-foreign-key="col.cn in belongsTo || col.cn in hasMany"
|
||||
:value="col._cn"
|
||||
:is-foreign-key="col.type === UITypes.ForeignKey"
|
||||
:value="col.title"
|
||||
:column="col"
|
||||
:sql-ui="sqlUi"
|
||||
/>
|
||||
|
||||
</label>
|
||||
<virtual-cell
|
||||
v-if="col.virtual"
|
||||
v-if="isVirtualCol(col)"
|
||||
ref="virtual"
|
||||
:disabled-columns="disabledColumns"
|
||||
:column="col"
|
||||
@@ -119,7 +110,6 @@
|
||||
:meta="meta"
|
||||
:api="api"
|
||||
:active="true"
|
||||
:sql-ui="sqlUi"
|
||||
:is-new="isNew"
|
||||
:is-form="true"
|
||||
:breadcrumbs="localBreadcrumbs"
|
||||
@@ -128,7 +118,7 @@
|
||||
/>
|
||||
|
||||
<div
|
||||
v-else-if="col.ai || (col.pk && !isNew) || disabledColumns[col._cn]"
|
||||
v-else-if="col.ai || (col.pk && !isNew) || disabledColumns[col.title]"
|
||||
style="height:100%; width:100%"
|
||||
class="caption xc-input"
|
||||
@click="col.ai && $toast.info('Auto Increment field is not editable').goAway(3000)"
|
||||
@@ -137,14 +127,14 @@
|
||||
style="height:100%; width: 100%"
|
||||
readonly
|
||||
disabled
|
||||
:value="localState[col._cn]"
|
||||
:value="localState[col.title]"
|
||||
>
|
||||
</div>
|
||||
|
||||
<editable-cell
|
||||
v-else
|
||||
:id="`data-table-form-${col._cn}`"
|
||||
v-model="localState[col._cn]"
|
||||
:id="`data-table-form-${col.title}`"
|
||||
v-model="localState[col.title]"
|
||||
:db-alias="dbAlias"
|
||||
:column="col"
|
||||
class="xc-input body-2"
|
||||
@@ -152,9 +142,9 @@
|
||||
:sql-ui="sqlUi"
|
||||
:is-form="true"
|
||||
:is-locked="isLocked"
|
||||
@focus="active = col._cn"
|
||||
@focus="active = col.title"
|
||||
@blur="active = ''"
|
||||
@input="$set(changedColumns,col._cn, true)"
|
||||
@input="$set(changedColumns,col.title, true)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,37 +172,46 @@
|
||||
'darken-4':$vuetify.theme.dark
|
||||
}"
|
||||
>
|
||||
<v-list-item v-for="log in logs" :key="log.id" class="d-flex">
|
||||
<v-list-item-icon class="ma-0 mr-2">
|
||||
<v-icon :color="isYou(log.user) ? 'pink lighten-2' : 'blue lighten-2'">
|
||||
mdi-account-circle
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<div class="flex-grow-1" style="min-width: 0">
|
||||
<p class="mb-1 caption edited-text">
|
||||
{{ isYou(log.user) ? 'You' : log.user==null?'Shared base':log.user }} {{
|
||||
log.op_type === 'COMMENT' ? 'commented' : (
|
||||
log.op_sub_type === 'INSERT' ? 'created' : 'edited'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p v-if="log.op_type === 'COMMENT'" class="caption mb-0 nc-chip" :style="{background :colors[2]}">
|
||||
{{ log.description }}
|
||||
</p>
|
||||
<div>
|
||||
<v-list-item v-for="log in logs" :key="log.id" class="d-flex">
|
||||
<v-list-item-icon class="ma-0 mr-2">
|
||||
<v-icon :color="isYou(log.user) ? 'pink lighten-2' : 'blue lighten-2'">
|
||||
mdi-account-circle
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<div class="flex-grow-1" style="min-width: 0">
|
||||
<p class="mb-1 caption edited-text">
|
||||
{{ isYou(log.user) ? 'You' : log.user == null ? 'Shared base' : log.user }} {{
|
||||
log.op_type === 'COMMENT' ? 'commented' : (
|
||||
log.op_sub_type === 'INSERT' ? 'created' : 'edited'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p v-if="log.op_type === 'COMMENT'" class="caption mb-0 nc-chip" :style="{background :colors[2]}">
|
||||
{{ log.description }}
|
||||
</p>
|
||||
|
||||
<p v-else class="caption mb-0" style="word-break: break-all;" v-html="log.details" />
|
||||
<p v-else class="caption mb-0" style="word-break: break-all;" v-html="log.details" />
|
||||
|
||||
<p class="time text-right mb-0">
|
||||
{{ calculateDiff(log.created_at) }}
|
||||
</p>
|
||||
</div>
|
||||
</v-list-item>
|
||||
<p class="time text-right mb-0">
|
||||
{{ calculateDiff(log.created_at) }}
|
||||
</p>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</v-list>
|
||||
|
||||
<v-spacer />
|
||||
<v-divider />
|
||||
<div class="d-flex align-center justify-center">
|
||||
<v-switch v-model="commentsOnly" class="mt-1" dense hide-details @change="getAuditsAndComments">
|
||||
<v-switch
|
||||
v-model="commentsOnly"
|
||||
v-t="['record:comment:comments-only']"
|
||||
class="mt-1"
|
||||
dense
|
||||
hide-details
|
||||
@change="getAuditsAndComments"
|
||||
>
|
||||
<template #label>
|
||||
<span class="caption grey--text">Comments only</span>
|
||||
</template>
|
||||
@@ -251,6 +250,7 @@
|
||||
<v-btn
|
||||
v-if="_isUIAllowed('rowComments')"
|
||||
v-show="!toggleDrawer"
|
||||
v-t="['record:comment-toggle']"
|
||||
class="comment-icon"
|
||||
color="primary"
|
||||
fab
|
||||
@@ -264,13 +264,13 @@
|
||||
<script>
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import { AuditOperationSubTypes, AuditOperationTypes, isVirtualCol, UITypes } from 'nocodb-sdk'
|
||||
import form from '../mixins/form'
|
||||
import HeaderCell from '@/components/project/spreadsheet/components/headerCell'
|
||||
import EditableCell from '@/components/project/spreadsheet/components/editableCell'
|
||||
import colors from '@/mixins/colors'
|
||||
import VirtualCell from '@/components/project/spreadsheet/components/virtualCell'
|
||||
import VirtualHeaderCell from '@/components/project/spreadsheet/components/virtualHeaderCell'
|
||||
import { UITypes } from '@/components/project/spreadsheet/helpers/uiTypes'
|
||||
|
||||
const relativeTime = require('dayjs/plugin/relativeTime')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
@@ -278,9 +278,15 @@ dayjs.extend(utc)
|
||||
dayjs.extend(relativeTime)
|
||||
export default {
|
||||
name: 'ExpandedForm',
|
||||
components: { VirtualHeaderCell, VirtualCell, EditableCell, HeaderCell },
|
||||
components: {
|
||||
VirtualHeaderCell,
|
||||
VirtualCell,
|
||||
EditableCell,
|
||||
HeaderCell
|
||||
},
|
||||
mixins: [colors, form],
|
||||
props: {
|
||||
showFields: Object,
|
||||
showNextPrev: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -295,8 +301,8 @@ export default {
|
||||
value: Object,
|
||||
table: String,
|
||||
primaryValueColumn: String,
|
||||
hasMany: [Object, Array],
|
||||
belongsTo: [Object, Array],
|
||||
// hasMany: [Object, Array],
|
||||
// belongsTo: [Object, Array],
|
||||
isNew: Boolean,
|
||||
oldRow: Object,
|
||||
iconColor: {
|
||||
@@ -307,9 +313,11 @@ export default {
|
||||
queryParams: Object,
|
||||
meta: Object,
|
||||
presetValues: Object,
|
||||
isLocked: Boolean,
|
||||
isLocked: Boolean
|
||||
},
|
||||
data: () => ({
|
||||
isVirtualCol,
|
||||
UITypes,
|
||||
showborder: false,
|
||||
loadingLogs: true,
|
||||
toggleDrawer: false,
|
||||
@@ -323,7 +331,7 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
primaryKey() {
|
||||
return this.isNew ? '' : this.meta.columns.filter(c => c.pk).map(c => this.localState[c._cn]).join('___')
|
||||
return this.isNew ? '' : this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
|
||||
},
|
||||
edited() {
|
||||
return !!Object.keys(this.changedColumns).length
|
||||
@@ -338,8 +346,8 @@ export default {
|
||||
if (this.showSystemFields) {
|
||||
return this.meta.columns || []
|
||||
} else {
|
||||
return this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) &&
|
||||
!((this.meta.v || []).some(v => v.bt && v.bt.cn === c.cn))
|
||||
return this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.column_name) &&
|
||||
!((this.meta.v || []).some(v => v.bt && v.bt.column_name === c.column_name))
|
||||
) || []
|
||||
}
|
||||
},
|
||||
@@ -347,7 +355,7 @@ export default {
|
||||
return Object.values(this.changedColumns).some(Boolean)
|
||||
},
|
||||
localBreadcrumbs() {
|
||||
return [...this.breadcrumbs, `${this.meta ? this.meta._tn : this.table} (${this.primaryValue()})`]
|
||||
return [...this.breadcrumbs, `${this.meta ? this.meta.title : this.table} ${this.primaryValue() ? `(${this.primaryValue()})` : ''}`]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -389,17 +397,25 @@ export default {
|
||||
},
|
||||
async getAuditsAndComments() {
|
||||
this.loadingLogs = true
|
||||
const data = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.dbAlias }, 'xcModelRowAuditAndCommentList', {
|
||||
model_id: this.meta.columns.filter(c => c.pk).map(c => this.localState[c._cn]).join('___'),
|
||||
model_name: this.meta._tn,
|
||||
comments: this.commentsOnly
|
||||
}])
|
||||
this.logs = data.list
|
||||
|
||||
const data = (await this.$api.utils.commentList({
|
||||
row_id: this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___'),
|
||||
fk_model_id: this.meta.id,
|
||||
comments_only: this.commentsOnly
|
||||
}))
|
||||
|
||||
this.logs = data.reverse()
|
||||
this.loadingLogs = false
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.commentsList && this.$refs.commentsList.$el && this.$refs.commentsList.$el.firstElementChild) {
|
||||
this.$refs.commentsList.$el.scrollTop = this.$refs.commentsList.$el.firstElementChild.offsetHeight
|
||||
}
|
||||
})
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c._cn]).join('___')
|
||||
const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
|
||||
|
||||
if (this.presetValues) {
|
||||
// cater presetValues
|
||||
@@ -414,7 +430,7 @@ export default {
|
||||
}, {})
|
||||
|
||||
if (this.isNew) {
|
||||
const data = await this.api.insert(updatedObj)
|
||||
const data = (await this.$api.data.create(this.viewId || this.meta.id, updatedObj))
|
||||
this.localState = { ...this.localState, ...data }
|
||||
|
||||
// save hasmany and manytomany relations from local state
|
||||
@@ -431,7 +447,18 @@ export default {
|
||||
if (!id) {
|
||||
return this.$toast.info('Update not allowed for table which doesn\'t have primary Key').goAway(3000)
|
||||
}
|
||||
await this.api.update(id, updatedObj, this.oldRow)
|
||||
await this.$api.data.update(this.viewId || this.meta.id, id, updatedObj)
|
||||
for (const key of Object.keys(updatedObj)) {
|
||||
// audit
|
||||
this.$api.utils.auditRowUpdate({
|
||||
fk_model_id: this.meta.id,
|
||||
column_name: key,
|
||||
row_id: id,
|
||||
value: updatedObj[key],
|
||||
prev_value: this.oldRow[key]
|
||||
}).then(() => {
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return this.$toast.info('No columns to update').goAway(3000)
|
||||
}
|
||||
@@ -447,31 +474,25 @@ export default {
|
||||
} catch (e) {
|
||||
this.$toast.error(`Failed to update row : ${e.message}`).goAway(3000)
|
||||
}
|
||||
this.$tele.emit('record:add:submit')
|
||||
},
|
||||
async reload() {
|
||||
const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c._cn]).join('___')
|
||||
// const where = this.meta.columns.filter(c => c.pk).map(c => `(${c._cn},eq,${this.localState[c._cn]})`).join('~and')
|
||||
const id = this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___')
|
||||
this.$set(this, 'changedColumns', {})
|
||||
this.localState = await this.api.read(id, this.queryParams || {})
|
||||
// const data = await this.api.list({ ...(this.queryParams || {}), where }) || [{}]
|
||||
// this.localState = data[0] || this.localState
|
||||
if (!this.isNew && this.toggleDrawer) {
|
||||
this.getAuditsAndComments()
|
||||
}
|
||||
this.localState = (await this.$api.data.read(this.viewId || this.meta.id, id, { query: this.queryParams || {} }))
|
||||
},
|
||||
calculateDiff(date) {
|
||||
return dayjs.utc(date).fromNow()
|
||||
},
|
||||
async saveComment() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{ dbAlias: this.dbAlias },
|
||||
'xcAuditCommentInsert', {
|
||||
model_id: this.meta.columns.filter(c => c.pk).map(c => this.localState[c._cn]).join('___'),
|
||||
model_name: this.meta._tn,
|
||||
description: this.comment
|
||||
}
|
||||
])
|
||||
|
||||
await this.$api.utils.commentRow({
|
||||
fk_model_id: this.meta.id,
|
||||
row_id: this.meta.columns.filter(c => c.pk).map(c => this.localState[c.title]).join('___'),
|
||||
description: this.comment
|
||||
})
|
||||
|
||||
this.comment = ''
|
||||
this.$toast.success('Comment added successfully').goAway(3000)
|
||||
this.$emit('commented')
|
||||
@@ -479,22 +500,32 @@ export default {
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('record:comment:insert')
|
||||
},
|
||||
primaryValue() {
|
||||
if (this.localState) {
|
||||
const value = this.localState[this.primaryValueColumn]
|
||||
const col = this.meta.columns.find(c => c._cn == this.primaryValueColumn)
|
||||
if (!col) { return }
|
||||
const col = this.meta.columns.find(c => c.title == this.primaryValueColumn)
|
||||
if (!col) {
|
||||
return
|
||||
}
|
||||
const uidt = col.uidt
|
||||
if (uidt == UITypes.Date) {
|
||||
return (/^\d+$/.test(value) ? dayjs(+value) : dayjs(value)).format('YYYY-MM-DD')
|
||||
} else if (uidt == UITypes.DateTime) {
|
||||
return (/^\d+$/.test(this.value) ? dayjs(+this.value) : dayjs(this.value)).format('YYYY-MM-DD HH:mm')
|
||||
return (/^\d+$/.test(value) ? dayjs(+value) : dayjs(value)).format('YYYY-MM-DD HH:mm')
|
||||
} else if (uidt == UITypes.Time) {
|
||||
let dateTime = dayjs(value)
|
||||
if (!dateTime.isValid()) { dateTime = dayjs(value, 'HH:mm:ss') }
|
||||
if (!dateTime.isValid()) { dateTime = dayjs(`1999-01-01 ${value}`) }
|
||||
if (!dateTime.isValid()) { return value }
|
||||
if (!dateTime.isValid()) {
|
||||
dateTime = dayjs(value, 'HH:mm:ss')
|
||||
}
|
||||
if (!dateTime.isValid()) {
|
||||
dateTime = dayjs(`1999-01-01 ${value}`)
|
||||
}
|
||||
if (!dateTime.isValid()) {
|
||||
return value
|
||||
}
|
||||
return dateTime.format('HH:mm:ss')
|
||||
}
|
||||
return value
|
||||
@@ -635,8 +666,8 @@ h5 {
|
||||
background: var(--v-backgroundColorDefault-base);
|
||||
}
|
||||
|
||||
.nc-chip{
|
||||
padding:8px;
|
||||
.nc-chip {
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,14 +13,6 @@
|
||||
<div class="text-center caption grey--text mt-3 mb-1">
|
||||
Built with Vue JS<br><img src="vue.svg" class="vue-icon mt-1 mb-n1" alt="vue.js" width="30">
|
||||
</div>
|
||||
|
||||
<!-- <div class="justify-center caption grey--text mt-2 d-flex align-center ">
|
||||
<img src="favicon-32.png" alt="nocodb" width="20px">
|
||||
<v-icon size="13" color="red" class="mx-3">
|
||||
mdi-heart
|
||||
</v-icon>
|
||||
<img src="vue.svg" class="vue-icon" alt="vue.js" width="20px">
|
||||
</div>-->
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="d-flex justify-end">
|
||||
@@ -29,12 +21,12 @@
|
||||
class="
|
||||
flex-shrink-1
|
||||
text-left
|
||||
elevation-1
|
||||
elevation-0
|
||||
rounded-sm
|
||||
community-card
|
||||
item
|
||||
"
|
||||
:class="{ active: showCommunity }"
|
||||
:class="{ active: true }"
|
||||
dense
|
||||
>
|
||||
<v-list-item dense href="https://discord.gg/5RgZmkW" target="_blank">
|
||||
@@ -43,36 +35,33 @@
|
||||
<v-icon class="mr-1" small :color="textColors[0]">
|
||||
mdi-discord
|
||||
</v-icon>
|
||||
<span class="caption" :title="$t('labels.community.joinDiscord')">{{
|
||||
<span class="caption" :title="$t('labels.community.joinDiscord')" v-t="['community:discord']">{{
|
||||
$t('labels.community.joinDiscord')
|
||||
}}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item dense href="https://twitter.com/NocoDB" target="_blank">
|
||||
<!-- Follow NocoDB -->
|
||||
<v-list-item-title>
|
||||
<v-icon class="mr-1" small :color="textColors[1]">
|
||||
mdi-twitter
|
||||
</v-icon>
|
||||
<span class="caption" title="$t('labels.community.followNocodb')"> {{
|
||||
<span class="caption" title="$t('labels.community.followNocodb')" v-t="['community:twitter']"> {{
|
||||
$t('labels.community.followNocodb')
|
||||
}}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item dense href="https://www.reddit.com/r/NocoDB/" target="_blank">
|
||||
<!-- Get your questions answered -->
|
||||
<v-list-item-title>
|
||||
<v-icon class="mr-1" small color="#ff4600">
|
||||
mdi-reddit
|
||||
</v-icon>
|
||||
<span class="caption" :title="$t('labels.community.joinReddit')">{{
|
||||
<span class="caption" :title="$t('labels.community.joinReddit')" v-t="['community:reddit']">{{
|
||||
$t('labels.community.joinReddit')
|
||||
}}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider />
|
||||
<v-list-item
|
||||
dense
|
||||
target="_blank"
|
||||
@@ -83,19 +72,13 @@
|
||||
<v-icon class="mr-1" small :color="textColors[3]">
|
||||
mdi-calendar-month
|
||||
</v-icon>
|
||||
<span class="caption" :title="$t('labels.community.bookDemo')">{{
|
||||
<span class="caption" :title="$t('labels.community.bookDemo')" v-t="['community:book-demo']">{{
|
||||
$t('labels.community.bookDemo')
|
||||
}}</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
|
||||
<sponsor-mini
|
||||
:class="{ active: !showCommunity }"
|
||||
class="item"
|
||||
:nav="true"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -107,7 +90,7 @@ import colors from '~/mixins/colors'
|
||||
|
||||
export default {
|
||||
name: 'Extras',
|
||||
components: { ShareIcons, SponsorMini },
|
||||
components: { ShareIcons },
|
||||
mixins: [colors],
|
||||
data: () => ({
|
||||
showCommunity: true
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
overlap
|
||||
>
|
||||
<v-btn
|
||||
v-t="['fields:trigger']"
|
||||
class="nc-fields-menu-btn px-2 nc-remove-border"
|
||||
:disabled="isLocked"
|
||||
outlined
|
||||
@@ -39,7 +40,7 @@
|
||||
outlined
|
||||
:items="attachmentFields"
|
||||
item-text="alias"
|
||||
item-value="_cn"
|
||||
item-value="id"
|
||||
hide-details
|
||||
@click.stop
|
||||
>
|
||||
@@ -63,7 +64,7 @@
|
||||
outlined
|
||||
:items="singleSelectFields"
|
||||
item-text="alias"
|
||||
item-value="_cn"
|
||||
item-value="title"
|
||||
hide-details
|
||||
@click.stop
|
||||
>
|
||||
@@ -98,29 +99,40 @@
|
||||
</template>-->
|
||||
</v-text-field>
|
||||
</v-list-item>
|
||||
<draggable v-model="fieldsOrderLoc" @start="drag=true" @end="drag=false">
|
||||
<draggable
|
||||
v-model="fields"
|
||||
@start="drag=true"
|
||||
@end="drag=false"
|
||||
@change="onMove($event)"
|
||||
>
|
||||
<template
|
||||
v-for="field in fieldsOrderLoc"
|
||||
v-for="(field,i) in fields"
|
||||
>
|
||||
<v-list-item
|
||||
v-if="field && field.toLowerCase().indexOf(fieldFilter.toLowerCase()) > -1"
|
||||
:key="field"
|
||||
v-show="(!fieldFilter || (field.title||'').toLowerCase().includes(fieldFilter.toLowerCase()))
|
||||
&& !(!showSystemFieldsLoc && systemColumnsIds.includes(field.fk_column_id))
|
||||
"
|
||||
:key="field.id"
|
||||
dense
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="showFields[field]"
|
||||
v-model="field.show"
|
||||
class="mt-0 pt-0"
|
||||
dense
|
||||
hide-details
|
||||
@click.stop
|
||||
@change="saveOrUpdate(field, i)"
|
||||
>
|
||||
<template #label>
|
||||
<span class="caption">{{ field }}</span>
|
||||
<span class="caption">{{ field.title }}</span>
|
||||
</template>
|
||||
</v-checkbox>
|
||||
<v-spacer />
|
||||
<v-icon small color="grey"
|
||||
:class="`align-self-center drag-icon nc-child-draggable-icon-${field}`">
|
||||
<v-icon
|
||||
small
|
||||
color="grey"
|
||||
:class="`align-self-center drag-icon nc-child-draggable-icon-${field}`"
|
||||
>
|
||||
mdi-drag
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
@@ -142,7 +154,7 @@
|
||||
<span class="caption">
|
||||
<!-- Show System Fields -->
|
||||
{{ $t('activity.showSystemFields') }}
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</v-checkbox>
|
||||
</v-list-item>
|
||||
@@ -162,6 +174,7 @@
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import { getSystemColumnsIds } from 'nocodb-sdk'
|
||||
|
||||
export default {
|
||||
name: 'FieldsMenu',
|
||||
@@ -179,28 +192,33 @@ export default {
|
||||
value: [Object, Array],
|
||||
fieldList: [Array, Object],
|
||||
showSystemFields: {
|
||||
type: Boolean,
|
||||
type: [Boolean, Number],
|
||||
default: false
|
||||
},
|
||||
isLocked: Boolean,
|
||||
isPublic: Boolean
|
||||
isPublic: Boolean,
|
||||
viewId: String
|
||||
},
|
||||
data: () => ({
|
||||
fields: [],
|
||||
fieldFilter: '',
|
||||
showFields: {},
|
||||
fieldsOrderLoc: []
|
||||
}),
|
||||
computed: {
|
||||
systemColumnsIds() {
|
||||
return getSystemColumnsIds(this.meta && this.meta.columns)
|
||||
},
|
||||
attachmentFields() {
|
||||
return [...(this.meta && this.meta.columns ? this.meta.columns.filter(f => f.uidt === 'Attachment') : []), {
|
||||
alias: 'None',
|
||||
_cn: ''
|
||||
id: null
|
||||
}]
|
||||
},
|
||||
singleSelectFields() {
|
||||
return [...(this.meta && this.meta.columns ? this.meta.columns.filter(f => f.uidt === 'SingleSelect') : []), {
|
||||
alias: 'None',
|
||||
_cn: ''
|
||||
id: null
|
||||
}]
|
||||
},
|
||||
coverImageFieldLoc: {
|
||||
@@ -220,11 +238,18 @@ export default {
|
||||
}
|
||||
},
|
||||
columnMeta() {
|
||||
return this.meta && this.meta.columns ? this.meta.columns.reduce((o, c) => ({ ...o, [c._cn]: c }), {}) : {}
|
||||
return this.meta && this.meta.columns
|
||||
? this.meta.columns.reduce((o, c) => ({
|
||||
...o,
|
||||
[c.title]: c
|
||||
}), {})
|
||||
: {}
|
||||
},
|
||||
|
||||
isAnyFieldHidden() {
|
||||
return Object.values(this.showFields).some(v => !v)
|
||||
return this.fields.some(f => !(!this.showSystemFieldsLoc && this.systemColumnsIds.includes(f.fk_column_id)) &&
|
||||
!f.show
|
||||
)// Object.values(this.showFields).some(v => !v)
|
||||
},
|
||||
showSystemFieldsLoc: {
|
||||
get() {
|
||||
@@ -232,16 +257,27 @@ export default {
|
||||
},
|
||||
set(v) {
|
||||
this.$emit('update:showSystemFields', v)
|
||||
this.showFields = this.fields.reduce((o, c) => ({ [c.title]: c.show, ...o }), {})
|
||||
this.$emit('update:fieldsOrder', this.fields.map(c => c.title))
|
||||
|
||||
this.$tele.emit('fields:system-field-checkbox')
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
async viewId(v) {
|
||||
if (v) {
|
||||
await this.loadFields()
|
||||
}
|
||||
},
|
||||
fieldList(f) {
|
||||
this.fieldsOrderLoc = [...f]
|
||||
},
|
||||
showFields: {
|
||||
handler(v) {
|
||||
this.$emit('input', v)
|
||||
this.$nextTick(() => {
|
||||
this.$emit('input', v)
|
||||
})
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
@@ -265,17 +301,97 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadFields()
|
||||
this.showFields = this.value
|
||||
this.fieldsOrderLoc = this.fieldsOrder && this.fieldsOrder.length ? this.fieldsOrder : [...this.fieldList]
|
||||
},
|
||||
methods: {
|
||||
showAll() {
|
||||
async loadFields() {
|
||||
let fields = []
|
||||
let order = 1
|
||||
if (this.viewId) {
|
||||
const data = await this.$api.dbViewColumn.list(this.viewId)
|
||||
const fieldById = data.reduce((o, f) => ({
|
||||
...o,
|
||||
[f.fk_column_id]: f
|
||||
}), {})
|
||||
fields = this.meta.columns.map(c => ({
|
||||
title: c.title,
|
||||
fk_column_id: c.id,
|
||||
...(fieldById[c.id] ? fieldById[c.id] : {}),
|
||||
order: (fieldById[c.id] && fieldById[c.id].order) || order++
|
||||
})
|
||||
).sort((a, b) => a.order - b.order)
|
||||
} else if (this.isPublic) {
|
||||
fields = this.meta.columns
|
||||
}
|
||||
|
||||
this.fields = fields
|
||||
|
||||
this.$emit('input', this.fields.reduce((o, c) => ({
|
||||
...o,
|
||||
[c.title]: c.show
|
||||
}), {}))
|
||||
this.$emit('update:fieldsOrder', this.fields.map(c => c.title))
|
||||
},
|
||||
async saveOrUpdate(field, i) {
|
||||
if (!this.isPublic && this._isUIAllowed('fieldsSync')) {
|
||||
if (field.id) {
|
||||
await this.$api.dbViewColumn.update(this.viewId, field.id, field)
|
||||
} else {
|
||||
this.fields[i] = (await this.$api.dbViewColumn.create(this.viewId, field))
|
||||
}
|
||||
}
|
||||
this.$emit('updated')
|
||||
this.$emit('input', this.fields.reduce((o, c) => ({
|
||||
...o,
|
||||
[c.title]: c.show
|
||||
}), {}))
|
||||
this.$emit('update:fieldsOrder', this.fields.map(c => c.title))
|
||||
|
||||
this.$tele.emit('fields:show-hide-checkbox')
|
||||
},
|
||||
async showAll() {
|
||||
if (!this.isPublic) {
|
||||
await this.$api.dbView.showAllColumn(this.viewId)
|
||||
}
|
||||
for (const f of this.fields) {
|
||||
f.show = true
|
||||
}
|
||||
this.$emit('updated')
|
||||
|
||||
// eslint-disable-next-line no-return-assign,no-sequences
|
||||
this.showFields = (this.fieldsOrderLoc || Object.keys(this.showFields)).reduce((o, k) => (o[k] = true, o), {})
|
||||
|
||||
this.$tele.emit('fields:show-all')
|
||||
},
|
||||
hideAll() {
|
||||
// eslint-disable-next-line no-return-assign,no-sequences
|
||||
this.showFields = (this.fieldsOrderLoc || Object.keys(this.showFields)).reduce((o, k) => (o[k] = false, o), {})
|
||||
async hideAll() {
|
||||
if (!this.isPublic) {
|
||||
await this.$api.dbView.hideAllColumn({ viewId: this.viewId })
|
||||
}
|
||||
for (const f of this.fields) {
|
||||
f.show = false
|
||||
}
|
||||
this.$emit('updated')
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.showFields = (this.fieldsOrderLoc || Object.keys(this.showFields)).reduce((o, k) => (o[k] = false, o), {})
|
||||
})
|
||||
|
||||
this.$tele.emit('fields:hide-all')
|
||||
},
|
||||
onMove(event) {
|
||||
if (this.fields.length - 1 === event.moved.newIndex) {
|
||||
this.$set(this.fields[event.moved.newIndex], 'order', this.fields[event.moved.newIndex - 1].order + 1)
|
||||
} else if (event.moved.newIndex === 0) {
|
||||
this.$set(this.fields[event.moved.newIndex], 'order', this.fields[1].order / 2)
|
||||
} else {
|
||||
this.$set(this.fields[event.moved.newIndex], 'order', (
|
||||
this.fields[event.moved.newIndex - 1].order + this.fields[event.moved.newIndex + 1].order) / 2
|
||||
)
|
||||
}
|
||||
this.saveOrUpdate(this.fields[event.moved.newIndex], event.moved.newIndex)
|
||||
this.$tele.emit('fields:drag')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,7 +420,7 @@ export default {
|
||||
max-height: 20px !important;
|
||||
}
|
||||
|
||||
.field-icon{
|
||||
.field-icon {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</v-icon>
|
||||
|
||||
<span v-else-if="isInt" class="font-weight-bold mr-1" style="font-size: 15px">#</span>
|
||||
<!-- <v-icon color="grey" class="mr-1" v-if="isInt">mdi-numeric</v-icon>-->
|
||||
<v-icon v-else-if="isFloat" color="grey" class="mr-1 mt-n1">
|
||||
mdi-decimal
|
||||
</v-icon>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
</v-icon>
|
||||
|
||||
<span v-else-if="isInt" class="font-weight-bold mr-1" style="font-size: 15px">#</span>
|
||||
<!-- <v-icon color="grey" class="mr-1" v-if="isInt">mdi-numeric</v-icon>-->
|
||||
<v-icon v-else-if="isFloat" color="grey" class="mr-1 mt-n1">
|
||||
mdi-decimal
|
||||
</v-icon>
|
||||
@@ -43,15 +42,15 @@
|
||||
|
||||
<span class="name" style="white-space: nowrap" :title="value">{{ value }}</span>
|
||||
|
||||
<span v-if="(column.rqd && !column.default) || required" class="error--text text--lighten-1"> *</span>
|
||||
<span v-if="(column.rqd && !column.cdf) || required" class="error--text text--lighten-1"> *</span>
|
||||
|
||||
<v-spacer />
|
||||
|
||||
<v-menu
|
||||
v-if="!isLocked &&!isPublicView && _isUIAllowed('edit-column') && !isForm"
|
||||
offset-y
|
||||
open-on-hover
|
||||
left
|
||||
z-index="999"
|
||||
>
|
||||
<template #activator="{on}">
|
||||
<v-icon v-if="!isLocked && !isVirtual" small v-on="on">
|
||||
@@ -59,7 +58,11 @@
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item class="nc-column-edit" dense @click="editColumnMenu = true">
|
||||
<v-list-item
|
||||
class="nc-column-edit"
|
||||
dense
|
||||
@click="editColumnMenu = true"
|
||||
>
|
||||
<x-icon small class="mr-1" color="primary">
|
||||
mdi-pencil
|
||||
</x-icon>
|
||||
@@ -68,7 +71,11 @@
|
||||
{{ $t('general.edit') }}
|
||||
</span>
|
||||
</v-list-item>
|
||||
<v-list-item dense @click="setAsPrimaryValue">
|
||||
<v-list-item
|
||||
v-t="['column:set-as-primary']"
|
||||
dense
|
||||
@click="setAsPrimaryValue"
|
||||
>
|
||||
<x-icon small class="mr-1" color="primary">
|
||||
mdi-key-star
|
||||
</x-icon>
|
||||
@@ -82,7 +89,10 @@
|
||||
<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 class="nc-column-delete" @click="columnDeleteDialog = true">
|
||||
<v-list-item
|
||||
class="nc-column-delete"
|
||||
@click="columnDeleteDialog = true"
|
||||
>
|
||||
<x-icon small class="mr-1" color="error">
|
||||
mdi-delete-outline
|
||||
</x-icon>
|
||||
@@ -94,7 +104,7 @@
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-menu v-model="editColumnMenu" offset-y content-class="" left>
|
||||
<v-menu v-model="editColumnMenu" z-index="999" offset-y content-class="" left>
|
||||
<template #activator="{on}">
|
||||
<span v-on="on" />
|
||||
</template>
|
||||
@@ -126,17 +136,26 @@
|
||||
<v-divider />
|
||||
<v-card-text class="mt-4 title">
|
||||
Do you want to delete <span class="font-weight-bold">'{{
|
||||
column._cn
|
||||
column.title
|
||||
}}'</span> column ?
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
<v-card-actions class="d-flex pa-4">
|
||||
<v-spacer />
|
||||
<v-btn small @click="columnDeleteDialog = false">
|
||||
<v-btn
|
||||
v-t="['column:delete:cancel']"
|
||||
small
|
||||
@click="columnDeleteDialog = false"
|
||||
>
|
||||
<!-- Cancel -->
|
||||
{{ $t('general.cancel') }}
|
||||
</v-btn>
|
||||
<v-btn small color="error" @click="deleteColumn">
|
||||
<v-btn
|
||||
v-t="['column:delete']"
|
||||
small
|
||||
color="error"
|
||||
@click="deleteColumn"
|
||||
>
|
||||
Confirm
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -161,19 +180,13 @@ export default {
|
||||
methods: {
|
||||
async deleteColumn() {
|
||||
try {
|
||||
const column = { ...this.column, cno: this.column.cn }
|
||||
const column = { ...this.column, cno: this.column.column_name }
|
||||
column.altered = 4
|
||||
const columns = this.meta.columns.slice()
|
||||
columns[this.columnIndex] = column
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOpPlus', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'tableUpdate', {
|
||||
tn: this.nodes.tn,
|
||||
_tn: this.meta._tn,
|
||||
originalColumns: this.meta.columns,
|
||||
columns
|
||||
}])
|
||||
await this.$api.dbTableColumn.delete(this.meta.id, column.id)
|
||||
|
||||
this.$emit('colDelete')
|
||||
this.$emit('saved')
|
||||
this.columnDeleteDialog = false
|
||||
} catch (e) {
|
||||
@@ -183,23 +196,7 @@ export default {
|
||||
async setAsPrimaryValue() {
|
||||
// todo: pass only updated fields
|
||||
try {
|
||||
const meta = JSON.parse(JSON.stringify(this.meta))
|
||||
for (const col of meta.columns) {
|
||||
if (col.pv) {
|
||||
delete col.pv
|
||||
}
|
||||
if (col.cn === this.column.cn) {
|
||||
col.pv = true
|
||||
}
|
||||
}
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
await this.$api.dbTableColumn.primaryColumnSet(this.meta.id, this.column.id)
|
||||
this.$toast.success('Successfully updated as primary column').goAway(3000)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<v-card>
|
||||
<v-card-actions>
|
||||
<v-card-title>
|
||||
Table : {{ meta._tn }}
|
||||
Table : {{ meta.title }}
|
||||
</v-card-title>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
:disabled="
|
||||
!valid ||
|
||||
(typeof requiredColumnValidationError === 'string' || requiredColumnValidationError) ||
|
||||
(typeof noSelectedColumnError === 'string' || noSelectedColumnError)
|
||||
(typeof requiredColumnValidationError === 'string' || requiredColumnValidationError) ||
|
||||
(typeof noSelectedColumnError === 'string' || noSelectedColumnError)
|
||||
"
|
||||
color="primary"
|
||||
large
|
||||
@@ -46,7 +46,7 @@
|
||||
<tbody>
|
||||
<tr v-for="(r,i) in mappings" :key="i">
|
||||
<td>
|
||||
<v-checkbox v-model="r.enabled" class="mt-0" dense hide-details @change="$refs.form.validate()"/>
|
||||
<v-checkbox v-model="r.enabled" class="mt-0" dense hide-details @change="$refs.form.validate()" />
|
||||
</td>
|
||||
<td class="caption" style="width:45%">
|
||||
<div :title="r.sourceCn" style="">
|
||||
@@ -60,8 +60,8 @@
|
||||
dense
|
||||
hide-details="auto"
|
||||
:items="meta.columns"
|
||||
item-text="_cn"
|
||||
:item-value="v => v && v._cn"
|
||||
item-text="title"
|
||||
:item-value="v => v && v.title"
|
||||
:rules="[
|
||||
v => validateField(v,r)
|
||||
]"
|
||||
@@ -71,13 +71,13 @@
|
||||
<v-icon small class="mr-1">
|
||||
{{ getIcon(item.uidt) }}
|
||||
</v-icon>
|
||||
{{ item._cn }}
|
||||
{{ item.title }}
|
||||
</template>
|
||||
<template #item="{item}">
|
||||
<v-icon small class="mr-1">
|
||||
{{ getIcon(item.uidt) }}
|
||||
</v-icon>
|
||||
<span class="caption"> {{ item._cn }}</span>
|
||||
<span class="caption"> {{ item.title }}</span>
|
||||
</template>
|
||||
</v-select>
|
||||
</td>
|
||||
@@ -118,10 +118,10 @@ export default {
|
||||
},
|
||||
requiredColumnValidationError() {
|
||||
const missingRequiredColumns = this.meta.columns.filter(c => (c.pk ? !c.ai && !c.cdf : !c.cdf && c.rqd) &&
|
||||
!this.mappings.some(r => r.destCn === c._cn))
|
||||
!this.mappings.some(r => r.destCn === c.title))
|
||||
|
||||
if (missingRequiredColumns.length) {
|
||||
return `Following columns are required : ${missingRequiredColumns.map(c => c._cn).join(', ')}`
|
||||
return `Following columns are required : ${missingRequiredColumns.map(c => c.title).join(', ')}`
|
||||
}
|
||||
return false
|
||||
},
|
||||
@@ -147,7 +147,7 @@ export default {
|
||||
return true
|
||||
}
|
||||
|
||||
const v = this.meta && this.meta.columns.find(c => c._cn === _cn)
|
||||
const v = this.meta && this.meta.columns.find(c => c.title === _cn)
|
||||
|
||||
if ((this.mappings || []).filter(v => v.destCn === _cn).length > 1) { return 'Duplicate mapping found, please remove one of the mapping' }
|
||||
|
||||
@@ -169,20 +169,20 @@ export default {
|
||||
case UITypes.Checkbox:
|
||||
if (
|
||||
this.parsedCsv && this.parsedCsv.data && this.parsedCsv.data.slice(0, 500)
|
||||
.some((r) => {
|
||||
if (r => r[row.sourceCn] !== null && r[row.sourceCn] !== undefined) {
|
||||
var input = r[row.sourceCn]
|
||||
if (typeof input === 'string') {
|
||||
input = input.replace(/["']/g, "").toLowerCase().trim()
|
||||
return (
|
||||
input == "false" || input == "no" || input == "n" || input == "0" ||
|
||||
input == "true" || input == "yes" || input == "y" || input == "1"
|
||||
) ? false : true
|
||||
.some((r) => {
|
||||
if (r => r[row.sourceCn] !== null && r[row.sourceCn] !== undefined) {
|
||||
let input = r[row.sourceCn]
|
||||
if (typeof input === 'string') {
|
||||
input = input.replace(/["']/g, '').toLowerCase().trim()
|
||||
return !((
|
||||
input == 'false' || input == 'no' || input == 'n' || input == '0' ||
|
||||
input == 'true' || input == 'yes' || input == 'y' || input == '1'
|
||||
))
|
||||
}
|
||||
return input != 1 && input != 0 && input != true && input != false
|
||||
}
|
||||
return input != 1 && input != 0 && input != true && input != false
|
||||
}
|
||||
return false
|
||||
})
|
||||
return false
|
||||
})
|
||||
) {
|
||||
return 'Source data contains some invalid boolean values'
|
||||
}
|
||||
@@ -194,13 +194,13 @@ export default {
|
||||
this.mappings = []
|
||||
for (const col of this.importDataColumns) {
|
||||
const o = { sourceCn: col, enabled: true }
|
||||
const tableColumn = this.meta.columns.find(c => c._cn === col)
|
||||
const tableColumn = this.meta.columns.find(c => c.title === col)
|
||||
if (tableColumn) {
|
||||
o.destCn = tableColumn._cn
|
||||
o.destCn = tableColumn.title
|
||||
}
|
||||
this.mappings.push(o)
|
||||
}
|
||||
this.$nextTick(()=> this.$refs.form.validate())
|
||||
this.$nextTick(() => this.$refs.form.validate())
|
||||
},
|
||||
getIcon(uidt) {
|
||||
return getUIDTIcon(uidt) || 'mdi-alpha-v-circle-outline'
|
||||
|
||||
@@ -86,6 +86,7 @@ export default {
|
||||
}),
|
||||
methods: {
|
||||
changeLockType(type) {
|
||||
this.$tele.emit(`lockmenu:${type}`)
|
||||
if (type === 'personal') {
|
||||
return this.$toast.info('Coming soon').goAway(3000)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
>
|
||||
<template #activator="{on}">
|
||||
<v-btn
|
||||
v-t="['actions:trigger']"
|
||||
outlined
|
||||
class="nc-actions-menu-btn caption px-2 nc-remove-border font-weight-medium"
|
||||
small
|
||||
@@ -27,6 +28,7 @@
|
||||
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
v-t="['actions:download-csv']"
|
||||
dense
|
||||
@click="exportCsv"
|
||||
>
|
||||
@@ -42,6 +44,7 @@
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="_isUIAllowed('csvImport') && !isView"
|
||||
v-t="['actions:upload-csv']"
|
||||
dense
|
||||
@click="importModal = true"
|
||||
>
|
||||
@@ -61,6 +64,7 @@
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="_isUIAllowed('csvImport') && !isView"
|
||||
v-t="['actions:shared-view-list']"
|
||||
dense
|
||||
@click="$emit('showAdditionalFeatOverlay', 'shared-views')"
|
||||
>
|
||||
@@ -73,8 +77,10 @@
|
||||
{{ $t('activity.listSharedView') }}
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item> <v-list-item
|
||||
</v-list-item>
|
||||
<v-list-item
|
||||
v-if="_isUIAllowed('csvImport') && !isView"
|
||||
v-t="['actions:webhook:trigger']"
|
||||
dense
|
||||
@click="$emit('webhook')"
|
||||
>
|
||||
@@ -104,6 +110,7 @@
|
||||
<script>
|
||||
|
||||
import FileSaver from 'file-saver'
|
||||
import { ExportTypes } from 'nocodb-sdk'
|
||||
import DropOrSelectFileModal from '~/components/import/dropOrSelectFileModal'
|
||||
import ColumnMappingModal from '~/components/project/spreadsheet/components/importExport/columnMappingModal'
|
||||
import CSVTemplateAdapter from '~/components/import/templateParsers/CSVTemplateAdapter'
|
||||
@@ -111,14 +118,18 @@ import { UITypes } from '~/components/project/spreadsheet/helpers/uiTypes'
|
||||
|
||||
export default {
|
||||
name: 'ExportImport',
|
||||
components: { ColumnMappingModal, DropOrSelectFileModal },
|
||||
components: {
|
||||
ColumnMappingModal,
|
||||
DropOrSelectFileModal
|
||||
},
|
||||
props: {
|
||||
meta: Object,
|
||||
nodes: Object,
|
||||
selectedView: Object,
|
||||
publicViewId: String,
|
||||
queryParams: Object,
|
||||
isView: Boolean
|
||||
isView: Boolean,
|
||||
reqPayload: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -152,22 +163,22 @@ export default {
|
||||
let prop, cn
|
||||
if (col.mm || (col.lk && col.lk.type === 'mm')) {
|
||||
const tn = col.mm ? col.mm.rtn : col.lk.ltn
|
||||
const _tn = col.mm ? col.mm._rtn : col.lk._ltn
|
||||
const title = col.mm ? col.mm._rtn : col.lk._ltn
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn
|
||||
})
|
||||
|
||||
prop = `${_tn}MMList`
|
||||
prop = `${title}MMList`
|
||||
cn = col.lk
|
||||
? col.lk._lcn
|
||||
: (this.$store.state.meta.metas[tn].columns.find(c => c.pv) || this.$store.state.meta.metas[tn].columns.find(c => c.pk) || {})._cn
|
||||
: (this.$store.state.meta.metas[tn].columns.find(c => c.pv) || this.$store.state.meta.metas[tn].columns.find(c => c.pk) || {}).title
|
||||
|
||||
row[col._cn] = r.row[prop] && r.row[prop].map(r => cn && r[cn])
|
||||
row[col.title] = r.row[prop] && r.row[prop].map(r => cn && r[cn])
|
||||
} else if (col.hm || (col.lk && col.lk.type === 'hm')) {
|
||||
const tn = col.hm ? col.hm.tn : col.lk.ltn
|
||||
const _tn = col.hm ? col.hm._tn : col.lk._ltn
|
||||
const tn = col.hm ? col.hm.table_name : col.lk.ltn
|
||||
const title = col.hm ? col.hm.title : col.lk._ltn
|
||||
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
@@ -175,89 +186,76 @@ export default {
|
||||
tn
|
||||
})
|
||||
|
||||
prop = `${_tn}List`
|
||||
prop = `${title}List`
|
||||
cn = col.lk
|
||||
? col.lk._lcn
|
||||
: (this.$store.state.meta.metas[tn].columns.find(c => c.pv) ||
|
||||
this.$store.state.meta.metas[tn].columns.find(c => c.pk))._cn
|
||||
row[col._cn] = r.row[prop] && r.row[prop].map(r => cn && r[cn])
|
||||
this.$store.state.meta.metas[tn].columns.find(c => c.pk)).title
|
||||
row[col.title] = r.row[prop] && r.row[prop].map(r => cn && r[cn])
|
||||
} else if (col.bt || (col.lk && col.lk.type === 'bt')) {
|
||||
const tn = col.bt ? col.bt.rtn : col.lk.ltn
|
||||
const _tn = col.bt ? col.bt._rtn : col.lk._ltn
|
||||
const title = col.bt ? col.bt._rtn : col.lk._ltn
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn
|
||||
})
|
||||
|
||||
prop = `${_tn}Read`
|
||||
prop = `${title}Read`
|
||||
cn = col.lk
|
||||
? col.lk._lcn
|
||||
: (this.$store.state.meta.metas[tn].columns.find(c => c.pv) ||
|
||||
this.$store.state.meta.metas[tn].columns.find(c => c.pk) || {})._cn
|
||||
row[col._cn] = r.row[prop] &&
|
||||
this.$store.state.meta.metas[tn].columns.find(c => c.pk) || {}).title
|
||||
row[col.title] = r.row[prop] &&
|
||||
r.row[prop] && cn && r.row[prop][cn]
|
||||
} else {
|
||||
row[col._cn] = r.row[col._cn]
|
||||
row[col.title] = r.row[col.title]
|
||||
}
|
||||
} else if (col.uidt === 'Attachment') {
|
||||
let data = []
|
||||
try {
|
||||
if (typeof r.row[col._cn] === 'string') {
|
||||
data = JSON.parse(r.row[col._cn])
|
||||
} else if (r.row[col._cn]) {
|
||||
data = r.row[col._cn]
|
||||
if (typeof r.row[col.title] === 'string') {
|
||||
data = JSON.parse(r.row[col.title])
|
||||
} else if (r.row[col.title]) {
|
||||
data = r.row[col.title]
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
row[col._cn] = (data || []).map(a => `${a.title}(${a.url})`)
|
||||
row[col.title] = (data || []).map(a => `${a.title}(${a.url})`)
|
||||
} else {
|
||||
row[col._cn] = r.row[col._cn]
|
||||
row[col.title] = r.row[col.title]
|
||||
}
|
||||
}
|
||||
return row
|
||||
}))
|
||||
},
|
||||
async exportCsv() {
|
||||
// const fields = this.availableColumns.map(c => c._cn)
|
||||
// const blob = new Blob([Papaparse.unparse(await this.extractCsvData())], { type: 'text/plain;charset=utf-8' })
|
||||
|
||||
let offset = 0
|
||||
let c = 1
|
||||
|
||||
try {
|
||||
while (!isNaN(offset) && offset > -1) {
|
||||
const res = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
this.publicViewId
|
||||
? null
|
||||
: {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: '_noco'
|
||||
},
|
||||
this.publicViewId ? 'sharedViewExportAsCsv' : 'xcExportAsCsv',
|
||||
{
|
||||
query: { offset },
|
||||
localQuery: this.queryParams || {},
|
||||
...(this.publicViewId
|
||||
? {
|
||||
view_id: this.publicViewId
|
||||
}
|
||||
: {
|
||||
view_name: this.selectedView.title,
|
||||
model_name: this.meta.tn
|
||||
})
|
||||
},
|
||||
null,
|
||||
{
|
||||
responseType: 'blob'
|
||||
},
|
||||
null,
|
||||
true
|
||||
])
|
||||
const data = res.data
|
||||
let res
|
||||
if (this.publicViewId) {
|
||||
res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.CSV, this.reqPayload, {
|
||||
responseType: 'blob',
|
||||
query: {
|
||||
offset
|
||||
}
|
||||
})
|
||||
} else {
|
||||
res = await this.$api.data.csvExport(this.selectedView.id, ExportTypes.CSV, {
|
||||
responseType: 'blob',
|
||||
query: {
|
||||
offset
|
||||
}
|
||||
})
|
||||
}
|
||||
const { data } = res
|
||||
|
||||
offset = +res.headers['nc-export-offset']
|
||||
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
|
||||
FileSaver.saveAs(blob, `${this.meta._tn}_exported_${c++}.csv`)
|
||||
FileSaver.saveAs(blob, `${this.meta.title}_exported_${c++}.csv`)
|
||||
if (offset > -1) {
|
||||
this.$toast.info('Downloading more files').goAway(3000)
|
||||
} else {
|
||||
@@ -265,13 +263,14 @@ export default {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
}
|
||||
},
|
||||
async importData(columnMappings) {
|
||||
try {
|
||||
const api = this.$ncApis.get({
|
||||
table: this.meta.tn
|
||||
table: this.meta.table_name
|
||||
})
|
||||
|
||||
const data = this.parsedCsv.data
|
||||
@@ -279,7 +278,7 @@ export default {
|
||||
const batchData = data.slice(i, i + 500).map(row => columnMappings.reduce((res, col) => {
|
||||
// todo: parse data
|
||||
if (col.enabled && col.destCn) {
|
||||
const v = this.meta && this.meta.columns.find(c => c._cn === col.destCn)
|
||||
const v = this.meta && this.meta.columns.find(c => c.title === col.destCn)
|
||||
let input = row[col.sourceCn]
|
||||
// parse potential boolean values
|
||||
if (v.uidt == UITypes.Checkbox) {
|
||||
|
||||
@@ -55,7 +55,8 @@ export default {
|
||||
this.page = v
|
||||
},
|
||||
count(c) {
|
||||
this.$emit('input', Math.max(1, Math.min(this.page, Math.ceil(c / this.size))))
|
||||
const page = Math.max(1, Math.min(this.page, Math.ceil(c / this.size)))
|
||||
if (this.value !== page) { this.$emit('input', page) }
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <v-menu
|
||||
open-on-hover
|
||||
bottom
|
||||
offset-y
|
||||
>
|
||||
<template #activator="{on}">-->
|
||||
<v-btn
|
||||
v-t="['share-view:trigger']"
|
||||
v-if="_isUIAllowed('add-user')"
|
||||
outlined
|
||||
class="nc-btn-share-view caption px-2 nc-remove-border font-weight-medium"
|
||||
@@ -20,37 +15,6 @@
|
||||
<!-- Share View -->
|
||||
{{ $t('activity.shareView') }}
|
||||
</v-btn>
|
||||
<!-- </template>
|
||||
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
dense
|
||||
>
|
||||
<v-list-item-title>
|
||||
<v-icon small class="mr-1">
|
||||
mdi-open-in-new
|
||||
</v-icon>
|
||||
<span class="caption">
|
||||
Share View
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list dense>
|
||||
<v-list-item
|
||||
dense
|
||||
>
|
||||
<v-list-item-title>
|
||||
<v-icon small class="mr-1">
|
||||
mdi-download-outline
|
||||
</v-icon>
|
||||
<span class="caption">
|
||||
Shared Views List
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-list>
|
||||
</v-menu>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
<tbody>
|
||||
<tr v-if="currentView">
|
||||
<td class="font-weight-bold caption text-left">
|
||||
<v-icon v-if="viewIcons[currentView.view_type]" small :color="viewIcons[currentView.view_type].color">
|
||||
{{ viewIcons[currentView.view_type].icon }}
|
||||
<v-icon v-if="viewIcons[currentView.type]" small :color="viewIcons[currentView.type].color">
|
||||
{{ viewIcons[currentView.type].icon }}
|
||||
</v-icon>
|
||||
|
||||
{{ currentView.view_name }}
|
||||
{{ currentView.title }}
|
||||
</td>
|
||||
<td class="caption text-left">
|
||||
<nuxt-link :to="sharedViewUrl(currentView)">
|
||||
@@ -63,13 +63,13 @@
|
||||
</td>
|
||||
</tr>
|
||||
<template v-if="allSharedLinks">
|
||||
<tr v-for="link of viewsList" :key="link.id">
|
||||
<tr v-for="link of viewList" :key="link.id">
|
||||
<td class="caption text-left">
|
||||
<v-icon v-if="viewIcons[link.view_type]" small :color="viewIcons[link.view_type].color">
|
||||
{{ viewIcons[link.view_type].icon }}
|
||||
<v-icon v-if="viewIcons[link.type]" small :color="viewIcons[link.type].color">
|
||||
{{ viewIcons[link.type].icon }}
|
||||
</v-icon>
|
||||
|
||||
{{ link.view_name }}
|
||||
{{ link.title }}
|
||||
</td>
|
||||
<td class="caption text-left">
|
||||
<nuxt-link :to="sharedViewUrl(link)">
|
||||
@@ -117,13 +117,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ViewTypes } from 'nocodb-sdk'
|
||||
import viewIcons from '~/helpers/viewIcons'
|
||||
import { copyTextToClipboard } from '~/helpers/xutils'
|
||||
|
||||
export default {
|
||||
name: 'SharedViewsList',
|
||||
props: ['modelName', 'nodes', 'selectedView'],
|
||||
props: ['modelName', 'nodes', 'selectedView', 'meta'],
|
||||
data: () => ({
|
||||
viewsList: null,
|
||||
viewList: null,
|
||||
currentView: null,
|
||||
viewIcons,
|
||||
allSharedLinks: false
|
||||
@@ -138,36 +140,31 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
copyLink(view) {
|
||||
this.$clipboard(`${this.dashboardUrl}#${this.sharedViewUrl(view)}`)
|
||||
copyTextToClipboard(`${this.dashboardUrl}#${this.sharedViewUrl(view)}`)
|
||||
this.$toast.info('Copied to clipboard').goAway(1000)
|
||||
},
|
||||
async loadSharedViewsList() {
|
||||
const viewsList = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.nodes.dbAlias }, 'listSharedViewLinks', {
|
||||
model_name: this.modelName
|
||||
}])
|
||||
// const viewList = await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.nodes.dbAlias }, 'listSharedViewLinks', {
|
||||
// model_name: this.modelName
|
||||
// }])
|
||||
|
||||
const index = viewsList.findIndex((v) => {
|
||||
if (this.selectedView) {
|
||||
// if current view is main view compare with model name
|
||||
return (['table', 'view'].includes(this.selectedView.type) ? this.modelName : this.selectedView.title) === v.view_name
|
||||
} else {
|
||||
return (v.view_name || '').toLowerCase() === (this.$route.query.view || '').toLowerCase()
|
||||
}
|
||||
const viewList = (await this.$api.dbViewShare.list(this.meta.id))
|
||||
|
||||
const index = viewList.findIndex((v) => {
|
||||
return this.selectedView && this.selectedView.id === v.id
|
||||
})
|
||||
|
||||
if (index > -1) {
|
||||
this.currentView = viewsList.splice(index, 1)[0]
|
||||
this.currentView = viewList.splice(index, 1)[0]
|
||||
} else {
|
||||
this.currentView = null
|
||||
}
|
||||
|
||||
this.viewsList = viewsList
|
||||
this.viewList = viewList
|
||||
},
|
||||
async deleteLink(id) {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.nodes.dbAlias }, 'deleteSharedViewLink', {
|
||||
id
|
||||
}])
|
||||
await this.$api.dbViewShare.delete(id)
|
||||
this.$toast.success('Deleted shared view successfully').goAway(3000)
|
||||
await this.loadSharedViewsList()
|
||||
} catch (e) {
|
||||
@@ -176,17 +173,17 @@ export default {
|
||||
},
|
||||
sharedViewUrl(view) {
|
||||
let viewType
|
||||
switch (view.view_type) {
|
||||
case 'form':
|
||||
switch (view.type) {
|
||||
case ViewTypes.FORM:
|
||||
viewType = 'form'
|
||||
break
|
||||
case 'kanban':
|
||||
case ViewTypes.KANBAN:
|
||||
viewType = 'kanban'
|
||||
break
|
||||
default:
|
||||
viewType = 'view'
|
||||
}
|
||||
return `/nc/${viewType}/${view.view_id}`
|
||||
return `/nc/${viewType}/${view.uuid}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
<v-menu offset-y>
|
||||
<template #activator="{ on }">
|
||||
<v-badge
|
||||
:value="sortList.length"
|
||||
:value="sortList && sortList.length"
|
||||
color="primary"
|
||||
dot
|
||||
overlap
|
||||
>
|
||||
<v-btn
|
||||
v-t="['sort:trigger']"
|
||||
class="nc-sort-menu-btn px-2 nc-remove-border"
|
||||
:disabled="isLocked"
|
||||
small
|
||||
text
|
||||
outlined
|
||||
:class=" { 'primary lighten-5 grey--text text--darken-3' : sortList.length}"
|
||||
:class=" { 'primary lighten-5 grey--text text--darken-3' : sortList && sortList.length}"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon small class="mr-1" color="#777">
|
||||
@@ -29,38 +30,46 @@
|
||||
</template>
|
||||
<div class="backgroundColor pa-2" style="min-width: 330px">
|
||||
<div class="sort-grid" @click.stop>
|
||||
<template v-for="(sort,i) in sortList" dense>
|
||||
<v-icon :key="i + 'icon'" class="nc-sort-item-remove-btn" small @click.stop="sortList.splice(i,1)">
|
||||
<template v-for="(sort,i) in sortList||[]" dense>
|
||||
<v-icon :key="i + 'icon'" class="nc-sort-item-remove-btn" small @click.stop="deleteSort(sort)">
|
||||
mdi-close-box
|
||||
</v-icon>
|
||||
|
||||
<v-select
|
||||
:key="i + 'sel1'"
|
||||
v-model="sort.field"
|
||||
v-model="sort.fk_column_id"
|
||||
class="caption nc-sort-field-select"
|
||||
:items="fieldList"
|
||||
:items="columns"
|
||||
item-value="id"
|
||||
item-text="title"
|
||||
:label="$t('objects.field')"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
hide-details
|
||||
@click.stop
|
||||
@change="saveOrUpdate(sort, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item }}</span>
|
||||
<span
|
||||
:class="`caption font-weight-regular nc-sort-fld-${item.title}`"
|
||||
>
|
||||
{{ item.title }}
|
||||
</span>
|
||||
</template>
|
||||
</v-select>
|
||||
<v-select
|
||||
:key="i + 'sel2'"
|
||||
v-model="sort.order"
|
||||
v-model="sort.direction"
|
||||
class="flex-shrink-1 flex-grow-0 caption nc-sort-dir-select"
|
||||
:items="[{text : 'A -> Z', value: ''},{text : 'Z -> A', value: '-'}]"
|
||||
:items="[{text : 'A -> Z', value: 'asc'},{text : 'Z -> A', value: 'desc'}]"
|
||||
:label="$t('labels.operation')"
|
||||
solo
|
||||
flat
|
||||
dense
|
||||
hide-details
|
||||
@click.stop
|
||||
@change="saveOrUpdate(sort, i)"
|
||||
>
|
||||
<template #item="{item}">
|
||||
<span class="caption font-weight-regular">{{ item.text }}</span>
|
||||
@@ -80,33 +89,86 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { RelationTypes, UITypes } from 'nocodb-sdk'
|
||||
|
||||
export default {
|
||||
name: 'SortListMenu',
|
||||
props: ['fieldList', 'value', 'isLocked'],
|
||||
props: {
|
||||
fieldList: Array,
|
||||
value: [Array, Object],
|
||||
isLocked: Boolean,
|
||||
meta: [Object],
|
||||
viewId: String,
|
||||
shared: Boolean
|
||||
},
|
||||
data: () => ({
|
||||
sortList: []
|
||||
}),
|
||||
watch: {
|
||||
sortList: {
|
||||
handler(v) {
|
||||
this.$emit('input', v)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
value(v) {
|
||||
this.sortList = v || []
|
||||
computed: {
|
||||
columns() {
|
||||
if (!this.meta || !this.meta.columns) { return [] }
|
||||
return this.meta.columns.filter(c => !(c.uidt === UITypes.LinkToAnotherRecord && c.colOptions.type !== RelationTypes.BELONGS_TO))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
watch: {
|
||||
value(v) {
|
||||
this.sortList = v || []
|
||||
},
|
||||
async viewId(v) {
|
||||
if (v) {
|
||||
await this.loadSortList()
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.sortList = this.value || []
|
||||
this.loadSortList()
|
||||
},
|
||||
methods: {
|
||||
addSort() {
|
||||
this.sortList.push({
|
||||
field: '',
|
||||
order: ''
|
||||
fk_column_id: null,
|
||||
direction: 'asc'
|
||||
})
|
||||
this.sortList = this.sortList.slice()
|
||||
this.$tele.emit(`sort:add:${this.sortList.length}`)
|
||||
},
|
||||
async loadSortList() {
|
||||
if (!this.shared) { // && !this._isUIAllowed('sortSync')) {
|
||||
let sortList = []
|
||||
|
||||
if (this.viewId) {
|
||||
const data = await this.$api.dbTableSort.list(this.viewId)
|
||||
sortList = data.sorts.list
|
||||
}
|
||||
|
||||
this.sortList = sortList
|
||||
}
|
||||
},
|
||||
async saveOrUpdate(sort, i) {
|
||||
if (!this.shared && this._isUIAllowed('sortSync')) {
|
||||
if (sort.id) {
|
||||
await this.$api.dbTableSort.update(this.viewId, sort.id, sort)
|
||||
} else {
|
||||
this.$set(this.sortList, i, (await this.$api.dbTableSort.create(this.viewId, sort)))
|
||||
}
|
||||
} else {
|
||||
this.$emit('input', this.sortList)
|
||||
}
|
||||
this.$emit('updated')
|
||||
|
||||
this.$tele.emit(`sort:dir:${sort.direction}`)
|
||||
},
|
||||
async deleteSort(sort, i) {
|
||||
if (!this.shared && sort.id && this._isUIAllowed('sortSync')) {
|
||||
await this.$api.dbTableSort.delete(this.viewId, sort.id)
|
||||
await this.loadSortList()
|
||||
} else {
|
||||
this.sortList.splice(i, 1)
|
||||
this.$emit('input', this.sortList)
|
||||
}
|
||||
this.$emit('updated')
|
||||
this.$tele.emit('sort:delete')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<v-container fluid class="h-100 py-0">
|
||||
<div class="d-flex flex-column h-100">
|
||||
<div class="flex-grow-1" style="overflow: auto; min-height: 350px">
|
||||
<v-list v-if="viewsList && viewsList.length" dense>
|
||||
<v-list v-if="views && views.length" dense>
|
||||
<v-list-item dense>
|
||||
<!-- Views -->
|
||||
<span class="body-2 font-weight-medium">{{ $t('objects.views') }}</span>
|
||||
@@ -27,12 +27,12 @@
|
||||
<v-list-item
|
||||
v-for="(view, i) in viewsList"
|
||||
:key="view.id"
|
||||
v-t="['view:open']"
|
||||
dense
|
||||
:value="view.id"
|
||||
active-class="x-active--text"
|
||||
class="body-2 view nc-view-item nc-draggable-child"
|
||||
:class="`nc-${view.show_as}-view-item`"
|
||||
@click="$emit('generateNewViewKey')"
|
||||
:class="`body-2 view nc-view-item nc-draggable-child nc-${viewTypeAlias[view.type]}-view-item`"
|
||||
@click="$emit('rerender')"
|
||||
>
|
||||
<v-icon
|
||||
v-if="_isUIAllowed('viewlist-drag-n-drop')"
|
||||
@@ -44,11 +44,11 @@
|
||||
</v-icon>
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon
|
||||
v-if="viewIcons[view.show_as]"
|
||||
v-if="viewIcons[view.type]"
|
||||
x-small
|
||||
:color="viewIcons[view.show_as].color"
|
||||
:color="viewIcons[view.type].color"
|
||||
>
|
||||
{{ viewIcons[view.show_as].icon }}
|
||||
{{ viewIcons[view.type].icon }}
|
||||
</v-icon>
|
||||
<v-icon v-else color="primary" small>
|
||||
mdi-table
|
||||
@@ -83,7 +83,7 @@
|
||||
<template v-if="_isUIAllowed('virtualViewsCreateOrEdit')">
|
||||
<!-- Copy view -->
|
||||
<x-icon
|
||||
v-if="view.type === 'vtable' && !view.edit"
|
||||
v-if="!view.edit"
|
||||
:tooltip="$t('activity.copyView')"
|
||||
x-small
|
||||
color="primary"
|
||||
@@ -94,7 +94,7 @@
|
||||
</x-icon>
|
||||
<!-- Rename view -->
|
||||
<x-icon
|
||||
v-if="view.type === 'vtable' && !view.edit"
|
||||
v-if="!view.edit"
|
||||
:tooltip="$t('activity.renameView')"
|
||||
x-small
|
||||
color="primary"
|
||||
@@ -105,7 +105,7 @@
|
||||
</x-icon>
|
||||
<!-- Delete view" -->
|
||||
<x-icon
|
||||
v-if="view.type === 'vtable'"
|
||||
v-if="!view.is_default"
|
||||
:tooltip="$t('activity.deleteView')"
|
||||
small
|
||||
color="error"
|
||||
@@ -162,7 +162,7 @@
|
||||
</v-list-item>
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item dense class="body-2 nc-create-grid-view" v-on="on" @click="openCreateViewDlg('grid')">
|
||||
<v-list-item dense class="body-2 nc-create-grid-view" v-on="on" @click="openCreateViewDlg(viewTypes.GRID)">
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon color="blue" x-small>
|
||||
mdi-grid-large
|
||||
@@ -189,7 +189,7 @@
|
||||
dense
|
||||
class="body-2 nc-create-gallery-view"
|
||||
v-on="on"
|
||||
@click="openCreateViewDlg('gallery')"
|
||||
@click="openCreateViewDlg(viewTypes.GALLERY)"
|
||||
>
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon color="orange" x-small>
|
||||
@@ -212,75 +212,20 @@
|
||||
<!-- Add Gallery View -->
|
||||
{{ $t('msg.info.addView.gallery') }}
|
||||
</v-tooltip>
|
||||
<!-- <v-tooltip bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item
|
||||
dense
|
||||
class="body-2"
|
||||
v-on="on"
|
||||
@click="enableDummyFeat ? openCreateViewDlg('calendar') : comingSoon()"
|
||||
>
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon x-small>
|
||||
mdi-calendar
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>
|
||||
<span class="font-weight-regular">
|
||||
<!– Calendar –>
|
||||
{{ $t('objects.viewType.calendar') }}
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
|
||||
<v-spacer />
|
||||
<v-icon class="mr-1" small>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<!– Add Calendar View –>
|
||||
{{ $t('msg.info.addView.calendar') }}
|
||||
</v-tooltip> -->
|
||||
<!-- <v-tooltip bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item
|
||||
dense
|
||||
open-class="body-2"
|
||||
v-on="on"
|
||||
@click="openCreateViewDlg('kanban')"
|
||||
>
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon x-small>
|
||||
mdi-tablet-dashboard
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-title>
|
||||
<span class="font-weight-regular">
|
||||
<!– Kanban –>
|
||||
{{ $t('objects.viewType.kanban') }}
|
||||
</span>
|
||||
</v-list-item-title>
|
||||
<v-spacer />
|
||||
<v-icon class="mr-1" small>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
</v-list-item>
|
||||
</template>
|
||||
Add Kanban View
|
||||
{{ $t('msg.info.addView.kanban') }}
|
||||
</v-tooltip>-->
|
||||
<v-tooltip
|
||||
bottom
|
||||
>
|
||||
<template #activator="{ on }">
|
||||
<v-list-item
|
||||
v-if="!isView"
|
||||
dense
|
||||
class="body-2 nc-create-form-view"
|
||||
v-on="on"
|
||||
@click="openCreateViewDlg('form')"
|
||||
@click="openCreateViewDlg(viewTypes.FORM)"
|
||||
>
|
||||
<v-list-item-icon class="mr-n1">
|
||||
<v-icon x-small :color="viewIcons['form'].color" class="mt-n1">
|
||||
<v-icon x-small :color="viewIcons[viewTypes.FORM].color" class="mt-n1">
|
||||
mdi-form-select
|
||||
</v-icon>
|
||||
</v-list-item-icon>
|
||||
@@ -317,7 +262,8 @@
|
||||
mdi-close-circle-outline
|
||||
</v-icon>
|
||||
|
||||
<extras />
|
||||
<!-- <extras />-->
|
||||
<sponsor-mini nav />
|
||||
</div>
|
||||
<!--<div class="text-center">
|
||||
<v-hover >
|
||||
@@ -435,12 +381,13 @@
|
||||
:nodes="nodes"
|
||||
:table="table"
|
||||
:show_as="createViewType"
|
||||
:views-count="viewsList.length"
|
||||
:views-count="views.length"
|
||||
:primary-value-column="primaryValueColumn"
|
||||
:meta="meta"
|
||||
:copy-view="copyViewRef"
|
||||
:alias="meta._tn"
|
||||
:views-list="viewsList"
|
||||
:alias="meta.title"
|
||||
:views-list="views"
|
||||
:selected-view-id="selectedViewId"
|
||||
@created="onViewCreate"
|
||||
/>
|
||||
|
||||
@@ -458,6 +405,7 @@
|
||||
{{ sharedViewUrl }}
|
||||
<v-spacer />
|
||||
<a
|
||||
v-t="['share-view:open-url']"
|
||||
:href="`${sharedViewUrl}`"
|
||||
style="text-decoration: none"
|
||||
target="_blank"
|
||||
@@ -519,16 +467,20 @@
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import { ViewTypes } from 'nocodb-sdk'
|
||||
import CreateViewDialog from '@/components/project/spreadsheet/dialog/createViewDialog'
|
||||
import Extras from '~/components/project/spreadsheet/components/extras'
|
||||
import viewIcons from '~/helpers/viewIcons'
|
||||
import { copyTextToClipboard } from '~/helpers/xutils'
|
||||
import SponsorMini from '~/components/sponsorMini'
|
||||
|
||||
export default {
|
||||
name: 'SpreadsheetNavDrawer',
|
||||
components: { Extras, CreateViewDialog, draggable },
|
||||
components: { SponsorMini, Extras, CreateViewDialog, draggable },
|
||||
props: {
|
||||
extraViewParams: Object,
|
||||
showAdvanceOptions: Boolean,
|
||||
isView: Boolean,
|
||||
hideViews: Boolean,
|
||||
primaryValueColumn: [Number, String],
|
||||
toggleDrawer: {
|
||||
@@ -552,10 +504,11 @@ export default {
|
||||
currentApiUrl: String,
|
||||
fieldsOrder: Array,
|
||||
viewStatus: Object,
|
||||
columnsWidth: Object,
|
||||
coverImageField: String,
|
||||
// columnsWidth: Object,
|
||||
// coverImageField: String,
|
||||
groupingField: String,
|
||||
showSystemFields: Boolean
|
||||
// showSystemFields: Boolean,
|
||||
views: Array
|
||||
},
|
||||
data: () => ({
|
||||
drag: false,
|
||||
@@ -574,15 +527,26 @@ export default {
|
||||
sharedViewPassword: '',
|
||||
overAdvShieldIcon: false,
|
||||
overShieldIcon: false,
|
||||
viewsList: [],
|
||||
viewIcons,
|
||||
copyViewRef: null,
|
||||
shareLink: {},
|
||||
showShareModel: false,
|
||||
showCreateView: false,
|
||||
loading: false
|
||||
loading: false,
|
||||
viewTypeAlias: { [ViewTypes.GRID]: 'grid', [ViewTypes.FORM]: 'form', [ViewTypes.GALLERY]: 'gallery' }
|
||||
}),
|
||||
computed: {
|
||||
viewsList: {
|
||||
set(v) {
|
||||
this.$emit('update:views', v)
|
||||
},
|
||||
get() {
|
||||
return this.views
|
||||
}
|
||||
},
|
||||
viewTypes() {
|
||||
return ViewTypes
|
||||
},
|
||||
newViewParams() {
|
||||
if (!this.showFields) {
|
||||
return {}
|
||||
@@ -595,37 +559,37 @@ export default {
|
||||
},
|
||||
selectedViewIdLocal: {
|
||||
set(val) {
|
||||
const view = (this.viewsList || []).find(v => v.id === val)
|
||||
const view = (this.views || []).find(v => v.id === val)
|
||||
this.$router.push({
|
||||
query: {
|
||||
...this.$route.query,
|
||||
view: view && (view.alias || view.title)
|
||||
view: view && (view.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
get() {
|
||||
let id
|
||||
if (this.viewsList) {
|
||||
console.log(this.viewsList)
|
||||
const view = this.viewsList.find(v => (v.alias ? v.alias : v.title) === this.$route.query.view)
|
||||
id = (view && view.id) || ((this.viewsList && this.viewsList[0]) || {}).id
|
||||
if (this.views) {
|
||||
const view = this.views.find(v => v.id === this.$route.query.view)
|
||||
id = (view && view.id) || ((this.views && this.views[0]) || {}).id
|
||||
}
|
||||
return id
|
||||
}
|
||||
},
|
||||
sharedViewUrl() {
|
||||
let viewType
|
||||
switch (this.shareLink.view_type) {
|
||||
case 'form':
|
||||
|
||||
switch (this.shareLink.type) {
|
||||
case this.viewTypes.FORM:
|
||||
viewType = 'form'
|
||||
break
|
||||
case 'kanban':
|
||||
case this.viewTypes.KANBAN:
|
||||
viewType = 'kanban'
|
||||
break
|
||||
default:
|
||||
viewType = 'view'
|
||||
}
|
||||
return `${this.dashboardUrl}#/nc/${viewType}/${this.shareLink.view_id}`
|
||||
return `${this.dashboardUrl}#/nc/${viewType}/${this.shareLink.uuid}`
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -648,47 +612,46 @@ export default {
|
||||
methods: {
|
||||
async onMove(event) {
|
||||
if (this.viewsList.length - 1 === event.moved.newIndex) {
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'view_order', this.viewsList[event.moved.newIndex - 1].view_order + 1)
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'order', this.viewsList[event.moved.newIndex - 1].order + 1)
|
||||
} else if (event.moved.newIndex === 0) {
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'view_order', this.viewsList[1].view_order / 2)
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'order', this.viewsList[1].order / 2)
|
||||
} else {
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'view_order', (this.viewsList[event.moved.newIndex - 1].view_order + this.viewsList[event.moved.newIndex + 1].view_order) / 2)
|
||||
this.$set(this.viewsList[event.moved.newIndex], 'order', (this.viewsList[event.moved.newIndex - 1].order + this.viewsList[event.moved.newIndex + 1].order) / 2)
|
||||
}
|
||||
await this.$api.dbView.update(this.viewsList[event.moved.newIndex].id, {
|
||||
title: this.viewsList[event.moved.newIndex].title,
|
||||
order: this.viewsList[event.moved.newIndex].order
|
||||
})
|
||||
|
||||
console.log(this.viewsList)
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: 'db' }, 'xcModelViewOrderSet', {
|
||||
id: this.viewsList[event.moved.newIndex].id,
|
||||
view_order: this.viewsList[event.moved.newIndex].view_order
|
||||
}])
|
||||
this.$tele.emit('view:drag')
|
||||
},
|
||||
onViewIdChange(id) {
|
||||
const selectedView = this.viewsList && this.viewsList.find(v => v.id === id)
|
||||
let queryParams = {}
|
||||
const selectedView = this.views && this.views.find(v => v.id === id)
|
||||
// const queryParams = {}
|
||||
this.$emit('update:selectedViewId', id)
|
||||
this.$emit('update:selectedView', selectedView)
|
||||
// if (selectedView.type === 'table') {
|
||||
// return;
|
||||
// }
|
||||
try {
|
||||
queryParams = JSON.parse(selectedView.query_params) || {}
|
||||
} catch (e) {
|
||||
// console.log(e)
|
||||
}
|
||||
this.$emit('update:filters', queryParams.filters || [])
|
||||
this.$emit('update:sortList', queryParams.sortList || [])
|
||||
this.$emit('update:fieldsOrder', queryParams.fieldsOrder || [])
|
||||
this.$emit('update:viewStatus', queryParams.viewStatus || {})
|
||||
this.$emit('update:columnsWidth', queryParams.columnsWidth || {})
|
||||
this.$emit('update:extraViewParams', queryParams.extraViewParams || {})
|
||||
this.$emit('update:coverImageField', queryParams.coverImageField)
|
||||
this.$emit('update:groupingField', queryParams.groupingField)
|
||||
this.$emit('update:showSystemFields', queryParams.showSystemFields)
|
||||
if (queryParams.showFields) {
|
||||
this.$emit('update:showFields', queryParams.showFields)
|
||||
} else {
|
||||
this.$emit('mapFieldsAndShowFields')
|
||||
}
|
||||
// try {
|
||||
// queryParams = JSON.parse(selectedView.query_params) || {}
|
||||
// } catch (e) {
|
||||
// // console.log(e)
|
||||
// }
|
||||
// this.$emit('update:filters', queryParams.filters || [])
|
||||
// this.$emit('update:sortList', queryParams.sortList || [])
|
||||
// this.$emit('update:fieldsOrder', queryParams.fieldsOrder || [])
|
||||
// this.$emit('update:viewStatus', queryParams.viewStatus || {})
|
||||
// this.$emit('update:columnsWidth', queryParams.columnsWidth || {})
|
||||
// this.$emit('update:extraViewParams', queryParams.extraViewParams || {})
|
||||
// this.$emit('update:coverImageField', queryParams.coverImageField)
|
||||
// this.$emit('update:groupingField', queryParams.groupingField)
|
||||
// this.$emit('update:showSystemFields', queryParams.showSystemFields)
|
||||
// if (queryParams.showFields) {
|
||||
// this.$emit('update:showFields', queryParams.showFields)
|
||||
// } else {
|
||||
// this.$emit('mapFieldsAndShowFields')
|
||||
// }
|
||||
this.$emit('loadTableData')
|
||||
},
|
||||
hideMiniSponsorCard() {
|
||||
@@ -707,6 +670,7 @@ export default {
|
||||
}
|
||||
this.createViewType = type
|
||||
this.showCreateView = true
|
||||
this.$tele.emit(`view:create:trigger:${type}`)
|
||||
},
|
||||
isCentrallyAligned(col) {
|
||||
return ![
|
||||
@@ -730,30 +694,41 @@ export default {
|
||||
},
|
||||
async saveShareLinkPassword() {
|
||||
try {
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{ dbAlias: this.nodes.dbAlias },
|
||||
'updateSharedViewLinkPassword',
|
||||
{
|
||||
id: this.shareLink.id,
|
||||
password: this.shareLink.password
|
||||
}
|
||||
])
|
||||
await this.$api.dbViewShare.update(this.shareLink.id, {
|
||||
password: this.shareLink.password
|
||||
})
|
||||
|
||||
// await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
// { dbAlias: this.nodes.dbAlias },
|
||||
// 'updateSharedViewLinkPassword',
|
||||
// {
|
||||
// id: this.shareLink.id,
|
||||
// password: this.shareLink.password
|
||||
// }
|
||||
// ])
|
||||
this.$toast.success('Successfully updated').goAway(3000)
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
|
||||
this.$tele.emit('share-view:enable-pwd')
|
||||
},
|
||||
async loadViews() {
|
||||
this.viewsList = await this.sqlOp(
|
||||
{
|
||||
dbAlias: this.nodes.dbAlias
|
||||
},
|
||||
'xcVirtualTableList',
|
||||
{
|
||||
tn: this.table
|
||||
}
|
||||
)
|
||||
// this.viewsList = await this.sqlOp(
|
||||
// {
|
||||
// dbAlias: this.nodes.dbAlias
|
||||
// },
|
||||
// 'xcVirtualTableList',
|
||||
// {
|
||||
// tn: this.table
|
||||
// }
|
||||
// )
|
||||
// this.selectedViewIdLocal = this.viewsList && this.viewsList[0] && this.viewsList[0].id
|
||||
|
||||
// this.viewsList = []
|
||||
|
||||
const views = (await this.$api.dbView.list(this.meta.id)).list
|
||||
this.$emit('update:views', views)
|
||||
},
|
||||
// async onViewChange() {
|
||||
// let query_params = {}
|
||||
@@ -773,7 +748,7 @@ export default {
|
||||
// this.$emit('loadTableData');
|
||||
// },
|
||||
copyapiUrlToClipboard() {
|
||||
this.$clipboard(this.currentApiUrl)
|
||||
copyTextToClipboard(this.currentApiUrl)
|
||||
this.clipboardSuccessHandler()
|
||||
},
|
||||
async updateViewName(view, index) {
|
||||
@@ -781,7 +756,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
const oldTitle = view.title
|
||||
// const oldTitle = view.title
|
||||
|
||||
this.$set(view, 'edit', false)
|
||||
if (view.title_temp === view.title) {
|
||||
@@ -801,16 +776,20 @@ export default {
|
||||
})
|
||||
}
|
||||
this.$set(view, 'title', view.title_temp)
|
||||
await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableRename', {
|
||||
id: view.id,
|
||||
old_title: oldTitle,
|
||||
title: view.title_temp,
|
||||
alias: view.alias,
|
||||
parent_model_title: this.meta.tn
|
||||
// await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableRename', {
|
||||
// id: view.id,
|
||||
// old_title: oldTitle,
|
||||
// title: view.title_temp,
|
||||
// alias: view.alias,
|
||||
// parent_model_title: this.meta.table_name
|
||||
// })
|
||||
await this.$api.dbView.update(view.id, {
|
||||
title: view.title,
|
||||
order: view.order
|
||||
})
|
||||
this.$toast.success('View renamed successfully').goAway(3000)
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
},
|
||||
showRenameTextBox(view, i) {
|
||||
@@ -821,59 +800,67 @@ export default {
|
||||
input.focus()
|
||||
input.setSelectionRange(0, input.value.length)
|
||||
})
|
||||
this.$tele.emit(`view:rename:trigger:${view.type}`)
|
||||
},
|
||||
async deleteView(view) {
|
||||
try {
|
||||
await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableDelete', {
|
||||
id: view.id,
|
||||
title: view.alias || view.title,
|
||||
view_name: view.alias || view.title,
|
||||
parent_model_title: this.table
|
||||
})
|
||||
// await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableDelete', {
|
||||
// id: view.id,
|
||||
// title: view.alias || view.title,
|
||||
// view_name: view.alias || view.title,
|
||||
// parent_model_title: this.table
|
||||
// })
|
||||
await this.$api.dbView.delete(view.id)
|
||||
this.$toast.success('View deleted successfully').goAway(3000)
|
||||
await this.loadViews()
|
||||
} catch (e) {
|
||||
this.$toast.error(e.message).goAway(3000)
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
this.$tele.emit(`view:delete:submit:${view.type}`)
|
||||
},
|
||||
async genShareLink() {
|
||||
const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
{ dbAlias: this.nodes.dbAlias },
|
||||
'createSharedViewLink',
|
||||
{
|
||||
model_name: this.table,
|
||||
// meta: this.meta,
|
||||
query_params: {
|
||||
where: this.concatenatedXWhere,
|
||||
sort: this.sort,
|
||||
fields: Object.keys(this.showFields)
|
||||
.filter(f => this.showFields[f])
|
||||
.join(','),
|
||||
showFields: this.showFields,
|
||||
fieldsOrder: this.fieldsOrder,
|
||||
extraViewParams: this.extraViewParams,
|
||||
selectedViewId: this.selectedViewId,
|
||||
columnsWidth: this.columnsWidth
|
||||
},
|
||||
view_name: this.selectedView.title,
|
||||
type: this.selectedView.type,
|
||||
show_as: this.selectedView.show_as,
|
||||
password: this.sharedViewPassword
|
||||
}
|
||||
])
|
||||
this.shareLink = sharedViewUrl
|
||||
// const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [
|
||||
// { dbAlias: this.nodes.dbAlias },
|
||||
// 'createSharedViewLink',
|
||||
// {
|
||||
// model_name: this.table,
|
||||
// // meta: this.meta,
|
||||
// query_params: {
|
||||
// where: this.concatenatedXWhere,
|
||||
// sort: this.sort,
|
||||
// fields: Object.keys(this.showFields)
|
||||
// .filter(f => this.showFields[f])
|
||||
// .join(','),
|
||||
// showFields: this.showFields,
|
||||
// fieldsOrder: this.fieldsOrder,
|
||||
// extraViewParams: this.extraViewParams,
|
||||
// selectedViewId: this.selectedViewId,
|
||||
// columnsWidth: this.columnsWidth
|
||||
// },
|
||||
// view_name: this.selectedView.title,
|
||||
// type: this.selectedView.type,
|
||||
// show_as: this.selectedView.show_as,
|
||||
// password: this.sharedViewPassword
|
||||
// }
|
||||
// ])
|
||||
const shared = (await this.$api.dbViewShare.create(this.selectedViewId))
|
||||
|
||||
// todo: url
|
||||
this.shareLink = shared
|
||||
this.showShareModel = true
|
||||
},
|
||||
copyView(view, i) {
|
||||
this.createViewType = view.show_as
|
||||
this.createViewType = view.type
|
||||
this.showCreateView = true
|
||||
this.copyViewRef = view
|
||||
this.$tele.emit(`view:copy:trigger${view.type}`)
|
||||
},
|
||||
async onViewCreate(viewMeta) {
|
||||
this.copyViewRef = null
|
||||
await this.loadViews()
|
||||
this.selectedViewIdLocal = viewMeta.id
|
||||
// await this.onViewChange();
|
||||
this.$tele.emit(`view:create:submit:${viewMeta.type}`)
|
||||
},
|
||||
clipboard(str) {
|
||||
const el = document.createElement('textarea')
|
||||
@@ -890,6 +877,7 @@ export default {
|
||||
copyShareUrlToClipboard() {
|
||||
this.clipboard(this.sharedViewUrl)
|
||||
this.clipboardSuccessHandler()
|
||||
this.$tele.emit('share-view:copy-url')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
v-if="hm"
|
||||
ref="cell"
|
||||
:row="row"
|
||||
:value="row[`${hm._tn}List`]"
|
||||
:value="row[column.title]"
|
||||
:meta="meta"
|
||||
:hm="hm"
|
||||
:nodes="nodes"
|
||||
@@ -27,9 +27,8 @@
|
||||
ref="cell"
|
||||
:is-public="isPublic"
|
||||
:row="row"
|
||||
:value="row[`${mm._rtn}MMList`]"
|
||||
:value="row[column.title]"
|
||||
:meta="meta"
|
||||
:mm="mm"
|
||||
:nodes="nodes"
|
||||
:sql-ui="sqlUi"
|
||||
:active="active"
|
||||
@@ -51,9 +50,8 @@
|
||||
:disabled-columns="disabledColumns"
|
||||
:active="active"
|
||||
:row="row"
|
||||
:value="row[`${bt._rtn}Read`]"
|
||||
:value="row[column.title]"
|
||||
:meta="meta"
|
||||
:bt="bt"
|
||||
:nodes="nodes"
|
||||
:api="api"
|
||||
:sql-ui="sqlUi"
|
||||
@@ -71,6 +69,7 @@
|
||||
:disabled-columns="disabledColumns"
|
||||
:active="active"
|
||||
:row="row"
|
||||
:value="row[column.title]"
|
||||
:meta="meta"
|
||||
:metas="metas"
|
||||
:nodes="nodes"
|
||||
@@ -99,6 +98,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UITypes } from 'nocodb-sdk'
|
||||
import RollupCell from './virtualCell/rollupCell'
|
||||
import FormulaCell from './virtualCell/formulaCell'
|
||||
import hasManyCell from './virtualCell/hasManyCell'
|
||||
@@ -150,22 +150,22 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
hm() {
|
||||
return this.column && this.column.hm
|
||||
return this.column && this.column.uidt === UITypes.LinkToAnotherRecord && this.column.colOptions.type === 'hm'
|
||||
},
|
||||
bt() {
|
||||
return this.column && this.column.bt
|
||||
return this.column && (this.column.uidt === UITypes.ForeignKey || this.column.uidt === UITypes.LinkToAnotherRecord) && this.column.colOptions.type === 'bt'
|
||||
},
|
||||
mm() {
|
||||
return this.column && this.column.mm
|
||||
return this.column && this.column.uidt === UITypes.LinkToAnotherRecord && this.column.colOptions.type === 'mm'
|
||||
},
|
||||
lookup() {
|
||||
return this.column && this.column.lk
|
||||
return this.column && this.column.uidt === UITypes.Lookup
|
||||
},
|
||||
formula() {
|
||||
return this.column && this.column.formula
|
||||
return this.column && this.column.uidt === UITypes.Formula
|
||||
},
|
||||
rollup() {
|
||||
return this.column && this.column.rl
|
||||
return this.column && this.column.uidt === UITypes.Rollup
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
v-model="newRecordModal"
|
||||
:size="10"
|
||||
:meta="parentMeta"
|
||||
:column="column"
|
||||
:primary-col="parentPrimaryCol"
|
||||
:primary-key="parentPrimaryKey"
|
||||
:api="parentApi"
|
||||
@@ -41,6 +42,7 @@
|
||||
:is-public="isPublic"
|
||||
:tn="bt && bt.rtn"
|
||||
:password="password"
|
||||
:row-id="rowId"
|
||||
@add-new-record="insertAndMapNewParentRecord"
|
||||
@add="addChildToParent"
|
||||
/>
|
||||
@@ -85,7 +87,7 @@
|
||||
:db-alias="nodes.dbAlias"
|
||||
:has-many="parentMeta.hasMany"
|
||||
:belongs-to="parentMeta.belongsTo"
|
||||
:table="parentMeta.tn"
|
||||
:table="parentMeta.table_name"
|
||||
:old-row="{...selectedParent}"
|
||||
:meta="parentMeta"
|
||||
:sql-ui="sqlUi"
|
||||
@@ -97,7 +99,7 @@
|
||||
:is-new.sync="isNewParent"
|
||||
icon-color="warning"
|
||||
:breadcrumbs="breadcrumbs"
|
||||
@cancel="selectedParent = null"
|
||||
@cancel="selectedParent = null; expandFormModal =false"
|
||||
@input="onParentSave"
|
||||
/>
|
||||
</v-dialog>
|
||||
@@ -106,6 +108,7 @@
|
||||
|
||||
<script>
|
||||
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
|
||||
import { RelationTypes, UITypes } from 'nocodb-sdk'
|
||||
import ListItems from '@/components/project/spreadsheet/components/virtualCell/components/listItems'
|
||||
import ListChildItems from '@/components/project/spreadsheet/components/virtualCell/components/listChildItems'
|
||||
import ItemChip from '~/components/project/spreadsheet/components/virtualCell/components/itemChip'
|
||||
@@ -123,9 +126,8 @@ export default {
|
||||
}
|
||||
},
|
||||
isForm: Boolean,
|
||||
value: [Object, Array],
|
||||
value: [Array, Object],
|
||||
meta: [Object],
|
||||
bt: Object,
|
||||
nodes: [Object],
|
||||
row: [Object],
|
||||
api: [Object, Function],
|
||||
@@ -135,7 +137,8 @@ export default {
|
||||
disabledColumns: Object,
|
||||
isPublic: Boolean,
|
||||
metas: Object,
|
||||
password: String
|
||||
password: String,
|
||||
column: Object
|
||||
},
|
||||
data: () => ({
|
||||
newRecordModal: false,
|
||||
@@ -154,42 +157,32 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
parentMeta() {
|
||||
return this.metas ? this.metas[this.bt.rtn] : this.$store.state.meta.metas[this.bt.rtn]
|
||||
return this.metas ? this.metas[this.column.colOptions.fk_related_model_id] : this.$store.state.meta.metas[this.column.colOptions.fk_related_model_id]
|
||||
},
|
||||
// todo : optimize
|
||||
parentApi() {
|
||||
return this.parentMeta && this.$ncApis.get({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
table: this.parentMeta.tn
|
||||
})
|
||||
// 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('___'))
|
||||
return this.pid ?? (this.value && this.parentMeta && this.parentMeta.columns.filter(c => c.pk).map(c => this.value[c.title]).join('___'))
|
||||
},
|
||||
rowId() {
|
||||
return (this.row && this.meta && this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___'))
|
||||
},
|
||||
parentPrimaryCol() {
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.pv) || {})._cn
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.pv) || {}).title
|
||||
},
|
||||
parentPrimaryKey() {
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.pk) || {})._cn
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.pk) || {}).title
|
||||
},
|
||||
parentReferenceKey() {
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.cn === this.bt.rcn) || {})._cn
|
||||
return this.parentMeta && (this.parentMeta.columns.find(c => c.id === this.column.colOptions.fk_parent_column_id) || {}).title
|
||||
},
|
||||
btWhereClause() {
|
||||
// if parent reference key is pk, then filter out the selected value
|
||||
// else, filter out the selected value + empty values (as we can't set an empty value)
|
||||
const prk = this.parentReferenceKey
|
||||
const isPk = !!(this.parentMeta && (this.parentMeta.columns.find(c => c.pk && c._cn === prk))) || false
|
||||
let selectedValue = this.meta && this.meta.columns ? this.meta.columns.filter(c => c.cn === this.bt.cn).map(c => this.row[c._cn] || '').join('___') : ''
|
||||
if (this.parentMeta && (this.parentMeta.columns.find(c => c._cn === prk)).type !== 'string') {
|
||||
selectedValue = selectedValue || 0
|
||||
}
|
||||
return `(${prk},not,${selectedValue})` + (!isPk ? `~and(${prk},not,)` : '')
|
||||
const selectedValue = this.meta && this.meta.columns ? this.meta.columns.filter(c => c.id === this.column.colOptions.fk_child_column_id).map(c => this.row[c.title] || '').join('___') : ''
|
||||
return `(${prk},not,${selectedValue})~or(${prk},is,null)`
|
||||
},
|
||||
parentQueryParams() {
|
||||
if (!this.parentMeta) {
|
||||
@@ -197,9 +190,6 @@ export default {
|
||||
}
|
||||
// todo: use reduce
|
||||
return {
|
||||
hm: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.hm).map(({ hm }) => hm.tn).join()) || '',
|
||||
bt: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.bt).map(({ bt }) => bt.rtn).join()) || '',
|
||||
mm: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.mm).map(({ mm }) => mm.rtn).join()) || ''
|
||||
}
|
||||
},
|
||||
parentAvailableColumns() {
|
||||
@@ -210,7 +200,7 @@ export default {
|
||||
|
||||
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))))
|
||||
columns.push(...this.parentMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.column_name) && !((this.parentMeta.v || []).some(v => v.bt && v.bt.column_name === c.column_name))))
|
||||
}
|
||||
if (this.parentMeta.v) {
|
||||
columns.push(...this.parentMeta.v.map(v => ({ ...v, virtual: 1 })))
|
||||
@@ -229,7 +219,7 @@ export default {
|
||||
return Object.values(this.value || this.localState)[1]
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isNew(n, o) {
|
||||
@@ -240,6 +230,9 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.isNew && this.value) {
|
||||
this.localState = this.value
|
||||
}
|
||||
if (this.isForm) {
|
||||
await this.loadParentMeta()
|
||||
}
|
||||
@@ -259,13 +252,21 @@ export default {
|
||||
await this.loadParentMeta()
|
||||
this.newRecordModal = false
|
||||
this.isNewParent = true
|
||||
this.selectedParent = {}
|
||||
this.selectedParent = {
|
||||
[(this.parentMeta.columns.find(c => c.uidt === UITypes.LinkToAnotherRecord &&
|
||||
c.colOptions &&
|
||||
this.column.colOptions &&
|
||||
c.colOptions.fk_child_column_id === this.column.colOptions.fk_child_column_id &&
|
||||
c.colOptions.fk_parent_column_id === this.column.colOptions.fk_parent_column_id &&
|
||||
c.colOptions.type === RelationTypes.HAS_MANY
|
||||
) || {}).title]: [this.row]
|
||||
}
|
||||
this.expandFormModal = true
|
||||
},
|
||||
|
||||
async unlink() {
|
||||
const column = this.meta.columns.find(c => c.cn === this.bt.cn)
|
||||
const _cn = column._cn
|
||||
async unlink(parent) {
|
||||
const column = this.meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id)
|
||||
const _cn = column.title
|
||||
if (this.isNew) {
|
||||
this.$emit('updateCol', this.row, _cn, null)
|
||||
this.localState = null
|
||||
@@ -276,8 +277,15 @@ export default {
|
||||
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)
|
||||
const id = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
|
||||
// todo: audit
|
||||
await this.$api.data.nestedDelete(
|
||||
this.meta.id,
|
||||
id,
|
||||
this.column.id,
|
||||
'bt',
|
||||
parent[this.parentPrimaryKey]
|
||||
)
|
||||
this.$emit('loadTableData')
|
||||
if (this.isForm && this.$refs.childList) {
|
||||
this.$refs.childList.loadData()
|
||||
@@ -286,8 +294,8 @@ export default {
|
||||
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
|
||||
const pid = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
|
||||
const _cn = this.parentMeta.columns.find(c => c.column_name === this.hm.column_name).title
|
||||
this.childList = await this.parentApi.paginatedList({
|
||||
where: `(${_cn},eq,${pid})`
|
||||
})
|
||||
@@ -300,7 +308,7 @@ export default {
|
||||
if (act === 'hideDialog') {
|
||||
this.dialogShow = false
|
||||
} else {
|
||||
const id = this.parentMeta.columns.filter(c => c.pk).map(c => child[c._cn]).join('___')
|
||||
const id = this.parentMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
await this.parentApi.delete(id)
|
||||
this.pid = null
|
||||
this.dialogShow = false
|
||||
@@ -317,15 +325,8 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.bt.rtn
|
||||
id: this.column.colOptions.fk_related_model_id
|
||||
})
|
||||
// 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() {
|
||||
@@ -333,28 +334,27 @@ export default {
|
||||
this.newRecordModal = true
|
||||
},
|
||||
async addChildToParent(parent) {
|
||||
const pkColumns = this.parentMeta.columns.filter(c => c.pk)
|
||||
const pid = pkColumns.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
|
||||
// let isNum = false
|
||||
|
||||
// if (pkColumns.length === 1) {
|
||||
// isNum = ['float', 'integer'].includes(this.sqlUi.getAbstractType(pkColumns[0]))
|
||||
// }
|
||||
const pid = this._extractRowId(parent, this.parentMeta)
|
||||
const id = this._extractRowId(this.row, this.meta)
|
||||
const _cn = this.meta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id).title
|
||||
|
||||
if (this.isNew) {
|
||||
const _rcn = this.parentMeta.columns.find(c => c.id === this.column.colOptions.fk_parent_column_id).title
|
||||
this.localState = parent
|
||||
this.$emit('update:localState', this.localState)
|
||||
this.$emit('updateCol', this.row, _cn, +pid || pid)
|
||||
this.$emit('updateCol', this.row, _cn, parent[_rcn])
|
||||
this.newRecordModal = false
|
||||
return
|
||||
}
|
||||
await this.api.update(id, {
|
||||
[_cn]: parseIfInteger(parent[this.parentReferenceKey])
|
||||
}, {
|
||||
[_cn]: this.value && this.value[this.parentPrimaryKey]
|
||||
})
|
||||
|
||||
await this.$api.data.nestedAdd(
|
||||
this.meta.id,
|
||||
id,
|
||||
this.column.id,
|
||||
'bt',
|
||||
pid
|
||||
)
|
||||
|
||||
this.pid = pid
|
||||
|
||||
this.newRecordModal = false
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
:color="isDark ? '' : 'primary lighten-5'"
|
||||
@click="!readonly && active && $emit('edit',item)"
|
||||
>
|
||||
<!-- <span class="name" :title="value">{{ value }}</span>-->
|
||||
<slot><span class="name" :title="value">{{ value }}</span></slot>
|
||||
<div v-show="active" v-if="!readonly && _isUIAllowed('table-remove-linked-record')" class="mr-n1 ml-2">
|
||||
<x-icon
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<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>
|
||||
<span v-if="!isForm">{{ meta ? meta.title : 'Children' }}</span>
|
||||
<v-spacer />
|
||||
<v-icon small class="mr-1" @click="loadData()">
|
||||
mdi-reload
|
||||
@@ -19,7 +18,7 @@
|
||||
>
|
||||
mdi-link
|
||||
</v-icon>
|
||||
Link to '{{ meta._tn }}'
|
||||
Link to '{{ meta.title }}'
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -38,7 +37,7 @@
|
||||
>
|
||||
mdi-link
|
||||
</v-icon>
|
||||
Link to '{{ meta._tn }}'
|
||||
Link to '{{ meta.title }}'
|
||||
</v-btn>
|
||||
</div>
|
||||
<template v-if="isDataAvail">
|
||||
@@ -52,7 +51,7 @@
|
||||
<div class="remove-child-icon d-flex align-center">
|
||||
<x-icon
|
||||
v-if="((isPublic && isForm) || (!isPublic && _isUIAllowed('xcDatatableEditable'))) && !readOnly "
|
||||
:tooltip="`Unlink this '${meta._tn}' from '${parentMeta._tn}'`"
|
||||
:tooltip="`Unlink this '${meta.title}' from '${parentMeta.title}'`"
|
||||
:color="['error','grey']"
|
||||
small
|
||||
icon.class="mr-1 mt-n1"
|
||||
@@ -61,8 +60,8 @@
|
||||
mdi-link-variant-remove
|
||||
</x-icon>
|
||||
<x-icon
|
||||
v-if="!isPublic && !mm && !bt && !readOnly && _isUIAllowed('xcDatatableEditable')"
|
||||
:tooltip="`Delete row in '${meta._tn}'`"
|
||||
v-if="!isPublic && type === RelationTypes.HAS_MANY && !readOnly && _isUIAllowed('xcDatatableEditable')"
|
||||
:tooltip="`Delete row in '${meta.title}'`"
|
||||
:color="['error','grey']"
|
||||
small
|
||||
@click.stop="$emit('delete',ch,i)"
|
||||
@@ -91,10 +90,10 @@
|
||||
|
||||
<div v-if="isForm" class="mb-2 d-flex align-center justify-center">
|
||||
<pagination
|
||||
v-if="!bt && data && data.count > 1"
|
||||
v-if="!bt && data && data.pageInfo&& data.pageInfo.totalRows > 1"
|
||||
v-model="page"
|
||||
:size="size"
|
||||
:count="data && data.count"
|
||||
:count="data && data.pageInfo&& data.pageInfo.totalRows"
|
||||
@input="loadData"
|
||||
/>
|
||||
</div>
|
||||
@@ -102,10 +101,10 @@
|
||||
</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"
|
||||
v-if="!bt && data && data.pageInfo&& data.pageInfo.totalRows > 1"
|
||||
v-model="page"
|
||||
:size="size"
|
||||
:count="data && data.count"
|
||||
:count="data && data.pageInfo&& data.pageInfo.totalRows"
|
||||
class="mb-3"
|
||||
@input="loadData"
|
||||
/>
|
||||
@@ -115,6 +114,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { RelationTypes } from 'nocodb-sdk'
|
||||
import Pagination from '@/components/project/spreadsheet/components/pagination'
|
||||
|
||||
export default {
|
||||
@@ -123,7 +123,7 @@ export default {
|
||||
props: {
|
||||
readOnly: Boolean,
|
||||
isForm: Boolean,
|
||||
bt: Object,
|
||||
bt: [Object],
|
||||
localState: [Array],
|
||||
isNew: Boolean,
|
||||
value: Boolean,
|
||||
@@ -151,6 +151,7 @@ export default {
|
||||
password: String
|
||||
},
|
||||
data: () => ({
|
||||
RelationTypes,
|
||||
data: null,
|
||||
page: 1
|
||||
}),
|
||||
@@ -178,28 +179,47 @@ export default {
|
||||
methods: {
|
||||
async loadData() {
|
||||
if ((!this.isForm && this.isPublic) && this.$route.params.id) {
|
||||
this.data = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewNestedChildDataGet', {
|
||||
password: this.password,
|
||||
limit: this.size,
|
||||
tn: this.tn,
|
||||
view_id: this.$route.params.id,
|
||||
row_id: this.rowId,
|
||||
offset: this.size * (this.page - 1),
|
||||
query: this.query,
|
||||
_cn: this.column._cn,
|
||||
ptn: this.parentMeta.tn,
|
||||
ctn: this.meta.tn,
|
||||
type: this.type
|
||||
}])
|
||||
|
||||
if (this.column && this.column.colOptions && this.rowId) {
|
||||
this.data = (await this.$api.public.dataNestedList(
|
||||
this.$route.params.id,
|
||||
this.rowId,
|
||||
this.column.colOptions.type,
|
||||
this.column.fk_column_id || this.column.id, {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1)
|
||||
}, {}))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.api || this.isNew) { return }
|
||||
this.data = await this.api.paginatedList({
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams
|
||||
})
|
||||
if (this.isNew) {
|
||||
return
|
||||
}
|
||||
if (this.column && this.column.colOptions) {
|
||||
this.data = (await this.$api.data.nestedList(
|
||||
this.column.fk_model_id,
|
||||
this.rowId,
|
||||
this.column.id,
|
||||
this.column.colOptions.type,
|
||||
{
|
||||
query: {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1)
|
||||
// ...this.queryParams
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
this.data = (await this.$api.data.list(
|
||||
this.meta.id, {
|
||||
query: {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
@click="$emit('add',ch)"
|
||||
>
|
||||
<v-card-text class="primary-value textColor--text text--lighten-2 d-flex">
|
||||
<span class="font-weight-bold"> {{ ch[primaryCol] }} </span>
|
||||
<span class="font-weight-bold"> {{ ch[primaryCol] || (ch &&Object.values(ch).slice(0,1).join()) }} </span>
|
||||
<span
|
||||
v-if="primaryKey"
|
||||
class="grey--text caption primary-key "
|
||||
@@ -73,7 +73,7 @@
|
||||
v-if="data && data.list && data.list.length"
|
||||
v-model="page"
|
||||
:size="size"
|
||||
:count="data.count"
|
||||
:count="data && data.pageInfo&& data.pageInfo.totalRows"
|
||||
class="mb-3"
|
||||
@input="loadData"
|
||||
/>
|
||||
@@ -111,7 +111,9 @@ export default {
|
||||
parentId: [String, Number],
|
||||
parentMeta: [Object],
|
||||
isPublic: Boolean,
|
||||
password: String
|
||||
password: String,
|
||||
column: Object,
|
||||
rowId: [Number, String]
|
||||
},
|
||||
data: () => ({
|
||||
data: null,
|
||||
@@ -130,7 +132,7 @@ export default {
|
||||
hmParentPrimaryValCol() {
|
||||
return this.hm &&
|
||||
this.parentMeta &&
|
||||
this.parentMeta.columns.find(v => v.pv)._cn
|
||||
this.parentMeta.columns.find(v => v.pv).title
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -139,41 +141,43 @@ export default {
|
||||
methods: {
|
||||
async loadData() {
|
||||
if (this.isPublic) {
|
||||
this.data = await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewNestedDataGet', {
|
||||
password: this.password,
|
||||
limit: this.size,
|
||||
tn: this.tn,
|
||||
view_id: this.$route.params.id,
|
||||
offset: this.size * (this.page - 1),
|
||||
query: this.query
|
||||
}])
|
||||
this.data = (await this.$api.public.dataRelationList(
|
||||
this.$route.params.id,
|
||||
this.column.id,
|
||||
{ password: this.password }, {
|
||||
query: {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
if (!this.api) {
|
||||
return
|
||||
}
|
||||
const isByPass = this.queryParams.isByPass || false
|
||||
if (isByPass) {
|
||||
return
|
||||
}
|
||||
let where = this.queryParams.where || ''
|
||||
if (this.query) {
|
||||
where += (where ? '~and' : '') + `(${this.primaryCol},like,%${this.query}%)`
|
||||
}
|
||||
const where = `(${this.primaryCol},like,%${this.query}%)`
|
||||
|
||||
if (this.mm) {
|
||||
this.data = await this.api.paginatedM2mNotChildrenList({
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams,
|
||||
where
|
||||
}, this.mm.vtn, this.parentId || -1)
|
||||
// eslint-disable-next-line no-lonely-if
|
||||
if (this.column && this.column.colOptions && this.rowId) {
|
||||
this.data = (await this.$api.data.nestedExcludedList(
|
||||
this.column.fk_model_id,
|
||||
this.rowId,
|
||||
this.column.id,
|
||||
this.column.colOptions.type,
|
||||
{
|
||||
query: {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
where: this.query && `(${this.primaryCol},like,${this.query})`
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
this.data = await this.api.paginatedList({
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams,
|
||||
where
|
||||
})
|
||||
this.data = (await this.$api.data.list(
|
||||
this.meta.id, {
|
||||
query: {
|
||||
limit: this.size,
|
||||
offset: this.size * (this.page - 1),
|
||||
...this.queryParams,
|
||||
where
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
<template>
|
||||
<v-tooltip
|
||||
v-if="column.formula && column.formula.error && column.formula.error.length"
|
||||
v-if="column && column.colOptions&& column.colOptions.error"
|
||||
bottom
|
||||
color="error"
|
||||
>
|
||||
<template #activator="{on}">
|
||||
<span class="caption" v-on="on">ERR<span class="error--text">!</span></span>
|
||||
</template>
|
||||
<span class=" font-weight-bold">{{ column.formula.error.join(', ') }}</span>
|
||||
<span class=" font-weight-bold">{{ column.colOptions.error }}</span>
|
||||
</v-tooltip>
|
||||
<div v-else-if="urls" v-html="urls"></div>
|
||||
<div v-else-if="urls" v-html="urls" />
|
||||
<div v-else>
|
||||
{{ row[column._cn] }}
|
||||
{{ row[column.title] }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'FormulaCell',
|
||||
props: ['column', 'row'],
|
||||
props: { column: Object, row: Object },
|
||||
computed: {
|
||||
urls: function() {
|
||||
if(this.row[this.column._cn] ) {
|
||||
let rawText = this.row[this.column._cn].toString();
|
||||
let matchURLs = rawText.match(/URI::\((.*?)\)/g);
|
||||
if(matchURLs) {
|
||||
for(const url of matchURLs) {
|
||||
let tmpuri = url.match(/URI::\((.*?)\)/)[1];
|
||||
rawText = rawText.replace(url, '<a href="' + tmpuri + '" target="_blank">' + tmpuri + '</a>');
|
||||
}
|
||||
return rawText;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
urls() {
|
||||
if (!this.row[this.column.title]) { return }
|
||||
|
||||
const rawText = this.row[this.column.title].toString()
|
||||
let found = false
|
||||
const out = rawText.match(/URI::\((.*?)\)/g, (_, url) => {
|
||||
found = true
|
||||
const a = document.createElement('a')
|
||||
a.textContent = url
|
||||
a.setAttribute('href', url)
|
||||
return a.innerHTML
|
||||
})
|
||||
|
||||
return found && out
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped>/**/
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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">
|
||||
<template v-if="value||localState">
|
||||
<item-chip
|
||||
v-for="(ch,i) in (value|| localState)"
|
||||
:key="i"
|
||||
@@ -22,6 +22,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!isLocked"
|
||||
class="actions align-center justify-center px-1 flex-shrink-1"
|
||||
@@ -44,14 +45,13 @@
|
||||
<list-items
|
||||
v-if="newRecordModal"
|
||||
v-model="newRecordModal"
|
||||
:hm="hm"
|
||||
:tn="hm && hm.tn"
|
||||
:size="10"
|
||||
:meta="childMeta"
|
||||
:primary-col="childPrimaryCol"
|
||||
:primary-key="childPrimaryKey"
|
||||
:api="childApi"
|
||||
:parent-meta="meta"
|
||||
:column="column"
|
||||
:query-params="{
|
||||
...childQueryParams,
|
||||
// check if it needs to bypass to
|
||||
@@ -67,6 +67,7 @@
|
||||
}"
|
||||
:is-public="isPublic"
|
||||
:password="password"
|
||||
:row-id="parentId"
|
||||
@add-new-record="insertAndAddNewChildRecord"
|
||||
@add="addChildToParent"
|
||||
/>
|
||||
@@ -109,7 +110,6 @@
|
||||
/>
|
||||
|
||||
<v-dialog
|
||||
v-if="selectedChild && !isPublic"
|
||||
v-model="expandFormModal"
|
||||
:overlay-opacity="0.8"
|
||||
width="1000px"
|
||||
@@ -124,10 +124,9 @@
|
||||
:db-alias="nodes.dbAlias"
|
||||
:has-many="childMeta.hasMany"
|
||||
:belongs-to="childMeta.belongsTo"
|
||||
:table="childMeta.tn"
|
||||
:table="childMeta.table_name"
|
||||
:old-row="{...selectedChild}"
|
||||
:meta="childMeta"
|
||||
:sql-ui="sqlUi"
|
||||
:primary-value-column="childPrimaryCol"
|
||||
:api="childApi"
|
||||
:available-columns="childAvailableColumns"
|
||||
@@ -137,7 +136,7 @@
|
||||
:is-new.sync="isNewChild"
|
||||
:disabled-columns="disabledChildColumns"
|
||||
:breadcrumbs="breadcrumbs"
|
||||
@cancel="selectedChild = null"
|
||||
@cancel="selectedChild = null; expandFormModal =false"
|
||||
@input="onChildSave"
|
||||
/>
|
||||
</v-dialog>
|
||||
@@ -146,6 +145,7 @@
|
||||
|
||||
<script>
|
||||
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
|
||||
import { isSystemColumn, RelationTypes, UITypes } from 'nocodb-sdk'
|
||||
import DlgLabelSubmitCancel from '@/components/utils/dlgLabelSubmitCancel'
|
||||
import Pagination from '@/components/project/spreadsheet/components/pagination'
|
||||
import ListItems from '@/components/project/spreadsheet/components/virtualCell/components/listItems'
|
||||
@@ -177,10 +177,8 @@ export default {
|
||||
},
|
||||
value: [Object, Array],
|
||||
meta: [Object],
|
||||
hm: Object,
|
||||
nodes: [Object],
|
||||
row: [Object],
|
||||
sqlUi: [Object, Function],
|
||||
active: Boolean,
|
||||
isNew: Boolean,
|
||||
isForm: Boolean,
|
||||
@@ -204,37 +202,30 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
childMeta() {
|
||||
return this.metas ? this.metas[this.hm.tn] : this.$store.state.meta.metas[this.hm.tn]
|
||||
return this.metas
|
||||
? this.metas[this.column.colOptions.fk_related_model_id]
|
||||
: this.$store.state.meta.metas[this.column.colOptions.fk_related_model_id]
|
||||
},
|
||||
// todo : optimize
|
||||
childApi() {
|
||||
return this.childMeta && this.$ncApis.get({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
table: this.childMeta.tn
|
||||
})
|
||||
// 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
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {}).title
|
||||
},
|
||||
primaryCol() {
|
||||
return this.meta && (this.meta.columns.find(c => c.pv) || {})._cn
|
||||
return this.meta && (this.meta.columns.find(c => c.pv) || {}).title
|
||||
},
|
||||
childPrimaryKey() {
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {}).title
|
||||
},
|
||||
childForeignKey() {
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.cn === this.hm.cn) || {})._cn
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id) || {}).title
|
||||
},
|
||||
childForeignKeyVal() {
|
||||
return this.meta && this.meta.columns ? this.meta.columns.filter(c => c._cn === this.childForeignKey).map(c => this.row[c._cn] || '').join('___') : ''
|
||||
return this.meta && this.meta.columns ? this.meta.columns.filter(c => c.title === this.childForeignKey).map(c => this.row[c.title] || '').join('___') : ''
|
||||
},
|
||||
isVirtualRelation() {
|
||||
return (this.childMeta && (!!this.childMeta.columns.find(c => c.cn === this.hm.cn && this.hm.type === 'virtual'))) || false
|
||||
return this.column && this.column.colOptions.virtual// (this.childMeta && (!!this.childMeta.columns.find(c => c.column_name === this.hm.column_name && this.hm.type === 'virtual'))) || false
|
||||
},
|
||||
isByPass() {
|
||||
if (this.isVirtualRelation) {
|
||||
@@ -242,7 +233,10 @@ export default {
|
||||
}
|
||||
// if child fk references a column in parent which is not pk,
|
||||
// then this column has to be filled
|
||||
if (((this.meta && this.meta.columns.find(c => !c.pk && c.cn === this.hm.rcn)) || false)) {
|
||||
// if (((this.meta && this.meta.columns.find(c => !c.pk && c.id === this.hm.rcn)) || false)) {
|
||||
// return this.childForeignKeyVal === ''
|
||||
// }
|
||||
if (((this.meta && this.meta.columns.find(c => !c.pk && c.id === this.column.fk_parent_column_id)) || false)) {
|
||||
return this.childForeignKeyVal === ''
|
||||
}
|
||||
return false
|
||||
@@ -255,12 +249,12 @@ export default {
|
||||
return this.selectedChild && !this.isPublic ? () => import('@/components/project/spreadsheet/components/expandedForm') : 'span'
|
||||
},
|
||||
childAvailableColumns() {
|
||||
const hideCols = ['created_at', 'updated_at']
|
||||
// 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))))
|
||||
columns.push(...this.childMeta.columns.filter(c => !isSystemColumn(c)))
|
||||
}
|
||||
if (this.childMeta.v) {
|
||||
columns.push(...this.childMeta.v.map(v => ({ ...v, virtual: 1 })))
|
||||
@@ -271,15 +265,15 @@ export default {
|
||||
if (!this.childMeta) { return {} }
|
||||
// todo: use reduce
|
||||
return {
|
||||
hm: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({ hm }) => hm.tn).join()) || '',
|
||||
hm: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({ hm }) => hm.table_name).join()) || '',
|
||||
bt: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.bt).map(({ bt }) => bt.rtn).join()) || '',
|
||||
mm: (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._cn === this.childForeignKey).map(c => this.row[c._cn] || '').join('___') ||
|
||||
this.meta.columns.filter(c => c.pk).map(c => this.row[c._cn]).join('___'))) || ''
|
||||
(this.meta.columns.filter(c => c.title === this.childForeignKey).map(c => this.row[c.title] || '').join('___') ||
|
||||
this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___'))) || ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -291,6 +285,10 @@ export default {
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadChildMeta()
|
||||
|
||||
if (this.isNew && this.value) {
|
||||
this.localState = [...this.value]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadChildMeta()
|
||||
@@ -315,16 +313,16 @@ export default {
|
||||
if (act === 'hideDialog') {
|
||||
this.dialogShow = false
|
||||
} else {
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c._cn]).join('___')
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
try {
|
||||
await this.childApi.delete(id)
|
||||
await this.$api.data.delete(this.childMeta.id, 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)
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,14 +335,21 @@ export default {
|
||||
}
|
||||
|
||||
await this.loadChildMeta()
|
||||
const column = this.childMeta.columns.find(c => c.cn === this.hm.cn)
|
||||
const column = this.childMeta.columns.find(c => c.id === this.column.colOptions.fk_child_column_id)
|
||||
|
||||
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)
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
await this.$api.data.nestedDelete(
|
||||
this.meta.id,
|
||||
this.parentId,
|
||||
this.column.id,
|
||||
'hm',
|
||||
id
|
||||
)
|
||||
|
||||
this.$emit('loadTableData')
|
||||
if ((this.childListModal || this.isForm) && this.$refs.childList) {
|
||||
this.$refs.childList.loadData()
|
||||
@@ -358,16 +363,8 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.hm.tn
|
||||
id: this.column.colOptions.fk_related_model_id
|
||||
})
|
||||
// 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() {
|
||||
@@ -383,14 +380,15 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c._cn]).join('___')
|
||||
const _cn = this.childForeignKey
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
this.newRecordModal = false
|
||||
await this.childApi.update(id, {
|
||||
[_cn]: parseIfInteger(this.parentId)
|
||||
}, {
|
||||
[_cn]: child[this.childForeignKey]
|
||||
})
|
||||
await this.$api.data.nestedAdd(
|
||||
this.meta.id,
|
||||
this.parentId,
|
||||
this.column.id,
|
||||
'hm',
|
||||
id
|
||||
)
|
||||
|
||||
this.$emit('loadTableData')
|
||||
if ((this.childListModal || this.isForm) && this.$refs.childList) {
|
||||
@@ -400,8 +398,8 @@ export default {
|
||||
async editChild(child) {
|
||||
await this.loadChildMeta()
|
||||
this.isNewChild = false
|
||||
this.selectedChild = child
|
||||
this.expandFormModal = true
|
||||
this.selectedChild = child
|
||||
setTimeout(() => {
|
||||
this.$refs.expandedForm && this.$refs.expandedForm.reload()
|
||||
}, 500)
|
||||
@@ -411,7 +409,14 @@ export default {
|
||||
await this.loadChildMeta()
|
||||
this.isNewChild = true
|
||||
this.selectedChild = {
|
||||
[this.childForeignKey]: parseIfInteger(this.parentId)
|
||||
[this.childForeignKey]: parseIfInteger(this.parentId),
|
||||
[(this.childMeta.columns.find(c => c.uidt === UITypes.LinkToAnotherRecord &&
|
||||
c.colOptions &&
|
||||
this.column.colOptions &&
|
||||
c.colOptions.fk_child_column_id === this.column.colOptions.fk_child_column_id &&
|
||||
c.colOptions.fk_parent_column_id === this.column.colOptions.fk_parent_column_id &&
|
||||
c.colOptions.type === RelationTypes.BELONGS_TO
|
||||
) || {}).title]: this.row
|
||||
}
|
||||
this.expandFormModal = true
|
||||
if (!this.isNew) {
|
||||
@@ -434,13 +439,13 @@ export default {
|
||||
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
|
||||
const pid = this.meta.columns.filter(c => c.pk).map(c => row[c.title]).join('___')
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
const title = this.childForeignKey
|
||||
await this.childApi.update(id, {
|
||||
[_cn]: parseIfInteger(pid)
|
||||
[title]: parseIfInteger(pid)
|
||||
}, {
|
||||
[_cn]: child[this.childForeignKey]
|
||||
[title]: child[this.childForeignKey]
|
||||
})
|
||||
} else {
|
||||
await this.addChildToParent(child)
|
||||
|
||||
@@ -1,59 +1,77 @@
|
||||
<template>
|
||||
<div class="chips-wrapper">
|
||||
<div class="chips d-flex align-center lookup-items">
|
||||
<template v-if="localValue">
|
||||
<item-chip
|
||||
v-for="(value,i) in localValue"
|
||||
:key="i"
|
||||
:active="active"
|
||||
:value="value"
|
||||
:readonly="true"
|
||||
<div class="d-flex flex-wrap wrapper">
|
||||
<template v-if="lookupColumnMeta">
|
||||
<template
|
||||
v-if="isVirtualCol(lookupColumnMeta)"
|
||||
>
|
||||
<template
|
||||
:is="virtualCell"
|
||||
v-if="lookupColumnMeta.uidt === UITypes.LinkToAnotherRecord &&lookupColumnMeta.colOptions.type === RelationTypes.BELONGS_TO && Array.isArray(value) "
|
||||
>
|
||||
<table-cell
|
||||
:column="lookUpColumn"
|
||||
:meta="lookUpMeta"
|
||||
:db-alias="nodes.dbAlias"
|
||||
:value="value"
|
||||
<div
|
||||
:is="virtualCell"
|
||||
v-for="(v,i) in value"
|
||||
:key="i"
|
||||
:is-public="true"
|
||||
:metas="metas"
|
||||
:is-locked="true"
|
||||
:column="lookupColumnMeta"
|
||||
:row="{[lookupColumnMeta.title]:v}"
|
||||
:nodes="nodes"
|
||||
:meta="lookupTableMeta"
|
||||
:sql-ui="sqlUi"
|
||||
/>
|
||||
</item-chip>
|
||||
</template>
|
||||
<div
|
||||
:is="virtualCell"
|
||||
v-else
|
||||
:is-public="true"
|
||||
:metas="metas"
|
||||
:is-locked="true"
|
||||
:column="lookupColumnMeta"
|
||||
:row="{[lookupColumnMeta.title]:value}"
|
||||
:nodes="nodes"
|
||||
:meta="lookupTableMeta"
|
||||
:sql-ui="sqlUi"
|
||||
/>
|
||||
</template>
|
||||
<span
|
||||
v-if="localValue && localValue.length === 10"
|
||||
class="caption pointer ml-1 grey--text"
|
||||
@click="showLookupListModal"
|
||||
>more...
|
||||
</span>
|
||||
</div>
|
||||
<list-child-items-modal
|
||||
v-if="lookUpMeta && lookupListModal"
|
||||
ref="childList"
|
||||
v-model="lookupListModal"
|
||||
:is-form="isForm"
|
||||
:is-new="isNew"
|
||||
:size="10"
|
||||
:meta="lookUpMeta"
|
||||
:parent-meta="meta"
|
||||
:primary-col="lookUpColumnAlias"
|
||||
:api="lookupApi"
|
||||
:password="password"
|
||||
:read-only="true"
|
||||
:query-params="queryParams"
|
||||
:metas="metas"
|
||||
/>
|
||||
<template v-else>
|
||||
<template v-if="localValue">
|
||||
<item-chip
|
||||
v-for="(value,i) in localValue"
|
||||
:key="i"
|
||||
style="margin: 1.5px"
|
||||
:active="active"
|
||||
:value="value"
|
||||
:readonly="true"
|
||||
>
|
||||
<table-cell
|
||||
|
||||
:is-locked="true"
|
||||
:column="lookupColumnMeta"
|
||||
:meta="lookupTableMeta"
|
||||
:db-alias="nodes.dbAlias"
|
||||
:value="value"
|
||||
:sql-ui="sqlUi"
|
||||
/>
|
||||
</item-chip>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
|
||||
import { isVirtualCol, RelationTypes, UITypes } from 'nocodb-sdk'
|
||||
import TableCell from '../cell'
|
||||
import ItemChip from '@/components/project/spreadsheet/components/virtualCell/components/itemChip'
|
||||
import ListChildItemsModal
|
||||
from '@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal'
|
||||
|
||||
export default {
|
||||
name: 'LookupCell',
|
||||
components: { TableCell, ListChildItemsModal, ItemChip },
|
||||
components: {
|
||||
TableCell,
|
||||
// ListChildItemsModal,
|
||||
ItemChip
|
||||
},
|
||||
props: {
|
||||
meta: [Object],
|
||||
metas: [Object],
|
||||
@@ -64,105 +82,69 @@ export default {
|
||||
sqlUi: [Object, Function],
|
||||
active: Boolean,
|
||||
isNew: Boolean,
|
||||
isForm: Boolean
|
||||
isForm: Boolean,
|
||||
value: [Object, Array, String, Number]
|
||||
},
|
||||
data: () => ({
|
||||
lookupListModal: false
|
||||
UITypes,
|
||||
lookupListModal: false,
|
||||
lookupTableMeta: null,
|
||||
lookupColumnMeta: null,
|
||||
isVirtualCol,
|
||||
RelationTypes
|
||||
}),
|
||||
computed: {
|
||||
virtualCell() {
|
||||
return this.lookupColumnMeta && isVirtualCol(this.lookupColumnMeta) ? () => import('@/components/project/spreadsheet/components/virtualCell') : 'div'
|
||||
},
|
||||
// todo : optimize
|
||||
lookupApi() {
|
||||
return this.column && this.$ncApis.get({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
table: this.column.lk.ltn
|
||||
})
|
||||
// return this.column && this.$ncApis.get({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// table: this.column.lk.ltn
|
||||
// })
|
||||
},
|
||||
lookUpMeta() {
|
||||
return this.metas ? this.metas[this.column.lk.ltn] : this.$store.state.meta.metas[this.column.lk.ltn]
|
||||
// return this.metas ? this.metas[this.column.lk.ltn] : this.$store.state.meta.metas[this.column.lk.ltn]
|
||||
},
|
||||
|
||||
assocMeta() {
|
||||
return this.column.lk.type === 'mm' && (this.metas ? this.metas[this.column.lk.vtn] : this.$store.state.meta.metas[this.column.lk.vtn])
|
||||
// return this.column.lk.type === 'mm' && (this.metas ? this.metas[this.column.lk.vtn] : this.$store.state.meta.metas[this.column.lk.vtn])
|
||||
},
|
||||
lookUpColumnAlias() {
|
||||
if (!this.lookUpMeta || !this.column.lk.lcn) {
|
||||
return
|
||||
}
|
||||
return (this.lookUpMeta.columns.find(cl => cl.cn === this.column.lk.lcn) || {})._cn
|
||||
return (this.lookUpMeta.columns.find(cl => cl.column_name === this.column.lk.lcn) || {}).title
|
||||
},
|
||||
lookUpColumn() {
|
||||
if (!this.lookUpMeta || !this.column.lk.lcn) {
|
||||
return
|
||||
}
|
||||
return (this.lookUpMeta.columns.find(cl => cl.cn === this.column.lk.lcn) || {})
|
||||
return (this.lookUpMeta.columns.find(cl => cl.column_name === this.column.lk.lcn) || {})
|
||||
},
|
||||
localValueObj() {
|
||||
if (!this.column || !this.row) {
|
||||
return null
|
||||
}
|
||||
switch (this.column.lk.type) {
|
||||
case 'mm':
|
||||
return this.row[`${this.column.lk._ltn}MMList`]
|
||||
case 'hm':
|
||||
return this.row[`${this.column.lk._ltn}List`]
|
||||
case 'bt':
|
||||
return this.row[`${this.column.lk._ltn}Read`]
|
||||
default:
|
||||
return null
|
||||
}
|
||||
},
|
||||
localValue() {
|
||||
if (!this.localValueObj || !this.lookUpColumnAlias) {
|
||||
return null
|
||||
}
|
||||
if (Array.isArray(this.localValueObj)) {
|
||||
return this.localValueObj.map(o => o[this.lookUpColumnAlias])
|
||||
}
|
||||
return [this.localValueObj[this.lookUpColumnAlias]]
|
||||
return this.value && (Array.isArray(this.value) ? this.value : [this.value])
|
||||
},
|
||||
queryParams() {
|
||||
switch (this.column.lk.type) {
|
||||
case 'bt':
|
||||
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.lk.rcn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.lk.cn)._cn]})` }
|
||||
case 'hm':
|
||||
return { where: `(${this.lookUpMeta.columns.find(c => c.cn === this.column.lk.cn)._cn},eq,${this.row[this.meta.columns.find(c => c.cn === this.column.lk.rcn)._cn]})` }
|
||||
case 'mm':
|
||||
return this.assocMeta
|
||||
? {
|
||||
conditionGraph: {
|
||||
[this.assocMeta.tn]: {
|
||||
relationType: 'hm',
|
||||
[this.assocMeta.columns.find(c => c.cn === this.column.lk.vcn).cn]: {
|
||||
eq: this.row[this.meta.columns.find(c => c.cn === this.column.lk.cn)._cn]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: {}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadLookupMeta()
|
||||
this.loadLookupColumnMeta()
|
||||
},
|
||||
methods: {
|
||||
|
||||
async loadLookupColumnMeta() {
|
||||
const relationColumn = this.meta.columns.find(c => c.id === this.column.colOptions.fk_relation_column_id)
|
||||
this.lookupTableMeta = await this.$store.dispatch('meta/ActLoadMeta', { id: relationColumn.colOptions.fk_related_model_id })
|
||||
this.lookupColumnMeta = this.lookupTableMeta.columns.find(c => c.id === this.column.colOptions.fk_lookup_column_id)
|
||||
},
|
||||
|
||||
async loadLookupMeta() {
|
||||
if (!this.metas && !this.lookUpMeta) {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.column.lk.ltn
|
||||
})
|
||||
}
|
||||
if (!this.metas && this.column.lk.type === 'mm' && !this.assocMeta) {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.column.lk.vtn
|
||||
})
|
||||
}
|
||||
},
|
||||
showLookupListModal() {
|
||||
this.lookupListModal = true
|
||||
@@ -172,24 +154,9 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chips-wrapper {
|
||||
.chips {
|
||||
max-width: 100%;
|
||||
|
||||
&.lookup-items {
|
||||
.wrapper{
|
||||
flex-wrap: wrap;
|
||||
row-gap: 3px;
|
||||
gap: 3px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.chips {
|
||||
max-width: calc(100% - 44px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
v-model="newRecordModal"
|
||||
:hm="true"
|
||||
:size="10"
|
||||
:column="column"
|
||||
:meta="childMeta"
|
||||
:primary-col="childPrimaryCol"
|
||||
:primary-key="childPrimaryKey"
|
||||
@@ -50,6 +51,7 @@
|
||||
:is-public="isPublic"
|
||||
:query-params="childQueryParams"
|
||||
:password="password"
|
||||
:row-id="row && row[parentPrimaryKey]"
|
||||
@add-new-record="insertAndAddNewChildRecord"
|
||||
@add="addChildToParent"
|
||||
/>
|
||||
@@ -105,19 +107,17 @@
|
||||
:db-alias="nodes.dbAlias"
|
||||
:has-many="childMeta.hasMany"
|
||||
:belongs-to="childMeta.belongsTo"
|
||||
:table="childMeta.tn"
|
||||
:table="childMeta.table_name"
|
||||
: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"
|
||||
:is-new.sync="isNewChild"
|
||||
:breadcrumbs="breadcrumbs"
|
||||
@cancel="selectedChild = null"
|
||||
@cancel="selectedChild = null; expandFormModal =false"
|
||||
@input="onChildSave"
|
||||
/>
|
||||
</v-dialog>
|
||||
@@ -125,12 +125,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import ApiFactory from '@/components/project/spreadsheet/apis/apiFactory'
|
||||
import { RelationTypes, UITypes } from 'nocodb-sdk'
|
||||
import DlgLabelSubmitCancel from '@/components/utils/dlgLabelSubmitCancel'
|
||||
import ListItems from '@/components/project/spreadsheet/components/virtualCell/components/listItems'
|
||||
import ListChildItems from '@/components/project/spreadsheet/components/virtualCell/components/listChildItems'
|
||||
import listChildItemsModal
|
||||
from '@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal'
|
||||
from '@/components/project/spreadsheet/components/virtualCell/components/listChildItemsModal'
|
||||
import { parseIfInteger } from '@/helpers'
|
||||
import ItemChip from '~/components/project/spreadsheet/components/virtualCell/components/itemChip'
|
||||
|
||||
@@ -187,23 +187,23 @@ export default {
|
||||
}
|
||||
},
|
||||
childMeta() {
|
||||
return this.metas ? this.metas[this.mm.rtn] : this.$store.state.meta.metas[this.mm.rtn]
|
||||
return this.metas ? this.metas[this.column.colOptions.fk_related_model_id] : this.$store.state.meta.metas[this.column.colOptions.fk_related_model_id]
|
||||
},
|
||||
assocMeta() {
|
||||
return this.metas ? this.metas[this.mm.vtn] : this.$store.state.meta.metas[this.mm.vtn]
|
||||
return this.metas ? this.metas[this.column.colOptions.fk_mm_model_id] : this.$store.state.meta.metas[this.column.colOptions.fk_mm_model_id]
|
||||
},
|
||||
// todo : optimize
|
||||
childApi() {
|
||||
return this.childMeta && this.$ncApis.get({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
table: this.childMeta.tn
|
||||
})
|
||||
// return this.childMeta && this.$ncApis.get({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// id: this.column.colOptions.fk_related_model_id
|
||||
// })
|
||||
//
|
||||
// return this.childMeta && this.childMeta._tn
|
||||
// return this.childMeta && this.childMeta.title
|
||||
// ? ApiFactory.create(
|
||||
// this.$store.getters['project/GtrProjectType'],
|
||||
// this.childMeta._tn,
|
||||
// this.childMeta.title,
|
||||
// this.childMeta.columns,
|
||||
// this,
|
||||
// this.childMeta
|
||||
@@ -212,15 +212,15 @@ export default {
|
||||
},
|
||||
// todo : optimize
|
||||
assocApi() {
|
||||
return this.childMeta && this.$ncApis.get({
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
table: this.assocMeta.tn
|
||||
})
|
||||
// return this.assocMeta && this.assocMeta._tn
|
||||
// return this.childMeta && this.$ncApis.get({
|
||||
// env: this.nodes.env,
|
||||
// dbAlias: this.nodes.dbAlias,
|
||||
// id: this.column.colOptions.fk_mm_model_id
|
||||
// })
|
||||
// return this.assocMeta && this.assocMeta.title
|
||||
// ? ApiFactory.create(
|
||||
// this.$store.getters['project/GtrProjectType'],
|
||||
// this.assocMeta._tn,
|
||||
// this.assocMeta.title,
|
||||
// this.assocMeta.columns,
|
||||
// this,
|
||||
// this.assocMeta
|
||||
@@ -228,33 +228,33 @@ export default {
|
||||
// : null
|
||||
},
|
||||
childPrimaryCol() {
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {})._cn
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {}).title
|
||||
},
|
||||
childPrimaryKey() {
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn
|
||||
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {}).title
|
||||
},
|
||||
parentPrimaryKey() {
|
||||
return this.meta && (this.meta.columns.find(c => c.pk) || {})._cn
|
||||
return this.meta && (this.meta.columns.find(c => c.pk) || {}).title
|
||||
},
|
||||
childQueryParams() {
|
||||
if (!this.childMeta) { return {} }
|
||||
// todo: use reduce
|
||||
return {
|
||||
hm: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({ hm }) => hm.tn).join()) || '',
|
||||
hm: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({ hm }) => hm.table_name).join()) || '',
|
||||
bt: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.bt).map(({ bt }) => bt.rtn).join()) || '',
|
||||
mm: (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]
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (!this.childMeta || !this.assocMeta) { return null }
|
||||
// return {
|
||||
// [this.assocMeta.table_name]: {
|
||||
// relationType: 'hm',
|
||||
// [this.assocMeta.columns.find(c => c.column_name === this.mm.vcn).column_name]: {
|
||||
// eq: this.row[this.parentPrimaryKey]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
},
|
||||
childAvailableColumns() {
|
||||
const hideCols = ['created_at', 'updated_at']
|
||||
@@ -262,7 +262,7 @@ export default {
|
||||
|
||||
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))))
|
||||
columns.push(...this.childMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.column_name) && !((this.childMeta.v || []).some(v => v.bt && v.bt.column_name === c.column_name))))
|
||||
}
|
||||
if (this.childMeta.v) {
|
||||
columns.push(...this.childMeta.v.map(v => ({ ...v, virtual: 1 })))
|
||||
@@ -285,6 +285,10 @@ export default {
|
||||
if (this.isForm) {
|
||||
await Promise.all([this.loadChildMeta(), this.loadAssociateTableMeta()])
|
||||
}
|
||||
|
||||
if (this.isNew && this.value) {
|
||||
this.localState = [...this.value]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadChildMeta()
|
||||
@@ -310,15 +314,15 @@ export default {
|
||||
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)
|
||||
const cid = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
const pid = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
|
||||
await this.$api.data.nestedDelete(
|
||||
this.meta.id,
|
||||
pid,
|
||||
this.column.id,
|
||||
'mm',
|
||||
cid
|
||||
)
|
||||
this.$emit('loadTableData')
|
||||
if ((this.childListModal || this.isForm) && this.$refs.childList) {
|
||||
this.$refs.childList.loadData()
|
||||
@@ -332,7 +336,7 @@ export default {
|
||||
if (act === 'hideDialog') {
|
||||
this.dialogShow = false
|
||||
} else {
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c._cn]).join('___')
|
||||
const id = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
await this.childApi.delete(id)
|
||||
this.dialogShow = false
|
||||
this.$emit('loadTableData')
|
||||
@@ -348,7 +352,8 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.mm.rtn
|
||||
// tn: this.mm.rtn,
|
||||
id: this.column.colOptions.fk_related_model_id
|
||||
})
|
||||
// const parentTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// env: this.nodes.env,
|
||||
@@ -365,7 +370,7 @@ export default {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
tn: this.mm.vtn
|
||||
id: this.column.colOptions.fk_mm_model_id
|
||||
})
|
||||
// const assocTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
// env: this.nodes.env,
|
||||
@@ -389,16 +394,25 @@ export default {
|
||||
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 cid = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
const pid = this.meta.columns.filter(c => c.pk).map(c => this.row[c.title]).join('___')
|
||||
|
||||
// const vcidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_parent_column_id).title
|
||||
// const vpidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_child_column_id).title
|
||||
|
||||
await this.$api.data.nestedAdd(
|
||||
this.meta.id,
|
||||
pid,
|
||||
this.column.id,
|
||||
'mm',
|
||||
cid
|
||||
)
|
||||
|
||||
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)
|
||||
})
|
||||
// await this.$api.data.create(this.assocMeta.id, {
|
||||
// [vcidCol]: parseIfInteger(cid),
|
||||
// [vpidCol]: parseIfInteger(pid)
|
||||
// })
|
||||
|
||||
this.$emit('loadTableData')
|
||||
} catch (e) {
|
||||
@@ -416,7 +430,15 @@ export default {
|
||||
await this.loadChildMeta()
|
||||
this.isNewChild = true
|
||||
this.selectedChild = {
|
||||
[this.childForeignKey]: this.parentId
|
||||
[this.childForeignKey]: this.parentId,
|
||||
[(this.childMeta.columns.find(c => c.uidt === UITypes.LinkToAnotherRecord &&
|
||||
c.colOptions &&
|
||||
this.column.colOptions &&
|
||||
c.colOptions.fk_child_column_id === this.column.colOptions.fk_parent_column_id &&
|
||||
c.colOptions.fk_parent_column_id === this.column.colOptions.fk_child_column_id &&
|
||||
c.colOptions.fk_mm_model_id === this.column.colOptions.fk_mm_model_id &&
|
||||
c.colOptions.type === RelationTypes.MANY_TO_MANY
|
||||
) || {}).title]: [this.row]
|
||||
}
|
||||
this.expandFormModal = true
|
||||
setTimeout(() => {
|
||||
@@ -438,11 +460,11 @@ export default {
|
||||
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 cid = this.childMeta.columns.filter(c => c.pk).map(c => child[c.title]).join('___')
|
||||
const pid = this.meta.columns.filter(c => c.pk).map(c => row[c.title]).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
|
||||
const vcidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_parent_column_id).title
|
||||
const vpidCol = this.assocMeta.columns.find(c => c.id === this.column.colOptions.fk_mm_child_column_id).title
|
||||
await this.assocApi.insert({
|
||||
[vcidCol]: parseIfInteger(cid),
|
||||
[vpidCol]: parseIfInteger(pid)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<span>
|
||||
{{ row[column._cn] }}
|
||||
{{ row[column.title] }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</v-icon>
|
||||
</template>
|
||||
<span v-on="on">
|
||||
<span class="name flex-grow-1" style="white-space: nowrap" :title="column._cn" v-html="alias" />
|
||||
<span class="name flex-grow-1" style="white-space: nowrap" :title="column.title" v-html="alias" />
|
||||
<span v-if="column.rqd || required" class="error--text text--lighten-1"> *</span>
|
||||
</span>
|
||||
</template>
|
||||
@@ -57,7 +57,10 @@
|
||||
</v-icon>
|
||||
</template>
|
||||
<v-list dense>
|
||||
<v-list-item dense @click="editColumnMenu = true">
|
||||
<v-list-item
|
||||
dense
|
||||
@click="editColumnMenu = true"
|
||||
>
|
||||
<x-icon small class="mr-1 nc-column-edit" color="primary">
|
||||
mdi-pencil
|
||||
</x-icon>
|
||||
@@ -66,16 +69,9 @@
|
||||
{{ $t('general.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">
|
||||
<v-list-item
|
||||
@click="columnDeleteDialog = true"
|
||||
>
|
||||
<x-icon small class="mr-1 nc-column-delete" color="error">
|
||||
mdi-delete-outline
|
||||
</x-icon>
|
||||
@@ -99,7 +95,7 @@
|
||||
<v-divider />
|
||||
<v-card-text class="mt-4 title">
|
||||
Do you want to delete <span class="font-weight-bold">'{{
|
||||
column._cn
|
||||
column.title
|
||||
}}'</span> column ?
|
||||
</v-card-text>
|
||||
<v-divider />
|
||||
@@ -109,7 +105,12 @@
|
||||
<!-- Cancel -->
|
||||
{{ $t('general.cancel') }}
|
||||
</v-btn>
|
||||
<v-btn small color="error" @click="deleteColumn">
|
||||
<v-btn
|
||||
v-t="['vitual:column:delete']"
|
||||
small
|
||||
color="error"
|
||||
@click="deleteColumn"
|
||||
>
|
||||
Confirm
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
@@ -148,8 +149,8 @@ export default {
|
||||
}),
|
||||
computed: {
|
||||
alias() {
|
||||
// return this.column.lk ? `${this.column.lk._lcn} <small class="grey--text text--darken-1">(from ${this.column.lk._ltn})</small>` : this.column._cn
|
||||
return this.column._cn
|
||||
// return this.column.lk ? `${this.column.lk._lcn} <small class="grey--text text--darken-1">(from ${this.column.lk._ltn})</small>` : this.column.title
|
||||
return this.column.title
|
||||
},
|
||||
type() {
|
||||
if (this.column.bt) {
|
||||
@@ -165,10 +166,10 @@ export default {
|
||||
},
|
||||
childColumn() {
|
||||
if (this.column.bt) {
|
||||
return this.column.bt.cn
|
||||
return this.column.bt.column_name
|
||||
}
|
||||
if (this.column.hm) {
|
||||
return this.column.hm.cn
|
||||
return this.column.hm.column_name
|
||||
}
|
||||
if (this.column.mm) {
|
||||
return this.column.mm.rcn
|
||||
@@ -177,10 +178,10 @@ export default {
|
||||
},
|
||||
childTable() {
|
||||
if (this.column.bt) {
|
||||
return this.column.bt.tn
|
||||
return this.column.bt.table_name
|
||||
}
|
||||
if (this.column.hm) {
|
||||
return this.column.hm.tn
|
||||
return this.column.hm.table_name
|
||||
}
|
||||
if (this.column.mm) {
|
||||
return this.column.mm.rtn
|
||||
@@ -195,7 +196,7 @@ export default {
|
||||
return this.column.hm.rtn
|
||||
}
|
||||
if (this.column.mm) {
|
||||
return this.column.mm.tn
|
||||
return this.column.mm.table_name
|
||||
}
|
||||
return ''
|
||||
},
|
||||
@@ -207,7 +208,7 @@ export default {
|
||||
return this.column.hm.rcn
|
||||
}
|
||||
if (this.column.mm) {
|
||||
return this.column.mm.cn
|
||||
return this.column.mm.column_name
|
||||
}
|
||||
return ''
|
||||
},
|
||||
@@ -216,11 +217,11 @@ export default {
|
||||
return ''
|
||||
}
|
||||
if (this.column.hm) {
|
||||
return `'${this.column.hm._rtn}' has many '${this.column.hm._tn}'`
|
||||
return `'${this.column.hm._rtn}' has many '${this.column.hm.title}'`
|
||||
} else if (this.column.mm) {
|
||||
return `'${this.column.mm._tn}' & '${this.column.mm._rtn}' have <br>many to many relation`
|
||||
return `'${this.column.mm.title}' & '${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}'`
|
||||
return `'${this.column.bt.title}' belongs to '${this.column.bt._rtn}'`
|
||||
} else if (this.column.lk) {
|
||||
return `'${this.column.lk._lcn}' from '${this.column.lk._ltn}' (${this.column.lk.type})`
|
||||
} else if (this.column.formula) {
|
||||
@@ -232,114 +233,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async deleteRelation() {
|
||||
try {
|
||||
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)
|
||||
}
|
||||
},
|
||||
async deleteLookupColumn() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
|
||||
// remove lookup from virtual columns
|
||||
meta.v = meta.v.filter(cl => cl.cn !== this.column.cn ||
|
||||
cl.type !== this.column.type ||
|
||||
cl._cn !== this.column._cn ||
|
||||
cl.tn !== this.column.tn)
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
this.$emit('saved')
|
||||
this.columnDeleteDialog = false
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async deleteFormulaColumn() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
// remove formula from virtual columns
|
||||
meta.v = meta.v.filter(cl => !cl.formula || cl._cn !== this.column._cn)
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
this.$emit('saved')
|
||||
this.columnDeleteDialog = false
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async deleteRollupColumn() {
|
||||
try {
|
||||
await this.$store.dispatch('meta/ActLoadMeta', {
|
||||
dbAlias: this.nodes.dbAlias,
|
||||
env: this.nodes.env,
|
||||
tn: this.meta.tn,
|
||||
force: true
|
||||
})
|
||||
const meta = JSON.parse(JSON.stringify(this.$store.state.meta.metas[this.meta.tn]))
|
||||
|
||||
// remove rollup from virtual columns
|
||||
meta.v = meta.v.filter(cl => !cl.rl || cl._cn !== this.column._cn)
|
||||
|
||||
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
|
||||
env: this.nodes.env,
|
||||
dbAlias: this.nodes.dbAlias
|
||||
}, 'xcModelSet', {
|
||||
tn: this.nodes.tn,
|
||||
meta
|
||||
}])
|
||||
this.$emit('saved')
|
||||
this.columnDeleteDialog = false
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async deleteColumn() {
|
||||
if (this.column.lk) {
|
||||
await this.deleteLookupColumn()
|
||||
} else if (this.column.formula) {
|
||||
await this.deleteFormulaColumn()
|
||||
} else if (this.column.rl) {
|
||||
await this.deleteRollupColumn()
|
||||
} else {
|
||||
await this.deleteRelation()
|
||||
try {
|
||||
await this.$api.dbTableColumn.delete(this.meta.id, this.column.id)
|
||||
this.$emit('saved')
|
||||
this.columnDeleteDialog = false
|
||||
} catch (e) {
|
||||
this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user