This commit is contained in:
lzc 2025-03-17 17:34:37 +08:00
commit e92f74f87f
13 changed files with 1338 additions and 44 deletions

View File

@ -0,0 +1,233 @@
<template>
<div class="carousel" style="width: 500px">
<el-carousel
v-if="type === 'image'"
ref="carouselRef"
:interval="option.interval"
:arrow="option.arrow"
:indicator-position="option.indicatorPosition"
@change="handleChange"
>
<el-carousel-item v-for="(item, index) in data" :key="index">
<img :src="item.image" style="width: 100%; height: auto" />
</el-carousel-item>
</el-carousel>
<div v-if="type === 'video'" class="carousel-video">
<!-- <img :src="state.videoPicture" class="carousel-video-picture" /> -->
<video ref="videoRef" controls class="carousel-video-video" width="100%" height="100%" @ended="pauseVideo">
<source :src="state.videoUrl" type="video/mp4" />
Your browser does not support the video tag.
</video>
<!-- <span class="carousel-video-btn" @click="handlePlay">
<el-icon><VideoPlay /></el-icon>
</span> -->
</div>
<div class="carousel-container">
<span class="carousel-arrow carousel-arrow-left" @click="handleLeftClick">
<el-icon><ArrowLeftBold /></el-icon>
</span>
<el-scrollbar ref="scrollbarRef" class="carousel-list">
<div
v-for="(item, index) in data"
:key="index"
:class="`carousel-list-item ${state.current === index ? 'active' : ''}`"
@click="handleClick(index)"
>
<el-image style="width: 100px; height: 100px" :src="item.image ?? item" fit="cover" />
</div>
</el-scrollbar>
<span class="carousel-arrow carousel-arrow-right" @click="handleRightClick">
<el-icon><ArrowRightBold /></el-icon>
</span>
</div>
</div>
</template>
<script setup name="custom-carousel-picture">
import { reactive, ref } from 'vue';
const props = defineProps({
data: { type: Array, default: () => [] },
type: { type: String, default: 'image' },
option: {
type: Object,
default: () => {
return {
height: '',
initialIndex: 0,
autoplay: true,
loop: true,
interval: 3000,
arrow: 'never',
indicatorPosition: 'none',
};
},
},
});
const emit = defineEmits(['']);
const carouselRef = ref(null);
const scrollbarRef = ref(null);
const videoRef = ref(null);
const state = reactive({
current: 0,
isReady: false,
videoPicture: '',
videoUrl: '',
});
const handleChange = (cur, last) => {
state.current = cur;
};
const handleLeftClick = () => {
// const index = carouselRef.value.activeIndex;
// carouselRef.value.setActiveItem(index + 1);
scrollbarRef.value.setScrollLeft(scrollbarRef.value.wrapRef.scrollLeft - 120);
};
const handleRightClick = () => {
// const index = carouselRef.value.activeIndex;
// carouselRef.value.setActiveItem(index - 1);
scrollbarRef.value.setScrollLeft(scrollbarRef.value.wrapRef.scrollLeft + 120);
};
const playVideo = () => {
if (videoRef.value) {
videoRef.value.play();
}
};
const pauseVideo = () => {
if (videoRef.value) {
videoRef.value.pause();
}
};
const handleClick = (index) => {
const { type, data } = props;
switch (type) {
case 'image': {
carouselRef.value.setActiveItem(index);
break;
}
case 'video': {
const url = data[index];
state.videoUrl = url;
playVideo();
break;
}
}
};
const handlePlay = () => {
playVideo();
};
</script>
<style lang="scss" scoped>
.carousel {
width: 100%;
display: flex;
flex-direction: column;
&-container {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 30px;
}
&-list {
flex: 3;
:deep(.el-scrollbar__bar) {
display: none !important;
}
:deep(.el-scrollbar__view) {
display: flex;
flex-direction: row;
align-items: center;
}
&-item {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
margin: 10px;
border: 2px solid #fff;
text-align: center;
border-radius: 4px;
background: var(--el-color-danger-light-9);
color: var(--el-color-danger);
cursor: pointer;
&.active {
border-color: var(--el-color-primary);
}
}
}
&-arrow {
width: 50px;
text-align: center;
.el-icon {
display: inline-block;
width: 40px;
height: 40px;
margin: 0 auto;
line-height: 40px;
font-size: 32px;
cursor: pointer;
}
}
&-video {
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
min-height: 300px;
&-picture {
position: absolute;
z-index: 10;
}
&-video {
position: absolute;
z-index: 11;
}
&-btn {
position: absolute;
left: 50%;
top: 50%;
z-index: 12;
width: 50px;
height: 50px;
margin-left: -25px;
margin-top: -25px;
border-radius: 25px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.2);
cursor: pointer;
.el-icon {
font-size: 32px;
color: #fff;
}
}
}
}
</style>

