Merge pull request #2135 from dataease/pr@dev@feat_tabs-extend

feat: tab组件支持选择其他组件
This commit is contained in:
王嘉豪 2022-04-19 14:07:00 +08:00 committed by GitHub
commit 82fb8b183d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 466 additions and 38 deletions

View File

@ -35,6 +35,12 @@ export default {
linkInfo: {
type: Object,
required: true
},
//
attrPosition: {
type: String,
required: false,
default: 'panel'
}
},
data() {
@ -63,7 +69,8 @@ export default {
},
computed: {
...mapState([
'curComponent'
'curComponent',
'curActiveTabInner'
])
},
methods: {
@ -72,7 +79,11 @@ export default {
},
onSubmit() {
this.linkInfoTemp.src = checkAddHttp(this.linkInfoTemp.src)
this.curComponent.frameLinks = this.linkInfoTemp
if (this.attrPosition === 'panel') {
this.curComponent.frameLinks = this.linkInfoTemp
} else {
this.curActiveTabInner.frameLinks = this.linkInfoTemp
}
this.$store.state.styleChangeTimes++
bus.$emit('frameLinksChange-' + this.curComponent.id)
this.popoverClose()

View File

@ -58,6 +58,12 @@ export default {
linkInfo: {
type: Object,
required: true
},
//
attrPosition: {
type: String,
required: false,
default: 'panel'
}
},
data() {
@ -86,7 +92,8 @@ export default {
},
computed: {
...mapState([
'curComponent'
'curComponent',
'curActiveTabInner'
])
},
methods: {
@ -95,7 +102,11 @@ export default {
},
onSubmit() {
this.streamMediaInfoTemp[this.streamMediaInfoTemp.videoType].url = checkAddHttp(this.streamMediaInfoTemp[this.streamMediaInfoTemp.videoType].url)
this.curComponent.streamMediaLinks = this.streamMediaInfoTemp
if (this.attrPosition === 'panel') {
this.curComponent.streamMediaLinks = this.streamMediaInfoTemp
} else {
this.curActiveTabInner.streamMediaLinks = this.streamMediaInfoTemp
}
this.$store.state.styleChangeTimes++
bus.$emit('streamMediaLinksChange-' + this.curComponent.id)
this.popoverClose()

View File

@ -43,13 +43,19 @@
import { mapState } from 'vuex'
import { deepCopy } from '@/components/canvas/utils/utils'
import { checkAddHttp } from '@/utils/urlUtils'
import bus from "@/utils/bus";
import bus from '@/utils/bus'
export default {
props: {
linkInfo: {
type: Object,
required: true
},
//
attrPosition: {
type: String,
required: false,
default: 'panel'
}
},
data() {
@ -78,7 +84,8 @@ export default {
},
computed: {
...mapState([
'curComponent'
'curComponent',
'curActiveTabInner'
])
},
methods: {
@ -87,6 +94,12 @@ export default {
},
onSubmit() {
this.linkInfoTemp[this.linkInfoTemp.videoType].sources[0].src = checkAddHttp(this.linkInfoTemp[this.linkInfoTemp.videoType].sources[0].src)
if (this.attrPosition === 'panel') {
this.curComponent.videoLinks = this.linkInfoTemp
} else {
this.curActiveTabInner.videoLinks = this.linkInfoTemp
}
this.curComponent.videoLinks = this.linkInfoTemp
this.$store.state.styleChangeTimes++
bus.$emit('videoLinksChange-' + this.curComponent.id)

View File

@ -156,6 +156,24 @@
<tab-style :style-info="styleInfo" />
</el-tooltip>
</div>
<!--tab 内部组件样式-->
<div v-if="attrTabShow('videoLinks')" style="width: 20px;float: left;margin-top: 2px;margin-left: 10px;">
<el-tooltip content="视频信息">
<VideoLinks :attr-position="'tab'" :link-info="curActiveTabInner.videoLinks" />
</el-tooltip>
</div>
<div v-if="attrTabShow('streamMediaLinks')" style="width: 20px;float: left;margin-top: 2px;margin-left: 10px;">
<el-tooltip content="流媒体信息">
<StreamMediaLinks :attr-position="'tab'" :link-info="curActiveTabInner.streamMediaLinks" />
</el-tooltip>
</div>
<div v-if="attrTabShow('frameLinks')" style="width: 20px;float: left;margin-top: 2px;margin-left: 10px;">
<el-tooltip content="网页地址">
<FrameLinks :attr-position="'tab'" :link-info="curActiveTabInner.frameLinks" />
</el-tooltip>
</div>
</div>
</el-card>
@ -340,7 +358,8 @@ export default {
...mapState([
'curComponent',
'curCanvasScale',
'canvasStyleData'
'canvasStyleData',
'curActiveTabInner'
])
},
@ -399,6 +418,10 @@ export default {
}
// console.log('mainWidthOffset:' + this.mainWidthOffset)
},
attrTabShow(attr) {
// console.log('attr:' + attr + this[this.curComponent.type].includes(attr))
return this.curActiveTabInner && this[this.curActiveTabInner.type] && this[this.curActiveTabInner.type].includes(attr)
},
attrShow(attr) {
// console.log('attr:' + attr + this[this.curComponent.type].includes(attr))
return this[this.curComponent.type].includes(attr)

View File

@ -171,6 +171,33 @@ export const pictureList = [
}
]
export const tabUseList = [
{
id: '20002',
component: 'video',
type: 'video',
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'
},
{
id: '30002',
component: 'de-frame',
type: 'de-frame',
label: '网页',
icon: 'iconfont icon-iframe',
defaultClass: 'text-filter'
}
]
export const otherList = [
{
id: '30001',

View File

@ -40,30 +40,35 @@
</el-dropdown-item>
<el-dropdown-item :command="beforeHandleCommond('selectOthers', item)">
添加其他组件
{{ $t('detabs.selectOthers') }}
</el-dropdown-item>
<el-dropdown-item v-if=" element.options.tabList.length > 1" :command="beforeHandleCommond('deleteCur', item)">
<el-dropdown-item
v-if=" element.options.tabList.length > 1"
:command="beforeHandleCommond('deleteCur', item)"
>
{{ $t('table.delete') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
<component
:is="item.content.component"
v-if="item.content && item.content.type!=='view'"
:ref="item.name"
:in-tab="true"
:is-edit="isEdit"
:active="active"
:element="item.content"
:filters="filterMap[item.content.propValue && item.content.propValue.viewId] || []"
:out-style="outStyle"
:edit-mode="editMode"
:h="tabH"
/>
<div v-if="activeTabName === item.name" class="de-tab-content">
<!-- <user-view
v-if="item.content && item.content.propValue && item.content.propValue.viewId"
:ref="item.name"
:in-tab="true"
:is-edit="isEdit"
:active="active"
:element="item.content"
:filters="item.content.filters"
:out-style="outStyle"
/> -->
<user-view
v-if="item.content && item.content.propValue && item.content.propValue.viewId"
v-if="item.content && item.content.type==='view' && item.content.propValue && item.content.propValue.viewId"
:ref="item.name"
:in-tab="true"
:is-edit="isEdit"
@ -121,6 +126,23 @@
</span>
</el-dialog>
<el-dialog
:title="$t('detabs.availableComponents')"
:append-to-body="true"
:visible.sync="otherComponentDialogVisible"
width="300px"
height="250px"
:show-close="false"
:close-on-click-modal="false"
center
>
<tab-use-list v-if="otherComponentDialogVisible" ref="otherComponentSelect" />
<span slot="footer" class="dialog-footer">
<el-button @click="otherComponentDialogVisible = false">{{ $t('table.cancel') }}</el-button>
<el-button type="primary" @click="sureOtherComponentSelector">{{ $t('table.confirm') }}</el-button>
</span>
</el-dialog>
</div>
</template>
@ -134,9 +156,11 @@ import componentList from '@/components/canvas/custom-component/component-list'
import { mapState } from 'vuex'
import { chartCopy } from '@/api/chart/chart'
import { buildFilterMap } from '@/utils/conditionUtil'
import TabUseList from '@/views/panel/AssistComponent/tabUseList'
export default {
name: 'DeTabls',
components: { ViewSelect, AsyncSoltComponent },
components: { TabUseList, ViewSelect, AsyncSoltComponent },
props: {
element: {
type: Object,
@ -156,6 +180,15 @@ export default {
default: function() {
return {}
}
},
editMode: {
type: String,
require: false,
default: 'edit'
},
h: {
type: Number,
default: 200
}
},
data() {
@ -169,16 +202,20 @@ export default {
textarea: '',
curItem: null,
viewDialogVisible: false,
otherComponentDialogVisible: false,
url: '/api/pluginCommon/component/dataease-tabs'
/* fontColor: '#999999',
activeColor: '#f18406',
activeColor: '#f18406',
borderColor: '#999999',
borderActiveColor: '#f18406' */
borderColor: '#999999',
borderActiveColor: '#f18406' */
}
},
computed: {
tabH() {
return this.h - 50
},
dropdownShow() {
return this.isEdit && !this.mobileLayoutStatus
},
@ -212,10 +249,26 @@ export default {
}
},
watch: {
curComponent: {
// curComponent: {
// handler(newVal, oldVla) {
// },
// deep: true
// },
active: {
handler(newVal, oldVla) {
},
deep: true
let activeTabInner
this.element.options.tabList.forEach(item => {
if (item && item.name === this.activeTabName && item.content) {
activeTabInner = item.content
}
})
if (newVal && activeTabInner) {
this.$store.commit('setCurActiveTabInner', activeTabInner)
} else {
this.$store.commit('setCurActiveTabInner', null)
}
}
}
},
created() {
@ -240,6 +293,9 @@ export default {
case 'selectView':
this.selectView(command.param)
break
case 'selectOthers':
this.selectOthers(command.param)
break
}
},
selectView(param) {
@ -247,6 +303,31 @@ export default {
this.curItem = param
this.viewDialogVisible = true
},
selectOthers(param) {
this.activeTabName = param.name
this.curItem = param
this.otherComponentDialogVisible = true
},
sureOtherComponentSelector() {
const curSelectedId = this.$refs.otherComponentSelect.getCurSelectedComponent()
if (curSelectedId) {
let component
const newComponentId = uuid.v1()
componentList.forEach(componentTemp => {
if (componentTemp.id === curSelectedId) {
component = JSON.parse(JSON.stringify(componentTemp))
component.style.width = '100%'
component.style.height = '100%'
this.curItem.content = component
this.curItem.name = newComponentId
this.activeTabName = newComponentId
this.$store.commit('setCurActiveTabInner', component)
this.styleChange()
}
})
}
this.otherComponentDialogVisible = false
},
sureViewSelector() {
const nodes = this.$refs.viewSelect.getCurrentSelected()
if (!nodes || nodes.length === 0) {
@ -341,23 +422,29 @@ export default {
handleClick(tab) {
const name = tab.name
this.element.options.tabList.forEach(item => {
if (item && item.name === name && item.content && item.content.propValue && item.content.propValue.viewId) {
this.filterMap[item.content.propValue.viewId] = item.content.filters
this.$store.dispatch('chart/setViewId', item.content.propValue.viewId)
if (item && item.name === name && item.content) {
this.$store.commit('setCurActiveTabInner', item.content)
if (item.content.propValue && item.content.propValue.viewId) {
this.filterMap[item.content.propValue.viewId] = item.content.filters
this.$store.dispatch('chart/setViewId', item.content.propValue.viewId)
}
}
})
// console.log(tab)
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-tabs__content {
height: calc(100% - 15px) !important;
}
.de-tabs-div {
height: 100%;
overflow: hidden;
}
.de-tabs-height {
height: 100%;
}

View File

@ -412,7 +412,9 @@ export default {
},
detabs: {
eidttitle: 'Edit Title',
selectview: 'Select View'
selectview: 'Select View',
selectOthers: 'Select Others',
availableComponents: 'Available Components'
},
example: {
warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details'

View File

@ -412,7 +412,9 @@ export default {
},
detabs: {
eidttitle: '編輯標題',
selectview: '選擇視圖'
selectview: '選擇視圖',
selectOthers: '选择组件',
availableComponents: '可选组件'
},
example: {
warning: '創建和編輯頁面是不能被 keep-alive 緩存的因爲keep-alive 的 include 目前不支持根據路由來緩存,所以目前都是基於 component name 來進行緩存的。如果妳想類似的實現緩存效果,可以使用 localStorage 等瀏覽器緩存方案。或者不要使用 keep-alive 的 include直接緩存所有頁面。詳情見'

View File

@ -413,7 +413,9 @@ export default {
},
detabs: {
eidttitle: '编辑标题',
selectview: '选择视图'
selectview: '选择视图',
selectOthers: '选择组件',
availableComponents: '可选组件'
},
example: {
warning: '创建和编辑页面是不能被 keep-alive 缓存的因为keep-alive 的 include 目前不支持根据路由来缓存,所以目前都是基于 component name 来进行缓存的。如果你想类似的实现缓存效果,可以使用 localStorage 等浏览器缓存方案。或者不要使用 keep-alive 的 include直接缓存所有页面。详情见'

View File

@ -105,7 +105,9 @@ const data = {
// 视图是否编辑记录
panelViewEditInfo: {},
// 仪表板视图明细
panelViewDetailsInfo: {}
panelViewDetailsInfo: {},
// 当前tab页内组件
curActiveTabInner: null
},
mutations: {
...animation.mutations,
@ -145,6 +147,10 @@ const data = {
state.curComponentIndex = index
},
setCurActiveTabInner(state, curActiveTabInner) {
state.curActiveTabInner = curActiveTabInner
},
setCurCanvasScale(state, curCanvasScale) {
state.curCanvasScale = curCanvasScale
},

View File

@ -0,0 +1,245 @@
<template>
<div class="filter-container">
<div class="filter-widget-content">
<div
v-for="(item, index) in tabUseList"
:key="index"
:data-id="item.id"
:data-index="index"
:class="[
{
['selected-class']:selectedId==item.id
},
'filter-widget '+ (item.defaultClass || '')
]"
@click="componentSelect(item)"
>
<div class="filter-widget-icon">
<i :class="(item.icon || 'el-icon-setting') + ' widget-icon-i'" />
</div>
<div class="filter-widget-text">{{ item.label }}</div>
</div>
</div>
</div>
</template>
<script>
import { tabUseList } from '@/components/canvas/custom-component/component-list'
import { mapState } from 'vuex'
export default {
name: 'TabUseList',
props: {
sourceSelectedId: {
type: String,
required: false
}
},
data() {
return {
tabUseList,
selectedId: this.sourceSelectedId
}
},
computed: {
...mapState([
'canvasStyleData'
])
},
methods: {
componentSelect(item) {
this.selectedId = item.id
},
getCurSelectedComponent() {
if (this.selectedId !== this.sourceSelectedId) {
return this.selectedId
} else {
return null
}
}
}
}
</script>
<style lang="scss" scoped>
.filter-container {
overflow: hidden auto;
min-height: 24px;
padding-top: 0px;
padding-bottom: 0px;
position: relative;
}
.filter-header {
overflow: hidden;
position: relative;
margin-top: 24px;
margin-left: 15px;
align-items: center;
word-break: break-all;
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: nowrap;
}
.filter-header-text {
font-size: 14px;
max-width: 100%;
color: var(--TextPrimary, gray);
text-align: left;
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;
}
.filter-widget-content {
position: relative;
margin-left: 5px;
}
.filter-widget {
width: 107px;
height: 36px;
position: relative;
float: left;
margin-top: 10px;
margin-left: 10px;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#1a3685f2,endColorstr=#1a3685f2);
font-size: 12px;
border-radius: 10px;
cursor: pointer;
overflow: hidden;
}
.time-filter {
background-color: rgba(54,133,242,.1);
.filter-widget-icon {
color: #3685f2;
}
.filter-widget-text {
color: var(--TextActive, #3d4d66);
}
}
.time-filter:hover {
background-color: #3685f2;
color: #fff;
.filter-widget-icon {
background-color: #3685f2;
color: #fff;
}
.filter-widget-text {
color: #fff;
}
}
.text-filter {
background-color: rgba(35,190,239,.1);
.filter-widget-icon {
color: #23beef;
}
.filter-widget-text {
color: var(--TextActive, #3d4d66);
}
}
.text-filter:hover {
background-color: #23beef;
color: #fff;
.filter-widget-icon {
background-color: #23beef;
color: #fff;
}
.filter-widget-text {
color: #fff;
}
}
.tree-filter {
background-color: rgba(22,160,132,.1);
.filter-widget-icon {
color: #37b4aa;
}
.filter-widget-text {
color: var(--TextActive, #3d4d66);
}
}
.tree-filter:hover {
background-color: #37b4aa;
.filter-widget-icon {
color: #37b4aa;
}
.filter-widget-text {
color: #fff;
}
}
.filter-widget-icon {
width: 40px;
height: 36px;
text-align: center;
line-height: 1;
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
justify-content: center;
align-items: center;
flex-direction: row;
flex-wrap: nowrap;
display: flex;
.widget-icon-i {
width: 24px;
height: 24px;
position: relative;
flex-shrink: 0;
font-size: 24px!important;
margin: auto;
font-family: fineui;
font-style: normal;
-webkit-font-smoothing: antialiased;
text-align: center;
}
}
.filter-widget-text {
font-size: 14px;
height: 36px;
line-height: 36px;
text-align: left;
white-space: pre;
text-overflow: ellipsis;
position: absolute;
/* inset: 0px 0px 0px 40px; */
margin-left: 40px;
box-sizing: border-box;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
word-break: break-all;
cursor: pointer;
}
.widget-subject {
display: flow-root;
}
.selected-class {
background-color: #23beef;
.filter-widget-icon {
color: #fff;
}
.filter-widget-text {
color: #fff;
}
}
</style>

View File

@ -419,7 +419,6 @@ export default {
this.editPanel.optType = param.optType
this.editPanel.panelInfo.nodeType = param.type
this.editPanel.visible = true
const temp = DEFAULT_COMMON_CANVAS_STYLE_STRING
switch (param.optType) {
case 'new':
this.editPanel.titlePre = this.$t('commons.create')