feat:完善舆情监测功能

This commit is contained in:
zjc
2024-12-26 13:46:27 +08:00
parent 50a6b06381
commit 809c950301
11 changed files with 192 additions and 139 deletions

View File

@@ -106,6 +106,7 @@ const instance = axios.create({
*/ */
instance.interceptors.request.use( instance.interceptors.request.use(
(config) => { (config) => {
console.log(config, 'config')
// 规范写法 不可随意自定义 // 规范写法 不可随意自定义
let urlParams = {} let urlParams = {}
if (config.params) { if (config.params) {

View File

@@ -41,10 +41,11 @@ export function getHotWordApi() {
} }
// 舆情指数 // 舆情指数
export function getLineChartApi() { export function getLineChartApi(data) {
return request({ return request({
url: '/api/largeScreen/gsdata/lineChart', url: '/api/largeScreen/gsdata/lineChart',
method: 'post' method: 'get',
params: data
}) })
} }
@@ -55,3 +56,11 @@ export function getAreaApi() {
method: 'post' method: 'post'
}) })
} }
// 景区选择
export function getSpotApi() {
return request({
url: '/api/largeScreen/gsdata/spot',
method: 'post'
})
}

View File

@@ -8,7 +8,7 @@
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item v-for="(item, index) in options" :key="index" :command="item"> <el-dropdown-item v-for="(item, index) in options" :key="index" :command="item">
<span class="label"> {{ item.label }}</span> <span class="label"> {{ item.ssname }}</span>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@@ -27,8 +27,10 @@
default: () => [] default: () => []
} }
}) })
let emit = defineEmits(['on-change'])
const handleCommand = (command) => { const handleCommand = (command) => {
console.log(command) emit('on-change', command)
} }
</script> </script>

View File

@@ -8,10 +8,10 @@
/> />
</template> </template>
<script setup> <script setup>
import * as echarts from 'echarts'
import styleUtil from '@/utils/styleUtil' import styleUtil from '@/utils/styleUtil'
import { fitChartSize } from '@/utils/dataUtil' import { fitChartSize } from '@/utils/dataUtil'
import { guid } from '@/utils/util' import { useEchart } from '@/hooks/echart'
const props = defineProps({ const props = defineProps({
width: { width: {
type: Number, type: Number,
@@ -38,9 +38,8 @@
default: () => {} default: () => {}
} }
}) })
const { id, setOption, initChart } = useEchart()
let id = ref(guid())
let lineChart = null
let timer = null let timer = null
let currentIndex = -1 let currentIndex = -1
@@ -131,20 +130,8 @@
}, },
series: [] series: []
} }
watch( const init = () => {
() => props.data,
(val) => {
if (val.length > 0) {
setTimeout(() => {
initChart() initChart()
}, 500)
}
},
{ immediate: true }
)
const initChart = () => {
const dom = document.getElementById(id.value)
lineChart = echarts.init(dom)
defaultConfig.xAxis.data = props.xAxisData defaultConfig.xAxis.data = props.xAxisData
props.data.map((item, index) => { props.data.map((item, index) => {
defaultConfig.series.push({ defaultConfig.series.push({
@@ -163,53 +150,28 @@
}) })
defaultConfig.legend.data.push(item.name) defaultConfig.legend.data.push(item.name)
}) })
lineChart.setOption({ setOption({
...defaultConfig, ...defaultConfig,
...props.config ...props.config
}) })
// // 开启轮播
// startTooltipLoop()
// // 鼠标悬浮,停止轮播
// dom.addEventListener('mousemove', () => {
// console.log('mouse move')
// closeSwitchTooltip()
// })
// // 鼠标离开,继续轮播
// dom.addEventListener('mousedown', () => {
// console.log('mouse down')
// startTooltipLoop()
// })
// 监听窗口大小变化
window.addEventListener('resize', resize)
}
const resize = () => {
if (lineChart) {
lineChart.dispose()
lineChart = null
initChart()
}
} }
// // 切换tooltip // // 切换tooltip
// const switchTooltip = () => { // const switchTooltip = () => {
// // 取消之前高亮的图形 // // 取消之前高亮的图形
// lineChart.dispatchAction({ // chart.value.dispatchAction({
// type: 'downplay', // type: 'downplay',
// seriesIndex: 0, // seriesIndex: 0,
// dataIndex: currentIndex // dataIndex: currentIndex
// }) // })
// currentIndex = (currentIndex + 1) % 8 // currentIndex = (currentIndex + 1) % 8
// // 高亮当前图形 // // 高亮当前图形
// lineChart.dispatchAction({ // chart.value.dispatchAction({
// type: 'highlight', // type: 'highlight',
// seriesIndex: 0, // seriesIndex: 0,
// dataIndex: currentIndex // dataIndex: currentIndex
// }) // })
// // 显示tooltip // // 显示tooltip
// lineChart.dispatchAction({ // chart.value.dispatchAction({
// type: 'showTip', // type: 'showTip',
// seriesIndex: 0, // seriesIndex: 0,
// dataIndex: currentIndex // dataIndex: currentIndex