View File

@ -1,6 +1,7 @@
import SvgIcon from './svg-icon';
import CustomTableOperate from './custom-table-operate';
import CustomTableTree from './custom-table-tree';
import CustomCarouselPicture from './custom-carousel-picture';
import CustomImportExcel from './custom-import-excel';
import CustomRichEditor from './custom-rich-editor';
import CustomEchartBar from './custom-echart-bar';
@ -15,6 +16,7 @@ export {
SvgIcon,
CustomTableOperate,
CustomTableTree,
CustomCarouselPicture,
CustomImportExcel,
CustomRichEditor,
CustomEchartBar,

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -0,0 +1,134 @@
<template>
<section class="custom_attrs_upload_content_lx" :style="{ '--columns': props.fileNum }">
<el-upload
v-if="props.type != 'view' && props.upBtn"
class="custom-form__uploader"
action=""
:show-file-list="false"
:accept="props.accept"
:limit="props.limit"
:http-request="rowUploadPicture"
:disabled="attrs_.length >= props.limit"
>
<el-icon class="custom-form__uploader__icon"><Plus /></el-icon>
</el-upload>
<div v-for="(item, i) in attrs_" :key="`attr_${item.uid}`" class="attrs_content__item">
<img :src="item.url" :alt="item.name" @click="handlePreview(i)" />
<el-icon v-if="props.type != 'view'" class="clear_btn" @click.stop="handleClearAttr(item.uid)"><CircleCloseFilled /></el-icon>
</div>
<el-image-viewer v-if="previewShow" :url-list="srcList" :initial-index="index" @close="previewShow = false" />
</section>
</template>
<script setup>
import { nextTick, ref, watch } from 'vue';
import { CommonUpload } from '@/apis';
const emit = defineEmits(['update:attrs']);
const props = defineProps({
accept: {
type: String,
default: 'image/*',
},
type: {
type: String,
default: 'view',
},
attrs: {
type: Array,
default: () => [],
},
limit: {
type: Number,
default: 1,
},
fileNum: {
type: Number,
default: 4,
},
upBtn: {
type: Boolean,
default: true,
},
});
const attrs_ = ref([]);
const srcList = ref([]);
watch(
() => props.attrs,
(val) => {
attrs_.value = val;
srcList.value = attrs_.value.map((item) => item.url);
},
{ deep: true, immediate: true }
);
const index = ref(0);
const previewShow = ref(false);
function handleClearAttr(uid) {
attrs_.value = attrs_.value.filter((item) => item.uid !== uid);
emit('update:attrs', attrs_.value);
}
async function rowUploadPicture({ file }) {
const formData = new FormData();
formData.append('file', file);
const res = await CommonUpload(formData);
if (res.code === 200) {
attrs_.value.push({
...res.data,
uid: 'id_' + Date.now(),
});
emit('update:attrs', attrs_.value);
}
}
function handlePreview(i) {
previewShow.value = false;
nextTick(() => {
index.value = i;
previewShow.value = true;
});
}
</script>
<style lang="scss">
.custom_attrs_upload_content_lx {
display: grid;
flex-wrap: wrap;
grid-template-columns: repeat(var(--columns), 1fr);
box-sizing: border-box;
gap: 20px;
> div {
width: 100%;
height: 100%;
aspect-ratio: 1 / 1;
box-sizing: border-box;
}
.attrs_content__item {
aspect-ratio: 1 / 1;
box-sizing: border-box;
position: relative;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
img,
video {
vertical-align: middle;
width: 100%;
height: 100%;
border-radius: 2px;
}
.clear_btn {
position: absolute;
right: 0px;
top: 0px;
font-size: 18px;
color: #f15c5c;
opacity: 0;
cursor: pointer;
background-color: #fff;
border-radius: 50%;
}
&:hover {
.clear_btn {
opacity: 1;
}
}
}
}
</style>

View File

@ -0,0 +1,50 @@
import { ref } from 'vue';
export function useBasicInfo() {
const pesticideData = ref([
{ label: '全部', value: '0' },
{
label: '防治对象',
value: '1',
children: [
{ label: '杀虫剂', value: '1-1' },
{ label: '除草剂', value: '1-2' },
{ label: '杀菌剂', value: '1-3' },
{ label: '杀螨剂', value: '1-4' },
{ label: '生物农药', value: '1-5' },
{ label: '植物生长调节剂', value: '1-6' },
],
},
{
label: '化学成分',
value: '2',
children: [
{ label: '无机农药', value: '2-1' },
{ label: '生物农药', value: '2-2' },
{
label: '有机农药',
value: '2-3',
children: [
{ label: '天然有机农药', value: '2-3-1' },
{ label: '人工合成农药', value: '2-3-2' },
],
},
],
},
{
label: '加工剂型',
value: '3',
children: [
{ label: '可湿性粉剂', value: '3-1' },
{ label: '可溶性粉剂', value: '3-2' },
{ label: '乳剂', value: '3-3' },
{ label: '颗粒剂', value: '4-4' },
{ label: '缓释剂', value: '5-5' },
{ label: '烟剂', value: '6-6' },
],
},
]);
return {
pesticideData,
};
}

View File

@ -1,25 +1,60 @@
<template>
<CustomCard>
<h2>农药基本信息</h2>
<TypeMenu v-model:type="type" />
<TypeMenu v-model:type="_type" :types="pesticideData" />
<br />
<avue-crud ref="crud" v-model:page="pageData" :data="data" :option="option"></avue-crud>
<avue-crud
ref="crud"
v-model:page="pageData"
:table-loading="_loading"
:data="data"
:option="option"
:before-close="handleCloseDialog"
@row-save="handleRowSave"
@row-update="handleRowUpdate"
>
<template #menu="{ row }">
<el-button type="primary" @click="handleEdit(row)">上传报告</el-button>
<el-button @click="handleInfo(row)">详情</el-button>
</template>
<template #img-form="{ type }">
<Attrs v-model:attrs="img" :type="type == 'add' ? 'add' : 'view'" />
</template>
<template #checkInfo-form="{ row }">
<section style="text-align: center; line-height: 58px">
{{ row }}
<el-button @click="handleCheckInfo('open')">查看报告</el-button>
</section>
</template>
<template #checkReport-form="{ type }">
<Attrs v-model:attrs="reportAttrs" :type="type" :up-btn="reportAttrs.length < 1" :file-num="reportAttrs.length > 0 ? 1 : 3" />
</template>
<template #checkBtn-form>
<section style="text-align: center; line-height: 58px">
<el-button @click="handleCheckInfo('close')">关闭</el-button>
</section>
</template>
</avue-crud>
</CustomCard>
</template>
<script setup>
import { ref, watch } from 'vue';
import { reactive, ref, watch, h } from 'vue';
import CustomCard from '@/components/CustomCard.vue';
import TypeMenu from '../../common/TypeMenu.vue';
import { CRUD_OPTIONS } from '@/config';
import { useBasicInfo } from '@/views/inputSuppliesManage/hooks/useBasicInfo';
import Attrs from '@/views/inputSuppliesManage/common/Attrs.vue';
const { pesticideData } = useBasicInfo();
/* --------------- data --------------- */
// #region
const type = ref('');
const _type = ref('');
watch(
() => type.value,
() => _type.value,
() => {
console.log(type.value);
console.log(_type.value);
},
{
deep: true,
@ -27,7 +62,37 @@ watch(
);
const crud = ref();
const data = ref([]);
const _loading = ref(false);
const data = ref([
{
name: '农药1',
id: '1111',
produicer: '生产商1',
dealer: '经销商1',
img: [
'https://gips0.baidu.com/it/u=3602773692,1512483864&fm=3028',
'https://gips3.baidu.com/it/u=100751361,1567855012&fm=3028',
'https://gips2.baidu.com/it/u=195724436,3554684702&fm=3028',
],
checkReport: [],
},
{
name: '农药2',
id: '2222',
produicer: '生产商2',
dealer: '经销商2',
img: ['https://gips3.baidu.com/it/u=100751361,1567855012&fm=3028'],
checkReport: ['https://gips3.baidu.com/it/u=100751361,1567855012&fm=3028'],
},
{
name: '农药3',
id: '3333',
produicer: '生产商3',
dealer: '经销商3',
img: [],
checkReport: [],
},
]);
const pageData = ref({
total: 0,
currentPage: 1,
@ -36,54 +101,251 @@ const pageData = ref({
const option = ref({
CRUD_OPTIONS,
selection: false,
labelWidth: 124,
editBtn: false,
delBtn: false,
column: [
{
prop: '',
prop: 'id',
label: '农药编号',
addDisplay: false,
editDisplay: false,
},
{
prop: '',
prop: 'name',
label: '名称',
editDisplay: false,
},
{
prop: '',
label: '厂家',
hide: true,
prop: 'code',
label: '产品标准证号',
viewDisplay: false,
editDisplay: false,
},
{
prop: '',
hide: true,
prop: 'img',
label: '农药图片',
editDisplay: false,
},
{
hide: true,
prop: 'code1',
label: '农药登记证号',
viewDisplay: false,
editDisplay: false,
},
{
prop: 'producer',
label: '生产厂家',
editDisplay: false,
},
{
prop: 'dealer',
label: '经销商',
editDisplay: false,
},
{
prop: '',
label: '防治对象',
},
{
prop: '',
label: '化学成分',
},
{
prop: '',
label: '加工剂型',
},
{
prop: '',
prop: 'format',
label: '产品规格',
editDisplay: false,
},
{
prop: '',
prop: 'toxicity',
label: '毒性',
editDisplay: false,
},
{
prop: '',
prop: 'validity',
label: '保质期',
editDisplay: false,
},
{
prop: 'preventTargets',
label: '防治对象',
type: 'cascader',
checkStrictly: true,
multiple: true,
dicData: pesticideData.value[1].children,
span: 24,
editDisplay: false,
},
{
prop: 'chemicalComposition',
label: '化学成分',
type: 'cascader',
checkStrictly: true,
multiple: true,
dicData: pesticideData.value[2].children,
span: 24,
editDisplay: false,
},
{
prop: 'dosageForm',
label: '加工剂型',
type: 'cascader',
checkStrictly: true,
multiple: true,
dicData: pesticideData.value[3].children,
span: 24,
editDisplay: false,
},
{
hide: true,
prop: 'useMethods',
label: '使用方法',
type: 'textarea',
span: 24,
viewDisplay: false,
editDisplay: false,
},
{
hide: true,
prop: 'tips',
label: '注意事项',
type: 'textarea',
span: 24,
viewDisplay: false,
editDisplay: false,
},
{
labelWidth: 0,
border: false,
prop: 'checkInfo',
span: 24,
addDisplay: false,
editDisplay: false,
},
],
group: [
{
label: '农药基本信息',
prop: 'basic_info',
addDisplay: false,
viewDisplay: false,
column: [
{
prop: 'name',
label: '农药名称',
editDisabled: true,
},
{
prop: 'producer',
label: '生产商',
editDisabled: true,
},
{
prop: 'img',
label: '农药图片',
editDisabled: true,
},
{
prop: 'dealer',
label: '经销商',
editDisabled: true,
},
],
},
{
label: '农药检测信息',
prop: 'check_info',
addDisplay: false,
viewDisplay: false,
column: [
{
prop: 'checkDate',
label: '农药名称',
},
{
prop: 'checkResult',
label: '检测结果',
},
{
prop: 'checkUnit',
label: '检测单位',
},
{
prop: 'checkConclusion',
label: '检测结论',
},
{
prop: 'checkReport',
label: '质检报告',
span: 24,
},
{
labelWidth: 0,
prop: 'checkBtn',
span: 24,
editDisplay: false,
},
],
},
],
});
const img = ref([]);
const reportAttrs = ref([]);
// #endregion
/* --------------- methods --------------- */
// #region
function handleCloseDialog(done) {
handleCheckInfoChange();
done();
}
function handleRowSave(form, done, loading) {
console.log('handleRowSave', form);
loading();
}
function handleEdit(row) {
console.log('handleEdit', row);
handleCheckInfoChange('open');
img.value = row.img.map((v) => {
return {
url: v,
uid: Date.now(),
};
});
reportAttrs.value = row.checkReport.map((v) => {
return {
url: v,
uid: Date.now(),
};
});
crud.value.rowEdit(row);
}
function handleInfo(row) {
console.log('row', row);
crud.value.rowView(row);
}
function handleCheckInfo(type) {
console.log('checkInfo', type);
handleCheckInfoChange(type == 'open');
}
/* bol && 查看检测报告关闭其他信息 !bol && 关闭检测报告打开其他信息 */
function handleCheckInfoChange(bol = false) {
console.log('bol', bol);
option.value.group.forEach((v) => {
v.viewDisplay = bol;
});
if (bol) {
option.value.column.forEach((v) => {
v.viewDisplay = false;
});
} else {
option.value.column.forEach((v) => {
if (!v.hide || v.prop == 'img') {
v.viewDisplay = true;
}
});
}
}
async function handleRowUpdate(form, done, loading) {
console.log('update from -- ', form);
loading();
}
// #endregion
</script>

View File

@ -1,5 +1,5 @@
<template>
<section class="create_land_attrs_content_" :style="{ '--columns': props.fileNum }">
<section class="custom_attrs_upload_content_lx" :style="{ '--columns': props.fileNum }">
<el-upload
v-if="props.type != 'view'"
class="custom-form__uploader"
@ -14,13 +14,14 @@
</el-upload>
<div v-for="item in attrs_" :key="`attr_${item.uid}`" class="attrs_content__item">
<video v-if="isMP4(item.url)" :src="item.url" controls />
<img v-else :src="item.url" :alt="item.name" />
<img v-else :src="item.url" :alt="item.name" @click="handlePreview(i)" />
<el-icon v-if="props.type != 'view'" class="clear_btn" @click="handleClearAttr(item.uid)"><CircleCloseFilled /></el-icon>
</div>
<el-image-viewer v-if="previewShow" :url-list="srcList" :initial-index="index" @close="previewShow = false" />
</section>
</template>
<script setup>
import { ref, watch } from 'vue';
import { nextTick, ref, watch } from 'vue';
import { CommonUpload } from '@/apis';
const emit = defineEmits(['update:attrs']);
@ -47,6 +48,8 @@ const props = defineProps({
},
});
const attrs_ = ref([]);
const srcList = ref([]);
srcList.value = attrs_.value.map((item) => item.url);
watch(
() => props.attrs,
(val) => {
@ -54,6 +57,9 @@ watch(
},
{ deep: true, immediate: true }
);
const index = ref(0);
const previewShow = ref(false);
function handleClearAttr(uid) {
attrs_.value = attrs_.value.filter((item) => item.uid !== uid);
emit('update:attrs', attrs_.value);
@ -75,24 +81,29 @@ function isMP4(filePath) {
const regex = /\.mp4$/i;
return regex.test(filePath);
}
function handlePreview(i) {
previewShow.value = false;
nextTick(() => {
index.value = i;
previewShow.value = true;
});
}
</script>
<style lang="scss">
.create_land_attrs_content_ {
.custom_attrs_upload_content_lx {
display: grid;
flex-wrap: wrap;
grid-template-columns: repeat(var(--columns), 1fr);
box-sizing: border-box;
gap: 20px;
.custom-form__uploader {
> div {
width: 100%;
height: 100%;
aspect-ratio: 1 / 1;
box-sizing: border-box;
}
> div {
aspect-ratio: 1 / 1;
}
.attrs_content__item {
aspect-ratio: 1 / 1;
box-sizing: border-box;
position: relative;
padding: 6px;

View File

@ -1,7 +1,7 @@
<template>
<section class="create_land_attrs_content_">
<el-upload
v-if="props.view != 'view'"
v-if="props.type != 'view'"
class="custom-form__uploader"
action=""
:show-file-list="false"
@ -13,7 +13,7 @@
</el-upload>
<div v-for="item in attrs_" :key="`attr_${item.uid}`" class="attrs_content__item">
<img :src="item.url" :alt="item.name" style="" />
<el-icon v-if="props.view != 'view'" class="clear_btn" @click="handleClearAttr(item.uid)"><CircleCloseFilled /></el-icon>
<el-icon v-if="props.type != 'view'" class="clear_btn" @click="handleClearAttr(item.uid)"><CircleCloseFilled /></el-icon>
</div>
</section>
</template>
@ -27,7 +27,7 @@ const props = defineProps({
type: String,
default: 'image/*',
},
view: {
type: {
type: String,
default: 'view',
},

View File

@ -39,16 +39,16 @@
</el-popconfirm>
</template>
<template #propertyCertificateUrl-form="{ type }">
<Attrs v-model:attrs="attrs" :view="type" />
<Attrs v-model:attrs="attrs" :type="type" />
</template>
<template #landCertificateUrl-form="{ type }">
<Attrs v-model:attrs="landOwnerAttrs" :view="type" />
<Attrs v-model:attrs="landOwnerAttrs" :type="type" />
</template>
<template #coordinate-form>
<avue-input-map v-model="local" :params="params" placeholder="请选择位置"></avue-input-map>
</template>
<template #landUrl-form="{ type }">
<Attrs v-model:attrs="landAttrs" :view="type" />
<Attrs v-model:attrs="landAttrs" :type="type" />
</template>
</avue-crud>
<custom-import-excel

View File

@ -1,6 +1,409 @@
<template>
<div class="custom-page">病虫害监测</div>
<div class="custom-page">
<el-row :gutter="20">
<el-col :span="4">
<custom-table-tree title="土地分类" :data="landTypeData" @node-click="onNodeClick" />
</el-col>
<el-col :span="20">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-del="rowDel"
@row-save="rowSave"
@row-update="rowUpdate"
>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</el-col>
</el-row>
</div>
<custom-info ref="customInfoRef" :row="state.currentRow" />
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { useApp } from '@/hooks';
import { useUserStore } from '@/store/modules/user';
import { CRUD_OPTIONS } from '@/config';
import { mockData, sleep } from '@/utils';
import CustomInfo from './info.vue';
const { VITE_APP_BASE_API, VITE_APP_NAME } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const router = useRouter();
const crudRef = ref(null);
const landTypeData = ref([
{
label: '农用地',
id: '0',
children: [
{ label: '耕地', id: '01', children: [], pId: '0' },
{ label: '园地', children: [], id: '02', pId: '0' },
],
},
{
label: '建设用地',
id: '1',
children: [{ label: '城乡建设用地', children: [], id: '11', pId: '10' }],
},
{
label: '住宅用地',
id: '2',
children: [],
},
]);
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
},
form: {},
// selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '阀值设置',
addBtn: false,
selection: false,
column: [
{
label: '地块名称',
prop: 'p1',
search: true,
width: 200,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '地点',
prop: 'p2',
width: 200,
// type: 'cascader',
// hide: true,
// addDisplay: true,
// editDisplay: true,
// viewDisplay: false,
// props: {
// label: 'areaName',
// value: 'areaCode',
// children: 'areaChildVOS',
// },
// dicUrl: `${VITE_APP_BASE_API}/system/area/region?areaCode=530000`,
// dicHeaders: {
// authorization: UserStore.token,
// },
// dicFormatter: (res) => res.data ?? [],
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{
label: '土壤环境报告',
children: [
{
label: '酸碱度(PH值)',
prop: 'p3',
width: 150,
},
{
label: '土壤质地',
prop: 'p4',
width: 150,
},
{
label: '养分含量',
prop: 'p5',
width: 150,
},
{
label: '重金属含量',
prop: 'p6',
width: 150,
overHidden: true,
},
],
},
{
label: '水环境报告',
children: [
{
label: '水温',
prop: 'p7',
},
{
label: '浑浊度',
prop: 'p8',
},
{
label: '酸碱度',
prop: 'p9',
},
{
label: '盐分含量',
prop: 'p10',
width: 150,
},
{
label: '重金属和有害物质',
prop: 'p11',
width: 150,
overHidden: true,
},
],
},
{
label: '大气环境报告',
children: [
{
label: '温度',
prop: 'p12',
},
{
label: '湿度',
prop: 'p13',
},
{
label: '风速',
prop: 'p14',
},
{
label: '风向',
prop: 'p15',
},
{
label: '光照强度',
prop: 'p16',
width: 150,
},
{
label: '降雨量',
prop: 'p17',
},
{
label: '有害气体',
prop: 'p18',
width: 150,
},
],
},
{
label: '创建时间',
prop: 'createTime',
width: 200,
search: true,
display: false,
},
{
label: '更新时间',
prop: 'updateTime',
width: 200,
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
// {
// type: 'success',
// name: ({ row }) => {
// return row.status === 1 ? '' : '';
// },
// icon: ({ row }) => {
// return row.status === 1 ? 'turnOff' : 'open';
// },
// event: ({ row }) => rowStatus(row),
// },
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
const loadData = async () => {
//state.loading = true;
// GetEntityList(state.query)
// .then((res) => {
// if (res.code === 200) {
// const { current, size, total, records } = res.data;
// state.data = records;
// state.pageData = {
// currentPage: current || 1,
// pageSize: size || 10,
// total: total,
// };
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// state.data = [];
// })
// .finally(() => {
// state.loading = false;
// });
state.loading = true;
await sleep(500);
state.data = mockData(
{
p1: '202501号地',
p2: '耿马县/耿马镇',
p3: '酸性',
p4: '正常',
p5: '正常',
p7: '正常',
p6: '正常',
p8: '正常',
p9: '正常',
p10: '正常',
p11: '铜含量接近阀值',
p12: '正常',
p13: '正常',
p14: '正常',
p15: '东南风',
p16: '正常',
p17: '偏低',
p18: '正常',
// status: 1,
createTime: '2025-01-01',
updateTime: '2025-01-15',
},
10
);
state.loading = false;
};
loadData();
const onNodeClick = (data) => {
console.log('onNodeClick', data);
};
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const customInfoRef = ref(null);
const rowView = (row) => {
state.currentRow = row;
customInfoRef.value && customInfoRef.value.show();
};
//
const rowStatus = (row) => {
console.info('操作状态');
};
//
const onAdd = () => {
crudRef.value && crudRef.value.rowAdd();
};
const rowSave = (row, done, loading) => {
// AddEntity(row)
// .then((res) => {
// if (res.code === 200) {
// app.$message.success('');
// done();
// loadData();
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// })
// .finally(() => {
// loading();
// });
};
//
const rowEdit = (row) => {
crudRef.value && crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
// UpdateEntity(row)
// .then((res) => {
// if (res.code === 200) {
// app.$message.success('');
// done();
// loadData();
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// })
// .finally(() => {
// loading();
// });
};
//
const rowDel = (row) => {};
</script>

View File

@ -0,0 +1,199 @@
<template>
<el-dialog
v-model="state.visible"
draggable
title="查看"
width="70%"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="state.visible = false"
>
<div class="monitor">
<div class="monitor-tabs">
<el-radio-group v-model="state.currentTab">
<el-radio-button :value="1">虫情监测</el-radio-button>
<el-radio-button :value="2">病情监测</el-radio-button>
</el-radio-group>
</div>
<div v-if="state.currentTab === 1" class="monitor-panel">
<el-row :gutter="16">
<el-col :span="12">
<div class="monitor-panel-item">
<h3>虫情数据</h3>
<custom-echart-line :chart-data="state.pestData" height="500px" :option="state.pestOption" />
</div>
</el-col>
<el-col :span="12">
<div class="monitor-panel-item">
<h3 style="margin-bottom: 20px">数据采集</h3>
<custom-carousel-picture :data="state.pestList" />
</div>
</el-col>
</el-row>
</div>
<div v-if="state.currentTab === 2" class="monitor-panel">
<el-row :gutter="16">
<el-col :span="12">
<div class="monitor-panel-item">
<h3>病情数据</h3>
<custom-echart-bar :chart-data="state.pestData2" height="500px" :option="state.pestOption2" />
</div>
</el-col>
<el-col :span="12">
<div class="monitor-panel-item">
<h3 style="margin-bottom: 20px">数据采集</h3>
<custom-carousel-picture :data="state.pestList" type="video" />
</div>
</el-col>
</el-row>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { reactive } from 'vue';
import { getAssetsFile } from '@/utils';
const props = defineProps({
row: {
type: Object,
default: () => {},
},
});
const state = reactive({
visible: false,
currentTab: 1,
pestOption: {
// color: ['#3685fe', '#41b879', '#fed500'],
title: {
text: '',
textStyle: {
color: '#333',
},
},
xAxis: {
type: 'category',
name: '月份',
},
yAxis: {
type: 'value',
name: '数量',
},
},
pestData: [
{
type: '蝗虫',
value: 40,
name: '1月',
},
{
type: '毛虫',
value: 146,
name: '1月',
},
{
type: '其他昆虫',
value: 81,
name: '1月',
},
{
type: '蝗虫',
value: 60,
name: '2月',
},
{
type: '毛虫',
value: 186,
name: '2月',
},
{
type: '其他昆虫',
value: 101,
name: '2月',
},
{
type: '蝗虫',
value: 230,
name: '3月',
},
{
type: '毛虫',
value: 256,
name: '3月',
},
{
type: '其他昆虫',
value: 301,
name: '3月',
},
],
pestList: [
{
image: getAssetsFile('images/plantingAndBreeding/pic1.png'),
video: '',
},
{
image: getAssetsFile('images/plantingAndBreeding/pic2.png'),
video: '',
},
{
image: getAssetsFile('images/plantingAndBreeding/pic3.png'),
video: '',
},
],
pestOption2: {
// color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: '',
textStyle: {
color: '#333',
},
},
label: {
color: '#333',
},
},
pestData2: [
{ value: 80, type: '黑星病叶片', name: '1月' },
{ value: 105, type: '火疫病叶片', name: '1月' },
{ value: 187, type: '黑星病叶片', name: '2月' },
{ value: 100, type: '火疫病叶片', name: '2月' },
{ value: 125, type: '黑星病叶片', name: '3月' },
{ value: 217, type: '火疫病叶片', name: '3月' },
],
});
defineExpose({
show: () => {
state.visible = true;
},
hide: () => {
state.visible = false;
},
});
</script>
<style lang="scss" scoped>
.monitor {
padding: 20px;
margin: 0 auto;
&-tabs {
margin-bottom: 20px;
text-align: center;
}
&-panel {
&-item {
margin-bottom: 20px;
h3 {
margin-bottom: 16px;
font-size: 14px;
color: var(--el-text-color-primary);
}
}
}
}
</style>