Compare commits

..

34 Commits

Author SHA1 Message Date
fd44ba2206 合并dev分支 2025-05-21 09:13:30 +08:00
608ddee02c 合并冲突 2025-05-21 09:00:50 +08:00
30743bd4e0 xiabin 2025-05-21 09:00:10 +08:00
2090205686@qq.com
2e057d7014 合并冲突 2025-05-20 17:51:57 +08:00
2090205686@qq.com
95ce33be37 智慧种植-生产管理模块开发 2025-05-20 17:49:10 +08:00
338c0cf779 icons 设备组件完善。环境监测页面完成 2025-05-20 17:34:11 +08:00
d485768e8f Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-20 13:33:04 +08:00
624fb109d2 使用申请 2025-05-20 13:32:43 +08:00
2090205686@qq.com
71abc7497e 冲突合并 2025-05-20 13:31:23 +08:00
2090205686@qq.com
c4304f8da7 智慧种植模块一体育苗页面开发,new政务数据大屏路由调整 2025-05-20 13:25:47 +08:00
417ad4bdcc Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-20 13:08:14 +08:00
ca887983dd 电商交易相关代码 2025-05-20 13:05:37 +08:00
d5bd7072e1 xiabin 2025-05-20 13:02:59 +08:00
7b9c9e70a3 node版本 18 2025-05-20 11:51:08 +08:00
575ec062cb Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-20 10:47:29 +08:00
438b388178 xiabin 2025-05-20 10:47:27 +08:00
939566e7d8 农业环境监测修改 2025-05-20 09:48:38 +08:00
32a1830f9d hls插件 2025-05-20 09:34:01 +08:00
c50655e1e9 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	sub-operation-service/yarn.lock
2025-05-20 09:32:54 +08:00
7c8a206519 hls插件 2025-05-20 09:31:30 +08:00
9e74b959d7 政务大屏修改 2025-05-20 08:53:11 +08:00
2272c21814 Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-19 13:45:24 +08:00
9e0ccac1e5 政务云大屏修改 2025-05-19 13:45:13 +08:00
2090205686@qq.com
5909699ca8 Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-19 13:44:40 +08:00
2090205686@qq.com
48bdf3923e 本地初始化项目 2025-05-19 13:44:35 +08:00
b52ef2f1d8 Merge remote-tracking branch 'origin/dev' into dev 2025-05-19 13:41:56 +08:00
47b8e18b56 智慧种植页面首页 田间监测页面 2025-05-19 13:41:18 +08:00
edbbf59a3b 智慧种植页面首页 田间监测页面 2025-05-19 13:38:34 +08:00
2090205686@qq.com
91756e6a8a Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-19 13:20:39 +08:00
2090205686@qq.com
bb9d4314d7 农产品溯源页面地图调整 2025-05-19 13:20:29 +08:00
1af489ab4b Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-19 11:56:06 +08:00
49cc00695f 投入品监管 2025-05-19 11:55:58 +08:00
2090205686@qq.com
9a1093f8f0 Merge branch 'dev' of http://47.109.205.240:3000/Web/daimp-front into dev 2025-05-16 17:46:48 +08:00
2090205686@qq.com
1986442641 生产经营主体和农产品溯源调整(未完) 2025-05-16 17:43:53 +08:00
316 changed files with 64373 additions and 11942 deletions

4
.gitignore vendored
View File

@ -23,3 +23,7 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
sub-operation-service/package-lock.json
main/package-lock.json
sub-operation-service/vite.config.js
main/.eslintrc.cjs

View File

