From 4c0449c3ce0542a98ef3957464b7bf3245ef7f85 Mon Sep 17 00:00:00 2001 From: fit2cloud-chenyw Date: Thu, 31 Mar 2022 17:52:22 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20=E8=A7=86=E5=9B=BE=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E8=87=AA=E5=AE=9A=E4=B9=89icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/impl/ShiroServiceImpl.java | 3 ++ .../plugins/server/PluginCommonServer.java | 41 +++++++++++++++++++ frontend/src/utils/validate.js | 2 +- frontend/src/views/chart/group/Group.vue | 34 ++++++++++++--- frontend/src/views/chart/view/ChartEdit.vue | 35 ++++++++++++---- frontend/src/views/panel/ViewSelect/index.vue | 26 ++++++++++-- 6 files changed, 124 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java index d6eb70a6f9..3e4f3a1155 100644 --- a/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java +++ b/backend/src/main/java/io/dataease/auth/service/impl/ShiroServiceImpl.java @@ -68,6 +68,8 @@ public class ShiroServiceImpl implements ShiroService { filterChainDefinitionMap.put("/**/*.js", ANON); filterChainDefinitionMap.put("/**/*.css", ANON); filterChainDefinitionMap.put("/**/*.map", ANON); + filterChainDefinitionMap.put("/**/*.svg", ANON); + filterChainDefinitionMap.put("/api/auth/login", ANON); filterChainDefinitionMap.put("/api/auth/isPluginLoaded", ANON); @@ -77,6 +79,7 @@ public class ShiroServiceImpl implements ShiroService { filterChainDefinitionMap.put("/api/auth/isOpenOidc", ANON); filterChainDefinitionMap.put("/api/auth/getPublicKey", ANON); filterChainDefinitionMap.put("/api/pluginCommon/component/*", ANON); + filterChainDefinitionMap.put("/api/pluginCommon/staticInfo/**", ANON); filterChainDefinitionMap.put("/plugin/oidc/authInfo", ANON); filterChainDefinitionMap.put("/sso/callBack*", ANON); diff --git a/backend/src/main/java/io/dataease/plugins/server/PluginCommonServer.java b/backend/src/main/java/io/dataease/plugins/server/PluginCommonServer.java index b33e60244c..03d1e3bca7 100644 --- a/backend/src/main/java/io/dataease/plugins/server/PluginCommonServer.java +++ b/backend/src/main/java/io/dataease/plugins/server/PluginCommonServer.java @@ -2,9 +2,11 @@ package io.dataease.plugins.server; import io.dataease.commons.utils.ServletUtils; import io.dataease.plugins.common.dto.PluginSysMenu; +import io.dataease.plugins.common.dto.StaticResource; import io.dataease.plugins.common.service.PluginComponentService; import io.dataease.plugins.common.service.PluginMenuService; import io.dataease.plugins.config.SpringContextUtil; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -102,4 +104,43 @@ public class PluginCommonServer { } }); } + + @GetMapping("/staticInfo/{name}/{suffix}") + public void staticInfo(@PathVariable("name") String name, @PathVariable("suffix") String suffix) { + Map beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(PluginComponentService.class); + beansOfType.values().stream().forEach(service -> { + List staticResources = service.staticResources(); + + if (staticResources.stream().anyMatch(resource -> resource.match(name, suffix))) { + HttpServletResponse response = ServletUtils.response(); + BufferedInputStream bis = null; + InputStream inputStream = null; + OutputStream os = null; //输出流 + try{ + inputStream = service.vueResource(name, suffix); + byte[] buffer = new byte[1024]; + os = response.getOutputStream(); + bis = new BufferedInputStream(inputStream); + int i = bis.read(buffer); + while(i != -1){ + os.write(buffer, 0, i); + i = bis.read(buffer); + } + response.setContentType("image/svg+xml"); + os.flush(); + }catch (Exception e) { + e.printStackTrace(); + }finally { + try { + bis.close(); + inputStream.close(); + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return; + } + }); + } } diff --git a/frontend/src/utils/validate.js b/frontend/src/utils/validate.js index de31eda81e..576f91d20c 100644 --- a/frontend/src/utils/validate.js +++ b/frontend/src/utils/validate.js @@ -7,7 +7,7 @@ * @returns {Boolean} */ export function isExternal(path) { - return /^(https?:|mailto:|tel:)/.test(path) + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') } /** diff --git a/frontend/src/views/chart/group/Group.vue b/frontend/src/views/chart/group/Group.vue index 0b1a562bd4..90db051db7 100644 --- a/frontend/src/views/chart/group/Group.vue +++ b/frontend/src/views/chart/group/Group.vue @@ -224,7 +224,7 @@ - + @@ -297,6 +297,7 @@ import TableSelector from '../view/TableSelector' import GroupMoveSelector from '../components/TreeSelector/GroupMoveSelector' import ChartMoveSelector from '../components/TreeSelector/ChartMoveSelector' import ChartType from '@/views/chart/view/ChartType' +import { pluginTypes } from '@/api/chart/chart' import { DEFAULT_COLOR_CASE, DEFAULT_LABEL, @@ -420,7 +421,8 @@ export default { folder: this.$t('commons.folder') }, currentViewNodeData: {}, - currentKey: null + currentKey: null, + pluginRenderOptions: [] } }, computed: { @@ -429,14 +431,14 @@ export default { }, panelInfo() { return this.$store.state.panel.panelInfo - }, - pluginRenderOptions() { + } + /* pluginRenderOptions() { const plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) || [] const pluginOptions = plugins.filter(plugin => !this.renderOptions.some(option => option.value === plugin.render)).map(plugin => { return { name: plugin.render, value: plugin.render } }) return [...this.renderOptions, ...pluginOptions] - } + } */ }, watch: { saveStatus() { @@ -460,6 +462,21 @@ export default { } }, + created() { + const plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) + if (plugins) { + this.loadPluginType() + } else { + pluginTypes().then(res => { + const plugins = res.data + localStorage.setItem('plugin-views', JSON.stringify(plugins)) + this.loadPluginType() + }).catch(e => { + localStorage.setItem('plugin-views', null) + this.loadPluginType() + }) + } + }, mounted() { if (this.mountedInit) { this.treeNode(true) @@ -468,6 +485,13 @@ export default { this.getChartGroupTree() }, methods: { + loadPluginType() { + const plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) || [] + const pluginOptions = plugins.filter(plugin => !this.renderOptions.some(option => option.value === plugin.render)).map(plugin => { + return { name: plugin.render, value: plugin.render } + }) + this.pluginRenderOptions = [...this.renderOptions, ...pluginOptions] + }, clickAdd(param) { this.currGroup = param.data if (param.type === 'group') { diff --git a/frontend/src/views/chart/view/ChartEdit.vue b/frontend/src/views/chart/view/ChartEdit.vue index d340d4b660..6ba6f361a3 100644 --- a/frontend/src/views/chart/view/ChartEdit.vue +++ b/frontend/src/views/chart/view/ChartEdit.vue @@ -154,7 +154,7 @@ {{ $t('chart.chart_type') }} - + !this.renderOptions.some(option => option.value === plugin.render)).map(plugin => { return { name: plugin.render, value: plugin.render } }) return [...this.renderOptions, ...pluginOptions] - } + } */ }, watch: { 'param': function(val) { @@ -1411,8 +1412,19 @@ export default { } }, created() { - // this.get(this.$store.state.chart.viewId); - // this.initAreas() + const plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) + if (plugins) { + this.loadPluginType() + } else { + pluginTypes().then(res => { + const plugins = res.data + localStorage.setItem('plugin-views', JSON.stringify(plugins)) + this.loadPluginType() + }).catch(e => { + localStorage.setItem('plugin-views', null) + this.loadPluginType() + }) + } }, mounted() { this.bindPluginEvent() @@ -1424,6 +1436,13 @@ export default { }, methods: { + loadPluginType() { + const plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) || [] + const pluginOptions = plugins.filter(plugin => !this.renderOptions.some(option => option.value === plugin.render)).map(plugin => { + return { name: plugin.render, value: plugin.render } + }) + this.pluginRenderOptions = [...this.renderOptions, ...pluginOptions] + }, emptyTableData() { this.table = {} this.dimension = [] diff --git a/frontend/src/views/panel/ViewSelect/index.vue b/frontend/src/views/panel/ViewSelect/index.vue index c28f3e2bc3..c6ac23043d 100644 --- a/frontend/src/views/panel/ViewSelect/index.vue +++ b/frontend/src/views/panel/ViewSelect/index.vue @@ -47,6 +47,7 @@ + {{ data.name }} @@ -74,7 +75,7 @@ import eventBus from '@/components/canvas/utils/eventBus' import { mapState } from 'vuex' import { queryPanelViewTree } from '@/api/panel/panel' import { deleteCircle } from '@/api/chart/chart' -import { delUser } from '@/api/system/user' +import { pluginTypes } from '@/api/chart/chart' export default { name: 'ViewSelect', @@ -96,7 +97,8 @@ export default { data: [], showdetail: false, detailItem: null, - loading: false + loading: false, + plugins: null } }, computed: { @@ -110,7 +112,20 @@ export default { } }, created() { - this.loadData() + this.plugins = localStorage.getItem('plugin-views') && JSON.parse(localStorage.getItem('plugin-views')) + if (this.plugins) { + this.loadData() + } else { + pluginTypes().then(res => { + this.plugins = res.data + localStorage.setItem('plugin-views', JSON.stringify(res.data)) + this.loadData() + }).catch(e => { + localStorage.setItem('plugin-views', null) + this.plugins = null + this.loadData() + }) + } }, methods: { filterNode(value, data) { @@ -172,6 +187,11 @@ export default { if (node.modelType === 'panel' || node.nodeType === 'spine') { node.disabled = true } + + if (node.modelType === 'view' && node.modelInnerType && this.plugins && this.plugins.length) { + node.isPlugin = this.plugins.some(plugin => plugin.value === node.modelInnerType) + } + if (node.children && node.children.length > 0) { this.setParentDisable(node.children) } From 730716d2c30606df9bd0132e87c390f450ba0231 Mon Sep 17 00:00:00 2001 From: wangjiahao <1522128093@qq.com> Date: Thu, 31 Mar 2022 21:54:41 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E4=BB=AA=E8=A1=A8=E6=9D=BF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0flv=E6=B5=81=E5=AA=92=E4=BD=93=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 1 + .../components/Editor/StreamMediaLinks.vue | 151 ++++++++++++++++++ .../components/canvas/components/TextAttr.vue | 14 +- .../canvas/custom-component/DeStreamMedia.vue | 120 ++++++++++++++ .../canvas/custom-component/component-list.js | 40 +++++ .../canvas/custom-component/index.js | 2 + frontend/src/lang/en.js | 5 + frontend/src/lang/tw.js | 5 + frontend/src/lang/zh.js | 5 + frontend/src/styles/deicon/demo_index.html | 29 +++- frontend/src/styles/deicon/iconfont.css | 10 +- frontend/src/styles/deicon/iconfont.js | 2 +- frontend/src/styles/deicon/iconfont.json | 7 + frontend/src/styles/deicon/iconfont.ttf | Bin 22220 -> 22436 bytes frontend/src/styles/deicon/iconfont.woff | Bin 13760 -> 13932 bytes frontend/src/styles/deicon/iconfont.woff2 | Bin 11704 -> 11872 bytes frontend/src/views/panel/edit/index.vue | 1 + 17 files changed, 383 insertions(+), 9 deletions(-) create mode 100644 frontend/src/components/canvas/components/Editor/StreamMediaLinks.vue create mode 100644 frontend/src/components/canvas/custom-component/DeStreamMedia.vue diff --git a/frontend/package.json b/frontend/package.json index e0c6fa78f8..a1d0d4da34 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,6 +27,7 @@ "file-save": "^0.2.0", "file-saver": "^2.0.5", "fit2cloud-ui": "1.5.4", + "flv.js": "^1.6.2", "html2canvasde": "^v1.1.4-de", "jquery": "^3.1.1", "js-base64": "^3.7.2", diff --git a/frontend/src/components/canvas/components/Editor/StreamMediaLinks.vue b/frontend/src/components/canvas/components/Editor/StreamMediaLinks.vue new file mode 100644 index 0000000000..d06d19b54c --- /dev/null +++ b/frontend/src/components/canvas/components/Editor/StreamMediaLinks.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/frontend/src/components/canvas/components/TextAttr.vue b/frontend/src/components/canvas/components/TextAttr.vue index 3f89341cc8..ec18af4c13 100644 --- a/frontend/src/components/canvas/components/TextAttr.vue +++ b/frontend/src/components/canvas/components/TextAttr.vue @@ -134,6 +134,12 @@ +
+ + + +
+
@@ -159,12 +165,13 @@ import { mapState } from 'vuex' import Hyperlinks from '@/components/canvas/components/Editor/Hyperlinks' import VideoLinks from '@/components/canvas/components/Editor/VideoLinks' +import StreamMediaLinks from '@/components/canvas/components/Editor/StreamMediaLinks' import DateFormat from '@/components/canvas/components/Editor/DateFormat' import { COLOR_PANEL } from '@/views/chart/chart/chart' import FrameLinks from '@/components/canvas/components/Editor/FrameLinks' export default { - components: { FrameLinks, Hyperlinks, DateFormat, VideoLinks }, + components: { FrameLinks, Hyperlinks, DateFormat, VideoLinks, StreamMediaLinks }, props: { scrollLeft: { type: Number, @@ -294,11 +301,14 @@ export default { 'backgroundColor', 'hyperlinks' ], - // 文本组件显示的属性 'de-video': [ 'opacity', 'videoLinks' ], + 'de-stream-media': [ + 'opacity', + 'streamMediaLinks' + ], 'de-frame': [ 'opacity', 'frameLinks' diff --git a/frontend/src/components/canvas/custom-component/DeStreamMedia.vue b/frontend/src/components/canvas/custom-component/DeStreamMedia.vue new file mode 100644 index 0000000000..c1920f07db --- /dev/null +++ b/frontend/src/components/canvas/custom-component/DeStreamMedia.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/frontend/src/components/canvas/custom-component/component-list.js b/frontend/src/components/canvas/custom-component/component-list.js index 78dfb8cd48..397f696cbf 100644 --- a/frontend/src/components/canvas/custom-component/component-list.js +++ b/frontend/src/components/canvas/custom-component/component-list.js @@ -96,7 +96,18 @@ export const VIDEOLINKS = { } } } +} +// 流媒体视频信息配置 +export const STREAMMEDIALINKS = { + videoType: 'flv', + flv: { + type: 'flv', + isLive: false, + cors: true, // 允许跨域 + loop: true + // url: null // 网络动画视频 + } } // 嵌套页面信息 @@ -148,6 +159,14 @@ export const pictureList = [ label: '视频', icon: 'iconfont icon-video', defaultClass: 'text-filter' + }, + { + id: '20003', + component: 'stream-media', + type: 'stream-media', + label: '流媒体', + icon: 'iconfont icon-a-liumeitimeitiliebiao', + defaultClass: 'text-filter' } ] @@ -441,6 +460,27 @@ const list = [ sizey: 5, miniSizex: 1, miniSizey: 1 + }, + { + id: '20003', + component: 'de-stream-media', + type: 'de-stream-media', + label: '', + icon: 'iconfont icon-picture', + defaultClass: 'text-filter', + mobileStyle: BASE_MOBILE_STYLE, + style: { + width: 400, + height: 200, + borderRadius: '' + }, + streamMediaLinks: STREAMMEDIALINKS, + x: 1, + y: 1, + sizex: 10, + sizey: 5, + miniSizex: 1, + miniSizey: 1 } ] diff --git a/frontend/src/components/canvas/custom-component/index.js b/frontend/src/components/canvas/custom-component/index.js index 6316c79087..6d9145a740 100644 --- a/frontend/src/components/canvas/custom-component/index.js +++ b/frontend/src/components/canvas/custom-component/index.js @@ -8,7 +8,9 @@ import RectShape from '@/components/canvas/custom-component/RectShape' import UserView from '@/components/canvas/custom-component/UserView' import DeVideo from '@/components/canvas/custom-component/DeVideo' import DeFrame from '@/components/canvas/custom-component/DeFrame' +import DeStreamMedia from '@/components/canvas/custom-component/DeStreamMedia' +Vue.component('DeStreamMedia', DeStreamMedia) Vue.component('Picture', Picture) Vue.component('VText', VText) Vue.component('VButton', VButton) diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 82636e1774..9758b4c53b 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -1392,6 +1392,11 @@ export default { sure_bt: 'Confirm' }, panel: { + is_live: 'Is Live', + yes: 'Yes', + no: 'No', + live_tips: 'User Https First', + stream_media_add_tips: 'Please Add Stream Media Info...', json_params_error: 'Third Party Parameters Parsing Failed. Please Check Whether The Parameters Format Is Correct', inner_padding: 'Inner Padding', board_radio: 'Board Radio', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index 45d46c9c11..8a8bfa1c4b 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -1393,6 +1393,11 @@ export default { sure_bt: '確定' }, panel: { + is_live: '是否直播', + yes: '是', + no: '否', + live_tips: '优先HTTPS链接', + stream_media_add_tips: '请点击添加配置流媒体信息...', json_params_error: '第三方参数解析失败,请检查参数格式是否正确', inner_padding: '内边距', board_radio: '边框半径', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index e20dc71f45..f29106533f 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -1401,6 +1401,11 @@ export default { sure_bt: '确定' }, panel: { + is_live: '是否直播', + yes: '是', + no: '否', + live_tips: '优先HTTPS链接', + stream_media_add_tips: '请点击添加配置流媒体信息...', json_params_error: '第三方参数解析失败,请检查参数格式是否正确', inner_padding: '内边距', board_radio: '边框半径', diff --git a/frontend/src/styles/deicon/demo_index.html b/frontend/src/styles/deicon/demo_index.html index e4e8b61d73..5b5669ac90 100644 --- a/frontend/src/styles/deicon/demo_index.html +++ b/frontend/src/styles/deicon/demo_index.html @@ -54,6 +54,12 @@