@ -1,31 +0,0 @@
|
||||
version: "2.4" |
||||
services: |
||||
nocodb: |
||||
depends_on: |
||||
root_db: |
||||
condition: service_healthy |
||||
environment: |
||||
NC_DB: "mssql://root_db:1433?u=sa&p=Password123.&d=root_db" |
||||
image: "nocodb/nocodb:latest" |
||||
ports: |
||||
- "8080:8080" |
||||
restart: always |
||||
volumes: |
||||
- "nc_data:/usr/app/data" |
||||
root_db: |
||||
environment: |
||||
ACCEPT_EULA: "Y" |
||||
SA_PASSWORD: Password123. |
||||
healthcheck: |
||||
interval: 10s |
||||
retries: 10 |
||||
start_period: 10s |
||||
test: "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P \"$$SA_PASSWORD\" -Q \"SELECT 1\" || exit 1" |
||||
timeout: 3s |
||||
image: "mcr.microsoft.com/mssql/server:2017-latest" |
||||
restart: always |
||||
volumes: |
||||
- "db_data:/var/opt/mssql" |
||||
volumes: |
||||
db_data: {} |
||||
nc_data: {} |
After Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 709 B |
Before Width: | Height: | Size: 682 B After Width: | Height: | Size: 683 B |
After Width: | Height: | Size: 965 B |
@ -0,0 +1,64 @@
|
||||
<script setup lang="ts"> |
||||
const props = defineProps<{ |
||||
allowMetaWrite: boolean |
||||
allowDataWrite: boolean |
||||
}>() |
||||
|
||||
const emits = defineEmits(['update:allowMetaWrite', 'update:allowDataWrite']) |
||||
|
||||
const dataWrite = useVModel(props, 'allowDataWrite', emits) |
||||
const metaWrite = useVModel(props, 'allowMetaWrite', emits) |
||||
</script> |
||||
|
||||
<template> |
||||
<a-form-item> |
||||
<template #help> |
||||
<span class="text-small"> |
||||
{{ $t('tooltip.allowDataWrite') }} |
||||
</span> |
||||
</template> |
||||
<template #label> |
||||
<div class="flex gap-1 justify-end"> |
||||
<span> |
||||
{{ $t('labels.allowDataWrite') }} |
||||
</span> |
||||
</div> |
||||
</template> |
||||
<div class="flex justify-start"> |
||||
<NcTooltip :disabled="!metaWrite" placement="topLeft"> |
||||
<template #title> |
||||
{{ $t('tooltip.dataWriteOptionDisabled') }} |
||||
</template> |
||||
<a-switch v-model:checked="dataWrite" :disabled="metaWrite" data-testid="nc-allow-data-write" size="small"></a-switch> |
||||
</NcTooltip> |
||||
</div> |
||||
</a-form-item> |
||||
<a-form-item> |
||||
<template #help> |
||||
<span class="text-small"> |
||||
<span class="font-weight-medium" :class="{ 'nc-allow-meta-write-help': metaWrite }"> |
||||
{{ $t('labels.notRecommended') }}: |
||||
</span> |
||||
{{ $t('tooltip.allowMetaWrite') }} |
||||
</span> |
||||
</template> |
||||
<template #label> |
||||
<div class="flex gap-1 justify-end"> |
||||
<span> |
||||
{{ $t('labels.allowMetaWrite') }} |
||||
</span> |
||||
</div> |
||||
</template> |
||||
<a-switch v-model:checked="metaWrite" data-testid="nc-allow-meta-write" class="nc-allow-meta-write" size="small"></a-switch> |
||||
</a-form-item> |
||||
</template> |
||||
|
||||
<style scoped> |
||||
.nc-allow-meta-write.ant-switch-checked { |
||||
background: #b33870; |
||||
} |
||||
|
||||
.nc-allow-meta-write-help { |
||||
color: #b33870; |
||||
} |
||||
</style> |
@ -0,0 +1,39 @@
|
||||
<script setup lang="ts"> |
||||
import type { TooltipPlacement } from 'ant-design-vue/es/tooltip' |
||||
import type { CSSProperties } from '@vue/runtime-dom' |
||||
|
||||
defineProps<{ |
||||
tooltipStyle?: CSSProperties |
||||
overlayInnerStyle?: CSSProperties |
||||
mouseLeaveDelay?: number |
||||
placement?: TooltipPlacement |
||||
trigger?: 'hover' | 'click' |
||||
message?: string |
||||
enabled?: boolean |
||||
}>() |
||||
</script> |
||||
|
||||
<template> |
||||
<NcTooltip |
||||
:disabled="!enabled" |
||||
:tooltip-style="{ 'min-width': 'max-content' }" |
||||
:overlay-inner-style="{ 'min-width': 'max-content' }" |
||||
:mouse-leave-delay="0.3" |
||||
placement="left" |
||||
trigger="hover" |
||||
> |
||||
<template #title> |
||||
{{ $t('tooltip.schemaChangeDisabled') }} <br /> |
||||
{{ message }} |
||||
<br v-if="message" /> |
||||
<a |
||||
class="!text-current" |
||||
href="https://docs.nocodb.com/data-sources/connect-to-data-source#configuring-permissions" |
||||
target="_blank" |
||||
> |
||||
Learn more |
||||
</a> |
||||
</template> |
||||
<slot /> |
||||
</NcTooltip> |
||||
</template> |
@ -0,0 +1,241 @@
|
||||
<script setup lang="ts"> |
||||
import NcTooltip from '~/components/nc/Tooltip.vue' |
||||
|
||||
const props = defineProps<{ |
||||
current: number |
||||
total: number |
||||
pageSize: number |
||||
entityName?: string |
||||
mode?: 'simple' | 'full' |
||||
prevPageTooltip?: string |
||||
nextPageTooltip?: string |
||||
firstPageTooltip?: string |
||||
lastPageTooltip?: string |
||||
showSizeChanger?: boolean |
||||
}>() |
||||
|
||||
const emits = defineEmits(['update:current', 'update:pageSize']) |
||||
|
||||
const { total, showSizeChanger } = toRefs(props) |
||||
|
||||
const current = useVModel(props, 'current', emits) |
||||
|
||||
const pageSize = useVModel(props, 'pageSize', emits) |
||||
|
||||
const { gridViewPageSize, setGridViewPageSize } = useGlobal() |
||||
|
||||
const localPageSize = computed({ |
||||
get: () => { |
||||
if (!showSizeChanger.value) return pageSize.value |
||||
|
||||
const storedPageSize = gridViewPageSize.value || 25 |
||||
|
||||
if (pageSize.value !== storedPageSize) { |
||||
pageSize.value = storedPageSize |
||||
} |
||||
|
||||
return pageSize.value |
||||
}, |
||||
set: (val) => { |
||||
setGridViewPageSize(val) |
||||
|
||||
pageSize.value = val |
||||
}, |
||||
}) |
||||
|
||||
const entityName = computed(() => props.entityName || 'item') |
||||
|
||||
const totalPages = computed(() => Math.max(Math.ceil(total.value / localPageSize.value), 1)) |
||||
|
||||
const { isMobileMode } = useGlobal() |
||||
|
||||
const mode = computed(() => props.mode || (isMobileMode.value ? 'simple' : 'full')) |
||||
|
||||
const changePage = ({ increase, set }: { increase?: boolean; set?: number }) => { |
||||
if (set) { |
||||
current.value = set |
||||
} else if (increase && current.value < totalPages.value) { |
||||
current.value = current.value + 1 |
||||
} else if (current.value > 0) { |
||||
current.value = current.value - 1 |
||||
} |
||||
} |
||||
|
||||
const goToLastPage = () => { |
||||
current.value = totalPages.value |
||||
} |
||||
|
||||
const goToFirstPage = () => { |
||||
current.value = 1 |
||||
} |
||||
|
||||
const pagesList = computed(() => { |
||||
return Array.from({ length: totalPages.value }, (_, i) => ({ |
||||
value: i + 1, |
||||
label: i + 1, |
||||
})) |
||||
}) |
||||
|
||||
const pageSizeOptions = [ |
||||
{ |
||||
value: 25, |
||||
label: '25 / page', |
||||
}, |
||||
{ |
||||
value: 50, |
||||
label: '50 / page', |
||||
}, |
||||
{ |
||||
value: 75, |
||||
label: '75 / page', |
||||
}, |
||||
{ |
||||
value: 100, |
||||
label: '100 / page', |
||||
}, |
||||
] |
||||
</script> |
||||
|
||||
<template> |
||||
<div class="nc-pagination flex flex-row items-center gap-x-0.25"> |
||||
<template v-if="totalPages > 1"> |
||||
<component :is="props.firstPageTooltip && mode === 'full' ? NcTooltip : 'div'" v-if="mode === 'full'"> |
||||
<template v-if="props.firstPageTooltip" #title> |
||||
{{ props.firstPageTooltip }} |
||||
</template> |
||||
<NcButton |
||||
v-e="[`a:pagination:${entityName}:first-page`]" |
||||
class="first-page !border-0" |
||||
type="text" |
||||
size="xsmall" |
||||
:disabled="current === 1" |
||||
@click="goToFirstPage" |
||||
> |
||||
<GeneralIcon icon="doubleLeftArrow" class="nc-pagination-icon" /> |
||||
</NcButton> |
||||
</component> |
||||
|
||||
<component :is="props.prevPageTooltip && mode === 'full' ? NcTooltip : 'div'"> |
||||
<template v-if="props.prevPageTooltip" #title> |
||||
{{ props.prevPageTooltip }} |
||||
</template> |
||||
<NcButton |
||||
v-e="[`a:pagination:${entityName}:prev-page`]" |
||||
class="prev-page !border-0" |
||||
type="secondary" |
||||
size="xsmall" |
||||
:disabled="current === 1" |
||||
@click="changePage({ increase: false })" |
||||
> |
||||
<GeneralIcon icon="arrowLeft" class="nc-pagination-icon" /> |
||||
</NcButton> |
||||
</component> |
||||
|
||||
<div v-if="!isMobileMode" class="text-gray-500"> |
||||
<NcDropdown placement="top" overlay-class-name="!shadow-none"> |
||||
<NcButton class="!border-0 nc-select-page" type="secondary" size="xsmall"> |
||||
<div class="flex gap-1 items-center px-2"> |
||||
<span class="nc-current-page"> |
||||
{{ current }} |
||||
</span> |
||||
<GeneralIcon icon="arrowDown" class="text-gray-800 mt-0.5 nc-select-expand-btn" /> |
||||
</div> |
||||
</NcButton> |
||||
|
||||
<template #overlay> |
||||
<NcMenu class="nc-scrollbar-md nc-pagination-menu max-h-54"> |
||||
<NcSubMenu :key="`${localPageSize}page`" class="bg-gray-100 z-20 top-0 !sticky"> |
||||
<template #title> |
||||
<div class="rounded-lg text-[13px] font-medium w-full">{{ localPageSize }} / page</div> |
||||
</template> |
||||
|
||||
<NcMenuItem v-for="option in pageSizeOptions" :key="option.value" @click="localPageSize = option.value"> |
||||
<span |
||||
class="text-[13px]" |
||||
:class="{ |
||||
'!text-brand-500': option.value === localPageSize, |
||||
}" |
||||
> |
||||
{{ option.value }} / page |
||||
</span> |
||||
</NcMenuItem> |
||||
</NcSubMenu> |
||||
|
||||
<div :key="localPageSize" class="flex flex-col mt-1 max-h-48 overflow-hidden nc-scrollbar-md gap-1"> |
||||
<NcMenuItem |
||||
v-for="x in pagesList" |
||||
:key="`${localPageSize}${x.value}`" |
||||
@click.stop=" |
||||
changePage({ |
||||
set: x.value, |
||||
}) |
||||
" |
||||
> |
||||
<div |
||||
:class="{ |
||||
'text-brand-500': x.value === current, |
||||
}" |
||||
class="flex text-[13px] !w-full text-gray-800 items-center justify-between" |
||||
> |
||||
{{ x.label }} |
||||
</div> |
||||
</NcMenuItem> |
||||
</div> |
||||
</NcMenu> |
||||
</template> |
||||
</NcDropdown> |
||||
</div> |
||||
|
||||
<component :is="props.nextPageTooltip && mode === 'full' ? NcTooltip : 'div'"> |
||||
<template v-if="props.nextPageTooltip" #title> |
||||
{{ props.nextPageTooltip }} |
||||
</template> |
||||
<NcButton |
||||
v-e="[`a:pagination:${entityName}:next-page`]" |
||||
class="next-page !border-0" |
||||
type="secondary" |
||||
size="xsmall" |
||||
:disabled="current === totalPages" |
||||
@click="changePage({ increase: true })" |
||||
> |
||||
<GeneralIcon icon="arrowRight" class="nc-pagination-icon" /> |
||||
</NcButton> |
||||
</component> |
||||
|
||||
<component :is="props.lastPageTooltip && mode === 'full' ? NcTooltip : 'div'" v-if="mode === 'full'"> |
||||
<template v-if="props.lastPageTooltip" #title> |
||||
{{ props.lastPageTooltip }} |
||||
</template> |
||||
<NcButton |
||||
v-e="[`a:pagination:${entityName}:last-page`]" |
||||
class="last-page !border-0" |
||||
type="secondary" |
||||
size="xsmall" |
||||
:disabled="current === totalPages" |
||||
@click="goToLastPage" |
||||
> |
||||
<GeneralIcon icon="doubleRightArrow" class="nc-pagination-icon" /> |
||||
</NcButton> |
||||
</component> |
||||
</template> |
||||
<div v-if="showSizeChanger && !isMobileMode" class="text-gray-500"></div> |
||||
</div> |
||||
</template> |
||||
|
||||
<style lang="scss" scoped> |
||||
.nc-pagination-icon { |
||||
@apply w-4 h-4; |
||||
} |
||||
|
||||
:deep(.ant-dropdown-menu-title-content) { |
||||
@apply justify-center; |
||||
} |
||||
|
||||
:deep(.nc-button:not(:disabled)) { |
||||
.nc-pagination-icon { |
||||
@apply !text-gray-500; |
||||
} |
||||
} |
||||
</style> |
||||
|
||||
<style lang="scss"></style> |