| | |
| | | Content: typeof import('./components/core/Content.vue')['default'] |
| | | copy: typeof import('./components/search-option/FYOptionScene copy.vue')['default'] |
| | | ElAffix: typeof import('element-plus/es')['ElAffix'] |
| | | ElAlert: typeof import('element-plus/es')['ElAlert'] |
| | | ElAside: typeof import('element-plus/es')['ElAside'] |
| | | ElAvatar: typeof import('element-plus/es')['ElAvatar'] |
| | | ElBadge: typeof import('element-plus/es')['ElBadge'] |
| | |
| | | <el-select |
| | | :model-value="formatedValue" |
| | | @update:model-value="handleChange" |
| | | placeholder="总任务" |
| | | :placeholder="label" |
| | | style="width: 260px" |
| | | > |
| | | <el-option |
| | | v-for="s in topTasks" |
| | | v-for="s in filtedBeforeTask" |
| | | :key="s.value" |
| | | :label="s.label" |
| | | :value="s.value" |
| | |
| | | type: String, |
| | | default: 'topTaskId' |
| | | }, |
| | | // 选项筛选条件,筛选某任务之前的相同行政区划内的任务 |
| | | beforeTask: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | } |
| | | } |
| | | }, |
| | | emits: ['update:value'], |
| | | data() { |
| | | return { |
| | | selected: {}, |
| | | topTasks: [], |
| | | topTasks: [] |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 选择框中使用顶层任务id作为选项值 |
| | | formatedValue() { |
| | | return this.value.tguid; |
| | | }, |
| | | // 某任务之前的相同行政区划内的任务 |
| | | filtedBeforeTask() { |
| | | const filteredTasks = this.topTasks.filter((t) => { |
| | | return ( |
| | | (!this.beforeTask.provincecode || |
| | | this.beforeTask.provincecode == t.data.provincecode) && |
| | | (!this.beforeTask.citycode || |
| | | this.beforeTask.citycode == t.data.citycode) && |
| | | (!this.beforeTask.districtcode || |
| | | this.beforeTask.districtcode == t.data.districtcode) && |
| | | (!this.beforeTask.starttime || |
| | | t.data.starttime < this.beforeTask.starttime) |
| | | ); |
| | | }); |
| | | if (filteredTasks.length > 0) { |
| | | this.handleChange(filteredTasks[0]?.value); |
| | | } |
| | | return filteredTasks; |
| | | } |
| | | }, |
| | | methods: { |
| | |
| | | }, |
| | | //查询子任务统计信息 |
| | | handleChange(value) { |
| | | const task = this.topTasks.find( |
| | | (t) => t.data.tguid == value |
| | | ); |
| | | const param = task ? task.data : {} |
| | | const task = this.topTasks.find((t) => t.data.tguid == value); |
| | | const param = task ? task.data : {}; |
| | | |
| | | this.$emit('update:value', param); |
| | | }, |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.getOptions(); |
| | |
| | | }; |
| | | } |
| | | |
| | | function barChartOption() { |
| | | return { |
| | | title: { |
| | | text: `柱状图默认名称`, |
| | | left: 'center' // 标题居中显示 |
| | | }, |
| | | // 添加工具栏配置,包含下载功能 |
| | | toolbox: { |
| | | show: true, |
| | | feature: { |
| | | saveAsImage: { |
| | | show: true, |
| | | title: '下载图表', |
| | | type: 'png', |
| | | pixelRatio: 2 // 提高图片清晰度 |
| | | } |
| | | } |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', // 柱状图使用axis触发tooltip |
| | | axisPointer: { |
| | | type: 'shadow' // 显示阴影指示器 |
| | | }, |
| | | formatter: '{b}: {c}' // 显示格式:名称: 数量 |
| | | }, |
| | | legend: { |
| | | show: true, |
| | | orient: 'horizontal', |
| | | bottom: '0%', // 图例底部水平排列 |
| | | }, |
| | | grid: { |
| | | // left: '3%', |
| | | // right: '4%', |
| | | bottom: '10%', |
| | | top: '15%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | name: '坐标轴', |
| | | type: 'category', |
| | | data: ['sample1', 'sample2', 'sample3'], // X轴数据 |
| | | axisTick: { |
| | | alignWithLabel: true |
| | | }, |
| | | axisLabel: { |
| | | rotate: 45, |
| | | } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: '数量', // Y轴名称 |
| | | axisLine: { |
| | | show: true |
| | | }, |
| | | axisLabel: { |
| | | formatter: '{value}' |
| | | } |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'sample', |
| | | type: 'bar', // 图表类型改为柱状图 |
| | | data: [100, 200, 300], // 数据值 |
| | | label: { |
| | | show: true, |
| | | position: 'top', // 标签显示在柱子顶部 |
| | | formatter: '{c}' // 标签格式:数量 |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | } |
| | | |
| | | // 通过 ECharts API 下载图片的函数 |
| | | function downloadChartImage(chart, fileName) { |
| | | if (!chart) return; // 确保图表已初始化 |
| | |
| | | document.body.removeChild(link); |
| | | } |
| | | |
| | | export { pieChartOption, downloadChartImage }; |
| | | export { pieChartOption, barChartOption, downloadChartImage }; |
| | |
| | | > |
| | | <div v-show="showStep1Content"> |
| | | <template v-if="$slots.step1"> |
| | | <slot name="step1"></slot> |
| | | <slot name="step1" :onSearch="onSearch"></slot> |
| | | </template> |
| | | <template v-else> |
| | | <ProdQueryOpt :loading="loading" @submit="onSearch"> </ProdQueryOpt> |
| | |
| | | </template> |
| | | <el-form :inline="false" label-position="left" label-width="150px"> |
| | | <el-form-item label="区县"> |
| | | <el-text>{{ queryOpt.districtName }}</el-text> |
| | | <el-text>{{ opts.districtName }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="时间范围"> |
| | | <el-text>{{ queryOpt.startTime }} 至 {{ queryOpt.endTime }}</el-text> |
| | | <el-text>{{ opts.startTime }} 至 {{ opts.endTime }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="场景类型"> |
| | | <el-text>{{ queryOpt.sceneTypeName }}</el-text> |
| | | <el-text>{{ opts.sceneTypeName }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="产品形式"> |
| | | <el-radio-group v-model="downloadType"> |
| | |
| | | const emit = defineEmits(['submit']); |
| | | |
| | | const downloadType = ref('1'); |
| | | const opts = computed(() => { |
| | | if (props.queryOpt instanceof Array && props.queryOpt.length > 0) { |
| | | return props.queryOpt[0]; |
| | | } else { |
| | | return props.queryOpt; |
| | | } |
| | | }); |
| | | |
| | | const submit = () => { |
| | | emit('submit', { |
| | |
| | | <template #header> |
| | | <div><el-text tag="b" size="large">产品生成选项</el-text></div> |
| | | </template> |
| | | <el-form :inline="true" :model="formSearch"> |
| | | <FYOptionTopTask v-model:value="formSearch.topTask"></FYOptionTopTask> |
| | | <FYOptionScene |
| | | :allOption="false" |
| | | :type="2" |
| | | v-model:value="formSearch.scenetype" |
| | | ></FYOptionScene> |
| | | </el-form> |
| | | <el-form :inline="true" :model="formSearch2"> |
| | | <FYOptionTopTask v-model:value="formSearch2.topTask"></FYOptionTopTask> |
| | | </el-form> |
| | | <el-space fill> |
| | | <el-alert type="info" show-icon :closable="false"> |
| | | <p>选择本期需要统计的总任务和场景类型</p> |
| | | </el-alert> |
| | | <el-form :inline="true" :model="formSearch"> |
| | | <FYOptionTopTask v-model:value="formSearch.topTask"></FYOptionTopTask> |
| | | <FYOptionScene |
| | | :allOption="false" |
| | | :type="2" |
| | | v-model:value="formSearch.scenetype" |
| | | ></FYOptionScene> |
| | | </el-form> |
| | | </el-space> |
| | | |
| | | <el-space fill> |
| | | <el-alert type="info" show-icon :closable="false"> |
| | | <p>选择需要进行对比的历史版本</p> |
| | | </el-alert> |
| | | <el-form :inline="true" :model="formSearch2"> |
| | | <FYOptionTopTask |
| | | :beforeTask="formSearch.topTask" |
| | | v-model:value="formSearch2.topTask" |
| | | ></FYOptionTopTask> |
| | | </el-form> |
| | | </el-space> |
| | | <template #footer> |
| | | <el-row v-show="active" justify="end"> |
| | | <el-button |
| | |
| | | <template> |
| | | <BaseProdProcess |
| | | v-model:active="active" |
| | | @onStep1="onStep1" |
| | | @onStep2="onStep2" |
| | | @onStep3="onStep3" |
| | | :loading="loading" |
| | | > |
| | | <template #step1> |
| | | <ProdQueryOptCompare @submit="onStep1"></ProdQueryOptCompare> |
| | | <template #step1="{ onSearch }"> |
| | | <ProdQueryOptCompare :loading="loading" @submit="onSearch"></ProdQueryOptCompare> |
| | | </template> |
| | | <template #step2="{ contentHeight }"> |
| | | <el-scrollbar :height="contentHeight"> |
| | | <el-table |
| | | id="prod-problem-count-table" |
| | | :data="tableData" |
| | | v-loading="loading" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | size="small" |
| | | border |
| | | > |
| | | <el-table-column fixed="left" type="index" label="编号" width="50"> |
| | | </el-table-column> |
| | | <el-table-column |
| | | fixed="left" |
| | | prop="districtName" |
| | | label="区县" |
| | | width="110" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column prop="townName" label="街镇" width="110" /> |
| | | <el-table-column |
| | | prop="sceneCount" |
| | | label="本期场景数" |
| | | min-width="70" |
| | | /> |
| | | <el-table-column |
| | | prop="lastSceneCount" |
| | | label="上期场景数" |
| | | min-width="70" |
| | | /> |
| | | <el-table-column |
| | | prop="problemCount" |
| | | label="本期问题数" |
| | | min-width="70" |
| | | /> |
| | | <el-table-column |
| | | prop="lastProblemCount" |
| | | label="上期问题数" |
| | | min-width="70" |
| | | /> |
| | | <el-table-column |
| | | prop="ratio" |
| | | label="本期问题数均值" |
| | | min-width="70" |
| | | :formatter="ratioFormat" |
| | | /> |
| | | <el-table-column |
| | | prop="lastRatio" |
| | | label="上期问题数均值" |
| | | min-width="70" |
| | | :formatter="ratioFormat" |
| | | /> |
| | | </el-table> |
| | | <el-row justify="center"> |
| | | <div |
| | | ref="chartRef" |
| | | style="height: 400px; width: 100%; max-width: 800px" |
| | | ></div> |
| | | </el-row> |
| | | </el-scrollbar> |
| | | </template> |
| | | </BaseProdProcess> |
| | | </template> |
| | | <script setup> |
| | | import { ref } from 'vue'; |
| | | import { computed, ref } from 'vue'; |
| | | import * as echarts from 'echarts'; |
| | | import dayjs from 'dayjs'; |
| | | import BaseProdProcess from '@/views/fysp/data-product/components/BaseProdProcess.vue'; |
| | | import dataprodmiddleApi from '@/api/fysp/dataprodmiddleApi.js'; |
| | | import { conversionFromTable } from '@/utils/excel'; |
| | | import { useProdStepChange } from '@/views/fysp/data-product/prod-step-change.js'; |
| | | import { barChartOption, downloadChartImage } from '@/utils/echart-util.js'; |
| | | import ProdQueryOptCompare from '@/views/fysp/data-product/components/ProdQueryOptCompare.vue'; |
| | | |
| | | const { active, changeActive } = useProdStepChange(); |
| | | const loading = ref(false); |
| | | const tableData1 = ref([]); |
| | | const tableData2 = ref([]); |
| | | const chartRef = ref(null); |
| | | let chart; |
| | | |
| | | const tableData = computed(() => { |
| | | return tableData1.value.map((item) => { |
| | | const last = tableData2.value.find( |
| | | (item2) => item2.townCode === item.townCode |
| | | ); |
| | | item.ratio = Math.round(item.ratio * 10) / 10 || 0; |
| | | return { |
| | | ...item, |
| | | lastSceneCount: last?.sceneCount || 0, |
| | | lastProblemCount: last?.problemCount || 0, |
| | | lastRatio: Math.round(item.ratio * 10) / 10 || 0 |
| | | }; |
| | | }); |
| | | }); |
| | | |
| | | function onStep1(opts) { |
| | | console.log('onStep1', opts); |
| | | loading.value = true; |
| | | const p1 = dataprodmiddleApi.fetchProblemCountByArea(opts[0]).then((res) => { |
| | | if (res.success) { |
| | | tableData1.value = res.data; |
| | | } |
| | | }); |
| | | const p2 = dataprodmiddleApi.fetchProblemCountByArea(opts[1]).then((res) => { |
| | | if (res.success) { |
| | | tableData2.value = res.data; |
| | | } |
| | | }); |
| | | Promise.all([p1, p2]) |
| | | .then(() => { |
| | | changeActive(); |
| | | setTimeout(() => { |
| | | genChart(opts[0], opts[1]); |
| | | }, 500); |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | |
| | | function onStep2() { |
| | | changeActive(); |
| | | } |
| | | |
| | | function onStep3(val) {} |
| | | function onStep3(val) { |
| | | if (val.downloadType == '1') { |
| | | loading.value = true; |
| | | conversionFromTable('prod-problem-count-table', '扬尘污染问题数均值对比'); |
| | | downloadChartImage(chart, '扬尘污染问题数均值对比'); |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | function genChart(opt1, opt2) { |
| | | if (chart == undefined) { |
| | | chart = echarts.init(chartRef.value); |
| | | } |
| | | const year = dayjs(opt1.startTime).year(); |
| | | const month1 = dayjs(opt1.startTime).month() + 1; |
| | | const month2 = dayjs(opt2.startTime).month() + 1; |
| | | const time = `${year}年${month1}月、${month2}月`; |
| | | const option = barChartOption(); |
| | | option.title.text = `${time}各街道(镇)${opt1.sceneTypeName}扬尘污染问题数均值对比`; |
| | | |
| | | option.xAxis.name = '街道(镇)'; |
| | | option.xAxis.data = tableData.value.map((item) => item.townName); |
| | | option.yAxis.name = '问题数均值'; |
| | | |
| | | option.series = [ |
| | | { |
| | | name: `${month1}月`, |
| | | type: 'bar', // 图表类型改为柱状图 |
| | | data: tableData1.value.map((item) => item.ratio), |
| | | label: { |
| | | show: true, |
| | | position: 'top', // 标签显示在柱子顶部 |
| | | formatter: '{c}' // 标签格式:数量 |
| | | } |
| | | }, |
| | | { |
| | | name: `${month2}月`, |
| | | type: 'bar', // 图表类型改为柱状图 |
| | | data: tableData1.value.map((item) => item.ratio), |
| | | label: { |
| | | show: true, |
| | | position: 'top', // 标签显示在柱子顶部 |
| | | formatter: '{c}' // 标签格式:数量 |
| | | } |
| | | } |
| | | ]; |
| | | chart.setOption(option); |
| | | } |
| | | |
| | | function ratioFormat(row, column, cellValue, index) { |
| | | return Math.round(cellValue * 10) / 10; |
| | | } |
| | | </script> |
| | |
| | | function onStep3(val) { |
| | | if (val.downloadType == '1') { |
| | | loading.value = true; |
| | | // conversionFromTable('prod-problem-type-table', '扬尘污染问题类型占比清单'); |
| | | conversionFromTable('prod-problem-type-table', '扬尘污染问题类型占比清单'); |
| | | downloadChartImage(chart, '扬尘污染问题类型占比'); |
| | | loading.value = false; |
| | | } |