View File

@@ -9,10 +9,9 @@
</template> </template>
<script setup> <script setup>
import * as echarts from 'echarts'
import styleUtil from '@/utils/styleUtil' import styleUtil from '@/utils/styleUtil'
import { fitChartSize } from '@/utils/dataUtil' import { fitChartSize } from '@/utils/dataUtil'
import { guid } from '@/utils/util' import { useEchart } from '@/hooks/echart'
const props = defineProps({ const props = defineProps({
width: { width: {
@@ -32,12 +31,16 @@
dataList: { dataList: {
type: Array, type: Array,
default: () => [] default: () => []
},
total: {
type: Number,
default: () => 0
} }
}) })
let id = ref(guid()) const { id, chart, setOption, initChart } = useEchart()
let pieChart = null
var colorList = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA', ''] var colorList = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA']
var defaultCofig = { var defaultCofig = {
color: colorList, color: colorList,
@@ -45,16 +48,12 @@
orient: 'vertical', orient: 'vertical',
bottom: 'center', bottom: 'center',
left: '70%', left: '70%',
data: [], itemWidth: fitChartSize(20),
itemWidth: fitChartSize(16), itemHeight: fitChartSize(20),
itemHeight: fitChartSize(16), itemGap: fitChartSize(20),
itemGap: fitChartSize(10),
formatter: (name) => {
return name + '\u3000' + '19%'
},
textStyle: { textStyle: {
color: '#ffffff', color: '#ffffff',
fontSize: fitChartSize(12) fontSize: fitChartSize(20)
} }
}, },
series: [ series: [
@@ -71,7 +70,7 @@
position: 'center', position: 'center',
fontWeight: 'bold', fontWeight: 'bold',
formatter: function (o) { formatter: function (o) {
return `{value|123456}` + '\n' + `{name|整改率}` return `{value|123456}` + '\n' + `{name|舆情总数 }`
}, },
rich: { rich: {
value: { value: {
@@ -94,30 +93,24 @@
] ]
} }
const initChart = () => {
const dom = document.getElementById(id.value)
pieChart = echarts.init(dom)
defaultCofig.series[0].data = props.dataList
pieChart.setOption({
...defaultCofig,
...props.config
})
// 监听窗口大小变化
window.addEventListener('resize', resize)
}
const resize = () => {
if (pieChart) {
pieChart.dispose()
pieChart = null
initChart()
}
}
watch( watch(
() => props.dataList, () => props.dataList,
(newVal) => { (newVal) => {
if (newVal.length > 0) { if (newVal.length > 0) {
nextTick(() => { nextTick(() => {
initChart() initChart()
defaultCofig.legend.formatter = (name) => {
let percent = props.dataList.find((item) => item.name == name).value
return name + '\u3000' + `${percent}%`
}
defaultCofig.series[0].data = props.dataList
defaultCofig.series[0].label.formatter = () => {
return `{value|${props.total}}` + '\n' + `{name|舆情总数 }`
}
setOption({
...defaultCofig,
...props.config
})
}) })
} }
}, },

28
src/hooks/echart.js Normal file
View File

@@ -0,0 +1,28 @@
import { ref } from 'vue'
import * as echarts from 'echarts'
import { guid } from '@/utils/util'
export function useEchart() {
let chart = ref(null)
let id = ref(guid())
const initChart = () => {
const dom = document.getElementById(id.value)
chart.value = echarts.init(dom)
}
const setOption = (params) => {
chart.value.setOption(params)
}
const resize = () => {
if (chart) {
chart.dispose()
chart = null
initChart()
}
}
onMounted(() => {
// 监听窗口大小变化
window.addEventListener('resize', resize)
})
return { id, chart, setOption, initChart }
}

View File

@@ -1,4 +1,5 @@
import { ref } from 'vue' import { ref } from 'vue'
export function useMap() { export function useMap() {
let map = ref(null) let map = ref(null)
// 初始化地图 // 初始化地图

57
src/hooks/socket.js Normal file
View File

@@ -0,0 +1,57 @@
import { ref } from 'vue'
export function useWebSocket(url) {
let socket = ref(null)
let data = ref(null)
let isConnected = ref(false)
const connectWebSocket = () => {
socket.value = new WebSocket(url, 'echo-protocol', {
headers: {
Authorization:
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.BTxvu6jUWbN0qONWf5K6VzXopE8T8qXzKuX-mij21VJT4U0LdgnqToyqeNDQ2OyJ6cvpdJBzQ9mEEb-dnwrTpQ'
}
})
socket.value.onopen = () => {
isConnected.value = true
console.log('WebSocket connected')
sendMessage(
JSON.stringify({
action: 'start',
type: 'index'
})
)
}
socket.value.onerror = (error) => {
console.error('WebSocket error:', error)
}
socket.value.onmessage = (message) => {
// 处理接收到的消息
console.log('Received message:', JSON.parse(message.data))
if (JSON.parse(message.data)) {
data.value = JSON.parse(message.data)
}
}
socket.value.onclose = () => {
console.log('WebSocket disconnected')
}
}
const sendMessage = (message) => {
if (socket.value) {
socket.value.send(message)
}
}
onMounted(() => {
connectWebSocket()
})
onUnmounted(() => {
if (socket.value) {
socket.value.close()
}
})
return { socket, data, isConnected, connectWebSocket, sendMessage }
}

View File

@@ -150,12 +150,12 @@
<span class="text">男性</span> <span class="text">男性</span>
<div class="progress"> <div class="progress">
<el-progress <el-progress
:percentage="50" :percentage="genderRate['男']"
:show-text="false" :show-text="false"
color="linear-gradient( to right, #074D90 0%, #55E0FF 100%)" color="linear-gradient( to right, #074D90 0%, #55E0FF 100%)"
/> />
</div> </div>
<span class="man">50%</span> <span class="man">{{ genderRate['男'] }}%</span>
</div> </div>
</div> </div>
<div class="cell pt-20"> <div class="cell pt-20">
@@ -164,13 +164,13 @@
<span class="text">女性</span> <span class="text">女性</span>
<div class="progress"> <div class="progress">
<el-progress <el-progress
:percentage="50" :percentage="genderRate['女']"
:show-text="false" :show-text="false"
color="linear-gradient( to right, color="linear-gradient( to right,
#0A4482 0%, #FF7021 100%)" #0A4482 0%, #FF7021 100%)"
/> />
</div> </div>
<span class="woman">50%</span> <span class="woman">{{ genderRate['女'] }}%</span>
</div> </div>
</div> </div>
</div> </div>
@@ -189,10 +189,23 @@
<script setup> <script setup>
import countup from 'vue-countup-v3' import countup from 'vue-countup-v3'
import AgeRatio from './age-ratio.vue'
import top from './top.vue' import top from './top.vue'
import gauge from './gauge.vue' import gauge from './gauge.vue'
import ticket from './ticket.vue' import ticket from './ticket.vue'
import ageRatio from './age-ratio.vue'
let props = defineProps({
data: {
type: Object,
default: () => ({})
}
})
let genderRate = computed(() => {
if (props.data?.userPortrait) return props.data?.userPortrait.genderRate
return {
: 0,
: 0
}
})
let xAxisData = ref([ let xAxisData = ref([
'12-16 10:00', '12-16 10:00',
'12-16 14:00', '12-16 14:00',

View File

@@ -1,16 +1,21 @@
<template> <template>
<Header title="奉节县旅游指挥调度中心" is-skip :nav-left="navLeft" :nav-right="navRight" /> <Header title="奉节县旅游指挥调度中心" is-skip :nav-left="navLeft" :nav-right="navRight" />
<CoreVideo /> <CoreVideo />
<box2 /> <box1 :data="data" />
<box3 /> <box3 />
<box4 /> <box4 />
<Correspondence /> <Correspondence />
</template> </template>
<script setup> <script setup>
import box2 from './components/box-2.vue' import box1 from './components/box-1.vue'
import box3 from './components/box-3.vue' import box3 from './components/box-3.vue'
import box4 from './components/box-4.vue' import box4 from './components/box-4.vue'
import { useWebSocket } from '@/hooks/socket'
const { data, sendMessage } = useWebSocket('ws://36.138.38.16:81/ws/third-party')
const navLeft = [ const navLeft = [
{ name: '安全', path: '/monitor' }, { name: '安全', path: '/monitor' },
{ name: '景区', path: '/scenic' }, { name: '景区', path: '/scenic' },
@@ -22,30 +27,4 @@
{ name: '舆情', path: '/sentiment' }, { name: '舆情', path: '/sentiment' },
{ name: '酒店' } { name: '酒店' }
] ]
onMounted(() => {
let socket = new WebSocket('ws://36.138.38.16:81/ws/third-party', 'echo-protocol', {
headers: {
Authorization:
'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImE1OWFmNWYwLTU3OWItNDJkNy1hZDJhLTY0Y2JlODA5ZWI1NiJ9.BTxvu6jUWbN0qONWf5K6VzXopE8T8qXzKuX-mij21VJT4U0LdgnqToyqeNDQ2OyJ6cvpdJBzQ9mEEb-dnwrTpQ'
}
})
socket.onopen = () => {
console.log('WebSocket connected')
socket.send(
JSON.stringify({
action: 'start',
type: 'index'
})
)
}
socket.onerror = (error) => {
console.error('WebSocket error:', error)
}
socket.onmessage = (message) => {
console.log('Received message:', message.data)
let data = JSON.parse(message.data)
console.log('Received message:', data)
}
socket.onclose = () => {}
})
</script> </script>

View File

@@ -24,18 +24,10 @@
</div> </div>
</div> </div>
<div class="flex mt-20 gap-8 ml-8 mr-8"> <div class="flex mt-20 gap-8 ml-8 mr-8">
<div v-for="(item, index) in stateList" class="border flex-1"> <div v-for="(item, index) in stateList" :key="index" class="border flex-1">
<Title3 :title="index" /> <Title3 :title="item.name" />
<pie-row :width="500" :height="330" :dataList="item" /> <pie-row :width="500" :height="330" :dataList="item.data" :total="item.total" />
</div> </div>
<!-- <div class="border flex-1">
<Title3 title="山峡之巅" />
<pie-row :width="500" :height="330" />
</div>
<div class="border flex-1">
<Title3 title="山峡之巅" />
<pie-row :width="500" :height="330" />
</div> -->
</div> </div>
</div> </div>
<div class="box-1"> <div class="box-1">
@@ -46,6 +38,7 @@
<div class="flex mt-10"> <div class="flex mt-10">
<div class="box-2 mr-10"> <div class="box-2 mr-10">
<Title1 title="舆情指数" /> <Title1 title="舆情指数" />
<Dropdown :options="options" @on-change="onChange" />
<Line <Line
:width="1560" :width="1560"
:height="400" :height="400"
@@ -71,7 +64,13 @@
import Area from './components/area.vue' import Area from './components/area.vue'
import wordCloud from './components/wordCloud.vue' import wordCloud from './components/wordCloud.vue'
import dataSource from './components/dataSource.vue' import dataSource from './components/dataSource.vue'
import { getHotNewApi, getLineChartApi, getStateApi, getTotalApi } from '@/api/sentiment.js' import {
getHotNewApi,
getLineChartApi,
getStateApi,
getTotalApi,
getSpotApi
} from '@/api/sentiment.js'
let seriesData = ref([]) let seriesData = ref([])
let xAxisData = ref([]) let xAxisData = ref([])
@@ -80,7 +79,15 @@
let sensitive = ref(0) let sensitive = ref(0)
let unsensitive = ref(0) let unsensitive = ref(0)
let stateList = ref([]) let stateList = ref([])
let options = ref([])
const onChange = (e) => {
console.log(e, '===')
getLineChart(e.id)
}
const getStop = async () => {
let res = await getSpotApi()
options.value = res.data
}
const getTotal = async () => { const getTotal = async () => {
let res = await getTotalApi() let res = await getTotalApi()
total.value = res.data.total total.value = res.data.total
@@ -92,8 +99,8 @@
stateList.value = res.data stateList.value = res.data
console.log(stateList.value, '------') console.log(stateList.value, '------')
} }
const getLineChart = async () => { const getLineChart = async (id) => {
let res = await getLineChartApi() let res = await getLineChartApi({ id })
xAxisData.value = res.data.data xAxisData.value = res.data.data
seriesData.value = res.data.series seriesData.value = res.data.series
} }
@@ -102,6 +109,7 @@
hotNewList.value = res.data hotNewList.value = res.data
} }
onMounted(() => { onMounted(() => {
getStop()
getHotNew() getHotNew()
getTotal() getTotal()
getState() getState()