<template>
<el-dialog
ref="enlargeDialog"
:title="viewInfo?.title"
:append-to-body="true"
v-model="dialogShow"
width="70vw"
trigger="click"
class="userViewEnlarge-class"
:style="dialogStyle"
@close="handleClose"
>
<template #header v-if="!isIframe">
<div class="header-title">
<div>{{ viewInfo?.title }}</div>
<div class="export-button">
<el-select
v-if="optType === 'enlarge' && exportPermissions[0]"
v-model="pixel"
class="pixel-select"
size="small"
>
<el-option-group v-for="group in pixelOptions" :key="group.label" :label="group.label">
<el-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-option-group>
</el-select>
<el-button
class="m-button"
v-if="optType === 'enlarge' && exportPermissions[0]"
link
@click="downloadViewImage"
>
<el-icon size="16" style="margin-right: 3px"><icon_download_outlined /></el-icon>
{{ t('chart.export_img') }}
</el-button>
<el-button
class="m-button"
v-if="optType === 'details' && exportPermissions[1]"
link
:loading="exportLoading"
:disabled="
requestStore.loadingMap[permissionStore.currentPath] > 0 ||
state.dataFrom === 'template'
"
@click="downloadViewDetails('view')"
>
<el-icon size="16" style="margin-right: 3px"><icon_download_outlined /></el-icon>
{{ t('chart.export_excel') }}
</el-button>
<el-button
class="m-button"
v-if="optType === 'details' && exportPermissions[2]"
link
:loading="exportLoading"
@click="downloadViewDetails('dataset')"
:disabled="
requestStore.loadingMap[permissionStore.currentPath] > 0 ||
state.dataFrom === 'template'
"
>
<el-icon size="16" style="margin-right: 3px"><icon_download_outlined /></el-icon>
{{ t('chart.export_raw_details') }}
</el-button>
<el-button
class="m-button"
v-if="optType === 'details' && exportPermissions[2] && viewInfo.type === 'table-pivot'"
link
:loading="exportLoading"
@click="exportAsFormattedExcel"
>
<el-icon color="#1F2329" size="16" style="margin-right: 3px"
><icon_download_outlined
/></el-icon>
{{ t('chart.export_excel_formatter') }}
</el-button>
<el-divider
class="close-divider"
direction="vertical"
v-if="exportPermissions[0] || exportPermissions[1] || exportPermissions[2]"
/>
</div>
</div>
</template>
<div
v-loading="downLoading"
:element-loading-text="t('visualization.export_loading')"
element-loading-background="rgba(122, 122, 122, 1)"
class="enlarge-outer"
v-if="dialogShow"
>
<div
id="enlarge-inner-content"
class="enlarge-inner"
:class="{
'enlarge-inner-with-header': optType === 'details' && sourceViewType.includes('chart-mix')
}"
v-loading="requestStore.loadingMap[permissionStore.currentPath]"
ref="viewContainer"
:style="customExport"
>
<component-wrapper
v-if="optType === 'enlarge'"
class="enlarge-wrapper"
:opt-type="optType"
:view-info="viewInfo"
:config="config"
:dv-info="dvInfo"
:font-family="canvasStyleData?.fontFamily"
show-position="viewDialog"
/>
<template v-if="optType === 'details' && !sourceViewType.includes('chart-mix')">
<chart-component-s2
v-if="!detailsError"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails"
/>
<empty-background
v-if="detailsError"
:description="t('visualization.no_details')"
img-type="noneWhite"
/>
</template>
<template v-else-if="optType === 'details' && sourceViewType.includes('chart-mix')">
<el-tabs class="tab-header" v-model="activeName" @tab-change="handleClick">
<el-tab-pane :label="t('chart.drag_block_value_axis_left')" name="left"></el-tab-pane>
<el-tab-pane :label="t('chart.drag_block_value_axis_right')" name="right"></el-tab-pane>
</el-tabs>
<div style="flex: 1">
<chart-component-s2
v-if="activeName === 'left'"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails"
/>
<chart-component-s2
v-else-if="activeName === 'right'"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails2"
/>
</div>
</template>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import ComponentWrapper from '@/components/data-visualization/canvas/ComponentWrapper.vue'
import { computed, h, nextTick, reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { deepCopy } from '@/utils/utils'
import icon_download_outlined from '@/assets/svg/icon_download_outlined.svg'
import ChartComponentS2 from '@/views/chart/components/views/components/ChartComponentS2.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { exportExcelDownload } from '@/views/chart/components/js/util'
import { storeToRefs } from 'pinia'
import { RefreshLeft } from '@element-plus/icons-vue'
import { assign, merge } from 'lodash-es'
import { useEmitt } from '@/hooks/web/useEmitt'
import { ElMessage, ElButton } from 'element-plus-secondary'
import { exportPivotExcel } from '@/views/chart/components/js/panel/common/common_table'
import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { activeWatermarkCheckUser } from '@/components/watermark/watermark'
import { getCanvasStyle } from '@/utils/style'
import { exportPermission } from '@/utils/utils'
import EmptyBackground from '../empty-background/src/EmptyBackground.vue'
import { supportExtremumChartType } from '@/views/chart/components/js/extremumUitl'
import ChartCarouselTooltip from '@/views/chart/components/js/g2plot_tooltip_carousel'
import html2canvas from 'html2canvas'
const downLoading = ref(false)
const dvMainStore = dvMainStoreWithOut()
const dialogShow = ref(false)
const requestStore = useRequestStoreWithOut()
const permissionStore = usePermissionStoreWithOut()
let viewInfo = ref<DeepPartial<ChartObj>>(null)
const config = ref(null)
const viewContainer = ref(null)
const { t } = useI18n()
const optType = ref(null)
const chartComponentDetails = ref(null)
const chartComponentDetails2 = ref(null)
const { dvInfo, isIframe, canvasStyleData } = storeToRefs(dvMainStore)
const exportLoading = ref(false)
const sourceViewType = ref()
const activeName = ref('left')
const detailsError = ref(false)
const DETAIL_CHART_ATTR: DeepPartial<ChartObj> = {
render: 'antv',
type: 'table-info',
customAttr: {
basicStyle: {
tableColumnMode: 'dialog',
tablePageMode: 'pull'
},
tableHeader: {
tableHeaderBgColor: 'rgba(255,255,255,0.3)',
tableHeaderFontColor: '#7C7E81'
},
tableCell: {
tableItemBgColor: 'rgba(255,255,255,0)',
tableFontColor: '#7C7E81',
enableTableCrossBG: false,
mergeCells: false
},
tooltip: {
show: false
}
},
senior: {
scrollCfg: {
open: false
}
},
showPosition: 'dialog'
}
const state = reactive({
scale: 0.5,
componentSourceType: null,
dataFrom: null
})
const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = {
senior: {
scrollCfg: {
open: false
}
},
showPosition: 'dialog'
}
const dialogStyle = computed(() => {
return [
{ '--ed-dialog-bg-color': canvasStyleData.value.dialogBackgroundColor },
{ '--ed-dialog__de-text': canvasStyleData.value.dialogButton }
]
})
const exportPermissions = computed(() =>
exportPermission(dvInfo.value['weight'], dvInfo.value['ext'])
)
const customExport = computed(() => {
const style =
canvasStyleData.value && optType.value === 'enlarge'
? getCanvasStyle(canvasStyleData.value, 'canvas-main')
: {}
if (downLoading.value) {
const bashStyle = pixel.value.split(' * ')
style['width'] = bashStyle[0] + 'px!important'
style['height'] = bashStyle[1] + 'px!important'
return style
} else {
return style
}
})
const pixel = ref('1280 * 720')
const pixelOptions = [
{
label: 'Windows(16:9)',
options: [
{
value: '1920 * 1080',
label: '1920 * 1080'
},
{
value: '1600 * 900',
label: '1600 * 900'
},
{
value: '1280 * 720',
label: '1280 * 720'
}
]
},
{
label: 'MacOS(16:10)',
options: [
{
value: '2560 * 1600',
label: '2560 * 1600'
},
{
value: '1920 * 1200',
label: '1920 * 1200'
},
{
value: '1680 * 1050',
label: '1680 * 1050'
}
]
}
]
const dialogInit = (canvasStyle, view, item, opt, params = { scale: 0.5 }) => {
state.scale = params.scale
sourceViewType.value = view.type
detailsError.value = false
optType.value = opt
dialogShow.value = true
state.componentSourceType = view.type
state.dataFrom = view.dataFrom
viewInfo.value = deepCopy(view) as DeepPartial<ChartObj>
viewInfo.value.customStyle.text.show = false
config.value = deepCopy(item)
if (opt === 'details') {
if (!viewInfo.value.type?.includes('table')) {
assign(viewInfo.value, DETAIL_CHART_ATTR)
viewInfo.value.xAxis.forEach(i => (i.hide = false))
viewInfo.value.yAxis.forEach(i => (i.hide = false))
viewInfo.value['customAttr']['tableHeader']['tableHeaderFontColor'] =
canvasStyleData.value.dialogButton
viewInfo.value['customAttr']['tableCell']['tableFontColor'] =
canvasStyleData.value.dialogButton
} else {
assign(viewInfo.value, DETAIL_TABLE_ATTR)
}
dataDetailsOpt()
}
nextTick(() => {
initWatermark()
ChartCarouselTooltip.paused()
useEmitt().emitter.emit('showEnlargeDialog', true)
})
}
const dataDetailsOpt = () => {
nextTick(() => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
if (viewDataInfo) {
if (sourceViewType.value.includes('chart-mix')) {
chartComponentDetails.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.left)
chartComponentDetails2.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.right)
} else {
chartComponentDetails.value.renderChartFromDialog(viewInfo.value, viewDataInfo)
}
} else {
detailsError.value = true
}
})
}
const handleClick = tab => {
nextTick(() => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
if (tab === 'left') {
chartComponentDetails.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.left)
} else if (tab === 'right') {
chartComponentDetails2.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.right)
}
})
}
const downloadViewImage = () => {
htmlToImage()
}
const downloadViewDetails = (downloadType = 'view') => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
const viewInfoSource = deepCopy(dvMainStore.getViewDetails(viewInfo.value.id))
if (!viewDataInfo) {
ElMessage.error(t('chart.field_is_empty_export_error'))
return
}
const chartExtRequest = dvMainStore.getLastViewRequestInfo(viewInfo.value.id)
const chart = {
...viewInfoSource,
chartExtRequest,
data: viewDataInfo,
type: sourceViewType.value,
downloadType: downloadType,
busiFlag: dvInfo.value.type
}
exportLoading.value = true
exportExcelDownload(chart, dvInfo.value.name, () => {
openMessageLoading(exportData)
})
exportLoading.value = false
}
const exportAsFormattedExcel = () => {
const s2Instance = dvMainStore.getViewInstanceInfo(viewInfo.value.id)
if (!s2Instance) {
return
}
const chart = dvMainStore.getViewDetails(viewInfo.value.id)
exportPivotExcel(s2Instance, chart)
}
const exportData = () => {
useEmitt().emitter.emit('data-export-center', { activeName: 'IN_PROGRESS' })
}
const openMessageLoading = cb => {
const iconClass = `el-icon-loading`
const customClass = `de-message-loading de-message-export`
ElMessage({
message: h('p', null, [
t('data_fill.exporting'),
h(
ElButton,
{
text: true,
size: 'small',
class: 'btn-text',
onClick: () => {
cb()
}
},
t('data_export.export_center')
),
t('data_fill.progress_to_download')
]),
iconClass,
icon: h(RefreshLeft),
showClose: true,
customClass
})
}
// 地图
const mapChartTypes = ['bubble-map', 'flow-map', 'heat-map', 'map', 'symbolic-map']
const htmlToImage = () => {
downLoading.value = mapChartTypes.includes(viewInfo.value.type) ? false : true
useEmitt().emitter.emit('renderChart-viewDialog-' + viewInfo.value.id)
useEmitt().emitter.emit('l7-prepare-picture', viewInfo.value.id)
// 表格和支持最值图表的渲染时间为2000毫秒,其他图表为500毫秒。
const renderTime =
viewInfo.value.type?.includes('table') ||
supportExtremumChartType({ type: viewInfo.value.type })
? 2000
: 500
setTimeout(() => {
initWatermark()
html2canvas(viewContainer.value)
.then(canvas => {
const dom = document.body.appendChild(canvas)
dom.style.display = 'none'
document.body.removeChild(dom)
const dataUrl = dom.toDataURL('image/png', 1)
downLoading.value = false
const a = document.createElement('a')
a.setAttribute('download', viewInfo.value.title)
a.href = dataUrl
a.click()
useEmitt().emitter.emit('l7-unprepare-picture', viewInfo.value.id)
useEmitt().emitter.emit('renderChart-viewDialog-' + viewInfo.value.id)
initWatermark()
})
.catch(error => {
downLoading.value = false
initWatermark()
useEmitt().emitter.emit('l7-unprepare-picture', viewInfo.value.id)
useEmitt().emitter.emit('renderChart-viewDialog-' + viewInfo.value.id)
console.error('oops, something went wrong!', error)
})
}, renderTime)
}
const initWatermark = () => {
activeWatermarkCheckUser('enlarge-inner-content', 'canvas-main', state.scale)
}
const handleClose = () => {
useEmitt().emitter.emit('showEnlargeDialog', false)
ChartCarouselTooltip.closeEnlargeDialogDestroy(viewInfo.value.id)
}
defineExpose({
dialogInit
})
</script>
<style lang="less">
.userViewEnlarge-class {
.ed-dialog__close {
color: var(--ed-dialog__de-text) !important;
}
.ed-dialog__header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-right: unset;
}
.ed-dialog__headerbtn {
position: unset;
}
.header-title {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
color: var(--ed-dialog__de-text);
font-size: 16px;
font-weight: 500;
line-height: 24px;
}
}
</style>
<style lang="less" scoped>
.export-button {
.pixel-select {
width: 125px;
margin-right: 8px;
:deep(.ed-select__wrapper) {
background-color: rgba(255, 255, 255, 0) !important;
}
:deep(.ed-select__placeholder) {
color: var(--ed-dialog__de-text);
}
}
.m-button {
color: var(--ed-dialog__de-text);
font-size: 14px;
font-style: normal;
font-weight: 400;
}
.ed-button.is-link {
font-size: 14px;
font-weight: 400;
padding: 4px;
&:not(.is-disabled):focus,
&:not(.is-disabled):hover {
color: var(--ed-dialog__de-text) !important;
opacity: 0.5;
border-color: transparent;
background-color: rgba(31, 35, 41, 0.1);
}
&:not(.is-disabled):active {
color: #1f2329;
border-color: transparent;
background-color: rgba(31, 35, 41, 0.2);
}
}
}
.close-divider {
margin: 0 16px 0 12px;
}
.enlarge-outer {
position: relative;
height: 65vh;
overflow: hidden;
.enlarge-inner {
position: relative;
width: 100%;
height: 100%;
background-size: 100% 100% !important;
}
.enlarge-inner-with-header {
display: flex;
flex-direction: column;
}
.enlarge-wrapper {
width: 100%;
height: 100%;
}
}
.tab-header {
margin-top: -10px;
margin-bottom: 10px;
--ed-tabs-header-height: 34px;
--custom-tab-color: #646a73;
:deep(.ed-tabs__nav-wrap::after) {
background-color: unset;
}
&.dark {
--custom-tab-color: #a6a6a6;
}
:deep(.ed-tabs__item) {
font-weight: 400;
font-size: 12px;
padding: 0 8px !important;
margin-right: 12px;
color: var(--custom-tab-color);
}
:deep(.is-active) {
font-weight: 500;
color: var(--ed-color-primary, #3370ff);
}
:deep(.ed-tabs__nav-scroll) {
padding-left: 0 !important;
}
:deep(.ed-tabs__header) {
margin: 0 !important;
}
:deep(.ed-tabs__content) {
height: calc(100% - 35px);
overflow-y: auto;
overflow-x: hidden;
}
}
</style>