diff --git a/core/core-backend/src/main/java/io/dataease/MybatisPlusGenerator.java b/core/core-backend/src/main/java/io/dataease/MybatisPlusGenerator.java index e648e87858..2c7d9c5aa8 100644 --- a/core/core-backend/src/main/java/io/dataease/MybatisPlusGenerator.java +++ b/core/core-backend/src/main/java/io/dataease/MybatisPlusGenerator.java @@ -14,18 +14,18 @@ public class MybatisPlusGenerator { * 第一 我嫌麻烦 * 第二 后面配置会放到nacos读起来更麻烦了 */ - private static final String url = "jdbc:mysql://localhost:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false"; + private static final String url = "jdbc:mysql://localhost:3306/de_standalone?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false"; private static final String username = "root"; - private static final String password = "123456"; + private static final String password = "Password123@mysql"; /** * 业务模块例如datasource,dataset,panel等 */ - private static final String busi = "visualization"; + private static final String busi = "share"; /** * 这是要生成代码的表名称 */ - private static final String TABLE_NAME = "data_visualization_info"; + private static final String TABLE_NAME = "xpack_share"; /** * 下面两个配置基本上不用动 diff --git a/core/core-backend/src/main/java/io/dataease/datasource/provider/Provider.java b/core/core-backend/src/main/java/io/dataease/datasource/provider/Provider.java index e564f6c5d2..1617e237f6 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/provider/Provider.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/provider/Provider.java @@ -1,7 +1,28 @@ package io.dataease.datasource.provider; +import io.dataease.api.dataset.dto.DatasetTableDTO; +import io.dataease.api.ds.vo.TableField; +import io.dataease.datasource.dao.auto.entity.CoreDatasource; +import io.dataease.datasource.request.DatasourceRequest; +import io.dataease.exception.DEException; + +import java.sql.Connection; +import java.util.List; +import java.util.Map; + /** * @Author Junjun */ public abstract class Provider { + public abstract List getSchema(DatasourceRequest datasourceRequest); + + public abstract List getTables(DatasourceRequest datasourceRequest); + + public abstract Connection getConnection(CoreDatasource coreDatasource) throws DEException; + + public abstract String checkStatus(DatasourceRequest datasourceRequest) throws Exception; + + public abstract Map fetchResultField(DatasourceRequest datasourceRequest) throws DEException; + + public abstract List fetchTableField(DatasourceRequest datasourceRequest) throws DEException; } diff --git a/core/core-backend/src/main/java/io/dataease/home/RestIndexController.java b/core/core-backend/src/main/java/io/dataease/home/RestIndexController.java index ce541e1d53..e848d88e9d 100644 --- a/core/core-backend/src/main/java/io/dataease/home/RestIndexController.java +++ b/core/core-backend/src/main/java/io/dataease/home/RestIndexController.java @@ -1,5 +1,6 @@ package io.dataease.home; +import io.dataease.license.utils.LicenseUtil; import io.dataease.utils.ModelUtils; import io.dataease.utils.RsaUtils; import org.springframework.beans.factory.annotation.Value; @@ -31,7 +32,7 @@ public class RestIndexController { @GetMapping("/xpackModel") @ResponseBody public boolean xpackModel() { - return xpackFrontDistributed; + return xpackFrontDistributed && LicenseUtil.licenseValid(); } } diff --git a/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/CoreShareTicket.java b/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/CoreShareTicket.java new file mode 100644 index 0000000000..80b048beca --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/CoreShareTicket.java @@ -0,0 +1,108 @@ +package io.dataease.share.dao.auto.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; + +/** + *

+ * + *

+ * + * @author fit2cloud + * @since 2024-06-21 + */ +@TableName("core_share_ticket") +public class CoreShareTicket implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private Long id; + + /** + * 分享uuid + */ + private String uuid; + + /** + * ticket + */ + private String ticket; + + /** + * ticket有效期 + */ + private Long exp; + + /** + * ticket参数 + */ + private String args; + + /** + * 首次访问时间 + */ + private Long accessTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public Long getExp() { + return exp; + } + + public void setExp(Long exp) { + this.exp = exp; + } + + public String getArgs() { + return args; + } + + public void setArgs(String args) { + this.args = args; + } + + public Long getAccessTime() { + return accessTime; + } + + public void setAccessTime(Long accessTime) { + this.accessTime = accessTime; + } + + @Override + public String toString() { + return "CoreShareTicket{" + + "id = " + id + + ", uuid = " + uuid + + ", ticket = " + ticket + + ", exp = " + exp + + ", args = " + args + + ", accessTime = " + accessTime + + "}"; + } +} diff --git a/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/XpackShare.java b/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/XpackShare.java index 22861cab29..d4fb7720b7 100644 --- a/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/XpackShare.java +++ b/core/core-backend/src/main/java/io/dataease/share/dao/auto/entity/XpackShare.java @@ -9,7 +9,7 @@ import java.io.Serializable; *

