智慧种植监测

This commit is contained in:
lzc 2025-03-21 16:48:39 +08:00
parent 141a3553e0
commit 1c228277b4
29 changed files with 730 additions and 214 deletions

View File

@ -1,86 +0,0 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script>
import { ref, reactive, watchEffect } from 'vue';
import { cloneDeep } from 'lodash';
import { useEcharts } from '../../hooks/useEcharts';
export default {
name: 'CustomEchartBarLine',
props: {
chartData: {
type: Array,
default: () => [],
},
option: {
type: Object,
default: () => ({}),
},
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: 'calc(100vh - 78px)',
},
},
setup(props) {
const chartRef = ref(null);
const { setOptions } = useEcharts(chartRef);
const option = reactive({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
label: {
show: true,
backgroundColor: '#333',
},
},
},
xAxis: {
type: 'category',
data: [],
},
yAxis: [],
series: [
{
name: 'bar',
type: 'bar',
data: [],
},
],
});
watchEffect(() => {
props.chartData && initCharts();
});
function initCharts() {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
let typeArr = Array.from(new Set(props.chartData.map((item) => item.type)));
let xAxisData = Array.from(new Set(props.chartData.map((item) => item.name)));
let seriesData = [];
typeArr.forEach((type, index) => {
let obj = { name: type };
let chartArr = props.chartData.filter((item) => type === item.type);
obj['data'] = chartArr.map((item) => item.value);
obj['type'] = chartArr[0].seriesType;
obj['barWidth'] = chartArr[0].barWidth;
obj['itemStyle'] = chartArr[0].itemStyle;
seriesData.push(obj);
});
option.series = seriesData;
option.xAxis.data = xAxisData;
option.yAxis = props.option.yAxis;
setOptions(option);
}
return { chartRef };
},
};
</script>

View File

@ -9,7 +9,6 @@ import CustomEchartBar from './custom-echart-bar';
import CustomEchartPie from './custom-echart-pie';
import CustomEchartLine from './custom-echart-line';
import CustomEchartMixin from './custom-echart-mixin';
import CustomEchartBarLine from './custom-echart-bar-line';
import customEchartPictorialBar from './custom-echart-pictorial-bar';
import CustomEchartLineLine from './custom-echart-line-line';
import CustomEchartBubble from './custom-echart-bubble';
@ -27,7 +26,6 @@ export {
CustomEchartPie,
CustomEchartLine,
CustomEchartMixin,
CustomEchartBarLine,
customEchartPictorialBar,
CustomEchartLineLine,
CustomEchartBubble,

View File

@ -466,3 +466,18 @@ export const endDate = (num = 0, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss
if (typeof num === 'string') return dayjs(num).endOf(type).format(formater);
return num === 0 ? dayjs().endOf(type).format(formater) : dayjs().subtract(num, type).endOf(type).format(formater);
};
/**
* @Title: 生成随机数
* @param len
* @returns
*/
export const randomNumber = (len) => {
let randomlen = len ? len : 10;
const chars = '0123456789';
let result = '';
for (let i = 0; i < randomlen; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};

View File

@ -1,8 +1,7 @@
<template>
<div style="margin-top: 16px">
<!-- <el-dialog v-model="isShowVal" title="种植阶段详情" width="1000" center @closed="stageClose"> -->
<el-text class="mx-1" size="large">种植阶段详情</el-text>
<div style="margin-top: 16px">
<el-dialog v-model="isShowVal" title="种植阶段详情" width="1000" center @closed="stageClose">
<!-- <div style="margin-top: 16px"> -->
<avue-crud
ref="stateCrudRef"
v-model="stageState.form"
@ -37,8 +36,8 @@
<custom-table-operate :actions="stageState.options.actions" :data="scope" />
</template>
</avue-crud>
<!-- </el-dialog> -->
</div>
<!-- </div> -->
</el-dialog>
</div>
</template>
<script setup>
@ -90,7 +89,7 @@ let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
// console.info('loadList', props);
getStageList();
}
};

View File

