按需加载,移除vue-chart

This commit is contained in:
xh
2025-12-07 15:06:54 +08:00
parent 894eb74e24
commit 78a3c048a1
21 changed files with 342 additions and 251 deletions

View File

@@ -28,7 +28,7 @@
"crypto-js": "^4.2.0",
"css-color-function": "^1.3.3",
"dayjs": "^1.11.19",
"echarts": "^5.6.0",
"echarts": "^6.0.0",
"element-plus": "^2.12.0",
"highlight.js": "^11.11.1",
"lodash-es": "^4.17.21",
@@ -39,12 +39,11 @@
"spark-md5": "^3.0.2",
"vue": "^3.5.25",
"vue-clipboard3": "^2.0.0",
"vue-echarts": "^7.0.3",
"vue-router": "^4.5.1",
"vue-virtual-scroller": "2.0.0-beta.8",
"vue3-video-play": "^1.3.2",
"vuedraggable": "^4.1.0",
"vxe-table": "^4.16.11",
"vue-virtual-scroller": "2.0.0-beta.8"
"vxe-table": "^4.16.11"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.11.0",

View File

@@ -0,0 +1,123 @@
<!-- src/components/BaseChart.vue -->
<template>
<div ref="chartRef" class="base-chart" :style="{ width, height }"></div>
</template>
<script setup lang="ts">
import { onMounted, onBeforeUnmount, watch, nextTick, ref, toRaw, shallowRef } from 'vue'
import { echarts, ECOption } from '@/utils/echart'
// Props
const props = withDefaults(
defineProps<{
option?: any
width?: string
height?: string
loading?: boolean
autoresize?: boolean // 是否自动 resize
}>(),
{
width: '100%',
height: '100%',
loading: false,
autoresize: true
}
)
// Emits
const emit = defineEmits<{
(e: 'chartInstance', instance: echarts.ECharts): void
}>()
// Refs
const chartRef = ref<HTMLDivElement | null>(null)
const chartInstance = shallowRef<echarts.ECharts | null>(null)
// 初始化图表
const initChart = () => {
if (!chartRef.value) return
// 销毁已有实例
if (chartInstance.value) {
chartInstance.value.dispose()
}
// 创建新实例
chartInstance.value = echarts.init(chartRef.value)
emit('chartInstance', chartInstance.value)
// 监听 option 变化
setOption(props.option)
}
// 设置配置项
const setOption = (option: ECOption) => {
if (!chartInstance.value || !option) return
chartInstance.value.setOption(toRaw(option), {
notMerge: false, // 合并配置(可设为 true 实现增量更新)
lazyUpdate: false
})
}
// 处理 loading
watch(
() => props.loading,
(loading) => {
if (chartInstance.value) {
loading ? chartInstance.value.showLoading() : chartInstance.value.hideLoading()
}
}
)
// 响应 option 变化
watch(
() => props.option,
() => {
nextTick(() => {
setOption(props.option)
})
},
{ deep: true }
)
// 自动 resize
let resizeObserver: ResizeObserver | null = null
const handleResize = () => {
chartInstance.value?.resize()
}
onMounted(() => {
initChart()
// 方式1监听 window resize简单场景
if (props.autoresize) {
window.addEventListener('resize', handleResize)
}
// 方式2更精准使用 ResizeObserver推荐
if (typeof ResizeObserver !== 'undefined' && chartRef.value) {
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(chartRef.value)
}
})
onBeforeUnmount(() => {
if (chartInstance.value) {
chartInstance.value.dispose()
}
window.removeEventListener('resize', handleResize)
if (resizeObserver) {
resizeObserver.disconnect()
}
})
defineExpose({
setOption
})
</script>
<style scoped>
.base-chart {
width: 100%;
height: 100%;
}
</style>

View File

