Merge pull request #2650 from dataease/pr@dev@feat_panel-market

feat: 模板市场增加预览功能并可以直接应用模板
This commit is contained in:
王嘉豪 2022-07-21 11:17:25 +08:00 committed by GitHub
commit daebb5c79f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 939 additions and 128 deletions

View File

@ -4,6 +4,8 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.auth.annotation.DePermission;
import io.dataease.auth.annotation.DePermissionProxy;
import io.dataease.auth.annotation.DePermissions;
import io.dataease.auth.service.impl.ExtAuthServiceImpl;
import io.dataease.commons.constants.PanelConstants;
import io.dataease.controller.request.panel.PanelGroupBaseInfoRequest;
import io.dataease.plugins.common.base.domain.PanelGroup;
import io.dataease.commons.constants.DePermissionType;
@ -17,6 +19,7 @@ import io.dataease.dto.panel.PanelGroupDTO;
import io.dataease.service.panel.PanelGroupService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.pentaho.di.core.util.UUIDUtil;
import springfox.documentation.annotations.ApiIgnore;
import org.apache.shiro.authz.annotation.Logical;
import org.springframework.web.bind.annotation.*;
@ -39,6 +42,8 @@ public class PanelGroupController {
@Resource
private PanelGroupService panelGroupService;
@Resource
private ExtAuthServiceImpl authService;
@ApiOperation("查询树")
@PostMapping("/tree")
@ -59,9 +64,15 @@ public class PanelGroupController {
@DePermission(type = DePermissionType.PANEL, value = "pid", level = ResourceAuthLevel.PANNEL_LEVEL_MANAGE)
}, logical = Logical.AND)
@I18n
public PanelGroup save(@RequestBody PanelGroupRequest request) throws Exception{
public PanelGroupDTO save(@RequestBody PanelGroupRequest request) throws Exception{
String panelId = panelGroupService.save(request);
return findOne(panelId);
PanelGroupDTO result = findOne(panelId);
// 如果新建来源来自模板市场在返回数据中加入父级ID便于跳转展开仪表板树
if(PanelConstants.NEW_PANEL_FROM.NEW_MARKET_TEMPLATE.equals(request.getNewFrom())){
result.setParents(authService.parentResource(panelId,"panel"));
result.setRequestId(UUIDUtil.getUUIDAsString());
}
return result;
}
@ApiOperation("更新")

View File

@ -33,5 +33,8 @@ public class PanelGroupDTO extends PanelGroupWithBLOBs implements ITreeBase<Pane
private List<PanelGroupDTO> children;
@ApiModelProperty("视图信息")
private List<Map<String, ChartViewDTO>> viewsInfo;
@ApiModelProperty("父级ID")
private List<String> parents;
@ApiModelProperty("请求ID")
private String requestId;
}

View File

