This commit is contained in:
李想 2025-03-21 16:15:58 +08:00
commit 1700a7ab42
40 changed files with 1419 additions and 456 deletions

View File

@ -23,6 +23,7 @@
"axios": "^1.6.5",
"dayjs": "^1.11.11",
"echarts": "^5.6.0",
"echarts-gl": "^2.0.9",
"element-plus": "^2.7.3",
"file-saver": "^2.0.5",
"js-base64": "^3.7.7",

View File

@ -35,23 +35,7 @@ export default {
const chartRef = ref(null);
const { setOptions, getInstance, resize } = useEcharts(chartRef);
const option = reactive({
tooltip: {
formatter: '{b} ({c})',
},
series: [
{
type: 'pie',
radius: '72%',
center: ['50%', '55%'],
data: [],
labelLine: { show: true },
label: {
show: true,
formatter: '{b} \n ({d}%)',
color: '#B1B9D3',
},
},
],
series: [],
});
watchEffect(() => {
@ -72,8 +56,7 @@ export default {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
// option.series[0].data = props.chartData;
option.series[0].data = props.chartData;
option.series = props.chartData;
setOptions(option);
resize();
getInstance()?.off('click', onClick);

View File

@ -0,0 +1,39 @@
<template>
<iframe v-if="state.url" :src="state.url" frameborder="0" width="100%" height="100%" @load="onLoad"></iframe>
</template>
<script setup name="custom-iframe">
import { reactive, watch } from 'vue';
import { isExternal } from '@/utils/validate';
const props = defineProps({
url: {
type: String,
required: true,
},
});
const emit = defineEmits(['on-load']);
const state = reactive({
url: '',
loaded: false,
});
watch(
() => props.url,
(val) => {
if (isExternal(val)) {
state.url = val;
}
},
{
immediate: true,
}
);
const onLoad = () => {
state.loaded = true;
emit('on-load', state.loaded);
};
</script>

View File

@ -2,14 +2,18 @@
<div :class="`custom-table-tree ${shadow ? 'custom-table-tree__shadow' : ''}`">
<div v-if="title" class="title">{{ title }}</div>
<el-tree
node-key="id"
:data="data"
:node-key="option.nodeKey"
:show-checkbox="option.showCheckbox"
:default-expanded-keys="option.defaultExpandedKeys"
:default-checked-keys="option.defaultCheckedKeys"
:props="option.defaultProps"
:props="option.props ?? option.defaultProps"
@node-click="handleNodeClick"
/>
>
<template #default="{ data: rows }">
<slot :data="rows"></slot>
</template>
</el-tree>
</div>
</template>
<script setup name="custom-table-tree">
@ -21,7 +25,9 @@ const props = defineProps({
type: Object,
default: () => {
return {
nodeKey: 'id',
showCheckbox: false,
props: {},
defaultProps: {
children: 'children',
label: 'label',
@ -34,8 +40,8 @@ const props = defineProps({
});
const emit = defineEmits(['node-click']);
const handleNodeClick = (data) => {
emit('node-click', data);
const handleNodeClick = (data, node) => {
emit('node-click', data, node);
};
</script>
<style lang="scss" scoped>

View File

@ -1,4 +1,5 @@
import SvgIcon from './svg-icon';
import CustomIframe from './custom-iframe';
import CustomTableOperate from './custom-table-operate';
import CustomTableTree from './custom-table-tree';
import CustomCarouselPicture from './custom-carousel-picture';
@ -16,6 +17,7 @@ import CustomEchartPie3d from './custom-echart-pie-3d';
export {
SvgIcon,
CustomIframe,
CustomTableOperate,
CustomTableTree,
CustomCarouselPicture,

View File

@ -1,7 +1,7 @@
import * as echarts from 'echarts/core';
import { BarChart, LineChart, PieChart, MapChart, PictorialBarChart, RadarChart, GraphChart } from 'echarts/charts';
import 'echarts-gl';
import {
TitleComponent,
TooltipComponent,

View File

@ -45,7 +45,7 @@ export function isHttp(url) {
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path);
return /^(http?:|https?:|mailto:|tel:)/.test(path);
}
/**

View File

@ -1628,6 +1628,11 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
claygl@^1.2.1:
version "1.3.0"
resolved "https://registry.npmmirror.com/claygl/-/claygl-1.3.0.tgz#7a6e2903210519ac358848f5d78070ed211685f3"
integrity sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==
clean-css@^5.2.2:
version "5.3.3"
resolved "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"
@ -2177,6 +2182,14 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1:
es-errors "^1.3.0"
gopd "^1.2.0"
echarts-gl@^2.0.9:
version "2.0.9"
resolved "https://registry.npmmirror.com/echarts-gl/-/echarts-gl-2.0.9.tgz#ee228a6c7520a6fb7bbb71ea94394f3637ade033"
integrity sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==
dependencies:
claygl "^1.2.1"
zrender "^5.1.1"
echarts@^5.6.0:
version "5.6.0"
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
@ -7095,7 +7108,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zrender@5.6.1:
zrender@5.6.1, zrender@^5.1.1:
version "5.6.1"
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b"
integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==

View File

@ -1,4 +1,4 @@
import request from '@/utils/request';
import request from '@/utils/request'
// 登录方法
export function login(username, password, code, uuid) {
@ -10,7 +10,7 @@ export function login(username, password, code, uuid) {
},
method: 'post',
data: { username, password, code, uuid },
});
})
}
// 注册方法
@ -22,7 +22,7 @@ export function register(data) {
},
method: 'post',
data: data,
});
})
}
// 刷新方法
@ -30,7 +30,7 @@ export function refreshToken() {
return request({
url: '/auth/refresh',
method: 'post',
});
})
}
// 获取用户详细信息
@ -38,7 +38,7 @@ export function getInfo() {
return request({
url: '/system/user/getInfo',
method: 'get',
});
})
}
// 退出方法
@ -46,7 +46,7 @@ export function logout() {
return request({
url: '/auth/logout',
method: 'delete',
});
})
}
// 获取验证码
@ -58,5 +58,5 @@ export function getCodeImg() {
},
method: 'get',
timeout: 20000,
});
})
}

View File

@ -7,14 +7,13 @@ const TokenKey = 'Admin-Token';
const ExpiresInKey = 'Admin-Expires-In';
export function getToken() {
return true
// let token = Cookies.get(TokenKey);
// if (undefined === token || null == token || 'null' === token) {
// token = getQueryString('Authorization');
// if (null != token || 'null' !== token) Cookies.set(TokenKey, token);
// }
// if (undefined === token || null == token || 'null' === token) token = false;
// return token;
let token = Cookies.get(TokenKey);
if (undefined === token || null == token || 'null' === token) {
token = getQueryString('Authorization');
if (null != token || 'null' !== token) Cookies.set(TokenKey, token);
}
if (undefined === token || null == token || 'null' === token) token = false;
return token;
}
export function setToken(token) {

View File

@ -28,7 +28,7 @@ service.interceptors.request.use(
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
config.headers['Authorization'] = getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {

View File

@ -18,7 +18,7 @@ module.exports = {
// 部署生产环境和开发环境下的URL。
// 默认情况下Vue CLI 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
publicPath: `/sub-admin/`,
publicPath: './',
// 在npm run build 或 yarn build 时 生成文件的目录名称要和baseUrl的生产环境路径一致默认dist
outputDir: 'dist',
// 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
@ -68,7 +68,7 @@ module.exports = {
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `${name}`,
filename: `[name].[hash].js`,
chunkFilename: `[name].[hash].js`
chunkFilename: `[name].[hash].js`,
},
plugins: [
new CompressionPlugin({

View File

@ -44,35 +44,25 @@ export function delPlanCrop(params) {
//种植产物对应的种植阶段相关
// #region
// export function getPlanStage(params = {}) {
// return request('land-resource/baseInfo/planTypePage', {
// method: 'GET',
// params,
// });
// }
export function getPlanStage(params = {}) {
return request('land-resource/baseInfo/stageTypePage', {
method: 'GET',
params,
});
}
// export function upPlanStage(data = {}) {
// return request('land-resource/baseInfo/planTypeEdit', {
// method: 'PUT',
// data,
// });
// }
export function upPlanStage(data = {}) {
return request('land-resource/baseInfo/stageTypeEdit', {
method: 'PUT',
data,
});
}
// export function exportPlanStage(params = {}) {
// return request('/land-resource/baseInfo/planTypeExport', {
// method: 'GET',
// params,
// responseType: 'blob',
// });
// }
// export function delPlanStage(params) {
// return request('land-resource/baseInfo/planTypeDelete/' + params.id, {
// method: 'DELETE',
// });
// }
// #endregion
export function delPlanStage(params) {
return request('land-resource/baseInfo/stageTypeDelete/' + params.id, {
method: 'DELETE',
});
}
export function savePlanStage(data) {
return request('land-resource/baseInfo/stageTypeSave', {
@ -81,6 +71,14 @@ export function savePlanStage(data) {
});
}
export function getWorkPage(params = {}) {
return request('land-resource/planManage/workPage', {
method: 'GET',
params,
});
}
// #endregion
/* ------ 土壤类型 ------ */
// #region

View File

@ -99,6 +99,13 @@ export function delPlan(params) {
});
}
export function getPlanHistory(params = {}) {
return request('land-resource/planManage/historyPlanPage', {
method: 'GET',
params,
});
}
//种植阶段相关
export function getPlantingStage(params = {}) {
return request('land-resource/planManage/pageStage', {

View File

@ -3,14 +3,14 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-13 16:02:18
* @LastEditTime: 2025-03-20 14:04:09
-->
<template>
<el-breadcrumb class="layout-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-if="matched[0].meta.title !== '首页'" key="home" :to="{ path: '/' }">
<el-breadcrumb-item v-if="matched[0].meta.title !== '政务服务'" key="home" :to="{ path: '/' }">
<div class="layout-breadcrumb-item">
<span class="layout-breadcrumb-title">首页</span>
<span class="layout-breadcrumb-title">政务服务</span>
</div>
</el-breadcrumb-item>
<el-breadcrumb-item v-for="(item, index) in matched" :key="item.name">

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 11:48:41
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-03-12 11:10:12
* @LastEditTime: 2025-03-20 14:40:44
*/
import { createRouter, createWebHistory } from 'vue-router';
import Layout from '@/layouts/index.vue';

View File

@ -0,0 +1,326 @@
<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">
<avue-crud
ref="stateCrudRef"
v-model="stageState.form"
v-model:search="stageState.query"
v-model:page="stageState.pageData"
:table-loading="stageState.loading"
:data="stageState.data"
:option="stageState.options"
@refresh-change="stageRefresh"
@search-reset="stageSearch"
@search-change="stageSearch"
@selection-change="stageSelection"
@current-change="stageCurrent"
@size-change="stageSize"
@row-del="stageRowDel"
@row-save="stageRowSave"
@row-update="stageRowUpdate"
>
<template #menu-left>
<el-button type="primary" icon="Plus" @click="onStateAdd">新增</el-button>
</template>
<template #stage="{ row }">
<el-tag size="small">{{ stageObj[row.stage] }}</el-tag>
</template>
<template #cropId="{ row }">
{{ row.cropName }}
</template>
<template #menu="scope">
<custom-table-operate :actions="stageState.options.actions" :data="scope" />
</template>
</avue-crud>
<!-- </el-dialog> -->
</div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { useUserStore } from '@/store/modules/user';
import { getPlanStage, savePlanStage, upPlanStage, delPlanStage } from '@/apis/baseInfo.js';
import { isEmpty, imageToBase64, getAssetsFile, downloadFile } from '@/utils';
const props = defineProps({
isShow: {
type: Boolean,
default: true,
},
rowOriginal: {
type: Object,
default: () => {
return {};
},
},
});
const emit = defineEmits(['close']);
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: '采收期' },
]);
const workOptions = reactive([
{ label: '作业计划1', value: '000001' },
{ label: '作业计划2', value: '000002' },
]);
const stageObj = reactive({
0: '苗期',
1: '花果期',
2: '采收期',
});
const isShowVal = ref(false);
let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
getStageList();
}
};
onMounted(() => {
isShowVal.value = props.isShow;
currentRow = props.rowOriginal;
loadList();
// console.info('onMounted', props);
});
watch(
() => (props.isShow, props.rowOriginal),
() => {
isShowVal.value = props.isShow;
currentRow = props.rowOriginal;
console.info('watch', props);
loadList();
},
{ deep: true, immediate: true }
);
let stageState = reactive({
loading: false,
query: {
current: 1,
size: 10,
stage: null,
},
form: {},
options: {
...CRUD_OPTIONS,
addBtn: false,
selection: false,
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',
},
],
},
{
label: '所属阶段',
prop: 'stage',
type: 'select',
search: true,
dicData: stageOptions,
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{
label: '作业类型',
prop: 'workType',
// type: 'select',
search: false,
// dicData: workOptions,
rules: { required: true, message: '请选择', trigger: 'blur' },
},
{
label: '作业时间(多少天后)',
prop: 'workTime',
rules: { required: true, message: '请输入', trigger: 'blur' },
props: {
type: 'Number',
},
addDisplay: true,
editDisplay: true,
},
],
actions: [
{
name: '编辑',
icon: 'edit',
event: ({ row }) => stageRowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => stageRowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
async function getStageList() {
if (!currentRow.id) {
return app.$message.success('请选择种植产物');
}
stageState.loading = true;
getPlanStage({ ...stageState.query, cropId: currentRow.id })
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
stageState.data = records || [];
stageState.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
stageState.data = [];
})
.finally(() => {
stageState.loading = false;
});
}
const stageSearch = (params, done) => {
if (done) done();
stageState.query = params;
stageState.query.current = 1;
getStageList();
};
const stageSelection = (rows) => {
stageState.selection = rows;
};
const stageCurrent = (current) => {
stageState.query.current = current;
getStageList();
};
const stageSize = (size) => {
stageState.query.size = size;
getStageList();
};
const stageRowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`删除后信息将不可查看,确认要删除吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
console.info('删除');
delPlanStage({ id: row.id })
.then((res) => {
if (res.code === 200) {
app.$message.success('删除成功!');
getStageList();
done();
}
})
.catch((err) => {
app.$message.error(err.msg);
});
})
.catch(() => {});
};
const stageRowEdit = (row) => {
stateCrudRef.value.rowEdit(row);
};
const onStateAdd = () => {
if (!currentRow.id) {
app.$message.error('请选择种植产物');
return;
}
stateCrudRef.value.rowAdd();
};
const stageRowSave = (row, done, loading) => {
console.info('stageRowSave', row);
savePlanStage({ ...row })
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
getStageList();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
const stageRowUpdate = (row, index, done, loading) => {
console.info('stageRowUpdate');
upPlanStage(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('更新成功!');
done();
getStageList();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
const stageClose = () => {
isShowVal.value = false;
emit('close');
};
</script>

View File

@ -33,39 +33,8 @@
</template>
</avue-crud>
<stageList :is-show="stageShow" :row-original="state.currentRow" @close="stageHide"></stageList>
<el-text class="mx-1" size="large">种植阶段详情</el-text>
<div style="margin-top: 16px">
<avue-crud
ref="stateCrudRef"
v-model="stageState.form"
v-model:search="stageState.query"
v-model:page="stageState.pageData"
:table-loading="stageState.loading"
:data="stageState.data"
:option="stageState.options"
@refresh-change="stageRefresh"
@search-reset="stageSearch"
@search-change="stageSearch"
@selection-change="stageSelection"
@current-change="stageCurrent"
@size-change="stageSize"
@row-del="stageRowDel"
@row-save="stageRowSave"
@row-update="stageRowUpdate"
>
<template #menu-left>
<el-button type="primary" icon="Plus" @click="onStateAdd">新增</el-button>
</template>
<template #stage="{ row }">
<el-tag size="small">{{ stageObj[row.stage] }}</el-tag>
</template>
<template #menu="scope">
<custom-table-operate :actions="stageState.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</div>
</template>
<script setup>
@ -75,7 +44,8 @@ import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { compact } from 'lodash';
import { getPlanCrop, savePlanCrop, upPlanCrop, exportPlanCrop, delPlanCrop } from '@/apis/baseInfo';
import { getPlanCrop, savePlanCrop, upPlanCrop, exportPlanCrop, delPlanCrop, getPlanStage } from '@/apis/baseInfo';
import stageList from '../dictCrop/component/stage.vue';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
@ -83,21 +53,7 @@ const UserStore = useUserStore();
const crudRef = ref(null);
const stateCrudRef = ref(null);
const stageOptions = reactive([
{ value: '0', label: '苗期' },
{ value: '1', label: '花果期' },
{ value: '2', label: '采收期' },
]);
const stageObj = reactive({
0: '苗期',
1: '花果期',
2: '采收期',
});
const workOptions = reactive([
{ planName: '作业计划1', id: '000001' },
{ planName: '作业计划2', id: '000002' },
]);
let stageShow = ref(false);
const state = reactive({
loading: false,
query: {
@ -134,19 +90,19 @@ const state = reactive({
trigger: 'blur',
},
},
{
label: '种植时间',
prop: 'planDate',
type: 'date',
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
width: 160,
rules: {
required: true,
message: '请选择作业日期',
trigger: 'blur',
},
},
// {
// label: '',
// prop: 'planDate',
// type: 'date',
// format: 'YYYY-MM-DD',
// valueFormat: 'YYYY-MM-DD',
// width: 160,
// rules: {
// required: true,
// message: '',
// trigger: 'blur',
// },
// },
{
label: '状态',
prop: 'status',
@ -191,68 +147,6 @@ const state = reactive({
currentRow: {},
});
const stageState = reactive({
loading: false,
query: {
current: 1,
size: 10,
},
form: {},
options: {
...CRUD_OPTIONS,
addBtn: false,
selection: false,
column: [
{
label: '所属阶段',
prop: 'stage',
type: 'select',
search: true,
dicData: stageOptions,
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{
label: '作业计划',
prop: 'workId',
type: 'select',
search: true,
dicData: workOptions,
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{ label: '作业时间', prop: 'coordinate', disabled: true, addDisplay: false, editDisplay: false },
{ label: '结束时间', prop: 'createTime', disabled: true, addDisplay: false, editDisplay: false },
],
actions: [
{
name: '编辑',
icon: 'edit',
event: ({ row }) => stageRowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => stageRowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
//
const loadData = () => {
state.loading = true;
@ -380,13 +274,6 @@ const rowUpdate = (row, index, done, loading) => {
});
};
//
//
const stageRefresh = () => {
// loadData();
app.$message.success('刷新成功');
};
const rowStatus = (row) => {
console.info('操作状态');
let parmer = {
@ -433,126 +320,10 @@ const rowDel = (row, index, done) => {
const rowClick = (row) => {
state.currentRow = { ...row };
stageShow.value = true;
console.info('rowClick', state.currentRow);
getStageList();
};
async function getStageList() {
// stageState.loading = true;
// getPlantingStage({ ...stageState.query, planId: state.currentRow.id })
// .then((res) => {
// if (res.code === 200) {
// const { current, size, total, records } = res.data;
// stageState.data = records || [];
// stageState.pageData = {
// currentPage: current || 1,
// pageSize: size || 10,
// total: total,
// };
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// stageState.data = [];
// })
// .finally(() => {
// stageState.loading = false;
// });
}
const stageSearch = (params, done) => {
if (done) done();
stageState.query = params;
stageState.query.current = 1;
getStageList();
};
const stageSelection = (rows) => {
stageState.selection = rows;
};
const stageCurrent = (current) => {
stageState.query.current = current;
getStageList();
};
const stageSize = (size) => {
stageState.query.size = size;
getStageList();
};
const stageRowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`删除后信息将不可查看,确认要删除吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
console.info('删除');
// delPlantingStage({ id: row.id })
// .then((res) => {
// if (res.code === 200) {
// app.$message.success('');
// getStageList();
// done();
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// });
})
.catch(() => {});
};
const stageRowEdit = (row) => {
stateCrudRef.value.rowEdit(row);
};
const onStateAdd = () => {
if (!state.currentRow.id) {
app.$message.error('请选择种植产物');
return;
}
// stageInfoData.landName = state.currentRow.landName || '';
// stageInfoData.crop = state.currentRow.crop || '';
// stageInfoVisible.value = true;
stateCrudRef.value.rowAdd();
};
const stageRowSave = (row, done, loading) => {
row.planId = state.currentRow.planId;
console.info('stageRowSave', row);
// savePlantingStage({ ...row })
// .then((res) => {
// if (res.code === 200) {
// app.$message.success('');
// done();
// getStageList();
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// })
// .finally(() => {
// loading();
// });
};
const stageRowUpdate = (row, index, done, loading) => {
console.info('stageRowUpdate');
// editPlantingStage(row)
// .then((res) => {
// if (res.code === 200) {
// app.$message.success('');
// done();
// getStageList();
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// })
// .finally(() => {
// loading();
// });
const stageHide = () => {
stageShow.value = false;
};
</script>

View File

@ -3,13 +3,14 @@
<div>
<el-row :gutter="20">
<el-col :span="4">
<el-tree
<!-- <el-tree
style="max-width: 600px"
:data="typeTree"
node-key="areaCode"
:props="{ children: 'areaChildVOS', label: 'areaName' }"
@node-click="handleNodeClick"
/>
/> -->
<custom-table-tree title="行政区域" :data="typeTree" :option="treeOption" @node-click="handleNodeClick" />
</el-col>
<el-col :span="20">
<avue-crud
@ -126,35 +127,13 @@ const state = reactive({
currentRow: {},
});
let typeTree = ref([
// {
// label: '',
// id: '0',
// level: '0',
// children: [
// {
// label: '1',
// level: '1',
// id: '01',
// children: [
// { label: '1', children: [], id: '0101', pId: '01', level: '2' },
// { label: '2', children: [], id: '0102', pId: '01', level: '2' },
// ],
// pId: '0',
// },
// { label: '2', level: '1', children: [], id: '02', pId: '0' },
// ],
// },
// {
// label: '1',
// id: '1',
// level: '0',
// children: [{ label: '1', children: [], id: '11', pId: '10', level: '1' }],
// },
]);
let townOptions = reactive([]);
let infoData = reactive({
const typeTree = ref([]);
const treeOption = ref({
nodeKey: 'areaCode',
props: { children: 'areaChildVOS', label: 'areaName' },
});
const townOptions = reactive([]);
const infoData = reactive({
countyId: '',
townId: '',
name: '',

View File

@ -1,3 +0,0 @@
<template>
<div>开发中</div>
</template>

View File

@ -0,0 +1,259 @@
<template>
<div style="margin-top: 16px">
<el-dialog v-model="isShowVal" title="规划记录" width="1000" center @closed="stageClose">
<avue-crud
ref="listRef"
v-model="stageState.form"
v-model:search="stageState.query"
v-model:page="stageState.pageData"
:table-loading="stageState.loading"
:data="stageState.data"
:option="stageState.options"
@refresh-change="stageRefresh"
@search-reset="stageSearch"
@search-change="stageSearch"
@selection-change="stageSelection"
@current-change="stageCurrent"
@size-change="stageSize"
@row-del="stageRowDel"
@row-save="stageRowSave"
@row-update="stageRowUpdate"
>
<template #menu-left>
<!-- <el-button type="primary" icon="Plus" @click="onStateAdd">新增</el-button> -->
</template>
<template #planId="{ row }">
{{ row.planName }}
</template>
<template #menu="scope">
<custom-table-operate :actions="stageState.options.actions" :data="scope" />
</template>
</avue-crud>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { useUserStore } from '@/store/modules/user';
import { getPlantingStage, savePlantingStage, editPlantingStage, delPlantingStage, getPlanHistory } from '@/apis/land.js';
import { isEmpty, imageToBase64, getAssetsFile, downloadFile } from '@/utils';
const props = defineProps({
isShow: {
type: Boolean,
default: false,
},
rowOriginal: {
type: Object,
default: () => {
return {};
},
},
});
const emit = defineEmits(['close']);
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const listRef = ref(null);
const stageOptions = reactive([
{ value: '0', label: '苗期' },
{ value: '1', label: '花果期' },
{ value: '2', label: '采收期' },
]);
const workOptions = reactive([
{ label: '作业计划1', value: '000001' },
{ label: '作业计划2', value: '000002' },
]);
const stageObj = reactive({
0: '苗期',
1: '花果期',
2: '采收期',
});
const isShowVal = ref(false);
let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
getList();
}
};
onMounted(() => {
isShowVal.value = props.isShow;
currentRow = props.rowOriginal;
loadList();
// console.info('onMounted', props);
});
watch(
() => (props.isShow, props.rowOriginal),
() => {
isShowVal.value = props.isShow;
currentRow = props.rowOriginal;
console.info('watch', props);
loadList();
},
{ deep: true, immediate: true }
);
const stageState = reactive({
loading: false,
query: {
current: 1,
size: 10,
},
form: {},
options: {
...CRUD_OPTIONS,
addBtn: false,
selection: false,
column: [
{
label: '年度计划',
prop: 'planId',
type: 'select',
remote: false,
width: '160px',
showOverflowTooltip: true,
props: {
label: 'planName',
value: 'id',
},
dicHeaders: {
authorization: UserStore.token,
},
dicUrl: `${VITE_APP_BASE_API}/land-resource/annualManage/page`,
dicFormatter: (res) => res.data.records ?? [],
rules: [
{
required: true,
message: '请选择',
trigger: 'blur',
},
],
},
{ label: '供应商', prop: 'seedSupplier', addDisplay: false, editDisplay: true },
{ label: '种植日期', prop: 'planDate', addDisplay: true, editDisplay: true },
{ label: '种植产物', prop: 'crop', addDisplay: false, editDisplay: false },
],
actions: [
{
name: '编辑',
icon: 'edit',
event: ({ row }) => stageRowEdit(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
async function getList() {
stageState.loading = true;
getPlanHistory({ ...stageState.query, landId: currentRow.landId })
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
stageState.data = records || [];
stageState.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
stageState.data = [];
})
.finally(() => {
stageState.loading = false;
});
}
const stageSearch = (params, done) => {
if (done) done();
stageState.query = params;
stageState.query.current = 1;
getList();
};
const stageSelection = (rows) => {
stageState.selection = rows;
};
const stageCurrent = (current) => {
stageState.query.current = current;
getList();
};
const stageSize = (size) => {
stageState.query.size = size;
getList();
};
const stageRowEdit = (row) => {
listRef.value.rowEdit(row);
};
const onStateAdd = () => {
if (!currentRow.landId) {
app.$message.error('请选择种植规划');
return;
}
listRef.value.rowAdd();
};
const stageRowSave = (row, done, loading) => {
row.landId = currentRow.landId;
console.info('stageRowSave', row);
savePlantingStage({ ...row })
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
getList();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
const stageRowUpdate = (row, index, done, loading) => {
console.info('stageRowUpdate');
editPlantingStage(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('更新成功!');
done();
getList();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
const stageClose = () => {
isShowVal.value = false;
emit('close');
};
</script>

View File

@ -78,12 +78,17 @@ const stageObj = reactive({
2: '采收期',
});
const handleWorkChange = async (value, form, done) => {
stageState.form.workTime = value.item?.workTime || '';
};
const isShowVal = ref(false);
let currentRow = reactive({});
const loadList = () => {
if (isShowVal.value) {
console.info('loadList', props);
getStageList();
}
};
onMounted(() => {
@ -107,13 +112,37 @@ const stageState = reactive({
query: {
current: 1,
size: 10,
stage: null,
},
form: {},
options: {
...CRUD_OPTIONS,
addBtn: false,
selection: false,
group: [
{ label: '所属阶段', prop: 'stage' },
{ label: '作业类型', prop: 'workType' },
{ label: '作业时间', prop: 'operationDate' },
],
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' }],
// },
{
label: '所属阶段',
prop: 'stage',
@ -127,19 +156,42 @@ const stageState = reactive({
},
},
{
label: '作业计划',
label: '作业类型',
prop: 'workId',
type: 'select',
search: true,
dicData: workOptions,
rules: {
required: true,
message: '请选择',
trigger: 'blur',
search: false,
remote: false,
width: '160px',
showOverflowTooltip: true,
props: {
label: 'workType',
value: 'id',
},
dicHeaders: {
authorization: UserStore.token,
},
dicUrl: `${VITE_APP_BASE_API}/land-resource/planManage/workPage?current=1&size=999`,
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,
},
{
label: '作业时间',
prop: 'operationDate',
editDisplay: false,
addDisplay: false,
},
{ label: '作业时间', prop: 'coordinate', addDisplay: false, editDisplay: false },
{ label: '结束时间', prop: 'createTime', addDisplay: false, editDisplay: false },
],
actions: [
{
@ -166,7 +218,7 @@ const stageState = reactive({
async function getStageList() {
stageState.loading = true;
getPlantingStage({ ...stageState.query, planId: currentRow.currentRow.id })
getPlantingStage({ ...stageState.query, planId: currentRow.id })
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
@ -245,7 +297,7 @@ const onStateAdd = () => {
};
const stageRowSave = (row, done, loading) => {
row.planId = currentRow.planId;
row.planId = currentRow.id;
console.info('stageRowSave', row);
savePlantingStage({ ...row })
.then((res) => {

View File

@ -51,7 +51,8 @@
</template>
</avue-crud>
<stageList :is-show="stageShow" :row-original="stageRowVal" @close="stageHide"></stageList>
<stageList :is-show="stageShow" :row-original="currentRowVal" @close="stageHide"></stageList>
<historyList :is-show="historyShow" :row-original="currentRowVal" @close="historyHide"></historyList>
</div>
</template>
<script setup>
@ -71,7 +72,8 @@ import {
delPlantingStage,
} from '@/apis/land.js';
import { isEmpty, imageToBase64, getAssetsFile, downloadFile } from '@/utils';
import stageList from '../../component/plantPlan/compoent/stage.vue';
import stageList from '../../component/plantPlan/component/stage.vue';
import historyList from '../../component/plantPlan/component/history.vue';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
@ -90,7 +92,8 @@ const workOptions = reactive([
]);
let stageShow = ref(false);
let stageRowVal = reactive({});
let currentRowVal = reactive({});
let historyShow = ref(false);
const state = reactive({
loading: false,
@ -229,6 +232,11 @@ const state = reactive({
icon: 'edit',
event: ({ row }) => toStage(row),
},
{
name: '规划记录',
icon: 'Memo',
event: ({ row }) => toHistory(row),
},
{
type: 'danger',
name: '删除',
@ -409,9 +417,9 @@ const rowDel = (row, index, done) => {
};
const toStage = (row) => {
stageRowVal = row;
currentRowVal = row;
stageShow.value = true;
console.info('toStage', stageRowVal);
console.info('toStage', currentRowVal);
};
//
@ -467,4 +475,12 @@ const rowClick = (row) => {
const stageHide = () => {
stageShow.value = false;
};
const toHistory = (row) => {
currentRowVal = row;
historyShow.value = true;
};
const historyHide = () => {
historyShow.value = false;
};
</script>

View File

@ -91,31 +91,61 @@ const state = reactive({
overHidden: true,
},
{
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 ?? [],
label: '基地分类',
prop: 'type',
type: 'select',
dicData: [
{
label: '种植基地',
value: 1,
},
{
label: '养殖基地',
value: 2,
},
],
rules: {
required: true,
message: '请选择',
message: '请输入',
trigger: 'blur',
},
overHidden: true,
},
{
label: '地块名',
prop: 'landName',
width: 200,
formslot: true,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
// {
// label: '',
// prop: 'p2',
// width: 200,
// type: 'select',
// // 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',
// },
// overHidden: true,
// },
{
label: '区域位置',
prop: 'p3',
@ -136,26 +166,6 @@ const state = reactive({
trigger: 'blur',
},
},
{
label: '基地分类',
prop: 'type',
type: 'select',
dicData: [
{
label: '种植基地',
value: 1,
},
{
label: '养殖基地',
value: 2,
},
],
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '状态',
prop: 'status',
@ -273,8 +283,8 @@ const loadData = async () => {
await sleep(500);
state.data = mockData(
{
p1: '202501基地',
p2: '耿马镇1号地块',
p1: '耿马镇一号基地',
p2: '耿马镇2025001号地块',
p3: '耿马傣族佤族自治县/耿马镇',
p4: '1000',
p5: '张三',

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2022-09-18 21:24:29
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-28 11:04:41
* @LastEditTime: 2025-03-20 14:24:31
*/
import { defineConfig, loadEnv } from 'vite';

View File

@ -21,6 +21,7 @@
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.5",
"echarts": "^5.6.0",
"echarts-gl": "^2.0.9",
"element-plus": "^2.7.2",
"js-base64": "^3.7.6",
"lodash": "^4.17.21",

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -80,7 +80,7 @@ const chartsData = reactive({
},
//
grid: {
x: '10%',
x: '16%',
x2: '5%',
y: '15%',
y2: '15%',

View File

@ -6,16 +6,17 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
const dataList = reactive([
{ name: '打车费', val: 1230, itemStyle: { color: 'rgba(56, 136, 235, 1)' } },
{ name: '住宿费', val: 300, itemStyle: { color: 'rgba(113, 70, 159, 1)' } },
{ name: '办公费', val: 800, itemStyle: { color: 'rgba(237, 171, 87, 1)' } },
{ name: '差旅费', val: 500, itemStyle: { color: 'rgba(231, 89, 77, 1)' } },
{ name: '肉类', val: 1230, itemStyle: { color: 'rgba(56, 136, 235, 1)' } },
{ name: '水果', val: 300, itemStyle: { color: 'rgba(113, 70, 159, 1)' } },
{ name: '蔬菜', val: 800, itemStyle: { color: 'rgba(237, 171, 87, 1)' } },
{ name: '水产', val: 500, itemStyle: { color: 'rgba(231, 89, 77, 1)' } },
{ name: '谷物', val: 600, itemStyle: { color: 'rgba(231, 89, 77, 1)' } },
]);
const heightProportion = ref(0.3); //
// series-surface.parametricEquation
// #region
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, height) => {
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, height, radiusScale) => {
//
let midRatio = (startRatio + endRatio) / 3;
@ -54,22 +55,22 @@ const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h
x: function (u, v) {
if (u < startRadian) {
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
if (u > endRadian) {
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
},
y: function (u, v) {
if (u < startRadian) {
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
if (u > endRadian) {
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
}
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate * radiusScale;
},
z: function (u, v) {
@ -86,7 +87,7 @@ const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h
// #endregion
// 3D
const getPie3D = (pieData, internalDiameterRatio) => {
const getPie3D = (pieData, internalDiameterRatio, radiusScale = 1.8) => {
let series = [];
let sumValue = 0;
let startValue = 0;
@ -138,7 +139,8 @@ const getPie3D = (pieData, internalDiameterRatio) => {
false,
false,
k,
series[i].pieData.value
series[i].pieData.value,
radiusScale
);
startValue = endValue;
@ -148,8 +150,8 @@ const getPie3D = (pieData, internalDiameterRatio) => {
const midRadian = (series[i].pieData.startRatio + series[i].pieData.endRatio) * Math.PI;
// v=0
const radius = 1 + k; //
const posX = Math.cos(midRadian) * radius;
const posY = Math.sin(midRadian) * radius;
const posX = Math.cos(midRadian) * radius * radiusScale;
const posY = Math.sin(midRadian) * radius * radiusScale;
//
const posZ = heightProportion.value * series[i].pieData.value;
let flag = (midRadian >= 0 && midRadian <= Math.PI / 2) || (midRadian >= (3 * Math.PI) / 2 && midRadian <= Math.PI * 2) ? 1 : -1;
@ -169,15 +171,15 @@ const getPie3D = (pieData, internalDiameterRatio) => {
{
type: 'scatter3D',
label: {
show: true,
show: true, //label
distance: 0,
position: 'center',
textStyle: {
color: '#ffffff',
backgroundColor: color,
borderWidth: 2,
fontSize: 14,
padding: 10,
fontSize: 8,
padding: 4,
borderRadius: 4,
},
formatter: '{b}',
@ -241,8 +243,8 @@ const getPie3D = (pieData, internalDiameterRatio) => {
},
parametricEquation: {
u: {
min: 0.74, // 92%
max: 0.75, // 100%
min: 0.94, // 92%
max: 0.95, // 100%
step: 0.0001,
},
v: {
@ -277,8 +279,8 @@ const getPie3D = (pieData, internalDiameterRatio) => {
},
parametricEquation: {
u: {
min: 0.54, // 92%
max: 0.55, // 100%
min: 0.94, // 92%
max: 0.95, // 100%
step: 0.0001,
},
v: {
@ -309,7 +311,6 @@ dataList.forEach((item) => {
const chartsData = reactive({
option: {
backgroundColor: '#2d58e4',
xAxis3D: {
min: -1.5,
max: 1.5,
@ -325,7 +326,7 @@ const chartsData = reactive({
grid3D: {
show: false,
boxHeight: 4,
bottom: '50%',
top: '10%',
viewControl: {
distance: 380,
alpha: 25,

View File

@ -7,7 +7,7 @@
<div class="left-charts-item">
<customBack top-title="生产经营主体类别统计" :top-postion="'left'">
<template #back>
<!-- <entitiesCategoryCharts></entitiesCategoryCharts> -->
<entitiesCategoryCharts></entitiesCategoryCharts>
</template>
</customBack>
</div>

View File

@ -0,0 +1,131 @@
<template>
<div class="cases-alerts-warp">
<div class="cases-alerts" :style="{ 'background-image': 'url(' + getAssetsFile('images/inputs/partbg1.png') + ')' }">
<div class="cases-alerts-content">
<div class="cases-alerts-item-pos">
<template v-for="(n, index) in datalist" :key="index">
<div class="cases-alerts-item">
<div class="header" :style="{ 'background-image': 'url(' + getAssetsFile('images/inputs/partbg2.png') + ')' }">
<div class="title">{{ n.title }}</div>
</div>
<div class="content">
<template v-for="(m, mindex) in n.valStr" :key="mindex">
<div class="content-item" :style="{ 'background-image': 'url(' + getAssetsFile('images/inputs/partbg3.png') + ')' }">
<div class="num">{{ m }}</div>
</div>
</template>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { isEmpty, getAssetsFile } from '@/utils';
const datalist = reactive([
{ title: '案件次数', val: 100 },
{ title: '预警次数', val: 236 },
]);
onMounted(() => {
if (datalist.length && datalist.length > 0) {
datalist.forEach((m) => {
let valStr = '';
if (m.val < 1000 && m.val >= 100) {
valStr = '0' + m.val;
} else if (m.val < 100 && m.val >= 10) {
valStr = '00' + m.val;
} else if (m.val < 10) {
valStr = '000' + m.val;
} else {
valStr = m.val.toFixed(0);
}
m.valStr = [...valStr];
// console.info('valStr', m.valStr);
});
}
});
</script>
<style lang="scss" scoped>
div {
box-sizing: border-box;
}
.cases-alerts-warp {
height: 100%;
padding: 8px;
.cases-alerts {
width: 100%;
height: 100%;
background-size: 100% 100%;
background-repeat: no-repeat;
.cases-alerts-content {
width: 100%;
height: 100%;
padding: 0 20px;
display: inline-flex;
justify-content: center;
flex-direction: column;
.cases-alerts-item-pos {
width: 100%;
display: inline-flex;
justify-content: space-around;
}
.cases-alerts-item {
display: inline-block;
width: calc((100% - 20px) / 2);
.header,
.content {
display: inline-flex;
width: 100%;
}
.header {
background-size: 100% 100%;
background-repeat: no-repeat;
line-height: 32px;
height: 32px;
display: inline-block;
width: 100%;
.title {
text-align: center;
font-size: 16px;
font-weight: bold;
transform: skewX(-8deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
letter-spacing: 8px;
text-shadow: -6px 0 0 1px #add8f1;
}
}
.content {
justify-content: space-around;
margin-top: 50%;
.content-item {
height: 36px;
display: inline-block;
width: calc((100% - 20px) / 4);
background-size: 100% 100%;
background-repeat: no-repeat;
line-height: 36px;
text-align: center;
.num {
text-align: center;
font-size: 20px;
font-weight: bold;
transform: skewX(-8deg);
background: linear-gradient(to bottom, '#ff7e5f', '#548fff');
-webkit-background-clip: text;
color: #fff;
text-shadow: -6px 0 0 1px #add8f1;
}
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,171 @@
<template>
<div class="demo roll-list-land-plan" 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="datalist">
<div v-for="(item, index) in datalist" :key="index" class="list-item">
<div class="list-item-content">
<div class="list-item-boday item-warp" :style="{ flex: listKeys.length }">
<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>
</div>
</div>
</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: '农药', value: 357 },
{ title: '肥料', value: 159 },
{ title: '种源', value: 516 },
{ title: '兽药', value: 82 },
{ title: '农机', value: 45 },
]);
let datalist = computed(() => {
return list.map((m) => {
return { ...m, percent: parseInt(Number(m.value / max.value) * 200) };
});
});
let max = computed(() => {
let valueList = new Set(list.map((item) => item.value));
let sortValue = [...valueList].sort((a, b) => b - a) || [];
// console.info('valueList', sortValue);
return sortValue.length ? sortValue[0] : 0;
});
const listKeys = reactive(['title', 'value']);
const listKeysHeader = reactive({
title: '分类',
value: '数量',
});
const classOptions = {
singleHeight: 48,
};
onMounted(() => {});
</script>
<style scoped lang="scss">
.roll-list-land-plan {
margin-top: 8px;
::v-deep() {
.el-progress-bar__outer {
background: transparent;
}
}
.scroll-wrap {
height: 90%;
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: #6cd1f9 !important;
}
&.zebra-b {
background: #051225 !important;
}
}
}
.item-warp {
display: inline-flex;
justify-content: space-around;
.item-td {
display: inline-block;
vertical-align: middle;
text-align: center;
color: #fff;
}
.item-content {
width: 100%;
display: inline-flex;
justify-content: flex-start;
margin: 8px 0;
.label,
.val {
display: inline-block;
vertical-align: middle;
}
.label {
width: 50px;
color: #fff;
}
.val {
width: calc(100% - 50px);
.progress-warp {
.progress {
height: 10px;
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%;
}
}
}
}
}
}
.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

@ -6,7 +6,9 @@
<el-col :span="6" class="left-charts">
<div class="left-charts-item">
<customBack top-title="投入品分类统计" :top-postion="'left'">
<template #back> </template>
<template #back>
<inputsType></inputsType>
</template>
</customBack>
</div>
<div class="left-charts-item">
@ -30,7 +32,9 @@
<el-col :span="6" class="right-charts">
<div class="right-charts-item">
<customBack top-title="案件次数" :top-postion="'right'">
<template #back> </template>
<template #back>
<casesAlerts></casesAlerts>
</template>
</customBack>
</div>
<div class="right-charts-item">
@ -61,6 +65,8 @@ import inputsGmp from './components/inputsGmp.vue';
import landbreedCharts from './components/landbreedCharts.vue';
import monthlyuseCharts from './components/monthlyuseCharts.vue';
import dealerDistributionCharts from './components/dealerDistributionCharts.vue';
import casesAlerts from './components/casesAlerts.vue';
import inputsType from './components/inputsType.vue';
</script>
<style lang="scss" scoped>
.data-home-index {

View File

@ -1,12 +1,12 @@
<template>
<div class="distribution-charts">
<custom-echart-pie :chart-data="plantBreed.valData" height="100%" :option="plantBreed.option" />
<custom-echart-pie :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const plantBreed = reactive({
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
@ -42,6 +42,9 @@ const plantBreed = reactive({
itemStyle: {
borderRadius: 5,
},
// animationDuration: 15000,
// //
// animationEasing: 'elasticOut',
},
],
},

View File

@ -0,0 +1,65 @@
<template>
<div class="insect-pests-charts">
<custom-echart-bar :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const chartsData = reactive({
option: {
grid: {
left: '3%',
right: '4%',
bottom: '2%',
top: '18%',
containLabel: true,
},
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
label: {
color: '#333',
},
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
},
},
legend: {
show: false,
},
},
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: '大兴乡' },
],
});
</script>
<style lang="scss" scoped>
.insect-pests-charts {
height: 100%;
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<div class="plant-type-charts">
<custom-echart-pie :chart-data="chartsData.valData" height="100%" :option="chartsData.option" />
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
const chartsData = reactive({
option: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: ' ',
textStyle: {
color: '#333',
},
},
legend: {
data: ['甘蔗', '菠萝', '土豆', '番茄', '白菜', '其他'],
right: '0', // 10%
top: 'middle', //
orient: 'vertical', //
itemWidth: 15, //
itemHeight: 8, //
textStyle: {
fontSize: 10, //
color: '#fff', //
},
},
label: {
color: '#333',
},
series: [
{
type: 'pie',
radius: [20, 80],
roseType: 'area',
center: ['40%', '50%'],
label: {
show: false,
},
itemStyle: {
borderRadius: 5,
},
// animationDuration: 15000,
// //
// animationEasing: 'elasticOut',
},
],
},
valData: [
{ value: 205.6, name: '甘蔗' },
{ value: 308.7, name: '菠萝' },
{ value: 359.6, name: '土豆' },
{ value: 452.6, name: '番茄' },
{ value: 388.9, name: '白菜' },
{ value: 512.5, name: '其他' },
],
});
onMounted(() => {
if (chartsData.valData && chartsData.valData.length) {
chartsData.valData.forEach((m, index) => {
let num = 100;
m.value = (Number(m.value) + Math.random() + num).toFixed(2);
});
}
});
</script>
<style lang="scss" scoped>
.plant-type-charts {
height: 100%;
}
</style>

View File

@ -4,15 +4,53 @@
<template #center>
<el-row style="width: 100%; height: 100%">
<el-col :span="6" class="left-charts">
<div class="left-charts-item"></div>
<div class="left-charts-item"></div>
<div class="left-charts-item"></div>
<div class="left-charts-item">
<customBack top-title="智慧种植种类分析" :top-postion="'left'">
<template #back>
<plantTypeCharts></plantTypeCharts>
</template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="昆虫害监测" :top-postion="'left'">
<template #back></template>
</customBack>
</div>
<div class="left-charts-item">
<customBack top-title="病理害监测" :top-postion="'left'">
<template #back></template>
</customBack>
</div>
</el-col>
<el-col :span="12">
<el-row style="height: 60%">
<el-col :span="24"> 1-1</el-col>
</el-row>
<el-row style="height: 40%">
<el-col :span="12">
<customBack top-title="水肥检测分析" :top-postion="'left'"> <template #back></template> </customBack>
</el-col>
<el-col :span="12">
<customBack top-title="智慧水肥灌溉" :top-postion="'right'"> <template #back></template> </customBack>
</el-col>
</el-row>
</el-col>
<el-col :span="12"></el-col>
<el-col :span="6" class="right-charts">
<div class="right-charts-item"></div>
<div class="right-charts-item"></div>
<div class="right-charts-item"></div>
<div class="right-charts-item">
<customBack top-title="智慧监控 A区 QQI" :top-postion="'right'">
<template #back></template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="种植产量分析" :top-postion="'right'">
<template #back></template>
</customBack>
</div>
<div class="right-charts-item">
<customBack top-title="水质检测分析" :top-postion="'right'">
<template #back></template>
</customBack>
</div>
</el-col>
</el-row>
</template>
@ -21,6 +59,8 @@
</template>
<script setup>
import baseBg from '@/components/baseBg.vue';
import customBack from '@/components/customBack.vue';
import plantTypeCharts from './components/plantTypeCharts.vue';
</script>
<style lang="scss" scoped>
.data-home-index {

View File

@ -1601,6 +1601,11 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
claygl@^1.2.1:
version "1.3.0"
resolved "https://registry.npmmirror.com/claygl/-/claygl-1.3.0.tgz#7a6e2903210519ac358848f5d78070ed211685f3"
integrity sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==
clone-regexp@^1.0.0:
version "1.0.1"
resolved "https://registry.npmmirror.com/clone-regexp/-/clone-regexp-1.0.1.tgz#051805cd33173375d82118fc0918606da39fd60f"
@ -2086,6 +2091,14 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1:
es-errors "^1.3.0"
gopd "^1.2.0"
echarts-gl@^2.0.9:
version "2.0.9"
resolved "https://registry.npmmirror.com/echarts-gl/-/echarts-gl-2.0.9.tgz#ee228a6c7520a6fb7bbb71ea94394f3637ade033"
integrity sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==
dependencies:
claygl "^1.2.1"
zrender "^5.1.1"
echarts@^5.6.0:
version "5.6.0"
resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6"
@ -6686,7 +6699,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zrender@5.6.1:
zrender@5.6.1, zrender@^5.1.1:
version "5.6.1"
resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b"
integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==