Merge branch 'dev-v2' into pr@dev-v2@perf_free_resource

This commit is contained in:
fit2cloud-chenyw 2024-11-22 10:39:13 +08:00
commit 3fb3b97d9e
47 changed files with 1244 additions and 80 deletions

View File

@ -113,8 +113,9 @@ public class DatasetDataManage {
sql = provider.transSqlDialect(sql, datasourceRequest.getDsList());
} else {
// parser sql params and replace default value
String originSql = provider.replaceComment(new String(Base64.getDecoder().decode(tableInfoDTO.getSql())));
originSql = SqlparserUtils.handleVariableDefaultValue(originSql, datasetTableDTO.getSqlVariableDetails(), false, false, null, false, datasourceRequest.getDsList(), pluginManage);
String s = new String(Base64.getDecoder().decode(tableInfoDTO.getSql()));
String originSql = SqlparserUtils.handleVariableDefaultValue(s, datasetTableDTO.getSqlVariableDetails(), false, false, null, false, datasourceRequest.getDsList(), pluginManage);
originSql = provider.replaceComment(originSql);
// add sql table schema
sql = SQLUtils.buildOriginPreviewSql(SqlPlaceholderConstants.TABLE_PLACEHOLDER, 0, 0);
@ -403,8 +404,9 @@ public class DatasetDataManage {
// parser sql params and replace default value
String originSql = provider.replaceComment(new String(Base64.getDecoder().decode(dto.getSql())));
originSql = SqlparserUtils.handleVariableDefaultValue(datasetSQLManage.subPrefixSuffixChar(originSql), dto.getSqlVariableDetails(), true, true, null, false, dsMap, pluginManage);
String s = new String(Base64.getDecoder().decode(dto.getSql()));
String originSql = SqlparserUtils.handleVariableDefaultValue(datasetSQLManage.subPrefixSuffixChar(s), dto.getSqlVariableDetails(), true, true, null, false, dsMap, pluginManage);
originSql = provider.replaceComment(originSql);
// sql 作为临时表外层加上limit
String sql;

View File

@ -451,8 +451,9 @@ public class DatasetSQLManage {
} else if (StringUtils.equalsIgnoreCase(currentDs.getType(), DatasetTableTypeConstants.DATASET_TABLE_SQL)) {
Provider provider = ProviderFactory.getProvider(dsMap.entrySet().iterator().next().getValue().getType());
// parser sql params and replace default value
String sql = provider.replaceComment(new String(Base64.getDecoder().decode(infoDTO.getSql())));
sql = SqlparserUtils.handleVariableDefaultValue(sql, currentDs.getSqlVariableDetails(), false, isFromDataSet, parameters, isCross, dsMap, pluginManage);
String s = new String(Base64.getDecoder().decode(infoDTO.getSql()));
String sql = SqlparserUtils.handleVariableDefaultValue(s, currentDs.getSqlVariableDetails(), false, isFromDataSet, parameters, isCross, dsMap, pluginManage);
sql = provider.replaceComment(sql);
// add table schema
if (isCross) {
sql = SqlUtils.addSchema(sql, tableSchema);

View File

@ -0,0 +1,16 @@
package io.dataease.msgCenter;
import io.dataease.api.msgCenter.MsgCenterApi;
import io.dataease.license.config.XpackInteract;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/msg-center")
public class MsgCenterServer implements MsgCenterApi {
@Override
@XpackInteract(value = "msgCenterServer", replace = true)
public long count() {
return 0;
}
}

View File

@ -43,6 +43,7 @@
<result column="publicJumpId" jdbcType="VARCHAR" property="publicJumpId"/>
<collection property="targetViewInfoList"
ofType="io.dataease.api.visualization.vo.VisualizationLinkJumpTargetViewInfoVO">
<result column="target_id" jdbcType="BIGINT" property="targetId"/>
<result column="target_view_id" jdbcType="BIGINT" property="targetViewId"/>
<result column="target_field_id" jdbcType="BIGINT" property="targetFieldId"/>
<result column="source_field_active_id" jdbcType="VARCHAR" property="sourceFieldActiveId"/>
@ -87,6 +88,7 @@
xpack_share.uuid AS publicJumpId,
ifnull( visualization_link_jump_info.checked, 0 ) AS checked,
ifnull( visualization_link_jump_info.attach_params, 0 ) AS attach_params,
visualization_link_jump_target_view_info.target_id,
visualization_link_jump_target_view_info.target_view_id,
visualization_link_jump_target_view_info.target_field_id,
visualization_link_jump_target_view_info.target_type,
@ -102,7 +104,7 @@
LEFT JOIN visualization_link_jump_target_view_info ON visualization_link_jump_info.id = visualization_link_jump_target_view_info.link_jump_info_id
LEFT JOIN xpack_share ON xpack_share.creator = #{uid}
AND visualization_link_jump_info.target_dv_id = xpack_share.resource_id
left join visualization_outer_params_info on visualization_outer_params_info.params_id = visualization_link_jump_target_view_info.target_view_id
left join visualization_outer_params_info on visualization_outer_params_info.params_info_id = visualization_link_jump_target_view_info.target_view_id
WHERE
core_chart_view.id = #{source_view_id}
AND core_chart_view.type != 'VQuery'
@ -162,7 +164,7 @@
<select id="queryOutParamsTargetWithDvId" resultType="io.dataease.api.visualization.vo.VisualizationOutParamsJumpVO">
SELECT
vopi.params_id as id,
vopi.params_info_id as id,
vopi.param_name as name,
vopi.param_name as title,
'outerParams' as type

View File

@ -0,0 +1,3 @@
import request from '@/config/axios'
export const msgCountApi = () => request.post({ url: '/msg-center/count', data: {} })

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="44px" height="44px" viewBox="0 0 44 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>模糊修复</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="模糊修复" fill="#BBBFC3" fill-rule="nonzero">
<path d="M4.94539535,3.06976745 C3.90986044,3.06976745 3.06976745,3.90883718 3.06976745,4.94539535 L3.06976745,39.0546046 C3.06976745,40.0901396 3.90883718,40.9302326 4.94539535,40.9302326 L39.0546046,40.9302326 C40.0901396,40.9302326 40.9302326,40.0911628 40.9302326,39.0546046 L40.9302326,4.94539535 C40.9302326,3.90986044 40.0911628,3.06976745 39.0546046,3.06976745 L4.94539535,3.06976745 Z M0,4.94539535 C0,2.21412892 2.21412892,0 4.94539535,0 L39.0546046,0 C41.7858711,0 44,2.21412892 44,4.94539535 L44,39.0546046 C44,41.7858711 41.7858711,44 39.0546046,44 L4.94539535,44 C2.21412892,44 0,41.7858711 0,39.0546046 L0,4.94539535 L0,4.94539535 Z" id="形状"></path>
<path d="M10,11.4868562 C10,10.6656882 10.6656354,10 11.4867383,10 L33.5132617,10 C34.3343646,10 35,10.6656882 35,11.4868562 L35,33.5131438 C35,34.3343118 34.3343646,35 33.5132617,35 L11.4867383,35 C10.6656354,35 10,34.3343118 10,33.5131438 L10,11.4868562 L10,11.4868562 Z M12.9734765,12.9737123 L12.9734765,32.0262877 L32.0265234,32.0262877 L32.0265234,12.9737123 L12.9734765,12.9737123 Z" id="形状"></path>
<path d="M34.5646998,20.3474464 C35.1451,20.9280211 35.1451,21.8691328 34.5646998,22.4497075 L22.4506634,34.5647187 C22.074953,34.9402515 21.5274377,35.0868193 21.0143611,34.9492114 C20.5012846,34.8116035 20.1005953,34.4107259 19.9632291,33.8975852 C19.8258629,33.3844446 19.972689,32.8369991 20.3483993,32.4614663 L32.4624357,20.3474464 C33.0430113,19.7670469 33.9841243,19.7670469 34.5646998,20.3474464 L34.5646998,20.3474464 Z M24.6540264,10.4357952 C25.2344267,11.01637 25.2344267,11.9574817 24.6540264,12.5380565 L12.5380077,24.6530676 C11.9572098,25.233591 11.0157722,25.2333691 10.435248,24.652572 C9.85472385,24.0717749 9.85494572,23.1303387 10.4357436,22.5498153 L22.54978,10.4357952 C22.828627,10.1567699 23.206932,10 23.6014076,10 C23.9958833,10 24.3741883,10.1567699 24.6530352,10.4357952 L24.6540264,10.4357952 Z M34.5646998,10.4357952 C35.1451001,11.01637 35.1451001,11.9574817 34.5646998,12.5380565 L12.5389988,34.5647187 C11.9582009,35.1452421 11.0167634,35.1450202 10.4362392,34.5642231 C9.85571504,33.983426 9.8559369,33.0419897 10.4367348,32.4614663 L32.4624357,10.4357952 C33.0430113,9.85539577 33.9841243,9.85539577 34.5646998,10.4357952 Z" id="形状"></path>
<path d="M11.4859238,18.8111098 C11.8802783,18.8111098 12.258481,18.9677602 12.5373316,19.2465999 C12.8161823,19.5254395 12.972839,19.9036272 12.972839,20.297966 L12.972839,32.0262877 L24.7036082,32.0262877 C25.2348319,32.0262876 25.7257028,32.3096808 25.9913146,32.7697157 C26.2569264,33.2297506 26.2569264,33.796537 25.9913146,34.2565719 C25.7257028,34.7166069 25.2348319,35 24.7036082,35 L11.4869151,35 C10.6657146,35 10,34.3343118 10,33.5131438 L10,20.297966 C10,19.9036272 10.1566567,19.5254395 10.4355073,19.2465999 C10.714358,18.9677602 11.0925607,18.8111098 11.4869151,18.8111098 L11.4859238,18.8111098 Z M18.8084853,11.4868562 C18.8084853,11.0925174 18.965142,10.7143297 19.2439927,10.4354901 C19.5228434,10.1566504 19.9010461,10 20.2954005,10 L33.5130849,10 C34.3342854,10 35,10.6656882 35,11.4868562 L35,24.7030253 C35,25.5241932 34.3342854,26.1898814 33.5130849,26.1898814 C32.6918843,26.1898814 32.0261697,25.5241932 32.0261697,24.7030253 L32.0261697,12.9737123 L20.2954005,12.9737123 C19.9010461,12.9737124 19.5228434,12.8170619 19.2439927,12.5382223 C18.965142,12.2593826 18.8084853,11.881195 18.8084853,11.4868562 L18.8084853,11.4868562 Z" id="形状"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -224,7 +224,15 @@ const saveResource = () => {
ElMessage.success(t('common.save_success'))
let url = window.location.href
url = url.replace(/\?opt=create/, `?resourceId=${dvInfo.value.id}`)
window.history.replaceState(null, '', url)
if (!embeddedStore.baseUrl) {
window.history.replaceState(
{
path: url
},
'',
url
)
}
if (appData.value) {
initCanvasData(dvInfo.value.id, 'dashboard', () => {

View File

@ -23,6 +23,21 @@
</color-button>
</el-space>
</el-form-item>
<el-form-item
v-if="dvInfo.type === 'dashboard'"
class="form-item"
:class="'form-item-' + themes"
label="仪表板字体选择"
>
<el-select :effect="themes" v-model="canvasStyleData.fontFamily" @change="fontFamilyChange()">
<el-option
v-for="option in fontFamily"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="dvInfo.type === 'dashboard'"
class="form-item"
@ -225,6 +240,7 @@ import {
LIGHT_THEME_DASHBOARD_BACKGROUND
} from '@/utils/canvasStyle'
import {
CHART_FONT_FAMILY,
DEFAULT_COLOR_CASE_DARK,
DEFAULT_COLOR_CASE_LIGHT,
DEFAULT_TAB_COLOR_CASE_DARK,
@ -245,8 +261,11 @@ import {
COMMON_COMPONENT_BACKGROUND_DARK,
COMMON_COMPONENT_BACKGROUND_LIGHT
} from '@/custom-component/component-list'
import { ElFormItem, ElIcon, ElMessage, ElSpace } from 'element-plus-secondary'
import { ElFormItem, ElIcon, ElSpace } from 'element-plus-secondary'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
const appearanceStore = useAppearanceStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const props = defineProps({
themes: {
@ -254,6 +273,13 @@ const props = defineProps({
default: 'light'
}
})
const fontFamily = CHART_FONT_FAMILY.concat(
appearanceStore.fontList.map(ele => ({
name: ele.name,
value: ele.name
}))
)
const toolTip = computed(() => {
return props.themes === 'dark' ? 'ndark' : 'dark'
})
@ -271,6 +297,9 @@ const onRefreshChange = val => {
}
themeChange()
}
const fontFamilyChange = () => {
appearanceStore.setCurrentFont(canvasStyleData.fontFamily)
}
const themeChange = (modifyName?) => {
if (modifyName === 'themeColor') {

View File

@ -166,7 +166,15 @@ const saveResource = () => {
ElMessage.success('保存成功')
let url = window.location.href
url = url.replace(/\?opt=create/, `?dvId=${dvInfo.value.id}`)
window.history.replaceState(null, '', url)
if (!embeddedStore.baseUrl) {
window.history.replaceState(
{
path: url
},
'',
url
)
}
if (appData.value) {
initCanvasData(dvInfo.value.id, 'dataV', () => {
useEmitt().emitter.emit('refresh-dataset-selector')

View File

@ -196,6 +196,8 @@ const onMouseEnter = () => {
const componentBackgroundStyle = computed(() => {
if (config.value.commonBackground) {
const {
backdropFilterEnable,
backdropFilter,
backgroundColorSelect,
backgroundColor,
backgroundImageEnable,
@ -243,6 +245,9 @@ const componentBackgroundStyle = computed(() => {
if (config.value.component !== 'UserView') {
style['overflow'] = 'hidden'
}
if (backdropFilterEnable) {
style['backdrop-filter'] = 'blur(' + backdropFilter + 'px)'
}
return style
}
return {}

View File

@ -881,6 +881,8 @@ const padding3D = computed(() => {
const componentBackgroundStyle = computed(() => {
if (element.value.commonBackground && element.value.component !== 'GroupArea') {
const {
backdropFilterEnable,
backdropFilter,
backgroundColorSelect,
backgroundColor,
backgroundImageEnable,
@ -931,6 +933,9 @@ const componentBackgroundStyle = computed(() => {
if (element.value.component !== 'UserView') {
style['overflow'] = 'hidden'
}
if (backdropFilterEnable) {
style['backdrop-filter'] = 'blur(' + backdropFilter + 'px)'
}
return style
}
return {}

View File

@ -1,6 +1,25 @@
<template>
<div style="width: 100%" ref="bgForm">
<el-form label-position="top" style="width: 100%; margin-bottom: 16px">
<el-form-item
class="form-item no-margin-bottom"
:class="'form-item-' + themes"
label="数据大屏字体选择"
>
<el-select
:effect="themes"
v-model="canvasStyleData.fontFamily"
@change="onFontFamilyChange"
>
<el-option
v-for="option in fontFamily"
:key="option.value"
:label="option.name"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item class="form-item no-margin-bottom" :class="'form-item-' + themes">
<el-checkbox
size="small"
@ -63,11 +82,22 @@ import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { ElFormItem, ElIcon } from 'element-plus-secondary'
import Icon from '../icon-custom/src/Icon.vue'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
import { CHART_FONT_FAMILY } from '@/views/chart/components/editor/util/chart'
const snapshotStore = snapshotStoreWithOut()
const dvMainStore = dvMainStoreWithOut()
const { canvasStyleData } = storeToRefs(dvMainStore)
const appearanceStore = useAppearanceStoreWithOut()
const fontFamily = CHART_FONT_FAMILY.concat(
appearanceStore.fontList.map(ele => ({
name: ele.name,
value: ele.name
}))
)
const onFontFamilyChange = () => {
appearanceStore.setCurrentFont(canvasStyleData.fontFamily)
}
const onThemeChange = () => {
snapshotStore.recordSnapshotCache()
}

View File

@ -324,7 +324,7 @@
<el-button
class="m-del-icon-btn"
text
@click="deleteLinkJumpField(index)"
@click="deleteLinkJumpFieldById(targetViewInfo.targetId)"
>
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined"
@ -442,7 +442,7 @@
<el-button
class="m-del-icon-btn"
text
@click="deleteLinkJumpField(index)"
@click="deleteLinkJumpFieldById(targetViewInfo.targetId)"
>
<el-icon size="20px">
<Icon name="icon_delete-trash_outlined"
@ -601,6 +601,7 @@ import { useAppStoreWithOut } from '@/store/modules/app'
import { XpackComponent } from '@/components/plugin'
import { useCache } from '@/hooks/web/useCache'
import { useEmbedded } from '@/store/modules/embedded'
import { guid } from '@/views/visualized/data/dataset/form/util'
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, canvasViewInfo, componentData } = storeToRefs(dvMainStore)
const linkJumpInfoTree = ref(null)
@ -909,14 +910,25 @@ const dvNodeClick = data => {
}
const addLinkJumpField = (type = 'view') => {
state.linkJumpInfo.targetViewInfoList.push({
targetId: guid(),
targetViewId: '',
targetType: type,
targetFieldId: ''
})
}
const deleteLinkJumpFieldById = index => {
state.linkJumpInfo.targetViewInfoList.splice(index, 1)
const deleteLinkJumpFieldById = targetId => {
if (targetId) {
let indexResult
state.linkJumpInfo.targetViewInfoList.forEach((item, index) => {
if (targetId === item.targetId) {
indexResult = index
}
})
if (indexResult !== undefined) {
state.linkJumpInfo.targetViewInfoList.splice(indexResult, 1)
}
}
}
const deleteLinkJumpField = index => {

View File

@ -596,8 +596,14 @@ const jsonArrayCheck = params => {
const save = () => {
const outerParamsCopy = deepCopy(state.outerParams)
let checkErrorNum = 0
let checkNullErrorNum = 0
let checkMessage = ''
const paramNameArray = []
outerParamsCopy.outerParamsInfoArray?.forEach(outerParamsInfo => {
if (!outerParamsInfo.paramName || paramNameArray.includes(outerParamsInfo.paramName)) {
checkNullErrorNum++
}
paramNameArray.push(outerParamsInfo.paramName)
if (outerParamsInfo.defaultValue && !jsonArrayCheck(outerParamsInfo.defaultValue)) {
checkErrorNum++
checkMessage = checkMessage + `${outerParamsInfo.paramName}`
@ -636,6 +642,14 @@ const save = () => {
})
return
}
if (checkNullErrorNum > 0) {
ElMessage({
message: `存在未配置的参数名或者参数名称重复!`,
type: 'warning',
showClose: true
})
return
}
updateOuterParamsSet(outerParamsCopy).then(() => {
ElMessage({
message: t('commons.save_success'),

View File

@ -53,6 +53,34 @@
</el-col>
</el-row>
<el-form-item class="form-item no-margin-bottom" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.commonBackground.backdropFilterEnable"
@change="onBackgroundChange"
>
{{ $t('chart.backdrop_blur') }}
</el-checkbox>
</el-form-item>
<div class="indented-container">
<div class="indented-item">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-input-number
style="width: 100%"
:effect="themes"
controls-position="right"
size="middle"
:min="0"
:max="30"
:disabled="!state.commonBackground.backdropFilterEnable"
v-model="state.commonBackground.backdropFilter"
@change="onBackgroundChange"
/>
</el-form-item>
</div>
</div>
<el-form-item class="form-item no-margin-bottom" :class="'form-item-' + themes">
<el-checkbox
size="small"

View File

@ -96,6 +96,7 @@ watch(
class="color-picker-style"
:triggerWidth="65"
is-custom
show-alpha
:predefine="state.predefineColors"
@change="changeStylePre('borderColor')"
>
@ -135,6 +136,7 @@ watch(
class="color-picker-style"
:triggerWidth="65"
is-custom
show-alpha
:effect="themes"
:predefine="state.predefineColors"
@change="changeStylePre('borderColor')"

View File

@ -57,6 +57,7 @@
:prefix-icon="styleColorKey.icon"
:triggerWidth="styleColorKey.width"
is-custom
show-alpha
:predefine="state.predefineColors"
@change="
changeStyle({ key: styleColorKey.value, value: styleForm[styleColorKey.value] })
@ -291,6 +292,7 @@ import dvStyleHeadFontColor from '@/assets/svg/dv-style-headFontColor.svg'
import dvStyleScrollSpeed from '@/assets/svg/dv-style-scroll-speed.svg'
import dvStyleOpacity from '@/assets/svg/dv-style-opacity.svg'
import dvStyleTabHead from '@/assets/svg/dv-style-tab-head.svg'
import dvStyleBlur from '@/assets/svg/dv-style-blur.svg'
import dvStyleFontSize from '@/assets/svg/dv-style-fontSize.svg'
import dvStyleLetterSpacing from '@/assets/svg/dv-style-letterSpacing.svg'
import dvStyleActiveFont from '@/assets/svg/dv-style-activeFont.svg'
@ -371,6 +373,39 @@ const opacitySizeList = [
{ name: '0.9', value: 0.9 },
{ name: '1', value: 1 }
]
const backdropBlurList = [
{ name: '0', value: 'blur(0px)' },
{ name: '1', value: 'blur(1px)' },
{ name: '2', value: 'blur(2px)' },
{ name: '3', value: 'blur(3px)' },
{ name: '4', value: 'blur(4px)' },
{ name: '5', value: 'blur(5px)' },
{ name: '6', value: 'blur(6px)' },
{ name: '7', value: 'blur(7px)' },
{ name: '8', value: 'blur(8px)' },
{ name: '9', value: 'blur(9px)' },
{ name: '10', value: 'blur(10px)' },
{ name: '11', value: 'blur(11px)' },
{ name: '12', value: 'blur(12px)' },
{ name: '13', value: 'blur(13px)' },
{ name: '14', value: 'blur(14px)' },
{ name: '15', value: 'blur(15px)' },
{ name: '16', value: 'blur(16px)' },
{ name: '17', value: 'blur(17px)' },
{ name: '18', value: 'blur(18px)' },
{ name: '19', value: 'blur(19px)' },
{ name: '20', value: 'blur(20px)' },
{ name: '21', value: 'blur(21px)' },
{ name: '22', value: 'blur(22px)' },
{ name: '23', value: 'blur(23px)' },
{ name: '24', value: 'blur(24px)' },
{ name: '25', value: 'blur(25px)' },
{ name: '26', value: 'blur(26px)' },
{ name: '27', value: 'blur(27px)' },
{ name: '28', value: 'blur(28px)' },
{ name: '29', value: 'blur(29px)' },
{ name: '30', value: 'blur(30px)' }
]
const titleHideList = [
{ name: '隐藏', value: true },
@ -479,6 +514,13 @@ const styleOptionKeyArray = [
customOption: titleHideList,
width: '90px',
icon: dvStyleTabHead
},
{
value: 'backdropFilter',
label: '背景模糊',
customOption: backdropBlurList,
width: '90px',
icon: dvStyleBlur
}
]

View File

@ -177,12 +177,14 @@ export const MULTI_DIMENSIONAL = {
export const COMMON_COMPONENT_BACKGROUND_BASE = {
backgroundColorSelect: true,
backdropFilterEnable: false,
backgroundImageEnable: false,
backgroundType: 'innerImage',
innerImage: 'board/board_1.svg',
outerImage: null,
innerPadding: 12,
borderRadius: 0
borderRadius: 0,
backdropFilter: 4
}
export const COMMON_COMPONENT_BACKGROUND_LIGHT = {
@ -455,7 +457,8 @@ const list = [
style: {
width: 40,
height: 40,
color: ''
color: '',
backdropFilter: 'blur(0px)'
}
},
{
@ -474,7 +477,8 @@ const list = [
style: {
width: 600,
height: 300,
color: 'rgb(255, 255, 255,1)'
color: 'rgb(255, 255, 255,1)',
backdropFilter: 'blur(0px)'
}
},
{
@ -487,7 +491,8 @@ const list = [
width: 200,
height: 200,
backgroundColor: 'rgba(236,231,231,0.1)',
borderActive: true
borderActive: true,
backdropFilter: 'blur(0px)'
}
},
{
@ -502,7 +507,8 @@ const list = [
borderWidth: 1,
borderStyle: 'solid',
borderColor: '#cccccc',
backgroundColor: 'rgba(236,231,231,0.1)'
backgroundColor: 'rgba(236,231,231,0.1)',
backdropFilter: 'blur(0px)'
}
},
{
@ -516,7 +522,8 @@ const list = [
height: 200,
borderWidth: 1,
borderColor: '#cccccc',
backgroundColor: 'rgba(236,231,231,0.1)'
backgroundColor: 'rgba(236,231,231,0.1)',
backdropFilter: 'blur(0px)'
}
},
{

View File

@ -143,7 +143,7 @@ const curFontFamily = () => {
value: ele.name
}))
).forEach(font => {
result = result + font.name + '=' + font.name + ';'
result = result + font.name + '=' + font.value + ';'
})
return result
}

View File

@ -197,7 +197,7 @@ const relativeToCurrentListRange = computed(() => {
value: 'thisMonth'
},
{
label: t('dynamic_month.dynamic_month'),
label: t('dynamic_month.last'),
value: 'lastMonth'
},
{

View File

@ -198,7 +198,7 @@ const relativeToCurrentListRange = computed(() => {
value: 'thisMonth'
},
{
label: t('dynamic_month.dynamic_month'),
label: t('dynamic_month.last'),
value: 'lastMonth'
},
{
@ -283,7 +283,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting" v-if="timeRange.intervalType !== 'timeInterval'">
<div class="setting-label">{{ t('dynamic_time.relative') }}</div>
<div class="setting-value select">
<el-select v-model="timeRange.relativeToCurrent">
<el-select :teleported="false" v-model="timeRange.relativeToCurrent">
<el-option
v-for="item in relativeToCurrentList"
:key="item.value"
@ -296,7 +296,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting" v-if="timeRange.relativeToCurrent === 'custom'">
<div class="setting-input">
<el-input-number v-model="timeRange.timeNum" :min="0" controls-position="right" />
<el-select v-model="timeRange.relativeToCurrentType">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentType">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -304,7 +304,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.around">
<el-select :teleported="false" v-model="timeRange.around">
<el-option
v-for="item in aroundList"
:key="item.value"
@ -319,7 +319,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting">
<div class="setting-label">{{ t('dynamic_time.relative') }}</div>
<div class="setting-value select">
<el-select v-model="timeRange.relativeToCurrentRange">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentRange">
<el-option
v-for="item in relativeToCurrentListRange"
:key="item.value"
@ -342,7 +342,7 @@ const relativeToCurrentListRange = computed(() => {
:min="0"
controls-position="right"
/>
<el-select v-model="timeRange.relativeToCurrentType">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentType">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -350,7 +350,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.around">
<el-select :teleported="false" v-model="timeRange.around">
<el-option
v-for="item in aroundList"
:key="item.value"
@ -372,7 +372,7 @@ const relativeToCurrentListRange = computed(() => {
step-strictly
controls-position="right"
/>
<el-select v-model="timeRange.relativeToCurrentTypeRange">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentTypeRange">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -380,7 +380,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.aroundRange">
<el-select :teleported="false" v-model="timeRange.aroundRange">
<el-option
v-for="item in aroundList"
:key="item.value"

View File

@ -1948,7 +1948,7 @@ const relativeToCurrentListRange = computed(() => {
value: 'thisMonth'
},
{
label: t('dynamic_month.dynamic_month'),
label: t('dynamic_month.last'),
value: 'lastMonth'
},
{

View File

@ -209,7 +209,7 @@ const relativeToCurrentListRange = computed(() => {
value: 'thisMonth'
},
{
label: t('dynamic_month.dynamic_month'),
label: t('dynamic_month.last'),
value: 'lastMonth'
},
{
@ -294,7 +294,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting" v-if="timeRange.intervalType !== 'timeInterval'">
<div class="setting-label">{{ t('dynamic_time.relative') }}</div>
<div class="setting-value select">
<el-select v-model="timeRange.relativeToCurrent">
<el-select :teleported="false" v-model="timeRange.relativeToCurrent">
<el-option
v-for="item in relativeToCurrentList"
:key="item.value"
@ -307,7 +307,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting" v-if="timeRange.relativeToCurrent === 'custom'">
<div class="setting-input">
<el-input-number v-model="timeRange.timeNum" :min="0" controls-position="right" />
<el-select v-model="timeRange.relativeToCurrentType">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentType">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -315,7 +315,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.around">
<el-select :teleported="false" v-model="timeRange.around">
<el-option
v-for="item in aroundList"
:key="item.value"
@ -330,7 +330,7 @@ const relativeToCurrentListRange = computed(() => {
<div class="setting">
<div class="setting-label">{{ t('dynamic_time.relative') }}</div>
<div class="setting-value select">
<el-select v-model="timeRange.relativeToCurrentRange">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentRange">
<el-option
v-for="item in relativeToCurrentListRange"
:key="item.value"
@ -356,7 +356,7 @@ const relativeToCurrentListRange = computed(() => {
:min="0"
controls-position="right"
/>
<el-select v-model="timeRange.relativeToCurrentType">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentType">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -364,7 +364,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.around">
<el-select :teleported="false" v-model="timeRange.around">
<el-option
v-for="item in aroundList"
:key="item.value"
@ -389,7 +389,7 @@ const relativeToCurrentListRange = computed(() => {
step-strictly
controls-position="right"
/>
<el-select v-model="timeRange.relativeToCurrentTypeRange">
<el-select :teleported="false" v-model="timeRange.relativeToCurrentTypeRange">
<el-option
v-for="item in relativeToCurrentTypeList"
:key="item.value"
@ -397,7 +397,7 @@ const relativeToCurrentListRange = computed(() => {
:value="item.value"
/>
</el-select>
<el-select v-model="timeRange.aroundRange">
<el-select :teleported="false" v-model="timeRange.aroundRange">
<el-option
v-for="item in aroundList"
:key="item.value"

View File

@ -8,7 +8,9 @@ export const useMoveLine = (type: Sidebar) => {
const width = ref(wsCache.get(type) || 280)
const getCoordinates = () => {
document.querySelector('.sidebar-move-line').className = 'sidebar-move-line dragging'
if (document.querySelector('.sidebar-move-line')) {
document.querySelector('.sidebar-move-line').className = 'sidebar-move-line dragging'
}
document.addEventListener('mousemove', setCoordinates)
document.addEventListener('mouseup', cancelEvent)
document.querySelector('body').style['user-select'] = 'none'
@ -26,7 +28,9 @@ export const useMoveLine = (type: Sidebar) => {
}
const cancelEvent = () => {
document.querySelector('.sidebar-move-line').className = 'sidebar-move-line'
if (document.querySelector('.sidebar-move-line')) {
document.querySelector('.sidebar-move-line').className = 'sidebar-move-line'
}
document.querySelector('body').style['user-select'] = 'auto'
wsCache.set(type, width.value)
document.removeEventListener('mousemove', setCoordinates)

View File

@ -11,7 +11,6 @@ import { formatRoute } from '@/router/establish'
import HeaderMenuItem from './HeaderMenuItem.vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { Icon } from '@/components/icon-custom'
import { ElHeader, ElMenu } from 'element-plus-secondary'
import SystemCfg from './SystemCfg.vue'
import ToolboxCfg from './ToolboxCfg.vue'
import { useRouter, useRoute } from 'vue-router'
@ -30,6 +29,7 @@ const { push } = useRouter()
const route = useRoute()
import { useCache } from '@/hooks/web/useCache'
import { useI18n } from '@/hooks/web/useI18n'
import { msgCountApi } from '@/api/msg'
const { wsCache } = useCache('localStorage')
const aiBaseUrl = ref('https://maxkb.fit2cloud.com/ui/chat/2ddd8b594ce09dbb?mode=embed')
const handleIconClick = () => {
@ -119,12 +119,17 @@ const copilotConfirm = () => {
wsCache.set('DE-COPILOT-TIPS-CHECK', 'CHECKED')
showOverlayCopilot.value = false
}
const badgeCount = ref(0)
onMounted(() => {
initShowSystem()
initShowToolbox()
initAiBase()
initCopilotBase()
msgCountApi().then(res => {
badgeCount.value = res?.data || 0
})
})
</script>
@ -184,15 +189,17 @@ onMounted(() => {
<ToolboxCfg v-if="showToolbox" />
<TopDoc v-if="appearanceStore.getShowDoc" />
<el-tooltip effect="dark" :content="$t('v_query.msg_center')" placement="bottom">
<el-icon
class="preview-download_icon"
style="margin-right: 10px"
:class="navigateBg === 'light' && 'is-light-setting'"
>
<Icon name="dv-preview-download"
><msgNotice @click="msgNoticePush" class="svg-icon"
/></Icon>
</el-icon>
<el-badge :hidden="badgeCount === 0" :value="badgeCount" class="item">
<el-icon
class="preview-download_icon"
style="margin-right: 10px"
:class="navigateBg === 'light' && 'is-light-setting'"
>
<Icon name="dv-preview-download"
><msgNotice @click="msgNoticePush" class="svg-icon"
/></Icon>
</el-icon>
</el-badge>
</el-tooltip>
<SystemCfg v-if="showSystem" />

View File

@ -1231,6 +1231,7 @@ export default {
quick_calc: '快速計算',
show_name_set: '編輯顯示名稱',
show_name: '顯示名稱',
backdrop_blur: '背景模糊',
color: '顏色',
color_case: '配色方案',
pls_slc_color_case: '請選擇配色方案',

View File

@ -1244,6 +1244,7 @@ export default {
quick_calc: '快速计算',
show_name_set: '编辑显示名称',
show_name: '显示名称',
backdrop_blur: '背景模糊',
color: '颜色',
color_case: '配色方案',
pls_slc_color_case: '请选择配色方案',

View File

@ -147,6 +147,10 @@ declare interface ChartThreshold {
* 文本卡阈值
*/
textLabelThreshold: Threshold[]
/**
* 折线阈值
*/
lineThreshold: TableThreshold[]
}
declare interface TableThreshold {
/**

View File

@ -35,7 +35,9 @@ const { inMobile, dvInfo, canvasStyleData, componentData, canvasViewInfo, appDat
storeToRefs(dvMainStore)
const snapshotStore = snapshotStoreWithOut()
import { useI18n } from '@/hooks/web/useI18n'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
const { t } = useI18n()
const appearanceStore = useAppearanceStoreWithOut()
export function chartTransStr2Object(targetIn, copy) {
const target = copy === 'Y' ? cloneDeep(targetIn) : targetIn
@ -223,6 +225,7 @@ export function historyAdaptor(
canvasVersion
) {
//历史字段适配
canvasStyleResult['fontFamily'] = canvasStyleResult['fontFamily'] || 'PingFang'
canvasStyleResult.dashboard['showGrid'] = canvasStyleResult.dashboard['showGrid'] || false
canvasStyleResult.dashboard['matrixBase'] = canvasStyleResult.dashboard['matrixBase'] || 4
canvasStyleResult.component['seniorStyleSetting'] =
@ -345,6 +348,7 @@ export function initCanvasDataPrepare(dvId, busiFlag, callBack) {
dvInfo.type === 'dashboard' && canvasStyleResult['dashboard'].gap === 'yes'
? canvasStyleResult['dashboard'].gapSize
: 0
appearanceStore.setCurrentFont(canvasStyleData.fontFamily)
callBack({ canvasDataResult, canvasStyleResult, dvInfo, canvasViewInfoPreview, curPreviewGap })
})
}

View File

@ -200,7 +200,8 @@ export function getCanvasStyle(canvasStyleData, canvasId = 'canvas-main') {
backgroundColor,
backgroundImageEnable,
fontSize,
mobileSetting
mobileSetting,
fontFamily
} = canvasStyleData
const style = { fontSize: fontSize + 'px', color: canvasStyleData.color }
if (isMainCanvas(canvasId)) {
@ -225,6 +226,7 @@ export function getCanvasStyle(canvasStyleData, canvasId = 'canvas-main') {
style['background'] = `url(${imgUrlTrans(background)}) no-repeat`
}
}
style['font-family'] = fontFamily + '!important'
}
return style

View File

@ -7,6 +7,7 @@ import { DEFAULT_THRESHOLD } from '@/views/chart/components/editor/util/chart'
import TableThresholdEdit from '@/views/chart/components/editor/editor-senior/components/dialog/TableThresholdEdit.vue'
import TextLabelThresholdEdit from '@/views/chart/components/editor/editor-senior/components/dialog/TextLabelThresholdEdit.vue'
import TextThresholdEdit from '@/views/chart/components/editor/editor-senior/components/dialog/TextThresholdEdit.vue'
import LineThresholdEdit from '@/views/chart/components/editor/editor-senior/components/dialog/LineThresholdEdit.vue'
import { fieldType } from '@/utils/attr'
import { defaultsDeep } from 'lodash-es'
import { iconFieldMap } from '@/components/icon-group/field-list'
@ -50,7 +51,9 @@ const state = reactive({
editLabelThresholdDialog: false,
thresholdArr: [],
editTableThresholdDialog: false,
tableThresholdArr: []
tableThresholdArr: [],
editLineThresholdDialog: false,
lineThresholdArr: []
})
const init = () => {
@ -63,6 +66,7 @@ const init = () => {
state.textThresholdArr = JSON.parse(JSON.stringify(state.thresholdForm.textLabelThreshold))
state.thresholdArr = JSON.parse(JSON.stringify(state.thresholdForm.labelThreshold))
state.tableThresholdArr = JSON.parse(JSON.stringify(state.thresholdForm.tableThreshold))
state.lineThresholdArr = JSON.parse(JSON.stringify(state.thresholdForm.lineThreshold ?? []))
}
}
const changeThreshold = () => {
@ -257,6 +261,77 @@ const changeTableThreshold = () => {
changeThreshold()
closeTableThreshold()
}
const lineThresholdChange = val => {
state.lineThresholdArr = val
}
const editLineThreshold = () => {
state.editLineThresholdDialog = true
}
const closeLineThreshold = () => {
state.editLineThresholdDialog = false
}
const changeLineThreshold = () => {
// check line config
for (let i = 0; i < state.lineThresholdArr?.length; i++) {
const field = state.lineThresholdArr[i]
if (!field.fieldId) {
ElMessage.error(t('chart.field_can_not_empty'))
return
}
if (!field.conditions || field.conditions.length === 0) {
ElMessage.error(t('chart.conditions_can_not_empty'))
return
}
for (let j = 0; j < field.conditions.length; j++) {
const ele = field.conditions[j]
if (!ele.term || ele.term === '') {
ElMessage.error(t('chart.exp_can_not_empty'))
return
}
if (ele.term === 'between') {
if (
!ele.term.includes('null') &&
!ele.term.includes('empty') &&
(ele.min === '' || ele.max === '')
) {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
(parseFloat(ele.min).toString() === 'NaN' || parseFloat(ele.max).toString() === 'NaN')
) {
ElMessage.error(t('chart.value_error'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.min) > parseFloat(ele.max)
) {
ElMessage.error(t('chart.value_min_max_invalid'))
return
}
} else {
if (!ele.term.includes('null') && !ele.term.includes('empty') && ele.value === '') {
ElMessage.error(t('chart.value_can_not_empty'))
return
}
if (
(field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) &&
parseFloat(ele.value).toString() === 'NaN'
) {
ElMessage.error(t('chart.value_error'))
return
}
}
}
}
state.thresholdForm.lineThreshold = JSON.parse(JSON.stringify(state.lineThresholdArr ?? []))
changeThreshold()
closeLineThreshold()
}
const getFieldName = field => (field.chartShowName ? field.chartShowName : field.name)
const getDynamicStyleLabel = (item, fieldObj) => {
@ -713,6 +788,129 @@ init()
</div>
</el-col>
</el-col>
<!--折线-->
<el-col v-show="showProperty('lineThreshold')">
<el-col>
<div class="inner-container">
<span class="label" :class="'label-' + props.themes">条件样式设置</span>
<span class="right-btns">
<span
class="set-text-info"
:class="{ 'set-text-info-dark': themes === 'dark' }"
v-if="state.thresholdForm?.tableThreshold?.length > 0"
>
已设置
</span>
<el-button
:title="t('chart.edit')"
:class="'label-' + props.themes"
:style="{ width: '24px', marginLeft: '6px' }"
:disabled="!state.thresholdForm.enable"
class="circle-button"
text
size="small"
@click="editLineThreshold"
>
<template #icon>
<el-icon size="14px">
<Icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></Icon>
</el-icon>
</template>
</el-button>
</span>
</div>
<div
class="threshold-container"
:class="{ 'threshold-container-dark': themes === 'dark' }"
v-if="state.thresholdForm.lineThreshold?.length > 0"
>
<el-row
v-for="(fieldItem, fieldIndex) in state.thresholdForm.lineThreshold"
:key="fieldIndex"
style="flex-direction: column"
>
<div class="field-style" :class="{ 'field-style-dark': themes === 'dark' }">
<el-icon>
<Icon :className="`field-icon-${fieldType[fieldItem.field.deType]}`"
><component
class="svg-icon"
:class="`field-icon-${fieldType[fieldItem.field.deType]}`"
:is="iconFieldMap[fieldType[fieldItem.field.deType]]"
></component
></Icon>
</el-icon>
<span :title="fieldItem.field.name" class="field-text">{{
fieldItem.field.name
}}</span>
</div>
<div v-for="(item, index) in fieldItem.conditions" :key="index" class="line-style">
<div style="flex: 1">
<span v-if="item.term === 'lt'" :title="t('chart.filter_lt')">
{{ t('chart.filter_lt') }}
</span>
<span v-else-if="item.term === 'gt'" :title="t('chart.filter_gt')">
{{ t('chart.filter_gt') }}
</span>
<span v-else-if="item.term === 'le'" :title="t('chart.filter_le')">
{{ t('chart.filter_le') }}
</span>
<span v-else-if="item.term === 'ge'" :title="t('chart.filter_ge')">
{{ t('chart.filter_ge') }}
</span>
<span v-else-if="item.term === 'between'" :title="t('chart.filter_between')">
{{ t('chart.filter_between') }}
</span>
<span v-else-if="item.term === 'default'" title="默认"> 默认 </span>
</div>
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.fix') }}
</span>
</div>
<div v-else style="flex: 1; margin: 0 8px">
<span style="margin: 0 8px">
{{ t('chart.dynamic') }}
</span>
</div>
<div v-if="item.type !== 'dynamic'" style="flex: 1; margin: 0 8px">
<span
v-if="
!item.term.includes('null') &&
!item.term.includes('default') &&
!item.term.includes('empty') &&
item.term !== 'between'
"
:title="item.value + ''"
>{{ item.value }}</span
>
<span
v-else-if="
!item.term.includes('null') &&
!item.term.includes('empty') &&
item.term === 'between'
"
:title="item.min + ' ≤= ' + t('chart.drag_block_label_value') + ' ≤ ' + item.max"
>
{{ item.min }}&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;{{ item.max }}
</span>
<span v-else>&nbsp;</span>
</div>
<template v-if="chart.type !== 'picture-group'">
<div
:title="t('chart.color')"
:style="{
backgroundColor: item.color
}"
class="color-div"
:class="{ 'color-div-dark': themes === 'dark' }"
></div>
</template>
</div>
</el-row>
</div>
</el-col>
</el-col>
<!--编辑文本卡阈值-->
<el-dialog
@ -794,6 +992,30 @@ init()
</div>
</template>
</el-dialog>
<!--编辑折线阈值-->
<el-dialog
v-if="state.editLineThresholdDialog"
v-model="state.editLineThresholdDialog"
:title="t('chart.threshold')"
:visible="state.editLineThresholdDialog"
width="1050px"
class="dialog-css"
append-to-body
>
<line-threshold-edit
:threshold="state.thresholdForm.lineThreshold"
:chart="chart"
@onLineThresholdChange="lineThresholdChange"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeLineThreshold">{{ t('chart.cancel') }}</el-button>
<el-button type="primary" @click="changeLineThreshold">{{
t('chart.confirm')
}}</el-button>
</div>
</template>
</el-dialog>
</div>
</template>

View File

@ -0,0 +1,499 @@
<script lang="tsx" setup>
import icon_info_filled from '@/assets/svg/icon_info_filled.svg'
import icon_deleteTrash_outlined from '@/assets/svg/icon_delete-trash_outlined.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import { PropType, reactive } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { COLOR_PANEL } from '../../../util/chart'
import { fieldType } from '@/utils/attr'
import { iconFieldMap } from '@/components/icon-group/field-list'
const { t } = useI18n()
const props = defineProps({
chart: {
type: Object as PropType<ChartObj>,
required: true
},
threshold: {
type: Array,
required: true,
default: () => []
}
})
const emit = defineEmits(['onLineThresholdChange'])
const thresholdCondition = {
term: 'lt',
field: '0',
value: '0',
color: '#ff0000ff',
backgroundColor: '#ffffff00',
min: '0',
max: '1',
type: 'fixed'
}
const valueOptions = [
{
label: '',
options: [
{
value: 'lt',
label: t('chart.filter_lt')
},
{
value: 'gt',
label: t('chart.filter_gt')
}
]
},
{
label: '',
options: [
{
value: 'between',
label: t('chart.filter_between')
}
]
}
]
const predefineColors = COLOR_PANEL
const state = reactive({
thresholdArr: [] as LineThreshold[],
fields: [],
thresholdObj: {
fieldId: '',
field: {},
conditions: []
} as LineThreshold
})
const init = () => {
state.thresholdArr = JSON.parse(JSON.stringify(props.threshold)) as LineThreshold[]
initFields()
}
const initOptions = (item, fieldObj) => {
if (fieldObj) {
item.options = JSON.parse(JSON.stringify(valueOptions))
item.conditions &&
item.conditions.forEach(ele => {
ele.term = ''
})
}
}
const initFields = () => {
let fields = []
const yAxis = JSON.parse(JSON.stringify(props.chart.yAxis))
fields = [...yAxis]
state.fields.splice(0, state.fields.length, ...fields)
//
let change = false
state.thresholdArr.forEach(item => {
const fieldItemObj = state.fields.filter(ele => ele.id === item.fieldId)
if (fieldItemObj.length === 0) {
change = true
item.fieldId = null
}
})
if (change) {
changeThreshold()
}
}
const addThreshold = () => {
state.thresholdArr.push(JSON.parse(JSON.stringify(state.thresholdObj)))
changeThreshold()
}
const removeThreshold = index => {
state.thresholdArr.splice(index, 1)
changeThreshold()
}
const changeThreshold = () => {
emit('onLineThresholdChange', state.thresholdArr)
}
const addConditions = item => {
const newCondition = JSON.parse(JSON.stringify(thresholdCondition))
item.conditions.push(newCondition)
changeThreshold()
}
const removeCondition = (item, index) => {
item.conditions.splice(index, 1)
changeThreshold()
}
const addField = item => {
// get field
if (state.fields && state.fields.length > 0) {
state.fields.forEach(ele => {
if (item.fieldId === ele.id) {
item.field = JSON.parse(JSON.stringify(ele))
initOptions(item, item.field)
}
})
}
changeThreshold()
}
const fieldOptions = [{ label: t('chart.field_fixed'), value: 'fixed' }]
const isNotEmptyAndNull = item => {
return !item.term.includes('null') && !item.term.includes('empty')
}
const isBetween = item => {
return item.term === 'between'
}
const isDynamic = item => {
return item.type === 'dynamic'
}
const getFieldOptions = fieldItem => {
const deType = state.fields.filter(ele => ele.id === fieldItem.fieldId)?.[0]?.deType
if (deType === 1) {
return fieldOptions.filter(ele => ele.value === 'fixed')
} else {
return fieldOptions
}
}
init()
</script>
<template>
<el-col>
<div class="tip">
<Icon name="icon_info_filled" class="icon-style"
><icon_info_filled class="svg-icon icon-style"
/></Icon>
<span style="padding-left: 10px">{{ t('chart.table_threshold_tip') }}</span>
</div>
<div @keydown.stop @keyup.stop style="max-height: 50vh; overflow-y: auto">
<div
v-for="(fieldItem, fieldIndex) in state.thresholdArr"
:key="fieldIndex"
class="field-item"
>
<el-row style="margin-top: 6px; align-items: center; justify-content: space-between">
<el-form-item class="form-item">
<el-select v-model="fieldItem.fieldId" @change="addField(fieldItem)">
<el-option
class="series-select-option"
v-for="fieldOption in state.fields"
:key="fieldOption.id"
:label="fieldOption.name"
:value="fieldOption.id"
:disabled="chart.type === 'table-info' && fieldOption.deType === 7"
>
<el-icon style="margin-right: 8px">
<Icon
><component
:class="`field-icon-${
fieldType[[2, 3].includes(fieldOption.deType) ? 2 : 0]
}`"
class="svg-icon"
:is="iconFieldMap[fieldType[fieldOption.deType]]"
></component
></Icon>
</el-icon>
{{ fieldOption.name }}
</el-option>
</el-select>
</el-form-item>
<el-button
class="circle-button m-icon-btn"
text
:style="{ float: 'right' }"
@click="removeThreshold(fieldIndex)"
>
<el-icon size="20px" style="color: #646a73">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</el-row>
<el-row :style="{ marginTop: '16px', borderTop: '1px solid #d5d6d8' }">
<el-row
v-for="(item, index) in fieldItem.conditions"
:key="index"
class="line-item"
:gutter="12"
>
<el-col :span="4">
<el-form-item class="form-item">
<el-select v-model="item.term" @change="changeThreshold">
<el-option-group
v-for="(group, idx) in fieldItem.options"
:key="idx"
:label="group.label"
>
<el-option
v-for="opt in group.options"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4" v-if="isNotEmptyAndNull(item)" style="padding-left: 0 !important">
<el-form-item class="form-item">
<el-select v-model="item.type" class="select-item" style="width: 100%">
<el-option
v-for="opt in getFieldOptions(fieldItem)"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
</el-col>
<!--不是between 不是动态值-->
<el-col
v-if="isNotEmptyAndNull(item) && !isBetween(item) && !isDynamic(item)"
:span="12"
style="text-align: center"
>
<el-form-item class="form-item">
<el-input-number
v-model="item.value"
v-if="[2, 3].includes(fieldItem.field.deType)"
:placeholder="t('chart.drag_block_label_value')"
controls-position="right"
class="value-item"
clearable
@change="changeThreshold"
/>
<el-input
v-model="item.value"
v-else
:placeholder="t('chart.drag_block_label_value')"
controls-position="right"
clearable
@change="changeThreshold"
/>
</el-form-item>
</el-col>
<!--between 开始值-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
:span="5"
style="text-align: center"
>
<el-form-item class="form-item">
<el-input-number
v-model="item.min"
controls-position="right"
class="between-item"
:placeholder="t('chart.axis_value_min')"
clearable
@change="changeThreshold"
/>
</el-form-item>
</el-col>
<el-col
v-if="isBetween(item) && !isDynamic(item)"
:span="2"
style="margin-top: 4px; text-align: center"
>
<span style="margin: 0 -5px">
&nbsp;{{ t('chart.drag_block_label_value') }}&nbsp;
</span>
</el-col>
<!--between 结束值-->
<el-col
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
:span="5"
style="text-align: center"
>
<el-form-item class="form-item">
<el-input-number
v-model="item.max"
controls-position="right"
class="between-item"
:placeholder="t('chart.axis_value_max')"
clearable
@change="changeThreshold"
/>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item class="form-item" :label="t('chart.textColor')">
<el-color-picker
is-custom
size="large"
v-model="item.color"
show-alpha
class="color-picker-style"
:predefine="predefineColors"
@change="changeThreshold"
/>
</el-form-item>
</el-col>
<el-col :span="1">
<div style="display: flex; align-items: center; justify-content: center">
<el-button
class="circle-button m-icon-btn"
text
@click="removeCondition(fieldItem, index)"
>
<el-icon size="20px" style="color: #646a73">
<Icon name="icon_delete-trash_outlined"
><icon_deleteTrash_outlined class="svg-icon"
/></Icon>
</el-icon>
</el-button>
</div>
</el-col>
</el-row>
</el-row>
<el-button
style="margin-top: 10px"
class="circle-button"
type="primary"
text
@click="addConditions(fieldItem)"
>
<template #icon>
<Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon>
</template>
{{ t('chart.add_style') }}
</el-button>
</div>
</div>
<el-button
class="circle-button"
text
type="primary"
style="margin-top: 10px"
@click="addThreshold"
>
<template #icon>
<Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon>
</template>
{{ t('chart.add_condition') }}
</el-button>
</el-col>
</template>
<style lang="less" scoped>
.field-item {
width: 100%;
border-radius: 4px;
padding: 10px 16px;
margin-top: 10px;
background: #f5f6f7;
}
.line-item {
width: 100%;
display: flex;
justify-content: left;
align-items: center;
margin-top: 16px;
}
.form-item {
height: 28px !important;
:deep(.el-form-item__label) {
font-size: 12px;
}
}
span {
font-size: 12px;
}
.value-item {
position: relative;
display: inline-block;
width: 100% !important;
}
.between-item {
position: relative;
display: inline-block;
width: 100% !important;
}
.select-item {
position: relative;
display: inline-block;
width: 100% !important;
}
.el-select-dropdown__item {
padding: 0 20px;
font-size: 12px;
}
.color-picker-style {
cursor: pointer;
z-index: 1003;
width: 28px;
height: 28px;
}
.color-picker-style :deep(.el-color-picker__trigger) {
width: 28px;
height: 28px;
}
.color-title {
color: #646a73;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
padding: 0 8px;
}
.tip {
font-size: 12px;
background: #d6e2ff;
border-radius: 4px;
padding: 10px 20px;
display: flex;
align-items: center;
}
:deep(.ed-form-item) {
margin-bottom: 0 !important;
}
.icon-style {
width: 14px;
height: 14px;
color: var(--ed-color-primary);
}
.m-icon-btn {
&:hover {
background: rgba(31, 35, 41, 0.1) !important;
}
&:focus {
background: rgba(31, 35, 41, 0.1) !important;
}
&:active {
background: rgba(31, 35, 41, 0.2) !important;
}
}
.series-select-option {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 0 11px;
}
</style>

View File

@ -297,6 +297,33 @@ initParams()
:predefine="COLOR_PANEL"
/>
</el-form-item>
<el-form-item class="form-item margin-bottom-8" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="commonBackgroundPop.backdropFilterEnable"
>
{{ $t('chart.backdrop_blur') }}
</el-checkbox>
</el-form-item>
<el-form-item
style="padding-left: 20px"
class="form-item margin-bottom-8"
:class="'form-item-' + themes"
>
<el-input-number
style="width: 100%"
:effect="themes"
controls-position="right"
size="middle"
:min="0"
:max="30"
:disabled="!commonBackgroundPop.backdropFilterEnable"
v-model="commonBackgroundPop.backdropFilter"
/>
</el-form-item>
<el-form-item class="form-item margin-bottom-8" :class="'form-item-' + themes">
<el-checkbox
:effect="themes"

View File

@ -706,6 +706,7 @@ onMounted(() => {
v-model="state.basicStyleForm.areaBorderColor"
:effect="themes"
is-custom
show-alpha
:trigger-width="108"
class="color-picker-style"
:predefine="predefineColors"
@ -725,6 +726,7 @@ onMounted(() => {
:persistent="false"
v-model="state.basicStyleForm.areaBaseColor"
is-custom
show-alpha
:effect="themes"
:trigger-width="108"
class="color-picker-style"

View File

@ -761,7 +761,8 @@ export const DEFAULT_THRESHOLD: ChartThreshold = {
liquidThreshold: '',
labelThreshold: [],
tableThreshold: [],
textLabelThreshold: []
textLabelThreshold: [],
lineLabelThreshold: []
}
export const DEFAULT_SCROLL: ScrollCfg = {
open: false,

View File

@ -102,7 +102,8 @@ export const DEFAULT_CANVAS_STYLE_DATA_BASE = {
background: '',
openCommonStyle: true,
opacity: 1, // 废弃
fontSize: 14
fontSize: 14,
fontFamily: 'PingFang' //字体设置 默认PingFang
}
// 基础亮色主题

View File

@ -13,6 +13,8 @@ import {
import { cloneDeep } from 'lodash-es'
import {
flow,
getLineConditions,
getLineLabelColorByCondition,
hexColorToRGBA,
parseJson,
setUpStackSeriesColor
@ -135,6 +137,7 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
}
}
const { label: labelAttr, basicStyle } = parseJson(chart.customAttr)
const conditions = getLineConditions(chart)
const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => {
pre[next.id] = next
return pre
@ -163,6 +166,9 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
? -2 - basicStyle.lineSymbolSize
: 10 + basicStyle.lineSymbolSize
const value = valueFormatter(data.value, labelCfg.formatterCfg)
const color =
getLineLabelColorByCondition(conditions, data.value, data.quotaList[0].id) ||
labelCfg.color
const group = new Group({})
group.addShape({
type: 'text',
@ -173,7 +179,7 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
textAlign: 'start',
textBaseline: 'top',
fontSize: labelCfg.fontSize,
fill: labelCfg.color
fill: color
}
})
return group
@ -281,7 +287,8 @@ export class Area extends G2PlotChartView<AreaOptions, G2Area> {
this.configXAxis,
this.configYAxis,
this.configSlider,
this.configAnalyse
this.configAnalyse,
this.configConditions
)(chart, options, {}, this)
}
@ -310,6 +317,7 @@ export class StackArea extends Area {
}
protected configLabel(chart: Chart, options: AreaOptions): AreaOptions {
const { label: labelAttr, basicStyle } = parseJson(chart.customAttr)
const conditions = getLineConditions(chart)
if (!labelAttr?.show) {
return {
...options,
@ -333,7 +341,10 @@ export class StackArea extends Area {
fill: labelAttr.color,
fontSize: labelAttr.fontSize
},
formatter: function (param: Datum) {
formatter: function (param: Datum, point) {
point.color =
getLineLabelColorByCondition(conditions, param.value, point._origin.quotaList[0]) ||
labelAttr.color
return valueFormatter(param.value, labelAttr.labelFormatter)
}
}

View File

@ -11,7 +11,8 @@ export const LINE_EDITOR_PROPERTY: EditorProperty[] = [
'assist-line',
'function-cfg',
'jump-set',
'linkage'
'linkage',
'threshold'
]
export const LINE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'background-overall-component': ['all'],
@ -59,7 +60,8 @@ export const LINE_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
'fontShadow'
],
'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'],
'function-cfg': ['slider', 'emptyDataStrategy']
'function-cfg': ['slider', 'emptyDataStrategy'],
threshold: ['lineThreshold']
}
export const LINE_AXIS_TYPE: AxisType[] = [

View File

@ -11,6 +11,8 @@ import {
} from '../../common/common_antv'
import {
flow,
getLineConditions,
getLineLabelColorByCondition,
hexColorToRGBA,
parseJson,
setUpGroupSeriesColor
@ -134,6 +136,7 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
}
}
const { label: labelAttr, basicStyle } = parseJson(chart.customAttr)
const conditions = getLineConditions(chart)
const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => {
pre[next.id] = next
return pre
@ -162,6 +165,9 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
? -2 - basicStyle.lineSymbolSize
: 10 + basicStyle.lineSymbolSize
const value = valueFormatter(data.value, labelCfg.formatterCfg)
const color =
getLineLabelColorByCondition(conditions, data.value, data.quotaList[0].id) ||
labelCfg.color
const group = new Group({})
group.addShape({
type: 'text',
@ -172,7 +178,7 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
textAlign: 'start',
textBaseline: 'top',
fontSize: labelCfg.fontSize,
fill: labelCfg.color
fill: color
}
})
return group
@ -358,7 +364,8 @@ export class Line extends G2PlotChartView<LineOptions, G2Line> {
this.configXAxis,
this.configYAxis,
this.configSlider,
this.configAnalyse
this.configAnalyse,
this.configConditions
)(chart, options)
}

View File

@ -220,7 +220,7 @@ export class BubbleMap extends L7PlotChartView<ChoroplethOptions, Choropleth> {
const senior = parseJson(chart.senior)
const curAreaNameMapping = senior.areaMapping?.[areaId]
handleGeoJson(geoJson, curAreaNameMapping)
options.color = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha)
options.color = basicStyle.areaBaseColor
if (!chart.data?.data?.length || !geoJson?.features?.length) {
options.label && (options.label.field = 'name')
return options

View File

@ -1,4 +1,4 @@
import { hexColorToRGBA, parseJson } from '../../util'
import { hexColorToRGBA, isAlphaColor, isTransparent, parseJson } from '../../util'
import {
DEFAULT_BASIC_STYLE,
DEFAULT_XAXIS_STYLE,
@ -1328,3 +1328,56 @@ export const TOOLTIP_TPL =
'<span class="g2-tooltip-name">{name}</span>:' +
'<span class="g2-tooltip-value">{value}</span>' +
'</li>'
export function getConditions(chart: Chart) {
const { threshold } = parseJson(chart.senior)
const annotations = []
if (!threshold.enable) return annotations
const conditions = threshold.lineThreshold ?? []
const yAxisIds = chart.yAxis.map(i => i.id)
for (const field of conditions) {
if (!yAxisIds.includes(field.fieldId)) {
continue
}
for (const t of field.conditions) {
if ([2, 3, 4].includes(field.field.deType)) {
const annotation = {
type: 'regionFilter',
start: ['start', 'median'],
end: ['end', 'min'],
color: t.color
}
// 加中线
const annotationLine = {
type: 'line',
start: ['start', t.value],
end: ['end', t.value],
style: {
stroke: t.color,
lineDash: [2, 2]
}
}
if (t.term === 'between') {
annotation.start = ['start', parseFloat(t.min)]
annotation.end = ['end', parseFloat(t.max)]
annotationLine.start = ['start', parseFloat(t.min)]
annotationLine.end = ['end', parseFloat(t.min)]
annotations.push(JSON.parse(JSON.stringify(annotationLine)))
annotationLine.start = ['start', parseFloat(t.max)]
annotationLine.end = ['end', parseFloat(t.max)]
annotations.push(annotationLine)
} else if (['lt', 'le'].includes(t.term)) {
annotation.start = ['start', t.value]
annotation.end = ['end', 'min']
annotations.push(annotationLine)
} else if (['gt', 'ge'].includes(t.term)) {
annotation.start = ['start', t.value]
annotation.end = ['end', 'max']
annotations.push(annotationLine)
}
annotations.push(annotation)
}
}
}
return annotations
}

View File

@ -10,7 +10,8 @@ import {
getTheme,
getTooltip,
getXAxis,
getYAxis
getYAxis,
getConditions
} from '@/views/chart/components/js/panel/common/common_antv'
import {
AntVAbstractChartView,
@ -171,6 +172,14 @@ export abstract class G2PlotChartView<
return undefined
}
protected configConditions(chart: Chart, options: O) {
const annotations = getConditions(chart)
return {
...options,
annotations: [...annotations, ...((options as unknown as Options).annotations || [])]
}
}
/**
* 流式配置公共参数处理常用的配置后续如果有其他通用配置也可以放进来需要单独配置的属性在各个图表自行实现
* @param chart 数据库图表对象

View File

@ -1071,3 +1071,51 @@ export function filterEmptyMinValue(sourceData, field) {
)
return notEmptyMinValue
}
/**
* 获取折线条件样式
* @param chart
*/
export function getLineConditions(chart) {
const { threshold } = parseJson(chart.senior)
const conditions = []
if (threshold.enable) {
threshold.lineThreshold?.forEach(item =>
item.conditions?.forEach(c =>
conditions.push({
fieldId: item.fieldId,
term: c.term,
value: c.value,
color: c.color,
min: c.min,
max: c.max
})
)
)
}
return conditions
}
/**
* 根据折线阈值条件获取新的标签颜色
* @param conditions
* @param value
* @param fieldId
*/
export function getLineLabelColorByCondition(conditions, value, fieldId) {
const fieldConditions = conditions.filter(item => item.fieldId === fieldId)
let color = undefined
if (fieldConditions.length) {
fieldConditions.some(item => {
if (
(item.term === 'lt' && value <= item.value) ||
(item.term === 'gt' && value >= item.value) ||
(item.term === 'between' && value >= item.min && value <= item.max)
) {
color = item.color
return true
}
})
}
return color
}

View File

@ -252,7 +252,7 @@ defineExpose({
<el-container
class="preview-area"
:class="{ 'no-data': !hasTreeData }"
v-loading="requestStore.loadingMap[permissionStore.currentPath]"
v-loading="requestStore.loadingMap && requestStore.loadingMap[permissionStore.currentPath]"
>
<div
@click="slideOpenChange"

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { nextTick, onMounted, reactive, ref } from 'vue'
import { nextTick, onMounted, reactive, ref, watch } from 'vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import router from '@/router'
import { useEmitt } from '@/hooks/web/useEmitt'
@ -16,6 +16,8 @@ import { propTypes } from '@/utils/propTypes'
import { downloadCanvas2 } from '@/utils/imgUtils'
import { setTitle } from '@/utils/utils'
import EmptyBackground from '../../components/empty-background/src/EmptyBackground.vue'
import { useRoute } from 'vue-router'
const routeWatch = useRoute()
const dvMainStore = dvMainStoreWithOut()
const { t } = useI18n()
@ -151,6 +153,15 @@ const downloadH2 = type => {
})
})
}
//
//
watch(
() => ({ path: routeWatch.path, params: routeWatch.params }),
() => {
location.reload() //
},
{ deep: true }
)
let p = null
const XpackLoaded = () => p(true)

View File

@ -216,6 +216,9 @@ const handleDragOver = e => {
const handleMouseDown = e => {
// e.stopPropagation()
if (isSpaceDown.value) {
return
}
dvMainStore.setClickComponentStatus(false)
// curComponent
dvMainStore.setCurComponent({ component: null, index: null })
@ -224,6 +227,9 @@ const handleMouseDown = e => {
}
const deselectCurComponent = e => {
if (isSpaceDown.value) {
return
}
if (!isClickComponent.value) {
curComponent.value && dvMainStore.setCurComponent({ component: null, index: null })
}
@ -245,13 +251,15 @@ listenGlobalKeyDown()
const initScroll = () => {
nextTick(() => {
const { width, height } = canvasStyleData.value
const mainWidth = canvasCenterRef.value.clientWidth
mainHeight.value = canvasCenterRef.value.clientHeight
const scrollX = (1.5 * width - mainWidth) / 2
const scrollY = (1.5 * height - mainHeight.value) / 2 + 20
//
canvasOut.value.scrollTo(scrollX, scrollY)
if (canvasCenterRef.value) {
const { width, height } = canvasStyleData.value
const mainWidth = canvasCenterRef.value.clientWidth
mainHeight.value = canvasCenterRef.value.clientHeight
const scrollX = (1.5 * width - mainWidth) / 2
const scrollY = (1.5 * height - mainHeight.value) / 2 + 20
//
canvasOut.value.scrollTo(scrollX, scrollY)
}
})
}
const doUseCache = flag => {
@ -471,7 +479,7 @@ eventBus.on('tabSort', tabSort)
<div class="custom-dv-divider" />
<el-container
v-if="loadFinish"
v-loading="requestStore.loadingMap[permissionStore.currentPath]"
v-loading="requestStore.loadingMap && requestStore.loadingMap[permissionStore.currentPath]"
element-loading-background="rgba(0, 0, 0, 0)"
class="dv-layout-container"
:class="{ 'preview-layout-container': previewStatus }"
@ -513,7 +521,6 @@ eventBus.on('tabSort', tabSort)
@mousemove="onMouseMove"
@mouseleave="disableDragging"
>
<div v-if="isSpaceDown" class="canvas-drag" :style="contentStyle"></div>
<div
id="canvas-dv-outer"
ref="canvasInner"
@ -523,6 +530,7 @@ eventBus.on('tabSort', tabSort)
@mousedown="handleMouseDown"
@mouseup="deselectCurComponent"
>
<div v-if="isSpaceDown" class="canvas-drag"></div>
<div class="canvas-dv-inner">
<canvas-core
class="canvas-area-shadow editor-main"
@ -715,6 +723,8 @@ eventBus.on('tabSort', tabSort)
z-index: 1;
opacity: 0.3;
cursor: pointer;
width: 100%;
height: 100%;
}
.canvas-drag-tip {

View File

@ -0,0 +1,12 @@
package io.dataease.api.msgCenter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
@Tag(name = "消息中心")
public interface MsgCenterApi {
@PostMapping("/count")
long count();
}