@@ -63,7 +63,7 @@
import { ref, useTemplateRef, watch, defineAsyncComponent } from 'vue'
// import XForm from './XForm/index.vue'
// import XForm2 from './XForm2/index.vue'
const XForm2 = defineAsyncComponent(() => import('./XForm2/index.vue'))
const XForm2 = defineAsyncComponent(() => import('./XForm/index.vue'))
// import FlowEdit from './flowEdit/index.vue'
const FlowEdit = defineAsyncComponent(() => import('./flowEdit/index.vue'))
import BasicSetting from './BasicSetting/index.vue'

View File

@@ -1,17 +1,42 @@
<template>
<v-form-designer ref="designerRef" :designer-config="designerConfig"></v-form-designer>
<!-- <v-form-designer ref="designerRef" :designer-config="designerConfig"></v-form-designer> -->
<fc-designer ref="designerRef" :config="config" />
</template>
<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue'
import 'vform3-builds/dist/designer.style.css' //引入VForm3样式
const designerRef = useTemplateRef<any>('designerRef')
function setData(json) {
import FcDesigner from '@form-create/designer'
const designerRef = useTemplateRef<InstanceType<typeof FcDesigner>>('designerRef')
const config = {}
function setData(json: any[]) {
console.log('setFormJson', json)
designerRef.value.setFormJson(json)
json && designerRef.value.setRule(json)
// 使用 getJson和 getOptionsJson导出数据。
// 使用 setRule和 setOptions方法回显数据。
}
function getFieldWidgets() {
const fieldList = designerRef.value.getFieldWidgets()
const description = designerRef.value.getDescription()
console.log('description', description)
const fieldList: { id: string; name: string }[] = []
function deepChild(item: any) {
if (item.children) {
item.children.forEach((child: any) => {
deepChild(child)
})
} else {
fieldList.push({
id: item.field,
name: item.title
})
}
}
description &&
description.forEach((item) => {
deepChild(item)
})
console.log('getFieldWidgets', fieldList)
return fieldList
}
@@ -20,8 +45,15 @@ function getData() {
formData: any
}>((resolve, reject) => {
try {
const jsonData = designerRef.value.getFormJson()
const jsonData = designerRef.value.getRule()
const getOption = designerRef.value.getOption()
const getDescription = designerRef.value.getDescription()
// 表单组件的层级结构数据
const getFormDescription = designerRef.value.getFormDescription()
console.log('jsonData', jsonData)
console.log('getOption', getOption)
console.log('getDescription', getDescription)
console.log('getFormDescription', getFormDescription)
resolve({ formData: jsonData })
} catch (error) {
reject(error)
@@ -30,15 +62,11 @@ function getData() {
}
const props = defineProps({
conf: {
type: Object,
default: () => {}
type: Array,
default: () => []
}
})
const designerConfig = {
languageMenu: false,
externalLink: false,
formTemplates: false
}
onMounted(() => {
setData(props.conf)
})

View File

@@ -1,84 +0,0 @@
<template>
<!-- <v-form-designer ref="designerRef" :designer-config="designerConfig"></v-form-designer> -->
<fc-designer ref="designerRef" :config="config" />
</template>
<script setup lang="ts">
import { onMounted, useTemplateRef } from 'vue'
import FcDesigner from '@form-create/designer'
const designerRef = useTemplateRef<InstanceType<typeof FcDesigner>>('designerRef')
const config = {}
function setData(json: any[]) {
console.log('setFormJson', json)
json && designerRef.value.setRule(json)
// 使用 getJson和 getOptionsJson导出数据。
// 使用 setRule和 setOptions方法回显数据。
}
function getFieldWidgets() {
const description = designerRef.value.getDescription()
console.log('description', description)
const fieldList: { id: string; name: string }[] = []
function deepChild(item: any) {
if (item.children) {
item.children.forEach((child: any) => {
deepChild(child)
})
} else {
fieldList.push({
id: item.field,
name: item.title
})
}
}
description &&
description.forEach((item) => {
deepChild(item)
})
console.log('getFieldWidgets', fieldList)
return fieldList
}
function getData() {
return new Promise<{
formData: any
}>((resolve, reject) => {
try {
const jsonData = designerRef.value.getRule()
const getOption = designerRef.value.getOption()
const getDescription = designerRef.value.getDescription()
// 表单组件的层级结构数据
const getFormDescription = designerRef.value.getFormDescription()
console.log('jsonData', jsonData)
console.log('getOption', getOption)
console.log('getDescription', getDescription)
console.log('getFormDescription', getFormDescription)
resolve({ formData: jsonData })
} catch (error) {
reject(error)
}
})
}
const props = defineProps({
conf: {
type: Array,
default: () => []
}
})
onMounted(() => {
setData(props.conf)
})
defineExpose({
setData,
getData,
getFieldWidgets
})
</script>
<style lang="scss">
.el-range-editor.el-input__wrapper {
box-sizing: border-box;
}
</style>

View File

@@ -22,7 +22,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, useTemplateRef } from 'vue'
import { ref, onMounted, onBeforeUnmount, useTemplateRef, defineAsyncComponent } from 'vue'
import { LogicFlow } from '@logicflow/core'
import { SelectionSelect, Menu, BpmnElement, MiniMap } from '@logicflow/extension'
@@ -31,9 +31,12 @@ import type { NodeType, PropertiesType } from './PropertyPanel/property.type'
import '@logicflow/core/lib/style/index.css'
import '@logicflow/extension/lib/style/index.css'
import DiagramToolbar from './DiagramToolbar.vue'
import DiagramSidebar from './DiagramSidebar.vue'
import PropertyPanel from './PropertyPanel/index.vue'
// import DiagramToolbar from './DiagramToolbar.vue'
// import DiagramSidebar from './DiagramSidebar.vue'
// import PropertyPanel from './PropertyPanel/index.vue'
const DiagramToolbar = defineAsyncComponent(() => import('./DiagramToolbar.vue'))
const DiagramSidebar = defineAsyncComponent(() => import('./DiagramSidebar.vue'))
const PropertyPanel = defineAsyncComponent(() => import('./PropertyPanel/index.vue'))
import { registerCustomElement } from './node'
defineOptions({

View File

@@ -256,18 +256,19 @@
</el-scrollbar>
</div>
</div>
<preview v-model="showPreview" :url="previewUrl" />
<Preview v-model="showPreview" :url="previewUrl" />
</div>
</template>
<script lang="ts" setup>
import { FileTabsMap } from '@/enums/fileEnums'
import { onMounted, ref, watch, computed } from 'vue'
import { onMounted, ref, watch, computed, defineAsyncComponent } from 'vue'
import { FileExt } from '@/enums/fileEnums'
import { useCate, useFile } from './hook'
import FileItem from './file.vue'
import Preview from './preview.vue'
// import Preview from './preview.vue'
const Preview = defineAsyncComponent(() => import('./preview.vue'))
const props = defineProps({
limit: {
type: Number,

View File

@@ -1,33 +1,57 @@
//引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core'
import type {
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption,
LineSeriesOption
} from 'echarts/charts'
import type {
// 组件类型的定义后缀都为 ComponentOption
TitleComponentOption,
TooltipComponentOption,
GridComponentOption,
DatasetComponentOption
} from 'echarts/components'
import type { ComposeOption } from 'echarts/core'
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = ComposeOption<
| BarSeriesOption
| LineSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| DatasetComponentOption
>
//引入柱状图图表,图表后缀都为 Chart
import {
BarChart,
LineChart,
PieChart,
MapChart,
PictorialBarChart,
RadarChart,
ScatterChart,
GaugeChart
PieChart
// MapChart,//地图
// PictorialBarChart,//象形柱状图
// RadarChart,//雷达图
// ScatterChart,//散点图
// GaugeChart,//仪表盘
} from 'echarts/charts'
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
// PolarComponent,//极坐标系
// AriaComponent, //无障碍访问组件
// ParallelComponent,//平行坐标系
LegendComponent,
RadarComponent,
ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
GraphicComponent
// RadarComponent,//雷达图
// ToolboxComponent,
DataZoomComponent
// VisualMapComponent,//视觉映射组件
// TimelineComponent,//时间线组件
// CalendarComponent,//日历组件
// GraphicComponent,//图形组件
} from 'echarts/components'
//引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
@@ -41,25 +65,27 @@ echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
PolarComponent,
AriaComponent,
ParallelComponent,
// PolarComponent,
// AriaComponent,
// ParallelComponent,
BarChart,
LineChart,
PieChart,
MapChart,
RadarChart,
PictorialBarChart,
RadarComponent,
ToolboxComponent,
// MapChart,
// RadarChart,
// PictorialBarChart,
// RadarComponent,
// ToolboxComponent,
DataZoomComponent,
VisualMapComponent,
TimelineComponent,
CalendarComponent,
GraphicComponent,
ScatterChart,
// VisualMapComponent,
// TimelineComponent,
// CalendarComponent,
// GraphicComponent,
// ScatterChart,
CanvasRenderer,
LabelLayout,
UniversalTransition,
GaugeChart
UniversalTransition
// GaugeChart
])
export { echarts }

View File

@@ -3,7 +3,7 @@
<div class="flex-1 flex items-center justify-center">
<div class="login-card flex rounded-md">
<div class="flex-1 h-full hidden md:inline-block">
<image-contain :src="config.webBackdrop" :width="400" height="100%" />
<ImageContain :src="config.webBackdrop" :width="400" height="100%" />
</div>
<div
class="login-form bg-body flex flex-col justify-center px-10 py-10 md:w-[400px] w-[375px] flex-none mx-auto"
@@ -102,7 +102,7 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, useTemplateRef } from 'vue'
import { computed, onMounted, reactive, useTemplateRef, defineAsyncComponent } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { InputInstance, FormInstance } from 'element-plus'
import LayoutFooter from '@/layout/components/footer.vue'
@@ -113,8 +113,8 @@ import { ACCOUNT_KEY } from '@/enums/cacheEnums'
import { PageEnum } from '@/enums/pageEnum'
import { useLockFn } from '@/hooks/useLockFn'
import { encryptPassword } from '@/utils/util'
import Verify from '@/components/verify/Verify.vue'
import ImageContain from '@/components/image-contain/index.vue'
const Verify = defineAsyncComponent(() => import('@/components/verify/Verify.vue'))
const ImageContain = defineAsyncComponent(() => import('@/components/image-contain/index.vue'))
// const verifyRef = ref(null)
const verifyRef = useTemplateRef<InstanceType<typeof Verify>>('verifyRef')
const onShowCaptcha = () => {

View File

@@ -144,7 +144,7 @@
</template>
<script lang="ts" setup>
import { ref, reactive, onActivated } from 'vue'
import { ref, reactive, onActivated, defineAsyncComponent } from 'vue'
import {
generateTable,
syncColumn,
@@ -153,8 +153,8 @@ import {
downloadCode
} from '@/api/tools/code'
import { usePaging } from '@/hooks/usePaging'
import DataTable from './components/data-table.vue'
import CodePreview from './components/code-preview.vue'
const DataTable = defineAsyncComponent(() => import('./components/data-table.vue'))
const CodePreview = defineAsyncComponent(() => import('./components/code-preview.vue'))
import feedback from '@/utils/feedback'
import { streamFileDownload } from '@/utils/file'
defineOptions({

View File

@@ -7,13 +7,13 @@
:destroy-on-close="true"
:title="applyDetail.flowName"
>
<formCreate
<FormCreate
v-if="dialogVisible"
:rule="formJson"
v-model="formData"
v-model:api="api"
:option="options"
></formCreate>
></FormCreate>
<!-- <v-form-render
:form-json="formJson"
@@ -48,8 +48,9 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import formCreate from '@form-create/element-ui'
import { computed, ref, defineAsyncComponent } from 'vue'
// import formCreate from '@form-create/element-ui'
const FormCreate = defineAsyncComponent(() => import('@form-create/element-ui'))
import type { Api } from '@form-create/element-ui'
import { useDictData } from '@/hooks/useDictOptions'
import type { type_dict } from '@/hooks/useDictOptions'

View File

@@ -92,7 +92,7 @@
</div>
</template>
<script lang="ts" setup>
import { shallowRef, reactive } from 'vue'
import { shallowRef, reactive, defineAsyncComponent } from 'vue'
import {
flow_apply_delete,
flow_apply_lists,
@@ -105,9 +105,8 @@ import { useDictData } from '@/hooks/useDictOptions'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import ApplySubmit from './components/apply_submit.vue'
import ViewForm from './components/ViewForm.vue'
const ApplySubmit = defineAsyncComponent(() => import('./components/apply_submit.vue'))
const ViewForm = defineAsyncComponent(() => import('./components/ViewForm.vue'))
defineOptions({
name: 'flow_apply'
})

View File

@@ -127,7 +127,7 @@
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup
<EditPopup
v-if="showEdit"
ref="editRef"
:dict-data="dictData"
@@ -144,7 +144,7 @@
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, shallowRef, nextTick } from 'vue'
import { ref, reactive, shallowRef, nextTick, defineAsyncComponent } from 'vue'
import {
flow_apply_delete,
flow_apply_lists,
@@ -158,10 +158,10 @@ import { useDictData } from '@/hooks/useDictOptions'
import type { type_dict } from '@/hooks/useDictOptions'
import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import EditPopup from './edit.vue'
const EditPopup = defineAsyncComponent(() => import('./edit.vue'))
import ApplySubmit from './components/apply_submit.vue'
import ViewForm from './components/ViewForm.vue'
const ApplySubmit = defineAsyncComponent(() => import('./components/apply_submit.vue'))
const ViewForm = defineAsyncComponent(() => import('./components/ViewForm.vue'))
import useUserStore from '@/stores/modules/user'

View File

@@ -8,13 +8,13 @@
draggable
:title="applyDetail.flowName"
>
<formCreate
<FormCreate
v-if="dialogVisible"
:rule="formJson"
v-model="formData"
v-model:api="api"
:option="options"
></formCreate>
></FormCreate>
<!-- <v-form-render
:form-json="formJson"
:form-data="formData"
@@ -36,8 +36,8 @@
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import formCreate from '@form-create/element-ui'
import { ref, defineAsyncComponent } from 'vue'
const FormCreate = defineAsyncComponent(() => import('@form-create/element-ui'))
import type { Api } from '@form-create/element-ui'
// import { flow_apply_detail } from '@/api/flow/flow_apply'

View File

@@ -68,7 +68,7 @@
</div>
</template>
<script lang="ts" setup>
import { shallowRef, reactive } from 'vue'
import { shallowRef, reactive, defineAsyncComponent } from 'vue'
import { flow_apply_detail } from '@/api/flow/flow_apply'
import { flow_history_list } from '@/api/flow/flow_history'
import type { type_flow_apply } from '@/api/flow/flow_apply'
@@ -76,9 +76,7 @@ import type { type_flow_apply } from '@/api/flow/flow_apply'
import { useDictData } from '@/hooks/useDictOptions'
import { usePaging } from '@/hooks/usePaging'
import useUserStore from '@/stores/modules/user'
// import ApplySubmit from '@/views/flow/flow_apply/components/apply_submit.vue'
import ViewForm from './components/ViewForm.vue'
const ViewForm = defineAsyncComponent(() => import('./components/ViewForm.vue'))
const userStore = useUserStore()
defineOptions({

View File

@@ -69,7 +69,7 @@
</div>
</template>
<script lang="ts" setup>
import { shallowRef, reactive } from 'vue'
import { shallowRef, reactive, defineAsyncComponent } from 'vue'
import { flow_apply_detail } from '@/api/flow/flow_apply'
import { flow_history_list, flow_history_edit } from '@/api/flow/flow_history'
import type { type_flow_history } from '@/api/flow/flow_history'
@@ -78,7 +78,8 @@ import { usePaging } from '@/hooks/usePaging'
import feedback from '@/utils/feedback'
import useUserStore from '@/stores/modules/user'
import ApplySubmit from './components/apply_submit.vue'
import ViewForm from './components/ViewForm.vue'
// import ViewForm from './components/ViewForm.vue'
const ViewForm = defineAsyncComponent(() => import('./components/ViewForm.vue'))
import Back from './components/Back.vue'
const userStore = useUserStore()

View File

@@ -5,7 +5,9 @@
</template>
<script lang="ts" setup>
import MaterialComponent from '@/components/material/index.vue'
// import MaterialComponent from '@/components/material/index.vue'
import { defineAsyncComponent } from 'vue'
const MaterialComponent = defineAsyncComponent(() => import('@/components/material/index.vue'))
defineOptions({
name: 'materialCenter'

View File

@@ -160,7 +160,7 @@
</el-table-column>
</el-table> -->
</div>
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
<EditPopup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
</template>
<script lang="ts" setup>
import { ref, useTemplateRef, nextTick } from 'vue'

View File

@@ -118,7 +118,10 @@
<div>
<div class="mb-10">命令统计</div>
<div class="flex h-[300px] items-center">
<v-charts autoresize :option="chartOptions.commandChartOption" />
<echart-component
:option="chartOptions.commandChartOption"
:autoresize="true"
/>
</div>
</div>
</el-card>
@@ -128,7 +131,10 @@
<div>
<div class="mb-10">内存信息</div>
<div class="flex h-[300px] items-center">
<v-charts autoresize :option="chartOptions.memoryChartOption" />
<echart-component
:option="chartOptions.memoryChartOption"
:autoresize="true"
/>
</div>
</div>
</el-card>
@@ -138,8 +144,7 @@
<script setup lang="ts">
import { systemCache } from '@/api/setting/system'
import '@/utils/echart'
import vCharts from 'vue-echarts'
import { reactive, ref } from 'vue'
// import { ElTable } from 'element-plus'
defineOptions({

View File

@@ -79,10 +79,12 @@
<template #header>
<span>访问量趋势图</span>
</template>
<div>
<v-charts
style="height: 350px"
:option="workbenchData.visitorOption"
<echart-component
ref="visitorChartRef"
height="350px"
:option="visitorOption"
:autoresize="true"
/>
</div>
@@ -92,12 +94,14 @@
</template>
<script lang="ts" setup>
import { reactive, onDeactivated, onActivated, onMounted, onUnmounted } from 'vue'
import { reactive, onDeactivated, onActivated, onMounted, useTemplateRef, onUnmounted } from 'vue'
import { getWorkbench } from '@/api/app'
import '@/utils/echart'
import vCharts from 'vue-echarts'
import feedback from '@/utils/feedback'
import useUserStore from '@/stores/modules/user'
import type { ECOption } from '@/utils/echart'
const userStore = useUserStore()
defineOptions({
name: 'workbench'
@@ -118,37 +122,36 @@ const workbenchData: any = reactive({
today: {}, // 今日数据
visitor: [], // 访问量
article: [], // 文章阅读量
visitorOption: {
xAxis: {
type: 'category',
data: [0]
},
yAxis: {
type: 'value'
},
legend: {
data: ['访问量']
},
itemStyle: {
// 点的颜色。
color: 'red'
},
tooltip: {
trigger: 'axis'
},
series: [
{
name: '访问量',
data: [0],
type: 'line',
smooth: true
}
]
}
article: [] // 文章阅读量
})
const visitorOption = {
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
legend: {
data: ['访问量']
},
itemStyle: {
// 点的颜色。
color: 'red'
},
tooltip: {
trigger: 'axis'
},
series: [
{
name: '访问量',
data: [],
type: 'line',
smooth: true
}
]
}
const visitorChartRef = useTemplateRef('visitorChartRef')
// 获取工作台主页数据
const getData = async () => {
const res = await getWorkbench()
@@ -156,27 +159,22 @@ const getData = async () => {
workbenchData.today = res.today
workbenchData.visitor = res.visitor
// 清空echarts 数据
workbenchData.visitorOption.xAxis.data = []
workbenchData.visitorOption.series[0].data = []
// 写入从后台拿来的数据
workbenchData.visitorOption.xAxis.data = res.visitor.date
workbenchData.visitorOption.series[0].data = res.visitor.list
visitorOption.xAxis.data = res.visitor.date
visitorOption.series[0].data = res.visitor.list
visitorChartRef.value?.setOption(visitorOption as ECOption)
}
const timer: any = null
function updateChart(val) {
// clearInterval(timer)
// timer = setInterval(() => {
workbenchData.visitorOption.xAxis.data.push(new Date().toLocaleTimeString())
workbenchData.visitorOption.series[0].data.push(val)
visitorOption.xAxis.data.push(new Date().toLocaleTimeString())
visitorOption.series[0].data.push(val)
// 保持数据长度在10个
if (workbenchData.visitorOption.xAxis.data.length > 20) {
workbenchData.visitorOption.xAxis.data.shift()
workbenchData.visitorOption.series[0].data.shift()
if (visitorOption.xAxis.data.length > 20) {
visitorOption.xAxis.data.shift()
visitorOption.series[0].data.shift()
}
// }, 1000)
visitorChartRef.value?.setOption(visitorOption as ECOption)
}
// // 用户 A加入 room1
const wsA = new WebSocket(`ws://localhost:8080/api/ws?token=${userStore.token}&room=room1`)
@@ -192,6 +190,9 @@ wsA.onmessage = (event) => {
feedback.msgSuccess(event.data)
updateChart(JSON.parse(event.data).onlineCount)
}
wsA.onerror = (error) => {
console.error('用户 A 连接错误:', error)
}
// // 用户 B加入 room1
// wsB.onmessage = (event) => {

View File

@@ -25,7 +25,7 @@ export default defineConfig(({ mode }) => {
},
optimizeDeps: {
// 依赖预构建,避免开发刷新
include: ['@wangeditor/editor-for-vue', 'vuedraggable', 'vue-echarts', 'crypto-js']
include: ['@wangeditor/editor-for-vue', 'vuedraggable', 'crypto-js']
},
base: '/',
@@ -42,63 +42,51 @@ export default defineConfig(({ mode }) => {
},
{
name: 'vue-router',
test: /node_modules\/vue-router/
test: /node_modules\/vue-router\//
},
{
name: 'element-plus',
test: /node_modules\/element-plus/
test: /node_modules\/element-plus\//
},
{
name: 'axios',
test: /node_modules\/axios/
test: /node_modules\/axios\//
},
{
name: 'dayjs',
test: /node_modules\/dayjs/
test: /node_modules\/dayjs\//
},
// vuedraggable
{
name: 'vuedraggable',
test: /node_modules\/vuedraggable/
test: /node_modules\/vuedraggable\//
},
// vue3-video-play
{
name: 'vue3-video-play',
test: /node_modules\/vue3-video-play/
test: /node_modules\/vue3-video-play\//
},
// echarts
{
name: 'echarts',
test: /node_modules\/echarts/
test: /node_modules\/echarts\//
},
// highlight.js
{
name: 'highlight.js',
test: /node_modules\/highlight\.js/
test: /node_modules\/highlight\.js\//
},
// lodash-es
{
name: 'lodash-es',
test: /node_modules\/lodash-es/
test: /node_modules\/lodash-es\//
},
// @wangeditor/editor
{
name: '@wangeditor/editor',
test: /node_modules\/@wangeditor/
test: /node_modules\/@wangeditor\//
}
]
// vue: ['vue'],
// 'vue-router': ['vue-router'],
// pinia: ['pinia'],
// axios: ['axios'],
// dayjs: ['dayjs'],
// // echarts: ['echarts'],
// // 'highlight.js': ['highlight.js'],
// 'element-plus': ['element-plus']
// // 'lodash-es': ['lodash-es'],
// // vuedraggable: ['vuedraggable'],
// // 'vform3-builds': ['vform3-builds']
}
}
}