* * @author fit2cloud - * @since 2024-04-07 + * @since 2024-06-21 */ @TableName("xpack_share") public class XpackShare implements Serializable { @@ -66,6 +66,11 @@ public class XpackShare implements Serializable { */ private Boolean autoPwd; + /** + * ticket必须 + */ + private Boolean ticketRequire; + public Long getId() { return id; } @@ -146,6 +151,14 @@ public class XpackShare implements Serializable { this.autoPwd = autoPwd; } + public Boolean getTicketRequire() { + return ticketRequire; + } + + public void setTicketRequire(Boolean ticketRequire) { + this.ticketRequire = ticketRequire; + } + @Override public String toString() { return "XpackShare{" + @@ -159,6 +172,7 @@ public class XpackShare implements Serializable { ", oid = " + oid + ", type = " + type + ", autoPwd = " + autoPwd + + ", ticketRequire = " + ticketRequire + "}"; } } diff --git a/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/CoreShareTicketMapper.java b/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/CoreShareTicketMapper.java new file mode 100644 index 0000000000..cb912a7c07 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/CoreShareTicketMapper.java @@ -0,0 +1,18 @@ +package io.dataease.share.dao.auto.mapper; + +import io.dataease.share.dao.auto.entity.CoreShareTicket; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author fit2cloud + * @since 2024-06-21 + */ +@Mapper +public interface CoreShareTicketMapper extends BaseMapper { + +} diff --git a/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/XpackShareMapper.java b/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/XpackShareMapper.java index b2ecd60bb4..ffea74deac 100644 --- a/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/XpackShareMapper.java +++ b/core/core-backend/src/main/java/io/dataease/share/dao/auto/mapper/XpackShareMapper.java @@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper; *

* * @author fit2cloud - * @since 2024-04-07 + * @since 2024-06-21 */ @Mapper public interface XpackShareMapper extends BaseMapper { diff --git a/core/core-backend/src/main/java/io/dataease/share/dao/ext/mapper/XpackShareExtMapper.java b/core/core-backend/src/main/java/io/dataease/share/dao/ext/mapper/XpackShareExtMapper.java index 140f0b0a1a..fa437945f4 100644 --- a/core/core-backend/src/main/java/io/dataease/share/dao/ext/mapper/XpackShareExtMapper.java +++ b/core/core-backend/src/main/java/io/dataease/share/dao/ext/mapper/XpackShareExtMapper.java @@ -6,6 +6,7 @@ import io.dataease.share.dao.ext.po.XpackSharePO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; @Mapper public interface XpackShareExtMapper { @@ -28,4 +29,7 @@ public interface XpackShareExtMapper { @Select("select type from data_visualization_info where id = #{id}") String visualizationType(@Param("id") Long id); + + @Update("update core_share_ticket set uuid = #{ticketUuid} where uuid = #{originUuid}") + void updateTicketUuid(@Param("originUuid") String originUuid, @Param("ticketUuid") String ticketUuid); } diff --git a/core/core-backend/src/main/java/io/dataease/share/manage/ShareTicketManage.java b/core/core-backend/src/main/java/io/dataease/share/manage/ShareTicketManage.java new file mode 100644 index 0000000000..e639f8be70 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/manage/ShareTicketManage.java @@ -0,0 +1,153 @@ +package io.dataease.share.manage; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.dataease.api.xpack.share.request.TicketCreator; +import io.dataease.api.xpack.share.request.TicketDelRequest; +import io.dataease.api.xpack.share.request.TicketSwitchRequest; +import io.dataease.api.xpack.share.vo.TicketVO; +import io.dataease.api.xpack.share.vo.TicketValidVO; +import io.dataease.commons.utils.CodingUtil; +import io.dataease.exception.DEException; +import io.dataease.share.dao.auto.entity.CoreShareTicket; +import io.dataease.share.dao.auto.entity.XpackShare; +import io.dataease.share.dao.auto.mapper.CoreShareTicketMapper; +import io.dataease.share.dao.auto.mapper.XpackShareMapper; +import io.dataease.share.dao.ext.mapper.XpackShareExtMapper; +import io.dataease.utils.AuthUtils; +import io.dataease.utils.BeanUtils; +import io.dataease.utils.IDUtils; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Component +public class ShareTicketManage { + + @Resource + private CoreShareTicketMapper coreShareTicketMapper; + + @Resource + private XpackShareMapper xpackShareMapper; + + @Resource + private XpackShareExtMapper xpackShareExtMapper; + + public CoreShareTicket getByTicket(String ticket) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("ticket", ticket); + return coreShareTicketMapper.selectOne(queryWrapper); + } + + public String saveTicket(TicketCreator creator) { + String ticket = creator.getTicket(); + if (StringUtils.isNotBlank(ticket)) { + CoreShareTicket ticketEntity = getByTicket(ticket); + if (ObjectUtils.isNotEmpty(ticketEntity)) { + if (creator.isGenerateNew()) { + ticketEntity.setAccessTime(null); + ticketEntity.setTicket(CodingUtil.shortUuid()); + } + ticketEntity.setArgs(creator.getArgs()); + ticketEntity.setExp(creator.getExp()); + ticketEntity.setUuid(creator.getUuid()); + coreShareTicketMapper.updateById(ticketEntity); + return ticketEntity.getTicket(); + } + } + ticket = CodingUtil.shortUuid(); + CoreShareTicket linkTicket = new CoreShareTicket(); + linkTicket.setId(IDUtils.snowID()); + linkTicket.setTicket(ticket); + linkTicket.setArgs(creator.getArgs()); + linkTicket.setExp(creator.getExp()); + linkTicket.setUuid(creator.getUuid()); + coreShareTicketMapper.insert(linkTicket); + return ticket; + } + + public void deleteTicket(TicketDelRequest request) { + String ticket = request.getTicket(); + if (StringUtils.isBlank(ticket)) { + DEException.throwException("ticket为必填参数"); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("ticket", ticket); + coreShareTicketMapper.delete(queryWrapper); + } + + public void switchRequire(TicketSwitchRequest request) { + String resourceId = request.getResourceId(); + Boolean require = request.getRequire(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("resource_id", resourceId); + queryWrapper.eq("creator", AuthUtils.getUser().getUserId()); + XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper); + xpackShare.setTicketRequire(require); + xpackShareMapper.updateById(xpackShare); + } + + public List query(Long resourceId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("resource_id", resourceId); + queryWrapper.eq("creator", AuthUtils.getUser().getUserId()); + XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper); + if (ObjectUtils.isEmpty(xpackShare)) return null; + String uuid = xpackShare.getUuid(); + if (StringUtils.isBlank(uuid)) return null; + QueryWrapper ticketQueryWrapper = new QueryWrapper<>(); + ticketQueryWrapper.eq("uuid", uuid); + List coreShareTickets = coreShareTicketMapper.selectList(ticketQueryWrapper); + if (CollectionUtils.isEmpty(coreShareTickets)) return null; + return coreShareTickets.stream().map(item -> BeanUtils.copyBean(new TicketVO(), item)).toList(); + } + + @Transactional + public void updateByUuidChange(String originalUuid, String newUuid) { + xpackShareExtMapper.updateTicketUuid(originalUuid, newUuid); + } + + @Transactional + public void deleteByShare(String uuid) { + QueryWrapper ticketQueryWrapper = new QueryWrapper<>(); + ticketQueryWrapper.eq("uuid", uuid); + coreShareTicketMapper.delete(ticketQueryWrapper); + } + + public TicketValidVO validateTicket(String ticket, XpackShare share) { + TicketValidVO vo = new TicketValidVO(); + if (StringUtils.isBlank(ticket)) { + vo.setTicketValid(!share.getTicketRequire()); + return vo; + } + CoreShareTicket linkTicket = getByTicket(ticket); + if (ObjectUtils.isEmpty(linkTicket)) { + vo.setTicketValid(false); + return vo; + } + vo.setTicketValid(true); + vo.setArgs(linkTicket.getArgs()); + Long accessTime = linkTicket.getAccessTime(); + long now = System.currentTimeMillis(); + if (ObjectUtils.isEmpty(accessTime)) { + accessTime = now; + vo.setTicketExp(false); + linkTicket.setAccessTime(accessTime); + coreShareTicketMapper.updateById(linkTicket); + return vo; + } + Long exp = linkTicket.getExp(); + if (ObjectUtils.isEmpty(exp) || exp.equals(0L)) { + vo.setTicketExp(false); + return vo; + } + long expTime = exp * 60L * 1000L; + long time = now - accessTime; + vo.setTicketExp(time > expTime); + return vo; + } +} 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 9df28b897a..9392461ac8 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 @@ -7,6 +7,7 @@ import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest import io.dataease.api.xpack.share.request.XpackShareProxyRequest; import io.dataease.api.xpack.share.request.XpackSharePwdValidator; import io.dataease.api.xpack.share.request.XpackShareUuidEditor; +import io.dataease.api.xpack.share.vo.TicketValidVO; import io.dataease.api.xpack.share.vo.XpackShareGridVO; import io.dataease.api.xpack.share.vo.XpackShareProxyVO; import io.dataease.auth.bo.TokenUserBO; @@ -28,6 +29,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.HashMap; @@ -46,6 +48,9 @@ public class XpackShareManage { @Resource(name = "xpackShareExtMapper") private XpackShareExtMapper xpackShareExtMapper; + @Resource + private ShareTicketManage shareTicketManage; + public XpackShare queryByResource(Long resourceId) { Long userId = AuthUtils.getUser().getUserId(); QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -54,10 +59,12 @@ public class XpackShareManage { return xpackShareMapper.selectOne(queryWrapper); } + @Transactional public void switcher(Long resourceId) { XpackShare originData = queryByResource(resourceId); if (ObjectUtils.isNotEmpty(originData)) { xpackShareMapper.deleteById(originData.getId()); + shareTicketManage.deleteByShare(originData.getUuid()); return; } TokenUserBO user = AuthUtils.getUser(); @@ -74,6 +81,7 @@ public class XpackShareManage { xpackShareMapper.insert(xpackShare); } + @Transactional public String editUuid(XpackShareUuidEditor editor) { Long resourceId = editor.getResourceId(); String uuid = editor.getUuid(); @@ -98,6 +106,7 @@ public class XpackShareManage { if (!matcher.matches()) { return "仅支持8-16位(字母数字),请重新输入!"; } + shareTicketManage.updateByUuidChange(originData.getUuid(), uuid); originData.setUuid(uuid); xpackShareMapper.updateById(originData); return ""; @@ -196,7 +205,8 @@ public class XpackShareManage { response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken); Integer type = xpackShare.getType(); String typeText = (ObjectUtils.isNotEmpty(type) && type == 1) ? "dashboard" : "dataV"; - return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError); + TicketValidVO validVO = shareTicketManage.validateTicket(request.getTicket(), xpackShare); + return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText, inIframeError, validVO); } private boolean linkExp(XpackShare xpackShare) { diff --git a/core/core-backend/src/main/java/io/dataease/share/server/ShareTicketServer.java b/core/core-backend/src/main/java/io/dataease/share/server/ShareTicketServer.java new file mode 100644 index 0000000000..f89ca4f1e3 --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/share/server/ShareTicketServer.java @@ -0,0 +1,40 @@ +package io.dataease.share.server; + +import io.dataease.api.xpack.share.ShareTicketApi; +import io.dataease.api.xpack.share.request.TicketCreator; +import io.dataease.api.xpack.share.request.TicketDelRequest; +import io.dataease.api.xpack.share.request.TicketSwitchRequest; +import io.dataease.api.xpack.share.vo.TicketVO; +import io.dataease.share.manage.ShareTicketManage; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.List; + +@RestController +@RequestMapping("/ticket") +public class ShareTicketServer implements ShareTicketApi { + + @Resource + private ShareTicketManage shareTicketManage; + + @Override + public String saveTicket(TicketCreator creator) { + return shareTicketManage.saveTicket(creator); + } + + @Override + public void deleteTicket(TicketDelRequest request) { + shareTicketManage.deleteTicket(request); + } + + @Override + public void switchRequire(TicketSwitchRequest request) { + shareTicketManage.switchRequire(request); + } + + @Override + public List query(Long resourceId) { + return shareTicketManage.query(resourceId); + } +} diff --git a/core/core-backend/src/main/java/io/dataease/visualization/server/StaticResourceServer.java b/core/core-backend/src/main/java/io/dataease/visualization/server/StaticResourceServer.java index 5ddb6c01b3..33b94ccf6a 100644 --- a/core/core-backend/src/main/java/io/dataease/visualization/server/StaticResourceServer.java +++ b/core/core-backend/src/main/java/io/dataease/visualization/server/StaticResourceServer.java @@ -76,7 +76,7 @@ public class StaticResourceServer implements StaticResourceApi { return true; } // 判断其他图片 - if (image == null || image.getWidth() <= 0 || image.getHeight() <= 0 || !isValidSVG(file)) { + if (image == null || image.getWidth() <= 0 || image.getHeight() <= 0) { return false; } return true; diff --git a/core/core-backend/src/main/resources/db/migration/V2.8__ddl.sql b/core/core-backend/src/main/resources/db/migration/V2.8__ddl.sql index 9ac0579979..6dcf4ba043 100644 --- a/core/core-backend/src/main/resources/db/migration/V2.8__ddl.sql +++ b/core/core-backend/src/main/resources/db/migration/V2.8__ddl.sql @@ -1 +1,36 @@ -ALTER TABLE `core_export_task` ADD COLUMN `msg` LONGTEXT NULL COMMENT '错误信息' AFTER `params`; +ALTER TABLE `core_export_task` + ADD COLUMN `msg` LONGTEXT NULL COMMENT '错误信息' AFTER `params`; + + +DROP TABLE IF EXISTS `xpack_plugin`; +CREATE TABLE `xpack_plugin` +( + `id` bigint NOT NULL COMMENT 'ID', + `name` varchar(255) NOT NULL COMMENT '插件名称', + `icon` longtext NOT NULL COMMENT '图标', + `version` varchar(255) NOT NULL COMMENT '版本', + `install_time` bigint NOT NULL COMMENT '安装时间', + `flag` varchar(255) NOT NULL COMMENT '类型', + `developer` varchar(255) NOT NULL COMMENT '开发者', + `config` longtext NOT NULL COMMENT '插件配置', + `require_version` varchar(255) NOT NULL COMMENT 'DE最低版本', + `module_name` varchar(255) NOT NULL COMMENT '模块名称', + `jar_name` varchar(255) NOT NULL COMMENT 'Jar包名称', + PRIMARY KEY (`id`) +) COMMENT ='插件表'; + +ALTER TABLE `xpack_share` + ADD COLUMN `ticket_require` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'ticket必须' AFTER `auto_pwd`; + + +DROP TABLE IF EXISTS `core_share_ticket`; +CREATE TABLE `core_share_ticket` +( + `id` bigint NOT NULL COMMENT 'ID', + `uuid` varchar(255) NOT NULL COMMENT '分享uuid', + `ticket` varchar(255) NOT NULL COMMENT 'ticket', + `exp` bigint DEFAULT NULL COMMENT 'ticket有效期', + `args` longtext COMMENT 'ticket参数', + `access_time` bigint DEFAULT NULL COMMENT '首次访问时间', + PRIMARY KEY (`id`) +) COMMENT ='分享Ticket表'; \ No newline at end of file diff --git a/core/core-frontend/config/base.ts b/core/core-frontend/config/base.ts index 61513f83d1..ddb203e13c 100644 --- a/core/core-frontend/config/base.ts +++ b/core/core-frontend/config/base.ts @@ -26,6 +26,6 @@ export default { } } }, - sourcemap: false + sourcemap: true } } diff --git a/core/core-frontend/config/distributed.ts b/core/core-frontend/config/distributed.ts index ca1cd87465..9db5d95f95 100644 --- a/core/core-frontend/config/distributed.ts +++ b/core/core-frontend/config/distributed.ts @@ -14,7 +14,7 @@ export default { ], build: { rollupOptions: { - external: id => /de-xpack/.test(id) || /extensions-view-3dpie/.test(id), + external: id => /de-xpack/.test(id) || /extensions/.test(id), output: { // 用于命名代码拆分时创建的共享块的输出命名 chunkFileNames: `assets/chunk/[name]-${pkg.version}-${pkg.name}.js`, diff --git a/core/core-frontend/src/api/auth.ts b/core/core-frontend/src/api/auth.ts index 5765efd182..834f407124 100644 --- a/core/core-frontend/src/api/auth.ts +++ b/core/core-frontend/src/api/auth.ts @@ -1,6 +1,7 @@ import request from '@/config/axios' export const queryUserApi = data => request.post({ url: '/user/byCurOrg', data }) +export const queryUserOptionsApi = () => request.get({ url: '/user/org/option' }) export const queryRoleApi = data => request.post({ url: '/role/byCurOrg', data }) export const resourceTreeApi = (flag: string) => request.get({ url: '/auth/busiResource/' + flag }) diff --git a/core/core-frontend/src/assets/svg/dv-ruler.svg b/core/core-frontend/src/assets/svg/dv-ruler.svg new file mode 100644 index 0000000000..46f262abb1 --- /dev/null +++ b/core/core-frontend/src/assets/svg/dv-ruler.svg @@ -0,0 +1 @@ + diff --git a/core/core-frontend/src/assets/svg/edit-done.svg b/core/core-frontend/src/assets/svg/edit-done.svg new file mode 100644 index 0000000000..e2049b6b67 --- /dev/null +++ b/core/core-frontend/src/assets/svg/edit-done.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/core-frontend/src/components/data-visualization/ComponentToolBar.vue b/core/core-frontend/src/components/data-visualization/ComponentToolBar.vue index cda9545518..3c2e1c1353 100644 --- a/core/core-frontend/src/components/data-visualization/ComponentToolBar.vue +++ b/core/core-frontend/src/components/data-visualization/ComponentToolBar.vue @@ -157,6 +157,7 @@ onUnmounted(() => { background-color: @side-area-background; border-top: 1px solid @side-outline-border-color; color: #fff; + z-index: 2; transition: 0.5s; .scale-area { display: flex; diff --git a/core/core-frontend/src/components/data-visualization/RealTimeListTree.vue b/core/core-frontend/src/components/data-visualization/RealTimeListTree.vue index db7ed3259b..a445159ac6 100644 --- a/core/core-frontend/src/components/data-visualization/RealTimeListTree.vue +++ b/core/core-frontend/src/components/data-visualization/RealTimeListTree.vue @@ -160,6 +160,7 @@ const dragOnEnd = ({ oldIndex, newIndex }) => { componentData.value.splice(comLength - 1 - oldIndex, 1) componentData.value.splice(comLength - 1 - newIndex, 0, target) dvMainStore.setCurComponent({ component: target, index: transformIndex(comLength - oldIndex) }) + snapshotStore.recordSnapshotCache() } const getIconName = item => { diff --git a/core/core-frontend/src/components/data-visualization/canvas/CanvasCore.vue b/core/core-frontend/src/components/data-visualization/canvas/CanvasCore.vue index f3e6b67ffc..2217027e66 100644 --- a/core/core-frontend/src/components/data-visualization/canvas/CanvasCore.vue +++ b/core/core-frontend/src/components/data-visualization/canvas/CanvasCore.vue @@ -1490,25 +1490,6 @@ defineExpose({ @linkJumpSetOpen="linkJumpSetOpen(item)" @linkageSetOpen="linkageSetOpen(item)" > - - { } const importProxy = (bytesArray: any[]) => { - /* const promise = import( - `../../../../../../../${formatArray(bytesArray[7])}/${formatArray(bytesArray[8])}/${formatArray( - bytesArray[9] - )}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue` - ) */ const promise = import( - `../../../../../../../extensions-view-3dpie/${formatArray(bytesArray[8])}/${formatArray( + `../../../../../../../extensions/${formatArray(bytesArray[8])}/${formatArray( bytesArray[9] - )}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue` + )}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}/${formatArray( + bytesArray[12] + )}.vue` ) promise .then((res: any) => { @@ -53,16 +50,23 @@ const importProxy = (bytesArray: any[]) => { }) } +const getModuleName = () => { + const jsPath = window.atob(attrs.jsname.toString()) + return jsPath.split('/')[0] +} const loadComponent = () => { + const moduleName = getModuleName() loading.value = true - const byteArray = wsCache.get(`de-plugin-proxy-plugin`) + const byteArray = wsCache.get(`de-plugin-proxy-${moduleName}`) if (byteArray) { importProxy(JSON.parse(byteArray)) loading.value = false return } const key = generateRamStr(randomKey()) - loadPluginApi(key) + const moduleNameKey = window.btoa(moduleName) + const saltKey = `${key},${moduleNameKey}` + loadPluginApi(saltKey) .then(response => { let code = response.data const byteArray = execute(code, key) @@ -82,7 +86,8 @@ const storeCacheProxy = byteArray => { byteArray.forEach(item => { result.push([...item]) }) - wsCache.set(`de-plugin-proxy-plugin`, JSON.stringify(result)) + const moduleName = getModuleName() + wsCache.set(`de-plugin-proxy-${moduleName}`, JSON.stringify(result)) } const pluginProxy = ref(null) const invokeMethod = param => { @@ -104,8 +109,9 @@ onMounted(async () => { distributed = wsCache.get(key) } if (distributed) { - if (window['DEXPack']) { - const xpack = await window['DEXPack'].mapping[attrs.jsname] + const moduleName = getModuleName() + if (window[moduleName]) { + const xpack = await window[moduleName].mapping[attrs.jsname] plugin.value = xpack.default } else { window['Vue'] = Vue @@ -114,9 +120,10 @@ onMounted(async () => { window['vueRouter'] = vueRouter window['MittAll'] = useEmitt().emitter.all window['I18n'] = i18n - loadDistributed().then(async res => { - new Function(res.data)() - const xpack = await window['DEXPack'].mapping[attrs.jsname] + const url = `/xpackComponent/pluginStaticInfo/${moduleName}` + request.get({ url }).then(async res => { + new Function(res.data || res)() + const xpack = await window[moduleName].mapping[attrs.jsname] plugin.value = xpack.default }) } diff --git a/core/core-frontend/src/components/visualization/OuterParamsSet.vue b/core/core-frontend/src/components/visualization/OuterParamsSet.vue index fb0ed6a2e8..717fb98f2f 100644 --- a/core/core-frontend/src/components/visualization/OuterParamsSet.vue +++ b/core/core-frontend/src/components/visualization/OuterParamsSet.vue @@ -618,7 +618,7 @@ defineExpose({ } :deep(.ed-tree--highlight-current .ed-tree-node.is-current > .ed-tree-node__content) { - background-color: #8dbbef !important; + background-color: rgba(51, 112, 255, 0.1) !important; } .tree-content ::deep(.ed-input__inner) { diff --git a/core/core-frontend/src/components/visualization/common/DeUpload.vue b/core/core-frontend/src/components/visualization/common/DeUpload.vue index 8e5d7556a7..25a40d32d4 100644 --- a/core/core-frontend/src/components/visualization/common/DeUpload.vue +++ b/core/core-frontend/src/components/visualization/common/DeUpload.vue @@ -20,7 +20,7 @@ id="input" ref="files" type="file" - accept=".jpeg,.jpg,.png,.gif" + accept=".jpeg,.jpg,.png,.gif,.svg" hidden @click=" e => { diff --git a/core/core-frontend/src/components/visualization/component-background/BackgroundOverallCommon.vue b/core/core-frontend/src/components/visualization/component-background/BackgroundOverallCommon.vue index bc9149a725..9ef4f697a8 100644 --- a/core/core-frontend/src/components/visualization/component-background/BackgroundOverallCommon.vue +++ b/core/core-frontend/src/components/visualization/component-background/BackgroundOverallCommon.vue @@ -4,7 +4,7 @@ id="input" ref="files" type="file" - accept=".jpeg,.jpg,.png,.gif" + accept=".jpeg,.jpg,.png,.gif,.svg" hidden @click=" e => { diff --git a/core/core-frontend/src/components/visualization/component-background/CanvasBackground.vue b/core/core-frontend/src/components/visualization/component-background/CanvasBackground.vue index e046b30527..76c95d8b04 100644 --- a/core/core-frontend/src/components/visualization/component-background/CanvasBackground.vue +++ b/core/core-frontend/src/components/visualization/component-background/CanvasBackground.vue @@ -4,7 +4,7 @@ id="input" ref="files" type="file" - accept=".jpeg,.jpg,.png,.gif" + accept=".jpeg,.jpg,.png,.gif,.svg" hidden @click=" e => { diff --git a/core/core-frontend/src/config/axios/service.ts b/core/core-frontend/src/config/axios/service.ts index cced05ba8a..1f710c179c 100644 --- a/core/core-frontend/src/config/axios/service.ts +++ b/core/core-frontend/src/config/axios/service.ts @@ -173,6 +173,8 @@ service.interceptors.response.use( return response } else if (response.config.url.includes('DEXPack.umd.js')) { return response + } else if (response.config.url.startsWith('/xpackComponent/pluginStaticInfo/extensions-')) { + return response } else { if ( !response?.config?.url.startsWith('/xpackComponent/content') && @@ -269,18 +271,9 @@ const executeVersionHandler = (response: AxiosResponse) => { return } if (executeVersion && executeVersion !== cacheVal) { + wsCache.clear() wsCache.set(key, executeVersion) showMsg('系统有升级,请点击刷新页面', '-sys-upgrade-') - /* ElMessageBox.confirm('系统有升级,请点击刷新页面', { - confirmButtonType: 'primary', - type: 'warning', - confirmButtonText: '刷新', - cancelButtonText: '取消', - autofocus: false, - showClose: false - }).then(() => { - window.location.reload() - }) */ } } export { service, cancelMap } diff --git a/core/core-frontend/src/custom-component/common/DeRuler.vue b/core/core-frontend/src/custom-component/common/DeRuler.vue new file mode 100644 index 0000000000..a12441b9ae --- /dev/null +++ b/core/core-frontend/src/custom-component/common/DeRuler.vue @@ -0,0 +1,219 @@ + + + + + diff --git a/core/core-frontend/src/custom-component/common/DeRulerVertical.vue b/core/core-frontend/src/custom-component/common/DeRulerVertical.vue new file mode 100644 index 0000000000..1c83edb05a --- /dev/null +++ b/core/core-frontend/src/custom-component/common/DeRulerVertical.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/core/core-frontend/src/custom-component/component-group/UserViewGroup.vue b/core/core-frontend/src/custom-component/component-group/UserViewGroup.vue index a29141eaf4..887eeac210 100644 --- a/core/core-frontend/src/custom-component/component-group/UserViewGroup.vue +++ b/core/core-frontend/src/custom-component/component-group/UserViewGroup.vue @@ -48,8 +48,8 @@ const anchorPosition = anchor => { scrollTo(element.offsetTop) } -const newComponent = (innerType, isPlugin) => { - eventBus.emit('handleNew', { componentName: 'UserView', innerType: innerType, isPlugin }) +const newComponent = (innerType, staticMap) => { + eventBus.emit('handleNew', { componentName: 'UserView', innerType: innerType, staticMap }) } const handleDragStart = e => { @@ -66,14 +66,15 @@ const groupActiveChange = category => { } const loadPluginCategory = data => { data.forEach(item => { - const { category, title, render, chartValue, chartTitle, icon } = item + const { category, title, render, chartValue, chartTitle, icon, staticMap } = item const node = { render, category, icon, value: chartValue, title: chartTitle, - isPlugin: true + isPlugin: true, + staticMap } const stack = [...state.chartGroupList] let findParent = false @@ -128,7 +129,7 @@ const loadPluginCategory = data => { :key="chartInfo.title" >
{ if (comp.component === componentName) { @@ -540,6 +541,9 @@ export function findNewComponentFromList( newComponent.label = viewConfig?.title newComponent.render = viewConfig?.render newComponent.isPlugin = !!isPlugin + if (isPlugin) { + newComponent.staticMap = staticMap + } } return newComponent } diff --git a/core/core-frontend/src/custom-component/picture/Attr.vue b/core/core-frontend/src/custom-component/picture/Attr.vue index a118b5e757..aa4c0800b0 100644 --- a/core/core-frontend/src/custom-component/picture/Attr.vue +++ b/core/core-frontend/src/custom-component/picture/Attr.vue @@ -99,7 +99,7 @@ onBeforeUnmount(() => { id="input" ref="files" type="file" - accept=".jpeg,.jpg,.png,.gif" + accept=".jpeg,.jpg,.png,.gif,.svg" hidden @click=" e => { diff --git a/core/core-frontend/src/custom-component/rich-text/DeRichEditor.vue b/core/core-frontend/src/custom-component/rich-text/DeRichEditor.vue index 4a691ab51a..62d56883f7 100644 --- a/core/core-frontend/src/custom-component/rich-text/DeRichEditor.vue +++ b/core/core-frontend/src/custom-component/rich-text/DeRichEditor.vue @@ -19,6 +19,8 @@ import 'tinymce/plugins/table' // 插入表格插件 import 'tinymce/plugins/lists' // 列表插件 import 'tinymce/plugins/wordcount' // 字数统计插件 import 'tinymce/plugins/code' // 源码 +import './plugins' //自定义插件 +import '@npkg/tinymce-plugins/letterspacing' //接下来定义编辑器所需要的插件数据 import { reactive, ref } from 'vue' @@ -49,7 +51,7 @@ const props = defineProps({ toolbar: { type: [String, Array], default: - 'codesample bold italic underline alignleft aligncenter alignright alignjustify | undo redo | formatselect | fontselect | fontsizeselect | forecolor backcolor | bullist numlist outdent indent | lists link table code | removeformat ' + 'codesample bold italic underline alignleft aligncenter alignright alignjustify | undo redo | formatselect | fontselect | fontsizeselect | forecolor backcolor | bullist numlist outdent indent | lists link table code | removeformat letterspacing ' } //必填 }) //用于接收外部传递进来的富文本 diff --git a/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue b/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue index 6c418c4c40..c1ccdf7c0e 100644 --- a/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue +++ b/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue @@ -59,6 +59,8 @@ import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain' import ChartError from '@/views/chart/components/views/components/ChartError.vue' import { useEmitt } from '@/hooks/web/useEmitt' import { valueFormatter } from '@/views/chart/components/js/formatter' +import { parseJson } from '@/views/chart/components/js/util' +import { mappingColor } from '@/views/chart/components/js/panel/common/common_table' const snapshotStore = snapshotStoreWithOut() const errMsg = ref('') const dvMainStore = dvMainStoreWithOut() @@ -103,6 +105,7 @@ const { element, editMode, active, disabled, showPosition } = toRefs(props) const state = reactive({ data: null, + viewDataInfo: null, totalItems: 0 }) const dataRowSelect = ref({}) @@ -150,7 +153,7 @@ watch( () => active.value, val => { if (!val) { - const ed = tinymce.editors[tinymceId] + const ed = window.tinymce.editors[tinymceId] if (canEdit.value) { element.value.propValue.textValue = ed.getContent() } @@ -158,6 +161,7 @@ watch( canEdit.value = false reShow() myValue.value = assignment(element.value.propValue.textValue) + ed.setContent(myValue.value) } } ) @@ -166,7 +170,7 @@ watch( () => myValue.value, () => { if (canEdit.value) { - const ed = tinymce.editors[tinymceId] + const ed = window.tinymce.editors[tinymceId] element.value.propValue.textValue = ed.getContent() } if (initReady.value && canEdit.value) { @@ -216,23 +220,19 @@ const initCurFieldsChange = () => { const assignment = content => { const on = content.match(/\[(.+?)\]/g) if (on) { + const thresholdStyleInfo = conditionAdaptor(state.viewDataInfo) on.forEach(itm => { if (dataRowFiledName.value.includes(itm)) { const ele = itm.slice(1, -1) + let value = dataRowNameSelect.value[ele] !== undefined ? dataRowNameSelect.value[ele] : null + if (value && thresholdStyleInfo && thresholdStyleInfo[ele]) { + const thresholdStyle = thresholdStyleInfo[ele] + value = `${value}` + } if (initReady.value) { - content = content.replace( - itm, - dataRowNameSelect.value[ele] !== undefined - ? dataRowNameSelect.value[ele] - : '[未获取字段值]' - ) + content = content.replace(itm, !!value ? value : '[未获取字段值]') } else { - content = content.replace( - itm, - dataRowNameSelect.value[ele] !== undefined - ? dataRowNameSelect.value[ele] - : '[获取中...]' - ) + content = content.replace(itm, !!value ? value : '[获取中...]') } } }) @@ -245,7 +245,7 @@ const assignment = content => { return content } const fieldSelect = field => { - const ed = tinymce.editors[tinymceId] + const ed = window.tinymce.editors[tinymceId] const fieldId = 'changeText-' + guid() const value = ' - {{ item.chartShowName ? item.chartShowName : item.name }} + {{ item.chartShowName ? item.chartShowName : item.name + }}{{ item.desensitized ? '(已脱敏)' : '' }} diff --git a/core/core-frontend/src/views/chart/components/editor/drag-item/DrillItem.vue b/core/core-frontend/src/views/chart/components/editor/drag-item/DrillItem.vue index 0097636f48..fb4feaba3f 100644 --- a/core/core-frontend/src/views/chart/components/editor/drag-item/DrillItem.vue +++ b/core/core-frontend/src/views/chart/components/editor/drag-item/DrillItem.vue @@ -18,6 +18,10 @@ const props = defineProps({ type: Object, required: true }, + chart: { + type: Object, + required: true + }, index: { type: Number, required: true @@ -41,7 +45,7 @@ const emit = defineEmits(['onDimensionItemRemove']) const { item } = toRefs(props) watch( - [() => props.dimensionData, () => props.item], + [() => props.dimensionData, () => props.item, () => props.chart.type], () => { getItemTagType() }, @@ -70,6 +74,10 @@ const removeItem = () => { emit('onDimensionItemRemove', item.value) } const getItemTagType = () => { + if (props.chart.type !== 'table-info' && props.item.desensitized) { + tagType.value = '#F54A45' + return + } tagType.value = getItemType(props.dimensionData, props.quotaData, props.item) } onMounted(() => { @@ -93,7 +101,9 @@ onMounted(() => { > - {{ item.name }} + {{ item.name }}{{ item.desensitized ? '(已脱敏)' : '' }} diff --git a/core/core-frontend/src/views/chart/components/editor/drag-item/QuotaItem.vue b/core/core-frontend/src/views/chart/components/editor/drag-item/QuotaItem.vue index 4669542aa9..5256db3634 100644 --- a/core/core-frontend/src/views/chart/components/editor/drag-item/QuotaItem.vue +++ b/core/core-frontend/src/views/chart/components/editor/drag-item/QuotaItem.vue @@ -68,7 +68,7 @@ const toolTip = computed(() => { return props.themes === 'dark' ? 'ndark' : 'dark' }) watch( - [() => props.quotaData, () => props.item], + [() => props.quotaData, () => props.item, () => props.chart.type], () => { getItemTagType() }, @@ -214,6 +214,10 @@ const removeItem = () => { } const getItemTagType = () => { + if (props.chart.type !== 'table-info' && props.item.desensitized) { + tagType.value = '#F54A45' + return + } tagType.value = getItemType(props.dimensionData, props.quotaData, props.item) } @@ -304,7 +308,10 @@ onMounted(() => { :content="item.chartShowName ? item.chartShowName : item.name" > - {{ item.chartShowName ? item.chartShowName : item.name }} + {{ item.chartShowName ? item.chartShowName : item.name + }}{{ item.desensitized ? '(已脱敏)' : '' }} ({{ t('chart.' + item.summary) }}) diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue index 6fcbbe8dc2..c647d27039 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/ChartStyle.vue @@ -260,6 +260,7 @@ watch( :themes="themes" :chart="chart" @onLegendChange="onLegendChange" + @onMiscChange="onMiscChange" /> import { computed, onMounted, reactive, watch } from 'vue' import { useI18n } from '@/hooks/web/useI18n' -import { COLOR_PANEL, DEFAULT_LEGEND_STYLE } from '@/views/chart/components/editor/util/chart' -import { ElSpace } from 'element-plus-secondary' +import { + COLOR_PANEL, + DEFAULT_LEGEND_STYLE, + DEFAULT_MISC +} from '@/views/chart/components/editor/util/chart' +import { ElCol, ElRow, ElSpace } from 'element-plus-secondary' +import { cloneDeep } from 'lodash-es' +import { useEmitt } from '@/hooks/web/useEmitt' const { t } = useI18n() @@ -14,8 +20,11 @@ const props = withDefaults( }>(), { themes: 'dark' } ) - -const emit = defineEmits(['onLegendChange']) +useEmitt({ + name: 'map-default-range', + callback: args => mapDefaultRange(args) +}) +const emit = defineEmits(['onLegendChange', 'onMiscChange']) const toolTip = computed(() => { return props.themes === 'dark' ? 'ndark' : 'dark' }) @@ -36,7 +45,15 @@ const iconSymbolOptions = [ ] const state = reactive({ - legendForm: JSON.parse(JSON.stringify(DEFAULT_LEGEND_STYLE)) + legendForm: { + ...JSON.parse(JSON.stringify(DEFAULT_LEGEND_STYLE)), + miscForm: JSON.parse(JSON.stringify(DEFAULT_MISC)) as ChartMiscAttr + } +}) + +const chartType = computed(() => { + const chart = JSON.parse(JSON.stringify(props.chart)) + return chart?.type }) const fontSizeList = computed(() => { @@ -54,6 +71,10 @@ const changeLegendStyle = prop => { emit('onLegendChange', state.legendForm, prop) } +const changeMisc = prop => { + emit('onMiscChange', { data: state.legendForm.miscForm, requestData: true }, prop) +} + const init = () => { const chart = JSON.parse(JSON.stringify(props.chart)) if (chart.customStyle) { @@ -63,13 +84,21 @@ const init = () => { } else { customStyle = JSON.parse(chart.customStyle) } + const miscStyle = cloneDeep(props.chart.customAttr.misc) if (customStyle.legend) { state.legendForm = customStyle.legend + state.legendForm.miscForm = miscStyle } } } const showProperty = prop => props.propertyInner?.includes(prop) - +const mapDefaultRange = args => { + if (args.from === 'map') { + state.legendForm.miscForm.mapLegendMax = args.data.max + state.legendForm.miscForm.mapLegendMin = args.data.min + state.legendForm.miscForm.mapLegendNumber = args.data.legendNumber + } +} onMounted(() => { init() }) @@ -145,6 +174,82 @@ onMounted(() => { + +
+ + + + + {{ t('chart.margin_model_auto') }} + + + + +
+ + + + + + + + + + + + + + + + + + + +
+
+
+ { } const drop = (ev: MouseEvent, type = 'xAxis') => { + let hasSesensitized = false ev.preventDefault() const arr = activeDimension.value.length ? activeDimension.value : activeQuota.value for (let i = 0; i < arr.length; i++) { const obj = cloneDeep(arr[i]) + if (obj.desensitized && view.value.type !== 'table-info') { + hasSesensitized = true + continue + } + state.moveId = obj.id as unknown as number view.value[type].push(obj) const e = { newDraggableIndex: view.value[type].length - 1 } + if ('drillFields' === type) { addDrill(e) } else { addAxis(e, type as AxisType) } } + + if (hasSesensitized) { + ElMessage.error('脱敏字段不能用于制作该图表!') + } } const fieldLoading = ref(false) @@ -1605,7 +1616,7 @@ const deleteChartFieldItem = id => {
{ { if (!areaId) { return } + const sourceData = JSON.parse(JSON.stringify(chart.data?.data || [])) + let data = [] + const { misc } = parseJson(chart.customAttr) + const { legend } = parseJson(chart.customStyle) + // 自定义图例 + if (!misc.mapAutoLegend && legend.show) { + let minValue = misc.mapLegendMin + let maxValue = misc.mapLegendMax + setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => { + maxValue = max + minValue = min + action({ + from: 'map', + data: { + max: maxValue, + min: minValue, + legendNumber: 9 + } + }) + }) + data = filterChartDataByRange(sourceData, maxValue, minValue) + } else { + data = sourceData + } const geoJson = cloneDeep(await getGeoJsonFile(areaId)) let options: ChoroplethOptions = { preserveDrawingBuffer: true, @@ -61,7 +93,7 @@ export class Map extends L7PlotChartView { type: 'geojson' }, source: { - data: chart.data?.data || [], + data: data, joinBy: { sourceField: 'name', geoField: 'name', @@ -125,7 +157,7 @@ export class Map extends L7PlotChartView { ): ChoroplethOptions { const { areaId }: L7PlotDrawOptions = context.drawOption const geoJson: FeatureCollection = context.geoJson - const { basicStyle, label } = parseJson(chart.customAttr) + const { basicStyle, label, misc } = parseJson(chart.customAttr) const senior = parseJson(chart.senior) const curAreaNameMapping = senior.areaMapping?.[areaId] handleGeoJson(geoJson, curAreaNameMapping) @@ -141,7 +173,32 @@ export class Map extends L7PlotChartView { options.label && (options.label.field = 'name') return options } - const data = chart.data.data + const sourceData = JSON.parse(JSON.stringify(chart.data.data)) + const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha)) + const { legend } = parseJson(chart.customStyle) + let data = [] + let colorScale = [] + if (legend.show) { + let minValue = misc.mapLegendMin + let maxValue = misc.mapLegendMax + let mapLegendNumber = misc.mapLegendNumber + setMapChartDefaultMaxAndMinValueByData(sourceData, maxValue, minValue, (max, min) => { + maxValue = max + minValue = min + mapLegendNumber = 9 + }) + // 非自动,过滤数据 + if (!misc.mapAutoLegend) { + data = filterChartDataByRange(sourceData, maxValue, minValue) + } else { + mapLegendNumber = 9 + } + // 定义最大值、最小值、区间数量和对应的颜色 + colorScale = getDynamicColorScale(minValue, maxValue, mapLegendNumber, colors) + } else { + data = sourceData + colorScale = colors + } const areaMap = data.reduce((obj, value) => { obj[value['field']] = value.value return obj @@ -164,12 +221,11 @@ export class Map extends L7PlotChartView { item.properties['_DE_LABEL_'] = content.join('\n\n') } }) - let colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha)) - if (validArea < colors.length) { - colors = colors.slice(0, validArea) + if (validArea < colorScale.length && !misc.mapAutoLegend) { + colorScale = colorScale.map(item => (item.color ? item.color : item)).slice(0, validArea) } - if (colors.length) { - options.color['value'] = colors + if (colorScale.length) { + options.color['value'] = colorScale.map(item => (item.color ? item.color : item)) } return options } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/others/rich-text.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/others/rich-text.ts index 60bf5e236c..3b54e11a33 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/others/rich-text.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/others/rich-text.ts @@ -6,9 +6,10 @@ const { t } = useI18n() * 富文本图表 */ export class RichTextChartView extends AbstractChartView { - properties: EditorProperty[] = ['background-overall-component'] + properties: EditorProperty[] = ['background-overall-component', 'threshold'] propertyInner: EditorPropertyInner = { - 'background-overall-component': ['all'] + 'background-overall-component': ['all'], + threshold: ['tableThreshold'] } axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] axisConfig: AxisConfig = { diff --git a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts index 205e7095a5..f2abee3199 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/common/common_table.ts @@ -458,7 +458,7 @@ export function getConditions(chart: Chart) { return res } -function mappingColor(value, defaultColor, field, type) { +export function mappingColor(value, defaultColor, field, type) { let color for (let i = 0; i < field.conditions.length; i++) { let flag = false diff --git a/core/core-frontend/src/views/chart/components/js/util.ts b/core/core-frontend/src/views/chart/components/js/util.ts index 9bc8765741..6100f516e1 100644 --- a/core/core-frontend/src/views/chart/components/js/util.ts +++ b/core/core-frontend/src/views/chart/components/js/util.ts @@ -527,3 +527,68 @@ export const copyString = (content: string, notify = false) => { } }) } + +/** + * 计算动态区间和颜色 + * @param minValue + * @param maxValue + * @param intervals + * @param colors + */ +export const getDynamicColorScale = ( + minValue: number, + maxValue: number, + intervals: number, + colors: string[] +) => { + const step = (maxValue - minValue) / intervals + + const colorScale = [] + for (let i = 0; i < intervals; i++) { + colorScale.push({ + value: [minValue + i * step, minValue + (i + 1) * step], + color: colors[i], + label: `${(minValue + i * step).toFixed(2)} - ${(minValue + (i + 1) * step).toFixed(2)}` + }) + } + + return colorScale +} +/** + * 过滤掉不在区间的数据 + * @param data + * @param maxValue + * @param minValue + */ +export const filterChartDataByRange = (data: any[], maxValue: number, minValue: number) => { + return data.filter( + item => + item.value === null || + item.value === undefined || + (item.value >= minValue && item.value <= maxValue) + ) +} + +/** + * 获取地图默认最大最小值根据数据 + * @param data + * @param maxValue + * @param minValue + * @param callback + */ +export const setMapChartDefaultMaxAndMinValueByData = ( + data: any[], + maxValue: number, + minValue: number, + callback: (max: number, min: number) => void +) => { + if (minValue === 0 && maxValue === 0) { + const maxResult = data.reduce((max, current) => { + return current.value > max ? current.value : max + }, Number.MIN_SAFE_INTEGER) + const minResult = data.reduce((min, current) => { + return current.value < min ? current.value : min + }, Number.MAX_SAFE_INTEGER) + callback(maxResult, minResult) + } +} diff --git a/core/core-frontend/src/views/chart/components/views/components/ChartComponentG2Plot.vue b/core/core-frontend/src/views/chart/components/views/components/ChartComponentG2Plot.vue index 6549f29d3a..d0564551d6 100644 --- a/core/core-frontend/src/views/chart/components/views/components/ChartComponentG2Plot.vue +++ b/core/core-frontend/src/views/chart/components/views/components/ChartComponentG2Plot.vue @@ -287,6 +287,10 @@ const pointClickTrans = () => { } const action = param => { + if (param.from === 'map') { + emitter.emit('map-default-range', param) + return + } state.pointParam = param.data // 点击 pointClickTrans() diff --git a/core/core-frontend/src/views/chart/components/views/index.vue b/core/core-frontend/src/views/chart/components/views/index.vue index 3dd5a3995d..73cefa64fa 100644 --- a/core/core-frontend/src/views/chart/components/views/index.vue +++ b/core/core-frontend/src/views/chart/components/views/index.vue @@ -762,7 +762,7 @@ const showActionIcons = computed(() => {
diff --git a/core/core-frontend/src/views/share/share/ShareVisualHead.vue b/core/core-frontend/src/views/share/share/ShareVisualHead.vue index 5399827c96..61f1989d10 100644 --- a/core/core-frontend/src/views/share/share/ShareVisualHead.vue +++ b/core/core-frontend/src/views/share/share/ShareVisualHead.vue @@ -5,7 +5,7 @@ width="480" placement="bottom-start" :show-arrow="false" - popper-class="share-popover" + :popper-class="`share-popover ${showTicket ? 'share-ticket-popover' : ''}`" @show="share" > -