Browse Source

fix:增加svg全屏功能

dev-xjf
肖晋飞 4 weeks ago
parent
commit
5b3d8eb091
  1. 8
      public/测试.svg
  2. 7
      public/测试深.svg
  3. 21
      src/views/dashboard/demo/components/staticSvg.vue
  4. 194
      src/views/dashboard/demo/components/svg copy.vue
  5. 164
      src/views/dashboard/demo/components/svg.vue
  6. 68
      src/views/dashboard/demo/index copy.vue

8
public/测试.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 325 KiB

After

Width:  |  Height:  |  Size: 325 KiB

7
public/测试深.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 325 KiB

21
src/views/dashboard/demo/components/staticSvg.vue

@ -1,6 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { watch } from 'vue' import { ref, watch } from 'vue'
import { Card } from 'ant-design-vue' import { Card } from 'ant-design-vue'
import ExpandAltOutlined from '@ant-design/icons-vue/ExpandAltOutlined'
import Svg from './svg.vue' import Svg from './svg.vue'
const props = defineProps({ const props = defineProps({
@ -9,20 +10,34 @@ const props = defineProps({
default: () => {}, default: () => {},
}, },
}) })
const path = ref<string>('')
watch( watch(
() => props.data, () => props.data,
(newValue, oldValue) => { (newValue, oldValue) => {
// //
console.log('a has changed', newValue, oldValue) console.log('a has changed', newValue, oldValue)
path.value = newValue.path
}, },
) )
const svgRef: any = ref(null)
function fullScreen() {
//
if (svgRef.value) {
// .value 访
svgRef.value.toggleFullscreen()
}
}
</script> </script>
<template> <template>
<Card class="enter-y !my-1" :title="props.data.Header"> <Card class="enter-y !my-1" :title="props.data.Header">
<!-- <img class="mx-auto h-full w-full" :src="props.data.path"> --> <template #extra>
<Svg :data="props.data.path" /> <ExpandAltOutlined style="cursor:pointer" @click="fullScreen" />
</template>
<Svg ref="svgRef" :path="path" />
</Card> </Card>
</template> </template>

194
src/views/dashboard/demo/components/svg copy.vue

