forked from github/dataease
Merge branch 'dev-v2' into pr@dev-v2@refactor_recommond-template
This commit is contained in:
commit
028f6170bb
@ -13,10 +13,11 @@ WORKDIR /opt/apps
|
||||
ADD core/core-backend/target/CoreApplication.jar /opt/apps/app.jar
|
||||
ADD de-xpack/xpack-permissions/target/xpack-permissions-$IMAGE_TAG.jar /opt/apps/xpack-permission.jar
|
||||
ADD de-xpack/xpack-base/target/xpack-base-$IMAGE_TAG.jar /opt/apps/xpack-base.jar
|
||||
ADD de-xpack/xpack-sync/target/xpack-sync-$IMAGE_TAG.jar /opt/apps/xpack-sync.jar
|
||||
|
||||
ENV JAVA_APP_JAR=/opt/apps/app.jar
|
||||
|
||||
HEALTHCHECK --interval=15s --timeout=5s --retries=20 --start-period=30s CMD curl -f 127.0.0.1:8100
|
||||
|
||||
|
||||
CMD java -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/dataease2.0/logs/dump.hprof -Dloader.path=/opt/apps/xpack-permission.jar,/opt/apps/xpack-base.jar -jar /opt/apps/app.jar
|
||||
CMD java -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/dataease2.0/logs/dump.hprof -Dloader.path=/opt/apps/xpack-permission.jar,/opt/apps/xpack-base.jar,/opt/apps/xpack-sync.jar -jar /opt/apps/app.jar
|
||||
|
@ -23,6 +23,11 @@
|
||||
<artifactId>api-permissions</artifactId>
|
||||
<version>${dataease.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dataease</groupId>
|
||||
<artifactId>api-sync</artifactId>
|
||||
<version>${dataease.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
|
@ -60,12 +60,13 @@ public class SqlparserUtils {
|
||||
}
|
||||
|
||||
private static void getDependencies(SqlNode sqlNode, Boolean fromOrJoin) {
|
||||
|
||||
if (sqlNode == null) {
|
||||
return;
|
||||
}
|
||||
if (sqlNode.getKind() == JOIN) {
|
||||
SqlJoin sqlKind = (SqlJoin) sqlNode;
|
||||
|
||||
} else if (sqlNode.getKind() == IDENTIFIER) {
|
||||
|
||||
} else if (sqlNode.getKind() == AS) {
|
||||
SqlBasicCall sqlKind = (SqlBasicCall) sqlNode;
|
||||
} else if (sqlNode.getKind() == SELECT) {
|
||||
@ -80,9 +81,19 @@ public class SqlparserUtils {
|
||||
SqlNode newWhere = sqlKind.getWhere().accept(getSqlShuttle());
|
||||
sqlKind.setWhere(newWhere);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO 这里可根据需求拓展处理其他类型的 sqlNode
|
||||
} else if (sqlNode.getKind() == ORDER_BY) {
|
||||
SqlOrderBy sqlKind = (SqlOrderBy) sqlNode;
|
||||
List<SqlNode> operandList = sqlKind.getOperandList();
|
||||
for (int i = 0; i < operandList.size(); i++) {
|
||||
getDependencies(operandList.get(i), false);
|
||||
}
|
||||
} else if (sqlNode.getKind() == UNION) {
|
||||
SqlBasicCall sqlKind = (SqlBasicCall) sqlNode;
|
||||
if (sqlKind.getOperandList().size() >= 2) {
|
||||
for (int i = 0; i < sqlKind.getOperandList().size(); i++) {
|
||||
getDependencies(sqlKind.getOperandList().get(i), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,9 @@ public class DatasourceServer implements DatasourceApi {
|
||||
dataSourceManage.move(dataSourceDTO);
|
||||
}
|
||||
case "rename" -> {
|
||||
if(StringUtils.isEmpty(dataSourceDTO.getName())){
|
||||
DEException.throwException("名称不能为空!");
|
||||
}
|
||||
CoreDatasource datasource = datasourceMapper.selectById(dataSourceDTO.getId());
|
||||
datasource.setName(dataSourceDTO.getName());
|
||||
dataSourceManage.innerEdit(datasource);
|
||||
@ -464,8 +467,9 @@ public class DatasourceServer implements DatasourceApi {
|
||||
CoreDatasource coreDatasource = new CoreDatasource();
|
||||
BeanUtils.copyBean(coreDatasource, dataSourceDTO);
|
||||
checkDatasourceStatus(coreDatasource);
|
||||
dataSourceDTO.setStatus(coreDatasource.getStatus());
|
||||
return dataSourceDTO;
|
||||
DatasourceDTO result = new DatasourceDTO();
|
||||
result.setStatus(coreDatasource.getStatus());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,150 @@
|
||||
package io.dataease.share.dao.auto.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author fit2cloud
|
||||
* @since 2023-09-22
|
||||
*/
|
||||
@TableName("xpack_share")
|
||||
public class XpackShare implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Long creator;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Long time;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Long exp;
|
||||
|
||||
/**
|
||||
* uuid
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String pwd;
|
||||
|
||||
/**
|
||||
* 资源ID
|
||||
*/
|
||||
private Long resourceId;
|
||||
|
||||
/**
|
||||
* 组织ID
|
||||
*/
|
||||
private Long oid;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
public void setCreator(Long creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public Long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(Long time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public Long getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public void setExp(Long exp) {
|
||||
this.exp = exp;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getPwd() {
|
||||
return pwd;
|
||||
}
|
||||
|
||||
public void setPwd(String pwd) {
|
||||
this.pwd = pwd;
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(Long resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public Long getOid() {
|
||||
return oid;
|
||||
}
|
||||
|
||||
public void setOid(Long oid) {
|
||||
this.oid = oid;
|
||||
}
|
||||
|
||||
public Integer getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Integer type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "XpackShare{" +
|
||||
"id = " + id +
|
||||
", creator = " + creator +
|
||||
", time = " + time +
|
||||
", exp = " + exp +
|
||||
", uuid = " + uuid +
|
||||
", pwd = " + pwd +
|
||||
", resourceId = " + resourceId +
|
||||
", oid = " + oid +
|
||||
", type = " + type +
|
||||
"}";
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.dataease.share.dao.auto.mapper;
|
||||
|
||||
import io.dataease.share.dao.auto.entity.XpackShare;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author fit2cloud
|
||||
* @since 2023-09-22
|
||||
*/
|
||||
@Mapper
|
||||
public interface XpackShareMapper extends BaseMapper<XpackShare> {
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package io.dataease.share.dao.ext.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
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;
|
||||
|
||||
@Mapper
|
||||
public interface XpackShareExtMapper {
|
||||
|
||||
@Select("""
|
||||
select
|
||||
s.id as share_id,
|
||||
v.id as resource_id,
|
||||
v.type,
|
||||
s.creator,
|
||||
s.time,
|
||||
s.exp,
|
||||
v.name
|
||||
from xpack_share s
|
||||
left join data_visualization_info v on s.resource_id = v.id
|
||||
${ew.customSqlSegment}
|
||||
""")
|
||||
IPage<XpackSharePO> query(IPage<XpackSharePO> page, @Param("ew") QueryWrapper<Object> ew);
|
||||
|
||||
@Select("select type from data_visualization_info where id = #{id}")
|
||||
String visualizationType(@Param("id") Long id);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.dataease.share.dao.ext.po;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class XpackSharePO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7929343371768885789L;
|
||||
|
||||
private Long shareId;
|
||||
|
||||
private Long resourceId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
private Long creator;
|
||||
|
||||
private Long time;
|
||||
|
||||
private Long exp;
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
package io.dataease.share.manage;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.dataease.api.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.vo.XpackShareGridVO;
|
||||
import io.dataease.api.xpack.share.vo.XpackShareProxyVO;
|
||||
import io.dataease.auth.bo.TokenUserBO;
|
||||
import io.dataease.constant.AuthConstant;
|
||||
import io.dataease.constant.BusiResourceEnum;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.license.config.XpackInteract;
|
||||
import io.dataease.share.dao.auto.mapper.XpackShareMapper;
|
||||
import io.dataease.utils.*;
|
||||
import io.dataease.share.dao.auto.entity.XpackShare;
|
||||
import io.dataease.share.dao.ext.mapper.XpackShareExtMapper;
|
||||
import io.dataease.share.dao.ext.po.XpackSharePO;
|
||||
import io.dataease.share.util.LinkTokenUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component("xpackShareManage")
|
||||
public class XpackShareManage {
|
||||
|
||||
@Resource(name = "xpackShareMapper")
|
||||
private XpackShareMapper xpackShareMapper;
|
||||
|
||||
@Resource(name = "xpackShareExtMapper")
|
||||
private XpackShareExtMapper xpackShareExtMapper;
|
||||
|
||||
public XpackShare queryByResource(Long resourceId) {
|
||||
Long userId = AuthUtils.getUser().getUserId();
|
||||
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("creator", userId);
|
||||
queryWrapper.eq("resource_id", resourceId);
|
||||
return xpackShareMapper.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
public void switcher(Long resourceId) {
|
||||
XpackShare originData = queryByResource(resourceId);
|
||||
if (ObjectUtils.isNotEmpty(originData)) {
|
||||
xpackShareMapper.deleteById(originData.getId());
|
||||
return;
|
||||
}
|
||||
TokenUserBO user = AuthUtils.getUser();
|
||||
Long userId = user.getUserId();
|
||||
XpackShare xpackShare = new XpackShare();
|
||||
xpackShare.setId(IDUtils.snowID());
|
||||
xpackShare.setCreator(userId);
|
||||
xpackShare.setTime(System.currentTimeMillis());
|
||||
xpackShare.setResourceId(resourceId);
|
||||
xpackShare.setUuid(RandomStringUtils.randomAlphanumeric(8));
|
||||
xpackShare.setOid(user.getDefaultOid());
|
||||
String dType = xpackShareExtMapper.visualizationType(resourceId);
|
||||
xpackShare.setType(StringUtils.equalsIgnoreCase("dataV", dType) ? 2 : 1);
|
||||
xpackShareMapper.insert(xpackShare);
|
||||
}
|
||||
|
||||
public void editExp(Long resourceId, Long exp) {
|
||||
XpackShare originData = queryByResource(resourceId);
|
||||
if (ObjectUtils.isEmpty(originData)) {
|
||||
DEException.throwException("share instance not exist");
|
||||
}
|
||||
originData.setExp(exp);
|
||||
if (ObjectUtils.isEmpty(exp)) {
|
||||
originData.setExp(0L);
|
||||
}
|
||||
xpackShareMapper.updateById(originData);
|
||||
}
|
||||
|
||||
public void editPwd(Long resourceId, String pwd) {
|
||||
XpackShare originData = queryByResource(resourceId);
|
||||
if (ObjectUtils.isEmpty(originData)) {
|
||||
DEException.throwException("share instance not exist");
|
||||
}
|
||||
originData.setPwd(pwd);
|
||||
xpackShareMapper.updateById(originData);
|
||||
}
|
||||
|
||||
public IPage<XpackSharePO> querySharePage(int goPage, int pageSize, VisualizationWorkbranchQueryRequest request) {
|
||||
Long uid = AuthUtils.getUser().getUserId();
|
||||
QueryWrapper<Object> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("s.creator", uid);
|
||||
if (StringUtils.isNotBlank(request.getType())) {
|
||||
BusiResourceEnum busiResourceEnum = BusiResourceEnum.valueOf(request.getType().toUpperCase());
|
||||
if (ObjectUtils.isEmpty(busiResourceEnum)) {
|
||||
DEException.throwException("type is invalid");
|
||||
}
|
||||
String resourceType = convertResourceType(request.getType());
|
||||
if (StringUtils.isNotBlank(resourceType)) {
|
||||
queryWrapper.eq("v.type", resourceType);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(request.getKeyword())) {
|
||||
queryWrapper.like("v.name", request.getKeyword());
|
||||
}
|
||||
queryWrapper.orderBy(true, request.isAsc(), "s.time");
|
||||
Page<XpackSharePO> page = new Page<>(goPage, pageSize);
|
||||
return xpackShareExtMapper.query(page, queryWrapper);
|
||||
}
|
||||
|
||||
private String convertResourceType(String busiFlag) {
|
||||
return switch (busiFlag) {
|
||||
case "panel" -> "dashboard";
|
||||
case "screen" -> "dataV";
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
@XpackInteract(value = "perFilterShareManage", recursion = true)
|
||||
public IPage<XpackShareGridVO> query(int pageNum, int pageSize, VisualizationWorkbranchQueryRequest request) {
|
||||
IPage<XpackSharePO> poiPage = proxy().querySharePage(pageNum, pageSize, request);
|
||||
List<XpackShareGridVO> vos = proxy().formatResult(poiPage.getRecords());
|
||||
IPage<XpackShareGridVO> ipage = new Page<>();
|
||||
ipage.setSize(poiPage.getSize());
|
||||
ipage.setCurrent(poiPage.getCurrent());
|
||||
ipage.setPages(poiPage.getPages());
|
||||
ipage.setTotal(poiPage.getTotal());
|
||||
ipage.setRecords(vos);
|
||||
return ipage;
|
||||
}
|
||||
|
||||
public List<XpackShareGridVO> formatResult(List<XpackSharePO> pos) {
|
||||
if (CollectionUtils.isEmpty(pos)) return new ArrayList<>();
|
||||
return pos.stream().map(po ->
|
||||
new XpackShareGridVO(
|
||||
po.getShareId(), po.getResourceId(), po.getName(), po.getCreator().toString(),
|
||||
po.getTime(), po.getExp(), 9)).toList();
|
||||
}
|
||||
|
||||
private XpackShareManage proxy() {
|
||||
return CommonBeanFactory.getBean(this.getClass());
|
||||
}
|
||||
|
||||
public XpackShareProxyVO proxyInfo(XpackShareProxyRequest request) {
|
||||
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("uuid", request.getUuid());
|
||||
XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper);
|
||||
if (ObjectUtils.isEmpty(xpackShare))
|
||||
return null;
|
||||
String linkToken = LinkTokenUtil.generate(xpackShare.getCreator(), xpackShare.getResourceId(), xpackShare.getExp(), xpackShare.getPwd(), xpackShare.getOid());
|
||||
HttpServletResponse response = ServletUtils.response();
|
||||
response.addHeader(AuthConstant.LINK_TOKEN_KEY, linkToken);
|
||||
Integer type = xpackShare.getType();
|
||||
String typeText = (ObjectUtils.isNotEmpty(type) && type == 1) ? "dashboard" : "dataV";
|
||||
return new XpackShareProxyVO(xpackShare.getResourceId(), xpackShare.getCreator(), linkExp(xpackShare), pwdValid(xpackShare, request.getCiphertext()), typeText);
|
||||
}
|
||||
|
||||
private boolean linkExp(XpackShare xpackShare) {
|
||||
if (ObjectUtils.isEmpty(xpackShare.getExp()) || xpackShare.getExp().equals(0L)) return false;
|
||||
return System.currentTimeMillis() > xpackShare.getExp();
|
||||
}
|
||||
|
||||
private boolean pwdValid(XpackShare xpackShare, String ciphertext) {
|
||||
if (StringUtils.isBlank(xpackShare.getPwd())) return true;
|
||||
if (StringUtils.isBlank(ciphertext)) return false;
|
||||
String text = RsaUtils.decryptStr(ciphertext);
|
||||
int len = text.length();
|
||||
int splitIndex = len - 4;
|
||||
String pwd = text.substring(splitIndex);
|
||||
String uuid = text.substring(0, splitIndex);
|
||||
return StringUtils.equals(xpackShare.getUuid(), uuid) && StringUtils.equals(xpackShare.getPwd(), pwd);
|
||||
}
|
||||
|
||||
public boolean validatePwd(XpackSharePwdValidator validator) {
|
||||
String ciphertext = RsaUtils.decryptStr(validator.getCiphertext());
|
||||
int len = ciphertext.length();
|
||||
int splitIndex = len - 4;
|
||||
String pwd = ciphertext.substring(splitIndex);
|
||||
String uuid = ciphertext.substring(0, splitIndex);
|
||||
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("uuid", uuid);
|
||||
XpackShare xpackShare = xpackShareMapper.selectOne(queryWrapper);
|
||||
return StringUtils.equals(xpackShare.getUuid(), uuid) && StringUtils.equals(xpackShare.getPwd(), pwd);
|
||||
}
|
||||
|
||||
public Map<String, String> queryRelationByUserId(Long uid) {
|
||||
QueryWrapper<XpackShare> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("creator", uid);
|
||||
List<XpackShare> result = xpackShareMapper.selectList(queryWrapper);
|
||||
if (CollectionUtils.isNotEmpty(result)) {
|
||||
return result.stream()
|
||||
.collect(Collectors.toMap(xpackShare -> String.valueOf(xpackShare.getResourceId()), XpackShare::getUuid));
|
||||
}
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package io.dataease.share.server;
|
||||
|
||||
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
|
||||
import io.dataease.api.xpack.share.XpackShareApi;
|
||||
import io.dataease.api.xpack.share.request.XpackShareExpRequest;
|
||||
import io.dataease.api.xpack.share.request.XpackShareProxyRequest;
|
||||
import io.dataease.api.xpack.share.request.XpackSharePwdRequest;
|
||||
import io.dataease.api.xpack.share.request.XpackSharePwdValidator;
|
||||
import io.dataease.api.xpack.share.vo.XpackShareGridVO;
|
||||
import io.dataease.api.xpack.share.vo.XpackShareProxyVO;
|
||||
import io.dataease.api.xpack.share.vo.XpackShareVO;
|
||||
import io.dataease.utils.BeanUtils;
|
||||
import io.dataease.share.dao.auto.entity.XpackShare;
|
||||
import io.dataease.share.manage.XpackShareManage;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RequestMapping("/share")
|
||||
@RestController
|
||||
public class XpackShareServer implements XpackShareApi {
|
||||
|
||||
@Resource(name = "xpackShareManage")
|
||||
private XpackShareManage xpackShareManage;
|
||||
@Override
|
||||
public boolean status(Long resourceId) {
|
||||
return ObjectUtils.isNotEmpty(xpackShareManage.queryByResource(resourceId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switcher(Long resourceId) {
|
||||
xpackShareManage.switcher(resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editExp(XpackShareExpRequest request) {
|
||||
xpackShareManage.editExp(request.getResourceId(), request.getExp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editPwd(XpackSharePwdRequest request) {
|
||||
xpackShareManage.editPwd(request.getResourceId(), request.getPwd());
|
||||
}
|
||||
|
||||
@Override
|
||||
public XpackShareVO detail(Long resourceId) {
|
||||
XpackShare xpackShare = xpackShareManage.queryByResource(resourceId);
|
||||
if (ObjectUtils.isEmpty(xpackShare)) return null;
|
||||
return BeanUtils.copyBean(new XpackShareVO(), xpackShare);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<XpackShareGridVO> query(VisualizationWorkbranchQueryRequest request) {
|
||||
return xpackShareManage.query(1, 20, request).getRecords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public XpackShareProxyVO proxyInfo(XpackShareProxyRequest request) {
|
||||
return xpackShareManage.proxyInfo(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validatePwd(XpackSharePwdValidator validator) {
|
||||
return xpackShareManage.validatePwd(validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> queryRelationByUserId(@PathVariable("uid") Long uid) {
|
||||
return xpackShareManage.queryRelationByUserId(uid);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.dataease.share.util;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.JWTCreator;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class LinkTokenUtil {
|
||||
private static final String defaultPwd = "link-pwd-fit2cloud";
|
||||
public static String generate(Long uid, Long resourceId, Long exp, String pwd, Long oid) {
|
||||
pwd = StringUtils.isBlank(pwd) ? defaultPwd : pwd;
|
||||
Algorithm algorithm = Algorithm.HMAC256(pwd);
|
||||
JWTCreator.Builder builder = JWT.create();
|
||||
builder.withClaim("uid", uid).withClaim("resourceId", resourceId).withClaim("oid", oid);
|
||||
if (ObjectUtils.isNotEmpty(exp) && !exp.equals(0L)) {
|
||||
builder = builder.withExpiresAt(new Date(exp));
|
||||
}
|
||||
return builder.sign(algorithm);
|
||||
}
|
||||
}
|
@ -3,10 +3,12 @@ package io.dataease.system.server;
|
||||
import io.dataease.api.system.SysParameterApi;
|
||||
import io.dataease.api.system.request.OnlineMapEditor;
|
||||
import io.dataease.api.system.vo.SettingItemVO;
|
||||
import io.dataease.constant.XpackSettingConstants;
|
||||
import io.dataease.system.dao.auto.entity.CoreSysSetting;
|
||||
import io.dataease.system.manage.SysParameterManage;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@ -18,6 +20,7 @@ public class SysParameterServer implements SysParameterApi {
|
||||
|
||||
@Resource
|
||||
private SysParameterManage sysParameterManage;
|
||||
|
||||
@Override
|
||||
public String singleVal(String key) {
|
||||
return sysParameterManage.singleVal(key);
|
||||
@ -45,4 +48,17 @@ public class SysParameterServer implements SysParameterApi {
|
||||
public void saveBasicSetting(List<SettingItemVO> settingItemVOS) {
|
||||
sysParameterManage.saveBasic(settingItemVOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer RequestTimeOut() {
|
||||
Integer frontTimeOut = 60;
|
||||
List<SettingItemVO> settingItemVOS = queryBasicSetting();
|
||||
for (int i = 0; i < settingItemVOS.size(); i++) {
|
||||
SettingItemVO settingItemVO = settingItemVOS.get(i);
|
||||
if (StringUtils.isNotBlank(settingItemVO.getPkey()) && settingItemVO.getPkey().equalsIgnoreCase(XpackSettingConstants.Front_Time_Out) && StringUtils.isNotBlank(settingItemVO.getPval())) {
|
||||
frontTimeOut = Integer.parseInt(settingItemVO.getPval());
|
||||
}
|
||||
}
|
||||
return frontTimeOut;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ quartz:
|
||||
|
||||
dataease:
|
||||
version: '@project.version@'
|
||||
origin-list: http://192.168.2.70:9080
|
||||
apisix-api:
|
||||
domain: http://192.168.0.121:9180
|
||||
key: edd1c9f034335f136f87ad84b625c8f1
|
||||
|
@ -15,3 +15,4 @@ ALTER TABLE `visualization_template`
|
||||
ADD COLUMN `use_count` int NULL DEFAULT 0 COMMENT '使用次数' AFTER `dynamic_data`;
|
||||
|
||||
update visualization_template set use_count = 0;
|
||||
INSERT INTO `core_sys_setting` (`id`, `pkey`, `pval`, `type`, `sort`) VALUES (9, 'basic.frontTimeOut', '60', 'text', 1);
|
||||
|
@ -1,50 +1,5 @@
|
||||
ALTER TABLE `QRTZ_BLOB_TRIGGERS` COMMENT = '自定义触发器存储(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_CALENDARS` COMMENT = 'Quartz日历(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_CRON_TRIGGERS` COMMENT = 'CronTrigger存储(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_FIRED_TRIGGERS` COMMENT = '存储已经触发的trigger相关信息(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_JOB_DETAILS` COMMENT = '存储jobDetails信息(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_LOCKS` COMMENT = 'Quartz锁表,为多个节点调度提供分布式锁(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_PAUSED_TRIGGER_GRPS` COMMENT = '存放暂停掉的触发器(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_SCHEDULER_STATE` COMMENT = '存储所有节点的scheduler(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_SIMPLE_TRIGGERS` COMMENT = 'SimpleTrigger存储(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_SIMPROP_TRIGGERS` COMMENT = '存储CalendarIntervalTrigger和DailyTimeIntervalTrigger两种类型的触发器(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `QRTZ_TRIGGERS` COMMENT = '存储定义的trigger(开源作业调度框架Quartz)';
|
||||
ALTER TABLE `area` COMMENT = '地图区域表';
|
||||
ALTER TABLE `core_area_custom` COMMENT = '自定义地图区域信息表';
|
||||
ALTER TABLE `core_chart_view` COMMENT = '组件视图表';
|
||||
ALTER TABLE `core_dataset_group` COMMENT = '数据集分组表';
|
||||
ALTER TABLE `core_dataset_table` COMMENT = 'table数据集';
|
||||
ALTER TABLE `core_dataset_table_field` COMMENT = 'table数据集表字段';
|
||||
ALTER TABLE `core_dataset_table_sql_log` COMMENT = 'table数据集查询sql日志';
|
||||
ALTER TABLE `core_datasource` COMMENT = '数据源表';
|
||||
ALTER TABLE `core_datasource_task` COMMENT = '数据源定时同步任务';
|
||||
ALTER TABLE `core_datasource_task_log` COMMENT = '数据源定时同步任务执行日志';
|
||||
ALTER TABLE `core_de_engine` COMMENT = '数据引擎';
|
||||
ALTER TABLE `core_driver` COMMENT = '驱动';
|
||||
ALTER TABLE `core_driver_jar` COMMENT = '驱动详情';
|
||||
ALTER TABLE `core_menu` COMMENT = '路由菜单';
|
||||
ALTER TABLE `core_opt_recent` COMMENT = '可视化资源表';
|
||||
ALTER TABLE `core_rsa` COMMENT = 'rsa 密钥表';
|
||||
ALTER TABLE `core_store` COMMENT = '用户收藏表';
|
||||
ALTER TABLE `core_sys_setting` COMMENT = '系统设置表';
|
||||
ALTER TABLE `data_visualization_info` COMMENT = '可视化大屏信息表';
|
||||
ALTER TABLE `de_standalone_version` COMMENT = '数据库版本变更记录表';
|
||||
ALTER TABLE `license` COMMENT = '企业版许可证信息表';
|
||||
ALTER TABLE `per_api_key` COMMENT = 'API Key 密钥表';
|
||||
ALTER TABLE `per_auth_busi_role` COMMENT = '角色资源权限配置';
|
||||
ALTER TABLE `per_auth_busi_user` COMMENT = '用户资源权限配置';
|
||||
ALTER TABLE `per_auth_menu` COMMENT = '菜单资源权限配置';
|
||||
ALTER TABLE `per_busi_resource` COMMENT = '企业资源';
|
||||
ALTER TABLE `per_dataset_column_permissions` COMMENT = '数据集列权限';
|
||||
ALTER TABLE `per_dataset_row_permissions_tree` COMMENT = '数据集行权限';
|
||||
ALTER TABLE `per_embedded_instance` COMMENT = '嵌入式应用';
|
||||
ALTER TABLE `per_menu_resource` COMMENT = '菜单资源';
|
||||
ALTER TABLE `per_org` COMMENT = '组织机构';
|
||||
ALTER TABLE `per_role` COMMENT = '角色';
|
||||
ALTER TABLE `per_sys_setting` COMMENT = '系统设置表';
|
||||
ALTER TABLE `per_user` COMMENT = '用户表';
|
||||
ALTER TABLE `per_user_role` COMMENT = '用户角色表';
|
||||
ALTER TABLE `visualization_background` COMMENT = '边框背景表';
|
||||
/*
|
||||
ALTER TABLE `visualization_background_image` COMMENT = '背景图';
|
||||
ALTER TABLE `visualization_link_jump` COMMENT = '跳转记录表';
|
||||
ALTER TABLE `visualization_link_jump_info` COMMENT = '跳转配置表';
|
||||
@ -53,8 +8,6 @@ ALTER TABLE `visualization_linkage` COMMENT = '联动记录表';
|
||||
ALTER TABLE `visualization_linkage_field` COMMENT = '联动字段';
|
||||
ALTER TABLE `visualization_subject` COMMENT = '主题表';
|
||||
ALTER TABLE `visualization_template_extend_data` COMMENT = '模板视图明细信息表';
|
||||
ALTER TABLE `xpack_setting_authentication` COMMENT = '认证设置';
|
||||
ALTER TABLE `xpack_share` COMMENT = '公共链接';
|
||||
|
||||
ALTER TABLE `core_dataset_group`
|
||||
MODIFY COLUMN `qrtz_instance` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'Quartz 实例 ID' AFTER `create_time`;
|
||||
@ -91,20 +44,7 @@ ALTER TABLE `de_standalone_version`
|
||||
MODIFY COLUMN `execution_time` int(0) NOT NULL COMMENT '执行时长' AFTER `installed_on`,
|
||||
MODIFY COLUMN `success` tinyint(1) NOT NULL COMMENT '状态(1-成功,0-失败)' AFTER `execution_time`;
|
||||
|
||||
ALTER TABLE `license`
|
||||
MODIFY COLUMN `id` bigint(0) NOT NULL COMMENT '主键' FIRST,
|
||||
MODIFY COLUMN `update_time` bigint(0) NULL DEFAULT NULL COMMENT '更新时间' AFTER `id`,
|
||||
MODIFY COLUMN `license` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT 'license' AFTER `update_time`,
|
||||
MODIFY COLUMN `f2c_license` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT 'F2C License' AFTER `license`;
|
||||
|
||||
ALTER TABLE `per_dataset_column_permissions`
|
||||
MODIFY COLUMN `update_time` bigint(0) NULL DEFAULT NULL COMMENT '更新时间' AFTER `white_list_user`;
|
||||
|
||||
ALTER TABLE `per_dataset_row_permissions_tree`
|
||||
MODIFY COLUMN `update_time` bigint(0) NULL DEFAULT NULL COMMENT '更新时间' AFTER `white_list_dept`;
|
||||
|
||||
ALTER TABLE `per_user`
|
||||
MODIFY COLUMN `pwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码' AFTER `account`;
|
||||
|
||||
ALTER TABLE `visualization_background`
|
||||
MODIFY COLUMN `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键' FIRST,
|
||||
@ -175,3 +115,5 @@ ALTER TABLE `visualization_template_extend_data`
|
||||
|
||||
ALTER TABLE `core_opt_recent`
|
||||
MODIFY COLUMN `resource_type` int(0) NOT NULL COMMENT '资源类型 1-可视化资源 2-仪表板 3-数据大屏 4-数据集 5-数据源 6-模板' AFTER `uid`;
|
||||
|
||||
*/
|
||||
|
@ -15,3 +15,4 @@ ALTER TABLE `visualization_template`
|
||||
ADD COLUMN `use_count` int NULL DEFAULT 0 COMMENT '使用次数' AFTER `dynamic_data`;
|
||||
|
||||
update visualization_template set use_count = 0;
|
||||
INSERT INTO `core_sys_setting` (`id`, `pkey`, `pval`, `type`, `sort`) VALUES (9, 'basic.frontTimeOut', '60', 'text', 1);
|
||||
|
@ -20,7 +20,7 @@ i18n_menu.org=\u7EC4\u7EC7\u7BA1\u7406
|
||||
i18n_menu.auth=\u6743\u9650\u914D\u7F6E
|
||||
i18n_menu.sync=\u540C\u6B65\u7BA1\u7406
|
||||
i18n_menu.summary=\u6982\u89C8
|
||||
i18n_menu.ds=\u6570\u636E\u6E90\u7BA1\u7406
|
||||
i18n_menu.ds=\u6570\u636e\u8fde\u63a5\u7ba1\u7406
|
||||
i18n_menu.task=\u4EFB\u52A1\u7BA1\u7406
|
||||
i18n_menu.embedded=\u5D4C\u5165\u5F0F\u7BA1\u7406
|
||||
i18n_menu.platform=\u5E73\u53F0\u5BF9\u63A5
|
||||
|
@ -111,6 +111,14 @@ export const getDatasetTree = async (data = {}): Promise<IResponse> => {
|
||||
})
|
||||
}
|
||||
|
||||
export const getDsTree = async (data = {}): Promise<IResponse> => {
|
||||
return request
|
||||
.post({ url: '/datasource/tree', data: { ...data, ...{ busiFlag: 'datasource' } } })
|
||||
.then(res => {
|
||||
return res?.data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteById = (id: number) => request.get({ url: '/datasource/delete/' + id })
|
||||
|
||||
export const getById = (id: number) => request.get({ url: '/datasource/get/' + id })
|
||||
|
47
core/core-frontend/src/api/sync/syncDatasource.ts
Normal file
47
core/core-frontend/src/api/sync/syncDatasource.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const sourceDsPageApi = (page: number, limit: number, data) => {
|
||||
return request.post({ url: `/sync/datasource/source/pager/${page}/${limit}`, data })
|
||||
}
|
||||
|
||||
export const targetDsPageApi = (page: number, limit: number, data) => {
|
||||
return request.post({ url: `/sync/datasource/target/pager/${page}/${limit}`, data })
|
||||
}
|
||||
export const latestUseApi = () => {
|
||||
return request.post({ url: '/sync/datasource/latestUse', data: {} })
|
||||
}
|
||||
|
||||
export const validateApi = data => {
|
||||
return request.post({ url: '/sync/datasource/validate', data })
|
||||
}
|
||||
|
||||
export const getSchemaApi = data => {
|
||||
return request.post({ url: '/sync/datasource/getSchema', data })
|
||||
}
|
||||
|
||||
export const saveApi = data => {
|
||||
return request.post({ url: '/sync/datasource/save', data })
|
||||
}
|
||||
|
||||
export const getByIdApi = (id: string) => {
|
||||
return request.get({ url: `/sync/datasource/get/${id}` })
|
||||
}
|
||||
|
||||
export const updateApi = data => {
|
||||
return request.post({ url: '/sync/datasource/update', data })
|
||||
}
|
||||
|
||||
export const deleteByIdApi = (id: string) => {
|
||||
return request.post({ url: `/sync/datasource/delete/${id}` })
|
||||
}
|
||||
|
||||
export const batchDelApi = (ids: string[]) => {
|
||||
return request.post({ url: `/sync/datasource/batchDel`, data: ids })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取源数据库字段集合以及目标数据库数据类型集合
|
||||
*/
|
||||
export const getFieldListApi = data => {
|
||||
return request.post({ url: `/sync/datasource/fields`, data })
|
||||
}
|
26
core/core-frontend/src/api/sync/syncSummary.ts
Normal file
26
core/core-frontend/src/api/sync/syncSummary.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
interface IResourceCount {
|
||||
jobCount: number
|
||||
datasourceCount: number
|
||||
jobLogCount: number
|
||||
}
|
||||
|
||||
export const getResourceCount = () => {
|
||||
return request
|
||||
.get({
|
||||
url: 'sync/summary/resourceCount',
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
return res.data as IResourceCount
|
||||
})
|
||||
}
|
||||
|
||||
export const getJobLogLienChartInfo = () => {
|
||||
return request.post({
|
||||
url: '/sync/summary/logChartData',
|
||||
method: 'post',
|
||||
data: ''
|
||||
})
|
||||
}
|
224
core/core-frontend/src/api/sync/syncTask.ts
Normal file
224
core/core-frontend/src/api/sync/syncTask.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface IGetTaskInfoReq {
|
||||
id?: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface ITaskInfoInsertReq {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface ISchedulerOption {
|
||||
interval: number
|
||||
unit: string
|
||||
}
|
||||
|
||||
export interface ISource {
|
||||
type: string
|
||||
query: string
|
||||
tables: string
|
||||
datasourceId: string
|
||||
tableExtract: string
|
||||
dsTableList?: IDsTable[]
|
||||
dsList?: []
|
||||
fieldList?: ITableField[]
|
||||
targetFieldTypeList?: string[]
|
||||
incrementCheckbox?: string
|
||||
incrementField?: string
|
||||
}
|
||||
|
||||
export interface ITableField {
|
||||
id: string
|
||||
fieldSource: string
|
||||
fieldName: string
|
||||
fieldType: string
|
||||
remarks: string
|
||||
fieldSize: number
|
||||
fieldPk: boolean
|
||||
fieldIndex: boolean
|
||||
}
|
||||
|
||||
export interface ITargetProperty {
|
||||
/**
|
||||
* 启用分区on
|
||||
*/
|
||||
partitionEnable: string
|
||||
/**
|
||||
* 分区类型
|
||||
* DateRange 日期
|
||||
* NumberRange 数值
|
||||
* List 列
|
||||
*/
|
||||
partitionType: string
|
||||
/**
|
||||
* 启用动态分区on
|
||||
*/
|
||||
dynamicPartitionEnable: string
|
||||
/**
|
||||
* 动态分区结束偏移量
|
||||
*/
|
||||
dynamicPartitionEnd: number
|
||||
/**
|
||||
* 动态分区间隔单位
|
||||
* HOUR WEEK DAY MONTH YEAR
|
||||
*/
|
||||
dynamicPartitionTimeUnit: string
|
||||
/**
|
||||
* 手动分区列值
|
||||
* 多个以','隔开
|
||||
*/
|
||||
manualPartitionColumnValue: string
|
||||
/**
|
||||
* 手动分区数值区间开始值
|
||||
*/
|
||||
manualPartitionStart: number
|
||||
/**
|
||||
* 手动分区数值区间结束值
|
||||
*/
|
||||
manualPartitionEnd: number
|
||||
/**
|
||||
* 手动分区数值区间间隔
|
||||
*/
|
||||
manualPartitionInterval: number
|
||||
/**
|
||||
* 手动分区日期区间
|
||||
* 2023-09-08 - 2023-09-15
|
||||
*/
|
||||
manualPartitionTimeRange: string
|
||||
/**
|
||||
* 手动分区日期区间间隔单位
|
||||
*/
|
||||
manualPartitionTimeUnit: string
|
||||
/**
|
||||
* 分区字段
|
||||
*/
|
||||
partitionColumn: string
|
||||
}
|
||||
|
||||
export interface ITarget {
|
||||
createTable?: string
|
||||
type: string
|
||||
fieldList: ITableField[]
|
||||
tableName: string
|
||||
datasourceId: string
|
||||
targetProperty: string
|
||||
dsList?: []
|
||||
multipleSelection?: ITableField[]
|
||||
property: ITargetProperty
|
||||
}
|
||||
|
||||
export class ITaskInfoRes {
|
||||
id: string
|
||||
|
||||
name: string
|
||||
|
||||
schedulerType: string
|
||||
|
||||
schedulerConf: string
|
||||
|
||||
schedulerOption: ISchedulerOption
|
||||
|
||||
taskKey: string
|
||||
|
||||
desc: string
|
||||
|
||||
executorTimeout: number
|
||||
|
||||
executorFailRetryCount: number
|
||||
|
||||
source: ISource
|
||||
|
||||
target: ITarget
|
||||
|
||||
status: string
|
||||
startTime: string
|
||||
stopTime: string
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
schedulerType: string,
|
||||
schedulerConf: string,
|
||||
taskKey: string,
|
||||
desc: string,
|
||||
executorTimeout: number,
|
||||
executorFailRetryCount: number,
|
||||
source: ISource,
|
||||
target: ITarget,
|
||||
status: string,
|
||||
startTime: string,
|
||||
stopTime: string,
|
||||
schedulerOption: ISchedulerOption
|
||||
) {
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.schedulerType = schedulerType
|
||||
this.schedulerConf = schedulerConf
|
||||
this.taskKey = taskKey
|
||||
this.desc = desc
|
||||
this.executorTimeout = executorTimeout
|
||||
this.executorFailRetryCount = executorFailRetryCount
|
||||
this.source = source
|
||||
this.target = target
|
||||
this.status = status
|
||||
this.startTime = startTime
|
||||
this.stopTime = stopTime
|
||||
this.schedulerOption = schedulerOption
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITaskInfoUpdateReq {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface IDsTable {
|
||||
datasourceId: string
|
||||
name: string
|
||||
remark: string
|
||||
enableCheck: string
|
||||
datasetPath: string
|
||||
}
|
||||
|
||||
export const getDatasourceListByTypeApi = (type: string) => {
|
||||
return request.get({ url: `/sync/datasource/list/${type}` })
|
||||
}
|
||||
export const getTaskInfoListApi = (current: number, size: number, data) => {
|
||||
return request.post({ url: `/sync/task/pager/${current}/${size}`, data: data })
|
||||
}
|
||||
|
||||
export const executeOneApi = (id: string) => {
|
||||
return request.get({ url: `/sync/task/execute/${id}` })
|
||||
}
|
||||
|
||||
export const startTaskApi = (id: string) => {
|
||||
return request.get({ url: `/sync/task/start/${id}` })
|
||||
}
|
||||
|
||||
export const stopTaskApi = (id: string) => {
|
||||
return request.get({ url: `/sync/task/stop/${id}` })
|
||||
}
|
||||
|
||||
export const addApi = (data: ITaskInfoInsertReq) => {
|
||||
return request.post({ url: `/sync/task/add`, data: data })
|
||||
}
|
||||
|
||||
export const removeApi = (taskId: string) => {
|
||||
return request.delete({ url: `/sync/task/remove/${taskId}` })
|
||||
}
|
||||
|
||||
export const batchRemoveApi = (taskIds: string[]) => {
|
||||
return request.post({ url: `/sync/task/batch/del`, data: taskIds })
|
||||
}
|
||||
|
||||
export const modifyApi = (data: ITaskInfoUpdateReq) => {
|
||||
return request.post({ url: `/sync/task/update`, data: data })
|
||||
}
|
||||
|
||||
export const findTaskInfoByIdApi = (taskId: string) => {
|
||||
return request.get({ url: `/sync/task/get/${taskId}` })
|
||||
}
|
||||
|
||||
export const getDatasourceTableListApi = (dsId: string) => {
|
||||
return request.get({ url: `/sync/datasource/table/list/${dsId}` })
|
||||
}
|
20
core/core-frontend/src/api/sync/syncTaskLog.ts
Normal file
20
core/core-frontend/src/api/sync/syncTaskLog.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const getTaskLogListApi = (current: number, size: number, data: any) => {
|
||||
return request.post({
|
||||
url: `/sync/task/log/pager/${current}/${size}`,
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const removeApi = (logId: string) => {
|
||||
return request.delete({ url: `/sync/task/log/delete/${logId}` })
|
||||
}
|
||||
|
||||
export const getTaskLogDetailApi = (logId: string, fromLineNum: number) => {
|
||||
return request.get({ url: `/sync/task/log/detail/${logId}/${fromLineNum}` })
|
||||
}
|
||||
|
||||
export const clear = (clearData: {}) => {
|
||||
return request.post({ url: `/sync/task/log/clear`, data: clearData })
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.95219 8.73169L15.5501 3.13376C15.7128 2.97104 15.9767 2.97104 16.1394 3.13376L16.7286 3.72301C16.8913 3.88573 16.8913 4.14955 16.7286 4.31227L11.1307 9.9102L16.7286 15.5081C16.8913 15.6708 16.8913 15.9347 16.7286 16.0974L16.1394 16.6866C15.9767 16.8494 15.7128 16.8494 15.5501 16.6866L9.95219 11.0887L4.35426 16.6866C4.19154 16.8494 3.92772 16.8494 3.76501 16.6866L3.17575 16.0974C3.01303 15.9347 3.01303 15.6708 3.17575 15.5081L8.77368 9.9102L3.17575 4.31227C3.01303 4.14955 3.01303 3.88573 3.17575 3.72301L3.76501 3.13376C3.92772 2.97104 4.19154 2.97104 4.35426 3.13376L9.95219 8.73169Z" fill="#646A73"/>
|
||||
</svg>
|
After Width: | Height: | Size: 724 B |
16
core/core-frontend/src/assets/svg/icon_sync_datasource.svg
Normal file
16
core/core-frontend/src/assets/svg/icon_sync_datasource.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_10054_936)">
|
||||
<path d="M0 6.66667C0 2.98477 3.25611 0 7.27273 0H32.7273C36.7439 0 40 2.98477 40 6.66667V33.3333C40 37.0152 36.7439 40 32.7273 40H7.27273C3.25611 40 0 37.0152 0 33.3333V6.66667Z" fill="#EBF1FF"/>
|
||||
<g clip-path="url(#clip1_10054_936)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.9019 28.8369C30.5346 28.3383 31.1111 27.6133 31.1111 26.6667V13.3334C31.1111 12.3868 30.5346 11.6618 29.9019 11.1632C29.2567 10.6547 28.392 10.2454 27.4225 9.92225C25.4718 9.27202 22.8476 8.88892 20 8.88892C17.1524 8.88892 14.5283 9.27202 12.5776 9.92225C11.6081 10.2454 10.7433 10.6547 10.0981 11.1632C9.46546 11.6618 8.88892 12.3868 8.88892 13.3334V26.6667C8.88892 27.6133 9.46546 28.3383 10.0981 28.8369C10.7433 29.3453 11.6081 29.7546 12.5776 30.0778C14.5283 30.728 17.1524 31.1111 20 31.1111C22.8476 31.1111 25.4718 30.728 27.4225 30.0778C28.392 29.7546 29.2567 29.3453 29.9019 28.8369ZM11.1111 13.3331C11.1118 12.1059 15.0912 11.1111 20 11.1111C24.9092 11.1111 28.8889 12.1061 28.8889 13.3334C28.8889 14.5607 24.9092 15.5556 20 15.5556C15.0908 15.5556 11.1111 14.5607 11.1111 13.3334C11.1111 13.3333 11.1111 13.3333 11.1111 13.3333C11.1111 13.3332 11.1111 13.3331 11.1111 13.3331ZM27.4225 16.7445C27.9482 16.5692 28.4431 16.3687 28.8889 16.1407V20.0003C28.8889 20.0016 28.8888 20.0064 28.8855 20.017C28.8818 20.0294 28.8724 20.0539 28.8499 20.0905C28.8028 20.1673 28.7066 20.2828 28.5264 20.4248C28.1596 20.714 27.5598 21.0229 26.7197 21.303C25.0511 21.8592 22.6753 22.2222 20 22.2222C17.3248 22.2222 14.9489 21.8592 13.2803 21.303C12.4402 21.0229 11.8405 20.714 11.4736 20.4248C11.2934 20.2828 11.1973 20.1673 11.1501 20.0905C11.1276 20.0539 11.1183 20.0294 11.1145 20.017C11.1113 20.0065 11.1111 20.0014 11.1111 20V16.1407C11.5569 16.3687 12.0519 16.5692 12.5776 16.7445C14.5283 17.3947 17.1524 17.7778 20 17.7778C22.8476 17.7778 25.4718 17.3947 27.4225 16.7445ZM27.4225 23.4111C27.9482 23.2359 28.4431 23.0353 28.8889 22.8074V26.6668C28.8889 26.6678 28.8889 26.6725 28.8855 26.6837C28.8818 26.6961 28.8724 26.7205 28.8499 26.7572C28.8028 26.834 28.7066 26.9495 28.5264 27.0915C28.1596 27.3806 27.5598 27.6896 26.7197 27.9696C25.0511 28.5258 22.6753 28.8889 20 28.8889C17.3248 28.8889 14.9489 28.5258 13.2803 27.9696C12.4402 27.6896 11.8405 27.3806 11.4736 27.0915C11.2934 26.9495 11.1973 26.834 11.1501 26.7572C11.1276 26.7205 11.1183 26.6961 11.1145 26.6837C11.1111 26.6725 11.1111 26.6677 11.1111 26.6667V22.8074C11.5569 23.0353 12.0519 23.2359 12.5776 23.4111C14.5283 24.0614 17.1524 24.4445 20 24.4445C22.8476 24.4445 25.4718 24.0614 27.4225 23.4111Z" fill="#3370FF"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_10054_936">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_10054_936">
|
||||
<rect width="26.6667" height="26.6667" fill="white" transform="translate(6.66675 6.66669)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
17
core/core-frontend/src/assets/svg/icon_sync_log_number.svg
Normal file
17
core/core-frontend/src/assets/svg/icon_sync_log_number.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_10054_2080)">
|
||||
<path d="M0 6.66667C0 2.98477 3.25611 0 7.27273 0H32.7273C36.7439 0 40 2.98477 40 6.66667V33.3333C40 37.0152 36.7439 40 32.7273 40H7.27273C3.25611 40 0 37.0152 0 33.3333V6.66667Z" fill="#E5FBF8"/>
|
||||
<g clip-path="url(#clip1_10054_2080)">
|
||||
<path d="M18.0958 25.682L25.7858 20.8437C25.928 20.7535 26.0452 20.6289 26.1264 20.4814C26.2075 20.3339 26.2501 20.1682 26.2501 19.9998C26.2501 19.8315 26.2075 19.6658 26.1264 19.5183C26.0452 19.3707 25.928 19.2461 25.7858 19.156L18.0962 14.318C17.9388 14.219 17.7575 14.1667 17.5722 14.1667C17.4419 14.1671 17.3131 14.1932 17.1929 14.2434C17.0728 14.2937 16.9637 14.3671 16.8719 14.4595C16.7801 14.5519 16.7075 14.6614 16.658 14.7819C16.6086 14.9024 16.5834 15.0314 16.5838 15.1617V24.8387C16.5839 25.0248 16.636 25.2072 16.7342 25.3653C16.8024 25.4759 16.8919 25.5719 16.9974 25.6478C17.1029 25.7236 17.2225 25.7778 17.349 25.8073C17.4756 25.8367 17.6068 25.8408 17.7349 25.8193C17.8631 25.7978 17.9858 25.7511 18.0958 25.682Z" fill="#00D6B9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9998 7.77771C26.7498 7.77771 32.222 13.2499 32.222 19.9999C32.222 26.7499 26.7498 32.2222 19.9998 32.2222C13.2498 32.2222 7.77759 26.7499 7.77759 19.9999C7.77759 13.2499 13.2498 7.77771 19.9998 7.77771ZM19.9998 9.44438C25.8293 9.44438 30.5554 14.1704 30.5554 19.9999C30.5554 25.8295 25.8293 30.5555 19.9998 30.5555C14.1703 30.5555 9.44425 25.8295 9.44425 19.9999C9.44425 14.1704 14.1703 9.44438 19.9998 9.44438Z" fill="#00D6B9"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_10054_2080">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_10054_2080">
|
||||
<rect width="26.6667" height="26.6667" fill="white" transform="translate(6.66675 6.66669)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="12" height="16" viewBox="0 0 12 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.66667 2.00033H1.33333V14.0003H10.6667V4.01333H9C8.91159 4.01333 8.82681 3.97821 8.7643 3.91569C8.70179 3.85318 8.66667 3.7684 8.66667 3.67999V2.00033ZM0.666667 0.666992H9.39033C9.47793 0.666967 9.56467 0.684205 9.64561 0.717722C9.72654 0.751238 9.80007 0.800376 9.862 0.862326L11.805 2.80599C11.9299 2.93104 12.0001 3.10058 12 3.27733V14.667C12 14.8438 11.9298 15.0134 11.8047 15.1384C11.6797 15.2634 11.5101 15.3337 11.3333 15.3337H0.666667C0.489856 15.3337 0.320286 15.2634 0.195262 15.1384C0.0702379 15.0134 4.14401e-09 14.8438 0 14.667V1.33366C4.14401e-09 1.15685 0.0702379 0.987279 0.195262 0.862254C0.320286 0.73723 0.489856 0.666992 0.666667 0.666992ZM3.33333 6.66699H8.66667C8.75507 6.66699 8.83986 6.70211 8.90237 6.76462C8.96488 6.82714 9 6.91192 9 7.00033V7.66699C9 7.7554 8.96488 7.84018 8.90237 7.90269C8.83986 7.96521 8.75507 8.00033 8.66667 8.00033H3.33333C3.24493 8.00033 3.16014 7.96521 3.09763 7.90269C3.03512 7.84018 3 7.7554 3 7.66699V7.00033C3 6.91192 3.03512 6.82714 3.09763 6.76462C3.16014 6.70211 3.24493 6.66699 3.33333 6.66699ZM3.33333 10.0003H6.33333C6.37711 10.0003 6.42045 10.0089 6.46089 10.0257C6.50134 10.0424 6.53808 10.067 6.56904 10.098C6.59999 10.1289 6.62454 10.1657 6.64129 10.2061C6.65805 10.2465 6.66667 10.2899 6.66667 10.3337V11.0003C6.66667 11.0441 6.65805 11.0874 6.64129 11.1279C6.62454 11.1683 6.59999 11.2051 6.56904 11.236C6.53808 11.267 6.50134 11.2915 6.46089 11.3083C6.42045 11.325 6.37711 11.3337 6.33333 11.3337H3.33333C3.24493 11.3337 3.16014 11.2985 3.09763 11.236C3.03512 11.1735 3 11.0887 3 11.0003V10.3337C3 10.2453 3.03512 10.1605 3.09763 10.098C3.16014 10.0354 3.24493 10.0003 3.33333 10.0003Z" fill="#3370FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.66666 12.0002H5.66666C2.90523 12.0002 0.666656 9.76164 0.666656 7.00022C0.666656 5.02373 1.81347 3.3151 3.47799 2.50343L4.14705 3.66228C2.8804 4.23986 1.99999 5.51723 1.99999 7.00022C1.99999 9.02526 3.64161 10.6669 5.66666 10.6669H8.66666V9.77154C8.66666 9.57824 8.82336 9.42154 9.01666 9.42154C9.10143 9.42154 9.18333 9.45231 9.24713 9.50814L11.0323 11.0702C11.1778 11.1974 11.1925 11.4186 11.0652 11.564C11.055 11.5757 11.044 11.5867 11.0323 11.597L9.24713 13.159C9.10166 13.2863 8.88054 13.2715 8.75325 13.126C8.69743 13.0622 8.66666 12.9803 8.66666 12.8956V12.0002ZM7.33333 3.33356V4.22891C7.33333 4.31369 7.30256 4.39559 7.24673 4.45939C7.11944 4.60486 6.89832 4.6196 6.75285 4.49231L4.96769 2.9303C4.956 2.92007 4.945 2.90907 4.93477 2.89737C4.80748 2.7519 4.82222 2.53078 4.96769 2.4035L6.75285 0.841481C6.81665 0.785654 6.89855 0.754883 6.98333 0.754883C7.17663 0.754883 7.33333 0.911583 7.33333 1.10488V2.00023H10.3333C13.0947 2.00023 15.3333 4.23881 15.3333 7.00023C15.3333 8.96233 14.2031 10.6605 12.5583 11.4791L11.8897 10.3212C13.1366 9.73578 14 8.46883 14 7.00023C14 4.97519 12.3584 3.33356 10.3333 3.33356H7.33333Z" fill="#3370FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
17
core/core-frontend/src/assets/svg/icon_sync_task_number.svg
Normal file
17
core/core-frontend/src/assets/svg/icon_sync_task_number.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7277_272394)">
|
||||
<path d="M0 6.66667C0 2.98477 3.25611 0 7.27273 0H32.7273C36.7439 0 40 2.98477 40 6.66667V33.3333C40 37.0152 36.7439 40 32.7273 40H7.27273C3.25611 40 0 37.0152 0 33.3333V6.66667Z" fill="#FFF3E5"/>
|
||||
<g clip-path="url(#clip1_7277_272394)">
|
||||
<path d="M14.7474 21.6382C14.7785 21.7131 14.824 21.7812 14.8814 21.8385L18.3726 25.3304L18.4311 25.3822C18.6733 25.5696 19.0237 25.5526 19.2459 25.3304L25.3563 19.2193C25.472 19.1035 25.537 18.9466 25.537 18.783C25.537 18.6193 25.472 18.4624 25.3563 18.3467L24.4837 17.4733C24.4264 17.4159 24.3583 17.3704 24.2834 17.3393C24.2084 17.3082 24.1281 17.2923 24.047 17.2923C23.9659 17.2923 23.8856 17.3082 23.8106 17.3393C23.7357 17.3704 23.6676 17.4159 23.6103 17.4733L18.8096 22.2748L16.6266 20.0926C16.5109 19.9769 16.354 19.9119 16.1903 19.9119C16.0267 19.9119 15.8697 19.9769 15.754 20.0926L14.8814 20.9652C14.824 21.0225 14.7785 21.0906 14.7474 21.1655C14.7164 21.2404 14.7004 21.3207 14.7004 21.4019C14.7004 21.483 14.7164 21.5633 14.7474 21.6382Z" fill="#FF8800"/>
|
||||
<path d="M24.4442 7.77777H15.5553C14.8739 7.77777 14.4442 8.2074 14.4442 8.88888V9.99999H11.1111C10.3489 9.99999 10 10.4296 10 11.1111V31.1111C10 31.7933 10.3489 32.2222 11.1111 32.2222L28.8889 32.2222C29.6511 32.2222 30 31.7926 30 31.1111V11.1111C30 10.4289 29.6511 9.99996 28.8889 9.99996L25.5556 9.99999V12.2222H27.7778V30H12.2222V12.2222H14.4442V13.3333C14.4442 14.0148 14.8739 14.4444 15.5553 14.4444H24.4442C25.1257 14.4444 25.5553 14.0148 25.5553 13.3333V8.88888C25.5553 8.20666 25.1257 7.77777 24.4442 7.77777ZM16.6664 12.2222V9.99999H23.3331V12.2222H16.6664Z" fill="#FF8800"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_7277_272394">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_7277_272394">
|
||||
<rect width="26.6667" height="26.6667" fill="white" transform="translate(6.66675 6.66669)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
@ -1,46 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ElSelect, ElPopover, ElOption, ElIcon } from 'element-plus-secondary'
|
||||
import { computed, reactive, nextTick, ref } from 'vue'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { computed, PropType, reactive, toRefs } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface Config {
|
||||
// 显示类型
|
||||
showType: string
|
||||
// 日期分隔符
|
||||
rangeSeparator: string
|
||||
// 前缀图标
|
||||
prefixIcon: string
|
||||
// 开始日期label
|
||||
startPlaceholder: string
|
||||
// 结束日期label
|
||||
endPlaceholder: string
|
||||
// 日期格式
|
||||
format: string
|
||||
// 日期值格式
|
||||
valueFormat: string
|
||||
// 尺寸
|
||||
size: string
|
||||
// 弹出位置
|
||||
placement: string
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
statusList: propTypes.arrayOf(
|
||||
propTypes.shape({
|
||||
id: propTypes.string,
|
||||
name: propTypes.string
|
||||
})
|
||||
),
|
||||
property: Object as PropType<Config>,
|
||||
title: propTypes.string
|
||||
})
|
||||
|
||||
const { property } = toRefs(props)
|
||||
const timeConfig = computed(() => {
|
||||
let obj = Object.assign(
|
||||
{
|
||||
showType: 'datetime',
|
||||
rangeSeparator: '-',
|
||||
prefixIcon: 'Calendar',
|
||||
startPlaceholder: t('datasource.start_time'),
|
||||
endPlaceholder: t('datasource.end_time'),
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
size: 'default',
|
||||
placement: 'bottom-end'
|
||||
},
|
||||
property.value
|
||||
)
|
||||
return obj
|
||||
})
|
||||
const state = reactive({
|
||||
currentStatus: [],
|
||||
activeStatus: []
|
||||
modelValue: []
|
||||
})
|
||||
|
||||
const elPopoverU = ref(null)
|
||||
const more = ref(null)
|
||||
|
||||
const statusChange = (id: string | number) => {
|
||||
state.activeStatus = state.activeStatus.filter(ele => ele.id !== id)
|
||||
const emits = defineEmits(['filter-change'])
|
||||
const onChange = () => {
|
||||
emits('filter-change', state.modelValue)
|
||||
}
|
||||
const selectStatus = ids => {
|
||||
const [item] = ids
|
||||
state.activeStatus.push(item)
|
||||
state.currentStatus = []
|
||||
nextTick(() => {
|
||||
elPopoverU.value.hide()
|
||||
more.value.click()
|
||||
})
|
||||
const clear = () => {
|
||||
state.modelValue = []
|
||||
}
|
||||
|
||||
const statusListNotSelect = computed(() => {
|
||||
return props.statusList.filter(ele => !state.activeStatus.map(t => t.id).includes(ele.id))
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
activeStatus: state.activeStatus
|
||||
clear
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -48,108 +68,42 @@ defineExpose({
|
||||
<div class="filter">
|
||||
<span>{{ title }}</span>
|
||||
<div class="filter-item">
|
||||
<span
|
||||
v-for="ele in state.activeStatus"
|
||||
:key="ele.id"
|
||||
class="item active"
|
||||
@click="statusChange(ele.id)"
|
||||
>{{ $t(ele.name) }}</span
|
||||
>
|
||||
<slot v-if="!!statusListNotSelect.length">
|
||||
<el-popover
|
||||
:show-arrow="false"
|
||||
ref="elPopoverU"
|
||||
placement="bottom"
|
||||
popper-class="filter-popper"
|
||||
width="200"
|
||||
trigger="click"
|
||||
>
|
||||
<el-select
|
||||
:teleported="false"
|
||||
style="width: 100%"
|
||||
v-model="state.currentStatus"
|
||||
value-key="id"
|
||||
filterable
|
||||
multiple
|
||||
@change="selectStatus"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusListNotSelect"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
<template #reference>
|
||||
<span ref="more" class="more">
|
||||
<el-icon>
|
||||
<Icon name="icon_add_outlined"> </Icon>
|
||||
</el-icon>
|
||||
更多
|
||||
</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</slot>
|
||||
<el-date-picker
|
||||
v-model="state.modelValue"
|
||||
:type="timeConfig.showType"
|
||||
:range-separator="timeConfig.rangeSeparator"
|
||||
:prefix-icon="timeConfig.prefixIcon"
|
||||
:start-placeholder="timeConfig.startPlaceholder"
|
||||
:end-placeholder="timeConfig.endPlaceholder"
|
||||
:format="timeConfig.format"
|
||||
:value-format="timeConfig.valueFormat"
|
||||
key="drawer-time-filt"
|
||||
:size="timeConfig.size"
|
||||
@change="onChange"
|
||||
:placement="timeConfig.placement"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scope>
|
||||
.filter {
|
||||
display: flex;
|
||||
min-height: 46px;
|
||||
min-height: 32px;
|
||||
|
||||
> :nth-child(1) {
|
||||
color: var(--deTextSecondary, #1f2329);
|
||||
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
|
||||
font-family: 'PingFang SC';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
white-space: nowrap;
|
||||
width: 116px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
flex: 1;
|
||||
|
||||
.item,
|
||||
.more {
|
||||
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
margin-right: 12px;
|
||||
text-align: center;
|
||||
padding: 1px 6px;
|
||||
background: var(--deTextPrimary5, #f5f6f7);
|
||||
color: var(--deTextPrimary, #1f2329);
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.active,
|
||||
.more:hover {
|
||||
background: var(--primary10, rgba(51, 112, 255, 0.1));
|
||||
color: var(--primaryselect, #0c296e);
|
||||
}
|
||||
|
||||
.more {
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.filter-popper {
|
||||
padding: 0 !important;
|
||||
background: #fff !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,7 +14,8 @@ const props = defineProps({
|
||||
type: propTypes.string,
|
||||
field: propTypes.string,
|
||||
option: propTypes.array,
|
||||
title: propTypes.string
|
||||
title: propTypes.string,
|
||||
property: propTypes.shape({})
|
||||
})
|
||||
),
|
||||
title: propTypes.string
|
||||
@ -126,7 +127,8 @@ defineExpose({
|
||||
:ref="el => (myRefs[index] = el)"
|
||||
v-if="component.type === 'time'"
|
||||
:title="component.title"
|
||||
@filter-change="v => filterChange(v, component.field, 'eq')"
|
||||
:property="component.property"
|
||||
@filter-change="v => filterChange(v, component.field, component.operator)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { Icon } from '@/components/icon-custom'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import type { Placement } from 'element-plus-secondary'
|
||||
import { ref, PropType } from 'vue'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
import ShareHandler from '@/views/share/share/ShareHandler.vue'
|
||||
export interface Menu {
|
||||
svgName?: string
|
||||
label?: string
|
||||
@ -44,7 +44,8 @@ const menus = ref([
|
||||
])
|
||||
const handleCommand = (command: string | number | object) => {
|
||||
if (command === 'share') {
|
||||
shareComponent.value.invokeMethod({ methodName: 'execute' })
|
||||
// shareComponent.value.invokeMethod({ methodName: 'execute' })
|
||||
shareComponent.value.execute()
|
||||
return
|
||||
}
|
||||
emit('handleCommand', command)
|
||||
@ -85,9 +86,8 @@ const emit = defineEmits(['handleCommand'])
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<XpackComponent
|
||||
<ShareHandler
|
||||
ref="shareComponent"
|
||||
jsname="c2hhcmUtaGFuZGxlcg=="
|
||||
:resource-id="props.node.id"
|
||||
:resource-type="props.resourceType"
|
||||
:weight="node.weight"
|
||||
|
@ -13,6 +13,7 @@ import { usePermissionStoreWithOut } from '@/store/modules/permission'
|
||||
import { useLinkStoreWithOut } from '@/store/modules/link'
|
||||
import { config } from './config'
|
||||
import { configHandler } from './refresh'
|
||||
|
||||
type AxiosErrorWidthLoading<T> = T & {
|
||||
config: {
|
||||
loading?: boolean
|
||||
@ -25,8 +26,10 @@ type InternalAxiosRequestConfigWidthLoading<T> = T & {
|
||||
|
||||
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
|
||||
import router from '@/router'
|
||||
|
||||
const { result_code } = config
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
|
||||
const { wsCache } = useCache()
|
||||
|
||||
export const PATH_URL = window.DataEaseBi
|
||||
@ -38,10 +41,41 @@ export interface AxiosInstanceWithLoading extends AxiosInstance {
|
||||
config: AxiosRequestConfig<D> & { loading?: boolean }
|
||||
): Promise<R>
|
||||
}
|
||||
|
||||
const getTimeOut = () => {
|
||||
let time = 100
|
||||
const url = PATH_URL + '/sysParameter/requestTimeOut'
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
if (xhr.responseText) {
|
||||
try {
|
||||
const response = JSON.parse(xhr.responseText)
|
||||
if (response.code === 0) {
|
||||
time = response.data
|
||||
} else {
|
||||
ElMessage.error('系统异常,请联系管理员')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('系统异常,请联系管理员')
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('网络异常,请联系网管')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xhr.open('get', url, false)
|
||||
xhr.send()
|
||||
console.log(time)
|
||||
return time
|
||||
}
|
||||
|
||||
// 创建axios实例
|
||||
const time = getTimeOut()
|
||||
const service: AxiosInstanceWithLoading = axios.create({
|
||||
baseURL: PATH_URL, // api 的 base_url
|
||||
timeout: config.request_timeout // 请求超时时间
|
||||
timeout: time ? time * 1000 : config.request_timeout // 请求超时时间
|
||||
})
|
||||
const mapping = {
|
||||
'zh-CN': 'zh-CN',
|
||||
|
@ -2126,12 +2126,64 @@ export default {
|
||||
setting_basic: {
|
||||
autoCreateUser: '第三方自动创建用户',
|
||||
dsIntervalTime: '数据源检测时间间隔',
|
||||
dsExecuteTime: '数据源检测频率'
|
||||
dsExecuteTime: '数据源检测频率',
|
||||
frontTimeOut: '请求超时时间(秒)'
|
||||
},
|
||||
template_manage: {
|
||||
name_already_exists_type: '分类名称已存在',
|
||||
the_same_category: '同一分类下,该模板名称已存在'
|
||||
},
|
||||
sync_manage: {
|
||||
title: '同步管理',
|
||||
ds_search_placeholder: '搜索名称,描述'
|
||||
},
|
||||
sync_datasource: {
|
||||
title: '数据连接管理',
|
||||
source_ds: '源数据源',
|
||||
target_ds: '目标数据源',
|
||||
add_source_ds: '@:common.add@:sync_datasource.source_ds',
|
||||
add_target_ds: '@:common.add@:sync_datasource.target_ds',
|
||||
name: '名称',
|
||||
desc: '描述',
|
||||
type: '类型',
|
||||
status: '状态',
|
||||
create_time: '创建时间',
|
||||
update_time: '更新时间',
|
||||
operation: '操作',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
confirm_batch_delete_target_ds: '确定删除{0}个目标数据源吗?',
|
||||
confirm_batch_delete_source_ds: '确定删除{0}个源数据源吗?'
|
||||
},
|
||||
sync_task: {
|
||||
title: '任务管理',
|
||||
list: '任务列表',
|
||||
log_list: '任务日志',
|
||||
add_task: '添加任务',
|
||||
name: '名称',
|
||||
desc: '描述',
|
||||
status: '状态',
|
||||
create_time: '创建时间',
|
||||
update_time: '更新时间',
|
||||
operation: '操作',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
start: '启动',
|
||||
stop: '停止',
|
||||
trigger_last_time: '上次执行时间',
|
||||
trigger_next_time: '下次执行时间',
|
||||
status_success: '成功',
|
||||
status_running: '执行中',
|
||||
status_failed: '失败',
|
||||
status_stopped: '已停止',
|
||||
status_waiting: '等待执行',
|
||||
status_done: '执行结束',
|
||||
status_suspend: '暂停',
|
||||
running_one: '执行一次',
|
||||
keep_going: '继续',
|
||||
log: '日志',
|
||||
show_log: '查看日志'
|
||||
},
|
||||
watermark: {
|
||||
support_params: '当前支持的参数:',
|
||||
enable: '启用水印',
|
||||
|
@ -18,7 +18,7 @@ const { start, done } = useNProgress()
|
||||
|
||||
const { loadStart, loadDone } = usePageLoading()
|
||||
|
||||
const whiteList = ['/login', '/de-link'] // 不重定向白名单
|
||||
const whiteList = ['/login', '/de-link', '/chart-view'] // 不重定向白名单
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
start()
|
||||
|
@ -112,6 +112,13 @@ export const routes: AppRouteRecordRaw[] = [
|
||||
meta: { hidden: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/chart-view',
|
||||
name: 'chart-view',
|
||||
hidden: true,
|
||||
meta: {},
|
||||
component: () => import('@/views/chart/ChartView.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
29
core/core-frontend/src/views/chart/ChartView.vue
Normal file
29
core/core-frontend/src/views/chart/ChartView.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script lang="ts" setup>
|
||||
import ViewWrapper from '@/pages/panel/ViewWrapper.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { onBeforeMount } from 'vue'
|
||||
const route = useRoute()
|
||||
const init = () => {
|
||||
const busiFlag = route.query.busiFlag
|
||||
const dvId = route.query.dvId
|
||||
const chartId = route.query.chartId
|
||||
const embeddedToken = route.query.embeddedToken
|
||||
console.log(busiFlag)
|
||||
console.log(dvId)
|
||||
console.log(chartId)
|
||||
console.log(embeddedToken)
|
||||
window.DataEaseBi = {
|
||||
token: embeddedToken,
|
||||
chartId: chartId,
|
||||
dvId: dvId,
|
||||
busiFlag: busiFlag
|
||||
}
|
||||
}
|
||||
onBeforeMount(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view-wrapper />
|
||||
</template>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<XpackComponent jsname="bGluaw==" :error-tips="true" />
|
||||
<link-index :error-tips="true" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
import LinkIndex from '@/views/share/link/index.vue'
|
||||
</script>
|
||||
|
@ -6,7 +6,7 @@ import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import DvDetailInfo from '@/views/common/DvDetailInfo.vue'
|
||||
import { storeApi, storeStatusApi } from '@/api/visualization/dataVisualization'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
import ShareVisualHead from '@/views/share/share/ShareVisualHead.vue'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const appStore = useAppStoreWithOut()
|
||||
const { dvInfo } = storeToRefs(dvMainStore)
|
||||
@ -94,8 +94,7 @@ watch(
|
||||
</template>
|
||||
预览</el-button
|
||||
>
|
||||
<XpackComponent
|
||||
jsname="L2NvbXBvbmVudC9zaGFyZS9TaGFyZVZpc3VhbEhlYWQ="
|
||||
<ShareVisualHead
|
||||
:resource-id="dvInfo.id"
|
||||
:weight="dvInfo.weight"
|
||||
:resource-type="dvInfo.type"
|
||||
|
@ -173,7 +173,8 @@ const showLoginErrorMsg = () => {
|
||||
onMounted(() => {
|
||||
checkPlatform()
|
||||
if (localStorage.getItem('DE-GATEWAY-FLAG')) {
|
||||
loginErrorMsg.value = localStorage.getItem('DE-GATEWAY-FLAG')
|
||||
const msg = localStorage.getItem('DE-GATEWAY-FLAG')
|
||||
loginErrorMsg.value = decodeURIComponent(msg)
|
||||
showLoginErrorMsg()
|
||||
localStorage.removeItem('DE-GATEWAY-FLAG')
|
||||
logoutHandler(true)
|
||||
|
42
core/core-frontend/src/views/share/link/ShareProxy.ts
Normal file
42
core/core-frontend/src/views/share/link/ShareProxy.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import request from '@/config/axios'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
const { wsCache } = useCache()
|
||||
export interface ProxyInfo {
|
||||
resourceId: string
|
||||
uid: string
|
||||
exp?: boolean
|
||||
pwdValid?: boolean
|
||||
type: string
|
||||
}
|
||||
class ShareProxy {
|
||||
uuid: string
|
||||
constructor() {
|
||||
this.uuid = ''
|
||||
}
|
||||
setUuid() {
|
||||
const curLocation = window.location.href
|
||||
const uuidObj = curLocation.substring(
|
||||
curLocation.lastIndexOf('de-link/') + 8,
|
||||
curLocation.lastIndexOf('?') > 0 ? curLocation.lastIndexOf('?') : curLocation.length
|
||||
)
|
||||
this.uuid = uuidObj
|
||||
}
|
||||
async loadProxy() {
|
||||
this.setUuid()
|
||||
if (!this.uuid) {
|
||||
return null
|
||||
}
|
||||
const uuid = this.uuid
|
||||
const url = '/share/proxyInfo'
|
||||
const param = { uuid, ciphertext: null }
|
||||
const ciphertext = wsCache.get(`link-${uuid}`)
|
||||
if (ciphertext) {
|
||||
param['ciphertext'] = ciphertext
|
||||
}
|
||||
const res = await request.post({ url, data: param })
|
||||
const proxyInfo: ProxyInfo = res.data as ProxyInfo
|
||||
return proxyInfo
|
||||
}
|
||||
}
|
||||
|
||||
export const shareProxy = new ShareProxy()
|
6
core/core-frontend/src/views/share/link/error.vue
Normal file
6
core/core-frontend/src/views/share/link/error.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
|
||||
</script>
|
||||
<template>
|
||||
<EmptyBackground img-type="noneWhite" description="链接不存在" />
|
||||
</template>
|
6
core/core-frontend/src/views/share/link/exp.vue
Normal file
6
core/core-frontend/src/views/share/link/exp.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
|
||||
</script>
|
||||
<template>
|
||||
<EmptyBackground img-type="noneWhite" description="链接已过期" />
|
||||
</template>
|
55
core/core-frontend/src/views/share/link/index.vue
Normal file
55
core/core-frontend/src/views/share/link/index.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="link-container" v-loading="loading">
|
||||
<LinkError v-if="!loading && !linkExist" />
|
||||
<Exp v-else-if="!loading && linkExp" />
|
||||
<PwdTips v-else-if="!loading && !pwdValid" />
|
||||
<PreviewCanvas
|
||||
v-else
|
||||
:class="{ 'hidden-link': loading }"
|
||||
ref="pcanvas"
|
||||
public-link-status="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, ref } from 'vue'
|
||||
import PreviewCanvas from '@/views/data-visualization/PreviewCanvas.vue'
|
||||
import { ProxyInfo, shareProxy } from './ShareProxy'
|
||||
import Exp from './exp.vue'
|
||||
import LinkError from './error.vue'
|
||||
import PwdTips from './pwd.vue'
|
||||
const pcanvas = ref(null)
|
||||
const linkExist = ref(false)
|
||||
const loading = ref(true)
|
||||
const linkExp = ref(false)
|
||||
const pwdValid = ref(false)
|
||||
onMounted(async () => {
|
||||
const proxyInfo = (await shareProxy.loadProxy()) as ProxyInfo
|
||||
if (!proxyInfo?.resourceId) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
linkExist.value = true
|
||||
linkExp.value = !!proxyInfo.exp
|
||||
pwdValid.value = !!proxyInfo.pwdValid
|
||||
nextTick(() => {
|
||||
const method = pcanvas?.value?.loadCanvasDataAsync
|
||||
if (method) {
|
||||
method(proxyInfo.resourceId, proxyInfo.type, null)
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.link-container {
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.hidden-link {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
218
core/core-frontend/src/views/share/link/pwd.vue
Normal file
218
core/core-frontend/src/views/share/link/pwd.vue
Normal file
@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="pwd-body" v-loading="loading">
|
||||
<div class="pwd-wrapper">
|
||||
<div class="pwd-content">
|
||||
<div class="span-header">
|
||||
<div class="bi-text">
|
||||
<span style="text-align: center">{{ t('pblink.key_pwd') }} </span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-layout">
|
||||
<div class="input-main">
|
||||
<div class="div-input">
|
||||
<el-form ref="pwdForm" :model="form" :rules="rule" size="small" @submit.stop.prevent>
|
||||
<el-form-item label="" prop="password">
|
||||
<CustomPassword
|
||||
v-model="form.password"
|
||||
maxlength="4"
|
||||
show-password
|
||||
class="real-input"
|
||||
:placeholder="t('pblink.input_placeholder')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="abs-input">
|
||||
<div class="input-text">{{ msg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="auth-root-class">
|
||||
<el-button size="small" type="primary" @click="refresh">{{
|
||||
t('pblink.sure_bt')
|
||||
}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import request from '@/config/axios'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import { rsaEncryp } from '@/utils/encryption'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { queryDekey } from '@/api/login'
|
||||
import { CustomPassword } from '@/components/custom-password'
|
||||
const { wsCache } = useCache()
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
const { t } = useI18n()
|
||||
const msg = ref('')
|
||||
const loading = ref(true)
|
||||
const pwdForm = ref<FormInstance>()
|
||||
const form = ref({
|
||||
password: ''
|
||||
})
|
||||
const rule = reactive<FormRules>({
|
||||
password: [
|
||||
{ required: true, message: t('pblink.key_pwd'), trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
pattern: /^[a-zA-Z0-9]{4}$/,
|
||||
message: t('pblink.pwd_format_error'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const refresh = () => {
|
||||
const curLocation = window.location.href
|
||||
const paramIndex = curLocation.indexOf('?')
|
||||
const uuidIndex = curLocation.indexOf('de-link/') + 8
|
||||
const uuid = curLocation.substring(uuidIndex, paramIndex !== -1 ? paramIndex : curLocation.length)
|
||||
const pwd = form.value.password
|
||||
const text = uuid + pwd
|
||||
const ciphertext = rsaEncryp(text)
|
||||
request.post({ url: '/share/validate', data: { ciphertext } }).then(res => {
|
||||
if (res.data) {
|
||||
wsCache.set(`link-${uuid}`, ciphertext)
|
||||
window.location.reload()
|
||||
} else {
|
||||
msg.value = '密码错误'
|
||||
}
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
if (!wsCache.get(appStore.getDekey)) {
|
||||
queryDekey()
|
||||
.then(res => {
|
||||
wsCache.set(appStore.getDekey, res.data)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.pwd-body {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-repeat: repeat;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
color: #3d4d66;
|
||||
// font: normal 12px Helvetica Neue,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Heiti,黑体,sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-decoration: none;
|
||||
-kthml-user-focus: normal;
|
||||
-moz-user-focus: normal;
|
||||
-moz-outline: 0 none;
|
||||
outline: 0 none;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.pwd-wrapper {
|
||||
background-color: #f7f8fa;
|
||||
height: 100%;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
min-height: 25px;
|
||||
display: flex;
|
||||
-moz-flex-direction: row;
|
||||
-o-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-moz-justify-content: flex-start;
|
||||
-ms-justify-content: flex-start;
|
||||
-o-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
-moz-align-items: flex-start;
|
||||
-ms-align-items: flex-start;
|
||||
-o-align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
-o-flex-wrap: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.pwd-content {
|
||||
width: 450px;
|
||||
height: 250px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
background-color: #ffffff;
|
||||
display: block;
|
||||
}
|
||||
.span-header {
|
||||
position: relative;
|
||||
margin: 57px auto 0px;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
.bi-text {
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
word-break: break-all;
|
||||
display: block;
|
||||
}
|
||||
.input-layout {
|
||||
width: 200px;
|
||||
position: relative;
|
||||
margin: 0px auto;
|
||||
padding: 0;
|
||||
display: block;
|
||||
}
|
||||
.input-main {
|
||||
width: 192px;
|
||||
height: 35px;
|
||||
position: relative;
|
||||
margin-top: 30px;
|
||||
// border: 1px solid #e8eaed;
|
||||
display: block;
|
||||
}
|
||||
.abs-input {
|
||||
height: 20px;
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
.input-text {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
text-overflow: ellipsis;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
color: #e65251;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.auth-root-class {
|
||||
margin: 15px 0px 5px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
267
core/core-frontend/src/views/share/share/ShareGrid.vue
Normal file
267
core/core-frontend/src/views/share/share/ShareGrid.vue
Normal file
@ -0,0 +1,267 @@
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import GridTable from '@/components/grid-table/src/GridTable.vue'
|
||||
import request from '@/config/axios'
|
||||
import dayjs from 'dayjs'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import ShareHandler from './ShareHandler.vue'
|
||||
import { interactiveStoreWithOut } from '@/store/modules/interactive'
|
||||
const props = defineProps({
|
||||
activeName: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const interactiveStore = interactiveStoreWithOut()
|
||||
|
||||
const busiDataMap = computed(() => interactiveStore.getData)
|
||||
const panelKeyword = ref()
|
||||
const userAddPopper = ref(false)
|
||||
const activeCommand = ref('all_types')
|
||||
const state = reactive({
|
||||
tableData: [],
|
||||
curTypeList: ['all_types', 'panel', 'screen'],
|
||||
tableColumn: [
|
||||
{ field: 'creator', label: '分享人' },
|
||||
{ field: 'time', label: '分享时间', type: 'time' },
|
||||
{ field: 'exp', label: '有效期', type: 'time' }
|
||||
]
|
||||
})
|
||||
|
||||
const handleVisibleChange = (val: boolean) => {
|
||||
userAddPopper.value = val
|
||||
}
|
||||
|
||||
const handleCommand = (command: string) => {
|
||||
activeCommand.value = command
|
||||
loadTableData()
|
||||
}
|
||||
|
||||
const triggerFilterPanel = () => {
|
||||
loadTableData()
|
||||
}
|
||||
const preview = id => {
|
||||
const routeUrl = `/#/preview?dvId=${id}`
|
||||
window.open(routeUrl, '_blank')
|
||||
}
|
||||
const formatterTime = (_, _column, cellValue) => {
|
||||
if (!cellValue) {
|
||||
return '-'
|
||||
}
|
||||
return dayjs(new Date(cellValue)).format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
emits('setLoading', true)
|
||||
}
|
||||
const closeLoading = () => {
|
||||
emits('setLoading', false)
|
||||
}
|
||||
const emits = defineEmits(['setLoading'])
|
||||
const loadTableData = () => {
|
||||
showLoading()
|
||||
const queryType = activeCommand.value === 'all_types' ? '' : activeCommand.value
|
||||
request
|
||||
.post({
|
||||
url: '/share/query',
|
||||
data: { type: queryType, keyword: panelKeyword.value, asc: !orderDesc.value }
|
||||
})
|
||||
.then(res => {
|
||||
state.tableData = res.data
|
||||
})
|
||||
.finally(() => {
|
||||
imgType.value = getEmptyImg()
|
||||
emptyDesc.value = getEmptyDesc()
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
const orderDesc = ref(true)
|
||||
const sortChange = param => {
|
||||
orderDesc.value = true
|
||||
const type = param.order.substring(0, param.order.indexOf('ending'))
|
||||
orderDesc.value = type === 'desc'
|
||||
loadTableData()
|
||||
}
|
||||
const getBusiListWithPermission = () => {
|
||||
const baseFlagList: string[] = ['panel', 'screen']
|
||||
const busiFlagList: string[] = []
|
||||
for (const key in busiDataMap.value) {
|
||||
if (busiDataMap.value[key].menuAuth) {
|
||||
busiFlagList.push(baseFlagList[parseInt(key)])
|
||||
}
|
||||
}
|
||||
return busiFlagList
|
||||
}
|
||||
const busiAuthList: string[] = getBusiListWithPermission()
|
||||
|
||||
const imgType = ref()
|
||||
const emptyDesc = ref('')
|
||||
const getEmptyImg = (): string => {
|
||||
if (panelKeyword.value) {
|
||||
return 'tree'
|
||||
}
|
||||
return 'noneWhite'
|
||||
}
|
||||
|
||||
const getEmptyDesc = (): string => {
|
||||
if (panelKeyword.value) {
|
||||
return '没有找到相关内容'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
watch(
|
||||
() => props.activeName,
|
||||
() => {
|
||||
if (props.activeName === 'share') {
|
||||
loadTableData()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-row v-if="props.activeName === 'share'">
|
||||
<el-col :span="12">
|
||||
<el-dropdown
|
||||
placement="bottom-start"
|
||||
@visible-change="handleVisibleChange"
|
||||
popper-class="menu-panel-select_popper"
|
||||
@command="handleCommand"
|
||||
trigger="click"
|
||||
>
|
||||
<el-button secondary>
|
||||
{{ t(`auth.${activeCommand}`) }}
|
||||
<el-icon style="margin-left: 4px">
|
||||
<arrow-up v-if="userAddPopper" />
|
||||
<arrow-down v-else />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
:class="activeCommand === ele && 'active'"
|
||||
v-for="ele in state.curTypeList.filter(
|
||||
busi => busi === 'all_types' || busiAuthList.includes(busi)
|
||||
)"
|
||||
:command="ele"
|
||||
:key="ele"
|
||||
>
|
||||
{{ t(`auth.${ele}`) }}
|
||||
<el-icon v-if="activeCommand === ele">
|
||||
<Icon name="icon_done_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-col>
|
||||
<el-col class="search" :span="12">
|
||||
<el-input
|
||||
v-model="panelKeyword"
|
||||
clearable
|
||||
@change="triggerFilterPanel"
|
||||
placeholder="搜索关键词"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Icon name="icon_search-outline_outlined"></Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="props.activeName === 'share'" class="panel-table">
|
||||
<GridTable
|
||||
:show-pagination="false"
|
||||
:table-data="state.tableData"
|
||||
@sort-change="sortChange"
|
||||
:empty-desc="emptyDesc"
|
||||
:empty-img="imgType"
|
||||
class="workbranch-grid"
|
||||
>
|
||||
<el-table-column key="name" width="280" prop="name" :label="t('common.name')">
|
||||
<template v-slot:default="scope">
|
||||
<div class="name-content">
|
||||
<el-icon class="main-color"> <Icon name="icon_dashboard_outlined" /> </el-icon>
|
||||
<el-tooltip placement="top">
|
||||
<template #content>{{ scope.row.name }}</template>
|
||||
<span class="ellipsis" style="max-width: 250px">{{ scope.row.name }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-for="item in state.tableColumn"
|
||||
:key="item.label"
|
||||
prop="name"
|
||||
show-overflow-tooltip
|
||||
:label="item.label"
|
||||
:sortable="item.type === 'time' && item.field === 'time'"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span v-if="item.type && item.type === 'time'">{{
|
||||
formatterTime(null, null, scope.row[item.field])
|
||||
}}</span>
|
||||
<span v-else>{{ scope.row[item.field] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="96" fixed="right" key="_operation" :label="t('common.operate')">
|
||||
<template #default="scope">
|
||||
<el-tooltip effect="dark" content="新页面预览" placement="top">
|
||||
<el-icon class="hover-icon hover-icon-in-table" @click="preview(scope.row.resourceId)">
|
||||
<Icon name="icon_pc_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<ShareHandler
|
||||
:in-grid="true"
|
||||
:resource-id="scope.row.resourceId"
|
||||
:weight="scope.row.weight"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</GridTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search {
|
||||
text-align: right;
|
||||
.ed-input {
|
||||
width: 240px;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-table {
|
||||
margin-top: 16px;
|
||||
height: calc(100% - 110px);
|
||||
|
||||
.name-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.main-color {
|
||||
font-size: 21.33px;
|
||||
padding: 5.33px;
|
||||
margin-right: 12px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
background: #3370ff;
|
||||
}
|
||||
.name-star {
|
||||
font-size: 15px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
.workbranch-grid :deep(.ed-empty) {
|
||||
padding: 80px 0 !important;
|
||||
.ed-empty__description {
|
||||
margin-top: 0px;
|
||||
line-height: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
370
core/core-frontend/src/views/share/share/ShareHandler.vue
Normal file
370
core/core-frontend/src/views/share/share/ShareHandler.vue
Normal file
@ -0,0 +1,370 @@
|
||||
<template>
|
||||
<el-tooltip
|
||||
v-if="props.weight >= 7 && props.inGrid"
|
||||
effect="dark"
|
||||
:content="t('visualization.share')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon class="hover-icon hover-icon-in-table share-button-icon" @click="share">
|
||||
<Icon name="icon_share-label_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-button v-if="props.weight >= 7 && props.isButton" @click="share" icon="Share">{{
|
||||
t('visualization.share')
|
||||
}}</el-button>
|
||||
|
||||
<el-dialog
|
||||
v-if="dialogVisible && props.weight >= 7"
|
||||
class="copy-link_dialog"
|
||||
:class="{ 'hidden-footer': !shareEnable }"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="true"
|
||||
:append-to-body="true"
|
||||
title="公共链接分享"
|
||||
width="480px"
|
||||
:show-close="false"
|
||||
>
|
||||
<div class="share-dialog-container">
|
||||
<div class="copy-link">
|
||||
<div class="open-share flex-align-center">
|
||||
<el-switch size="small" v-model="shareEnable" @change="enableSwitcher" />
|
||||
{{ shareTips }}
|
||||
</div>
|
||||
<div v-if="shareEnable" class="text">{{ linkAddr }}</div>
|
||||
<div v-if="shareEnable" class="exp-container">
|
||||
<el-checkbox
|
||||
:disabled="!shareEnable"
|
||||
v-model="overTimeEnable"
|
||||
@change="expEnableSwitcher"
|
||||
:label="t('visualization.over_time')"
|
||||
/>
|
||||
<div class="inline-share-item-picker">
|
||||
<el-date-picker
|
||||
:clearable="false"
|
||||
size="small"
|
||||
v-if="state.detailInfo.exp"
|
||||
class="share-exp-picker"
|
||||
v-model="state.detailInfo.exp"
|
||||
type="datetime"
|
||||
placeholder=""
|
||||
:shortcuts="shortcuts"
|
||||
@change="expChangeHandler"
|
||||
:disabled-date="disabledDate"
|
||||
value-format="x"
|
||||
/>
|
||||
<span v-if="expError" class="exp-error">必须大于当前时间</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="shareEnable" class="pwd-container">
|
||||
<el-checkbox
|
||||
:disabled="!shareEnable"
|
||||
v-model="passwdEnable"
|
||||
@change="pwdEnableSwitcher"
|
||||
:label="t('visualization.passwd_protect')"
|
||||
/>
|
||||
|
||||
<div class="inline-share-item" v-if="state.detailInfo.pwd">
|
||||
<el-input v-model="state.detailInfo.pwd" readonly size="small">
|
||||
<template #append>
|
||||
<div @click="resetPwd" class="share-reset-container">
|
||||
<span>{{ t('commons.reset') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="!shareEnable || expError" type="primary" @click="copyInfo">
|
||||
{{ t('visualization.copy_link') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import request from '@/config/axios'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ShareInfo, SHARE_BASE, shortcuts } from './option'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
const { toClipboard } = useClipboard()
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
inGrid: propTypes.bool.def(false),
|
||||
resourceId: propTypes.string.def(''),
|
||||
resourceType: propTypes.string.def(''),
|
||||
weight: propTypes.number.def(0),
|
||||
isButton: propTypes.bool.def(false)
|
||||
})
|
||||
const loadingInstance = ref<any>(null)
|
||||
const dialogVisible = ref(false)
|
||||
const overTimeEnable = ref(false)
|
||||
const passwdEnable = ref(false)
|
||||
const shareEnable = ref(false)
|
||||
const linkAddr = ref('')
|
||||
const expError = ref(false)
|
||||
const state = reactive({
|
||||
detailInfo: {
|
||||
id: '',
|
||||
uuid: '',
|
||||
pwd: '',
|
||||
exp: 0
|
||||
} as ShareInfo
|
||||
})
|
||||
const emits = defineEmits(['loaded'])
|
||||
const shareTips = computed(
|
||||
() =>
|
||||
`开启后,用户可以通过该链接访问${props.resourceType === 'dashboard' ? '仪表板' : '数据大屏'}`
|
||||
)
|
||||
|
||||
const copyInfo = async () => {
|
||||
if (shareEnable.value) {
|
||||
try {
|
||||
await toClipboard(linkAddr.value)
|
||||
ElMessage.success(t('common.copy_success'))
|
||||
} catch (e) {
|
||||
ElMessage.warning(t('common.copy_unsupported'))
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning(t('common.copy_unsupported'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const disabledDate = date => {
|
||||
return date.getTime() < new Date().getTime()
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({ target: '.share-dialog-container' })
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const share = () => {
|
||||
dialogVisible.value = true
|
||||
loadShareInfo()
|
||||
}
|
||||
|
||||
const loadShareInfo = () => {
|
||||
showLoading()
|
||||
const resourceId = props.resourceId
|
||||
const url = `/share/detail/${resourceId}`
|
||||
request
|
||||
.get({ url })
|
||||
.then(res => {
|
||||
state.detailInfo = { ...res.data }
|
||||
setPageInfo()
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
|
||||
const setPageInfo = () => {
|
||||
if (state.detailInfo.id && state.detailInfo.uuid) {
|
||||
shareEnable.value = true
|
||||
formatLinkAddr()
|
||||
}
|
||||
passwdEnable.value = !!state.detailInfo.pwd
|
||||
overTimeEnable.value = !!state.detailInfo.exp
|
||||
}
|
||||
|
||||
const enableSwitcher = () => {
|
||||
const resourceId = props.resourceId
|
||||
const url = `/share/switcher/${resourceId}`
|
||||
request.post({ url }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const formatLinkAddr = () => {
|
||||
const href = window.location.href
|
||||
const prefix = href.substring(0, href.indexOf('#') + 1)
|
||||
linkAddr.value = prefix + SHARE_BASE + state.detailInfo.uuid
|
||||
}
|
||||
|
||||
const expEnableSwitcher = val => {
|
||||
let exp = 0
|
||||
if (val) {
|
||||
const now = new Date()
|
||||
now.setTime(now.getTime() + 3600 * 1000)
|
||||
exp = now.getTime()
|
||||
state.detailInfo.exp = exp
|
||||
}
|
||||
expChangeHandler(exp)
|
||||
}
|
||||
|
||||
const expChangeHandler = exp => {
|
||||
if (overTimeEnable.value && exp < new Date().getTime()) {
|
||||
expError.value = true
|
||||
return
|
||||
}
|
||||
expError.value = false
|
||||
const resourceId = props.resourceId
|
||||
const url = '/share/editExp'
|
||||
const data = { resourceId, exp }
|
||||
request.post({ url, data }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const pwdEnableSwitcher = val => {
|
||||
let pwd = ''
|
||||
if (val) {
|
||||
pwd = getUuid()
|
||||
}
|
||||
resetPwdHandler(pwd)
|
||||
}
|
||||
const resetPwd = () => {
|
||||
const pwd = getUuid()
|
||||
resetPwdHandler(pwd)
|
||||
}
|
||||
const resetPwdHandler = (pwd?: string) => {
|
||||
const resourceId = props.resourceId
|
||||
const url = '/share/editPwd'
|
||||
const data = { resourceId, pwd }
|
||||
request.post({ url, data }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const getUuid = () => {
|
||||
return 'xyxy'.replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
const execute = () => {
|
||||
share()
|
||||
}
|
||||
defineExpose({
|
||||
execute
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!props.inGrid && props.weight >= 7) {
|
||||
const commandInfo = {
|
||||
label: '分享',
|
||||
command: 'share',
|
||||
svgName: 'dv-share'
|
||||
}
|
||||
emits('loaded', commandInfo)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
.copy-link_dialog {
|
||||
.ed-dialog__header {
|
||||
padding: 16px 16px 10px !important;
|
||||
.ed-dialog__title {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
}
|
||||
.ed-dialog__body {
|
||||
padding: 16px !important;
|
||||
}
|
||||
.ed-dialog__footer {
|
||||
border-top: 1px solid #1f232926;
|
||||
padding: 12px 16px 16px;
|
||||
}
|
||||
}
|
||||
.hidden-footer {
|
||||
.ed-dialog__footer {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.share-button-icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.copy-link_dialog {
|
||||
.exp-container {
|
||||
.ed-checkbox {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.inline-share-item-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
:deep(.share-exp-picker) {
|
||||
margin-left: 25px !important;
|
||||
.ed-input__wrapper {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
.exp-error {
|
||||
color: var(--ed-color-danger);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pwd-container {
|
||||
.ed-checkbox {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.inline-share-item {
|
||||
margin-left: 25px;
|
||||
width: 220px;
|
||||
|
||||
:deep(.ed-input-group__append) {
|
||||
width: 45px !important;
|
||||
background: none;
|
||||
color: #1f2329;
|
||||
padding: 0px 0px !important;
|
||||
.share-reset-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f5f6f7;
|
||||
}
|
||||
&:active {
|
||||
cursor: pointer;
|
||||
background-color: #eff0f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copy-link {
|
||||
font-weight: 400;
|
||||
font-family: PingFang SC;
|
||||
|
||||
.open-share {
|
||||
margin: -18px 0 8px 0;
|
||||
color: #646a73;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
.ed-switch {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bbbfc4;
|
||||
background: #eff0f1;
|
||||
margin-bottom: 16px;
|
||||
height: 32px;
|
||||
padding: 5px 12px;
|
||||
color: #8f959e;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
14
core/core-frontend/src/views/share/share/SharePanel.vue
Normal file
14
core/core-frontend/src/views/share/share/SharePanel.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { onMounted } from 'vue'
|
||||
const { t } = useI18n()
|
||||
|
||||
const emits = defineEmits(['loaded'])
|
||||
const panelInfo = {
|
||||
title: t('visualization.share_out'),
|
||||
name: 'share'
|
||||
}
|
||||
onMounted(() => {
|
||||
emits('loaded', panelInfo)
|
||||
})
|
||||
</script>
|
332
core/core-frontend/src/views/share/share/ShareVisualHead.vue
Normal file
332
core/core-frontend/src/views/share/share/ShareVisualHead.vue
Normal file
@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<el-button ref="shareButtonRef" v-if="props.weight >= 7" v-click-outside="openShare">
|
||||
<template #icon>
|
||||
<icon name="icon_share-label_outlined"></icon>
|
||||
</template>
|
||||
{{ t('visualization.share') }}
|
||||
</el-button>
|
||||
<el-popover
|
||||
ref="sharePopoverRef"
|
||||
:virtual-ref="shareButtonRef"
|
||||
trigger="click"
|
||||
title=""
|
||||
virtual-triggering
|
||||
width="480"
|
||||
placement="bottom-start"
|
||||
:show-arrow="false"
|
||||
popper-class="share-popover"
|
||||
@show="share"
|
||||
>
|
||||
<div class="share-container">
|
||||
<div class="share-title share-padding">公共链接分享</div>
|
||||
<div class="open-share flex-align-center share-padding">
|
||||
<el-switch size="small" v-model="shareEnable" @change="enableSwitcher" />
|
||||
{{ shareTips }}
|
||||
</div>
|
||||
<div v-if="shareEnable" class="text share-padding">
|
||||
<el-input v-model="linkAddr" disabled />
|
||||
</div>
|
||||
<div v-if="shareEnable" class="exp-container share-padding">
|
||||
<el-checkbox
|
||||
:disabled="!shareEnable"
|
||||
v-model="overTimeEnable"
|
||||
@change="expEnableSwitcher"
|
||||
:label="t('visualization.over_time')"
|
||||
/>
|
||||
<div class="inline-share-item-picker">
|
||||
<el-date-picker
|
||||
:clearable="false"
|
||||
size="small"
|
||||
class="share-exp-picker"
|
||||
v-if="state.detailInfo.exp"
|
||||
v-model="state.detailInfo.exp"
|
||||
type="datetime"
|
||||
:teleported="false"
|
||||
placeholder=""
|
||||
:shortcuts="shortcuts"
|
||||
@change="expChangeHandler"
|
||||
:disabled-date="disabledDate"
|
||||
value-format="x"
|
||||
/>
|
||||
<span v-if="expError" class="exp-error">必须大于当前时间</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="shareEnable" class="pwd-container share-padding">
|
||||
<el-checkbox
|
||||
:disabled="!shareEnable"
|
||||
v-model="passwdEnable"
|
||||
@change="pwdEnableSwitcher"
|
||||
:label="t('visualization.passwd_protect')"
|
||||
/>
|
||||
<div class="inline-share-item" v-if="state.detailInfo.pwd">
|
||||
<el-input v-model="state.detailInfo.pwd" readonly size="small">
|
||||
<template #append>
|
||||
<div @click="resetPwd" class="share-reset-container">
|
||||
<span>{{ t('commons.reset') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider v-if="shareEnable" class="share-divider" />
|
||||
<div v-if="shareEnable" class="share-foot share-padding">
|
||||
<el-button :disabled="!shareEnable || expError" type="primary" @click="copyInfo">
|
||||
{{ t('visualization.copy_link') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { ref, reactive, unref, computed } from 'vue'
|
||||
import request from '@/config/axios'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { ShareInfo, SHARE_BASE, shortcuts } from './option'
|
||||
import { ElMessage, ElLoading } from 'element-plus-secondary'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
const { toClipboard } = useClipboard()
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
resourceId: propTypes.string.def(''),
|
||||
resourceType: propTypes.string.def(''),
|
||||
weight: propTypes.number.def(0)
|
||||
})
|
||||
const shareButtonRef = ref()
|
||||
const sharePopoverRef = ref()
|
||||
|
||||
const loadingInstance = ref<any>(null)
|
||||
const dialogVisible = ref(false)
|
||||
const overTimeEnable = ref(false)
|
||||
const passwdEnable = ref(false)
|
||||
const shareEnable = ref(false)
|
||||
const linkAddr = ref('')
|
||||
const expError = ref(false)
|
||||
const state = reactive({
|
||||
detailInfo: {
|
||||
id: '',
|
||||
uuid: '',
|
||||
pwd: '',
|
||||
exp: 0
|
||||
} as ShareInfo
|
||||
})
|
||||
|
||||
const openShare = () => {
|
||||
unref(sharePopoverRef).popperRef?.delayHide?.()
|
||||
}
|
||||
|
||||
const shareTips = computed(
|
||||
() =>
|
||||
`开启后,用户可以通过该链接访问${props.resourceType === 'dashboard' ? '仪表板' : '数据大屏'}`
|
||||
)
|
||||
|
||||
const copyInfo = async () => {
|
||||
if (shareEnable.value) {
|
||||
try {
|
||||
await toClipboard(linkAddr.value)
|
||||
ElMessage.success(t('common.copy_success'))
|
||||
} catch (e) {
|
||||
ElMessage.warning(t('common.copy_unsupported'))
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning(t('common.copy_unsupported'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
openShare()
|
||||
}
|
||||
|
||||
const disabledDate = date => {
|
||||
return date.getTime() < new Date().getTime()
|
||||
}
|
||||
|
||||
const showLoading = () => {
|
||||
loadingInstance.value = ElLoading.service({ target: '.share-dialog-container' })
|
||||
}
|
||||
const closeLoading = () => {
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
|
||||
const share = () => {
|
||||
dialogVisible.value = true
|
||||
loadShareInfo()
|
||||
}
|
||||
|
||||
const loadShareInfo = () => {
|
||||
showLoading()
|
||||
const resourceId = props.resourceId
|
||||
const url = `/share/detail/${resourceId}`
|
||||
request
|
||||
.get({ url })
|
||||
.then(res => {
|
||||
state.detailInfo = { ...res.data }
|
||||
setPageInfo()
|
||||
})
|
||||
.finally(() => {
|
||||
closeLoading()
|
||||
})
|
||||
}
|
||||
|
||||
const setPageInfo = () => {
|
||||
if (state.detailInfo.id && state.detailInfo.uuid) {
|
||||
shareEnable.value = true
|
||||
formatLinkAddr()
|
||||
passwdEnable.value = !!state.detailInfo.pwd
|
||||
overTimeEnable.value = !!state.detailInfo.exp
|
||||
} else {
|
||||
shareEnable.value = false
|
||||
passwdEnable.value = false
|
||||
overTimeEnable.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const enableSwitcher = () => {
|
||||
const resourceId = props.resourceId
|
||||
const url = `/share/switcher/${resourceId}`
|
||||
request.post({ url }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const formatLinkAddr = () => {
|
||||
const href = window.location.href
|
||||
const prefix = href.substring(0, href.indexOf('#') + 1)
|
||||
linkAddr.value = prefix + SHARE_BASE + state.detailInfo.uuid
|
||||
}
|
||||
|
||||
const expEnableSwitcher = val => {
|
||||
let exp = 0
|
||||
if (val) {
|
||||
const now = new Date()
|
||||
now.setTime(now.getTime() + 3600 * 1000)
|
||||
exp = now.getTime()
|
||||
state.detailInfo.exp = exp
|
||||
}
|
||||
expChangeHandler(exp)
|
||||
}
|
||||
|
||||
const expChangeHandler = exp => {
|
||||
if (overTimeEnable.value && exp < new Date().getTime()) {
|
||||
expError.value = true
|
||||
return
|
||||
}
|
||||
expError.value = false
|
||||
const resourceId = props.resourceId
|
||||
const url = '/share/editExp'
|
||||
const data = { resourceId, exp }
|
||||
request.post({ url, data }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const pwdEnableSwitcher = val => {
|
||||
let pwd = ''
|
||||
if (val) {
|
||||
pwd = getUuid()
|
||||
}
|
||||
resetPwdHandler(pwd)
|
||||
}
|
||||
const resetPwd = () => {
|
||||
const pwd = getUuid()
|
||||
resetPwdHandler(pwd)
|
||||
}
|
||||
const resetPwdHandler = (pwd?: string) => {
|
||||
const resourceId = props.resourceId
|
||||
const url = '/share/editPwd'
|
||||
const data = { resourceId, pwd }
|
||||
request.post({ url, data }).then(() => {
|
||||
loadShareInfo()
|
||||
})
|
||||
}
|
||||
|
||||
const getUuid = () => {
|
||||
return 'xyxy'.replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
const execute = () => {
|
||||
share()
|
||||
}
|
||||
defineExpose({
|
||||
execute
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.share-popover {
|
||||
padding: 16px 0px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.share-container {
|
||||
.share-title {
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
.share-padding {
|
||||
padding: 0px 16px;
|
||||
}
|
||||
.share-divider {
|
||||
margin-bottom: 10px !important;
|
||||
border-top: 1px #1f232926 solid;
|
||||
}
|
||||
.share-foot {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.open-share {
|
||||
font-size: 12px;
|
||||
color: #646a73;
|
||||
.ed-switch {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
padding-bottom: 5px !important;
|
||||
}
|
||||
}
|
||||
.inline-share-item-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
:deep(.share-exp-picker) {
|
||||
margin-left: 25px !important;
|
||||
.ed-input__wrapper {
|
||||
width: 200px !important;
|
||||
}
|
||||
}
|
||||
.exp-error {
|
||||
color: var(--ed-color-danger);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.inline-share-item {
|
||||
margin-left: 25px;
|
||||
width: 220px;
|
||||
|
||||
:deep(.ed-input-group__append) {
|
||||
width: 45px !important;
|
||||
background: none;
|
||||
color: #1f2329;
|
||||
padding: 0px 0px !important;
|
||||
.share-reset-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f5f6f7;
|
||||
}
|
||||
&:active {
|
||||
cursor: pointer;
|
||||
background-color: #eff0f1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
35
core/core-frontend/src/views/share/share/option.ts
Normal file
35
core/core-frontend/src/views/share/share/option.ts
Normal file
@ -0,0 +1,35 @@
|
||||
export interface ShareInfo {
|
||||
id: string
|
||||
exp?: number
|
||||
uuid: string
|
||||
pwd?: string
|
||||
}
|
||||
|
||||
export const SHARE_BASE = '/de-link/'
|
||||
|
||||
export const shortcuts = [
|
||||
{
|
||||
text: '一小时',
|
||||
value: () => {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() + 3600 * 1000)
|
||||
return date
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '一天',
|
||||
value: () => {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() + 3600 * 1000 * 24)
|
||||
return date
|
||||
}
|
||||
},
|
||||
{
|
||||
text: '一周',
|
||||
value: () => {
|
||||
const date = new Date()
|
||||
date.setTime(date.getTime() + 7 * 3600 * 1000 * 24)
|
||||
return date
|
||||
}
|
||||
}
|
||||
]
|
@ -15,7 +15,8 @@ const options = [
|
||||
const state = reactive({
|
||||
form: reactive({
|
||||
dsIntervalTime: '30',
|
||||
dsExecuteTime: 'minute'
|
||||
dsExecuteTime: 'minute',
|
||||
frontTimeOut: '30'
|
||||
}),
|
||||
settingList: []
|
||||
})
|
||||
@ -165,7 +166,19 @@ defineExpose({
|
||||
</el-select>
|
||||
<span class="ds-span">执行一次</span>
|
||||
</div>
|
||||
<div v-else />
|
||||
<div v-else-if="item.pkey === 'frontTimeOut'">
|
||||
<el-input-number
|
||||
v-model="state.form.frontTimeOut"
|
||||
autocomplete="off"
|
||||
step-strictly
|
||||
class="text-left"
|
||||
:min="1"
|
||||
:placeholder="t('common.inputText')"
|
||||
controls-position="right"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<v-else />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
|
@ -22,7 +22,7 @@ const editor = ref()
|
||||
const infoTemplate = ref()
|
||||
const tooltips = [
|
||||
{
|
||||
key: '请求超时时间',
|
||||
key: 'setting_basic.frontTimeOut',
|
||||
val: '请求超时时间(单位:秒,注意:保存后刷新浏览器生效)'
|
||||
}
|
||||
]
|
||||
@ -45,6 +45,7 @@ const search = cb => {
|
||||
item.pval = item.pval
|
||||
}
|
||||
item.pkey = 'setting_' + item.pkey
|
||||
console.log(item.pkey)
|
||||
state.templateList.push(item)
|
||||
}
|
||||
cb && cb()
|
||||
|
@ -103,7 +103,7 @@ const rule = reactive<FormRules>({
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 25,
|
||||
max: 64,
|
||||
message: t('datasource.input_limit_2_25', [2, 64]),
|
||||
trigger: 'blur'
|
||||
}
|
||||
|
@ -272,6 +272,7 @@ const saveDataset = () => {
|
||||
showClose: false,
|
||||
tip: ''
|
||||
}
|
||||
request.apiConfiguration = ''
|
||||
checkRepeat(request).then(res => {
|
||||
if (res) {
|
||||
ElMessageBox.confirm(t('datasource.has_same_ds'), options as ElMessageBoxOptions).then(
|
||||
|
@ -64,7 +64,7 @@ const defaultRule = {
|
||||
},
|
||||
{
|
||||
min: 2,
|
||||
max: 25,
|
||||
max: 64,
|
||||
message: t('datasource.input_limit_2_25', [2, 64]),
|
||||
trigger: 'blur'
|
||||
}
|
||||
|
@ -391,6 +391,7 @@ const saveDS = () => {
|
||||
request.configuration = Base64.encode(JSON.stringify(request.configuration))
|
||||
}
|
||||
const validate = detail.value.submitForm()
|
||||
request.apiConfiguration = ''
|
||||
validate(val => {
|
||||
if (val) {
|
||||
if (editDs.value && form.id) {
|
||||
@ -401,7 +402,6 @@ const saveDS = () => {
|
||||
showClose: false,
|
||||
tip: ''
|
||||
}
|
||||
|
||||
checkRepeat(request).then(res => {
|
||||
if (res) {
|
||||
ElMessageBox.confirm(t('datasource.has_same_ds'), options as ElMessageBoxOptions).then(
|
||||
|
@ -6,11 +6,13 @@ import GridTable from '@/components/grid-table/src/GridTable.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { shortcutOption } from './ShortcutOption'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
/* import { XpackComponent } from '@/components/plugin' */
|
||||
import { interactiveStoreWithOut } from '@/store/modules/interactive'
|
||||
import { storeApi } from '@/api/visualization/dataVisualization'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import ShareGrid from '@/views/share/share/ShareGrid.vue'
|
||||
import ShareHandler from '@/views/share/share/ShareHandler.vue'
|
||||
const userStore = useUserStoreWithOut()
|
||||
const { resolve } = useRouter()
|
||||
const { t } = useI18n()
|
||||
@ -111,17 +113,18 @@ const loadTableData = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const panelLoad = paneInfo => {
|
||||
/* const panelLoad = paneInfo => {
|
||||
tablePaneList.value.push({
|
||||
title: paneInfo.title,
|
||||
name: paneInfo.name,
|
||||
disabled: tablePaneList.value[1].disabled
|
||||
})
|
||||
}
|
||||
} */
|
||||
|
||||
const tablePaneList = ref([
|
||||
{ title: '最近使用', name: 'recent', disabled: false },
|
||||
{ title: '我的收藏', name: 'store', disabled: false }
|
||||
{ title: '我的收藏', name: 'store', disabled: false },
|
||||
{ title: t('visualization.share_out'), name: 'share', disabled: false }
|
||||
])
|
||||
|
||||
const busiAuthList = getBusiListWithPermission()
|
||||
@ -206,8 +209,9 @@ const getEmptyDesc = (): string => {
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<XpackComponent jsname="c2hhcmUtcGFuZWw=" @loaded="panelLoad" />
|
||||
<XpackComponent :active-name="activeName" jsname="c2hhcmU=" @set-loading="setLoading" />
|
||||
<!-- <XpackComponent jsname="c2hhcmUtcGFuZWw=" @loaded="panelLoad" /> -->
|
||||
<!-- <XpackComponent :active-name="activeName" jsname="c2hhcmU=" @set-loading="setLoading" /> -->
|
||||
<share-grid :active-name="activeName" @set-loading="setLoading" />
|
||||
<el-row v-if="activeName === 'recent' || activeName === 'store'">
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
@ -304,14 +308,19 @@ const getEmptyDesc = (): string => {
|
||||
<Icon name="icon_pc_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<XpackComponent
|
||||
<ShareHandler
|
||||
:in-grid="true"
|
||||
:weight="scope.row.weight"
|
||||
:resource-id="activeName === 'recent' ? scope.row.id : scope.row.resourceId"
|
||||
:resource-type="scope.row.type"
|
||||
/>
|
||||
<!-- <XpackComponent
|
||||
:in-grid="true"
|
||||
jsname="c2hhcmUtaGFuZGxlcg=="
|
||||
:weight="scope.row.weight"
|
||||
:resource-id="activeName === 'recent' ? scope.row.id : scope.row.resourceId"
|
||||
:resource-type="scope.row.type"
|
||||
/>
|
||||
/> -->
|
||||
</template>
|
||||
|
||||
<template v-if="['dataset'].includes(scope.row.type)">
|
||||
|
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit 8b378277440ec171aa960a537274017d0609551b
|
||||
Subproject commit c941619c1cb929453b6a8bf4517e3f0036232128
|
11
installer/dataease/docker-compose-task.yml
Normal file
11
installer/dataease/docker-compose-task.yml
Normal file
@ -0,0 +1,11 @@
|
||||
version: '2.1'
|
||||
services:
|
||||
task-actuator:
|
||||
image: registry.cn-qingdao.aliyuncs.com/dataease/dataease-sync-task:DE_TAG
|
||||
container_name: sync-task-actuator
|
||||
ports:
|
||||
- 9001:9001
|
||||
volumes:
|
||||
- ${DE_BASE}/dataease2.0/logs:/opt/dataease2.0/logs
|
||||
networks:
|
||||
- dataease-network
|
@ -13,4 +13,9 @@ spring:
|
||||
dataease:
|
||||
apisix-api:
|
||||
domain: http://apisix:9180
|
||||
key: DE_APISIX_KEY
|
||||
key: DE_APISIX_KEY
|
||||
task:
|
||||
executor:
|
||||
address: http://sync-task-actuator:9001
|
||||
log:
|
||||
path: /opt/dataease2.0/logs/sync-task/task-handler-log
|
@ -52,6 +52,14 @@ function _check_apisix_init() {
|
||||
_prepare_apisix
|
||||
fi
|
||||
}
|
||||
function _check_task_init() {
|
||||
if [[ $DE_INSTALL_MODE != "community" ]];then
|
||||
_prepare_task
|
||||
fi
|
||||
}
|
||||
function _prepare_task() {
|
||||
compose_files="${compose_files} -f docker-compose-task.yml"
|
||||
}
|
||||
function _prepare_apisix() {
|
||||
if [[ -z $DE_APISIX_KEY ]];then
|
||||
need_init_apisix=true
|
||||
@ -132,6 +140,7 @@ function status() {
|
||||
function start() {
|
||||
echo
|
||||
_check_apisix_init
|
||||
_check_task_init
|
||||
cd ${DE_RUNNING_BASE}
|
||||
${compose_cmd} ${compose_files} up -d
|
||||
_healthcheck
|
||||
|
@ -63,7 +63,8 @@ env | grep DE_ >.env
|
||||
mkdir -p ${DE_RUN_BASE}/{cache,logs,conf}
|
||||
mkdir -p ${DE_RUN_BASE}/data/{mysql,static-resource,map,etcd_data,geo}
|
||||
mkdir -p ${DE_RUN_BASE}/apisix/logs
|
||||
chmod 777 ${DE_RUN_BASE}/apisix/logs ${DE_RUN_BASE}/data/etcd_data
|
||||
mkdir -p ${DE_RUN_BASE}/task/logs
|
||||
chmod 777 ${DE_RUN_BASE}/apisix/logs ${DE_RUN_BASE}/data/etcd_data ${DE_RUN_BASE}/task/logs
|
||||
|
||||
if [ "${DE_EXTERNAL_MYSQL}" = "false" ]; then
|
||||
compose_files="${compose_files} -f docker-compose-mysql.yml"
|
||||
|
@ -39,4 +39,8 @@ public interface SysParameterApi {
|
||||
@PostMapping("/basic/save")
|
||||
void saveBasicSetting(@RequestBody List<SettingItemVO> settingItemVOS);
|
||||
|
||||
@Operation(summary = "查询超时时间(非xpack)")
|
||||
@GetMapping("/requestTimeOut")
|
||||
public Integer RequestTimeOut();
|
||||
|
||||
}
|
||||
|
20
sdk/api/api-sync/pom.xml
Normal file
20
sdk/api/api-sync/pom.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>api</artifactId>
|
||||
<groupId>io.dataease</groupId>
|
||||
<version>${dataease.version}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>api-sync</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
@ -0,0 +1,80 @@
|
||||
package io.dataease.api.sync.datasource.api;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.dataease.api.sync.datasource.dto.DBTableDTO;
|
||||
import io.dataease.api.sync.datasource.dto.GetDatasourceRequest;
|
||||
import io.dataease.api.sync.datasource.dto.SyncDatasourceDTO;
|
||||
import io.dataease.api.sync.datasource.vo.SyncDatasourceVO;
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import io.dataease.auth.DePermit;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.request.BaseGridRequest;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.dataease.constant.AuthResourceEnum.SYNC_DATASOURCE;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/20 10:14
|
||||
**/
|
||||
@DeApiPath(value = "/sync/datasource", rt = SYNC_DATASOURCE)
|
||||
public interface SyncDatasourceApi {
|
||||
|
||||
@DePermit("m:read")
|
||||
@PostMapping("/source/pager/{goPage}/{pageSize}")
|
||||
IPage<SyncDatasourceVO> sourcePager(@PathVariable("goPage") int goPage, @PathVariable("pageSize") int pageSize, @RequestBody BaseGridRequest request);
|
||||
|
||||
@DePermit("m:read")
|
||||
@PostMapping("/target/pager/{goPage}/{pageSize}")
|
||||
IPage<SyncDatasourceVO> targetPager(@PathVariable("goPage") int goPage, @PathVariable("pageSize") int pageSize, @RequestBody BaseGridRequest request);
|
||||
|
||||
@PostMapping("/save")
|
||||
void save(@RequestBody SyncDatasourceDTO dataSourceDTO) throws DEException;
|
||||
|
||||
@PostMapping("/update")
|
||||
void update(@RequestBody SyncDatasourceDTO dataSourceDTO) throws DEException;
|
||||
|
||||
@PostMapping("/delete/{datasourceId}")
|
||||
void delete(@PathVariable("datasourceId") String datasourceId) throws DEException;
|
||||
|
||||
@GetMapping("/types")
|
||||
Object datasourceTypes() throws DEException;
|
||||
|
||||
@PostMapping("/validate")
|
||||
String validate(@RequestBody SyncDatasourceDTO dataSourceDTO) throws DEException;
|
||||
|
||||
@PostMapping("/getSchema")
|
||||
List<String> getSchema(@RequestBody SyncDatasourceDTO dataSourceDTO) throws DEException;
|
||||
|
||||
@DePermit({"#p0+':manage'"})
|
||||
@GetMapping("/validate/{datasourceId}")
|
||||
SyncDatasourceDTO validate(@PathVariable("datasourceId") String datasourceId) throws DEException;
|
||||
|
||||
@PostMapping("/latestUse")
|
||||
List<String> latestUse();
|
||||
|
||||
@GetMapping("/get/{datasourceId}")
|
||||
SyncDatasourceDTO get(@PathVariable("datasourceId") String datasourceId) throws DEException;
|
||||
|
||||
@PostMapping("/batchDel")
|
||||
void batchDel(@RequestBody List<String> ids) throws DEException;
|
||||
|
||||
@PostMapping("/fields")
|
||||
Map<String, Object> getFields(@RequestBody GetDatasourceRequest getDsRequest) throws DEException ;
|
||||
|
||||
@GetMapping("/list/{type}")
|
||||
List<SyncDatasourceDTO> listByType(@PathVariable("type") String type) throws DEException;
|
||||
|
||||
@GetMapping("/table/list/{dsId}")
|
||||
List<DBTableDTO> getTableList(@PathVariable("dsId") String dsId) throws DEException;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.dataease.api.sync.datasource.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @Author gin
|
||||
* @Date 2021/4/30 10:57 上午
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DBTableDTO {
|
||||
private String datasourceId;
|
||||
private String name;
|
||||
private String remark;
|
||||
private boolean enableCheck;
|
||||
private String datasetPath;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.dataease.api.sync.datasource.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GetDatasourceRequest extends SyncDatasourceDTO {
|
||||
|
||||
/**
|
||||
* 查询sql
|
||||
*/
|
||||
private String query;
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String table;
|
||||
/**
|
||||
* 表格的抽取数据方式
|
||||
*/
|
||||
private boolean tableExtract;
|
||||
/**
|
||||
* 不为空时,获取源数据库表字段,将转换为doris的数据类型
|
||||
*/
|
||||
private String targetDbType;
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package io.dataease.api.sync.datasource.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/20 11:14
|
||||
**/
|
||||
@Data
|
||||
public class SyncDatasourceDTO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 详细信息
|
||||
*/
|
||||
private String configuration;
|
||||
|
||||
/**
|
||||
* Create timestamp
|
||||
*/
|
||||
private Long createTime;
|
||||
|
||||
/**
|
||||
* Update timestamp
|
||||
*/
|
||||
private Long updateTime;
|
||||
|
||||
/**
|
||||
* 创建人ID
|
||||
*/
|
||||
private Long createBy;
|
||||
private String createByUserName;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
private String statusRemark;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package io.dataease.api.sync.datasource.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/20 11:18
|
||||
**/
|
||||
@Data
|
||||
public class SyncDatasourceVO {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String desc;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 详细信息
|
||||
*/
|
||||
private String configuration;
|
||||
|
||||
/**
|
||||
* Create timestamp
|
||||
*/
|
||||
private Long createTime;
|
||||
|
||||
/**
|
||||
* Update timestamp
|
||||
*/
|
||||
private Long updateTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Long createBy;
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
private String statusRemark;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package io.dataease.api.sync.summary.api;
|
||||
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static io.dataease.constant.AuthResourceEnum.SUMMARY;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/12/4 12:43
|
||||
**/
|
||||
@DeApiPath(value = "/sync/summary", rt = SUMMARY)
|
||||
public interface SummaryApi {
|
||||
|
||||
@GetMapping("/resourceCount")
|
||||
Map<String, Long> resourceCount();
|
||||
|
||||
@PostMapping("/logChartData")
|
||||
Map<String, Object> logChartData(@RequestBody String executeTaskLogDate);
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package io.dataease.api.sync.task.api;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.dataease.api.sync.task.dto.TaskInfoDTO;
|
||||
import io.dataease.api.sync.task.vo.TaskInfoVO;
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.request.BaseGridRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.dataease.constant.AuthResourceEnum.TASK;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/20 10:14
|
||||
**/
|
||||
@DeApiPath(value = "/sync/task", rt = TASK)
|
||||
public interface TaskApi {
|
||||
|
||||
@PostMapping("/pager/{goPage}/{pageSize}")
|
||||
IPage<TaskInfoVO> pager(@PathVariable("goPage") int goPage, @PathVariable("pageSize") int pageSize, @RequestBody BaseGridRequest request);
|
||||
|
||||
@PostMapping("/add")
|
||||
void add(@RequestBody TaskInfoDTO jobInfo) throws DEException;
|
||||
|
||||
@PostMapping("/update")
|
||||
void update(@RequestBody TaskInfoDTO jobInfo) throws DEException;
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
void remove(@PathVariable(value = "id") String id) throws DEException;
|
||||
|
||||
@GetMapping("start/{id}")
|
||||
void startJob(@PathVariable(value = "id") String id) throws DEException;
|
||||
|
||||
@GetMapping("stop/{id}")
|
||||
void stopJob(@PathVariable(value = "id") String id) throws DEException;
|
||||
|
||||
@GetMapping("/get/{id}")
|
||||
TaskInfoVO getOneById(@PathVariable(value = "id") String id) throws DEException;
|
||||
|
||||
@GetMapping("/execute/{id}")
|
||||
void execute(@PathVariable(value = "id") String id) throws DEException;
|
||||
|
||||
@PostMapping("/batch/del")
|
||||
void batchDelete(@RequestBody List<String> ids) throws DEException;
|
||||
|
||||
@GetMapping("/count")
|
||||
Long count() throws DEException;
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package io.dataease.api.sync.task.api;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.dataease.api.sync.task.vo.LogResultVO;
|
||||
import io.dataease.api.sync.task.vo.TaskLogVO;
|
||||
import io.dataease.auth.DeApiPath;
|
||||
import io.dataease.request.BaseGridRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import static io.dataease.constant.AuthResourceEnum.TASK_LOG;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/12/4 12:43
|
||||
**/
|
||||
@DeApiPath(value = "/sync/task/log", rt = TASK_LOG)
|
||||
public interface TaskLogApi {
|
||||
@PostMapping("/pager/{goPage}/{pageSize}")
|
||||
IPage<TaskLogVO> pager(@PathVariable("goPage") int goPage, @PathVariable("pageSize") int pageSize, @RequestBody BaseGridRequest request);
|
||||
|
||||
@GetMapping("/detail/{logId}/{fromLineNum}")
|
||||
LogResultVO logDetail(@PathVariable("logId") String logId, @PathVariable("fromLineNum") int fromLineNum);
|
||||
|
||||
@PostMapping("/save")
|
||||
void saveLog(@RequestBody TaskLogVO logDetail);
|
||||
|
||||
@PostMapping("/update")
|
||||
void updateLog(@RequestBody TaskLogVO logDetail);
|
||||
|
||||
@DeleteMapping("/deleteByJobId/{jobId}")
|
||||
void deleteByJobId(@PathVariable("jobId") String jobId);
|
||||
|
||||
@DeleteMapping("/delete/{logId}")
|
||||
void deleteById(@PathVariable("logId") String logId);
|
||||
|
||||
@PostMapping("/clear")
|
||||
void clearJobLog(@RequestBody TaskLogVO taskLogVO);
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.dataease.api.sync.task.dto;
|
||||
|
||||
import io.dataease.api.sync.datasource.dto.SyncDatasourceDTO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/8/10 16:38
|
||||
**/
|
||||
@Data
|
||||
public class Source {
|
||||
private String type;
|
||||
private String query;
|
||||
private String tables;
|
||||
private SyncDatasourceDTO datasource;
|
||||
private String datasourceId;
|
||||
private String tableExtract;
|
||||
private List<TableField> fieldList;
|
||||
private String incrementField;
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.dataease.api.sync.task.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class TableField {
|
||||
private String fieldSource;
|
||||
private String fieldName;
|
||||
private String remarks;
|
||||
private String fieldType;
|
||||
private int fieldSize;
|
||||
private boolean fieldPk;
|
||||
private boolean fieldIndex;
|
||||
private int accuracy;
|
||||
private Object defaultValue;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.dataease.api.sync.task.dto;
|
||||
|
||||
import io.dataease.api.sync.datasource.dto.SyncDatasourceDTO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/8/10 16:39
|
||||
**/
|
||||
@Data
|
||||
public class Target {
|
||||
private String type;
|
||||
private String createTable;
|
||||
private List<TableField> fieldList;
|
||||
private String tableName;
|
||||
private SyncDatasourceDTO datasource;
|
||||
private String datasourceId;
|
||||
private String targetProperty;
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package io.dataease.api.sync.task.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/28 17:17
|
||||
**/
|
||||
@Data
|
||||
public class TaskInfoDTO {
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 任务类型KEY
|
||||
*/
|
||||
private String jobKey;
|
||||
|
||||
private String desc;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private LocalDateTime modifyTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
private Long modifyBy;
|
||||
|
||||
/**
|
||||
* 任务参数
|
||||
*/
|
||||
private String parameter;
|
||||
|
||||
/**
|
||||
* 扩展参数
|
||||
*/
|
||||
private String extParameter;
|
||||
|
||||
/**
|
||||
* 当前任务状态
|
||||
* unexecuted未执行 currentTime<startTime
|
||||
* waiting等待执行 stopTime>=currentTime>=startTime,status=1
|
||||
* suspend暂停 stopTime>=currentTime>=startTime,status=0
|
||||
* done执行结束 currentTime>stopTime
|
||||
* running执行中,通过当前任务的日志状态判断,如果有日志在执行中
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*/
|
||||
private Boolean deleted;
|
||||
|
||||
/**
|
||||
* 任务执行超时时间
|
||||
*/
|
||||
private Long executorTimeout;
|
||||
|
||||
/**
|
||||
* 任务执行失败重试次数
|
||||
*/
|
||||
private Long executorFailRetryCount;
|
||||
|
||||
/**
|
||||
* 上次调度时间
|
||||
*/
|
||||
private Long triggerLastTime;
|
||||
|
||||
/**
|
||||
* 下次次调度时间
|
||||
*/
|
||||
private Long triggerNextTime;
|
||||
|
||||
/**
|
||||
* 调度类型,NONE,CRON,FIX_RATE,FIX_DELAY
|
||||
*/
|
||||
private String schedulerType;
|
||||
|
||||
/**
|
||||
* 调度配置,取决于调度类型
|
||||
*/
|
||||
private String schedulerConf;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private String stopTime;
|
||||
|
||||
|
||||
/**
|
||||
* 源数据源信息
|
||||
*/
|
||||
private Source source;
|
||||
/**
|
||||
* 目标数据源信息
|
||||
*/
|
||||
private Target target;
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package io.dataease.api.sync.task.vo;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 日志返回结果
|
||||
*
|
||||
* @author fit2cloud
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class LogResultVO {
|
||||
|
||||
/**
|
||||
* 日志开始行号
|
||||
*/
|
||||
private int fromLineNum;
|
||||
/**
|
||||
* 日志结束行号
|
||||
*/
|
||||
private int toLineNum;
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
private String logContent;
|
||||
/**
|
||||
* 是否是最后一行
|
||||
*/
|
||||
private boolean isEnd;
|
||||
|
||||
public LogResultVO() {
|
||||
|
||||
}
|
||||
|
||||
public LogResultVO(int fromLineNum, int toLineNum, String logContent, boolean isEnd) {
|
||||
this.fromLineNum = fromLineNum;
|
||||
this.toLineNum = toLineNum;
|
||||
this.logContent = logContent;
|
||||
this.isEnd = isEnd;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package io.dataease.api.sync.task.vo;
|
||||
|
||||
import io.dataease.api.sync.task.dto.Source;
|
||||
import io.dataease.api.sync.task.dto.Target;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author fit2cloud
|
||||
* @date 2023/11/28 17:15
|
||||
**/
|
||||
@Data
|
||||
public class TaskInfoVO {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 任务类型KEY
|
||||
*/
|
||||
private String jobKey;
|
||||
|
||||
private String desc;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private LocalDateTime modifyTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private Long createBy;;
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 任务参数
|
||||
*/
|
||||
private String parameter;
|
||||
|
||||
/**
|
||||
* 扩展参数
|
||||
*/
|
||||
private String extParameter;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
* unexecuted未执行 currentTime<startTime
|
||||
* waiting等待执行 stopTime>=currentTime>=startTime,status=1
|
||||
* suspend暂停 stopTime>=currentTime>=startTime,status=0
|
||||
* done执行结束 currentTime>stopTime
|
||||
* running执行中,通过当前任务的日志状态判断,如果有日志在执行中
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 删除标识
|
||||
*/
|
||||
private Boolean deleted;
|
||||
|
||||
/**
|
||||
* 任务执行超时时间
|
||||
*/
|
||||
private Long executorTimeout;
|
||||
|
||||
/**
|
||||
* 任务执行失败重试次数
|
||||
*/
|
||||
private Long executorFailRetryCount;
|
||||
|
||||
/**
|
||||
* 上次调度时间
|
||||
*/
|
||||
private Long triggerLastTime;
|
||||
|
||||
/**
|
||||
* 下次次调度时间
|
||||
*/
|
||||
private Long triggerNextTime;
|
||||
|
||||
/**
|
||||
* 调度类型,NONE,CRON,FIX_RATE,FIX_DELAY
|
||||
*/
|
||||
private String schedulerType;
|
||||
|
||||
/**
|
||||
* 调度配置,取决于调度类型
|
||||
*/
|
||||
private String schedulerConf;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Long startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private Long stopTime;
|
||||
|
||||
private Source source;
|
||||
private Target target;
|
||||
|
||||
/**
|
||||
* 上次执行结果,获取任务最新的日志状态
|
||||
* running执行中
|
||||
* success
|
||||
* fail失败
|
||||
*/
|
||||
private String lastExecuteStatus;
|
||||
private Long incrementValue;
|
||||
|
||||
// 以下为日志信息
|
||||
private String logId;
|
||||
private Long executorStartTime;
|
||||
private Long executorEndTime;
|
||||
private String executorMsg;
|
||||
/**
|
||||
* 成功SUCCESS,失败FAIL,执行中RUNNING
|
||||
*/
|
||||
private String logStatus;
|
||||
|
||||
/**
|
||||
* 在执行周期内
|
||||
*/
|
||||
private boolean withinCycle;
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.dataease.api.sync.task.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 任务日志
|
||||
* @author fit2cloud
|
||||
* @date 2023/9/19 17:44
|
||||
**/
|
||||
@Data
|
||||
public class TaskLogVO {
|
||||
|
||||
private String id;
|
||||
private String jobId;
|
||||
private String jobName;
|
||||
private String jobDesc;
|
||||
private Long executorStartTime;
|
||||
private Long executorEndTime;
|
||||
private String status;
|
||||
private String executorMsg;
|
||||
private String executorAddress;
|
||||
|
||||
private String clearType;
|
||||
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
<modules>
|
||||
<module>api-permissions</module>
|
||||
<module>api-base</module>
|
||||
<module>api-sync</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
@ -2,7 +2,7 @@ package io.dataease.constant;
|
||||
|
||||
public enum AuthResourceEnum {
|
||||
|
||||
PANEL(2, 1), SCREEN(3, 2), DATASET(5, 3), DATASOURCE(6, 4), SYSTEM(7, 0), USER(8, 5), ROLE(8, 6), ORG(9, 7);
|
||||
PANEL(2, 1), SCREEN(3, 2), DATASET(5, 3), DATASOURCE(6, 4), SYSTEM(7, 0), USER(8, 5), ROLE(8, 6), ORG(9, 7), SYNC_DATASOURCE(10, 8), TASK(11, 9),TASK_LOG(12, 10), SUMMARY(13, 11);
|
||||
|
||||
private long menuId;
|
||||
|
||||
|
@ -3,4 +3,5 @@ package io.dataease.constant;
|
||||
public class XpackSettingConstants {
|
||||
|
||||
public static final String AUTO_CREATE_USER = "basic.autoCreateUser";
|
||||
public static final String Front_Time_Out = "basic.frontTimeOut";
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ public class WhitelistUtils {
|
||||
"/panel.html",
|
||||
"/lark/info",
|
||||
"/lark/token",
|
||||
"/sysParameter/requestTimeOut",
|
||||
"/setting/authentication/status",
|
||||
"/");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user