Files
ai-agent-admin/web/apps/web-ele/src/components/report-design/modules/preview-dialog.vue
T

290 lines
7.5 KiB
Vue

<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Download, Printer } from '@vben/icons';
import { $t } from '@vben/locales';
import { ReportDialogHeader, ZqUniverPrint } from '@zq/univer';
import '@zq/univer/style';
import UniverHost from '../univer-host.vue';
import { ElButton, ElDialog, ElMessage, ElMessageBox } from 'element-plus';
import {
exportReportDesignExcelApi,
previewReportDesignApi,
} from '#/api/online-dev/report-manager';
import { useReportQuery } from '#/components/report-design/hooks/useReportQuery';
import ReportQueryForm from '#/components/report-design/modules/report-query-form.vue';
import { countSnapshotImages } from '#/components/report-design/utils/report-media-url';
import { showPreviewWarnings } from '#/components/report-design/utils/preview-warnings';
const visible = defineModel<boolean>('visible', { default: false });
const props = defineProps<{
versionId: string;
reportCode?: string;
reportName?: string;
queryList: any[];
getDraftPayload?: () => Record<string, any> | null | undefined;
}>();
const loading = ref(false);
const exportLoading = ref(false);
const allowExport = ref(true);
const allowPrint = ref(true);
const printRef = ref<InstanceType<typeof ZqUniverPrint> | null>(null);
const previewReady = ref(false);
const previewSessionKey = ref(0);
const previewSnapshot = ref<Record<string, any>>({});
const previewCells = ref<Record<string, any>>({});
const previewChartData = ref<any[]>([]);
const previewWatermark = ref<{ show?: boolean; config?: Record<string, any> }>({
show: false,
config: {},
});
const { searchSchemas, formValues, setQueryList, getDefaultParams, setFormValues } =
useReportQuery();
watch(
() => props.queryList,
(list) => setQueryList(list || []),
{ immediate: true },
);
function resetPreviewState() {
previewReady.value = false;
allowExport.value = true;
allowPrint.value = true;
previewSnapshot.value = {};
previewCells.value = {};
previewChartData.value = [];
previewWatermark.value = { show: false, config: {} };
}
watch(visible, async (v) => {
if (!v) {
resetPreviewState();
return;
}
if (!props.versionId) return;
resetPreviewState();
await runPreview();
previewSessionKey.value += 1;
previewReady.value = true;
});
function buildDraftPayload() {
const draft = props.getDraftPayload?.();
if (!draft) return undefined;
return {
snapshot: draft.snapshot,
cells: draft.cells,
queryList: draft.queryList,
sortList: draft.sortList,
columnList: draft.columnList,
fenceList: draft.fenceList,
convertConfig: draft.convertConfig,
};
}
async function runPreview() {
if (!props.versionId) return;
loading.value = true;
try {
const res = await previewReportDesignApi(
props.versionId,
getDefaultParams(),
buildDraftPayload(),
);
previewSnapshot.value = res.snapshot || {};
previewCells.value = res.cells || {};
previewChartData.value = Array.isArray(res.chartData)
? res.chartData
: [];
previewWatermark.value =
res.watermark && typeof res.watermark === 'object'
? res.watermark
: {
show: !!res.allowWatermark,
config: res.watermarkConfig || {},
};
allowExport.value = res.allowExport !== false;
allowPrint.value = res.allowPrint !== false;
showPreviewWarnings(Array.isArray(res.warnings) ? res.warnings : []);
} finally {
loading.value = false;
}
}
async function handleExportExcel() {
if (exportLoading.value || !props.versionId) return;
if (!allowExport.value) {
ElMessage.warning($t('report-manager.export.notAllowed'));
return;
}
exportLoading.value = true;
try {
const blob = await exportReportDesignExcelApi(
props.versionId,
getDefaultParams(),
buildDraftPayload(),
);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${props.reportCode || props.reportName || 'report'}.xlsx`;
a.click();
URL.revokeObjectURL(url);
ElMessage.success($t('report-manager.export.success'));
} catch (error: any) {
ElMessage.error(error?.message || $t('report-manager.export.failed'));
} finally {
exportLoading.value = false;
}
}
async function handlePrint() {
if (!allowPrint.value) {
ElMessage.warning($t('report-manager.print.notAllowed'));
return;
}
if (!previewSnapshot.value || !Object.keys(previewSnapshot.value).length) {
ElMessage.warning($t('report-manager.render.empty'));
return;
}
const imageCount = countSnapshotImages(previewSnapshot.value, previewCells.value);
if (imageCount > 100) {
try {
await ElMessageBox.confirm(
$t('report-manager.print.imageWarn', { count: imageCount }),
$t('report-manager.print.title'),
{ type: 'warning' },
);
} catch {
return;
}
}
printRef.value?.handleCreatePrintUnit?.({
snapshot: previewSnapshot.value as any,
watermarkConfig: previewWatermark.value,
reportName: props.reportName,
reportCode: props.reportCode,
});
}
function handleClose() {
visible.value = false;
}
</script>
<template>
<ElDialog
v-model="visible"
class="report-preview-dialog"
fullscreen
append-to-body
destroy-on-close
:show-close="false"
:close-on-click-modal="false"
>
<div class="bg-background flex h-full min-h-0 flex-col">
<ReportDialogHeader
:report-name="reportName"
:report-code="reportCode"
:fallback-title="$t('report-manager.preview.title')"
>
<template #actions>
<ElButton
v-if="allowExport"
:icon="Download"
:loading="exportLoading"
@click="handleExportExcel"
>
{{ $t('report-manager.export.title') }}
</ElButton>
<ElButton v-if="allowPrint" :icon="Printer" @click="handlePrint">
{{ $t('report-manager.print.title') }}
</ElButton>
<ElButton @click="handleClose">
{{ $t('common.close') }}
</ElButton>
</template>
</ReportDialogHeader>
<main
v-loading="loading"
class="report-preview-dialog__content m-3 min-h-0 min-w-0 flex-1 overflow-hidden rounded-lg"
>
<ReportQueryForm
:schemas="searchSchemas"
:model-value="formValues"
:loading="loading"
@update:model-value="setFormValues"
@search="runPreview"
/>
<div class="report-preview-dialog__sheet">
<UniverHost
v-if="previewReady"
:key="previewSessionKey"
mode="preview"
readonly
class="h-full w-full"
:snapshot="previewSnapshot"
:cells="previewCells"
:chart-data="previewChartData"
:watermark="previewWatermark"
/>
</div>
</main>
</div>
</ElDialog>
<Teleport to="body">
<ZqUniverPrint ref="printRef" />
</Teleport>
</template>
<style lang="scss">
.report-preview-dialog.el-dialog {
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
.el-dialog__header {
display: none;
}
.el-dialog__body {
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
overflow: hidden;
padding: 0;
}
}
.report-preview-dialog__content {
display: flex;
flex: 1;
flex-direction: column;
gap: 12px;
min-height: 0;
min-width: 0;
}
.report-preview-dialog__sheet {
flex: 1;
min-height: 0;
width: 100%;
overflow: hidden;
border-radius: 8px;
}
</style>