@ -39,5 +39,47 @@ VALUES ('Mongo 数据源插件', 'default', '0', '0', 'datasource', 'Mongo 数
'1650765903630', 'mongo-backend', 'mongobi');
INSERT INTO `sys_menu` (
`menu_id`,
`pid`,
`sub_count`,
`type`,
`title`,
`name`,
`component`,
`menu_sort`,
`icon`,
`path`,
`i_frame`,
`cache`,
`hidden`,
`permission`,
`create_by`,
`update_by`,
`create_time`,
`update_time`
)
VALUES
(
41,
1,
1,
1,
'模板市场',
'template-market',
'panel/templateMarket/index',
13,
'dashboard',
'panel/templateMarket/index',
0,
0,
0,
'',
NULL,
NULL,
NULL,
1620444227389
);
INSERT INTO `system_parameter` (`param_key`, `param_value`, `type`, `sort`) VALUES ('basic.templateAccessKey', 'dataease', 'text', NULL);
INSERT INTO `system_parameter` (`param_key`, `param_value`, `type`, `sort`) VALUES ('basic.templateMarketUlr', 'https://dataease.io/templates', 'text', 4);

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1658301542532" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="957" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M-15.88624103 59.48859927h301.64928058c249.87872281 0 452.47392089 202.59519808 452.4739209 452.4739209S535.64176236 964.43644107 285.76303955 964.43644107H-15.88624103V59.48859927z" fill="#e6e6e6" p-id="958"></path><path d="M285.76303955 97.19475935H-15.88624103v829.53552164h301.64928058c229.06492245 0 414.76776082-185.70283837 414.76776081-414.76776082S514.82796201 97.19475935 285.76303955 97.19475935zM-15.88624103 59.48859927v904.9478418h301.64928058c249.87872281 0 452.47392089-202.59519808 452.4739209-452.4739209S535.64176236 59.48859927 285.76303955 59.48859927H-15.88624103z" fill="#e7e3e3" p-id="959"></path><path d="M113.40818187 377.31382255l134.46016681 134.46016682-134.46016681 134.42246066a9.42654002 9.42654002 0 0 0 0 13.34798068l13.31027449 13.3102745a9.42654002 9.42654002 0 0 0 13.34798065 0l147.8081475-147.77044133a18.85308003 18.85308003 0 0 0 0-26.65825517l-147.8081475-147.8081475a9.42654002 9.42654002 0 0 0-13.34798065 0l-13.31027449 13.34798069a9.42654002 9.42654002 0 0 0 0 13.34798065z" fill="#646A73" p-id="960"></path><path d="M280.93665106 377.31382255l134.42246066 134.46016682-134.42246066 134.42246066a9.42654002 9.42654002 0 0 0 0 13.34798068l13.3102745 13.3102745a9.42654002 9.42654002 0 0 0 13.34798068 0l147.80814749-147.77044133a18.85308003 18.85308003 0 0 0 0-26.65825517l-147.80814749-147.8081475a9.42654002 9.42654002 0 0 0-13.34798068 0l-13.3102745 13.34798069a9.42654002 9.42654002 0 0 0 0 13.34798065z" fill="#646A73" p-id="961"></path></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1499,6 +1499,10 @@ export default {
sure_bt: 'Confirm'
},
panel: {
template_market: 'Template Market',
template_preview: 'Template Preview',
apply: 'Apply',
apply_this_template: 'Apply This Template',
market_network_tips: 'View template Market template requires server and template Market https://dataease.io/templates , please check the network... ',
enter_name_tips: 'Please enter the name of the panel',
name: 'Name',

View File

@ -1500,11 +1500,15 @@ export default {
sure_bt: '確定'
},
panel: {
template_market: '模板市场',
template_preview: '预览模板',
apply: '应用',
apply_this_template: '应用此模板',
market_network_tips: '查看模板市场模板需要服务器与模板市场(https://dataease.io/templates)联通,请检查网络...',
enter_name_tips: '请输入仪表板名称',
name: '名称',
apply_template: '应用模板',
enter_template_name_tips: '请输入模板名称...',
enter_template_name_tips: '搜索模板名称',
pic_adaptation: '适应组件',
pic_equiratio: '等比适应',
pic_original: '原始尺寸',

View File

@ -1508,11 +1508,15 @@ export default {
sure_bt: '确定'
},
panel: {
template_market: '模板市场',
template_preview: '预览模板',
apply: '应用',
apply_this_template: '应用此模板',
market_network_tips: '查看模板市场模板需要服务器与模板市场(https://dataease.io/templates)联通,请检查网络...',
enter_name_tips: '请输入仪表板名称',
name: '名称',
apply_template: '应用模板',
enter_template_name_tips: '请输入模板名称...',
enter_template_name_tips: '搜索模板名称',
pic_adaptation: '适应组件',
pic_equiratio: '等比适应',
pic_original: '原始尺寸',

View File

@ -22,7 +22,6 @@
<div class="right-menu" style="color: var(--TopTextColor)">
<template>
<span class="hover-effect right-menu-item template-market-item" @click="changeTemplateMarketShow(true)">模板市场</span>
<notification class="right-menu-item hover-effect" />
<lang-select class="right-menu-item hover-effect" />
<div style="height: 100%;padding: 0 8px;" class="right-menu-item hover-effect">

View File

@ -54,6 +54,42 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe635;</span>
<div class="name">button_right</div>
<div class="code-name">&amp;#xe635;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe631;</span>
<div class="name">icon-maybe</div>
<div class="code-name">&amp;#xe631;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe632;</span>
<div class="name">icon_up-left_outlined</div>
<div class="code-name">&amp;#xe632;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe633;</span>
<div class="name">close</div>
<div class="code-name">&amp;#xe633;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe634;</span>
<div class="name">Frame 3425</div>
<div class="code-name">&amp;#xe634;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe636;</span>
<div class="name">icon-filter</div>
<div class="code-name">&amp;#xe636;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe630;</span>
<div class="name">icon_Batch_outlined</div>
@ -726,9 +762,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1657078050131') format('woff2'),
url('iconfont.woff?t=1657078050131') format('woff'),
url('iconfont.ttf?t=1657078050131') format('truetype');
src: url('iconfont.woff2?t=1658301977080') format('woff2'),
url('iconfont.woff?t=1658301977080') format('woff'),
url('iconfont.ttf?t=1658301977080') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -754,6 +790,60 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-button_right"></span>
<div class="name">
button_right
</div>
<div class="code-name">.icon-button_right
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon-maybe"></span>
<div class="name">
icon-maybe
</div>
<div class="code-name">.icon-icon-maybe
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon_up-left_outlined"></span>
<div class="name">
icon_up-left_outlined
</div>
<div class="code-name">.icon-icon_up-left_outlined
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-close"></span>
<div class="name">
close
</div>
<div class="code-name">.icon-close
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-a-Frame3425"></span>
<div class="name">
Frame 3425
</div>
<div class="code-name">.icon-a-Frame3425
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon-filter"></span>
<div class="name">
icon-filter
</div>
<div class="code-name">.icon-icon-filter
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon_Batch_outlined"></span>
<div class="name">
@ -1762,6 +1852,54 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-button_right"></use>
</svg>
<div class="name">button_right</div>
<div class="code-name">#icon-button_right</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon-maybe"></use>
</svg>
<div class="name">icon-maybe</div>
<div class="code-name">#icon-icon-maybe</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_up-left_outlined"></use>
</svg>
<div class="name">icon_up-left_outlined</div>
<div class="code-name">#icon-icon_up-left_outlined</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-close"></use>
</svg>
<div class="name">close</div>
<div class="code-name">#icon-close</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-a-Frame3425"></use>
</svg>
<div class="name">Frame 3425</div>
<div class="code-name">#icon-a-Frame3425</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon-filter"></use>
</svg>
<div class="name">icon-filter</div>
<div class="code-name">#icon-icon-filter</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_Batch_outlined"></use>

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2459092 */
src: url('iconfont.woff2?t=1657078050131') format('woff2'),
url('iconfont.woff?t=1657078050131') format('woff'),
url('iconfont.ttf?t=1657078050131') format('truetype');
src: url('iconfont.woff2?t=1658301977080') format('woff2'),
url('iconfont.woff?t=1658301977080') format('woff'),
url('iconfont.ttf?t=1658301977080') format('truetype');
}
.iconfont {
@ -13,6 +13,30 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-button_right:before {
content: "\e635";
}
.icon-icon-maybe:before {
content: "\e631";
}
.icon-icon_up-left_outlined:before {
content: "\e632";
}
.icon-close:before {
content: "\e633";
}
.icon-a-Frame3425:before {
content: "\e634";
}
.icon-icon-filter:before {
content: "\e636";
}
.icon-icon_Batch_outlined:before {
content: "\e630";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,48 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "30729285",
"name": "button_right",
"font_class": "button_right",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "30705847",
"name": "icon-maybe",
"font_class": "icon-maybe",
"unicode": "e631",
"unicode_decimal": 58929
},
{
"icon_id": "30725622",
"name": "icon_up-left_outlined",
"font_class": "icon_up-left_outlined",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "30725623",
"name": "close",
"font_class": "close",
"unicode": "e633",
"unicode_decimal": 58931
},
{
"icon_id": "30725624",
"name": "Frame 3425",
"font_class": "a-Frame3425",
"unicode": "e634",
"unicode_decimal": 58932
},
{
"icon_id": "30725626",
"name": "icon-filter",
"font_class": "icon-filter",
"unicode": "e636",
"unicode_decimal": 58934
},
{
"icon_id": "30342179",
"name": "icon_Batch_outlined",

View File

@ -609,7 +609,6 @@ export default {
created() {
// Global listening for key events
listenGlobalKeyDown()
this.init(this.$store.state.panel.panelInfo.id)
},
mounted() {
this.initEvents()
@ -622,6 +621,7 @@ export default {
})
})
this.loadMultiplexingViewTree()
this.init(this.$store.state.panel.panelInfo.id)
},
beforeDestroy() {
bus.$off('component-on-drag', this.componentOnDrag)

View File

@ -41,19 +41,16 @@ export default {
}
},
watch: {
// $route(to, from) {
// }
},
mounted() {
bus.$on('to-msg-share', this.toMsgShare)
bus.$on('PanelSwitchComponent', this.panelSwitchComponent)
},
beforeDestroy() {
bus.$off('to-msg-share', this.toMsgShare)
bus.$off('PanelSwitchComponent', this.panelSwitchComponent)
},
created() {
bus.$emit('PanelSwitchComponent', { name: 'PanelMain' })
bus.$on('PanelSwitchComponent', this.panelSwitchComponent)
this.$store.dispatch('app/toggleSideBarHide', true)
const routerParam = this.$router.currentRoute.params
this.toMsgShare(routerParam)

View File

@ -241,6 +241,7 @@ export default {
components: { GrantAuth, LinkGenerate, EditPanel, TreeSelector },
data() {
return {
historyRequestId: null,
lastActiveNode: null, //
lastActiveNodeData: null,
activeTree: 'self', // self system
@ -354,7 +355,7 @@ export default {
watch: {
//
'$store.state.panel.mainActiveName': function(newVal, oldVal) {
if (newVal === 'PanelMain' && this.lastActiveNodeData) {
if (newVal === 'PanelMain' && this.lastActiveNodeData) {
this.activeNodeAndClickOnly(this.lastActiveNodeData)
}
},
@ -376,14 +377,19 @@ export default {
this.$store.commit('setComponentData', [])
this.$store.commit('setCanvasStyle', DEFAULT_COMMON_CANVAS_STYLE_STRING)
this.defaultTree(true)
this.tree(true)
this.initCache()
bus.$on('newPanelFromMarket', this.newPanelFromMarket)
const routerParam = this.$router.currentRoute.params
if (routerParam && routerParam.nodeType === 'panel' && this.historyRequestId !== routerParam.requestId) {
this.historyRequestId = routerParam.requestId
this.tree()
this.edit(routerParam, null)
} else {
this.tree(true)
}
},
methods: {
newPanelFromMarket(panelInfo) {
if (panelInfo) {
this.defaultTree()
this.tree()
this.edit(panelInfo, null)
}
@ -741,16 +747,17 @@ export default {
//
activeNodeAndClick(panelInfo) {
if (panelInfo) {
this.$nextTick(() => {
const _this = this
_this.$nextTick(() => {
// CurrentKey
this.$refs.panel_list_tree.setCurrentKey(panelInfo.id)
_this.$refs.panel_list_tree.setCurrentKey(panelInfo.id)
// default_tree
this.$refs.default_panel_tree.setCurrentKey(null)
this.$nextTick(() => {
_this.$refs.default_panel_tree.setCurrentKey(null)
_this.$nextTick(() => {
document.querySelector('.is-current').firstChild.click()
//
if (panelInfo.nodeType === 'panel') {
this.edit(this.lastActiveNodeData, this.lastActiveNode)
_this.edit(this.lastActiveNodeData, this.lastActiveNode)
}
})
})
@ -759,12 +766,16 @@ export default {
//
activeNodeAndClickOnly(panelInfo) {
if (panelInfo) {
this.$nextTick(() => {
const _this = this
_this.$nextTick(() => {
// CurrentKey
this.$refs.panel_list_tree.setCurrentKey(panelInfo.id)
_this.$refs.panel_list_tree.setCurrentKey(panelInfo.id)
// default_tree
this.$refs.default_panel_tree.setCurrentKey(null)
this.$nextTick(() => {
_this.$refs.default_panel_tree.setCurrentKey(null)
if (panelInfo.parents) {
_this.expandedArray = panelInfo.parents
}
_this.$nextTick(() => {
document.querySelector('.is-current').firstChild.click()
})
})

View File

@ -4,7 +4,7 @@
<el-tabs v-model="activeName" class="tab-panel" :stretch="true" @tab-click="handleClick">
<el-tab-pane name="PanelList">
<span slot="label"><i class="el-icon-document tablepanel-i" />{{ $t('panel.panel_list') }}</span>
<panel-list v-if="activeName==='PanelList'" ref="panelList" />
<panel-list v-show="activeName==='PanelList'" ref="panelList" />
</el-tab-pane>
<el-tab-pane name="panels_star" :lazy="true">
<span slot="label"><i class="el-icon-star-off tablepanel-i" />{{ $t('panel.store') }}</span>
@ -51,7 +51,7 @@ export default {
watch: {
//
'$store.state.panel.mainActiveName': function(newVal, oldVal) {
if (newVal === 'PanelMain' && this.lastActiveNode && this.lastActiveNodeData) {
if (newVal === 'PanelMain' && this.lastActiveNodeData) {
this.activeNodeAndClickOnly(this.lastActiveNodeData)
}
},
@ -70,7 +70,7 @@ export default {
localStorage.setItem('plugin-views', null)
this.$store.commit('initViewRender', [])
})
this.clear()
// this.clear()
},
methods: {
handleClick(tab, event) {

View File

@ -0,0 +1,403 @@
<template>
<el-row>
<el-col :class="asideActive?'aside-active':'aside-inActive'">
<svg-icon v-show="!asideActive" icon-class="button_right" class="open-button" @click="asideActiveChange(true)" />
<el-row v-show="asideActive" style="padding: 12px 12px 0 12px ">
<el-row>
<span class="icon iconfont icon-close icon20 insert" @click="closePreview()" />
<span class="main-title">{{$t('panel.template_preview')}}</span>
<span style="float: right" class="icon iconfont icon-icon_up-left_outlined insert icon20" @click="asideActiveChange(false)" />
</el-row>
<el-row class="margin-top16 search-area">
<el-input
v-model="searchText"
size="small"
class="title-name-search"
:placeholder="$t('panel.enter_template_name_tips')"
clearable="true"
/>
<span class="icon iconfont icon-icon-filter insert icon20 filter-icon-span" :class="extFilterActive?'filter-icon-active':''" @click="extFilterActiveChange()" />
</el-row>
<el-row v-show="extFilterActive">
<el-select v-model="marketActiveTab" class="margin-top16" size="small" placeholder="请选择">
<el-option
v-for="item in marketTabs"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-row>
<el-divider />
</el-row>
<el-row v-show="asideActive" class="aside-list" :class="extFilterActive?'aside-list-filter-active':''">
<template-market-preview-item
v-for="(templateItem) in currentMarketTemplateShowList"
v-show="templateItem.showFlag"
:key="templateItem.id"
:template="templateItem"
:base-url="baseUrl"
:active="active(templateItem)"
@previewTemplate="previewTemplate"
/>
</el-row>
</el-col>
<el-col class="main-area" :class="asideActive ? 'main-area-active': ''">
<el-row>
<span v-if="curTemplate" class="template-title">{{ curTemplate.title }}</span>
<el-button style="float: right" type="primary" size="small" @click="templateApply(curTemplate)">{{$t('panel.apply_this_template')}}</el-button>
</el-row>
<el-row class="img-main">
<img height="100%" :src="templatePreviewUrl" alt="">
</el-row>
</el-col>
</el-row>
</template>
<script>
import { searchMarket, getCategories } from '@/api/templateMarket'
import { groupTree, panelSave } from '@/api/panel/panel'
import bus from '@/utils/bus'
import { DEFAULT_COMMON_CANVAS_STYLE_STRING } from '@/views/panel/panel'
import TemplateMarketPreviewItem from '@/views/panel/templateMarket/component/TemplateMarketPreviewItem'
export default {
name: 'MarketPreview',
components: { TemplateMarketPreviewItem },
props: {
previewId: {
type: String,
default: null
}
},
data() {
return {
extFilterActive: false,
asideActive: true,
previewVisible: false,
templatePreviewUrl: null,
marketTabs: null,
marketActiveTab: null,
searchText: null,
panelForm: {
name: null,
pid: null,
nodeType: 'panel',
templateUrl: null,
newFrom: 'new_market_template',
panelType: 'self',
panelStyle: JSON.stringify(DEFAULT_COMMON_CANVAS_STYLE_STRING),
panelData: '[]'
},
panelGroupList: [],
curApplyTemplate: null,
folderSelectShow: false,
baseUrl: 'https://dataease.io/templates',
currentMarketTemplateShowList: [],
networkStatus: true,
curTemplate: null
}
},
computed: {
},
watch: {
marketActiveTab() {
this.initTemplateShow()
},
searchText() {
this.initTemplateShow()
},
previewId(val) {
const _this = this
_this.currentMarketTemplateShowList.forEach(template => {
if (val === template.id) {
_this.previewTemplate(template)
}
})
}
},
mounted() {
this.initMarketTemplate()
this.getGroupTree()
},
methods: {
initMarketTemplate() {
searchMarket({}).then(rsp => {
this.baseUrl = rsp.data.baseUrl
this.currentMarketTemplateShowList = rsp.data.contents
}).catch(() => {
this.networkStatus = false
})
getCategories().then(rsp => {
this.marketTabs = rsp.data
this.marketActiveTab = this.marketTabs[0]
}).catch(() => {
this.networkStatus = false
})
if (this.previewId) {
const _this = this
_this.currentMarketTemplateShowList.forEach(template => {
if (_this.previewId === template.id) {
_this.previewTemplate(template)
}
})
}
},
getGroupTree() {
groupTree({ nodeType: 'folder' }).then(res => {
this.panelGroupList = res.data
})
},
normalizer(node) {
// children=null
if (node.children === null || node.children === 'null') {
delete node.children
}
},
templateApply(template) {
this.$emit('templateApply', template)
},
closeDialog() {
this.$emit('closeDialog')
},
handleClick(item) {
},
initTemplateShow() {
this.currentMarketTemplateShowList.forEach(template => {
template.showFlag = this.templateShow(template)
})
},
templateShow(templateItem) {
let categoryMarch = false
let searchMarch = false
templateItem.categories.forEach(category => {
if (category.name === this.marketActiveTab) {
categoryMarch = true
}
})
if (!this.searchText || templateItem.title.indexOf(this.searchText) > -1) {
searchMarch = true
}
return categoryMarch && searchMarch
},
previewTemplate(template) {
this.curTemplate = template
if (template.thumbnail.indexOf('http') > -1) {
this.templatePreviewUrl = template.thumbnail
} else {
this.templatePreviewUrl = this.baseUrl + template.thumbnail
}
},
asideActiveChange(prop) {
this.asideActive = prop
},
extFilterActiveChange() {
this.extFilterActive = !this.extFilterActive
},
closePreview() {
this.$emit('closePreview')
},
active(template) {
return this.curTemplate && this.curTemplate.id === template.id
}
}
}
</script>
<style lang="scss" scoped>
.aside-list {
padding: 0px 12px 12px 12px;
width: 100%;
height: calc(100vh - 200px);
overflow-x: hidden;
overflow-y: auto;
}
.aside-list-filter-active {
height: calc(100vh - 250px);
}
.template-main {
border-radius: 4px;
box-shadow: 0 0 2px 0 rgba(31, 31, 31, 0.15), 0 1px 2px 0 rgba(31, 31, 31, 0.15);
border: solid 2px #fff;
padding-bottom: 24px;
min-height: calc(100vh - 190px);
}
.market-main {
padding: 24px
}
.title-left {
float: left;
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.title-right {
float: right;
width: 320px;
}
.dialog-footer-self {
text-align: center;
}
.search-button-self {
text-align: left;
padding-left: 10px;
}
.topbar-icon-active {
cursor: pointer;
transition: .1s;
border-radius: 3px;
font-size: 22px;
background-color: rgb(245, 245, 245);
&:active {
color: #000;
border-color: #3a8ee6;
background-color: red;
outline: 0;
}
&:hover {
background-color: rgba(31, 35, 41, 0.1);
color: #3a8ee6;
}
}
.custom-position {
height: 80vh;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
flex-flow: row nowrap;
color: #9ea6b2;
}
.aside-active {
width: 206px;
height: calc(100vh - 56px);
overflow-x: hidden;
overflow-y: auto;
border-right: 1px solid rgba(31,31,31,0.15);
}
.aside-inActive{
position: relative;
width: 0px;
}
.main-area-active {
width: calc(100% - 206px)!important;
}
.main-area {
width: 100%;
padding:24px;
text-align: center;
height: calc(100vh - 56px);
transition: 0.5s;
}
.title-name-search {
width: 140px;
float: left;
}
.icon20 {
font-size: 20px !important;
}
.main-title {
margin-left: 8px;
font-weight: 500;
font-size: 16px;
line-height: 24px;
}
.insert {
display: inline-block;
font-weight: 400 !important;
font-family: PingFang SC;
line-height: 1;
white-space: nowrap;
cursor: pointer;
color: var(--TextPrimary, #1F2329);
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: 0;
margin: 0;
transition: .1s;
border-radius: 3px;
&:active {
color: #000;
border-color: #3a8ee6;
background-color: red;
outline: 0;
}
&:hover {
background-color: rgba(31, 35, 41, 0.1);
color: #3a8ee6;
}
}
.template-title{
float: left;
font-weight: 500;
font-size: 20px;
line-height: 28px;
margin-bottom: 24px;
}
.margin-top16 {
margin-top: 16px;
}
.img-main{
border-radius: 4px;
box-shadow: 0 0 2px 0 rgba(31,31,31,0.15), 0 1px 2px 0 rgba(31,31,31,0.15);
border: solid 2px #fff;
height: calc(100% - 50px)!important;
}
.open-button{
cursor: pointer;
font-size: 30px;
position: absolute;
left: 0;
top: 16px;
z-index: 2;
}
.open-button:hover{
color: #3a8ee6;
}
.filter-icon-span{
float: left;
border: 1px solid #DCDFE6;
width: 32px;
height: 32px;
border-radius: 4px;
padding: 5px;
margin-left: 8px;
}
.filter-icon-active{
border: 1px solid #3370FF;
color: #3370FF;
}
.search-area{
width: 100%;
position: relative;
}
</style>

View File

@ -7,8 +7,8 @@
</el-row>
</el-row>
<el-row class="template-button">
<el-button size="mini" type="primary" icon="el-icon-view" @click="templatePreview">预览</el-button>
<el-button size="mini" type="success" icon="el-icon-files" @click="apply">应用</el-button>
<el-button size="mini" style="width: 141px" @click="templatePreview">{{ $t('panel.preview') }}</el-button>
<el-button size="mini" style="width: 141px" type="primary" @click="apply">{{ $t('panel.apply') }}</el-button>
</el-row>
</div>
</template>
@ -55,7 +55,7 @@ export default {
this.$emit('templateApply', this.template)
},
templatePreview() {
this.$emit('templatePreview', this.thumbnailUrl)
this.$emit('templatePreview', this.template.id)
}
}
}
@ -66,20 +66,20 @@ export default {
.testcase-template {
position: relative;
display: inline-block;
margin: 10px 30px;
margin: 24px 0 0 24px;
box-shadow: 0 0 2px 0 rgba(31,31,31,0.15), 0 1px 2px 0 rgba(31,31,31,0.15);
border: solid 2px #fff;
box-sizing: border-box;
border-radius: 3px;
height: 250px;
border-radius: 4px;
height: 256px;
}
.demonstration {
display: block;
font-size: 14px;
color: gray;
text-align: center;
margin: 10px auto;
font-size: 16px;
text-align: left;
margin-left: 12px;
margin-top: 12px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
@ -87,8 +87,8 @@ export default {
.template-img {
background-size: 100% 100%;
height: 170px;
width: 300px;
height: 180px;
width: 318px;
margin: 0 auto;
border: solid 2px #fff;
box-sizing: border-box;
@ -96,7 +96,7 @@ export default {
.template-img:hover {
border: solid 1px #4b8fdf;
border-radius: 3px;
border-radius: 4px;
color: deepskyblue;
cursor: pointer;
}
@ -110,7 +110,7 @@ export default {
position:absolute;
bottom: 5px;
left: 0px;
width: 300px;
width: 318px;
}
</style>

View File

@ -0,0 +1,112 @@
<template>
<div
:class="[
{
['template-item-main-active']: active
},
'template-item-main'
]"
@click.stop="previewTemplate"
>
<div class="template-item-img" :style="classBackground" />
<span class="demonstration">{{ template.title }}</span>
</div>
</template>
<script>
export default {
name: 'TemplateMarketPreviewItem',
props: {
template: {
type: Object,
default() {
return {}
}
},
baseUrl: {
type: String
},
active: {
type: Boolean,
required: false,
default: false
}
},
data() {
return {
}
},
computed: {
classBackground() {
return {
background: `url(${this.thumbnailUrl}) no-repeat`,
'background-size': `100% 100%`
}
},
thumbnailUrl() {
if (this.template.thumbnail.indexOf('http') > -1) {
return this.template.thumbnail
} else {
return this.baseUrl + this.template.thumbnail
}
}
},
methods: {
previewTemplate() {
this.$emit('previewTemplate', this.template)
}
}
}
</script>
<style scoped>
.template-item-main {
margin: 0 0 12px 0;
position: relative;
box-sizing: border-box;
width: 182px;
height: 116px;
background: #ffffff;
border: 1px solid #DEE0E3 ;
border-radius: 4px;
flex: none;
order: 0;
flex-grow: 0;
cursor: pointer;
overflow: hidden;
}
.template-item-main-active{
border: 2px solid #3370FF ;
}
.template-item-img{
position: absolute;
width: 182px;
height: 86px;
left: 0px;
top: 0px;
}
.demonstration {
position: absolute;
width: 166px;
height: 20px;
left: 8px;
top: 91px;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 20px;
display: block;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.template-item-main:hover {
border: solid 2px gray;
}
</style>

View File

@ -1,82 +1,71 @@
<template>
<el-row>
<el-row>
<el-col span="4" style="text-align: left">
<svg-icon
icon-class="icon_left_outlined"
class="topbar-icon-active"
@click="closeDialog"
/>
</el-col>
<el-col v-if="networkStatus" span="18">
<el-row>
<el-col span="20">
<el-input v-model="searchText" :placeholder="$t('panel.enter_template_name_tips')" clearable="true" />
</el-col>
<el-row class="outer-body">
<!--预览模式-->
<MarketPreview v-show="previewModel" :preview-id="templatePreviewId" @closePreview="previewModel=false" @templateApply="templateApply" />
<!--列表模式-->
<el-row v-show="!previewModel" class="market-main">
<el-row>
<el-col span="12">
<span class="title-left">{{ $t('panel.template_market') }}</span>
</el-col>
<el-col span="12">
<el-input v-model="searchText" size="small" class="title-right" :placeholder="$t('panel.enter_template_name_tips')" clearable="true" />
</el-col>
</el-row>
<el-row>
<el-tabs v-model="marketActiveTab" @tab-click="handleClick">
<el-tab-pane v-for="tabItem in marketTabs" :key="tabItem" :label="tabItem" :name="tabItem" />
</el-tabs>
</el-row>
<el-row v-if="networkStatus" class="template-main">
<el-row v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
<template-market-item
v-for="(templateItem) in currentMarketTemplateShowList"
v-show="templateItem.showFlag"
:key="templateItem.id"
:template="templateItem"
:base-url="baseUrl"
@templateApply="templateApply"
@templatePreview="templatePreview"
/>
</el-row>
<el-row>
<el-col span="20" style="margin-top: 15px">
<el-tabs v-model="marketActiveTab" @tab-click="handleClick">
<el-tab-pane v-for="tabItem in marketTabs" :key="tabItem" :label="tabItem" :name="tabItem" />
</el-tabs>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row v-if="networkStatus">
<el-row v-loading="$store.getters.loadingMap[$store.getters.currentPath]" style="text-align: center;">
<template-market-item
v-for="(templateItem) in currentMarketTemplateShowList"
v-show="templateItem.showFlag"
:key="templateItem.id"
:template="templateItem"
:base-url="baseUrl"
@templateApply="templateApply"
@templatePreview="templatePreview"
/>
<el-dialog
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
:title="$t('panel.apply_template')"
:visible.sync="folderSelectShow"
width="500px"
class="dialog-css"
append-to-body="true"
:destroy-on-close="true"
>
<el-form ref="panelForm" :model="panelForm" label-width="80px">
<el-form-item :label="$t('panel.name')">
<el-input v-model="panelForm.name" :placeholder="$t('panel.enter_name_tips')" />
</el-form-item>
<el-form-item :label="$t('commons.folder')">
<treeselect
v-model="panelForm.pid"
:clearable="false"
:options="panelGroupList"
:normalizer="normalizer"
:placeholder="$t('chart.select_group')"
:no-children-text="$t('commons.treeselect.no_children_text')"
:no-options-text="$t('commons.treeselect.no_options_text')"
:no-results-text="$t('commons.treeselect.no_results_text')"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer dialog-footer-self">
<el-button size="mini" @click="folderSelectShow=false">{{ $t('commons.cancel') }}</el-button>
<el-button size="mini" type="primary" :disabled="!panelForm.name || !panelForm.pid" @click="apply">{{ $t('commons.confirm') }}</el-button>
</div>
</el-dialog>
<!--预览-->
<el-dialog top="5vh" width="80%" append-to-body="true" :visible.sync="previewVisible">
<img width="100%" :src="templatePreviewUrl" alt="">
</el-dialog>
</el-row>
<el-row v-else class="custom-position">
{{ $t('panel.market_network_tips') }}
</el-row>
</el-row>
<el-row v-else class="custom-position">
{{ $t('panel.market_network_tips') }}
</el-row>
<el-dialog
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
:title="$t('panel.apply_template')"
:visible.sync="folderSelectShow"
width="500px"
class="dialog-css"
append-to-body="true"
:destroy-on-close="true"
>
<el-form ref="panelForm" :model="panelForm" label-width="80px">
<el-form-item :label="$t('panel.name')">
<el-input v-model="panelForm.name" :placeholder="$t('panel.enter_name_tips')" />
</el-form-item>
<el-form-item :label="$t('commons.folder')">
<treeselect
v-model="panelForm.pid"
:clearable="false"
:options="panelGroupList"
:normalizer="normalizer"
:placeholder="$t('chart.select_group')"
:no-children-text="$t('commons.treeselect.no_children_text')"
:no-options-text="$t('commons.treeselect.no_options_text')"
:no-results-text="$t('commons.treeselect.no_results_text')"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer dialog-footer-self">
<el-button size="mini" @click="folderSelectShow=false">{{ $t('commons.cancel') }}</el-button>
<el-button size="mini" type="primary" :disabled="!panelForm.name || !panelForm.pid" @click="apply">{{ $t('commons.confirm') }}</el-button>
</div>
</el-dialog>
</el-row>
</template>
<script>
@ -85,14 +74,16 @@ import TemplateMarketItem from '@/views/panel/templateMarket/component/TemplateM
import { groupTree, panelSave } from '@/api/panel/panel'
import bus from '@/utils/bus'
import { DEFAULT_COMMON_CANVAS_STYLE_STRING } from '@/views/panel/panel'
import MarketPreview from '@/views/panel/templateMarket/component/MarketPreview'
export default {
name: 'TemplateMarket',
components: { TemplateMarketItem },
components: { MarketPreview, TemplateMarketItem },
data() {
return {
previewModel: false,
previewVisible: false,
templatePreviewUrl: null,
templatePreviewId: '',
marketTabs: null,
marketActiveTab: null,
searchText: null,
@ -178,7 +169,7 @@ export default {
showClose: true
})
this.folderSelectShow = false
bus.$emit('newPanelFromMarket', response.data)
this.$router.push({ name: 'panel', params: response.data })
}).catch(() => {
this.loading = false
})
@ -207,9 +198,9 @@ export default {
}
return categoryMarch && searchMarch
},
templatePreview(url) {
this.templatePreviewUrl = url
this.previewVisible = true
templatePreview(previewId) {
this.templatePreviewId = previewId
this.previewModel = true
},
newPanel() {
@ -219,6 +210,26 @@ export default {
</script>
<style lang="scss" scoped>
.template-main{
border-radius: 4px;
box-shadow: 0 0 2px 0 rgba(31,31,31,0.15), 0 1px 2px 0 rgba(31,31,31,0.15);
border: solid 2px #fff;
padding-bottom: 24px;
min-height: calc(100vh - 190px);
}
.market-main{
padding:24px
}
.title-left{
float: left;
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.title-right{
float: right;
width: 320px;
}
.dialog-footer-self{
text-align: center;
}
@ -256,5 +267,9 @@ export default {
flex-flow: row nowrap;
color: #9ea6b2;
}
.outer-body{
width: 100%;
height: calc(100vh - 56px);
}
</style>