投入品监管

This commit is contained in:
沈鸿 2025-05-19 11:55:58 +08:00
parent 313705c4c0
commit 49cc00695f
18 changed files with 976 additions and 1487 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -259,6 +259,7 @@ export default {
},
},
legend: {
type: 'scroll',
show: true,
right: '5%',
top: '25%',

View File

@ -4,11 +4,15 @@
<script setup>
import { ref, watch, onMounted, nextTick } from 'vue';
import { cloneDeep } from 'lodash';
import { merge, cloneDeep } from 'lodash';
import { useEcharts } from '@/hooks/useEcharts';
defineOptions({ name: 'NewHyalineCake' });
//
let selectedIndex = null;
let hoveredIndex = null;
// props
const props = defineProps({
chartData: {
@ -23,18 +27,13 @@ const props = defineProps({
option: {
type: Object,
default: () => ({
// 1
k: 1,
//
itemGap: 0.2,
// z
itemHeight: 120,
// 使>0 itemHeight 使 autoItemHeight * value
autoItemHeight: 0,
//
opacity: 0.6,
//
legendSuffix: '',
k: 1, // 1
itemGap: 0.1, // ,
itemHeight: 120, // z
autoItemHeight: 0, // 使>0 itemHeight 使 autoItemHeight * value
opacity: 0.6, //
legendSuffix: '', //
// TODO series
}),
},
width: {
@ -58,8 +57,19 @@ const { setOptions, getInstance } = useEcharts(chartRef);
// ECharts
const chartOption = ref({});
// series-surface.parametricEquation
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
/**
* 获取 parametric 曲面方程
* @param {Number} startRatio 起点弧度
* @param {Number} endRatio 终点弧度
* @param {Boolean} isSelected 是否选中
* @param {Boolean} isHovered 是否高亮
* @param {Number} k 饼图内径/外径的占比
* @param {Number} h 饼图高度
* @param {Array} offsetZ 浮动系数
* @return {Object} parametric 曲面方程
*/
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h, offsetZ = 0) {
console.log('getParametricEquation params :>> ', startRatio, endRatio, isSelected, isHovered, k, h, offsetZ);
//
const midRatio = (startRatio + endRatio) / 2;
const startRadian = startRatio * Math.PI * 2;
@ -70,7 +80,7 @@ function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h
if (startRatio === 0 && endRatio === 1) {
isSelected = false;
}
// k 1/3 k 使
// k 1/3/ k 使
k = typeof k !== 'undefined' ? k : 1 / 3;
//
@ -117,167 +127,97 @@ function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h
z(u, v) {
// u < -π/2 使
if (u < -Math.PI * 0.5) {
return Math.sin(u);
return Math.sin(u) + offsetZ * h * 0.1;
}
// u > 2.5π
if (u > Math.PI * 2.5) {
return Math.sin(u) * h * 0.1;
return Math.sin(u) * h * 0.1 + offsetZ * h * 0.1;
}
// z v
// Zhvalue
return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
return Math.sin(v) > 0 ? 1 * h * 0.1 + offsetZ * h * 0.1 : -1 + offsetZ * h * 0.1;
},
};
}
// 3D
function getPie3D(pieData) {
/**
* 获取 3D 饼图的配置项
* @param {Array} pieData 饼图数据
* @param {Number} internalDiameterRatio 饼图内径/外径的占比
* @return {Object} 配置项
*/
function getPie3D(pieData, internalDiameterRatio) {
const series = [];
let sumValue = 0;
//
pieData.forEach((item) => {
sumValue += item.value;
});
let startValue = 0;
let endValue = 0;
const legendData = [];
// k
const k = props.option.k ?? 1;
const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
//
// pieData.forEach((item) => {
// sumValue += item.value;
// });
// series
pieData.forEach((dataItem, idx) => {
for (let i = 0; i < pieData.length; i += 1) {
sumValue += pieData[i].value;
const seriesItem = {
name: dataItem.name ?? `series${idx}`,
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
type: 'surface',
parametric: true,
wireframe: { show: false },
pieData: dataItem,
itemStyle: {
opacity: props.option.opacity,
borderRadius: 300,
borderColor: '#fff',
borderWidth: 0,
},
pieData: pieData[i],
// itemStyle: {
// opacity: props.option.opacity,
// borderRadius: 300,
// borderColor: '#fff',
// }
pieStatus: {
selected: false,
selected: true,
hovered: false,
k,
},
};
//
if (dataItem.itemStyle) {
const customStyle = {};
if (dataItem.itemStyle.color !== undefined) {
customStyle.color = dataItem.itemStyle.color;
}
if (dataItem.itemStyle.opacity !== undefined) {
customStyle.opacity = dataItem.itemStyle.opacity;
}
seriesItem.itemStyle = { ...seriesItem.itemStyle, ...customStyle };
if (typeof pieData[i].itemStyle !== 'undefined') {
const { itemStyle } = pieData[i];
typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
typeof pieData[i].itemStyle.opacity !== 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
seriesItem.itemStyle = itemStyle;
}
series.push(seriesItem);
});
// startRatio/endRatio
let startValue = 0;
series.forEach((serie) => {
const endValue = startValue + serie.pieData.value;
const startRatio = startValue / sumValue;
const endRatio = endValue / sumValue;
serie.pieData.startRatio = startRatio;
serie.pieData.endRatio = endRatio;
//
serie.parametricEquation = getParametricEquation(
startRatio,
endRatio,
false,
true,
k,
// 使
props.option.autoItemHeight > 0 ? props.option.autoItemHeight * serie.pieData.value : props.option.itemHeight
);
startValue = endValue;
});
// hover
series.push({
name: 'mouseoutSeries',
type: 'surface',
parametric: true,
wireframe: { show: false },
itemStyle: { opacity: 0 },
parametricEquation: {
u: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
v: { min: 0, max: Math.PI, step: Math.PI / 20 },
x(u, v) {
return Math.sin(v) * Math.sin(u) + Math.sin(u);
},
y(u, v) {
return Math.sin(v) * Math.cos(u) + Math.cos(u);
},
z(u, v) {
return Math.cos(v) > 0 ? 0.1 : -0.1;
},
},
});
//
const option = Object.assign(
{
tooltip: {
backgroundColor: 'rgba(18, 55, 85, 0.8)',
borderColor: '#35d0c0',
color: '#fff',
position: (point, params, dom, rect, size) => {
// tooltip
let x = point[0],
y = point[1];
const [viewW, viewH] = size.viewSize;
const [boxW, boxH] = size.contentSize;
if (x + boxW > viewW) x -= boxW;
if (y + boxH > viewH) y -= boxH;
if (x < 0) x = 0;
if (y < 0) y = 0;
return [x, y];
},
formatter: (params) => {
//
if (params.seriesName !== 'mouseoutSeries') {
return `
<span style="color:#FFF">
${params.seriesName}<br/>
<span style="
display:inline-block;
margin-right:5px;
border-radius:10px;
width:10px;
height:10px;
background-color:${params.color};"></span>
${chartOption.value.series[params.seriesIndex].pieData.value}
</span>`;
}
return '';
},
},
xAxis3D: { min: -1, max: 1 },
yAxis3D: { min: -1, max: 1 },
zAxis3D: { min: -1, max: 1 },
grid3D: {
show: false,
boxHeight: 5,
top: '0',
left: '-20%',
viewControl: {
// 3D
alpha: 60, //
distance: 240, //
rotateSensitivity: 10,
zoomSensitivity: 10,
panSensitivity: 10,
autoRotate: true,
autoRotateAfterStill: 2,
},
},
// 使 sumValue getParametricEquation
// series-surface series-surface.parametricEquation
console.log(series);
for (let i = 0; i < series.length; i += 1) {
endValue = startValue + series[i].pieData.value;
const z = series[i]?.pieData?.floatZ ?? 0;
series[i].pieData.startRatio = startValue / sumValue;
series[i].pieData.endRatio = endValue / sumValue;
series[i].parametricEquation = getParametricEquation(
series[i].pieData.startRatio,
series[i].pieData.endRatio,
series[i].pieStatus.selected,
series[i].pieStatus.hovered,
k,
props.option.autoItemHeight > 0 ? props.option.autoItemHeight * series[i].pieData.value : props.option.itemHeight,
z
);
startValue = endValue;
legendData.push(series[i].name);
}
//
// legendDataseries
const option = {
legend: {
type: 'scroll',
show: true,
selectedMode: false,
right: '5%',
top: '25%',
orient: 'vertical',
@ -290,27 +230,138 @@ function getPie3D(pieData) {
fontSize: 14,
fontWeight: '400',
},
//
formatter: (name) => {
const item = props.chartData.find((d) => d.name === name);
return item ? ` ${name} ${item.value}${props.option.legendSuffix || ''}` : name;
if (props.chartData.length) {
const item = props.chartData.filter((item) => item.name === name)[0];
return ` ${name} ${item.value}${props.option.legendSuffix ?? ''}`;
}
},
},
// animation: false,
tooltip: {
formatter: (params) => {
if (params.seriesName !== 'mouseoutSeries') {
return `${
params.seriesName
}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
params.color
};"></span>${option.series[params.seriesIndex].pieData.value}`;
}
return '';
},
},
xAxis3D: {
min: -1,
max: 1,
},
yAxis3D: {
min: -1,
max: 1,
},
zAxis3D: {
min: -1,
max: 1,
},
grid3D: {
show: true,
boxHeight: 5,
top: '-20%',
viewControl: {
// 3d
alpha: 35,
// beta: 30,
rotateSensitivity: 1,
zoomSensitivity: 0,
panSensitivity: 0,
autoRotate: true,
distance: 150,
},
// SSAO
postEffect: {
// 齿
enable: false,
bloom: {
enable: true,
bloomIntensity: 0.1,
},
SSAO: {
enable: true,
quality: 'medium',
radius: 2,
},
// temporalSuperSampling: {
// enable: true,
// },
},
},
series,
},
//
props.option
);
};
return option;
}
// mouseover
function handleMouseover(params) {
console.log('mouseover');
const idx = params.seriesIndex;
const series = chartOption.value.series;
//
if (hoveredIndex === idx) return;
// 1.
if (hoveredIndex !== null && hoveredIndex >= 0 && series[hoveredIndex]) {
updateSeriesHover(hoveredIndex, false);
}
// 2.
if (params.seriesName !== 'mouseoutSeries' && series[idx]) {
updateSeriesHover(idx, true);
hoveredIndex = idx;
} else {
hoveredIndex = null;
}
// 3.
setOptions(chartOption.value);
}
function handleGlobalout() {
const idx = hoveredIndex;
if (idx !== null && idx >= 0 && chartOption.value.series[idx]) {
updateSeriesHover(idx, false);
hoveredIndex = null;
setOptions(chartOption.value);
}
}
//
function updateSeriesHover(index, toHover) {
const item = chartOption.value.series[index];
// pieStatusfallback
const status = item.pieStatus || {};
const isSelected = !!status.selected;
const start = item.pieData?.startRatio ?? 0;
const end = item.pieData?.endRatio ?? 1;
const k = typeof status.k === 'number' ? status.k : 1 / 3;
// newHeight hover 5 height
const baseHeight = props.option.autoItemHeight > 0 ? props.option.autoItemHeight : props.option.itemHeight;
const newHeight = toHover ? baseHeight + 5 : baseHeight;
// parametricEquation
item.parametricEquation = getParametricEquation(start, end, isSelected, toHover, k, newHeight);
//
item.pieStatus = {
...status,
hovered: toHover,
};
}
//
function initChart() {
// 3D
const baseOption = getPie3D(props.chartData);
// 3D()
const baseOption = getPie3D(props.chartData, props.option.k);
//
const finalOption = Object.assign({}, baseOption, cloneDeep(props.option || {}));
// const finalOption = Object.assign({}, baseOption, cloneDeep(props.option || {}));
const finalOption = merge({}, baseOption, cloneDeep(props.option || {}));
chartOption.value = finalOption;
//
setOptions(chartOption.value);
@ -323,11 +374,11 @@ function initChart() {
}
//
chart.off('click');
// chart.off('click');
chart.off('mouseover');
chart.off('globalout');
chart.on('click', handleClick);
// chart.on('click', handleClick);
chart.on('mouseover', handleMouseover);
chart.on('globalout', handleGlobalout);
});
@ -380,95 +431,6 @@ window.debugZValues = {
current: null,
history: [],
};
function handleMouseover(params) {
if (params.seriesName === 'mouseoutSeries') return;
const chart = getInstance();
const optionVal = chart.getOption(); //
const series = optionVal.series;
const idx = params.seriesIndex;
const hh = series[idx].parametricEquation.z();
window.debugZValues.current = hh;
window.debugZValues.history.push({
event: 'mouseover',
series: series[idx].name,
zValue: hh,
time: new Date().toISOString(),
});
console.log('当前Z值:', hh, '历史记录:', window.debugZValues.history);
//
console.log(
'[移入] 当前所有扇形状态',
series.map((s) => ({
name: s.name,
hovered: s.pieStatus?.hovered,
selected: s.pieStatus?.selected,
height: s.parametricEquation?.z(Math.PI, Math.PI),
}))
);
//
if (hoveredIndex !== null && hoveredIndex !== idx) {
const prev = series[hoveredIndex];
prev.pieStatus.hovered = false; //
prev.parametricEquation = getParametricEquation(
//
prev.pieData.startRatio,
prev.pieData.endRatio,
prev.pieStatus.selected,
false, // isHovered=false
prev.pieStatus.k,
prev.pieData.value
);
}
//
const current = series[idx];
current.pieStatus.hovered = true;
current.parametricEquation = getParametricEquation(
current.pieData.startRatio,
current.pieData.endRatio,
current.pieStatus.selected,
true, // isHovered=true
current.pieStatus.k,
current.pieData.value
);
hoveredIndex = idx;
chart.setOption({ series }); // series
}
function handleGlobalout() {
if (hoveredIndex !== null) {
const chart = getInstance();
const optionVal = chart.getOption();
const series = optionVal.series;
const prev = series[hoveredIndex];
//
console.warn('[修复前] 异常状态', {
name: prev.name,
z: prev.parametricEquation.z(Math.PI, Math.PI),
equation: prev.parametricEquation,
});
//
prev.pieStatus.hovered = false;
prev.parametricEquation = getParametricEquation(
prev.pieData.startRatio,
prev.pieData.endRatio,
prev.pieStatus.selected,
false, // isHovered=false
prev.pieStatus.k,
prev.pieData.value
);
hoveredIndex = null;
chart.setOption({ series }, { replaceMerge: 'series' }); // series
}
}
//
let selectedIndex = null;
let hoveredIndex = null;
//
onMounted(() => {

View File

@ -108,35 +108,31 @@ export default {
}
let typeArr = Array.from(new Set(props.chartData.map((item) => item.type)));
let xAxisData = Array.from(new Set(props.chartData.map((item) => item.name)));
let seriesData = [];
typeArr.forEach((type, index) => {
let obj = {
name: type,
type: props.type,
smooth: true,
// 1. 使series
const externalSeries = props.option?.series || [];
option.series = typeArr.map((type, index) => {
// 2. type
const externalConfig = externalSeries.find((s) => s.name === type) || {};
// 3.
const data = xAxisData.map((x) => {
const item = props.chartData.find((item) => item.type === type && item.name === x);
return item ? item.value : null;
});
// 4.
return {
type: props.type, //
name: type, //
data, //
smooth: true, //
...externalConfig, //
// areaStyle
areaStyle: externalConfig.areaStyle || (props.option?.color ? setAreaStyle(props.option.color[index]) : undefined),
};
if (props.option?.color) {
obj.areaStyle = setAreaStyle(props.option?.color[index]);
}
const findItem = props.chartData.find((item) => item.type == type);
if (findItem && findItem.color) {
obj.color = findItem.color;
obj.areaStyle = setAreaStyle(findItem.color[index]);
}
let data = [];
xAxisData.forEach((x) => {
let dataArr = props.chartData.filter((item) => type === item.type && item.name == x);
if (dataArr && dataArr.length > 0) {
data.push(dataArr[0].value);
} else {
data.push(null);
}
});
obj['data'] = data;
seriesData.push(obj);
});
option.series = seriesData;
option.xAxis.data = xAxisData;
// console.log(option.series);
setOptions(option);
startAutoPlay({
interval: 2000,

View File

@ -1,116 +1,235 @@
<template>
<custom-echart-line :chart-data="state.data" height="100%" :option="state.option" />
<custom-echart-line :chart-data="chartData" :option="chartOption" height="100%" />
</template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty, sleep } from '@/utils';
import { ref } from 'vue';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
query: {
type: String,
default: '',
},
});
// 1. 12
const chartData = ref(
(() => {
//
const waveConfig = {
// []: (1.0)
peaks: { 3: 1.6, 9: 1.5 }, // 410 (0)
valleys: { 0: 0.7, 6: 0.8, 11: 0.6 }, // 1712
};
const state = reactive({
option: {
color: ['#35D0C0'],
grid: {
left: '5%',
right: '5%',
bottom: '5%',
top: '10%',
containLabel: true,
//
const generateWaveData = (baseVal) => {
return Array.from({ length: 12 }, (_, i) => {
if (waveConfig.peaks[i]) return baseVal * waveConfig.peaks[i];
if (waveConfig.valleys[i]) return baseVal * waveConfig.valleys[i];
//
return baseVal * (0.9 + Math.random() * 0.2);
}).map((v) => parseFloat(v.toFixed(1)));
};
//
return [
{ type: '种子种苗', values: generateWaveData(15) },
{ type: '化肥', values: generateWaveData(20) },
{ type: '农药', values: generateWaveData(10) },
].flatMap(({ type, values }) =>
values.map((value, i) => ({
type,
name: `${i + 1}`,
value,
}))
);
})()
);
function hexToRGBA(hex, alpha = 1) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
// 2.
const chartOption = ref({
color: ['#02FD94', '#FEF906', '#01FEFD'],
legend: {
data: ['种子种苗', '化肥', '农药'],
top: 8,
itemWidth: 12, //
itemHeight: 12, //
icon: 'rect',
itemGap: 20,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(18, 55, 85, 0.8);',
backgroundColor: 'rgba(0,0,0,0.5);',
borderColor: '#35d0c0',
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips">
<span>${params.name}</span><br/>
<span>${params.marker} ${params.data} 万元</span>
</div>`;
return str;
},
},
xAxis: {
type: 'category',
// name: '',
axisTick: {
show: false,
alignWithLabel: false,
interval: 'auto',
inside: false,
length: 5,
lineStyle: {
type: 'solid',
width: 1,
color: 'rgba(28, 158, 222, 1)',
},
borderRadius: 8,
formatter: (params) => `
<div style="font-weight:bold;margin-bottom:5px;color:#fff">${params[0].name}</div>
${params
.map(
(p) => `
<div style="display:flex;align-items:center;margin:3px 0;color:#fff">
<span style="display:inline-block;width:8px;height:8px;background:${p.color};margin-right:6px;color:#fff"></span>
${p.seriesName}: <span style="font-weight:bold;margin-left:5px;color:#fff">${p.value} </span>
</div>
`
)
.join('')}
`,
extraCssText: 'backdrop-filter: blur(8px);',
},
grid: {
left: '3%',
right: '5%',
bottom: '5%',
top: '20%',
containLabel: true,
},
yAxis: {
type: 'value',
// name: '',
},
xAxis: {
type: 'category',
boundaryGap: false,
axisLabel: {
interval: 0,
margin: 15,
align: 'center',
},
axisTick: {
show: false, // 线
},
},
data: [],
series: [
{
name: '种子种苗',
type: 'line',
smooth: true,
symbol: 'none',
lineStyle: {
width: 2,
color: {
type: 'linear',
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{ offset: 0, color: hexToRGBA('#02FD94', 0) },
{ offset: 0.2, color: hexToRGBA('#02FD94', 1) },
{ offset: 0.8, color: hexToRGBA('#02FD94', 1) },
{ offset: 1, color: hexToRGBA('#02FD94', 0) },
],
},
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: hexToRGBA('#02FD94', 1) }, //
{ offset: 1, color: hexToRGBA('#02FD94', 0) }, //
],
},
},
emphasis: {
focus: 'series',
},
},
{
name: '化肥',
type: 'line',
smooth: true,
symbol: 'none', //
lineStyle: {
width: 2,
color: {
// 线
type: 'linear',
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{ offset: 0, color: hexToRGBA('#E0A21C', 0) },
{ offset: 0.2, color: hexToRGBA('#E0A21C', 1) },
{ offset: 0.8, color: hexToRGBA('#E0A21C', 1) },
{ offset: 1, color: hexToRGBA('#E0A21C', 0) },
],
},
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: hexToRGBA('#FEF906', 1) },
{ offset: 1, color: hexToRGBA('#FEF906', 0) },
],
},
},
emphasis: {
focus: 'series',
},
},
{
name: '农药',
type: 'line',
smooth: true,
symbol: 'none', //
lineStyle: {
width: 2,
color: {
// 线
type: 'linear',
x: 0,
x2: 1,
y: 0,
y2: 0,
colorStops: [
{ offset: 0, color: hexToRGBA('#01FEFD', 0) },
{ offset: 0.2, color: hexToRGBA('#01FEFD', 1) },
{ offset: 0.8, color: hexToRGBA('#01FEFD', 1) },
{ offset: 1, color: hexToRGBA('#01FEFD', 0) },
],
},
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: hexToRGBA('#01FEFD', 1) },
{ offset: 1, color: hexToRGBA('#01FEFD', 0) },
],
},
},
emphasis: {
focus: 'series',
},
},
// ...
],
});
const loadData = async (code = '') => {
state.loading = true;
// GetInputsInfo()
// .then((res) => {
// if (res.code === 200) {
// state.data = res.data;
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// });
await sleep(500);
state.data = [
{ value: 5, name: '2020' },
{ value: 36, name: '2021' },
{ value: 70, name: '2022' },
{ value: 56, name: '2023' },
{ value: 70, name: '2024' },
{ value: 20, name: '2025' },
];
};
watch(
() => props.data,
(val) => {
if (!isEmpty(val)) {
state.data = val;
}
},
{
deep: true,
immediate: true,
}
);
watch(
() => props.query,
(val) => {
if (!isEmpty(val)) {
loadData(val);
}
},
{
deep: true,
immediate: true,
}
);
</script>
<style scoped>
/* 调整倾斜月份标签的间距 */
:deep(.echarts-axis-x .echarts-axis-tick) {
margin-top: 10px;
}
</style>

View File

@ -1,272 +1,84 @@
<template>
<custom-echart-pie-3d :chart-data="state.data" height="100%" :option="state.option" @click="handleClick" />
<div class="order-stats">
<el-row :gutter="20">
<el-col v-for="item in state.list" :key="item.label" :span="12">
<div class="order-stats-item flex-column">
<div class="order-stats-value">
{{ item.value }}<span>{{ item.unit }}</span>
</div>
<div class="order-stats-label">{{ item.label }}</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
});
<script setup>
import { reactive } from 'vue';
const state = reactive({
option: {},
data: [],
text: '',
});
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
//
let midRatio = (startRatio + endRatio) / 2;
let startRadian = startRatio * Math.PI * 2;
let endRadian = endRatio * Math.PI * 2;
let midRadian = midRatio * Math.PI * 2;
//
if (startRatio === 0 && endRatio === 1) {
isSelected = false;
}
// / k 1/3
k = typeof k !== 'undefined' ? k : 1 / 3;
// x y 0
let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
// 1
let hoverRate = isHovered ? 1.05 : 1;
//
return {
u: {
min: -Math.PI,
max: Math.PI * 3,
step: Math.PI / 32,
},
v: {
min: 0,
max: Math.PI * 2,
step: Math.PI / 20,
},
x: function (u, v) {
if (u < startRadian) {
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
if (u > endRadian) {
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
},
y: function (u, v) {
if (u < startRadian) {
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
if (u > endRadian) {
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
}
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
},
z: function (u, v) {
if (u < -Math.PI * 0.5) {
return Math.sin(u);
}
if (u > Math.PI * 2.5) {
return Math.sin(u) * h * 0.1;
}
return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
},
};
}
function fomatFloat(num, n) {
var f = parseFloat(num);
if (isNaN(f)) {
return false;
}
f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n
var s = f.toString();
var rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + n) {
s += '0';
}
return s;
}
function getHeight3D(series, height) {
series.sort((a, b) => {
return b.pieData.value - a.pieData.value;
});
return (height * 20) / series[0].pieData.value;
}
function getPie3D(pieData, internalDiameterRatio) {
let series = [];
let sumValue = 0;
let startValue = 0;
let endValue = 0;
let legendData = [];
let legendBfb = [];
let k = 1 - internalDiameterRatio;
pieData.sort((a, b) => {
return b.value - a.value;
});
for (let i = 0; i < pieData.length; i++) {
sumValue += pieData[i].value;
let seriesItem = {
//
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
type: 'surface',
//
parametric: true,
//线
wireframe: {
show: false,
},
pieData: pieData[i],
pieStatus: {
selected: false,
hovered: false,
k: k,
},
//()
// center: ['50%', '100%']
};
//
if (typeof pieData[i].itemStyle != 'undefined') {
let itemStyle = {};
typeof pieData[i].itemStyle.color != 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
typeof pieData[i].itemStyle.opacity != 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
seriesItem.itemStyle = itemStyle;
}
series.push(seriesItem);
}
legendData = [];
legendBfb = [];
for (let i = 0; i < series.length; i++) {
endValue = startValue + series[i].pieData.value * 1;
series[i].pieData.startRatio = startValue / sumValue;
series[i].pieData.endRatio = endValue / sumValue;
series[i].parametricEquation = getParametricEquation(
series[i].pieData.startRatio,
series[i].pieData.endRatio,
false,
false,
k,
series[i].pieData.value
);
startValue = endValue;
let bfb = fomatFloat(series[i].pieData.value / sumValue, 4);
legendData.push({
name: series[i].name,
value: bfb,
});
legendBfb.push({
name: series[i].name,
value: bfb,
});
}
let boxHeight = getHeight3D(series, 15);
let option = {
legend: {
data: legendData,
color: ['#8FD7FC', '#466BE7', '#F4BB29', '#49C384', '#8FD7FC', '#466BE7', '#F4BB29', '#49C384'],
bottom: 10,
itemGap: 20,
show: true,
icon: 'rect',
itemHeight: 10,
itemWidth: 10,
textStyle: {
fontSize: 12,
color: '#B8DDFF',
lineHeight: 20,
},
},
title: {
text: '',
x: 'center',
y: 'center',
textStyle: {
rich: {
a: {
fontSize: 20,
color: '#fff',
},
c: {
fontSize: 12,
color: '#fff',
padding: [15, 0],
},
},
},
},
xAxis3D: {
min: -1,
max: 1,
},
yAxis3D: {
min: -1,
max: 1,
},
zAxis3D: {
min: -1,
max: 1,
},
grid3D: {
show: false,
boxHeight: boxHeight,
left: 0,
top: -30,
viewControl: {
alpha: 55, //( )
distance: 150, //zoom()
rotateSensitivity: 1, //0
zoomSensitivity: 0, //0
panSensitivity: 0, //0
autoRotate: true, //
},
},
series: series,
};
return option;
}
const initData = (pieData = []) => {
const option = getPie3D(pieData, 0.8);
const { name, value } = option.series[0].pieData;
option.title.text = `{a|${value}%}{c|\n${name}}`;
state.option = option;
state.data = option.series;
};
const handleClick = (params) => {
const findItem = state.data.find((el) => el.name === params.seriesName);
state.option.title.text = `{a|${findItem?.pieData?.value}%}{c|\n${params.seriesName}}`;
};
watch(
() => props.data,
(val) => {
if (!isEmpty(val)) {
const pieData = val.map((row) => {
return {
name: row.category,
value: row.percentage,
};
});
initData(pieData);
}
list: [
{
label: '订单金额',
value: '548.86',
unit: '万元',
},
{
deep: true,
immediate: true,
}
);
label: '订单数量',
value: '110554',
unit: '笔',
},
],
});
</script>
<style lang="scss" scoped>
.order-stats {
padding: 20px;
&-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
&-value {
width: 160px;
height: 160px;
line-height: 100px;
text-align: center;
margin: 0 auto;
background-image: url('@/assets/images/inputs/order.webp');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
font-size: 24px;
font-weight: 400;
color: #fff;
position: relative;
border-radius: 8px;
overflow: hidden;
span {
font-size: 16px;
}
}
&-label {
width: 120px;
height: 24px;
line-height: 24px;
margin: 10px auto 0;
font-size: 16px;
color: #fff;
text-align: center;
background: url('@/assets/images/inputs/bg_label.png') center center no-repeat;
text-shadow:
0 0 10px #01eeff,
0 0 20px #01eeff,
0 0 30px #01eeff;
}
}
</style>

View File

@ -2,16 +2,22 @@
<centerMap :dialog-title="'投入品'" @mapclick="doMapclick">
<template #header>
<div class="land-map-pop-header">
<div class="title">{{ currentRegion && currentRegion.name ? currentRegion.name : '投入品' }}</div>
<div class="title">{{ currentRegion?.name || '投入品' }}</div>
<a class="view-case" @click.prevent="handleViewCase">查看案件 &gt;</a>
</div>
</template>
<template #dialogContent>
<div class="land-map-pop-content">
<div v-for="(n, index) in list" :key="index" class="list-item">
<div class="title">
<span class="title-val"> {{ n.title }}</span>
<div class="section-title">投入品管控情况</div>
<div class="main-content">
<div class="item">
<div class="label">巡检次数</div>
<div class="value green">562</div>
</div>
<div class="item">
<div class="label">案件次数</div>
<div class="value red">22</div>
</div>
<div class="value">{{ n.value }}{{ n.unit }}</div>
</div>
</div>
</template>
@ -19,85 +25,75 @@
</template>
<script setup>
import { ref, reactive } from 'vue';
const unit = ref('家');
const list = reactive([
{ title: '监管机构', value: '1', color: '#01FEFD', unit: '家' },
{ title: '监管人员', value: '2', color: '#FEF906', unit: '人' },
{ title: '村级监管员', value: '5', color: '#02FD94', unit: '人' },
{ title: '农资经营单位', value: '42', color: '#FE7F03', unit: '家' },
{ title: '生产主体', value: '2', color: '#41BDFC', unit: '家' },
{ title: '投入品规模', value: '3000', color: '#FC0003', unit: '万元' },
]);
import { ref } from 'vue';
let currentRegion = ref(null);
const doMapclick = (data) => {
currentRegion.value = data;
list.forEach((v) => {
v.value = Number(v.value) + 1 * (Math.floor(Math.random() * (6 - 2 + 1)) + 2.12).toFixed(0);
});
};
const handleViewCase = () => {
//
};
</script>
<style lang="scss" scoped>
.land-map-pop-header {
display: inline-flex;
display: flex;
justify-content: space-between;
width: 100%;
align-items: center;
padding: 0 6px;
margin-top: 3px;
.title,
.value {
display: inline-block;
vertical-align: middle;
color: $color-white;
}
.title {
font-size: 18px;
font-weight: bold;
color: #00f9ff;
}
.value {
font-size: 14px;
}
}
.land-map-pop-content {
width: 100%;
gap: 10px;
display: inline-flex;
justify-content: flex-start;
flex-wrap: wrap;
.list-item {
width: calc((100% - 10px) / 2);
display: inline-flex;
justify-content: space-between;
padding: 6px 0;
.title {
display: inline-flex;
justify-content: flex-start;
.before {
display: inline-flex;
flex-direction: column;
justify-content: center;
}
.b-content {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.before,
.title-val {
display: inline-block;
vertical-align: middle;
padding: 0 5px 0 2px;
color: $color-custom-main;
.view-case {
font-size: 16px;
color: #00ff99;
text-decoration: none;
cursor: pointer;
}
}
.land-map-pop-content {
width: 100%;
padding: 4px 10px;
color: #ffffff;
.section-title {
margin-bottom: 12px;
font-size: 15px;
text-align: left;
}
.main-content {
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
.item {
display: flex;
justify-content: space-between;
font-size: 16px;
.label {
color: #ffffff;
}
.value {
display: inline-block;
text-align: right;
color: $color-white;
font-size: 12px;
&.green {
color: #01ffb1;
}
&.red {
color: #ff4242;
}
}
}
}
}

View File

@ -1,74 +1,79 @@
<template>
<el-row :gutter="20">
<el-col v-for="(item, index) in state.list" :key="item.label" :span="12">
<div class="inputs-item flex-row">
<span class="inputs-item-icon">
<img :src="getAssetsFile(`images/inputs/${index + 1}.png`)" />
</span>
<div class="inputs-item-info flex-column">
<b>{{ item.label }}</b>
<div class="inputs-item-value flex-row">
<em>{{ item.value }}</em>
<span>{{ item.unit }}</span>
<div class="inputs">
<h2 class="inputs-title">全县投入品数量291.85万吨</h2>
<new-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" />
</div>
</div>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { reactive, watch } from 'vue';
import { getAssetsFile } from '@/utils';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
});
const state = reactive({
list: [],
});
watch(
() => props.data,
(val) => {
if (!isEmpty(val)) {
state.list = val;
}
option: {
k: 0,
opacity: 0.8,
itemGap: 0.1,
// legendSuffix: '',
itemHeight: 200,
startAngle: 60,
grid3D: {
show: false,
boxHeight: 2,
top: '-20',
bottom: '10',
// left: '-20%',
viewControl: {
//3d
alpha: 40, //( )
beta: -40,
distance: 260, //zoom()
rotateSensitivity: 0, //0
zoomSensitivity: 0, //0
panSensitivity: 0, //0
autoRotate: false, //
autoRotateAfterStill: 2, //, autoRotate
},
{
deep: true,
immediate: true,
}
);
},
legend: {
show: true,
top: 'bottom',
orient: 'horizontal',
itemWidth: 12, //
itemHeight: 12, //
itemGap: 20,
icon: 'rect',
left: 'center',
textStyle: {
color: '#fff', //
fontSize: 14,
},
itemStyle: {
display: 'float',
top: '120px',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
},
formatter: (name) => name,
},
},
data: [
{ value: 76, name: '产业运营平台' },
{ value: 24, name: '其它', floatZ: 1 },
],
});
</script>
<style lang="scss" scoped>
.inputs {
&-item {
align-items: center;
&-title {
width: 270px;
height: 40px;
line-height: 40px;
margin: 24px auto 0;
background-image: url('@/assets/images/inputs/bg_title.png');
background-position: center bottom;
background-repeat: no-repeat;
background-size: 100%;
font-size: 20px;
font-weight: 400;
color: #fff;
padding: 10px 0;
&-icon {
margin-right: 16px;
}
&-info {
font-size: 16px;
font-weight: bold;
}
&-value {
margin-top: 10px;
justify-content: space-between;
em {
color: #02fd94;
font-style: normal;
}
}
text-align: center;
}
}
</style>

View File

@ -1,93 +1,194 @@
<template>
<div class="board">
<custom-scroll-board v-if="state.loading" :chart-config="state.options" />
<div height="100%">
<custom-scroll-board :chart-config="chartConfig" />
<div class="announcement-bar">
<!-- 喇叭图标 -->
<div class="horn-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 10V14C3 15.1 3.9 16 5 16H6L9 19V5L6 8H5C3.9 8 3 8.9 3 10Z" fill="#34f9b7" />
<path d="M16.5 12C16.5 10.23 15.48 8.71 14 7.97V16.02C15.48 15.29 16.5 13.77 16.5 12Z" fill="#34f9b7" />
<path
d="M14 3.23V5.29C16.89 6.15 19 8.83 19 12C19 15.17 16.89 17.85 14 18.71V20.77C18.01 19.86 21 16.28 21 12C21 7.72 18.01 4.14 14 3.23Z"
fill="#34f9b7"
/>
</svg>
</div>
<!-- 滚动内容容器 -->
<div class="scroll-container" ref="scrollContainer">
<div class="scroll-content" :style="{ transform: `translateX(${scrollPosition}px)` }">
<span class="text-content">
距今产业运营平台已为全县投入品花费节省约
<span class="highlight">¥1256.8 万元</span>
截至{{ currentDate }}
</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
});
import { ref, onMounted, onUnmounted } from 'vue';
const header = ['白名单企业', '产品名称', '黑名单企业', '产品名称'];
const len = header.length;
const state = reactive({
loading: false,
options: {
attr: { w: 200, h: 240 },
//
const chartConfig = ref({
attr: {
w: 200, //
h: 192, //
},
option: {
header,
header: ['投入品种类', '平台价格', '市场价格'],
dataset: [
// ['', '824', '', '824'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
['圆茄种苗', '0.3元/棵', '0.4元/棵'],
['高氮复合肥', '1850元/吨', '1980元/吨'],
['硫酸钾', '1250元/吨', '1380元/吨'],
['西红柿种苗', '0.3元/棵', '0.4元/棵'],
],
index: false,
columnWidth: [100, 100, 100, 100],
align: new Array(len).fill('center'),
rowNum: 5,
waitTime: 5,
headerHeight: 40,
carousel: 'single',
headerBGC: 'rgba(53, 208, 192, 0.4)',
oddRowBGC: 'rgba(0, 59, 81, 0.1)',
evenRowBGC: 'rgba(10, 39, 50, 0.1)',
},
//
headerBGC: 'rgba(53, 208, 192, 0.4)', //
oddRowBGC: 'rgba(0, 59, 81, 0.1)', //
evenRowBGC: 'rgba(10, 39, 50, 0.1)', //
headerHeight: 40, //
columnWidth: [300, 200, 200], //
align: ['left', 'center', 'center'], //
rowNum: 4, //
waitTime: 3, // ()
carousel: 'single', //
hoverPause: true, //
},
});
const genBlankAndWihteList = (data = []) => {
const rList = data.filter((row) => row.currentRating === 'r');
const bList = data.filter((row) => row.currentRating === 'b');
const len = rList.length >= bList.length ? rList.length : bList.length;
let list = new Array(len).fill(['', '', '', '']);
rList.forEach((item, index) => {
let temp = new Array(4).fill('');
temp[0] = item.belongCompany;
temp[1] = item.comName;
list[index] = temp;
});
bList.forEach((item, index) => {
list[index][2] = item.belongCompany;
list[index][3] = item.comName;
});
return list;
//
const currentDate = ref('2023年12月15日');
const scrollPosition = ref(0);
const scrollContainer = ref(null);
let animationFrameId = null;
//
const startScrolling = () => {
const scrollSpeed = -60; // px/s
let lastTime = 0;
const scroll = (timestamp) => {
if (!lastTime) lastTime = timestamp;
const delta = (timestamp - lastTime) / 1000; //
lastTime = timestamp;
scrollPosition.value += scrollSpeed * delta;
//
const containerWidth = scrollContainer.value?.clientWidth || 0;
const contentWidth = scrollContainer.value?.scrollWidth || 0;
if (Math.abs(scrollPosition.value) > contentWidth + 50) {
scrollPosition.value = containerWidth;
}
animationFrameId = requestAnimationFrame(scroll);
};
watch(
() => props.data,
(val) => {
if (!isEmpty(val)) {
state.options.option.dataset = genBlankAndWihteList(val);
state.loading = true;
}
},
{
deep: true,
immediate: true,
}
);
</script>
<style scoped lang="scss">
.board {
padding: 10px 0px;
animationFrameId = requestAnimationFrame(scroll);
};
&:deep(.row-item) {
onMounted(() => {
//
const now = new Date();
currentDate.value = `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}`;
startScrolling();
});
onUnmounted(() => {
cancelAnimationFrame(animationFrameId);
});
</script>
<style scoped lang="scss">
/* 自定义样式 */
.dv-scroll-board {
font-family: 'PingFang SC', 'PingFang SC-Regular', sans-serif;
color: #ffffff;
font-size: 16px;
:deep(.header-item) {
text-align: left;
font-weight: 700;
padding-left: 16px;
}
:deep(.row-item) {
transition: all 0.5s ease;
font-weight: 400;
.ceil {
&:nth-child(3),
&:nth-child(4) {
color: $color-danger;
text-align: left;
padding-left: 16px;
}
}
}
.announcement-bar {
$primary-color: #0e6965;
$secondary-color: #34f9b7;
$text-color: #b5f5ff;
$bg-color: rgba(0, 60, 107, 0.8);
display: flex;
align-items: center;
padding: 0.75rem 1.25rem;
background: $bg-color;
border-radius: 0.5rem;
border-left: 0.25rem solid $secondary-color;
box-shadow: 0 0.25rem 1rem rgba($primary-color, 0.2);
overflow: hidden;
position: relative;
&::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 3rem;
background: linear-gradient(90deg, rgba($bg-color, 0) 0%, $bg-color 100%);
z-index: 1;
}
.horn-icon {
flex-shrink: 0;
margin-right: 1rem;
color: $secondary-color;
svg {
width: 1.5rem;
height: 1.5rem;
filter: drop-shadow(0 0 0.25rem rgba($secondary-color, 0.5));
}
}
.scroll-container {
flex: 1;
overflow: hidden;
position: relative;
height: 1.5rem;
.scroll-content {
position: absolute;
white-space: nowrap;
will-change: transform;
padding-right: 2rem; //
.text-content {
color: $text-color;
font-size: 1.125rem;
font-weight: 500;
text-shadow: 0 0 0.5rem rgba($primary-color, 0.3);
}
.highlight {
color: $secondary-color;
font-weight: bold;
margin: 0 0.25rem;
}
}
}

View File

@ -1,94 +1,120 @@
<template>
<custom-echart-bar :chart-data="state.data" height="100%" :option="state.option" />
<custom-echart-bar :chart-data="state.data" height="100%" :option="state.option" :is-series="true" />
</template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
<script setup>
import { reactive } from 'vue';
const rawData = [
{ name: '勐撒镇', type: '种子农药', value: 15 },
{ name: '勐撒镇', type: '化肥', value: 30 },
{ name: '勐撒镇', type: '农药', value: 22 },
{ name: '勐永镇', type: '种子农药', value: 18 },
{ name: '勐永镇', type: '化肥', value: 28 },
{ name: '勐永镇', type: '农药', value: 20 },
{ name: '孟定镇', type: '种子农药', value: 21 },
{ name: '孟定镇', type: '化肥', value: 25 },
{ name: '孟定镇', type: '农药', value: 24 },
{ name: '勐简乡', type: '种子农药', value: 13 },
{ name: '勐简乡', type: '化肥', value: 20 },
{ name: '勐简乡', type: '农药', value: 15 },
{ name: '贺派乡', type: '种子农药', value: 17 },
{ name: '贺派乡', type: '化肥', value: 18 },
{ name: '贺派乡', type: '农药', value: 16 },
{ name: '芒洪乡', type: '种子农药', value: 14 },
{ name: '芒洪乡', type: '化肥', value: 23 },
{ name: '芒洪乡', type: '农药', value: 21 },
{ name: '大兴乡', type: '种子农药', value: 12 },
{ name: '大兴乡', type: '化肥', value: 17 },
{ name: '大兴乡', type: '农药', value: 14 },
{ name: '耿马镇', type: '种子农药', value: 19 },
{ name: '耿马镇', type: '化肥', value: 26 },
{ name: '耿马镇', type: '农药', value: 23 },
];
const towns = ['勐撒镇', '勐永镇', '孟定镇', '勐简乡', '贺派乡', '芒洪乡', '大兴乡', '耿马镇'];
const types = ['种子农药', '化肥', '农药'];
const colors = ['#15EB90', '#F3F70F', '#08DFE4'];
const series = types.map((type, idx) => {
return {
name: type,
type: 'bar',
barWidth: 16,
stack: 'total',
itemStyle: {
color: colors[idx],
barBorderRadius: 8,
shadowColor: colors[idx],
// shadowBlur: 8,
shadowOffsetY: -16,
},
z: 10 - idx,
data: towns.map((town) => {
const found = rawData.find((d) => d.name === town && d.type === type);
return found ? found.value : 0;
}),
};
});
const state = reactive({
data: rawData,
option: {
legend: {
show: false,
},
grid: {
left: '5%',
right: '5%',
top: '20%',
left: '3%',
right: '4%',
bottom: '5%',
top: '10%',
containLabel: true,
},
xAxis: {
type: 'category',
axisLine: {
lineStyle: {
opacity: 1,
width: 0,
},
},
axisLabel: {
margin: 8,
},
axisTick: {
show: false,
interval: 0,
},
data: towns,
},
yAxis: {
type: 'value',
},
color: colors,
series,
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(18, 55, 85, 0.8);',
backgroundColor: 'rgba(0,0,0,0.6);',
borderColor: '#35d0c0',
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips">
<span>${params.name}</span><br/>
<span>${params.marker} ${params.data} 万元</span>
</div>`;
return str;
borderRadius: 8,
formatter: (params) => `
<div style="font-weight:700;margin-bottom:5px;color:#fff;font-size: 16px;">${params[0].name}</div>
${params
.map(
(p) => `
<div style="display:flex;align-items:center;margin:3px 0;color:#fff">
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${p.color};margin-right:6px;color:#fff"></span>
${p.seriesName}: <span style="font-weight:bold;margin-left:5px;color:#fff">${p.value} </span>
</div>
`
)
.join('')}
`,
extraCssText: 'backdrop-filter: blur(8px);',
},
},
barStyle: {
barWidth: 15,
itemStyle: {
borderRadius: [8, 8, 0, 0],
},
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#35D0C0' },
{ offset: 1, color: '#35D0C0' },
],
global: false,
},
},
xAxis: {
type: 'category',
// name: '',
axisTick: {
show: false,
alignWithLabel: false,
interval: 'auto',
inside: false,
length: 5,
lineStyle: {
type: 'solid',
width: 1,
color: 'rgba(28, 158, 222, 1)',
},
},
},
yAxis: {
type: 'value',
// name: '()',
},
},
data: [],
});
watch(
() => props.data,
(val) => {
if (!isEmpty(val)) {
state.data = val;
}
},
{
deep: true,
immediate: true,
}
);
</script>

View File

@ -1,6 +1,6 @@
<template>
<div class="inputs">
<h2 class="inputs-title">测批{{ state.counts }}</h2>
<h2 class="inputs-title">检次{{ state.counts }}</h2>
<el-row>
<el-col v-for="item in state.list" :key="item.label" :span="12">
<div class="inputs-item flex-column">
@ -31,7 +31,7 @@ const state = reactive({
},
{
label: '检测覆盖率',
value: '99.98%',
value: '25.46%',
},
],
});

View File

@ -2,21 +2,21 @@
<el-row class="data-home-index">
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="投入品监管体系建设" :top-postion="'left'">
<customBack top-title="全县投入品数量占比" :top-postion="'left'">
<template #back>
<inputsOne :data="state.data.one" />
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="投入品检测监管" :top-postion="'left'">
<customBack top-title="产业运营平台投入品数量" :top-postion="'left'">
<template #back>
<inputsTwo :data="state.data.InputDetector" />
<inputsFive ref="fiveRef" :data="state.data.five" :query="state.queryCode" />
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="投入品金额对比" :top-postion="'left'">
<customBack top-title="全县投入品使用情况" :top-postion="'left'">
<template #back>
<inputsThere :data="state.data.there" />
</template>
@ -29,43 +29,38 @@
<el-col :span="6" class="right-charts">
<div class="right-charts-item">
<customBack top-title="生产主体统计" :top-postion="'right'">
<customBack top-title="今日投入品价格" :top-postion="'right'">
<template #back>
<inputsFour :data="state.data.ProductEntity" />
<inputsSix :data="state.data.BlackWhite" />
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="投入品管控情况" :top-postion="'right'">
<template #back>
<inputsTwo :data="state.data.InputDetector" />
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack
top-title="历年投入品规模对比"
top-title="产业运营平台订单统计"
:top-postion="'right'"
:down-title="'全县'"
:down-title="'按日'"
:label-field="'label'"
:value-field="'value'"
:down-width="'100px'"
:options="[
{ label: '全县', value: '530926' },
{ label: '耿马镇', value: '42611' },
{ label: '勐撒镇', value: '9259' },
{ label: '勐永镇', value: '17787' },
{ label: '孟定镇', value: '42610' },
{ label: '勐简乡', value: '17788' },
{ label: '贺派乡', value: '40161' },
{ label: '四排山乡', value: '40163' },
{ label: '大兴乡', value: '40159' },
{ label: '按日', value: '0' },
{ label: '按月', value: '1' },
{ label: '按季度', value: '2' },
{ label: '按年', value: '3' },
]"
:is-down="true"
@command="handleCommand"
command="handleCommand"
>
<template #back>
<inputsFive ref="fiveRef" :data="state.data.five" :query="state.queryCode" />
</template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="投入品白名单/黑名单" :top-postion="'right'">
<template #back>
<inputsSix :data="state.data.BlackWhite" />
<inputsFour :data="state.data.ProductEntity" />
</template>
</customBack>
</div>
@ -140,7 +135,7 @@ const loadData = async () => {
unit: '家',
},
],
InputDetector: [{ JCFGL: 33.33, JCYPPC: 15, zero_count: 13, JCHGL: 86.67, counts: 45 }],
InputDetector: [{ JCFGL: 33.33, JCYPPC: 15684, zero_count: 13, JCHGL: 86.67, counts: 45 }],
there: [
{ value: 530, name: '种子' },
{ value: 1215, name: '化肥' },

View File

@ -23,13 +23,13 @@ const state = reactive({
containLabel: true,
},
tooltip: {
className: 'custom-tooltip-container',
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(0,0,0,0.6);',
borderColor: '#35d0c0',
borderRadius: 8,
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips" >

View File

@ -22,13 +22,13 @@ const state = reactive({
containLabel: true,
},
tooltip: {
className: 'custom-tooltip-container', //
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(0,0,0,0.5);',
borderColor: '#35d0c0',
borderRadius: 8,
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips" >
@ -42,7 +42,7 @@ const state = reactive({
barStyle: {
barWidth: 16,
itemStyle: {
borderRadius: 8, //
borderRadius: 8,
},
color: {
type: 'linear',

View File

@ -15,14 +15,14 @@ const props = defineProps({
const state = reactive({
option: {
k: 0.5,
k: 0,
opacity: 1,
itemGap: 0.1,
itemGap: 0.2,
legendSuffix: '万亩',
itemHeight: 200,
grid3D: {
show: false,
boxHeight: 1,
boxHeight: 3,
top: '0',
left: '-20%',
viewControl: {

View File

@ -27,13 +27,13 @@ const state = reactive({
containLabel: true,
},
tooltip: {
className: 'custom-tooltip-container',
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
backgroundColor: 'rgba(0,0,0,0.6);',
borderColor: '#35d0c0',
borderRadius: 8,
formatter: (data) => {
const params = data[0];
let str = `<div class="custom-echarts-tips">

524
yarn.lock

File diff suppressed because it is too large Load Diff