@ -34,7 +34,6 @@
</avue-crud>
<stageList :is-show="stageShow" :row-original="state.currentRow" @close="stageHide"></stageList>
<el-text class="mx-1" size="large">种植阶段详情</el-text>
</div>
</template>
<script setup>
@ -136,6 +135,11 @@ const state = reactive({
icon: 'delete',
event: ({ row }) => rowDel(row),
},
{
name: '种植阶段',
icon: 'Operation',
event: ({ row }) => rowStageView(row),
},
],
},
pageData: {
@ -320,10 +324,13 @@ const rowDel = (row, index, done) => {
const rowClick = (row) => {
state.currentRow = { ...row };
stageShow.value = true;
console.info('rowClick', state.currentRow);
};
const stageHide = () => {
stageShow.value = false;
};
const rowStageView = (row, index, done) => {
stageShow.value = true;
};
</script>

View File

@ -83,7 +83,7 @@ let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
// console.info('loadList', props);
getList();
}
};

View File

@ -0,0 +1,181 @@
<template>
<div class="inputs-list-warp">
<el-row v-if="!disabledVal" :gutter="20">
<template v-for="(n, index) in inputsList" :key="index">
<el-col :span="10">
<el-form-item label="投入品" label-width="80px">
<el-cascader v-model="n.id" :options="materialTypes" :props="{ value: 'id', label: 'dataName', checkStrictly: true }" />
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="预估投入量" label-width="100px">
<div class="right-warp">
<div class="right-item">
<el-input-number v-model="n.num" :min="1">
<template #suffix>
<span>{{ n.unit }}</span>
</template>
</el-input-number>
</div>
<div class="right-item right-do">
<el-icon :size="'20'" :color="color" @click="addVal">
<CirclePlus />
</el-icon>
<el-icon :size="'20'" :color="color" @click="delVal(index)">
<Remove />
</el-icon>
</div>
</div>
</el-form-item>
</el-col>
</template>
</el-row>
<el-row v-else :gutter="20" class="read-only">
<template v-for="(m, indexm) in listData" :key="indexm">
<el-col :span="16">
<el-cascader
v-model="m.id"
:options="materialTypes"
style="width: 150px"
disabled
:props="{ value: 'id', label: 'dataName', checkStrictly: true }"
/>
</el-col>
<el-col :span="8">
<span>{{ m.num }}</span>
<span>{{ m.unit }}</span>
</el-col>
</template>
</el-row>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import { useApp } from '@/hooks';
import inputSuppliesApis from '@/apis/inputSuppliesApi';
import { randomNumber } from '@/utils/index';
import { isEmpty } from '@/utils';
const app = useApp();
const emit = defineEmits(['']);
const props = defineProps({
isDisabled: {
type: Boolean,
default: false,
},
list: {
type: Array,
default: () => {
return [];
},
},
});
const { getMaterailTypes } = inputSuppliesApis;
let inputsList = reactive([]);
const materialTypes = reactive([]);
async function getTypes() {
let res = await getMaterailTypes();
if (res.code == 200) {
materialTypes.push(...res.data);
console.log('types', materialTypes);
}
}
let listData = reactive([]);
let disabledVal = ref(false);
onMounted(() => {
getTypes();
});
watch(
() => (props.isDisabled, props.list),
() => {
disabledVal.value = props.isDisabled;
inputsList = props.list.length > 0 ? props.list : reactive([{ id: '', num: 1, unit: 'ml' }]);
listData = [];
listData = props.list;
},
{
deep: true,
immediate: true,
}
);
const addVal = () => {
// let Id = randomNumber(20);
// console.info('id', Id);
inputsList.push({ id: '', num: 1, unit: 'ml' });
};
const delVal = (index) => {
if (index != undefined) {
if (inputsList.length < 2) {
return app.$message.error('请保留至少一个投入品');
}
inputsList.shift(index, 1);
}
};
const saveList = () => {
let list = [];
if (inputsList.length < 2) {
if (inputsList[0].id == '' || !inputsList[0].id) {
return app.$message.error('请选择投入品');
}
}
list = inputsList.filter((m) => {
return !isEmpty(m.id);
});
return list;
};
defineExpose({
saveList,
});
</script>
<style lang="scss" scoped>
.inputs-list-warp {
border-radius: 4px;
background-color: #ffffff;
.right-warp {
display: inline-flex;
justify-content: space-around;
.right-item {
display: inline-block;
vertical-align: middle;
}
.right-do {
padding-left: 16px;
::v-deep() {
.el-icon {
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px dashed #e6e6e6;
margin: 0 16px 0 0;
cursor: pointer;
}
}
}
}
.read-only {
font-size: 12px !important;
::v-deep() {
.el-input__wrapper {
box-shadow: 0 0 0 0 red !important;
background: transparent !important;
}
.el-input__inner {
font-size: 12px !important;
}
.el-input__suffix {
display: none !important;
}
}
}
}
</style>

