forked from github/dataease
feat: 分享功能从xpack下放到core
This commit is contained in:
parent
b5eb2d8835
commit
20e44797f4
@ -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,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"
|
||||
|
@ -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"
|
||||
|
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
|
||||
}
|
||||
}
|
||||
]
|
@ -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 c46269ec581913e102d21ded8690ed387509db87
|
||||
Subproject commit 39beda8526d237673972ffc5addaf0e8ec569e80
|
Loading…
Reference in New Issue
Block a user