<script lang="ts" setup>
|
import type { CustomPrintPreviewExpose } from '#/components/CustomPrint/types';
|
import type { CustomPrintTab, NormalizedReportViewConfig, ReportViewAction, ReportViewConfig } from './types';
|
|
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { BasicModal, useModal } from '@jnpf/ui/modal';
|
|
import { Button, Space } from 'ant-design-vue';
|
|
import CustomPrintPreviewPane from '#/components/CustomPrint/CustomPrintPreviewPane.vue';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
import ReportAttachmentsPane from './ReportAttachmentsPane.vue';
|
import ReportFormDetailPane from './ReportFormDetailPane.vue';
|
import {
|
getDefaultActiveKey,
|
getReportViewHeaderMode,
|
normalizeReportViewConfig,
|
shouldEnablePrintAction,
|
validateReportViewConfig,
|
} from './helpers/reportView';
|
|
const emitter = onlineUtils.getEmitter();
|
const { createConfirm } = useMessage();
|
const [registerModal, { closeModal, openModal }] = useModal();
|
|
const config = ref<NormalizedReportViewConfig | null>(null);
|
const activeKey = ref('customPrint');
|
const actionLoadingKey = ref('');
|
const customPrintRef = ref<CustomPrintPreviewExpose | null>(null);
|
const closing = ref(false);
|
const printPaneLoading = ref(false);
|
const printPaneError = ref('');
|
const printPaneLoaded = ref(false);
|
let openRequestId = 0;
|
|
const headerMode = computed(() => (config.value ? getReportViewHeaderMode(config.value) : 'print'));
|
const activeTab = computed(() => config.value?.tabs.find(tab => tab.internalKey === activeKey.value));
|
const activeCustomPrintTab = computed(() => {
|
const tab = activeTab.value;
|
return tab?.key === 'customPrint' ? (tab as CustomPrintTab) : null;
|
});
|
const printEnabled = computed(() => (config.value && activeTab.value ? shouldEnablePrintAction(config.value, activeTab.value.key) : false));
|
const canPrint = computed(() => printEnabled.value && printPaneLoaded.value && !printPaneLoading.value && !printPaneError.value);
|
|
function resetPrintPaneState() {
|
printPaneLoading.value = false;
|
printPaneError.value = '';
|
printPaneLoaded.value = false;
|
}
|
|
function handleOpen(configValue: ReportViewConfig) {
|
const currentRequestId = ++openRequestId;
|
validateReportViewConfig(configValue);
|
const normalizedConfig = normalizeReportViewConfig(configValue);
|
config.value = normalizedConfig;
|
activeKey.value = getDefaultActiveKey(normalizedConfig);
|
actionLoadingKey.value = '';
|
customPrintRef.value = null;
|
resetPrintPaneState();
|
closing.value = false;
|
|
nextTick(() => {
|
if (closing.value || !config.value || currentRequestId !== openRequestId) return;
|
openModal(true);
|
});
|
}
|
|
async function handleClose() {
|
if (closing.value) return true;
|
closing.value = true;
|
openRequestId++;
|
closeModal();
|
config.value?.onClose?.();
|
return true;
|
}
|
|
function handlePrint() {
|
if (!canPrint.value) return;
|
customPrintRef.value?.print();
|
}
|
|
function handleDownloadPdf() {
|
handlePrint();
|
}
|
|
async function runAction(action: ReportViewAction) {
|
actionLoadingKey.value = action.key;
|
try {
|
await action.onClick();
|
} catch (err) {
|
console.error('[GlobalReportViewModal] action failed:', err);
|
} finally {
|
if (actionLoadingKey.value === action.key) {
|
actionLoadingKey.value = '';
|
}
|
}
|
}
|
|
function handleAction(action: ReportViewAction) {
|
if (action.disabled || actionLoadingKey.value) return;
|
|
if (action.confirmText) {
|
createConfirm({
|
iconType: 'warning',
|
title: '提示',
|
content: action.confirmText,
|
onOk: () => {
|
void runAction(action);
|
},
|
});
|
return;
|
}
|
|
void runAction(action);
|
}
|
|
function handlePrintPaneLoading(loading: boolean) {
|
printPaneLoading.value = loading;
|
if (loading) {
|
printPaneError.value = '';
|
printPaneLoaded.value = false;
|
}
|
}
|
|
function handlePrintPaneError(message: string) {
|
printPaneError.value = message || '加载打印模板失败';
|
printPaneLoading.value = false;
|
printPaneLoaded.value = false;
|
}
|
|
function handlePrintPaneLoaded() {
|
printPaneError.value = '';
|
printPaneLoading.value = false;
|
printPaneLoaded.value = true;
|
}
|
|
watch(() => activeCustomPrintTab.value, resetPrintPaneState);
|
|
onMounted(() => {
|
emitter.on('OPEN_REPORT_VIEW', handleOpen);
|
emitter.on('CLOSE_REPORT_VIEW', handleClose);
|
});
|
|
onUnmounted(() => {
|
emitter.off('OPEN_REPORT_VIEW', handleOpen);
|
emitter.off('CLOSE_REPORT_VIEW', handleClose);
|
});
|
</script>
|
|
<template>
|
<BasicModal
|
:close-func="handleClose"
|
:closable="false"
|
:default-fullscreen="true"
|
:footer="null"
|
:keyboard="true"
|
class="global-report-view-modal"
|
destroy-on-close
|
@register="registerModal">
|
<template #title>
|
<div class="global-report-view-modal__header no-print">
|
<span class="header-title">{{ config?.title }}</span>
|
<Space class="header-actions" :size="10">
|
<template v-if="headerMode === 'actions'">
|
<Button
|
v-for="action in config?.actions"
|
:key="action.key"
|
:danger="action.danger"
|
:disabled="action.disabled || (!!actionLoadingKey && actionLoadingKey !== action.key)"
|
:loading="actionLoadingKey === action.key"
|
:type="action.type"
|
@click="handleAction(action)">
|
{{ action.label }}
|
</Button>
|
</template>
|
<template v-else>
|
<Button :disabled="!canPrint" @click="handleDownloadPdf">导出PDF</Button>
|
<Button type="primary" :disabled="!canPrint" @click="handlePrint">打印</Button>
|
</template>
|
<Button @click="handleClose">关闭</Button>
|
</Space>
|
</div>
|
</template>
|
|
<div class="global-report-view-modal__content">
|
<a-tabs v-if="config" v-model:active-key="activeKey" class="report-tabs">
|
<a-tab-pane v-for="tab in config.tabs" :key="tab.internalKey" :tab="tab.title" />
|
</a-tabs>
|
|
<div class="report-pane">
|
<ReportAttachmentsPane v-if="activeTab?.key === 'attachments'" />
|
<ReportFormDetailPane v-else-if="activeTab?.key === 'formDetail'" :tab="activeTab" />
|
<CustomPrintPreviewPane
|
v-else-if="activeCustomPrintTab"
|
:key="activeCustomPrintTab.print.template"
|
ref="customPrintRef"
|
:config="{ ...activeCustomPrintTab.print, type: 'html-file' }"
|
@error="handlePrintPaneError"
|
@loaded="handlePrintPaneLoaded"
|
@loading="handlePrintPaneLoading" />
|
</div>
|
</div>
|
</BasicModal>
|
</template>
|
|
<style lang="scss" scoped>
|
.global-report-view-modal__header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
width: 100%;
|
|
.header-title {
|
min-width: 0;
|
overflow: hidden;
|
font-weight: 600;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
|
.header-actions {
|
flex-shrink: 0;
|
}
|
}
|
|
.global-report-view-modal__content {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
min-height: 0;
|
background: #f5f5f5;
|
|
.report-tabs {
|
flex-shrink: 0;
|
padding: 0 16px;
|
background: #fff;
|
}
|
|
.report-pane {
|
flex: 1;
|
min-height: 0;
|
overflow: auto;
|
background: #fff;
|
}
|
}
|
|
:deep(.ant-modal-body > .scrollbar) {
|
padding: 0 !important;
|
background: #f5f5f5;
|
}
|
|
:deep(.ant-modal-body > .scrollbar > .scrollbar__wrap) {
|
margin-bottom: 0 !important;
|
}
|
|
:deep(.ant-modal-body > .scrollbar > .scrollbar__wrap > .scrollbar__view) {
|
height: 100%;
|
overflow: hidden;
|
}
|
|
@media print {
|
:deep(.ant-modal-header),
|
:deep(.ant-modal-close),
|
.no-print {
|
display: none !important;
|
}
|
|
:deep(.ant-modal-body) {
|
padding: 0 !important;
|
}
|
|
:deep(.scrollbar__view) {
|
overflow: visible !important;
|
}
|
|
.global-report-view-modal__content,
|
.global-report-view-modal__content .report-pane {
|
height: auto !important;
|
overflow: visible !important;
|
background: #fff !important;
|
}
|
}
|
</style>
|