@ -1,63 +1,65 @@
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, ref, toRefs, watch } from 'vue' import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { getEXANowListReal } from '@/api/alert/exa' import { getEXANowListReal } from '@/api/alert/exa'
const props = defineProps<{ const props = defineProps<{
data: string path: string
default: ''
}>() }>()
const { data } = toRefs(props) // const { data } = toRefs(props)
const svgContent = ref<string>('') const svgContent = ref<string>('')
const isLoading = ref<boolean>(true)
const svgError = ref<string>('') const svgError = ref<string>('')
const point = ref<{ itemNameArray: string[] }>({ itemNameArray: [] }) const point = ref<{ itemNameArray: string[] }>({ itemNameArray: [] })
const pointElements = ref<NodeListOf<SVGElement> | undefined>() // const pointElements = ref<NodeListOf<SVGElement> | undefined>()
//
watch(
() => props.path,
(newValue, oldValue) => {
//
console.log('a has changed', newValue)
if (newValue !== oldValue && newValue !== '' && newValue != null)
loadSvg()
},
{ immediate: true },
let updateInterval: number | undefined )
// SVG // SVG
async function loadSvg() { async function loadSvg() {
if (!data.value) { console.log('加载SVG文件...')
if (!props.path) {
svgError.value = '未提供SVG路径' svgError.value = '未提供SVG路径'
isLoading.value = false
return return
} }
isLoading.value = true
svgError.value = '' svgError.value = ''
try {
// URL // URL
let url = data.value let url = props.path
if (!url.startsWith('http') && !url.startsWith('/')) { if (!url.startsWith('http') && !url.startsWith('/')) {
// assets // assets
url = new URL(`../../../../assets/${url}`, import.meta.url).href url = new URL(`../../../../assets/${url}`, import.meta.url).href
} }
const response = await fetch(url) //
if (!response.ok) const timestamp = new Date().getTime()
throw new Error(`无法加载SVG: ${response.statusText}`) if (url.includes('?'))
url += `&t=${timestamp}`
else
url += `?t=${timestamp}`
svgContent.value = await response.text() const response = await fetch(url)
const svgElement = document.querySelector('.svg-container > svg')
svgElement?.removeAttribute('width')
svgElement?.removeAttribute('height')
try { if (!response.ok)
// SVG throw new Error(`无法加载SVG: ${response.statusText}`)
const svgElement = document.querySelector('.svg-container > svg')
if (!svgElement)
return
// point-name svgContent.value = await response.text()
pointElements.value = svgElement.querySelectorAll('[point-name]')
const pointList: string[] = []
pointElements.value.forEach(async (element) => {
const pointName = element.getAttribute('point-name')
pointName && pointList.push(pointName)
})
point.value = { itemNameArray: pointList }
// DOM // DOM
await nextTick() await nextTick()
document.querySelector('.svg-container > svg')?.removeAttribute('width')
document.querySelector('.svg-container > svg')?.removeAttribute('height')
// updateRealtimeValues() // updateRealtimeValues()
} }
@ -65,31 +67,47 @@ async function loadSvg() {
console.error('加载SVG失败:', error) console.error('加载SVG失败:', error)
svgError.value = `加载失败: ${error instanceof Error ? error.message : String(error)}` svgError.value = `加载失败: ${error instanceof Error ? error.message : String(error)}`
} }
finally {
isLoading.value = false
}
} }
// SVG // SVG
async function updateRealtimeValues() { async function updateRealtimeValues() {
if (!svgContent.value || isLoading.value) if (!svgContent.value)
return return
try { try {
const svgElement = document.querySelector('.svg-container > svg')
svgElement?.removeAttribute('width')
svgElement?.removeAttribute('height')
// SVG
if (!svgElement)
return
// point-name
const pointElements = svgElement.querySelectorAll('[point-name]')
const pointList: string[] = []
// 使point-name
pointElements.forEach((element) => {
const pointName = element.getAttribute('point-name')
if (pointName)
pointList.push(pointName)
})
point.value = { itemNameArray: pointList }
const res = await getEXANowListReal(point.value) const res = await getEXANowListReal(point.value)
if (res.ReturnValue && res.ReturnValue === 1) { if (res && res.ReturnValue === 1 && res.ValueArray) {
const data = res.ValueArray const dataValues = res.ValueArray
console.log(data)
if (pointElements.value) { if (pointElements) {
pointElements.value.forEach((element, index) => { pointElements.forEach((element, index) => {
console.log(element) //
console.log(element.tagName) const value = dataValues[index] || ''
// //
if (element.tagName === 'text' || element.tagName === 'tspan' || element.tagName === 'DIV') element.textContent = String(value)
console.log(element.tagName)
element.textContent = String(data[index] || '') // data-value便
element.setAttribute('data-value', String(value))
}) })
} }
} }
@ -99,38 +117,74 @@ async function updateRealtimeValues() {
} }
} }
// const updateInterval = ref<NodeJS.Timeout | null>(null)
watch(
() => data.value,
() => {
loadSvg()
},
{ immediate: true },
)
onMounted(() => { onMounted(() => {
loadSvg() // 便
// updateRealtimeValues() updateInterval.value = setInterval(() => {
setInterval(() => { console.log('定时更新数据...')
updateRealtimeValues() updateRealtimeValues()
}, 3000) // 2 }, 1000)
}) })
onUnmounted(() => { onUnmounted(() => {
// //
clearInterval(updateInterval) clearInterval(updateInterval.value!)
}) })
//
function toggleFullscreen() {
const container = $refs.svgContainer
if (isFullscreen())
exitFullscreen()
else
this.requestFullscreen(container)
}
//
function isFullscreen() {
return document.fullscreenElement
|| document.mozFullScreenElement
|| document.webkitFullscreenElement
|| document.msFullscreenElement
}
//
function requestFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen()
}
else if (element.mozRequestFullScreen) { // Firefox
element.mozRequestFullScreen()
}
else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
element.webkitRequestFullscreen()
}
else if (element.msRequestFullscreen) { // IE/Edge
element.msRequestFullscreen()
}
}
//
function exitFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen()
}
else if (document.mozCancelFullScreen) { // Firefox
document.mozCancelFullScreen()
}
else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera
document.webkitExitFullscreen()
}
else if (document.msExitFullscreen) { // IE/Edge
document.msExitFullscreen()
}
}
</script> </script>
<template> <template>
<!-- <Card class="enter-y !my-1" :title="data.Header || 'SVG可视化'"> --> <!-- <Card class="enter-y !my-1" :title="data.Header || 'SVG可视化'"> -->
<div v-if="isLoading" class="h-40 flex items-center justify-center"> <div v-if="svgError" class="h-40 flex items-center justify-center text-red-500">
加载中...
</div>
<div v-else-if="svgError" class="h-40 flex items-center justify-center text-red-500">
{{ svgError }} {{ svgError }}
</div> </div>
<div v-else class="svg-container" @click="updateRealtimeValues" v-html="svgContent" /> <div v-else class="svg-container" v-html="svgContent" />
<!-- </Card> --> <!-- </Card> -->
</template> </template>
@ -146,12 +200,6 @@ onUnmounted(() => {
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
min-height: 300px;
cursor: pointer; /* 点击时可以手动触发数据更新 */ cursor: pointer; /* 点击时可以手动触发数据更新 */
} }
.svg-container > svg {
max-width: 100%;
max-height: 100%;
}
</style> </style>

