diff --git a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java index 9392461ac8..2caae368b2 100644 --- a/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java +++ b/core/core-backend/src/main/java/io/dataease/share/manage/XpackShareManage.java @@ -3,6 +3,7 @@ package io.dataease.share.manage; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.dataease.api.system.vo.ShareBaseVO; import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest; import io.dataease.api.xpack.share.request.XpackShareProxyRequest; import io.dataease.api.xpack.share.request.XpackSharePwdValidator; @@ -21,6 +22,7 @@ import io.dataease.share.dao.auto.mapper.XpackShareMapper; import io.dataease.share.dao.ext.mapper.XpackShareExtMapper; import io.dataease.share.dao.ext.po.XpackSharePO; import io.dataease.share.util.LinkTokenUtil; +import io.dataease.system.manage.SysParameterManage; import io.dataease.utils.*; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; @@ -51,6 +53,9 @@ public class XpackShareManage { @Resource private ShareTicketManage shareTicketManage; + @Resource + private SysParameterManage sysParameterManage; + public XpackShare queryByResource(Long resourceId) { Long userId = AuthUtils.getUser().getUserId(); QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -190,7 +195,20 @@ public class XpackShareManage { return CommonBeanFactory.getBean(this.getClass()); } + private boolean peRequireValid(ShareBaseVO sharedBase, XpackShare share) { + if (ObjectUtils.isEmpty(sharedBase) || !sharedBase.isPeRequire()) return true; + Long exp = share.getExp(); + String pwd = share.getPwd(); + return StringUtils.isNotBlank(pwd) && ObjectUtils.isNotEmpty(exp); + } + public XpackShareProxyVO proxyInfo(XpackShareProxyRequest request) { + ShareBaseVO sharedBase = sysParameterManage.shareBase(); + if (ObjectUtils.isNotEmpty(sharedBase) && sharedBase.isDisable()) { + XpackShareProxyVO vo = new XpackShareProxyVO(); + vo.setShareDisable(true); + return vo; + } boolean inIframeError = request.isInIframe() && !LicenseUtil.licenseValid(); if (inIframeError) { return new XpackShareProxyVO(); @@ -200,13 +218,18 @@ public class XpackShareManage { XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper); if (ObjectUtils.isEmpty(xpackShare)) return null; + if (!peRequireValid(sharedBase, xpackShare)) { + XpackShareProxyVO vo = new XpackShareProxyVO(); + vo.setPeRequireValid(false); + return vo; + } String linkToken = LinkTokenUtil.generate(xpackShare.getCreator(), xpackShare.getResourceId(), xpackShare.getExp(), xpackShare.getPwd(), xpackShare.getOid()); HttpServletResponse response = ServletUtils.response(); response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken); Integer type = xpackShare.getType(); String typeText = (ObjectUtils.isNotEmpty(type) && type == 1) ? "dashboard" : "dataV"; TicketValidVO validVO = shareTicketManage.validateTicket(request.getTicket(), xpackShare); - return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError, validVO); + return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError, false, true, validVO); } private boolean linkExp(XpackShare xpackShare) { diff --git a/core/core-backend/src/main/java/io/dataease/system/manage/SysParameterManage.java b/core/core-backend/src/main/java/io/dataease/system/manage/SysParameterManage.java index 0b51783a38..60b7096b99 100644 --- a/core/core-backend/src/main/java/io/dataease/system/manage/SysParameterManage.java +++ b/core/core-backend/src/main/java/io/dataease/system/manage/SysParameterManage.java @@ -3,6 +3,7 @@ package io.dataease.system.manage; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import io.dataease.api.system.request.OnlineMapEditor; import io.dataease.api.system.vo.SettingItemVO; +import io.dataease.api.system.vo.ShareBaseVO; import io.dataease.datasource.server.DatasourceServer; import io.dataease.license.config.XpackInteract; import io.dataease.system.dao.auto.entity.CoreSysSetting; @@ -20,7 +21,6 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -import java.beans.PropertyDescriptor; import java.util.*; import java.util.stream.Collectors; @@ -159,4 +159,17 @@ public class SysParameterManage { private SysParameterManage proxy() { return CommonBeanFactory.getBean(SysParameterManage.class); } + + public ShareBaseVO shareBase() { + String disableText = singleVal("basic.shareDisable"); + String requireText = singleVal("basic.sharePeRequire"); + ShareBaseVO vo = new ShareBaseVO(); + if (StringUtils.isNotBlank(disableText) && StringUtils.equals("true", disableText)) { + vo.setDisable(true); + } + if (StringUtils.isNotBlank(requireText) && StringUtils.equals("true", requireText)) { + vo.setPeRequire(true); + } + return vo; + } } diff --git a/core/core-backend/src/main/java/io/dataease/system/server/SysParameterServer.java b/core/core-backend/src/main/java/io/dataease/system/server/SysParameterServer.java index 8bf4f0f075..5b97137b1c 100644 --- a/core/core-backend/src/main/java/io/dataease/system/server/SysParameterServer.java +++ b/core/core-backend/src/main/java/io/dataease/system/server/SysParameterServer.java @@ -3,6 +3,7 @@ package io.dataease.system.server; import io.dataease.api.system.SysParameterApi; import io.dataease.api.system.request.OnlineMapEditor; import io.dataease.api.system.vo.SettingItemVO; +import io.dataease.api.system.vo.ShareBaseVO; import io.dataease.constant.XpackSettingConstants; import io.dataease.system.dao.auto.entity.CoreSysSetting; import io.dataease.system.manage.SysParameterManage; @@ -69,4 +70,9 @@ public class SysParameterServer implements SysParameterApi { public Integer defaultLogin() { return sysParameterManage.defaultLogin(); } + + @Override + public ShareBaseVO shareBase() { + return sysParameterManage.shareBase(); + } } diff --git a/core/core-backend/src/main/resources/db/migration/V2.10.2__ddl.sql b/core/core-backend/src/main/resources/db/migration/V2.10.2__ddl.sql index 35e3855e0a..4099920a09 100644 --- a/core/core-backend/src/main/resources/db/migration/V2.10.2__ddl.sql +++ b/core/core-backend/src/main/resources/db/migration/V2.10.2__ddl.sql @@ -1,14 +1,24 @@ -INSERT INTO area (id, level, name, pid) VALUES ('156440315', 'district', '大鹏新区', '156440300'); +INSERT INTO area (id, level, name, pid) +VALUES ('156440315', 'district', '大鹏新区', '156440300'); DELETE ccv -FROM - core_chart_view ccv - INNER JOIN data_visualization_info dvi ON ccv.scene_id = dvi.id -WHERE - dvi.delete_flag =1; -delete from data_visualization_info dvi where dvi.delete_flag =1; -DELETE FROM area where pid = '156710100' OR id = '156710100'; +FROM core_chart_view ccv + INNER JOIN data_visualization_info dvi ON ccv.scene_id = dvi.id +WHERE dvi.delete_flag = 1; +delete +from data_visualization_info dvi +where dvi.delete_flag = 1; +DELETE +FROM area +where pid = '156710100' + OR id = '156710100'; ALTER TABLE `core_chart_view` - ADD COLUMN `custom_attr_mobile` longtext NULL COMMENT '图形属性_移动端' AFTER `custom_attr`, -ADD COLUMN `custom_style_mobile` longtext NULL COMMENT '组件样式_移动端' AFTER `custom_style`; \ No newline at end of file + ADD COLUMN `custom_attr_mobile` longtext NULL COMMENT '图形属性_移动端' AFTER `custom_attr`, + ADD COLUMN `custom_style_mobile` longtext NULL COMMENT '组件样式_移动端' AFTER `custom_style`; + + +INSERT INTO `core_sys_setting`(`id`, `pkey`, `pval`, `type`, `sort`) +VALUES (1048232869488627717, 'basic.shareDisable', 'false', 'text', 11); +INSERT INTO `core_sys_setting`(`id`, `pkey`, `pval`, `type`, `sort`) +VALUES (1048232869488627718, 'basic.sharePeRequire', 'false', 'text', 12); \ No newline at end of file diff --git a/core/core-frontend/src/api/visualization/dataVisualization.ts b/core/core-frontend/src/api/visualization/dataVisualization.ts index 777b5cd0e4..e9d48e7870 100644 --- a/core/core-frontend/src/api/visualization/dataVisualization.ts +++ b/core/core-frontend/src/api/visualization/dataVisualization.ts @@ -121,3 +121,10 @@ export const queryOuterParamsDsInfo = async dvId => { loading: false }) } + +export const queryShareBaseApi = () => { + return request.get({ + url: '/sysParameter/shareBase', + loading: false + }) +} diff --git a/core/core-frontend/src/components/handle-more/src/DvHandleMore.vue b/core/core-frontend/src/components/handle-more/src/DvHandleMore.vue index 9fc262c8be..17ee56a38a 100644 --- a/core/core-frontend/src/components/handle-more/src/DvHandleMore.vue +++ b/core/core-frontend/src/components/handle-more/src/DvHandleMore.vue @@ -3,8 +3,11 @@ import { Icon } from '@/components/icon-custom' import icon_more_outlined from '@/assets/svg/icon_more_outlined.svg' import { propTypes } from '@/utils/propTypes' import type { Placement } from 'element-plus-secondary' -import { ref, PropType } from 'vue' +import { ref, PropType, computed } from 'vue' import ShareHandler from '@/views/share/share/ShareHandler.vue' +import { useShareStoreWithOut } from '@/store/modules/share' +const shareStore = useShareStoreWithOut() + export interface Menu { svgName?: string label?: string @@ -34,6 +37,10 @@ const props = defineProps({ anyManage: propTypes.bool.def(false) }) +const shareDisable = computed(() => { + return shareStore.getShareDisable +}) + const shareComponent = ref(null) const menus = ref([ ...props.menuList.map(item => { @@ -52,6 +59,9 @@ const handleCommand = (command: string | number | object) => { emit('handleCommand', command) } const callBack = param => { + if (shareDisable.value) { + return + } if (props.node.leaf && props.node?.weight >= 7) { menus.value[0]['divided'] = true menus.value.splice(0, 0, param) @@ -89,6 +99,7 @@ const emit = defineEmits(['handleCommand']) { + return { + shareDisable: false, + sharePeRequire: false + } + }, + getters: { + getShareDisable(): boolean { + return this.shareDisable + }, + getSharePeRequire(): boolean { + return this.sharePeRequire + } + }, + actions: { + setData(data: ShareState) { + this.shareDisable = data.shareDisable + this.sharePeRequire = data.sharePeRequire + } + } +}) + +export const useShareStoreWithOut = () => { + return useShareStore(store) +} diff --git a/core/core-frontend/src/views/common/DeResourceTree.vue b/core/core-frontend/src/views/common/DeResourceTree.vue index 6410708517..724fc31cfd 100644 --- a/core/core-frontend/src/views/common/DeResourceTree.vue +++ b/core/core-frontend/src/views/common/DeResourceTree.vue @@ -17,7 +17,12 @@ import dvFolder from '@/assets/svg/dv-folder.svg' import icon_operationAnalysis_outlined from '@/assets/svg/icon_operation-analysis_outlined.svg' import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg' import { onMounted, reactive, ref, toRefs, watch, nextTick, computed } from 'vue' -import { copyResource, deleteLogic, ResourceOrFolder } from '@/api/visualization/dataVisualization' +import { + copyResource, + deleteLogic, + ResourceOrFolder, + queryShareBaseApi +} from '@/api/visualization/dataVisualization' import { ElIcon, ElMessage, ElMessageBox, ElScrollbar } from 'element-plus-secondary' import { Icon } from '@/components/icon-custom' import { useEmitt } from '@/hooks/web/useEmitt' @@ -30,6 +35,8 @@ import { useAppStoreWithOut } from '@/store/modules/app' import { storeToRefs } from 'pinia' import DvHandleMore from '@/components/handle-more/src/DvHandleMore.vue' import { interactiveStoreWithOut } from '@/store/modules/interactive' +import { useShareStoreWithOut } from '@/store/modules/share' +const shareStore = useShareStoreWithOut() const interactiveStore = interactiveStoreWithOut() import router from '@/router' import { useI18n } from '@/hooks/web/useI18n' @@ -503,9 +510,20 @@ const loadInit = () => { state.curSortType = historyTreeSort } } + +const loadShareBase = () => { + queryShareBaseApi().then(res => { + const param = { + shareDisable: res.data?.disable, + sharePeRequire: res.data?.peRequire + } + shareStore.setData(param) + }) +} onMounted(() => { loadInit() getTree() + loadShareBase() }) defineExpose({ diff --git a/core/core-frontend/src/views/data-visualization/PreviewHead.vue b/core/core-frontend/src/views/data-visualization/PreviewHead.vue index 168ea9ee0c..b39ba9b56a 100644 --- a/core/core-frontend/src/views/data-visualization/PreviewHead.vue +++ b/core/core-frontend/src/views/data-visualization/PreviewHead.vue @@ -17,6 +17,9 @@ import { ref, watch, computed } from 'vue' import ShareVisualHead from '@/views/share/share/ShareVisualHead.vue' import { XpackComponent } from '@/components/plugin' import { useEmitt } from '@/hooks/web/useEmitt' +import { useShareStoreWithOut } from '@/store/modules/share' +const shareStore = useShareStoreWithOut() + const dvMainStore = dvMainStoreWithOut() const appStore = useAppStoreWithOut() const { dvInfo } = storeToRefs(dvMainStore) @@ -38,6 +41,7 @@ const preview = () => { } const isDataEaseBi = computed(() => appStore.getIsDataEaseBi) const isIframe = computed(() => appStore.getIsIframe) +const shareDisable = computed(() => shareStore.getShareDisable) const reload = () => { emit('reload', dvInfo.value.id) @@ -156,6 +160,7 @@ const initOpenHandler = newWindow => { 预览 +import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue' +import { propTypes } from '@/utils/propTypes' +const props = defineProps({ + msg: propTypes.string.def('') +}) + + diff --git a/core/core-frontend/src/views/share/link/ShareProxy.ts b/core/core-frontend/src/views/share/link/ShareProxy.ts index 66cf7af9e6..60b779871c 100644 --- a/core/core-frontend/src/views/share/link/ShareProxy.ts +++ b/core/core-frontend/src/views/share/link/ShareProxy.ts @@ -15,6 +15,8 @@ export interface ProxyInfo { pwdValid?: boolean type: string inIframeError: boolean + shareDisable: boolean + peRequireValid: boolean ticketValidVO: TicketValidVO } class ShareProxy { diff --git a/core/core-frontend/src/views/share/link/index.vue b/core/core-frontend/src/views/share/link/index.vue index b7cd0f34d8..70bd935c0e 100644 --- a/core/core-frontend/src/views/share/link/index.vue +++ b/core/core-frontend/src/views/share/link/index.vue @@ -3,7 +3,13 @@ class="link-container" v-loading="loading || requestStore.loadingMap[permissionStore.currentPath]" > - + + @@ -30,10 +36,13 @@ import LinkError from './error.vue' import PwdTips from './pwd.vue' import IframeError from './IframeError.vue' import TicketError from './TicketError.vue' +import ErrorTemplate from './ErrorTemplate.vue' const requestStore = useRequestStoreWithOut() const permissionStore = usePermissionStoreWithOut() const pcanvas = ref(null) const iframeError = ref(true) +const disableError = ref(true) +const peRequireError = ref(true) const linkExist = ref(false) const loading = ref(true) const linkExp = ref(false) @@ -47,12 +56,24 @@ const state = reactive({ }) onMounted(async () => { const proxyInfo = (await shareProxy.loadProxy()) as ProxyInfo + if (proxyInfo?.shareDisable) { + loading.value = false + disableError.value = true + return + } + disableError.value = false if (proxyInfo?.inIframeError) { loading.value = false iframeError.value = true return } iframeError.value = false + if (proxyInfo && !proxyInfo.peRequireValid) { + loading.value = false + peRequireError.value = true + return + } + peRequireError.value = false if (!proxyInfo?.resourceId) { loading.value = false return diff --git a/core/core-frontend/src/views/share/share/ShareHandler.vue b/core/core-frontend/src/views/share/share/ShareHandler.vue index 25b9cce3d2..b1c8b0d92b 100644 --- a/core/core-frontend/src/views/share/share/ShareHandler.vue +++ b/core/core-frontend/src/views/share/share/ShareHandler.vue @@ -56,11 +56,18 @@
+ > +
+ {{ t('visualization.over_time') }} + + * + +
+
+ > +
+ {{ t('visualization.passwd_protect') }} + + * + +
+
(null) const dialogVisible = ref(false) const overTimeEnable = ref(false) @@ -189,6 +207,8 @@ const shareTips = computed( () => `开启后,用户可以通过该链接访问${props.resourceType === 'dashboard' ? '仪表板' : '数据大屏'}` ) +const shareDisable = computed(() => shareStore.getShareDisable) +const sharePeRequire = computed(() => shareStore.getSharePeRequire) const editUuid = () => { linkCustom.value = true nextTick(() => { @@ -274,10 +294,10 @@ const closeLoading = () => { const share = () => { dialogVisible.value = true - loadShareInfo() + loadShareInfo(validatePeRequire) } -const loadShareInfo = () => { +const loadShareInfo = cb => { showLoading() const resourceId = props.resourceId const url = `/share/detail/${resourceId}` @@ -289,6 +309,7 @@ const loadShareInfo = () => { }) .finally(() => { closeLoading() + cb && cb() }) } @@ -305,7 +326,7 @@ const enableSwitcher = () => { const resourceId = props.resourceId const url = `/share/switcher/${resourceId}` request.post({ url }).then(() => { - loadShareInfo() + loadShareInfo(null) }) } @@ -335,6 +356,7 @@ const expEnableSwitcher = val => { exp = now.getTime() state.detailInfo.exp = exp } + validateExpRequire() expChangeHandler(exp) } @@ -348,7 +370,7 @@ const expChangeHandler = exp => { const url = '/share/editExp' const data = { resourceId, exp } request.post({ url, data }).then(() => { - loadShareInfo() + loadShareInfo(null) }) } const beforeClose = async done => { @@ -357,6 +379,12 @@ const beforeClose = async done => { done() return } + if (sharePeRequire.value) { + const peRequireValid = validatePeRequire() + if (!peRequireValid) { + return + } + } const pwdValid = validatePwdFormat() const uuidValid = await validateUuid() if (pwdValid && uuidValid) { @@ -364,6 +392,32 @@ const beforeClose = async done => { done() } } +const validatePeRequire = () => { + if (shareEnable.value && sharePeRequire.value) { + const expRequireValid = validateExpRequire() + const pwdRequireValid = validatePwdRequire() + return expRequireValid && pwdRequireValid + } + return true +} + +const validateExpRequire = () => { + if (!sharePeRequire.value || overTimeEnable.value) { + showCheckboxError(null, expCheckbox) + return true + } + showCheckboxError('必填', expCheckbox) + return false +} + +const validatePwdRequire = () => { + if (!sharePeRequire.value || passwdEnable.value) { + showCheckboxError(null, pwdCheckbox) + return true + } + showCheckboxError('必填', pwdCheckbox) + return false +} const validatePwdFormat = () => { if (!shareEnable.value || state.detailInfo.autoPwd) { showPageError(null, pwdRef) @@ -383,6 +437,34 @@ const validatePwdFormat = () => { resetPwdHandler(val, false) return true } +const showCheckboxError = (msg, target, className?: string) => { + if (!target.value) { + return + } + className = className || 'checkbox-span-require' + const fullClassName = `.${className}` + const e = target.value.$el + if (!msg) { + e.style = null + e.children[0].children[1].style.borderColor = null + const child = e.children[1].children[0].querySelector(fullClassName) + if (child) { + e.children[1].children[0].removeChild(child) + } + } else { + e.style.color = 'red' + e.children[0].children[1].style.borderColor = 'red' + const child = e.children[1].children[0].querySelector(fullClassName) + if (!child) { + const errorDom = document.createElement('span') + errorDom.className = className + errorDom.innerText = msg + e.children[1].children[0].appendChild(errorDom) + } else { + child.innerText = msg + } + } +} const showPageError = (msg, target, className?: string) => { className = className || 'link-pwd-error-msg' const fullClassName = `.${className}` @@ -433,6 +515,7 @@ const pwdEnableSwitcher = val => { if (val) { pwd = getUuid() } + validatePwdRequire() resetPwdHandler(pwd, true) } const resetPwd = () => { @@ -444,7 +527,7 @@ const resetPwdHandler = (pwd?: string, autoPwd?: boolean) => { const url = '/share/editPwd' const data = { resourceId, pwd, autoPwd } request.post({ url, data }).then(() => { - loadShareInfo() + loadShareInfo(null) }) } @@ -642,4 +725,20 @@ onMounted(() => { display: none; } } +:deep(.checkbox-span) { + display: flex; + align-items: center; + .pe-require { + color: red; + font-size: 10px; + line-height: 32px; + margin: 0 4px; + } + .checkbox-span-require { + font-size: 10px; + } + .pe-tips-hidden { + display: none; + } +} diff --git a/core/core-frontend/src/views/share/share/ShareVisualHead.vue b/core/core-frontend/src/views/share/share/ShareVisualHead.vue index 75c94b6d03..0c09284003 100644 --- a/core/core-frontend/src/views/share/share/ShareVisualHead.vue +++ b/core/core-frontend/src/views/share/share/ShareVisualHead.vue @@ -23,7 +23,11 @@ {{ t('visualization.share') }} -