View File

@ -27,6 +27,22 @@
<el-tag size="small">{{ stageObj[row.stage] }}</el-tag>
</template>
<template #workTime="{ row }">
{{ row.operationDate }}
</template>
<template #input-form="{ row }">
<div style="display: flex; flex-direction: column; height: 100%">
<inputs ref="refinputs" :list="stageState.currentRow.input" :is-disabled="false"></inputs>
</div>
</template>
<template #input="{ row }">
<div style="display: flex; flex-direction: column; height: 100%">
<inputs ref="refinputs" :is-disabled="true" :list="row.input"></inputs>
</div>
</template>
<template #menu="scope">
<custom-table-operate :actions="stageState.options.actions" :data="scope" />
</template>
@ -41,6 +57,7 @@ import { CRUD_OPTIONS } from '@/config';
import { useUserStore } from '@/store/modules/user';
import { getPlantingStage, savePlantingStage, editPlantingStage, delPlantingStage } from '@/apis/land.js';
import { isEmpty, imageToBase64, getAssetsFile, downloadFile } from '@/utils';
import inputs from '../component/inputsList.vue';
const props = defineProps({
isShow: {
@ -56,15 +73,16 @@ const props = defineProps({
});
const emit = defineEmits(['close']);
let refinputs = ref(null);
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const stateCrudRef = ref(null);
const stageOptions = reactive([
{ value: '0', label: '苗期' },
{ value: '1', label: '花果期' },
{ value: '2', label: '采收期' },
{ value: 0, label: '苗期' },
{ value: 1, label: '花果期' },
{ value: 2, label: '采收期' },
]);
const workOptions = reactive([
@ -87,7 +105,7 @@ let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
// console.info('loadList', props);
getStageList();
}
};
@ -120,29 +138,27 @@ const stageState = reactive({
addBtn: false,
selection: false,
group: [
{ label: '所属阶段', prop: 'stage' },
{ label: '作业类型', prop: 'workType' },
{ label: '作业时间', prop: 'operationDate' },
{
prop: 'inputs',
label: '投入品',
column: [
{
prop: 'input',
span: 24,
labelWidth: 0,
},
],
},
],
column: [
// {
// label: '',
// prop: 'cropId',
// type: 'select',
// remote: false,
// width: '160px',
// showOverflowTooltip: true,
// props: {
// label: 'crop',
// value: 'id',
// },
// dicHeaders: {
// authorization: UserStore.token,
// },
// dicUrl: `${VITE_APP_BASE_API}/land-resource/baseInfo/planTypePage?current=1&size=999`,
// dicFormatter: (res) => res.data.records ?? [],
// rules: [{ required: true, message: '', trigger: 'blur' }],
// },
{
prop: 'input',
label: '投入品',
editDisplay: false,
addDisplay: false,
span: 24,
labelWidth: 0,
},
{
label: '所属阶段',
prop: 'stage',
@ -154,6 +170,8 @@ const stageState = reactive({
message: '请选择',
trigger: 'blur',
},
editDisplay: true,
addDisplay: true,
},
{
label: '作业类型',
@ -174,21 +192,14 @@ const stageState = reactive({
dicFormatter: (res) => res.data.records ?? [],
rules: { required: true, message: '请选择', trigger: 'blur' },
change: handleWorkChange,
},
{
label: '作业时间(多少天后)',
prop: 'workTime',
rules: { required: true, message: '请输入', trigger: 'blur' },
props: {
type: 'Number',
},
addDisabled: true,
editDisplay: false,
viewDisplay: false,
editDisplay: true,
addDisplay: true,
},
{
label: '作业时间',
prop: 'operationDate',
prop: 'workTime',
rules: { required: true, message: '请输入', trigger: 'blur' },
addDisabled: true,
editDisplay: false,
addDisplay: false,
},
@ -222,7 +233,11 @@ async function getStageList() {
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
stageState.data = records || [];
// stageState.data = records || [];
stageState.data =
records.map((m) => {
return { ...m, input: m.input ? JSON.parse(m.input) : [] };
}) || [];
stageState.pageData = {
currentPage: current || 1,
pageSize: size || 10,
@ -285,19 +300,22 @@ const stageRowDel = (row, index, done) => {
.catch(() => {});
};
const stageRowEdit = (row) => {
stageState.currentRow = row;
stateCrudRef.value.rowEdit(row);
};
const onStateAdd = () => {
stageState.currentRow = {};
if (!currentRow.id) {
app.$message.error('请选择种植规划');
return;
}
stateCrudRef.value.rowAdd();
stateCrudRef.value.rowAdd({});
};
const stageRowSave = (row, done, loading) => {
row.planId = currentRow.id;
row.input = JSON.stringify(refinputs.value ? refinputs.value.saveList() : []);
console.info('stageRowSave', row);
savePlantingStage({ ...row })
.then((res) => {
@ -317,6 +335,7 @@ const stageRowSave = (row, done, loading) => {
const stageRowUpdate = (row, index, done, loading) => {
console.info('stageRowUpdate');
row.input = JSON.stringify(refinputs.value ? refinputs.value.saveList() : []);
editPlantingStage(row)
.then((res) => {
if (res.code === 200) {

View File

@ -36,11 +36,19 @@ const startTime = () => {
const chearTime = () => {
if (interval.value) {
interval.value = null;
clearInterval(interval.value);
interval.value = null;
}
};
onMounted(() => {
// startTime();
});
onUnmounted(() => {
// chearTime();
});
defineExpose({
startTime,
chearTime,
@ -50,7 +58,7 @@ defineExpose({
.current-time-warp {
position: fixed;
right: 16px;
top: 32px;
top: 24px;
color: #add8f1;
z-index: 2;
}

View File

@ -466,3 +466,18 @@ export const endDate = (num = 0, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss
if (typeof num === 'string') return dayjs(num).endOf(type).format(formater);
return num === 0 ? dayjs().endOf(type).format(formater) : dayjs().subtract(num, type).endOf(type).format(formater);
};
/**
* @Title: 生成随机数
* @param len
* @returns
*/
export const randomNumber = (len) => {
let randomlen = len ? len : 10;
const chars = '0123456789';
let result = '';
for (let i = 0; i < randomlen; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};

View File

@ -62,10 +62,10 @@ const chartsData = reactive({
{ name: '耿马', value: 100, type: '合作社', seriesType: 'bar', stack: '耿马' },
{ name: '耿马', value: 60, type: '经营企业', seriesType: 'bar', stack: '耿马', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '耿马', value: 10, type: '趋势', seriesType: 'line' },
{ name: '大香乡', value: 20, type: '个体户', seriesType: 'bar', stack: '耿马' },
{ name: '大香乡', value: 20, type: '村集体', seriesType: 'bar', stack: '耿马' },
{ name: '大香乡', value: 80, type: '合作社', seriesType: 'bar', stack: '耿马' },
{ name: '大香乡', value: 40, type: '经营企业', seriesType: 'bar', stack: '耿马', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '大香乡', value: 20, type: '个体户', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 20, type: '村集体', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 80, type: '合作社', seriesType: 'bar', stack: '大香乡' },
{ name: '大香乡', value: 40, type: '经营企业', seriesType: 'bar', stack: '大香乡', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '大香乡', value: 50, type: '趋势', seriesType: 'line' },
],
});

View File

@ -80,7 +80,7 @@ const classOptions = {
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;

View File

@ -156,7 +156,7 @@ watch(
font-size: 13px;
}
.value {
color: #6cd1f9;
color: #6beff9;
font-size: 16px;
font-weight: bold;
margin-top: 6px;

View File

@ -142,7 +142,7 @@ watch(
font-size: 13px;
}
.value {
color: #6cd1f9;
color: #6beff9;
font-size: 16px;
font-weight: bold;
margin-top: 6px;

View File

@ -16,7 +16,7 @@ const plantBreed = ref({
},
},
legend: {
data: ['耕地', '林地', '建设用地'],
data: ['种植面积', '养殖面积', '种植基地', '养殖基地'],
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
@ -46,9 +46,10 @@ const plantBreed = ref({
],
},
valData: [
{ value: 100, name: '耕地' },
{ value: 105, name: '林地' },
{ value: 217, name: '建设用地' },
{ value: 100, name: '种植面积' },
{ value: 105, name: '养殖面积' },
{ value: 217, name: '种植基地' },
{ value: 315, name: '养殖基地' },
],
});
</script>

View File

@ -1,14 +1,18 @@
<template>
<div class="demo roll-list" style="height: 90%">
<vue3ScrollSeamless class="scroll-wrap" :classOptions="classOptions" :dataList="list">
<div v-for="(item, index) in items" :key="index" class="list-item">
<div class="demo roll-list" style="height: 100%" ref="refroll">
<vue3ScrollSeamless class="scroll-wrap" :classOptions="classOptions" :dataList="datalist">
<div v-for="(item, index) in datalist" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-l">
<div class="item-top">
<span class="label"> {{ item.title || '--' }}</span>
<span class="value"> {{ item.value || '0' }}</span>
</div>
<el-progress :percentage="50" :show-text="false" :stroke-width="3" color="#6cd1f9" />
<div class="progress-val">
<div class="progress-warp" :style="{ width: item.percent + 'px' }">
<div class="progress"></div>
</div>
</div>
</div>
<div class="list-item-r">
{{ '0' + (index + 1) }}
@ -31,10 +35,25 @@ const props = defineProps({
});
let list = reactive(props.items);
let refroll = ref(null);
const classOptions = {
singleHeight: 48,
};
let datalist = computed(() => {
let maxwidth = refroll.value && refroll.value.clientWidth;
return list.map((m) => {
return { ...m, percent: parseInt(Number(parseInt(m.value) / max.value) * maxwidth) };
});
});
let max = computed(() => {
let valueList = new Set(list.map((item) => parseInt(item.value)));
let sortValue = [...valueList].sort((a, b) => b - a) || [];
// console.info('valueList', sortValue);
return sortValue.length ? sortValue[0] : 0;
});
</script>
<style scoped lang="scss">
@ -73,7 +92,7 @@ const classOptions = {
}
.value {
font-size: 10px;
color: #6cd1f9;
color: #6beff9;
}
}
}
@ -81,11 +100,31 @@ const classOptions = {
text-align: right;
font-size: 20px;
font-weight: bold;
color: #6cd1f9;
color: #6beff9;
position: absolute;
right: 0;
bottom: 0;
}
.progress-val {
width: calc(100%);
.progress-warp {
.progress {
height: 6px;
border-radius: 6px;
background: linear-gradient(90deg, #45bfe9 0%, #01589c 100%);
animation: expandWidth 1s ease-in-out forwards;
}
@keyframes expandWidth {
from {
width: 0;
}
to {
width: 100%;
}
}
}
}
}
}
.ui-wrap {

View File

@ -1,6 +1,6 @@
<template>
<div class="home-trace-charts">
<custom-echart-bar-line :chart-data="chartsData.valData" :option="chartsData.option" height="100%" />
<custom-echart-mixin :chart-data="chartsData.valData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
@ -25,6 +25,9 @@ const chartsData = ref({
legend: {
data: ['赋码', '扫码'],
},
barStyle: {
barWidth: 18,
},
yAxis: [
{
type: 'value',
@ -74,13 +77,13 @@ const chartsData = ref({
},
},
{ name: '1月', value: 60, type: '扫码', seriesType: 'line' },
{ name: '2月', value: 20, type: '赋码', seriesType: 'bar' },
{ name: '2月', value: 20, type: '赋码', seriesType: 'bar', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '2月', value: 100, type: '扫码', seriesType: 'line' },
{ name: '3月', value: 80, type: '赋码', seriesType: 'bar' },
{ name: '3月', value: 80, type: '赋码', seriesType: 'bar', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '3月', value: 100, type: '扫码', seriesType: 'line' },
{ name: '4月', value: 40, type: '赋码', seriesType: 'bar' },
{ name: '4月', value: 40, type: '赋码', seriesType: 'bar', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '4月', value: 120, type: '扫码', seriesType: 'line' },
{ name: '5月', value: 50, type: '赋码', seriesType: 'bar' },
{ name: '5月', value: 50, type: '赋码', seriesType: 'bar', itemStyle: { borderRadius: [8, 8, 0, 0] } },
{ name: '5月', value: 60, type: '扫码', seriesType: 'line' },
],
});

View File

@ -64,7 +64,7 @@
<div class="home-data-contrast">
<span class="tips">同比去年</span>
<span class="value">4684.629</span>
<el-icon style="vertical-align: middle" class="contrast-icon" color="#6cd1f9">
<el-icon style="vertical-align: middle" class="contrast-icon" color="#6beff9">
<TopRight />
</el-icon>
</div>
@ -140,7 +140,7 @@ let rollDataList = reactive([
.home-data-contrast {
.tips {
font-size: 10px;
color: #6cd1f9;
color: #6beff9;
}
.value {
padding: 0 8px;

View File

@ -103,7 +103,7 @@ div {
}
.content {
justify-content: space-around;
margin-top: 50%;
margin-top: 30%;
.content-item {
height: 36px;
display: inline-block;

View File

@ -1,5 +1,5 @@
<template>
<div class="demo roll-list-land-plan" style="height: 90%">
<div class="demo roll-list-land-plan" style="height: 100%" ref="refroll">
<!-- <div class="list-item-header item-warp" :style="{ flex: listKeys.length }">
<template v-for="(h, indexh) in listKeys" :key="indexh">
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
@ -12,7 +12,6 @@
<div class="item-content">
<div class="label">{{ item.title }}</div>
<div class="val">
<!-- <el-progress :percentage="50" :show-text="false" :indeterminate="false" :stroke-width="12" color="#6cd1f9" :duration="8" /> -->
<div class="progress-warp" :style="{ width: item.percent + 'px' }">
<div class="progress"></div>
</div>
@ -44,7 +43,10 @@ let list = reactive([
{ title: '农机', value: 45 },
]);
let refroll = ref(null);
let datalist = computed(() => {
let maxwidth = refroll.value && refroll.value.clientWidth;
return list.map((m) => {
return { ...m, percent: parseInt(Number(m.value / max.value) * 200) };
});
@ -97,7 +99,7 @@ onMounted(() => {});
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;

View File

@ -80,7 +80,7 @@ const classOptions = {
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;

View File

@ -62,8 +62,8 @@ const chartsData = reactive({
});
onMounted(() => {
if (plantBreed.valData && plantBreed.length) {
plantBreed.valData.forEach((m, index) => {
if (chartsData.valData && chartsData.valData.length) {
chartsData.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});

View File

@ -77,7 +77,7 @@ const classOptions = {
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
}
}

View File

@ -81,7 +81,7 @@ const classOptions = {
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
&.td-warn {

View File

@ -75,7 +75,7 @@ const classOptions = {
.item-td {
padding: 4px 6px;
&.td-title {
color: #6cd1f9 !important;
color: #6beff9 !important;
}
&.zebra-b {
background: #051225 !important;

View File

@ -1,62 +1,119 @@
<template>
<div class="insect-pests-charts">
<custom-echart-bar :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
<custom-echart-mixin :chart-data="handelData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['蝗虫', '飞蛾', '其他', '蚜虫']);
const chartsData = reactive({
option: {
grid: {
left: '3%',
right: '4%',
bottom: '2%',
top: '18%',
containLabel: true,
},
color: ['#3685fe', '#41b879', '#ffd500', '#e57373'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
label: {
color: '#333',
legend: {
show: true,
data: legendList,
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 15,
itemStyle: {
borderRadius: [8, 8, 0, 0], //
},
color: {
type: 'linear', // 线
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#45bfe9' },
{ offset: 1, color: '#01589c' },
],
global: false, // false
},
barWidth: 10,
},
legend: {
show: false,
dataZoom: [
{
type: 'slider', //
startValue: 0, //
endValue: 2, //
},
{
type: 'inside', //
startValue: 0,
endValue: 2,
},
],
yAxis: [
{
type: 'value',
name: ' ',
axisLabel: {
formatter: '{value}',
},
splitLine: {
show: true, // 线
lineStyle: {
type: 'dashed', // 线
width: 0.5, // 线
},
},
itemStyle: { fontSize: 8 },
},
],
grid: {
x: '10%',
x2: '15%',
y: '20%',
y2: '20%',
},
},
valData: [
{ value: 80, type: '经销商', name: '耿马镇' },
{ value: 105, type: '经销商', name: '勐撒镇' },
{ value: 100, type: '经销商', name: '勐永镇' },
{ value: 125, type: '经销商', name: '孟定镇' },
{ value: 217, type: '经销商', name: '勐简乡' },
{ value: 200, type: '经销商', name: '贺派乡' },
{ value: 155, type: '经销商', name: '四排山乡' },
{ value: 80, type: '经销商', name: '芒洪乡' },
{ value: 105, type: '经销商', name: '大兴乡' },
{ name: '1月', value: 40, type: '蝗虫', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 30, type: '飞蛾', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 100, type: '其他', seriesType: 'bar', ...itemStyle },
{ name: '1月', value: 60, type: '蚜虫', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 20, type: '蝗虫', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 20, type: '飞蛾', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 80, type: '其他', seriesType: 'bar', ...itemStyle },
{ name: '2月', value: 40, type: '蚜虫', seriesType: 'bar', ...itemStyle },
],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: 'bar',
type: legendList[i],
...itemStyle,
};
list[i] = val;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m) => {
return { ...m, value: Number(m.value + Math.random() + 10).toFixed(2) };
});
// console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.insect-pests-charts {

View File

@ -0,0 +1,126 @@
<template>
<div class="pathology-charts">
<custom-echart-mixin :chart-data="handelData" :option="chartsData.option" height="100%" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
let itemStyle = reactive({
itemStyle: { borderRadius: [8, 8, 0, 0] },
});
let legendList = reactive(['黄叶病', '霜霉病', '白粉病', '其他']);
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500', '#e57373'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
show: true,
data: legendList,
left: '0', // 10%
top: '0', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
barStyle: {
barWidth: 10,
},
dataZoom: [
// {
// type: 'slider', //
// startValue: 0, //
// endValue: 2, //
// },
// {
// type: 'inside', //
// startValue: 0,
// endValue: 2,
// },
],
yAxis: [
{
type: 'value',
name: ' ',
axisLabel: {
formatter: '{value}',
},
splitLine: {
show: true, // 线
lineStyle: {
type: 'dashed', // 线
width: 0.5, // 线
},
},
itemStyle: { fontSize: 8 },
},
],
grid: {
x: '10%',
x2: '10%',
y: '20%',
y2: '20%',
},
},
valData: [
// { name: '1', value: 40, type: '', seriesType: 'bar', ...itemStyle, stack: '1' },
// { name: '1', value: 30, type: '', seriesType: 'bar', ...itemStyle, stack: '1' },
// { name: '1', value: 100, type: '', seriesType: 'bar', ...itemStyle, stack: '1' },
// { name: '1', value: 60, type: '', seriesType: 'bar', ...itemStyle, stack: '1' },
// { name: '2', value: 20, type: '', seriesType: 'bar', ...itemStyle, stack: '2' },
// { name: '2', value: 20, type: '', seriesType: 'bar', ...itemStyle, stack: '2' },
// { name: '2', value: 80, type: '', seriesType: 'bar', ...itemStyle, stack: '2' },
// { name: '2', value: 40, type: '', seriesType: 'bar', ...itemStyle, stack: '2' },
],
});
const randomVal = (num) => {
let list = [];
for (let i = 0; i < legendList.length; i++) {
let addNum = [10, 8, 2, 5];
let val = {
name: num + '月',
value: Number(Math.random() * 100 + addNum[i]).toFixed(2),
seriesType: 'bar',
type: legendList[i],
stack: num + '月',
};
let lastVal = {
...val,
...itemStyle,
};
list[i] = i < legendList.length - 1 ? val : lastVal;
}
return list;
};
let handelData = computed(() => {
let list = [];
let maxMouth = 12;
for (let i = 0; i < maxMouth; i++) {
let val = randomVal(i + 1);
list = [...list, ...val];
}
list.map((m, indexm) => {
return { ...m, value: Number(Number(m.value) + Math.random() + indexm).toFixed(0) };
});
console.info('handelData', list);
return list;
});
onMounted(() => {});
</script>
<style lang="scss" scoped>
.pathology-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,121 @@
<template>
<div class="demo water-fertilizer-charts" style="height: 90%">
<div class="list-item-header item-warp" :style="{ flex: listKeys.length }">
<template v-for="(h, indexh) in listKeys" :key="indexh">
<div class="item-td" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">{{ listKeysHeader[h] }}</div>
</template>
</div>
<vue3ScrollSeamless class="scroll-wrap" :class-options="classOptions" :data-list="list">
<div v-for="(item, index) in list" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
<template v-for="(b, indexb) in listKeys" :key="indexb">
<div class="item-td" :class="item.status == 1 ? 'td-title' : 'td-warn'" :style="{ width: 'calc(100% / ' + listKeys.length + ')' }">
<span v-if="b != 'status'">
{{ item[b] }}
</span>
<el-icon v-else>
<Bell></Bell>
</el-icon>
</div>
</template>
</div>
</div>
</div>
</vue3ScrollSeamless>
</div>
<!-- </div> -->
</template>
<script setup>
import { ref, onMounted, onUnmounted, computed, reactive } from 'vue';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
const props = defineProps({
// items: {
// type: Array,
// default: () => [],
// },
});
let list = reactive([
{ title: '耿马镇', waitNum: '24', violation: '14', status: 1 },
{ title: '勐撒镇', waitNum: '16', violation: '14', status: 1 },
{ title: '孟定镇', waitNum: '8', violation: '24', status: 0 },
{ title: '孟简镇', waitNum: '9', violation: '8', status: 1 },
{ title: '孟永镇', waitNum: '14', violation: '15', status: 1 },
]);
const listKeys = reactive(['title', 'waitNum', 'violation', 'status']);
const listKeysHeader = reactive({
title: '乡/镇',
waitNum: '灌溉',
violation: '施肥',
status: '预警',
});
const classOptions = {
singleHeight: 48,
};
</script>
<style scoped lang="scss">
.water-fertilizer-charts {
margin-top: 8px;
.scroll-wrap {
height: 80%;
width: 100%;
margin: 4px auto;
overflow: hidden;
}
.list-item-header {
background: #144482;
font-size: 10px;
width: 100%;
.item-td {
padding: 8px 6px;
}
}
.list-item-boday {
background: transparent;
width: 100%;
.item-td {
padding: 4px 6px;
&.td-title {
color: #6beff9 !important;
}
&.td-warn {
color: red !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
}
}
.list-item {
// border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
line-height: 18px;
.list-item-content {
display: inline-flex;
width: 100%;
justify-content: space-around;
position: relative;
}
}
}
.demo {
// display: flex;
// align-items: center;
// justify-content: center;
// margin-top: 10px;
}
</style>

View File

@ -13,22 +13,30 @@
</div>
<div class="left-charts-item">
<customBack top-title="昆虫害监测" :top-postion="'left'">
<template #back></template>
<template #back>
<insectPestsCharts></insectPestsCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="病理害监测" :top-postion="'left'">
<template #back></template>
<template #back>
<pathologyCharts></pathologyCharts>
</template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<el-row style="height: 60%">
<el-col :span="24"> 1-1</el-col>
<el-col :span="24"></el-col>
</el-row>
<el-row style="height: 40%">
<el-col :span="12">
<customBack top-title="水肥检测分析" :top-postion="'left'"> <template #back></template> </customBack>
<customBack top-title="水肥检测分析" :top-postion="'left'">
<template #back>
<waterfertilizerCharts></waterfertilizerCharts>
</template>
</customBack>
</el-col>
<el-col :span="12">
<customBack top-title="智慧水肥灌溉" :top-postion="'right'"> <template #back></template> </customBack>
@ -61,6 +69,9 @@
import baseBg from '@/components/baseBg.vue';
import customBack from '@/components/customBack.vue';
import plantTypeCharts from './components/plantTypeCharts.vue';
import insectPestsCharts from './components/insectPestsCharts';
import pathologyCharts from './components/pathologyCharts.vue';
import waterfertilizerCharts from './components/waterfertilizerCharts.vue';
</script>
<style lang="scss" scoped>
.data-home-index {