diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7abba6f42bde..7316b9586ba7 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,7 +6,7 @@ diff --git a/frontend/src/components/fu/FuDropdownItem.vue b/frontend/src/components/fu/FuDropdownItem.vue new file mode 100644 index 000000000000..a7c7cd7a882d --- /dev/null +++ b/frontend/src/components/fu/FuDropdownItem.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/components/fu/FuInputRwSwitch.vue b/frontend/src/components/fu/FuInputRwSwitch.vue index d33226355ccf..fcdf8e675f08 100644 --- a/frontend/src/components/fu/FuInputRwSwitch.vue +++ b/frontend/src/components/fu/FuInputRwSwitch.vue @@ -42,6 +42,11 @@ const emit = defineEmits(['update:modelValue', 'input', 'blur', 'enter']); const inputRef = ref(); const isWrite = ref(false); +const permissionDisabled = ref(false); + +const effectiveWriteTrigger = computed(() => { + return permissionDisabled.value ? 'disabled' : props.writeTrigger; +}); const displayValue = computed(() => { return props.modelValue === '' || props.modelValue === undefined || props.modelValue === null @@ -61,13 +66,13 @@ const closeWrite = () => { }; const handleReadClick = () => { - if (props.writeTrigger === 'onClick') { + if (effectiveWriteTrigger.value === 'onClick') { openWrite(); } }; const handleReadDblClick = () => { - if (props.writeTrigger === 'onDblclick') { + if (effectiveWriteTrigger.value === 'onDblclick') { openWrite(); } }; @@ -89,4 +94,15 @@ const handleEnter = (event: KeyboardEvent) => { emit('enter', event); closeWrite(); }; + +defineExpose({ + setPermissionDisabled: (disabled: boolean) => { + permissionDisabled.value = disabled; + if (disabled) { + isWrite.value = false; + } + }, + write: openWrite, + read: closeWrite, +}); diff --git a/frontend/src/components/fu/FuReadWriteSwitch.vue b/frontend/src/components/fu/FuReadWriteSwitch.vue index e10293514e86..b03f90dbe70c 100644 --- a/frontend/src/components/fu/FuReadWriteSwitch.vue +++ b/frontend/src/components/fu/FuReadWriteSwitch.vue @@ -39,6 +39,11 @@ const props = defineProps({ const emit = defineEmits(['update:modelValue', 'change']); const isWrite = ref(false); +const permissionDisabled = ref(false); + +const effectiveWriteTrigger = computed(() => { + return permissionDisabled.value ? 'disabled' : props.writeTrigger; +}); const displayValue = computed(() => { const value = props.modelValue !== '' && props.modelValue !== undefined ? props.modelValue : props.data; @@ -58,18 +63,24 @@ const closeWrite = (value?: any) => { }; const handleReadClick = () => { - if (props.writeTrigger === 'onClick') { + if (effectiveWriteTrigger.value === 'onClick') { openWrite(); } }; const handleReadDblClick = () => { - if (props.writeTrigger === 'onDblclick') { + if (effectiveWriteTrigger.value === 'onDblclick') { openWrite(); } }; defineExpose({ + setPermissionDisabled: (disabled: boolean) => { + permissionDisabled.value = disabled; + if (disabled) { + isWrite.value = false; + } + }, write: openWrite, read: closeWrite, }); diff --git a/frontend/src/components/fu/FuSelectRwSwitch.vue b/frontend/src/components/fu/FuSelectRwSwitch.vue index 8ac5a94e64b0..b7a280df73cb 100644 --- a/frontend/src/components/fu/FuSelectRwSwitch.vue +++ b/frontend/src/components/fu/FuSelectRwSwitch.vue @@ -50,6 +50,20 @@ const emit = defineEmits(['update:modelValue', 'input', 'blur', 'change']); const selectRef = ref(); const isWrite = ref(false); +const permissionDisabled = ref(false); + +defineExpose({ + setPermissionDisabled: (disabled: boolean) => { + permissionDisabled.value = disabled; + if (disabled) { + isWrite.value = false; + } + }, +}); + +const effectiveWriteTrigger = computed(() => { + return permissionDisabled.value ? 'disabled' : props.writeTrigger; +}); const displayValue = computed(() => { return props.modelValue === '' || props.modelValue === undefined || props.modelValue === null @@ -70,13 +84,13 @@ const closeWrite = () => { }; const handleReadClick = () => { - if (props.writeTrigger === 'onClick') { + if (effectiveWriteTrigger.value === 'onClick') { openWrite(); } }; const handleReadDblClick = () => { - if (props.writeTrigger === 'onDblclick') { + if (effectiveWriteTrigger.value === 'onDblclick') { openWrite(); } }; diff --git a/frontend/src/components/fu/FuTableOperations.vue b/frontend/src/components/fu/FuTableOperations.vue index 55c9e598edcd..0dbe06f8a883 100644 --- a/frontend/src/components/fu/FuTableOperations.vue +++ b/frontend/src/components/fu/FuTableOperations.vue @@ -59,6 +59,7 @@ import { computed, type PropType } from 'vue'; import { useI18n } from 'vue-i18n'; import { resolveMaybeFn, type FuTableOperationButton } from './shared'; +import { hasManagePermissionAccess } from '@/utils/permission'; defineOptions({ name: 'FuTableOperations' }); @@ -204,7 +205,10 @@ const getMoreButtons = (row: any) => { }; const isButtonDisabled = (button: FuTableOperationButton, row: any) => { - return Boolean(resolveMaybeFn(button.disabled ?? false, row)); + const permissionDisabled = + button.permission !== undefined && + !hasManagePermissionAccess(button.permission === true ? undefined : button.permission); + return permissionDisabled || Boolean(resolveMaybeFn(button.disabled ?? false, row)); }; const handleButtonClick = (button: FuTableOperationButton, row: any) => { diff --git a/frontend/src/components/fu/index.ts b/frontend/src/components/fu/index.ts index e2ab9f74c248..0a1ebe0cc974 100644 --- a/frontend/src/components/fu/index.ts +++ b/frontend/src/components/fu/index.ts @@ -1,6 +1,7 @@ import { type App } from 'vue'; import FuInputRwSwitch from './FuInputRwSwitch.vue'; +import FuDropdownItem from './FuDropdownItem.vue'; import FuReadWriteSwitch from './FuReadWriteSwitch.vue'; import FuSelectRwSwitch from './FuSelectRwSwitch.vue'; import FuStep from './FuStep'; @@ -14,6 +15,7 @@ const components = [ FuTable, FuTableOperations, FuTablePagination, + FuDropdownItem, FuInputRwSwitch, FuReadWriteSwitch, FuSelectRwSwitch, diff --git a/frontend/src/components/fu/shared.ts b/frontend/src/components/fu/shared.ts index e612a3065ad1..c063d3ec014f 100644 --- a/frontend/src/components/fu/shared.ts +++ b/frontend/src/components/fu/shared.ts @@ -1,4 +1,5 @@ import { Comment, Fragment, Text, type VNode } from 'vue'; +import type { PermissionBindingValue } from '@/utils/permission'; export interface FuTableColumnConfig { key: string; @@ -12,6 +13,7 @@ export interface FuTableOperationButton { label?: string | number; click?: (row: any) => void; disabled?: boolean | ((row: any) => boolean); + permission?: true | PermissionBindingValue; show?: boolean | ((row: any) => boolean); type?: string; icon?: any; diff --git a/frontend/src/components/license-import/index.vue b/frontend/src/components/license-import/index.vue index ec9629a05893..7fefabd04b97 100644 --- a/frontend/src/components/license-import/index.vue +++ b/frontend/src/components/license-import/index.vue @@ -61,10 +61,10 @@ import { ref } from 'vue'; import { MsgSuccess } from '@/utils/message'; import { uploadLicense, uploadEnterpriseLicense } from '@/api/modules/setting'; import DockerProxy from '@/components/docker-proxy/index.vue'; -import { GlobalStore } from '@/store'; import { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile, genFileId } from 'element-plus'; import { getXpackSettingForTheme, loadMasterProductProFromDB, loadProductProFromDB } from '@/utils/xpack'; -const globalStore = GlobalStore(); +import { useGlobalStore } from '@/composables/useGlobalStore'; +const { isIntl, isEnterprise, currentNode, isProductPro, isMasterProductPro, isEnterpriseLicensed } = useGlobalStore(); const em = defineEmits(['search']); @@ -112,7 +112,7 @@ const handleExceed: UploadProps['onExceed'] = (files) => { }; const toLxware = () => { - if (!globalStore.isIntl) { + if (!isIntl.value) { window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer'); } else { window.open('https://1panel.pro/pricing' + '', '_blank', 'noopener,noreferrer'); @@ -126,7 +126,7 @@ const submit = async () => { const file = uploaderFiles.value[0]; const formData = new FormData(); formData.append('file', file.raw); - if (globalStore.isEnterprise) { + if (isEnterprise.value) { loading.value = true; await uploadEnterpriseLicense(formData) .then(async () => { @@ -143,7 +143,7 @@ const submit = async () => { formData.append('oldLicenseName', oldLicense.value); } if (!isImport.value) { - formData.append('currentNode', globalStore.currentNode); + formData.append('currentNode', currentNode.value); formData.append('withDockerRestart', withDockerRestart.value); } formData.append('isForce', isForce.value); @@ -166,10 +166,10 @@ const handleAfterSubmit = () => { open.value = false; MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); if (!isImport.value) { - if (!globalStore.isEnterprise) globalStore.isProductPro = true; - globalStore.isMasterProductPro = true; + if (!isEnterprise.value) isProductPro.value = true; + isMasterProductPro.value = true; } else { - globalStore.isEnterpriseLicensed = true; + isEnterpriseLicensed.value = true; } if (!withoutReload.value) { loadMasterProductProFromDB(); diff --git a/frontend/src/components/log/compose/index.vue b/frontend/src/components/log/compose/index.vue index 0af49a0eb2a3..90ee7cf1845e 100644 --- a/frontend/src/components/log/compose/index.vue +++ b/frontend/src/components/log/compose/index.vue @@ -3,7 +3,7 @@ v-model="open" :header="resource" @close="handleClose" - :size="globalStore.isFullScreen ? 'full' : '60%'" + :size="isFullScreen ? 'full' : '60%'" :resource="container" > diff --git a/frontend/src/views/log/system/index.vue b/frontend/src/views/log/system/index.vue index afcec6f81e26..41face13ffe1 100644 --- a/frontend/src/views/log/system/index.vue +++ b/frontend/src/views/log/system/index.vue @@ -14,12 +14,7 @@ {{ $t('commons.button.watch') }} - + @@ -44,8 +39,8 @@ import LogFile from '@/components/log/file/index.vue'; import LogRouter from '@/views/log/router/index.vue'; import { nextTick, onMounted, reactive, ref } from 'vue'; import { getSystemFiles } from '@/api/modules/log'; -import { GlobalStore } from '@/store'; -const globalStore = GlobalStore(); +import { useGlobalStore } from '@/composables/useGlobalStore'; +const { currentNode } = useGlobalStore(); const loading = ref(); const isWatch = ref(); diff --git a/frontend/src/views/log/website/index.vue b/frontend/src/views/log/website/index.vue index d50afa2473f2..d992ef9790da 100644 --- a/frontend/src/views/log/website/index.vue +++ b/frontend/src/views/log/website/index.vue @@ -34,10 +34,10 @@ {{ $t('commons.button.watch') }} - + {{ $t('commons.button.download') }} - + {{ $t('logs.deleteLogs') }} diff --git a/frontend/src/views/login/components/login-form.vue b/frontend/src/views/login/components/login-form.vue index 5c189d30d48e..4f5cf935320c 100644 --- a/frontend/src/views/login/components/login-form.vue +++ b/frontend/src/views/login/components/login-form.vue @@ -52,10 +52,10 @@