164
src/views/dashboard/demo/components/svg.vue

@ -1,44 +1,43 @@
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, ref, toRefs, watch } from 'vue' import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { getEXANowListReal } from '@/api/alert/exa' import { getEXANowListReal } from '@/api/alert/exa'
const props = defineProps<{ const props = defineProps<{
data: string path: string
}>() }>()
const { data } = toRefs(props) // SVG
const svgContainer = ref<HTMLElement>()
// const { data } = toRefs(props)
const svgContent = ref<string>('') const svgContent = ref<string>('')
const isLoading = ref<boolean>(true)
const svgError = ref<string>('') const svgError = ref<string>('')
const point = ref<{ itemNameArray: string[] }>({ itemNameArray: [] }) const point = ref<{ itemNameArray: string[] }>({ itemNameArray: [] })
// const pointElements = ref<NodeListOf<SVGElement> | undefined>() // const pointElements = ref<NodeListOf<SVGElement> | undefined>()
// //
watch( watch(
() => data.value, () => props.path,
(newValue, oldValue) => { (newValue, oldValue) => {
// //
console.log('a has changed', newValue) console.log('a has changed', newValue)
if (newValue !== oldValue && newValue !== '' && newValue != null) if (newValue !== oldValue && newValue !== '' && newValue != null)
loadSvg() loadSvg()
}, },
{ immediate: true },
) )
// SVG // SVG
async function loadSvg() { async function loadSvg() {
console.log('加载SVG文件...') console.log('加载SVG文件...')
console.log(data.value) if (!props.path) {
if (!data.value) {
svgError.value = '未提供SVG路径' svgError.value = '未提供SVG路径'
isLoading.value = false
return return
} }
isLoading.value = true
svgError.value = '' svgError.value = ''
try { try {
// URL // URL
let url = data.value let url = props.path
if (!url.startsWith('http') && !url.startsWith('/')) { if (!url.startsWith('http') && !url.startsWith('/')) {
// assets // assets
url = new URL(`../../../../assets/${url}`, import.meta.url).href url = new URL(`../../../../assets/${url}`, import.meta.url).href
@ -69,16 +68,10 @@ async function loadSvg() {
console.error('加载SVG失败:', error) console.error('加载SVG失败:', error)
svgError.value = `加载失败: ${error instanceof Error ? error.message : String(error)}` svgError.value = `加载失败: ${error instanceof Error ? error.message : String(error)}`
} }
finally {
isLoading.value = false
}
} }
// SVG // SVG
async function updateRealtimeValues() { async function updateRealtimeValues() {
console.log('更新SVG实时数据...', Boolean(svgContent.value))
console.log('更新SVG实时数据...', Boolean(isLoading.value))
if (!svgContent.value) if (!svgContent.value)
return return
try { try {
@ -109,7 +102,6 @@ async function updateRealtimeValues() {
if (pointElements) { if (pointElements) {
pointElements.forEach((element, index) => { pointElements.forEach((element, index) => {
// //
const tagName = element.tagName.toLowerCase()
const value = dataValues[index] || '' const value = dataValues[index] || ''
// //
@ -140,32 +132,144 @@ onUnmounted(() => {
// //
clearInterval(updateInterval.value!) clearInterval(updateInterval.value!)
}) })
//
function toggleFullscreen() {
const container = svgContainer.value
if (!container)
return
if (isFullscreen())
exitFullscreen()
else
requestFullscreen(container)
}
//
function isFullscreen() {
return document.fullscreenElement
|| document.mozFullScreenElement
|| document.webkitFullscreenElement
|| document.msFullscreenElement
}
//
function requestFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen()
}
else if (element.mozRequestFullScreen) { // Firefox
element.mozRequestFullScreen()
}
else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
element.webkitRequestFullscreen()
}
else if (element.msRequestFullscreen) { // IE/Edge
element.msRequestFullscreen()
}
}
//
function exitFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen()
}
else if (document.mozCancelFullScreen) { // Firefox
document.mozCancelFullScreen()
}
else if (document.webkitExitFullscreen) { // Chrome, Safari and Opera
document.webkitExitFullscreen()
}
else if (document.msExitFullscreen) { // IE/Edge
document.msExitFullscreen()
}
}
// 2. 使 defineExpose greet
defineExpose({
toggleFullscreen,
})
</script> </script>
<template> <template>
<!-- <Card class="enter-y !my-1" :title="data.Header || 'SVG可视化'"> --> <div class="svg-wrapper">
<div v-if="isLoading" class="h-40 flex items-center justify-center"> <!-- 控制栏
加载中... <div class="svg-controls">
</div> <h3 class="svg-title">
<div v-else-if="svgError" class="h-40 flex items-center justify-center text-red-500"> SVG可视化
{{ svgError }} </h3>
<button class="fullscreen-btn" title="切换全屏" @click="toggleFullscreen">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
</svg>
</button>
</div> -->
<!-- SVG内容 -->
<div v-if="svgError" class="h-40 flex items-center justify-center text-red-500">
{{ svgError }}
</div>
<div v-else ref="svgContainer" class="svg-container" v-html="svgContent" />
</div> </div>
<div v-else class="svg-container" v-html="svgContent" />
<!-- </Card> -->
</template> </template>
<style scoped> <style scoped>
::v-deep .ant-card-body { .svg-wrapper {
padding: 10px; border: 1px solid #e8e8e8;
overflow: auto; border-radius: 6px;
overflow: hidden;
} }
/* .svg-controls {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
background-color: #f5f5f5;
border-bottom: 1px solid #e8e8e8;
}
.svg-title {
margin: 0;
font-size: 16px;
font-weight: 500;
color: #333;
} */
.svg-container { .svg-container {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%;
height:100%;
min-height: 300px;
padding: 10px;
overflow: auto;
}
/* 全屏状态样式 */
:global(.svg-wrapper:fullscreen),
:global(.svg-wrapper:-webkit-full-screen),
:global(.svg-wrapper:-moz-full-screen),
:global(.svg-wrapper:-ms-fullscreen) {
width: 100%; width: 100%;
height: 100%; height: 100%;
cursor: pointer; /* 点击时可以手动触发数据更新 */ display: flex;
flex-direction: column;
border: none;
border-radius: 0;
}
:global(.svg-wrapper:fullscreen) .svg-container,
:global(.svg-wrapper:-webkit-full-screen) .svg-container,
:global(.svg-wrapper:-moz-full-screen) .svg-container,
:global(.svg-wrapper:-ms-fullscreen) .svg-container {
flex: 1;
padding: 20px;
width:100%;
height:100%;
}
/* SVG内容自适应 */
.svg-container > svg {
max-width: 100%;
max-height: 100%;
} }
</style> </style>

68
src/views/dashboard/demo/index copy.vue

@ -1,68 +0,0 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import WorkbenchHeader from './components/WorkbenchHeader.vue'
import ProjectCard from './components/ProjectCard.vue'
import DynamicInfo from './components/DynamicInfo.vue'
import StaticImg from './components/staticImg.vue'
import ParameterInfo from './components/ParameterInfo.vue'
import Operation from './components/Operation.vue'
import data from './components/demo.json'
import { PageWrapper } from '@/components/Page'
const loading = ref(true)
onMounted(() => {
const htmlRoot = document.getElementsByClassName('workbench')
// let theme = window.localStorage.getItem('__APP__DARK__MODE__')
const theme = 'dark'
if (htmlRoot && theme) {
for (let i = 0; i < htmlRoot.length; i++)
htmlRoot[i].setAttribute('data-theme', theme)
}
// theme = htmlRoot = null
})
setTimeout(() => {
loading.value = false
}, 500)
</script>
<template>
<div class="workbench m-1" data-theme="light">
<WorkbenchHeader class="workbench m-1" data-theme="light" :data="data" />
<div class="enter-y workbench h-92 md:flex" data-theme="light">
<StaticImg data-theme="light" class="workbench m-1 w-full md:w-1/4" :loading="loading" :data="data.LeftUp" />
<ProjectCard data-theme="light" class="enter-y workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftMidUp" />
<div class="enter-y w-full !mr-4 md:w-1/4">
<DynamicInfo :loading="loading" data-theme="light" class="workbench enter-y m-1 h-35 w-full" :data="data.RightMidUp" />
<Operation data-theme="light" class="workbench enter-y m-1 h-54 w-full" :loading="loading" :data="data.RightUp" />
</div>
</div>
<div class="enter-y workbench h-95 md:flex" data-theme="light">
<StaticImg data-theme="light" class="workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.LeftDown" />
<ParameterInfo data-theme="light" class="workbench m-1 w-full md:w-1/2" :loading="loading" :data="data.RightDown" />
</div>
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<StaticImg :loading="loading" :data="data.LeftUp" class="enter-y" />
</div> -->
<!-- <div class="enter-y w-full !mr-4 lg:w-7/10">
<ProjectCard :loading="loading" class="enter-y" />
<DynamicInfo :loading="loading" class="enter-y !my-4" />
</div>
<div class="enter-y w-full lg:w-3/10">
<QuickNav :loading="loading" class="enter-y" />
<Card class="enter-y !my-4" :loading="loading">
<img class="mx-auto h-30 xl:h-50" src="@/assets/svg/illustration.svg">
</Card>
<SaleRadar :loading="loading" class="enter-y" />
</div> -->
</div>
</template>
<style lang="less">
</style>
Loading…
Cancel
Save