mirror of
https://github.com/dataease/dataease.git
synced 2026-06-17 21:08:31 +08:00
Merge remote-tracking branch 'origin/dev-v2' into dev-v2
This commit is contained in:
@@ -42,7 +42,7 @@ public class LinkInterceptor implements HandlerInterceptor {
|
||||
|
||||
String requestURI = ServletUtils.request().getRequestURI();
|
||||
if (StringUtils.startsWith(requestURI, WhitelistUtils.getContextPath())) {
|
||||
requestURI = requestURI.replaceFirst(WhitelistUtils.getContextPath(), "");
|
||||
requestURI = StringUtils.replaceOnce(requestURI, WhitelistUtils.getContextPath(), "");
|
||||
}
|
||||
if (StringUtils.startsWith(requestURI, AuthConstant.DE_API_PREFIX)) {
|
||||
requestURI = requestURI.replaceFirst(AuthConstant.DE_API_PREFIX, "");
|
||||
|
||||
@@ -27,6 +27,12 @@ watch(formatData, () => {
|
||||
watch([props.theme], () => {
|
||||
format()
|
||||
})
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
format()
|
||||
}
|
||||
)
|
||||
onMounted(() => {
|
||||
format()
|
||||
})
|
||||
@@ -3017,6 +3017,7 @@ export default {
|
||||
to_top: 'Pin to Top',
|
||||
publish_recover: 'Revert Publish',
|
||||
publish_tips1: 'Visible after publication',
|
||||
no_permission_tips: 'No permission',
|
||||
publish_tips2: 'Available after publication {0}',
|
||||
cancel_publish_tips: 'Successfully unpublished',
|
||||
resource_not_published: 'Resource not published',
|
||||
@@ -4894,6 +4895,8 @@ export default {
|
||||
add: 'Add Webhook',
|
||||
search_placeholder: 'Search by name',
|
||||
content_type: 'Content Type',
|
||||
msg_template: 'Message Template',
|
||||
msg_template_tips: 'Available placeholders: {t0}, {t1}, {t2}',
|
||||
del_confirm: 'Are you sure you want to delete this Webhook?',
|
||||
batch_del_confirm: 'Are you sure you want to delete {0} Webhooks?'
|
||||
},
|
||||
|
||||
@@ -2934,6 +2934,7 @@ export default {
|
||||
to_top: '置頂',
|
||||
publish_recover: '恢復到發佈版本',
|
||||
publish_tips1: '發佈後可查看',
|
||||
no_permission_tips: '當前資源無權限',
|
||||
publish_tips2: '發佈後可{0}',
|
||||
cancel_publish_tips: '取消發佈成功',
|
||||
resource_not_published: '该資源未發佈',
|
||||
@@ -4744,6 +4745,8 @@ export default {
|
||||
add: '添加 Webhook',
|
||||
search_placeholder: '通過名稱搜索',
|
||||
content_type: '內容類型',
|
||||
msg_template: '消息模板',
|
||||
msg_template_tips: '可用占位符:{t0}、{t1}、{t2}',
|
||||
del_confirm: '確定刪除該 Webhook嗎?',
|
||||
batch_del_confirm: '確定刪除 {0} 個 Webhook嗎'
|
||||
}
|
||||
|
||||
@@ -2940,6 +2940,7 @@ export default {
|
||||
to_top: '置顶',
|
||||
publish_recover: '恢复到发布版本',
|
||||
publish_tips1: '发布后可查看',
|
||||
no_permission_tips: '当前资源无权限',
|
||||
publish_tips2: '发布后可{0}',
|
||||
cancel_publish_tips: '取消发布成功',
|
||||
resource_not_published: '该资源未发布',
|
||||
@@ -4754,6 +4755,8 @@ export default {
|
||||
add: '添加 Webhook',
|
||||
search_placeholder: '通过名称搜索',
|
||||
content_type: '内容类型',
|
||||
msg_template: '消息模板',
|
||||
msg_template_tips: '可用占位符:{t0}、{t1}、{t2}',
|
||||
del_confirm: '确定删除该 Webhook吗?',
|
||||
batch_del_confirm: '确定删除 {0} 个 Webhook吗'
|
||||
},
|
||||
|
||||
@@ -111,6 +111,10 @@ function retain(value, n) {
|
||||
const tran = Math.round(value * Math.pow(10, n)) / Math.pow(10, n)
|
||||
let tranV = tran.toString()
|
||||
const newVal = tranV.indexOf('.')
|
||||
// 遇到科学计数法时用 toFixed(n) 转成普通小数字符串
|
||||
if (/e/i.test(tranV)) {
|
||||
tranV = tran.toFixed(n)
|
||||
}
|
||||
if (newVal < 0) {
|
||||
tranV += '.'
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ const props = defineProps({
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
disabled: (data: any) => data.extraFlag1 === 0
|
||||
disabled: (data: any) => data.extraFlag1 === 0 || data.weight === 0
|
||||
}
|
||||
const mounted = ref(false)
|
||||
const rootManage = ref(false)
|
||||
@@ -785,7 +785,10 @@ defineExpose({
|
||||
draggable
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node" :class="{ 'node-disabled-custom': data.extraFlag1 === 0 }">
|
||||
<span
|
||||
class="custom-tree-node"
|
||||
:class="{ 'node-disabled-custom': data.extraFlag1 === 0 || data.weight === 0 }"
|
||||
>
|
||||
<el-icon style="font-size: 18px" v-if="!data.leaf">
|
||||
<Icon name="dv-folder"><dvFolder class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
@@ -815,8 +818,12 @@ defineExpose({
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="t('visualization.publish_tips1')"
|
||||
:disabled="data.extraFlag1"
|
||||
:content="
|
||||
data.weight === 0
|
||||
? t('visualization.no_permission_tips')
|
||||
: t('visualization.publish_tips1')
|
||||
"
|
||||
:disabled="data.extraFlag1 && data.weight > 0"
|
||||
placement="top-start"
|
||||
>
|
||||
{{ node.label }}
|
||||
|
||||
@@ -361,7 +361,7 @@ const tableData = shallowRef([])
|
||||
const total = ref(null)
|
||||
|
||||
const handleNodeClick = (data: BusiTreeNode) => {
|
||||
if (!data.leaf) {
|
||||
if (!data.leaf || data.weight === 0) {
|
||||
datasetListTree.value.setCurrentKey(null)
|
||||
return
|
||||
}
|
||||
@@ -700,7 +700,8 @@ const datasetTypeList = computed(() => {
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
label: 'name',
|
||||
disabled: (data: any) => data.weight === 0
|
||||
}
|
||||
|
||||
const defaultTab = [
|
||||
@@ -947,14 +948,21 @@ const proxyAllowDrop = throttle((arg1, arg2) => {
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span class="custom-tree-node" :class="{ 'node-disabled-custom': data.weight === 0 }">
|
||||
<el-icon v-if="!data.leaf" style="font-size: 18px">
|
||||
<Icon name="dv-folder"><dvFolder class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<el-icon v-if="data.leaf" style="font-size: 18px">
|
||||
<Icon name="icon_dataset"><icon_dataset class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<span :title="node.label" class="label-tooltip ellipsis">{{ node.label }}</span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('visualization.no_permission_tips')"
|
||||
:disabled="data.weight > 0"
|
||||
placement="top-start"
|
||||
>
|
||||
<span :title="node.label" class="label-tooltip ellipsis">{{ node.label }}</span>
|
||||
</el-tooltip>
|
||||
<div class="icon-more" v-if="data.weight >= 7">
|
||||
<handle-more
|
||||
icon-size="24px"
|
||||
@@ -1470,4 +1478,9 @@ const proxyAllowDrop = throttle((arg1, arg2) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.node-disabled-custom {
|
||||
color: rgba(187, 191, 196, 1);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { propTypes } from '@/utils/propTypes'
|
||||
import { onBeforeMount, watch, toRefs, PropType } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import ApiVariable from './ApiVariable.vue'
|
||||
import CodeEdit from './CodeEdit.vue'
|
||||
import CodeEdit from '@/components/CodeEdit/CodeEdit.vue'
|
||||
import Convert from './convert.js'
|
||||
import { KeyValue, BODY_TYPE } from './ApiTestModel.js'
|
||||
export interface ApiBodyItem {
|
||||
|
||||
@@ -583,7 +583,7 @@ const sortTypeTip = computed(() => {
|
||||
const tableData = shallowRef([])
|
||||
const tabData = shallowRef([])
|
||||
const handleNodeClick = data => {
|
||||
if (!data.leaf) {
|
||||
if (!data.leaf || data.weight === 0) {
|
||||
dsListTree.value.setCurrentKey(null)
|
||||
return
|
||||
}
|
||||
@@ -1054,7 +1054,8 @@ const uploadExcel = editType => {
|
||||
const activeName = ref('table')
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
label: 'name',
|
||||
disabled: (data: any) => data.weight === 0
|
||||
}
|
||||
|
||||
const loadInit = () => {
|
||||
@@ -1231,7 +1232,11 @@ const getMenuList = (val: boolean) => {
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node" style="position: relative">
|
||||
<span
|
||||
class="custom-tree-node"
|
||||
style="position: relative"
|
||||
:class="{ 'node-disabled-custom': data.weight === 0 }"
|
||||
>
|
||||
<el-icon :class="data.leaf && 'icon-border'" style="font-size: 18px">
|
||||
<Icon :static-content="getDsIcon(data)"
|
||||
><component class="svg-icon" :is="getDsIconName(data)"></component
|
||||
@@ -1243,18 +1248,30 @@ const getMenuList = (val: boolean) => {
|
||||
>
|
||||
<Icon><icon_warning_colorful_red class="svg-icon" /></Icon>
|
||||
</el-icon>
|
||||
<span
|
||||
:title="node.label"
|
||||
class="label-tooltip ellipsis"
|
||||
:class="data.type === 'Excel' && 'excel'"
|
||||
v-if="data.extraFlag > -1"
|
||||
>{{ node.label }}</span
|
||||
>
|
||||
<el-tooltip
|
||||
v-if="data.extraFlag > -1"
|
||||
effect="dark"
|
||||
:content="t('visualization.no_permission_tips')"
|
||||
:disabled="data.weight > 0"
|
||||
placement="top-start"
|
||||
>
|
||||
<span
|
||||
:title="node.label"
|
||||
class="label-tooltip ellipsis"
|
||||
:class="data.type === 'Excel' && 'excel'"
|
||||
v-if="data.extraFlag > -1"
|
||||
>{{ node.label }}</span
|
||||
>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
v-else
|
||||
:content="`${t('data_set.invalid_data_source')}: ${node.label}`"
|
||||
placement="top"
|
||||
effect="dark"
|
||||
:content="
|
||||
data.weight === 0
|
||||
? t('visualization.no_permission_tips')
|
||||
: `${t('data_set.invalid_data_source')}: ${node.label}`
|
||||
"
|
||||
placement="top-start"
|
||||
>
|
||||
<span
|
||||
:title="node.label"
|
||||
@@ -2428,6 +2445,11 @@ const getMenuList = (val: boolean) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.node-disabled-custom {
|
||||
color: rgba(187, 191, 196, 1);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.record-drawer {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.dataease.api.webhook.request.WebhookSwitchRequest;
|
||||
import io.dataease.api.webhook.vo.WebhookGridVO;
|
||||
import io.dataease.api.webhook.vo.WebhookOption;
|
||||
import io.dataease.api.webhook.vo.WebhookVO;
|
||||
import io.dataease.model.KeywordRequest;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -33,7 +34,7 @@ public interface WebhookApi {
|
||||
|
||||
@Operation(summary = "保存")
|
||||
@PostMapping("/save")
|
||||
void save(@RequestBody WebhookGridVO creator);
|
||||
void save(@RequestBody WebhookVO creator);
|
||||
|
||||
@Operation(summary = "切换SSL")
|
||||
@PostMapping("/switchSsl")
|
||||
@@ -43,6 +44,10 @@ public interface WebhookApi {
|
||||
@PostMapping("/delete")
|
||||
void delete(@RequestBody List<Long> ids);
|
||||
|
||||
@Operation(summary = "查询详情")
|
||||
@GetMapping("/get/{id}")
|
||||
WebhookVO get(@PathVariable("id") Long id);
|
||||
|
||||
@Operation(summary = "查询选项")
|
||||
@GetMapping("/options")
|
||||
List<WebhookOption> options();
|
||||
|
||||
@@ -24,9 +24,4 @@ public class WebhookGridVO implements Serializable {
|
||||
private String contentType;
|
||||
|
||||
private Boolean ssl;
|
||||
|
||||
@JsonSerialize(using= ToStringSerializer.class)
|
||||
private Long oid;
|
||||
|
||||
private Long createTime;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.dataease.api.webhook.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class WebhookVO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
|
||||
private String secret;
|
||||
|
||||
private String contentType;
|
||||
|
||||
private Boolean ssl;
|
||||
|
||||
private String msgTemplate;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long oid;
|
||||
|
||||
private Long createTime;
|
||||
}
|
||||
@@ -74,8 +74,8 @@ public class FileUtils {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public static void validateExist(String path) {
|
||||
File dir = new File(path);
|
||||
public static void validateExist(String path) throws IOException {
|
||||
File dir = new File(path).getCanonicalFile();
|
||||
if (dir.exists()) return;
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
@@ -717,6 +717,39 @@ public class HttpClientUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String postRawBody(String url, String contentType, String body, boolean ssl, HttpClientConfig config) {
|
||||
CloseableHttpClient httpClient = null;
|
||||
try {
|
||||
httpClient = buildHttpClient(ssl);
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
if (ObjectUtils.isEmpty(config)) {
|
||||
config = new HttpClientConfig();
|
||||
}
|
||||
httpPost.setConfig(config.buildRequestConfig());
|
||||
Map<String, String> header = config.getHeader();
|
||||
for (String key : header.keySet()) {
|
||||
httpPost.addHeader(key, header.get(key));
|
||||
}
|
||||
EntityBuilder entityBuilder = EntityBuilder.create();
|
||||
entityBuilder.setText(body);
|
||||
entityBuilder.setContentType(ContentType.create(contentType, java.nio.charset.StandardCharsets.UTF_8));
|
||||
httpPost.setEntity(entityBuilder.build());
|
||||
HttpResponse response = httpClient.execute(httpPost);
|
||||
return getResponseStr(response, config);
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient POST raw body failed", e);
|
||||
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient POST raw body failed: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (httpClient != null) {
|
||||
httpClient.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient关闭连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MultipartResponse postForScreenshot(
|
||||
String url, Map<String,String> body, HttpClientConfig config) throws IOException {
|
||||
CloseableHttpClient httpClient = null;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class WhitelistUtils {
|
||||
public static boolean match(String requestURI) {
|
||||
invalidUrl(requestURI);
|
||||
if (StringUtils.startsWith(requestURI, getContextPath())) {
|
||||
requestURI = requestURI.replaceFirst(getContextPath(), "");
|
||||
requestURI = StringUtils.replaceOnce(requestURI, getContextPath(), "");
|
||||
}
|
||||
if (StringUtils.startsWith(requestURI, AuthConstant.DE_API_PREFIX)) {
|
||||
requestURI = requestURI.replaceFirst(AuthConstant.DE_API_PREFIX, "");
|
||||
|
||||
Reference in New Issue
Block a user