@ -39,7 +39,12 @@ module.exports = {
}, },
// 这里时配置规则的,自己看情况配置 // 这里时配置规则的,自己看情况配置
rules: { rules: {
'prettier/prettier': 'error', 'prettier/prettier': [
'error',
{
endOfLine: 'auto', // 允许自动检测换行符
},
],
'no-debugger': 'off', 'no-debugger': 'off',
'no-unused-vars': 'off', 'no-unused-vars': 'off',
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',

BIN
main/.yarn/install-state.gz Normal file

Binary file not shown.

1
main/.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

View File

@ -57,7 +57,7 @@ export const rightApps = [
}, },
{ {
// name: 'sub-government-screen-service', // name: 'sub-government-screen-service',
name: 'new-digital-agriculture-screen', name: 'new-digital-agriculture-screen/v2/land',
// entry: VITE_APP_SUB_GSS, // entry: VITE_APP_SUB_GSS,
entry: VITE_APP_SUB_GSR, entry: VITE_APP_SUB_GSR,
// activeRule: '/sub-government-screen-service', // activeRule: '/sub-government-screen-service',

View File

@ -35,9 +35,8 @@ import { leftApps, rightApps } from '@/micro/app';
import { getAssetsFile } from '@/utils'; import { getAssetsFile } from '@/utils';
const gotoPage = (row) => { const gotoPage = (row) => {
console.log(row); // window.history.pushState({}, row.name, row.activeRule);
console.log(window.history.pushState({}, row.name, row.activeRule)); window.location.href = row.activeRule;
window.history.pushState({}, row.name, row.activeRule);
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@ declare module 'vue' {
CustomTableOperate: typeof import('./src/components/custom-table-operate/index.vue')['default'] CustomTableOperate: typeof import('./src/components/custom-table-operate/index.vue')['default']
CustomTableTree: typeof import('./src/components/custom-table-tree/index.vue')['default'] CustomTableTree: typeof import('./src/components/custom-table-tree/index.vue')['default']
NewHyalineCake: typeof import('./src/components/custom-echart-hyaline-cake/new-hyaline-cake.vue')['default'] NewHyalineCake: typeof import('./src/components/custom-echart-hyaline-cake/new-hyaline-cake.vue')['default']
NewPie: typeof import('./src/components/custom-echart-hyaline-cake/new-pie.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
SubTop: typeof import('./src/components/subTop.vue')['default'] SubTop: typeof import('./src/components/subTop.vue')['default']

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

View File

@ -28,7 +28,7 @@ const props = defineProps({
}); });
const titles = ref([ const titles = ref([
{ label: '首页', value: 'home' }, // { label: '', value: 'home' },
{ label: '土地资源', value: 'land' }, { label: '土地资源', value: 'land' },
{ label: '投入品', value: 'inputs' }, { label: '投入品', value: 'inputs' },
{ label: '生产经营主体', value: 'entities' }, { label: '生产经营主体', value: 'entities' },

View File

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

View File

@ -4,17 +4,18 @@
<script setup> <script setup>
import { ref, watch, onMounted, nextTick } from 'vue'; import { ref, watch, onMounted, nextTick } from 'vue';
import { cloneDeep } from 'lodash'; import { merge, cloneDeep } from 'lodash';
import { useEcharts } from '@/hooks/useEcharts'; import { useEcharts } from '@/hooks/useEcharts';
defineOptions({ name: 'NewHyalineCake' }); defineOptions({ name: 'NewHyalineCake' });
// props let selectedIndex = null;
let hoveredIndex = null;
const props = defineProps({ const props = defineProps({
chartData: { chartData: {
type: Array, type: Array,
default: () => [ default: () => [
//
{ name: '项目一', value: 60 }, { name: '项目一', value: 60 },
{ name: '项目二', value: 44 }, { name: '项目二', value: 44 },
{ name: '项目三', value: 32 }, { name: '项目三', value: 32 },
@ -23,18 +24,13 @@ const props = defineProps({
option: { option: {
type: Object, type: Object,
default: () => ({ default: () => ({
// 1 k: 1, // 1
k: 1, itemGap: 0.1, //
// itemHeight: 120, // z
itemGap: 0.2, autoItemHeight: 0, // 使>0 itemHeight 使 autoItemHeight * value
// z opacity: 0.6, //
itemHeight: 120, legendSuffix: '', //
// 使>0 itemHeight 使 autoItemHeight * value // TODO series
autoItemHeight: 0,
//
opacity: 0.6,
//
legendSuffix: '',
}), }),
}, },
width: { width: {
@ -47,20 +43,92 @@ const props = defineProps({
}, },
}); });
//
const emit = defineEmits(['click']); const emit = defineEmits(['click']);
// DOM
const chartRef = ref(null); const chartRef = ref(null);
// 使 useEcharts ECharts
const { setOptions, getInstance } = useEcharts(chartRef); const { setOptions, getInstance } = useEcharts(chartRef);
// ECharts
const chartOption = ref({}); const chartOption = ref({});
// pie2d
const defaultPie2dOption = {
center: ['50%', '50%'],
label: {
show: false,
position: 'inside',
formatter: '{c}',
color: '#fff',
distance: 0,
},
labelLine: {
show: false,
smooth: false,
length: props.option.itemGap * 500,
length2: props.option.itemGap * 800,
lineStyle: { color: '#fff', width: 1 },
},
};
// pie2dConfig
const pie2dOption = {
...defaultPie2dOption,
...(props.option.pie2dConfig || {}),
};
// series-surface.parametricEquation //
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) { function initChart() {
// // 3D()
const baseOption = getPie3D(props.chartData, props.option.k);
//
const finalOption = merge({}, baseOption, cloneDeep(props.option || {}));
chartOption.value = finalOption;
//
setOptions(chartOption.value);
// DOM + ECharts
nextTick(() => {
const chart = getInstance();
if (!chart) {
return;
}
//
// chart.off('click');
chart.off('mouseover');
chart.off('globalout');
// chart.on('click', handleClick);
chart.on('mouseover', handleMouseover);
chart.on('globalout', handleGlobalout);
});
}
//
onMounted(() => {
initChart();
});
//
watch(
[() => props.chartData, () => props.option],
() => {
if (props.chartData && props.chartData.length) {
initChart();
}
},
{ immediate: true, deep: true }
);
/**
* 获取 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 midRatio = (startRatio + endRatio) / 2;
const startRadian = startRatio * Math.PI * 2; const startRadian = startRatio * Math.PI * 2;
const endRadian = endRatio * Math.PI * 2; const endRadian = endRatio * Math.PI * 2;
@ -70,7 +138,7 @@ function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h
if (startRatio === 0 && endRatio === 1) { if (startRatio === 0 && endRatio === 1) {
isSelected = false; isSelected = false;
} }
// k 1/3 k 使 // k 1/3/ k 使
k = typeof k !== 'undefined' ? k : 1 / 3; k = typeof k !== 'undefined' ? k : 1 / 3;
// //
@ -79,7 +147,6 @@ function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h
// 1 // 1
const hoverRate = isHovered ? 1.05 : 1; const hoverRate = isHovered ? 1.05 : 1;
// parametric
return { return {
u: { u: {
// u -π 3π // u -π 3π
@ -117,167 +184,117 @@ function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h
z(u, v) { z(u, v) {
// u < -π/2 使 // u < -π/2 使
if (u < -Math.PI * 0.5) { if (u < -Math.PI * 0.5) {
return Math.sin(u); return Math.sin(u) + offsetZ * h * 0.1;
} }
// u > 2.5π // u > 2.5π
if (u > Math.PI * 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 // z v
// Zhvalue // 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 = []; const series = [];
let sumValue = 0; let sumValue = 0;
// let startValue = 0;
pieData.forEach((item) => { let endValue = 0;
sumValue += item.value; const legendData = [];
});
const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;
// k
const k = props.option.k ?? 1;
// series // series
pieData.forEach((dataItem, idx) => { for (let i = 0; i < pieData.length; i += 1) {
sumValue += pieData[i].value;
const seriesItem = { const seriesItem = {
name: dataItem.name ?? `series${idx}`, name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
type: 'surface', type: 'surface',
parametric: true, parametric: true,
wireframe: { show: false }, wireframe: { show: false },
pieData: dataItem, pieData: pieData[i],
itemStyle: {
opacity: props.option.opacity,
borderRadius: 300,
borderColor: '#fff',
borderWidth: 0,
},
pieStatus: { pieStatus: {
selected: false, selected: false,
hovered: false, hovered: false,
k, k,
}, },
}; };
// if (typeof pieData[i].itemStyle !== 'undefined') {
if (dataItem.itemStyle) { const { itemStyle } = pieData[i];
const customStyle = {}; typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
if (dataItem.itemStyle.color !== undefined) { typeof pieData[i].itemStyle.opacity !== 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
customStyle.color = dataItem.itemStyle.color;
} seriesItem.itemStyle = itemStyle;
if (dataItem.itemStyle.opacity !== undefined) {
customStyle.opacity = dataItem.itemStyle.opacity;
}
seriesItem.itemStyle = { ...seriesItem.itemStyle, ...customStyle };
} }
series.push(seriesItem); 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 '';
}, // 使 sumValue getParametricEquation
}, // series-surface series-surface.parametricEquation
xAxis3D: { min: -1, max: 1 }, // console.log(series);
yAxis3D: { min: -1, max: 1 }, for (let i = 0; i < series.length; i += 1) {
zAxis3D: { min: -1, max: 1 }, endValue = startValue + series[i].pieData.value;
grid3D: {
show: false, const z = series[i]?.pieData?.floatZ ?? 0;
boxHeight: 5, series[i].pieData.startRatio = startValue / sumValue;
top: '0', series[i].pieData.endRatio = endValue / sumValue;
left: '-20%', series[i].parametricEquation = getParametricEquation(
viewControl: { series[i].pieData.startRatio,
// 3D series[i].pieData.endRatio,
alpha: 60, // series[i].pieStatus.selected,
distance: 240, // series[i].pieStatus.hovered,
rotateSensitivity: 10, k,
zoomSensitivity: 10, props.option.autoItemHeight > 0 ? props.option.autoItemHeight * series[i].pieData.value : props.option.itemHeight,
panSensitivity: 10, z
autoRotate: true, );
autoRotateAfterStill: 2,
}, startValue = endValue;
legendData.push(series[i].name);
}
// 3. 2D pie legend/label
const outerPercent = 50;
const innerPercent =
internalDiameterRatio != null ? Math.round(internalDiameterRatio * outerPercent) : Math.round(((1 - k) / (1 + k)) * outerPercent);
const pie2dData = [];
for (let i = 0; i < pieData.length; i++) {
pie2dData.push({
name: pieData[i].name,
value: pieData[i].value,
// itemStyle: { opacity: 1 },
});
}
series.push({
name: 'pie2d',
type: 'pie',
data: pie2dData,
radius: [`${innerPercent}%`, `${outerPercent}%`],
center: pie2dOption.center,
startAngle: props.option.startAngle || 90,
clockwise: false,
itemStyle: {
color: 'transparent',
}, },
avoidLabelOverlap: false,
label: pie2dOption.label,
labelLine: pie2dOption.labelLine,
});
//
// legendDataseries
const option = {
legend: { legend: {
type: 'scroll',
show: true, show: true,
selectedMode: false,
right: '5%', right: '5%',
top: '25%', top: '25%',
orient: 'vertical', orient: 'vertical',
@ -290,48 +307,114 @@ function getPie3D(pieData) {
fontSize: 14, fontSize: 14,
fontWeight: '400', fontWeight: '400',
}, },
//
formatter: (name) => { formatter: (name) => {
const item = props.chartData.find((d) => d.name === name); if (props.chartData.length) {
return item ? ` ${name} ${item.value}${props.option.legendSuffix || ''}` : name; const item = props.chartData.filter((item) => item.name === name)[0];
return ` ${name} ${item.value}${props.option.legendSuffix ?? ''}`;
}
},
},
// animation: false,
tooltip: {
formatter: (params) => {
// 3D surface
if (params.seriesType === 'surface') {
// params.seriesIndex
const serie = chartOption.value.series[params.seriesIndex];
const realValue = serie.pieData.value;
return `${params.seriesName}${realValue}`;
}
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,
}, },
}, },
series, series,
}, };
//
props.option
);
return option; return option;
} }
// // mouseover
function initChart() { function handleMouseover(params) {
// 3D // console.log('mouseover');
const baseOption = getPie3D(props.chartData); const idx = params.seriesIndex;
// const series = chartOption.value.series;
const finalOption = Object.assign({}, baseOption, cloneDeep(props.option || {}));
chartOption.value = finalOption; //
// if (hoveredIndex === idx) return;
setOptions(chartOption.value);
// DOM + ECharts // 1.
nextTick(() => { if (hoveredIndex !== null && hoveredIndex >= 0 && series[hoveredIndex]) {
const chart = getInstance(); updateSeriesHover(hoveredIndex, false);
if (!chart) {
console.warn('ECharts 实例未初始化,事件绑定失败');
return;
} }
// // 2.
chart.off('click'); if (params.seriesName !== 'mouseoutSeries' && series[idx]) {
chart.off('mouseover'); updateSeriesHover(idx, true);
chart.off('globalout'); hoveredIndex = idx;
} else {
hoveredIndex = null;
}
chart.on('click', handleClick); // 3.
chart.on('mouseover', handleMouseover); setOptions(chartOption.value);
chart.on('globalout', handleGlobalout);
});
} }
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 + 80 : baseHeight;
// parametricEquation
item.parametricEquation = getParametricEquation(start, end, isSelected, toHover, k, newHeight);
//
item.pieStatus = {
...status,
hovered: toHover,
};
}
function handleClick(params) { function handleClick(params) {
// //
if (params.seriesName === 'mouseoutSeries') return; if (params.seriesName === 'mouseoutSeries') return;
@ -367,124 +450,10 @@ function handleClick(params) {
series[idx].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h); series[idx].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h);
series[idx].pieStatus.selected = isSelected; series[idx].pieStatus.selected = isSelected;
//
selectedIndex = isSelected ? idx : null; selectedIndex = isSelected ? idx : null;
//
setOptions(optionVal); setOptions(optionVal);
// click 使
emit('click', params); emit('click', params);
} }
//
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(() => {
initChart();
});
//
watch(
[() => props.chartData, () => props.option],
() => {
if (props.chartData && props.chartData.length) {
initChart();
}
},
{ immediate: true, deep: true }
);
</script> </script>
<style scoped> <style scoped>

View File

@ -0,0 +1,114 @@
<template>
<div ref="chartRef" class="chart-container" />
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import { useEcharts } from '@/hooks/useEcharts';
const props = defineProps({
chartData: {
type: Array,
default: () => [],
},
// ['30%', '50%']
radius: {
type: Array,
default: () => ['30%', '50%'],
},
//
alpha: {
type: Number,
default: 30,
},
//
beta: {
type: Number,
default: 0,
},
//
autoRotate: {
type: Boolean,
default: false,
},
height: {
type: String,
default: '300px',
},
});
const chartRef = ref(null);
const { setOptions } = useEcharts(chartRef);
function buildOption(data) {
return {
tooltip: {
trigger: 'item',
formatter: '{b}{c} ({d}%)',
},
legend: {
orient: 'vertical',
right: 20,
top: 'center',
textStyle: { color: '#fff' },
},
series: [
{
type: 'pie3D',
name: '3D 饼图',
data,
radius: props.radius,
center: ['50%', '50%'],
label: {
show: true,
formatter: '{b}\n{c}',
},
shading: 'realistic', //
itemStyle: {
opacity: 1,
borderWidth: 1,
borderColor: '#333',
},
emphasis: {
label: { fontSize: 16, color: '#fff' },
itemStyle: { opacity: 1 },
},
viewControl: {
alpha: props.alpha, //
beta: props.beta, //
autoRotate: props.autoRotate,
distance: 120,
zoomSensitivity: 0,
panSensitivity: 0,
},
},
],
};
}
let option = buildOption(props.chartData);
onMounted(() => {
setOptions(option);
});
watch(
() => props.chartData,
(v) => {
option = buildOption(v);
setOptions(option);
},
{ deep: true }
);
</script>
<style scoped>
.chart-container {
width: 100%;
height: var(--chart-height);
}
/* 传递 height 到局部变量 */
:root {
--chart-height: 300px;
}
</style>

View File

@ -108,35 +108,31 @@ export default {
} }
let typeArr = Array.from(new Set(props.chartData.map((item) => item.type))); 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 xAxisData = Array.from(new Set(props.chartData.map((item) => item.name)));
let seriesData = []; // 1. 使series
typeArr.forEach((type, index) => { const externalSeries = props.option?.series || [];
let obj = { option.series = typeArr.map((type, index) => {
name: type, // 2. type
type: props.type, const externalConfig = externalSeries.find((s) => s.name === type) || {};
smooth: true,
// 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; option.xAxis.data = xAxisData;
// console.log(option.series);
setOptions(option); setOptions(option);
startAutoPlay({ startAutoPlay({
interval: 2000, interval: 2000,

View File

@ -29,6 +29,10 @@ export default {
type: String, type: String,
default: 'calc(100vh - 78px)', default: 'calc(100vh - 78px)',
}, },
activeIndex: {
type: Number,
default: 1,
},
}, },
emits: ['click'], emits: ['click'],
setup(props, { emit }) { setup(props, { emit }) {
@ -56,13 +60,16 @@ export default {
zlevel: 1, zlevel: 1,
name: '漏斗图', name: '漏斗图',
type: 'funnel', type: 'funnel',
animation: true, //
animationDuration: 500, //
animationEasing: 'cubicOut', //
top: '11%', top: '11%',
left: 'center', left: 'center',
width: '25%', width: '25%',
sort: 'ascending', sort: 'ascending',
gap: 0, gap: 0,
label: { label: {
show: true, show: false,
position: 'outside', position: 'outside',
width: '200px', width: '200px',
align: 'right', align: 'right',
@ -79,9 +86,8 @@ export default {
padding: [5, 6], // padding: [5, 6], //
}, },
labelLine: { labelLine: {
show: true, show: false,
length: 10, length: 30,
length2: 50,
smooth: true, smooth: true,
lineStyle: { lineStyle: {
width: 1, width: 1,
@ -90,28 +96,6 @@ export default {
type: 'solid', type: 'solid',
}, },
}, },
// 线
// markLine: {
// symbol: 'none', //
// lineStyle: {
// type: 'solid',
// color: '#fff',
// width: 1,
// },
// data: [
// // labelLine
// [
// {
// coord: [50, 50], // 线
// name: 'Label1',
// },
// {
// coord: [80, 50], // 线
// name: 'Label1',
// },
// ],
// ],
// },
itemStyle: { itemStyle: {
show: false, show: false,
borderColor: '#fff', borderColor: '#fff',
@ -141,12 +125,50 @@ export default {
} }
); );
watch(
() => props.activeIndex,
(newVal) => {
showLabel(newVal);
}
);
function initCharts() { function initCharts() {
if (props.option) { if (props.option) {
Object.assign(option, cloneDeep(props.option)); Object.assign(option, cloneDeep(props.option));
} }
option.series[0].data = props.chartData; option.series[0].data = props.chartData;
setOptions(option); setOptions(option);
showLabel(props.activeIndex);
}
// labeloption
function getOption(activeIndex) {
//
let myData = props.chartData.map((item, idx) => ({
...item,
label: {
show: idx === activeIndex, //
},
labelLine: {
show: idx === activeIndex, // 线
},
}));
let myOption = {
...option,
series: [
{
...option.series[0],
data: myData,
},
],
};
return myOption;
}
//
function showLabel(activeIndex) {
const newOption = getOption(activeIndex);
setOptions(newOption);
} }
function onClick(params) { function onClick(params) {

View File

@ -54,7 +54,7 @@ const props = defineProps({
type: Array, type: Array,
default() { default() {
return [ return [
{ label: '首页', value: '/new-digital-agriculture-screen/v2/home' }, // { label: '', value: '/new-digital-agriculture-screen/v2/home' },
{ label: '土地资源', value: '/new-digital-agriculture-screen/v2/land' }, { label: '土地资源', value: '/new-digital-agriculture-screen/v2/land' },
{ label: '投入品监管', value: '/new-digital-agriculture-screen/v2/inputs' }, { label: '投入品监管', value: '/new-digital-agriculture-screen/v2/inputs' },
{ label: '产出品管理', value: '/new-digital-agriculture-screen/v2/entities' }, { label: '产出品管理', value: '/new-digital-agriculture-screen/v2/entities' },
@ -176,7 +176,7 @@ function handleTitleClick(val) {
position: relative; position: relative;
width: 31%; width: 31%;
height: 100%; height: 100%;
overflow: hidden; // overflow: hidden;
line-height: 90px; line-height: 90px;
.title_content { .title_content {
position: absolute; position: absolute;

View File

@ -58,10 +58,10 @@ onMounted(() => {
user-select: none; user-select: none;
box-sizing: border-box; box-sizing: border-box;
width: 100%; width: 100%;
height: 100; height: 100%;
background: background:
url('@/assets/images/basic/containerBG.png') no-repeat center 100%, url('../assets/images/basic/containerBG.png') no-repeat center 100%,
url('@/assets/images/basic/containerBotBG.png') no-repeat bottom center; url('../assets/images/basic/containerBotBG.png') no-repeat bottom center;
&-header { &-header {
width: 100%; width: 100%;
margin-bottom: 16px; margin-bottom: 16px;

View File

@ -19,21 +19,86 @@ export const constantRoutes = [
}, },
{ {
path: '/new-digital-agriculture-screen', path: '/new-digital-agriculture-screen',
name: 'layout',
component: Layout,
redirect: '/new-digital-agriculture-screen/v2/home',
meta: { title: '首页', icon: 'House' },
children: [
{
path: '/new-digital-agriculture-screen/v2/home',
component: () => import('@/views/home/index.vue'),
name: 'home', name: 'home',
component: Layout,
redirect: '/new-digital-agriculture-screen/v2/land', //原home
meta: { title: '首页', icon: '' }, meta: { title: '首页', icon: '' },
children: [
// {
// path: '/new-digital-agriculture-screen/v2/home',
// component: () => import('@/views/home/index.vue'),
// name: 'home',
// meta: { title: '首页', icon: '' },
// },
{
path: '/new-digital-agriculture-screen/v2/land',
component: () => import('@/views/land/index.vue'),
name: 'land',
meta: { title: '土地资源', icon: '' },
}, },
{
path: '/new-digital-agriculture-screen/v2/inputs',
name: 'inputs',
component: () => import('@/views/inputs/index.vue'),
meta: { title: '投入品监管', icon: '' },
},
{
path: '/new-digital-agriculture-screen/v2/entities',
name: 'entities',
component: () => import('@/views/entities/index.vue'),
meta: { title: '产出品管理', icon: '' },
},
// {
// path: 'breed',
// name: 'breed',
// component: () => import('@/views/breed/index.vue'),
// meta: { title: '', icon: '' },
// },
// {
// path: 'plant',
// name: 'plant',
// component: () => import('@/views/plant/index.vue'),
// meta: { title: '', icon: '' },
// },
{
path: '/new-digital-agriculture-screen/v2/business',
name: 'business',
component: () => import('@/views/business/index.vue'),
meta: { title: '生产经营主体', icon: '' },
},
{
path: '/new-digital-agriculture-screen/v2/trace',
name: 'trace',
component: () => import('@/views/trace/index.vue'),
meta: { title: '农产品溯源', icon: '' },
},
// {
// path: 'early',
// name: 'early',
// component: () => import('@/views/early/index.vue'),
// meta: { title: '', icon: '' },
// },
], ],
}, },
// {
// // 原来的首页默认配置
// path: '/new-digital-agriculture-screen',
// name: 'layout',
// component: Layout,
// redirect: '/new-digital-agriculture-screen/v2/home',
// meta: { title: '首页', icon: 'House' },
// children: [
// {
// path: '/new-digital-agriculture-screen/v2/home',
// component: () => import('@/views/home/index.vue'),
// name: 'home',
// meta: { title: '首页', icon: '' },
// },
// ],
// },
// ...demoRouters, // ...demoRouters,
v2, // v2,
// { // {
// path: '/test', // path: '/test',
// name: 'test', // name: 'test',

View File

@ -4,7 +4,7 @@ export default {
path: '/new-digital-agriculture-screen', path: '/new-digital-agriculture-screen',
name: 'layout', name: 'layout',
component: Layout, component: Layout,
redirect: '/new-digital-agriculture-screen/v2/home', redirect: '/new-digital-agriculture-screen/v2/land', //原home
meta: { title: '首页', icon: '' }, meta: { title: '首页', icon: '' },
children: [ children: [
{ {
@ -50,7 +50,7 @@ export default {
meta: { title: '生产经营主体', icon: '' }, meta: { title: '生产经营主体', icon: '' },
}, },
{ {
path: '/new-digital-agriculture-screen/trace', path: '/new-digital-agriculture-screen/v2/trace',
name: 'trace', name: 'trace',
component: () => import('@/views/trace/index.vue'), component: () => import('@/views/trace/index.vue'),
meta: { title: '农产品溯源', icon: '' }, meta: { title: '农产品溯源', icon: '' },

View File

@ -11,6 +11,7 @@ import {
GaugeChart, GaugeChart,
ScatterChart, ScatterChart,
EffectScatterChart, EffectScatterChart,
FunnelChart,
} from 'echarts/charts'; } from 'echarts/charts';
import 'echarts-gl'; import 'echarts-gl';
import 'echarts-liquidfill'; import 'echarts-liquidfill';
@ -70,6 +71,7 @@ echarts.use([
MarkPointComponent, MarkPointComponent,
MarkLineComponent, MarkLineComponent,
MarkAreaComponent, MarkAreaComponent,
FunnelChart,
]); ]);
export default echarts; export default echarts;

View File

@ -20,10 +20,10 @@ const state = reactive({
option: { option: {
// //
dataset: [], dataset: [],
type: 'column', type: 'row',
rowNum: 6, rowNum: 6,
isAnimation: true, isAnimation: true,
waitTime: 5, waitTime: 12,
unit: '万元', unit: '万元',
sort: true, sort: true,
height: 12, height: 12,
@ -32,9 +32,9 @@ const state = reactive({
borderRadius: '12px', borderRadius: '12px',
carousel: 'single', carousel: 'single',
indexPrefix: 'TOP', indexPrefix: 'TOP',
indexFontSize: 12, indexFontSize: 22,
leftFontSize: 14, leftFontSize: 16,
rightFontSize: 14, rightFontSize: 16,
valueFormatter: (item) => { valueFormatter: (item) => {
return item.value; return item.value;
}, },

View File

@ -44,7 +44,28 @@ const state = reactive({
radius: '80%', radius: '80%',
center: ['50%', '50%'], center: ['50%', '50%'],
backgroundStyle: { backgroundStyle: {
color: 'transparent', color: {
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{
offset: 0,
color: 'rgba(220, 255, 255, 0.3)', //
},
{
offset: 0.7,
color: 'rgba(0, 231, 240, 0.2)', //
},
{
offset: 1,
color: 'rgba(0, 150, 200, 0.3)', //
},
],
globalCoord: false,
},
blur: 10, //
}, },
data: [], data: [],
amplitude: 12, // amplitude: 12, //
@ -57,6 +78,7 @@ const state = reactive({
}, },
}, },
outline: { outline: {
//
borderDistance: 2, borderDistance: 2,
itemStyle: { itemStyle: {
borderWidth: 2, borderWidth: 2,
@ -85,6 +107,8 @@ const state = reactive({
}, },
}, },
itemStyle: { itemStyle: {
opacity: 1, //
//
color: { color: {
type: 'linear', // 线 type: 'linear', // 线
x: 0, x: 0,
@ -92,8 +116,9 @@ const state = reactive({
x2: 0, x2: 0,
y2: 1, y2: 1,
colorStops: [ colorStops: [
{ offset: 0, color: 'rgba(13, 204, 163, 0.6)' }, { offset: 0, color: '#6af7e8' }, //
{ offset: 1, color: 'rgba(71, 202, 219, 1)' }, // { offset: 0.7, color: '#5fcbc1' },
{ offset: 1, color: '#55e7ff' },
], ],
global: false, // false global: false, // false
}, },
@ -107,8 +132,9 @@ watch(
() => props.data, () => props.data,
(val) => { (val) => {
if (!isEmpty(val)) { if (!isEmpty(val)) {
state.option.series[0].data = [0, val.percent]; //
state.option.series[0].label.formatter = val.percent * 100 + '%'; state.option.series[0].data = [val.percent + 0.05, val.percent]; //
state.option.series[0].label.formatter = val.percent * 100 + '%'; //
} }
}, },
{ {

View File

@ -193,20 +193,19 @@ function getPie3D(pieData, internalDiameterRatio) {
}, },
title: { title: {
text: '', text: '',
x: '35%', x: '34%', //
y: '30%', y: '32%',
itemGap: 4,
textAlign: 'center',
textStyle: { textStyle: {
rich: { color: '#ffffff',
a: { fontSize: '26px',
fontSize: 20, fontWeight: 'bold',
color: '#fff',
},
c: {
fontSize: 12,
color: '#fff',
padding: [15, 0],
},
}, },
subtext: '',
subtextStyle: {
color: '#02FD94',
fontSize: '18px',
}, },
}, },
xAxis3D: { xAxis3D: {
@ -224,14 +223,14 @@ function getPie3D(pieData, internalDiameterRatio) {
grid3D: { grid3D: {
show: false, show: false,
boxHeight: boxHeight, boxHeight: boxHeight,
left: '-10%', left: '-15%',
top: -20, top: -20,
viewControl: { viewControl: {
alpha: 55, //( ) alpha: 40, //( )
distance: 150, //zoom() distance: 150, //zoom()
rotateSensitivity: 1, //0 rotateSensitivity: 1, //0
zoomSensitivity: 0, //0 zoomSensitivity: 10, //0
panSensitivity: 0, //0 panSensitivity: 10, //0
autoRotate: true, // autoRotate: true, //
}, },
}, },
@ -243,7 +242,8 @@ function getPie3D(pieData, internalDiameterRatio) {
const initData = (pieData = []) => { const initData = (pieData = []) => {
const option = getPie3D(pieData, 0.8); const option = getPie3D(pieData, 0.8);
const { name, value } = option.series[0].pieData; const { name, value } = option.series[0].pieData;
option.title.text = `{a|${value}%}{c|\n${name}}`; option.title.text = value + '%'; //
option.title.subtext = name; //
state.option = option; state.option = option;
state.data = option.series; state.data = option.series;
}; };

View File

@ -15,7 +15,7 @@
:down-title="'农资企业'" :down-title="'农资企业'"
:label-field="'label'" :label-field="'label'"
:value-field="'value'" :value-field="'value'"
:down-width="'130px'" :down-width="''"
:options="[ :options="[
{ value: 1, label: '农企/合作社' }, { value: 1, label: '农企/合作社' },
{ value: 2, label: '农资企业' }, { value: 2, label: '农资企业' },

View File

@ -5,7 +5,7 @@
<span class="right_btn" @click="handleChange(1)"></span> <span class="right_btn" @click="handleChange(1)"></span>
{{ current.info.name }} {{ current.info.name }}
</section> </section>
<custom-echart-triangle :chart-data="data" height="100%" :option="option" /> <custom-echart-triangle :chart-data="data" height="100%" :option="option" :active-index="current.info.level - 1" />
</div> </div>
</template> </template>
<script setup> <script setup>
@ -23,14 +23,17 @@ const list = ref([
{ {
name: '茶叶', name: '茶叶',
value: '1', value: '1',
level: 1,
}, },
{ {
name: '核桃', name: '核桃',
value: '2', value: '2',
level: 2,
}, },
{ {
name: '玉米', name: '玉米',
value: '3', value: '3',
level: 3,
}, },
]); ]);
const current = reactive({ const current = reactive({
@ -38,7 +41,8 @@ const current = reactive({
length: list.value.length - 1, length: list.value.length - 1,
info: { info: {
name: '茶叶', name: '茶叶',
value: '20', value: '1',
level: 1,
}, },
}); });
function handleChange(n) { function handleChange(n) {

View File

@ -1,116 +1,235 @@
<template> <template>
<custom-echart-line :chart-data="state.data" height="100%" :option="state.option" /> <custom-echart-line :chart-data="chartData" :option="chartOption" height="100%" />
</template> </template>
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { ref } from 'vue';
import { isEmpty, sleep } from '@/utils';
const props = defineProps({ // 1. 12
data: { const chartData = ref(
type: Array, (() => {
default: () => [], //
}, const waveConfig = {
query: { // []: (1.0)
type: String, peaks: { 3: 1.6, 9: 1.5 }, // 410 (0)
default: '', valleys: { 0: 0.7, 6: 0.8, 11: 0.6 }, // 1712
}, };
});
const state = reactive({ //
option: { const generateWaveData = (baseVal) => {
color: ['#35D0C0'], return Array.from({ length: 12 }, (_, i) => {
grid: { if (waveConfig.peaks[i]) return baseVal * waveConfig.peaks[i];
left: '5%', if (waveConfig.valleys[i]) return baseVal * waveConfig.valleys[i];
right: '5%', //
bottom: '5%', return baseVal * (0.9 + Math.random() * 0.2);
top: '10%', }).map((v) => parseFloat(v.toFixed(1)));
containLabel: true, };
//
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: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow', type: 'shadow',
}, },
backgroundColor: 'rgba(18, 55, 85, 0.8);', backgroundColor: 'rgba(0,0,0,0.5);',
borderColor: '#35d0c0', borderColor: '#35d0c0',
formatter: (data) => { borderRadius: 8,
const params = data[0]; formatter: (params) => `
let str = `<div class="custom-echarts-tips"> <div style="font-weight:bold;margin-bottom:5px;color:#fff">${params[0].name}</div>
<span>${params.name}</span><br/> ${params
<span>${params.marker} ${params.data} 万元</span> .map(
</div>`; (p) => `
return str; <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>
xAxis: { </div>
type: 'category', `
// name: '', )
axisTick: { .join('')}
show: false, `,
alignWithLabel: false, extraCssText: 'backdrop-filter: blur(8px);',
interval: 'auto',
inside: false,
length: 5,
lineStyle: {
type: 'solid',
width: 1,
color: 'rgba(28, 158, 222, 1)',
},
}, },
grid: {
left: '3%',
right: '5%',
bottom: '5%',
top: '20%',
containLabel: true,
}, },
yAxis: { yAxis: {
type: 'value', 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> </script>
<style scoped>
/* 调整倾斜月份标签的间距 */
:deep(.echarts-axis-x .echarts-axis-tick) {
margin-top: 10px;
}
</style>

View File

@ -1,272 +1,84 @@
<template> <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> </template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({ <script setup>
data: { import { reactive } from 'vue';
type: Array,
default: () => [],
},
});
const state = reactive({ const state = reactive({
option: {}, list: [
data: [], {
text: '', label: '订单金额',
}); value: '548.86',
unit: '万元',
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);
}
}, },
{ {
deep: true, label: '订单数量',
immediate: true, value: '110554',
} unit: '笔',
); },
],
});
</script> </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"> <centerMap :dialog-title="'投入品'" @mapclick="doMapclick">
<template #header> <template #header>
<div class="land-map-pop-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> </div>
</template> </template>
<template #dialogContent> <template #dialogContent>
<div class="land-map-pop-content"> <div class="land-map-pop-content">
<div v-for="(n, index) in list" :key="index" class="list-item"> <div class="section-title">投入品管控情况</div>
<div class="title"> <div class="main-content">
<span class="title-val"> {{ n.title }}</span> <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>
<div class="value">{{ n.value }}{{ n.unit }}</div>
</div> </div>
</div> </div>
</template> </template>
@ -19,85 +25,75 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue'; import { ref } 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: '万元' },
]);
let currentRegion = ref(null); let currentRegion = ref(null);
const doMapclick = (data) => { const doMapclick = (data) => {
currentRegion.value = 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.land-map-pop-header { .land-map-pop-header {
display: inline-flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; align-items: center;
padding: 0 6px;
margin-top: 3px; margin-top: 3px;
.title,
.value {
display: inline-block;
vertical-align: middle;
color: $color-white;
}
.title { .title {
font-size: 18px; font-size: 18px;
font-weight: bold;
color: #00f9ff;
} }
.value {
font-size: 14px; .view-case {
font-size: 16px;
color: #00ff99;
text-decoration: none;
cursor: pointer;
} }
} }
.land-map-pop-content { .land-map-pop-content {
width: 100%; width: 100%;
gap: 10px; padding: 4px 10px;
display: inline-flex; color: #ffffff;
justify-content: flex-start;
flex-wrap: wrap; .section-title {
.list-item { margin-bottom: 12px;
width: calc((100% - 10px) / 2); font-size: 15px;
display: inline-flex; text-align: left;
justify-content: space-between; }
padding: 6px 0;
.title { .main-content {
display: inline-flex; width: 100%;
justify-content: flex-start; display: flex;
.before {
display: inline-flex;
flex-direction: column; flex-direction: column;
justify-content: center; gap: 12px;
}
.b-content { .item {
width: 8px; display: flex;
height: 8px; justify-content: space-between;
border-radius: 50%; font-size: 16px;
display: inline-block;
} .label {
.before, color: #ffffff;
.title-val {
display: inline-block;
vertical-align: middle;
padding: 0 5px 0 2px;
color: $color-custom-main;
}
} }
.value { .value {
display: inline-block; &.green {
text-align: right; color: #01ffb1;
color: $color-white; }
font-size: 12px;
&.red {
color: #ff4242;
}
}
} }
} }
} }

View File

@ -1,74 +1,108 @@
<template> <template>
<el-row :gutter="20"> <div class="inputs">
<el-col v-for="(item, index) in state.list" :key="item.label" :span="12"> <h2 class="inputs-title">全县投入品数量291.85万吨</h2>
<div class="inputs-item flex-row"> <new-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" />
<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> </div>
</div>
</div>
</el-col>
</el-row>
</template> </template>
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
import { getAssetsFile } from '@/utils';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
});
const state = reactive({ const state = reactive({
list: [], option: {
}); k: 0,
opacity: 0.8,
watch( itemGap: 0.1,
() => props.data, // legendSuffix: '',
(val) => { itemHeight: 200,
if (!isEmpty(val)) { startAngle: 60,
state.list = val; 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, legend: {
immediate: true, show: true,
} top: 'bottom',
); orient: 'horizontal',
itemWidth: 12, //
itemHeight: 12, //
itemGap: 20,
icon: 'rect',
left: 'center',
textStyle: {
color: '#fff',
fontSize: 16,
},
formatter: (name) => name,
},
pie2dConfig: {
center: ['50%', '40%'],
label: {
show: true,
position: 'outside',
formatter: (params) => {
const total = 221.8 + 70.01; //
const percent = ((params.value / total) * 100).toFixed(0);
return `{name|${params.name}}\n{value|${params.value} 万吨\n${percent}%}`;
},
rich: {
name: {
fontSize: 16,
fontWeight: 'bold',
color: '#ffffff',
lineHeight: 20,
align: 'center',
},
value: {
fontSize: 16,
color: '#79F5AF',
lineHeight: 18,
align: 'center',
},
},
color: '#fff',
},
labelLine: {
show: true,
length: 40,
length2: 40,
lineStyle: { color: '#fff', width: 2 },
},
},
},
data: [
{ value: 221.8, name: '产业运营平台' },
{ value: 70.01, name: '其它', floatZ: 1 },
],
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.inputs { .inputs {
&-item { &-title {
align-items: center; 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; color: #fff;
padding: 10px 0; text-align: center;
&-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;
}
}
} }
} }
</style> </style>

View File

@ -1,93 +1,195 @@
<template> <template>
<div class="board"> <div height="100%">
<custom-scroll-board v-if="state.loading" :chart-config="state.options" /> <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> </div>
</template> </template>
<script setup> <script setup>
import { reactive, watch } from 'vue'; import { ref, onMounted, onUnmounted } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({
data: {
type: Array,
default: () => [],
},
});
const header = ['白名单企业', '产品名称', '黑名单企业', '产品名称']; //
const len = header.length; const chartConfig = ref({
const state = reactive({ attr: {
loading: false, w: 200, //
options: { h: 192, //
attr: { w: 200, h: 240 }, },
option: { option: {
header, header: ['投入品种类', '平台价格', '市场价格'],
dataset: [ dataset: [
// ['', '824', '', '824'], ['圆茄种苗', '0.3元/棵', '0.4元/棵'],
// ['', '824', '', '824'], ['高氮复合肥', '1850元/吨', '1980元/吨'],
// ['', '824', '', '824'], ['硫酸钾', '1250元/吨', '1380元/吨'],
// ['', '824', '', '824'], ['高氮复合肥', '1850元/吨', '1980元/吨'],
// ['', '824', '', '824'], ['西红柿种苗', '0.3元/棵', '0.4元/棵'],
// ['', '824', '', '824'],
// ['', '824', '', '824'],
], ],
index: false, //
columnWidth: [100, 100, 100, 100], headerBGC: 'rgba(53, 208, 192, 0.4)', //
align: new Array(len).fill('center'), oddRowBGC: 'rgba(0, 59, 81, 0.1)', //
rowNum: 5, evenRowBGC: 'rgba(10, 39, 50, 0.1)', //
waitTime: 5, headerHeight: 40, //
headerHeight: 40, columnWidth: [300, 200, 200], //
carousel: 'single', align: ['left', 'center', 'center'], //
headerBGC: 'rgba(53, 208, 192, 0.4)', rowNum: 4, //
oddRowBGC: 'rgba(0, 59, 81, 0.1)', waitTime: 3, // ()
evenRowBGC: 'rgba(10, 39, 50, 0.1)', carousel: 'single', //
}, hoverPause: true, //
}, },
}); });
const genBlankAndWihteList = (data = []) => { //
const rList = data.filter((row) => row.currentRating === 'r'); const currentDate = ref('2023年12月15日');
const bList = data.filter((row) => row.currentRating === 'b'); const scrollPosition = ref(0);
const len = rList.length >= bList.length ? rList.length : bList.length; const scrollContainer = ref(null);
let list = new Array(len).fill(['', '', '', '']); let animationFrameId = null;
rList.forEach((item, index) => {
let temp = new Array(4).fill(''); //
temp[0] = item.belongCompany; const startScrolling = () => {
temp[1] = item.comName; const scrollSpeed = -60; // px/s
list[index] = temp; let lastTime = 0;
});
bList.forEach((item, index) => { const scroll = (timestamp) => {
list[index][2] = item.belongCompany; if (!lastTime) lastTime = timestamp;
list[index][3] = item.comName; const delta = (timestamp - lastTime) / 1000; //
}); lastTime = timestamp;
return list;
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);
};
animationFrameId = requestAnimationFrame(scroll);
}; };
watch( onMounted(() => {
() => props.data, //
(val) => { const now = new Date();
if (!isEmpty(val)) { currentDate.value = `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}`;
state.options.option.dataset = genBlankAndWihteList(val);
state.loading = true;
}
},
{
deep: true,
immediate: true,
}
);
</script>
<style scoped lang="scss">
.board {
padding: 10px 0px;
&:deep(.row-item) { 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; 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 { .ceil {
&:nth-child(3), text-align: left;
&:nth-child(4) { padding-left: 16px;
color: $color-danger; }
}
}
.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> <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> </template>
<script setup>
import { reactive, watch } from 'vue';
import { isEmpty } from '@/utils';
const props = defineProps({ <script setup>
data: { import { reactive } from 'vue';
type: Array,
default: () => [], 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({ const state = reactive({
data: rawData,
option: { option: {
legend: {
show: false,
},
grid: { grid: {
left: '5%', top: '20%',
right: '5%', left: '3%',
right: '4%',
bottom: '5%', bottom: '5%',
top: '10%',
containLabel: true, 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: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow', type: 'shadow',
}, },
backgroundColor: 'rgba(18, 55, 85, 0.8);', backgroundColor: 'rgba(0,0,0,0.6);',
borderColor: '#35d0c0', borderColor: '#35d0c0',
formatter: (data) => { borderRadius: 8,
const params = data[0]; formatter: (params) => `
let str = `<div class="custom-echarts-tips"> <div style="font-weight:700;margin-bottom:5px;color:#fff;font-size: 16px;">${params[0].name}</div>
<span>${params.name}</span><br/> ${params
<span>${params.marker} ${params.data} 万元</span> .map(
</div>`; (p) => `
return str; <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> </script>

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<section class="_circle">{{ _circleNum }}</section> <section class="_circle">{{ _circleNum }}</section>
<section class="_text">{{ allNum }}万亩</section> <section class="_text">{{ allNum }}万亩</section>
</section> </section>
<section class="_right _scroll"> <section ref="scrollRef" class="_right _scroll">
<section <section
v-for="(item, i) in list" v-for="(item, i) in list"
:key="`right_${i}`" :key="`right_${i}`"
@ -23,23 +23,26 @@
</template> </template>
<script setup> <script setup>
import { ref, watch, computed } from 'vue'; import { ref, watch, computed, nextTick, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps({ const props = defineProps({
data: { data: {
type: Array, type: Array,
default: () => { default: () => [],
return [];
},
}, },
}); });
const list = ref([]); const list = ref([]);
const ac = ref(0); const ac = ref(0);
const allNum = ref(0); const allNum = ref(0);
const currNum = ref(0); const currNum = ref(0);
const scrollRef = ref(null);
let timer = null;
watch( watch(
() => props.data, () => props.data,
() => { () => {
allNum.value = 0;
list.value = props.data.map((v, i) => { list.value = props.data.map((v, i) => {
allNum.value += v.value; allNum.value += v.value;
return { return {
@ -49,29 +52,74 @@ watch(
}; };
}); });
list.value.sort((a, b) => b.value - a.value); list.value.sort((a, b) => b.value - a.value);
},
{ if (list.value.length) {
immediate: true, ac.value = list.value[0].id;
currNum.value = list.value[0].value;
scrollToActive();
startAutoScroll();
} }
},
{ immediate: true }
); );
function randomColor() { function randomColor() {
let obj = { return `rgb(${rand255()},${rand255()},${rand255()})`;
r: 0,
g: 0,
b: 0,
};
Object.keys(obj).forEach((key) => {
obj[key] = Math.floor(Math.random() * 256);
});
return `rgb(${obj.r},${obj.g},${obj.b})`;
} }
function rand255() {
return Math.floor(Math.random() * 256);
}
function handleAc(val) { function handleAc(val) {
ac.value = val; ac.value = val;
currNum.value = list.value.find((v) => v.id == val).value ?? 0; const target = list.value.find((v) => v.id == val);
currNum.value = target?.value ?? 0;
scrollToActive();
} }
function scrollToActive() {
nextTick(() => {
const container = scrollRef.value;
const activeEl = container?.querySelector('.list_item.active');
if (container && activeEl) {
const containerTop = container.scrollTop;
const containerHeight = container.clientHeight;
const elTop = activeEl.offsetTop;
const elHeight = activeEl.offsetHeight;
if (elTop < containerTop || elTop + elHeight > containerTop + containerHeight) {
container.scrollTop = elTop - containerHeight / 2 + elHeight / 2;
}
}
});
}
const _circleNum = computed(() => { const _circleNum = computed(() => {
let s = ((currNum.value / allNum.value) * 100).toFixed(1); if (!allNum.value) return '0%';
return s + '%'; return ((currNum.value / allNum.value) * 100).toFixed(1) + '%';
});
function startAutoScroll() {
stopAutoScroll();
let index = list.value.findIndex((item) => item.id === ac.value);
timer = setInterval(() => {
index = (index + 1) % list.value.length;
handleAc(list.value[index].id);
}, 2000);
}
function stopAutoScroll() {
if (timer) {
clearInterval(timer);
timer = null;
}
}
onMounted(() => {
startAutoScroll();
});
onBeforeUnmount(() => {
stopAutoScroll();
}); });
</script> </script>
@ -156,5 +204,8 @@ const _circleNum = computed(() => {
} }
} }
} }
._right::-webkit-scrollbar {
display: none;
}
} }
</style> </style>

View File

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

View File

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

View File

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

View File

@ -1,344 +0,0 @@
let selectedIndex = '';
let hoveredIndex = '';
option = getPie3D(
[
{
name: 'cc',
value: 47,
itemStyle: {
color: '#f77b66',
},
},
{
name: 'aa',
value: 44,
itemStyle: {
color: '#3edce0',
},
},
{
name: 'bb',
value: 32,
itemStyle: {
color: '#f94e76',
},
},
{
name: 'ee',
value: 16,
itemStyle: {
color: '#018ef1',
},
},
{
name: 'dd',
value: 23,
itemStyle: {
color: '#9e60f9',
},
},
],
0.59
);
// 生成扇形的曲面参数方程
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
// 计算
const midRatio = (startRatio + endRatio) / 2;
const startRadian = startRatio * Math.PI * 2;
const endRadian = endRatio * Math.PI * 2;
const midRadian = midRatio * Math.PI * 2;
// 如果只有一个扇形,则不实现选中效果。
if (startRatio === 0 && endRatio === 1) {
// eslint-disable-next-line no-param-reassign
isSelected = false;
}
// 通过扇形内径/外径的值,换算出辅助参数 k默认值 1/3
// eslint-disable-next-line no-param-reassign
k = typeof k !== 'undefined' ? k : 1 / 3;
// 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0
const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
// 计算高亮效果的放大比例(未高亮,则比例为 1
const 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(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(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(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;
}
// 当前图形的高度是Z根据h每个value的值决定的
return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
},
};
}
// 生成模拟 3D 饼图的配置项
function getPie3D(pieData, internalDiameterRatio) {
const series = [];
// 总和
let sumValue = 0;
let startValue = 0;
let endValue = 0;
const legendData = [];
const k =
typeof internalDiameterRatio !== 'undefined'
? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
: 1 / 3;
// 为每一个饼图数据,生成一个 series-surface 配置
for (let i = 0; i < pieData.length; i += 1) {
sumValue += pieData[i].value;
const 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,
},
};
if (typeof pieData[i].itemStyle !== 'undefined') {
const { itemStyle } = pieData[i];
// eslint-disable-next-line no-unused-expressions
typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
// eslint-disable-next-line no-unused-expressions
typeof pieData[i].itemStyle.opacity !== 'undefined'
? (itemStyle.opacity = pieData[i].itemStyle.opacity)
: null;
seriesItem.itemStyle = itemStyle;
}
series.push(seriesItem);
}
// 使用上一次遍历时,计算出的数据和 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;
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,
// 我这里做了一个处理使除了第一个之外的值都是10
series[i].pieData.value === series[0].pieData.value ? 35 : 10
);
startValue = endValue;
legendData.push(series[i].name);
}
// 准备待返回的配置项,把准备好的 legendData、series 传入。
const option = {
// 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: false,
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,
};
return option;
}
// 修正取消高亮失败的 bug
// 监听 mouseover近似实现高亮放大效果
myChart.on('mouseover', function (params) {
// 准备重新渲染扇形所需的参数
let isSelected;
let isHovered;
let startRatio;
let endRatio;
let k;
let i;
// 如果触发 mouseover 的扇形当前已高亮,则不做操作
if (hoveredIndex === params.seriesIndex) {
return;
// 否则进行高亮及必要的取消高亮操作
} else {
// 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)
if (hoveredIndex !== '') {
// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。
isSelected = option.series[hoveredIndex].pieStatus.selected;
isHovered = false;
startRatio = option.series[hoveredIndex].pieData.startRatio;
endRatio = option.series[hoveredIndex].pieData.endRatio;
k = option.series[hoveredIndex].pieStatus.k;
i = option.series[hoveredIndex].pieData.value === option.series[0].pieData.value ? 35 : 10;
// 对当前点击的扇形,执行取消高亮操作(对 option 更新)
option.series[hoveredIndex].parametricEquation = getParametricEquation(
startRatio,
endRatio,
isSelected,
isHovered,
k,
i
);
option.series[hoveredIndex].pieStatus.hovered = isHovered;
// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
hoveredIndex = '';
}
// 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)
if (params.seriesName !== 'mouseoutSeries') {
// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
isSelected = option.series[params.seriesIndex].pieStatus.selected;
isHovered = true;
startRatio = option.series[params.seriesIndex].pieData.startRatio;
endRatio = option.series[params.seriesIndex].pieData.endRatio;
k = option.series[params.seriesIndex].pieStatus.k;
// 对当前点击的扇形,执行高亮操作(对 option 更新)
option.series[params.seriesIndex].parametricEquation = getParametricEquation(
startRatio,
endRatio,
isSelected,
isHovered,
k,
option.series[params.seriesIndex].pieData.value + 5
);
option.series[params.seriesIndex].pieStatus.hovered = isHovered;
// 记录上次高亮的扇形对应的系列号 seriesIndex
hoveredIndex = params.seriesIndex;
}
// 使用更新后的 option渲染图表
myChart.setOption(option);
}
});
// 修正取消高亮失败的 bug
myChart.on('globalout', function () {
if (hoveredIndex !== '') {
// 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
isSelected = option.series[hoveredIndex].pieStatus.selected;
isHovered = false;
k = option.series[hoveredIndex].pieStatus.k;
startRatio = option.series[hoveredIndex].pieData.startRatio;
endRatio = option.series[hoveredIndex].pieData.endRatio;
// 对当前点击的扇形,执行取消高亮操作(对 option 更新)
i = option.series[hoveredIndex].pieData.value === option.series[0].pieData.value ? 35 : 10;
option.series[hoveredIndex].parametricEquation = getParametricEquation(
startRatio,
endRatio,
isSelected,
isHovered,
k,
i
);
option.series[hoveredIndex].pieStatus.hovered = isHovered;
// 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
hoveredIndex = '';
}
// 使用更新后的 option渲染图表
myChart.setOption(option);
});

View File

@ -1,11 +1,10 @@
<template> <template>
<custom-echart-bar :chart-data="state.data" height="100%" :option="state.option" /> <custom-echart-bar :chart-data="dataList" height="100%" :option="state.option" />
</template> </template>
<script setup> <script setup>
import { reactive } from 'vue'; import { onMounted, reactive } from 'vue';
const state = reactive({ const dataList = reactive([
data: [
{ {
name: '其他', name: '其他',
value: 3500, value: 3500,
@ -26,7 +25,47 @@ const state = reactive({
name: '茶叶', name: '茶叶',
value: 12000, value: 12000,
}, },
], ]);
let titles = reactive([]);
let values = reactive([]);
const max = Math.max(...dataList);
const maxData = dataList.map((item, index) => {
return {
value: [max, index],
name: titles[index],
};
});
const barData = dataList.map((item, index) => {
return {
value: [item, index],
name: titles[index],
};
});
const getValue = () => {
let arr = [];
for (let i = 0; i < dataList.length; i++) {
arr.push(dataList[i].value);
}
return arr;
};
const getName = () => {
let arr = [];
for (let i = 0; i < dataList.length; i++) {
arr.push(dataList[i].name);
}
return arr;
};
onMounted(() => {
titles = getName();
values = getValue();
console.log(titles);
console.log(values);
});
const state = reactive({
option: { option: {
grid: { grid: {
left: '5%', left: '5%',
@ -36,11 +75,13 @@ const state = reactive({
containLabel: true, containLabel: true,
}, },
tooltip: { tooltip: {
show: false,
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow', type: 'shadow',
}, },
backgroundColor: 'rgba(18, 55, 85, 0.8);', className: 'custom-tooltip-container', //
backgroundColor: 'rgba(0,0,0,0.5)',
borderColor: '#35d0c0', borderColor: '#35d0c0',
formatter: (data) => { formatter: (data) => {
const params = data[0]; const params = data[0];
@ -53,19 +94,20 @@ const state = reactive({
}, },
barStyle: { barStyle: {
barWidth: 14, barWidth: 14,
showBackground: true,
itemStyle: { itemStyle: {
borderWidth: 14, borderWidth: 14,
borderRadius: [8, 8, 8, 8], // borderRadius: [10, 10, 10, 10], //
}, },
color: { color: {
type: 'linear', type: 'linear',
x: 0, x: 0,
y: 0, y: 0,
x2: 0, x2: 1,
y2: 1, y2: 0,
colorStops: [ colorStops: [
{ offset: 0, color: '#35D0C0' }, { offset: 0, color: 'rgba(1,254,253,0)' },
{ offset: 1, color: '#35D0C0' }, { offset: 1, color: '#01fefd' },
], ],
global: false, global: false,
}, },
@ -76,13 +118,13 @@ const state = reactive({
show: false, show: false,
lineStyle: { lineStyle: {
type: 'dashed', type: 'dashed',
color: '#E5E5E5', color: '#eee',
}, },
}, },
axisLabel: { axisLabel: {
show: false, show: false,
textStyle: { textStyle: {
color: '#424242', color: '#ffffff',
}, },
}, },
show: false, show: false,
@ -93,11 +135,16 @@ const state = reactive({
show: false, show: false,
}, },
}, },
yAxis: { yAxis: [
{
// title
type: 'category', type: 'category',
data: ['茶叶', '核桃', '甘蔗', '烟叶', '其他'], data: ['茶叶', '核桃', '甘蔗', '烟叶', '其他'],
axisLabel: { axisLabel: {
color: '#fff', textStyle: {
color: '#ffffff',
fontSize: '16',
},
}, },
axisTick: { axisTick: {
show: false, show: false,
@ -109,12 +156,123 @@ const state = reactive({
show: false, show: false,
}, },
}, },
{
//
type: 'category',
inverse: true,
offset: 10,
axisTick: 'none',
axisLine: 'none',
show: true,
axisLabel: {
textStyle: {
color: '#ffffff',
fontSize: '16',
},
formatter: function (value) {
let str = '{title|' + value + '吨}';
return str;
},
rich: {
title: {
color: '#fff',
fontSize: 16,
},
},
},
data: [12000, 8000, 6000, 4000, 3500],
},
],
series: [ series: [
{ {
name: '值',
type: 'bar', type: 'bar',
// barWidth: '40%', // zlevel: 1,
// barMinHeight: 2, // barBorderRadius: 10,
// barGap: '20%', // itemStyle: {
borderRadius: [10, 10, 10, 10],
color: 'rgba(100, 200, 255, 0.3)',
},
backgroundStyle: {
color: 'rgba(100, 200, 255, 0.3)',
borderRadius: [10, 10, 10, 10], // barBorderRadius
},
barWidth: 20,
data: values,
label: {
position: [0, -16],
align: 'left',
show: true,
formatter: (params) => {
return params.name;
},
},
barMaxWidth: 40,
markLine: {
label: {
color: '#26a69a',
},
},
},
{
name: '背景',
type: 'bar',
barWidth: 20,
barGap: '-100%',
data: maxData,
barBorderRadius: 30,
itemStyle: {
normal: {
color: 'rgba(105,131,242,.3)',
borderRadius: 10,
},
},
label: {
show: false,
},
barMaxWidth: 40,
markLine: {
label: {
color: '#26a69a',
},
},
},
{
name: '内圆',
type: 'scatter',
hoverAnimation: false,
data: barData,
yAxisIndex: 0,
symbolSize: 22,
itemStyle: {
normal: {
color: '#26a69a',
opacity: 1,
},
},
zlevel: 2,
label: {
show: false,
},
},
{
name: '外圆',
type: 'scatter',
hoverAnimation: false,
data: barData,
yAxisIndex: 0,
symbolSize: 28,
symbol: `path://M512 960C264.576 960 64 759.424 64 512S264.576 64 512 64s448 200.576 448 448-200.576 448-448 448z m0-268.8a179.2 179.2 0 1 0 0-358.4 179.2 179.2 0 0 0 0 358.4z`,
itemStyle: {
color: 'rgb(255, 255, 255)',
opacity: 1,
borderColor: 'rgba(44, 111, 226, 0.2)',
borderWidth: 2,
},
zlevel: 3,
label: {
show: false,
},
}, },
], ],
}, },

View File

@ -20,7 +20,7 @@
</div> </div>
</div> </div>
<section class="line_info" :style="{ '--top': info.show ? '18vh' : '140vh' }"> <section class="line_info" :style="{ '--top': info.show ? '18vh' : '140vh' }">
<i class="el-icon-close" @click="handleCloseInfo">X</i> <i class="el-icon-close" @click="handleCloseInfo"></i>
<section> <section>
<section class="info_box"> <section class="info_box">
<div> <div>
@ -59,8 +59,8 @@
<div class="_time">{{ item.time }}</div> <div class="_time">{{ item.time }}</div>
</div> </div>
<div style="text-align: center"> <div style="text-align: center">
<!-- <img src="../../../assets/1.png" style="width: 30%" /> --> <img src="../../../assets/images/trace/testReport.png" style="width: 30%" />
<div>测报告</div> <div>测报告</div>
</div> </div>
</section> </section>
</section> </section>
@ -104,7 +104,7 @@ function handleCloseInfo() {
display: grid; display: grid;
grid-template-columns: 25% 40% 18% 17%; grid-template-columns: 25% 40% 18% 17%;
background-color: rgba(53, 208, 192, 0.3); background-color: rgba(53, 208, 192, 0.3);
color: #c5d0d4; color: #ffffff;
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
border-radius: 4px; border-radius: 4px;
@ -149,10 +149,10 @@ function handleCloseInfo() {
} }
.line_info { .line_info {
position: fixed; position: fixed;
padding: 32px 10px 20px 20px; padding: 24px 10px 16px 16px;
left: 30%; right: 30%;
top: var(--top); top: var(--top);
max-height: 74vh; max-height: 85vh;
width: 400px; width: 400px;
color: #fff; color: #fff;
background-color: rgba(0, 0, 0, 0.2); background-color: rgba(0, 0, 0, 0.2);
@ -167,14 +167,18 @@ function handleCloseInfo() {
} }
.el-icon-close { .el-icon-close {
position: absolute; position: absolute;
right: 8px; right: -40px;
top: 8px; top: 0px;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
width: 30px;
height: 30px;
background-image: url(../../../assets/images/trace/delete.png);
background-size: contain;
} }
> section { > section {
padding-right: 10px; padding-right: 10px;
max-height: calc(74vh - 100px); max-height: calc(85vh - 100px);
overflow-y: auto; overflow-y: auto;
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 6px; width: 6px;

View File

@ -51,7 +51,7 @@ onMounted(() => {});
} }
._value { ._value {
> span { > span {
font-size: 24px; font-size: 20px;
} }
} }
img { img {

View File

@ -36,7 +36,7 @@ const option = reactive({
autoItemHeight: 2, autoItemHeight: 2,
grid3D: { grid3D: {
show: false, show: false,
boxHeight: 5, boxHeight: 3,
top: '0', top: '0',
left: '-20%', left: '-20%',
viewControl: { viewControl: {

View File

@ -51,7 +51,7 @@ import { ref } from 'vue';
._left { ._left {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: flex-start;
align-items: center; align-items: center;
color: #fff; color: #fff;
font-size: 18px; font-size: 18px;

View File

@ -8,6 +8,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import * as echarts from 'echarts';
import geoJsonData from './china.json'; // ; import geoJsonData from './china.json'; // ;
const route = useRoute(); const route = useRoute();
const props = defineProps({ const props = defineProps({
@ -26,6 +27,33 @@ const chinaGeoCoordMap = ref({
香港: [114.133834, 22.381374], 香港: [114.133834, 22.381374],
四川: [104.063707, 30.658753], 四川: [104.063707, 30.658753],
河北: [114.511072, 38.054693], 河北: [114.511072, 38.054693],
北京: [116.46, 39.92],
浙江: [120.19, 30.26],
山东: [117, 36.65],
福建: [119.3, 26.08],
上海: [121.48, 31.22],
重庆: [106.54, 29.59],
江西: [115.89, 28.68],
山西: [112.53, 37.87],
黑龙江: [126.63, 45.75],
陕西: [108.95, 34.27],
辽宁: [123.38, 41.8],
海南: [110.35, 20.02],
湖南: [113, 28.21],
宁夏: [106.27, 38.47],
湖北: [114.31, 30.52],
内蒙古: [111.65, 40.82],
天津: [117.2, 39.13],
贵州: [106.71, 26.57],
甘肃: [103.73, 36.03],
江苏: [118.78, 32.04],
吉林: [125.35, 43.88],
河南: [113.65, 34.76],
青海: [101.74, 36.56],
安徽: [117.27, 31.86],
广西: [108.33, 22.84],
西藏: [91.11, 29.97],
新疆: [87.68, 43.77],
}); });
const chinaDatas = ref([ const chinaDatas = ref([
[{ name: '云南', value: 382 }], [{ name: '云南', value: 382 }],
@ -33,7 +61,35 @@ const chinaDatas = ref([
[{ name: '香港', value: 9256 }], [{ name: '香港', value: 9256 }],
[{ name: '四川', value: 1256 }], [{ name: '四川', value: 1256 }],
[{ name: '河北', value: 382 }], [{ name: '河北', value: 382 }],
[{ name: '北京', value: 88 }],
[{ name: '浙江', value: 87 }],
[{ name: '山东', value: 87 }],
[{ name: '福建', value: 87 }],
[{ name: '上海', value: 87 }],
[{ name: '重庆', value: 87 }],
[{ name: '江西', value: 86 }],
[{ name: '山西', value: 84 }],
[{ name: '黑龙江', value: 83 }],
[{ name: '陕西', value: 83 }],
[{ name: '辽宁', value: 82 }],
[{ name: '海南', value: 82 }],
[{ name: '湖南', value: 82 }],
[{ name: '宁夏', value: 82 }],
[{ name: '湖北', value: 81 }],
[{ name: '内蒙古', value: 81 }],
[{ name: '天津', value: 80 }],
[{ name: '贵州', value: 80 }],
[{ name: '甘肃', value: 80 }],
[{ name: '江苏', value: 80 }],
[{ name: '吉林', value: 80 }],
[{ name: '河南', value: 79 }],
[{ name: '青海', value: 79 }],
[{ name: '广西', value: 78 }],
[{ name: '安徽', value: 77 }],
[{ name: '新疆', value: 76 }],
[{ name: '西藏', value: 76 }],
]); ]);
//
const convertData = (data) => { const convertData = (data) => {
var res = []; var res = [];
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
@ -73,6 +129,10 @@ const getSeries = () => {
width: 1, //线 width: 1, //线
opacity: 1, //线 opacity: 1, //线
curveness: 0.3, //线 curveness: 0.3, //线
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#0096FF' },
{ offset: 1, color: '#00FF00' },
]),
}, },
}, },
data: convertData(item[1]), data: convertData(item[1]),
@ -180,7 +240,8 @@ const chartsData = reactive({
}, },
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
backgroundColor: 'rgba(18, 55, 85, 0.8);', className: 'custom-tooltip-container', //
backgroundColor: 'rgba(0,0,0,0.5)',
borderColor: '#35d0c0', borderColor: '#35d0c0',
showDelay: 0, showDelay: 0,
hideDelay: 0, hideDelay: 0,
@ -189,6 +250,7 @@ const chartsData = reactive({
extraCssText: 'z-index:100', extraCssText: 'z-index:100',
formatter: function (params, ticket, callback) { formatter: function (params, ticket, callback) {
let val = 0; let val = 0;
let str = `<div class="custom-echarts-tips"></div>`;
if (typeof params.value == 'number') { if (typeof params.value == 'number') {
val = params.value; val = params.value;
} else { } else {
@ -197,7 +259,7 @@ const chartsData = reactive({
// //
var res = ''; var res = '';
var name = params.name; var name = params.name;
res = "<span style='color:#fff;'>" + name + '</span><br/>数据:' + val; res = `<span style='color:#fff;'> ${name}<br/>数据:${val}</span>`;
return res; return res;
}, },
}, },
@ -211,15 +273,27 @@ const chartsData = reactive({
show: false, show: false,
}, },
}, },
roam: true, // roam: false, //
itemStyle: { itemStyle: {
normal: { normal: {
color: 'rgba(75,255,180,0.3)', // color: 'rgba(75,255,180,0.5)', //
borderColor: '#4bffb4', //线00fcff 516a89 borderColor: '#4bffb4', //线00fcff 516a89
borderWidth: 1, borderWidth: 1,
areaColor: {
type: 'radial', //
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{ offset: 0, color: 'rgba(17,217,245,0.2)' },
{ offset: 1, color: 'rgba(10,209,231,0.6)' },
],
},
shadowColor: '#182f68',
shadowOffsetY: 2,
}, },
emphasis: { emphasis: {
color: 'rgba(75,255,180,0.5)', // color: 'rgba(10,209,231,0.6)', //
borderWidth: 2, borderWidth: 2,
}, },
}, },

View File

@ -23,7 +23,7 @@
:down-title="'1号仓库'" :down-title="'1号仓库'"
:label-field="'label'" :label-field="'label'"
:value-field="'value'" :value-field="'value'"
:down-width="'100px'" :down-width="''"
:options="[ :options="[
{ label: '1号仓库', value: '530926' }, { label: '1号仓库', value: '530926' },
{ label: '2号仓库', value: '42611' }, { label: '2号仓库', value: '42611' },

File diff suppressed because it is too large Load Diff

1074
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ VITE_PORT = 9526
VITE_APP_MIAN = 'daimp-front-main' VITE_APP_MIAN = 'daimp-front-main'
VITE_APP_MIAN_URL = 'http://localhost:9000' VITE_APP_MIAN_URL = 'http://localhost:9000'
VITE_APP_NAME = 'sub-operation-service' VITE_APP_NAME = 'sub-operation-service'
VITE_APP_BASE_API = '/platform' VITE_APP_BASE_API = '/apis'
VITE_APP_BASE_URL = 'http://192.168.18.99:88' VITE_APP_BASE_URL = 'http://192.168.18.99:88'
VITE_APP_UPLOAD_API = '/uploadApis' VITE_APP_UPLOAD_API = '/uploadApis'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:9300' VITE_APP_UPLOAD_URL = 'http://192.168.18.99:9300'

View File

@ -0,0 +1 @@
nodeLinker: node-modules

75
sub-operation-service/auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,75 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

54
sub-operation-service/components.d.ts vendored Normal file
View File

@ -0,0 +1,54 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
CenterMap: typeof import('./src/components/centerMap.vue')['default']
CodeDialog: typeof import('./src/components/code-dialog/index.vue')['default']
copy: typeof import('./src/components/custom-scroll-title copy/index.vue')['default']
CostomImg: typeof import('./src/components/costomImg.vue')['default']
CustomBack: typeof import('./src/components/customBack.vue')['default']
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
CustomEchartBar: typeof import('./src/components/custom-echart-bar/index.vue')['default']
CustomEchartBubble: typeof import('./src/components/custom-echart-bubble/index.vue')['default']
CustomEchartColumnLine: typeof import('./src/components/custom-echart-column-line/index.vue')['default']
CustomEchartHyalineCake: typeof import('./src/components/custom-echart-hyaline-cake/index.vue')['default']
CustomEchartLine: typeof import('./src/components/custom-echart-line/index.vue')['default']
CustomEchartLineLine: typeof import('./src/components/custom-echart-line-line/index.vue')['default']
CustomEchartMaps: typeof import('./src/components/custom-echart-maps/index.vue')['default']
CustomEchartMixin: typeof import('./src/components/custom-echart-mixin/index.vue')['default']
CustomEchartPictorialBar: typeof import('./src/components/custom-echart-pictorial-bar/index.vue')['default']
CustomEchartPie: typeof import('./src/components/custom-echart-pie/index.vue')['default']
CustomEchartPie3d: typeof import('./src/components/custom-echart-pie-3d/index.vue')['default']
CustomEchartPieGauge: typeof import('./src/components/custom-echart-pie-gauge/index.vue')['default']
CustomEchartRadar: typeof import('./src/components/custom-echart-radar/index.vue')['default']
CustomEchartScatterBlister: typeof import('./src/components/custom-echart-scatter-blister/index.vue')['default']
CustomEchartTriangle: typeof import('./src/components/custom-echart-triangle/index.vue')['default']
CustomEchartWaterDroplet: typeof import('./src/components/custom-echart-water-droplet/index.vue')['default']
CustomEchartWordCloud: typeof import('./src/components/custom-echart-word-cloud/index.vue')['default']
CustomIframe: typeof import('./src/components/custom-iframe/index.vue')['default']
CustomImportExcel: typeof import('./src/components/custom-import-excel/index.vue')['default']
CustomProgress: typeof import('./src/components/customProgress.vue')['default']
CustomRankList: typeof import('./src/components/custom-rank-list/index.vue')['default']
CustomRichEditor: typeof import('./src/components/custom-rich-editor/index.vue')['default']
CustomScrollBoard: typeof import('./src/components/custom-scroll-board/index.vue')['default']
CustomScrollTitle: typeof import('./src/components/custom-scroll-title/index.vue')['default']
'CustomScrollTitle copy': typeof import('./src/components/custom-scroll-title copy/index.vue')['default']
CustomTableOperate: typeof import('./src/components/custom-table-operate/index.vue')['default']
CustomTableTree: typeof import('./src/components/custom-table-tree/index.vue')['default']
IndexBak: typeof import('./src/components/page-menu/index-bak.vue')['default']
NewHyalineCake: typeof import('./src/components/custom-echart-hyaline-cake/new-hyaline-cake.vue')['default']
PageLayout: typeof import('./src/components/page-layout/index.vue')['default']
PageMenu: typeof import('./src/components/page-menu/index.vue')['default']
PagePagination: typeof import('./src/components/page-pagination/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SubTop: typeof import('./src/components/subTop.vue')['default']
UpFile: typeof import('./src/components/custom-rich-editor/upFile.js')['default']
UpImg: typeof import('./src/components/upImg.vue')['default']
}
}

View File

@ -1,5 +1,5 @@
{ {
"name": "sub-operation-service", "name": "digital-agriculture-screen",
"private": true, "private": true,
"version": "0.0.1", "version": "0.0.1",
"type": "module", "type": "module",
@ -7,7 +7,6 @@
"dev": "vite --mode development", "dev": "vite --mode development",
"build": "vite build --mode production", "build": "vite build --mode production",
"test": "vite build --mode test", "test": "vite build --mode test",
"pre": "vite build --mode pre",
"preview": "vite preview", "preview": "vite preview",
"format": "prettier --write 'src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}'", "format": "prettier --write 'src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}'",
"eslint": "npx eslint --init", "eslint": "npx eslint --init",
@ -18,11 +17,18 @@
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"@smallwei/avue": "^3.6.2", "@smallwei/avue": "^3.6.2",
"@vuemap/vue-amap": "^2.0",
"@vuemap/vue-amap-loca": "^2.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"animate.css": "^4.1.1",
"axios": "^1.6.5", "axios": "^1.6.5",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"echarts-gl": "^2.0.9",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0",
"element-plus": "^2.7.2", "element-plus": "^2.7.2",
"hls.js": "^1.6.2",
"js-base64": "^3.7.6", "js-base64": "^3.7.6",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.30.1", "moment": "^2.30.1",
@ -31,9 +37,11 @@
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"splitpanes": "^4.0.3",
"vue": "^3.3.11", "vue": "^3.3.11",
"vue-router": "^4.2.5" "vue-cesium": "^3.2.9",
"vue-echarts": "^7.0.3",
"vue-router": "^4.2.5",
"vue3-scroll-seamless": "^1.0.6"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.23.7", "@babel/core": "^7.23.7",

View File

@ -0,0 +1,19 @@
import request from '@/utils/axios';
//农资
//获取农资分类查询数据
export function transaction(params = {}) {
return request('goods/business/category/transactionType?type=1', {
method: 'GET',
params,
});
}
//获取农资列表数据
export function agriculturalList(params) {
return request('goods/business/category/transactionGoodInfo', {
method: 'GET',
params,
});
}

View File

@ -0,0 +1,23 @@
export default {
// 模拟获取商品列表
getProducts: () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ id: 1, name: '耿马绿色蔬菜', imageUrl: 'images/brand/11.png' },
{ id: 2, name: '云南高山茶', imageUrl: 'images/brand/12.png' },
{ id: 3, name: '新疆大枣', imageUrl: 'images/brand/13.png' },
{ id: 4, name: '东北大米', imageUrl: 'images/brand/14.png' },
{ id: 5, name: '山东苹果', imageUrl: 'images/brand/15.png' },
{ id: 6, name: '四川泡菜', imageUrl: 'images/brand/16.png' },
{ id: 7, name: '江苏阳澄湖大闸蟹', imageUrl: 'images/brand/11.png' },
{ id: 8, name: '海南椰子', imageUrl: 'images/brand/12.png' },
{ id: 9, name: '广东早茶', imageUrl: 'images/brand/13.png' },
{ id: 10, name: '北京烤鸭', imageUrl: 'images/brand/14.png' },
{ id: 11, name: '西藏青稞酒', imageUrl: 'images/brand/15.png' },
{ id: 12, name: '青海牦牛肉', imageUrl: 'images/brand/16.png' },
]);
}, 500); // 模拟网络延迟
});
},
};

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More