|
|
@@ -3,25 +3,47 @@
|
|
|
<div class="safety-platform-container__header">
|
|
|
<div class="breadcrumb-title"> 应急架构体系 </div>
|
|
|
</div>
|
|
|
- <div class="safety-platform-container__main">
|
|
|
- <OrgChart :treeData="treeData" @node-click="handleNodeClick" v-if="treeData.id !== '-1'" />
|
|
|
- <div v-else class="no-data">暂无队伍</div>
|
|
|
+ <div class="safety-platform-container__main" :class="{ 'zoom-mode': isChartZoomed }">
|
|
|
+ <div class="chart-container" ref="chartContainerRef" v-loading="loadingTeams">
|
|
|
+ <OrgChart
|
|
|
+ :treeData="treeData"
|
|
|
+ @node-click="handleNodeClick"
|
|
|
+ @canvas-click="handleCanvasClick"
|
|
|
+ v-if="treeData.id !== '-1'"
|
|
|
+ />
|
|
|
+ <div class="no-data" v-else>暂无队伍</div>
|
|
|
+ <div class="chart-actions">
|
|
|
+ <el-button v-if="!isChartFullscreen" size="small" @click="toggleChartZoom">
|
|
|
+ {{ isChartZoomed ? '恢复布局' : '放大模式' }}
|
|
|
+ </el-button>
|
|
|
+ <el-button size="small" @click="toggleChartFullscreen">
|
|
|
+ {{ isChartFullscreen ? '退出全屏' : '全屏查看' }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="detail-container" v-loading="loadingTeamsDetail">
|
|
|
+ <TeamDetailList :selected-team-id="selectedTeamId" class="team-detail" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div class="safety-platform-container__footer" v-if="showOperationBar">
|
|
|
<el-button @click="router.push('team-management')"> 编辑 </el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <TeamDetailDrawer ref="teamDetailDrawerRef" :selected-team-id="selectedTeamId" />
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { ref, onMounted } from 'vue';
|
|
|
+ import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
- import OrgChart from '../components/OrgChart.vue';
|
|
|
- import TeamDetailDrawer from './components/TeamDetailDrawer.vue';
|
|
|
+ import { storeToRefs } from 'pinia';
|
|
|
import { useUserInfoHook } from '@/views/disaster/hooks';
|
|
|
import useTeamStore from './team-management/store/userTeam';
|
|
|
import { EMERGENCY_PERMISSIONS } from '@/views/emergency/constant';
|
|
|
+ import OrgChart from '../components/OrgChart.vue';
|
|
|
+ import TeamDetailList from './components/TeamDetailList.vue';
|
|
|
+
|
|
|
+ const { permissions } = useUserInfoHook();
|
|
|
+ const { loadingTeams, loadingTeamsDetail } = storeToRefs(useTeamStore());
|
|
|
+ const { getLeaderTeams } = useTeamStore();
|
|
|
|
|
|
type OrganizationTreeType = {
|
|
|
id: string;
|
|
|
@@ -51,20 +73,49 @@
|
|
|
const router = useRouter();
|
|
|
|
|
|
const showOperationBar = ref(false);
|
|
|
-
|
|
|
- const { permissions } = useUserInfoHook();
|
|
|
- const { getLeaderTeams } = useTeamStore();
|
|
|
-
|
|
|
- const teamDetailDrawerRef = ref<InstanceType<typeof TeamDetailDrawer>>();
|
|
|
+ const chartContainerRef = ref<HTMLElement | null>(null);
|
|
|
+ const isChartFullscreen = ref(false);
|
|
|
+ const isChartZoomed = ref(false);
|
|
|
|
|
|
const selectedTeamId = ref<number | null>(null);
|
|
|
|
|
|
const handleNodeClick = (nodeData: any) => {
|
|
|
selectedTeamId.value = Number(nodeData.id);
|
|
|
+ };
|
|
|
|
|
|
- teamDetailDrawerRef.value?.drawerShow();
|
|
|
+ const handleCanvasClick = () => {
|
|
|
+ selectedTeamId.value = null;
|
|
|
};
|
|
|
|
|
|
+ async function toggleChartFullscreen() {
|
|
|
+ const el = chartContainerRef.value;
|
|
|
+ if (!el) return;
|
|
|
+
|
|
|
+ if (document.fullscreenElement === el) {
|
|
|
+ await document.exitFullscreen();
|
|
|
+ await triggerChartResize();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await el.requestFullscreen();
|
|
|
+ await triggerChartResize();
|
|
|
+ }
|
|
|
+
|
|
|
+ async function toggleChartZoom() {
|
|
|
+ isChartZoomed.value = !isChartZoomed.value;
|
|
|
+ await triggerChartResize();
|
|
|
+ }
|
|
|
+
|
|
|
+ function syncFullscreenState() {
|
|
|
+ isChartFullscreen.value = document.fullscreenElement === chartContainerRef.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function triggerChartResize() {
|
|
|
+ await nextTick();
|
|
|
+ // G6 组件内部监听 window resize,这里主动触发一次确保尺寸立即更新
|
|
|
+ window.dispatchEvent(new Event('resize'));
|
|
|
+ }
|
|
|
+
|
|
|
function convertData(leaderTeams): OrganizationTreeType {
|
|
|
return {
|
|
|
id: leaderTeams.teamId.toString(),
|
|
|
@@ -76,19 +127,70 @@
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
|
+ document.addEventListener('fullscreenchange', syncFullscreenState);
|
|
|
+
|
|
|
showOperationBar.value = Boolean(
|
|
|
permissions.find((item: { code: string }) => item.code === EMERGENCY_PERMISSIONS.ORGANIZATION_MANAGEMENT),
|
|
|
);
|
|
|
|
|
|
const res = await getLeaderTeams();
|
|
|
-
|
|
|
treeData.value = convertData(res);
|
|
|
});
|
|
|
+
|
|
|
+ onBeforeUnmount(() => {
|
|
|
+ document.removeEventListener('fullscreenchange', syncFullscreenState);
|
|
|
+ });
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@use '@/styles/page-details-layout.scss' as *;
|
|
|
|
|
|
+ .chart-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 40%;
|
|
|
+ box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container:fullscreen {
|
|
|
+ height: 100%;
|
|
|
+ padding: 12px;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-actions {
|
|
|
+ position: absolute;
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ right: 12px;
|
|
|
+ bottom: 12px;
|
|
|
+ z-index: 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ .safety-platform-container__main.zoom-mode {
|
|
|
+ .chart-container {
|
|
|
+ height: 75%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-container {
|
|
|
+ height: 25%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 60%;
|
|
|
+ padding-top: 20px;
|
|
|
+ overflow: auto;
|
|
|
+
|
|
|
+ .team-detail {
|
|
|
+ width: 60%;
|
|
|
+ margin: 0 auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.no-data {
|
|
|
width: 100%;
|
|
|
height: 100%;
|