de/frontend/src/views/panel/appTemplateMarket/index.vue

400 lines
9.7 KiB
Vue

<template>
<el-row class="outer-body">
<!--预览模式-->
<AppMarketPreview
v-if="previewModel"
:template-info="previewItem"
@closePreview="previewModel=false"
@appApply="appApply"
/>
<!--列表模式-->
<el-row
v-show="!previewModel"
class="market-main"
>
<el-row>
<el-col :span="12">
<span class="title-left">{{ $t('app_template.app_manager') }}</span>
</el-col>
</el-row>
<el-row>
<el-tabs
v-model="marketActiveTab"
@tab-click="handleClick"
>
<el-tab-pane
v-for="(tabItem,index) in marketTabs"
:key="index"
:label="tabItem.label"
:name="tabItem.name"
/>
</el-tabs>
</el-row>
<el-row v-show="marketActiveTab==='apps'">
<app-template-content
:ref="'appTemplateContent'"
class="template-main-content"
:show-position="'market-manage'"
@previewApp="previewApp"
@applyNew="applyNew"
/>
</el-row>
<el-row
v-if="marketActiveTab==='apply_logs'"
class="main-log-area template-main"
>
<app-template-log class="log-area"/>
</el-row>
</el-row>
<el-dialog
v-loading="$store.getters.loadingMap[$store.getters.currentPath]"
:title="$t('panel.apply_template')"
:visible.sync="folderSelectShow"
width="80%"
top="5vh"
class="market-dialog-css"
:append-to-body="true"
:destroy-on-close="true"
>
<el-form
ref="panelForm"
:model="panelForm"
:rules="rule"
label-width="80px"
>
<el-form-item
:label="$t('panel.name')"
prop="name"
>
<el-input
v-model="panelForm.name"
:clearable="true"
:placeholder="$t('panel.enter_name_tips')"
/>
</el-form-item>
<el-form-item
:label="$t('commons.folder')"
prop="pid"
>
<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>
<keep-alive>
<app-template-apply
ref="templateApply"
/>
</keep-alive>
</el-row>
</template>
<script>
import { searchAppTemplate } from '@/api/appTemplateMarket'
import { groupTree, panelSave } from '@/api/panel/panel'
import { DEFAULT_COMMON_CANVAS_STYLE_STRING } from '@/views/panel/panel'
import elementResizeDetectorMaker from 'element-resize-detector'
import AppTemplateLog from '@/views/panel/appTemplateMarket/log'
import AppTemplateContent from '@/views/panel/appTemplate/AppTemplateContent'
import AppMarketPreview from '@/views/panel/appTemplateMarket/component/AppMarketPreview'
import AppTemplateApply from '@/views/panel/appTemplate/component/AppTemplateApply'
export default {
name: 'AppTemplateMarket',
components: { AppTemplateApply, AppMarketPreview, AppTemplateContent, AppTemplateLog },
data() {
return {
previewItem: null,
hasResult: true,
templateMiniWidth: 330,
templateCurWidth: 310,
templateSpan: '25%',
previewModel: false,
previewVisible: false,
templatePreviewId: '',
currentApp: null,
marketTabs: [{ label: 'Apps', name: 'apps' }, { label: this.$t('app_template.apply_logs'), name: 'apply_logs' }],
marketActiveTab: 'apps',
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',
currentAppShowList: [],
networkStatus: true,
rule: {
name: [
{
required: true,
message: this.$t('panel.template_name_tips'),
trigger: 'blur'
}
],
pid: [
{
required: true,
message: '',
trigger: 'blur'
}
]
}
}
},
computed: {},
watch: {},
mounted() {
this.initMarketTemplate()
this.getGroupTree()
const _this = this
const erd = elementResizeDetectorMaker()
const templateMainDom = document.getElementById('template-main')
// 监听div变动事件
if (templateMainDom) {
erd.listenTo(templateMainDom, element => {
_this.$nextTick(() => {
const curSeparator = Math.trunc(templateMainDom.offsetWidth / _this.templateMiniWidth)
_this.templateSpan = (100 / Math.trunc(templateMainDom.offsetWidth / _this.templateMiniWidth)) + '%'
_this.templateCurWidth = Math.trunc(templateMainDom.offsetWidth / curSeparator) - 33
})
})
}
},
methods: {
applyNew(item) {
const datasourceInfo = JSON.parse(item.datasourceInfo)[0]
const param = {
datasourceType: datasourceInfo.type,
appTemplateId: item.id,
appTemplateName: item.name,
panelName: item.name,
datasetGroupName: item.name
}
this.$refs.templateApply.init(param)
},
appApply() {
this.applyNew(this.previewItem)
},
previewApp(item) {
this.previewModel = true
this.previewItem = item
},
initMarketTemplate() {
searchAppTemplate({ nodeType: 'folder' }).then(rsp => {
this.currentAppShowList = rsp.data
}).catch(() => {
this.networkStatus = false
})
},
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.curApplyTemplate = template
this.panelForm.name = template.title
this.panelForm.templateUrl = this.baseUrl + template.metas.theme_repo
this.folderSelectShow = true
},
apply() {
if (this.panelForm.name.length > 50) {
this.$warning(this.$t('commons.char_can_not_more_50'))
return false
}
if (!this.panelForm.templateUrl) {
this.$warning('未获取模板下载链接请联系模板市场官方')
return false
}
panelSave(this.panelForm).then(response => {
this.$message({
message: this.$t('commons.save_success'),
type: 'success',
showClose: true
})
this.folderSelectShow = false
this.$router.push({ name: 'panel', params: response.data })
}).catch(() => {
this.loading = false
})
},
closeDialog() {
this.$emit('closeDialog')
},
handleClick(item) {
},
newPanel() {
},
appPreview(appTemplate) {
this.templatePreviewId = appTemplate.id
this.currentApp = appTemplate
this.previewModel = true
}
}
}
</script>
<style lang="scss" scoped>
.template-main-content {
height: calc(100vh - 190px) !important;
}
.template-main {
text-align: center;
border-radius: 4px;
padding: 0 12px 24px 12px;
height: calc(100vh - 190px) !important;
overflow-x: hidden;
overflow-y: auto;
background-color: var(--ContentBG, #ffffff);
}
.market-main {
padding: 24px
}
.title-left {
float: left;
font-size: 20px;
font-weight: 500;
line-height: 28px;
color: var(--TextPrimary, #1F2329);
}
.title-right {
float: right;
width: 320px;
}
.dialog-footer-self {
text-align: right;
}
.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: #646A73;
font-weight: 400;
}
.outer-body {
width: 100%;
height: calc(100vh - 56px);
background-color: var(--MainBG, #f5f6f7);
}
.market-dialog-css {
::v-deep .el-form-item__label {
width: 100% !important;
text-align: left;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label:before {
display: none;
}
::v-deep
.el-form-item.is-required:not(.is-no-asterisk)
> .el-form-item__label::after {
content: "*";
color: #f54a45;
margin-left: 2px;
}
::v-deep .el-form-item__content {
margin-left: 0 !important;
}
::v-deep .vue-treeselect__input {
vertical-align: middle;
}
}
.main-log-area {
position: relative;
padding: 24px;
}
.log-area {
height: calc(100vh - 240px);
}
</style>