mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-01 17:47:19 +00:00
203 lines
5.6 KiB
Vue
203 lines
5.6 KiB
Vue
<script lang="ts" setup>
|
|
import type { BaseType } from 'nocodb-sdk'
|
|
|
|
interface Props {
|
|
workspaceId?: string
|
|
value?: string | null | undefined
|
|
forceLayout?: 'vertical' | 'horizontal'
|
|
filterBase?: (base: BaseType) => boolean
|
|
forceLoadBases?: boolean
|
|
disableLabel?: boolean
|
|
autoSelect?: boolean
|
|
disabled?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
forceLoadBases: false,
|
|
disableLabel: false,
|
|
autoSelect: false,
|
|
disabled: false,
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
'update:value': [value: string | null | undefined]
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { $api } = useNuxtApp()
|
|
const { getBaseUrl } = useGlobal()
|
|
const workspaceStore = useWorkspace()
|
|
|
|
const modelValue = useVModel(props, 'value', emit)
|
|
|
|
const isOpenBaseSelectDropdown = ref(false)
|
|
|
|
const handleValueUpdate = (value: any) => {
|
|
modelValue.value = value
|
|
}
|
|
|
|
const baseList = computedAsync(async () => {
|
|
let basesList: BaseType[] = []
|
|
|
|
try {
|
|
if (isEeUI) {
|
|
const wsId = props.workspaceId || workspaceStore.activeWorkspace?.id
|
|
if (!wsId) return []
|
|
const { list } = await $api.workspaceBase.list(wsId, {
|
|
baseURL: getBaseUrl(wsId),
|
|
})
|
|
basesList = list || []
|
|
} else {
|
|
const { list } = await $api.base.list()
|
|
basesList = list || []
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load bases:', error)
|
|
basesList = []
|
|
}
|
|
|
|
if (props.filterBase) {
|
|
basesList = basesList.filter(props.filterBase)
|
|
}
|
|
|
|
return basesList.map((base) => {
|
|
const ncItemTooltip = ''
|
|
|
|
return {
|
|
label: base.title || base.id,
|
|
value: base.id,
|
|
ncItemDisabled: false,
|
|
ncItemTooltip,
|
|
...base,
|
|
}
|
|
})
|
|
}, [])
|
|
|
|
const baseListMap = computed(() => {
|
|
if (!baseList.value || baseList.value.length === 0) return new Map()
|
|
|
|
return new Map(baseList.value.map((base) => [base.value, base]))
|
|
})
|
|
|
|
const selectedBase = computed(() => {
|
|
if (!baseListMap.value || baseListMap.value.size === 0) return undefined
|
|
|
|
return baseListMap.value.get(modelValue.value) || undefined
|
|
})
|
|
|
|
watch(
|
|
baseList,
|
|
(newBaseList) => {
|
|
if (newBaseList && newBaseList.length > 0) {
|
|
const baseValueSet = new Set(newBaseList.map((base) => base.value))
|
|
|
|
// Check if current value exists in the new base list
|
|
if (modelValue.value && !baseValueSet.has(modelValue.value)) {
|
|
// Current value is not in the list, set null to clear it
|
|
modelValue.value = null
|
|
return
|
|
}
|
|
|
|
// Auto-select logic (only if autoSelect is enabled and no current value)
|
|
if (!modelValue.value && props.autoSelect) {
|
|
const firstBase = newBaseList[0]!
|
|
|
|
if (firstBase.ncItemDisabled) {
|
|
modelValue.value = newBaseList.find((base) => !base.ncItemDisabled)?.value || firstBase.value
|
|
} else {
|
|
modelValue.value = firstBase.value
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
|
|
defineExpose({
|
|
modelValue,
|
|
selectedBase,
|
|
isOpenBaseSelectDropdown,
|
|
baseList,
|
|
baseListMap,
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<a-form-item
|
|
name="baseId"
|
|
class="!mb-0 nc-base-selector"
|
|
:class="`nc-force-layout-${forceLayout}`"
|
|
:validate-status="selectedBase?.ncItemDisabled ? 'error' : ''"
|
|
:help="selectedBase?.ncItemDisabled ? [selectedBase.ncItemTooltip] : []"
|
|
@click.stop
|
|
@dblclick.stop
|
|
>
|
|
<template v-if="!disableLabel" #label>
|
|
<div>
|
|
<slot name="label">{{ t('objects.project') }}</slot>
|
|
</div>
|
|
</template>
|
|
<NcListDropdown v-model:is-open="isOpenBaseSelectDropdown" :disabled="disabled" :has-error="!!selectedBase?.ncItemDisabled">
|
|
<div class="flex-1 flex items-center gap-2 min-w-0">
|
|
<div v-if="selectedBase" class="min-w-5 flex items-center justify-center">
|
|
<GeneralProjectIcon
|
|
:color="parseProp(selectedBase.meta).iconColor"
|
|
:managed-app="{
|
|
managed_app_master: selectedBase.managed_app_master,
|
|
managed_app_id: selectedBase.managed_app_id,
|
|
}"
|
|
size="small"
|
|
/>
|
|
</div>
|
|
<NcTooltip hide-on-click class="flex-1 truncate" show-on-truncate-only>
|
|
<span
|
|
v-if="selectedBase"
|
|
:key="selectedBase?.value"
|
|
class="text-sm flex-1 truncate"
|
|
:class="{ 'text-nc-content-gray-muted': !selectedBase }"
|
|
>
|
|
{{ selectedBase?.label }}
|
|
</span>
|
|
<span v-else class="text-sm flex-1 truncate text-nc-content-gray-muted">-- Select base --</span>
|
|
|
|
<template #title>
|
|
{{ selectedBase?.label || 'Select base' }}
|
|
</template>
|
|
</NcTooltip>
|
|
|
|
<GeneralIcon
|
|
icon="ncChevronDown"
|
|
class="flex-none h-4 w-4 transition-transform opacity-70"
|
|
:class="{ 'transform rotate-180': isOpenBaseSelectDropdown }"
|
|
/>
|
|
</div>
|
|
<template #overlay="{ onEsc }">
|
|
<NcList
|
|
v-model:open="isOpenBaseSelectDropdown"
|
|
:value="modelValue || selectedBase?.value || ''"
|
|
:list="baseList"
|
|
variant="medium"
|
|
class="!w-auto"
|
|
wrapper-class-name="!h-auto"
|
|
@update:value="handleValueUpdate"
|
|
@escape="onEsc"
|
|
>
|
|
<template #listItemExtraLeft="{ option }">
|
|
<div class="min-w-5 flex items-center justify-center">
|
|
<GeneralProjectIcon
|
|
:color="parseProp(option.meta).iconColor"
|
|
:managed-app="{
|
|
managed_app_master: option.managed_app_master,
|
|
managed_app_id: option.managed_app_id,
|
|
}"
|
|
size="small"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</NcList>
|
|
</template>
|
|
</NcListDropdown>
|
|
</a-form-item>
|
|
</template>
|