This commit is contained in:
lzc 2025-03-05 17:30:44 +08:00
commit 329b47255c
12 changed files with 739 additions and 348 deletions

View File

@ -5,18 +5,22 @@
import { ref, reactive, watchEffect } from 'vue';
import { cloneDeep } from 'lodash';
import { useEcharts } from '../../hooks/useEcharts';
export default {
name: 'CustomEchartBar',
props: {
chartData: {
type: Array,
default: () => [],
required: true,
},
option: {
type: Object,
default: () => ({}),
},
type: {
type: String,
default: 'bar',
},
width: {
type: String,
default: '100%',
@ -25,14 +29,11 @@ export default {
type: String,
default: 'calc(100vh - 78px)',
},
seriesColor: {
type: String,
default: '#1890ff',
},
},
setup(props) {
emits: ['click'],
setup(props, { emit }) {
const chartRef = ref(null);
const { setOptions } = useEcharts(chartRef);
const { setOptions, getInstance } = useEcharts(chartRef);
const option = reactive({
tooltip: {
trigger: 'axis',
@ -44,6 +45,12 @@ export default {
},
},
},
legend: {
top: 30,
},
grid: {
top: 60,
},
xAxis: {
type: 'category',
data: [],
@ -51,14 +58,7 @@ export default {
yAxis: {
type: 'value',
},
series: [
{
name: 'bar',
type: 'bar',
data: [],
color: props.seriesColor,
},
],
series: [],
});
watchEffect(() => {
@ -69,16 +69,36 @@ export default {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
let seriesData = props.chartData.map((item) => {
return item.value;
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) => {
const barStyle = props.option?.barStyle ?? {};
let obj = { name: type, type: props.type, ...barStyle };
let data = [];
xAxisData.forEach((x) => {
let dataArr = props.chartData.filter((item) => type === item.type && item.name == x);
if (dataArr && dataArr.length > 0) {
data.push(dataArr[0].value);
} else {
data.push(null);
}
});
obj['data'] = data;
if (props.option?.color) {
obj.color = props.option?.color[index];
}
seriesData.push(obj);
});
let xAxisData = props.chartData.map((item) => {
return item.name;
});
option.series[0].data = seriesData;
option.series[0].color = props.seriesColor;
option.series = seriesData;
option.xAxis.data = xAxisData;
setOptions(option);
getInstance()?.off('click', onClick);
getInstance()?.on('click', onClick);
}
function onClick(params) {
emit('click', params);
}
return { chartRef };
},

View File

@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash';
import { useEcharts } from '../../hooks/useEcharts';
export default {
name: 'CustomEchartMultiLine',
name: 'CustomEchartLine',
props: {
chartData: {
type: Array,

View File

@ -0,0 +1,85 @@
<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: 'CustomEchartMixin',
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: {
type: 'value',
},
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;
seriesData.push(obj);
});
option.series = seriesData;
option.xAxis.data = xAxisData;
setOptions(option);
}
return { chartRef };
},
};
</script>

View File

@ -4,6 +4,7 @@ import CustomImportExcel from './custom-import-excel';
import CustomRichEditor from './custom-rich-editor';
import CustomEchartBar from './custom-echart-bar';
import CustomEchartPie from './custom-echart-pie';
import CustomEchartMultiLine from './custom-echart-multi-line';
import CustomEchartLine from './custom-echart-line';
import CustomEchartMixin from './custom-echart-mixin';
export { SvgIcon, CustomTableOperate, CustomImportExcel, CustomRichEditor, CustomEchartBar, CustomEchartPie, CustomEchartMultiLine };
export { SvgIcon, CustomTableOperate, CustomImportExcel, CustomRichEditor, CustomEchartBar, CustomEchartPie, CustomEchartLine, CustomEchartMixin };

View File

@ -3,7 +3,7 @@
* @Author: zenghua.wang
* @Date: 2023-06-20 11:48:41
* @LastEditors: zenghua.wang
* @LastEditTime: 2025-02-28 13:50:00
* @LastEditTime: 2025-03-04 10:42:19
*/
import { createRouter, createWebHistory } from 'vue-router';
import Layout from '@/layouts/index.vue';
@ -12,7 +12,7 @@ import resourceRouter from './modules/resource';
import plantingAndBreedingRouter from './modules/plantingAndBreeding';
import landsRoutes from './modules/lands';
import annualplanRoutes from './modules/annualplan';
import statisticsRoutes from './modules/statistics';
import statisticsRoutes from './modules/statisticAnalysis';
import dictRoutes from './modules/dict';
export const constantRoutes = [

View File

@ -0,0 +1,32 @@
import Layout from '@/layouts/index.vue';
import Views from '@/layouts/Views.vue';
export default [
{
path: '/sub-government-affairs-service/analysis',
name: 'analysis',
component: Layout,
redirect: '/sub-government-affairs-service/analysis-land',
meta: { title: '统计分析', icon: 'icon-test' },
children: [
{
path: '/sub-government-affairs-service/analysis-land',
component: () => import('@/views/statisticAnalysis/land/index.vue'),
name: 'analysis-land',
meta: { title: '土地利用与规划分析', icon: 'Document' },
},
// {
// path: '/sub-government-affairs-service/analysis-agriculture',
// name: 'analysis-agriculture',
// component: () => import('@/views/statisticAnalysis/agriculture/index.vue'),
// meta: { title: '农业生产效率分析', icon: 'Document' },
// },
// {
// path: '/sub-government-affairs-service/analysis-environment',
// name: 'analysis-environment',
// component: () => import('@/views/statisticAnalysis/environment/index.vue'),
// meta: { title: '环境影响与经济效益分析', icon: 'Document' },
// },
],
},
];

View File

@ -0,0 +1,86 @@
<template>
<el-upload
ref="upload"
class="upload-demo"
:limit="props.limit"
:auto-upload="false"
:file-list="fileList"
:on-change="fileChange"
:before-remove="handleDelFile"
>
<el-button type="primary" :disabled="fileList.length == 5">点击上传</el-button>
<template #tip>
<div v-if="props.format.length" class="el-upload__tip">只能上传{{ props.format.join() }} 文件且不超过20MB</div>
</template>
</el-upload>
</template>
<script setup>
import { ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['update:list']);
const props = defineProps({
list: {
type: Array,
default: () => [],
},
limit: {
type: Number,
default: 5,
},
format: {
type: Array,
default: () => [],
},
size: {
type: Number,
default: 20,
},
});
/* --------------- data --------------- */
// #region
const fileList = ref([]);
watch(
() => props.list,
(val) => {
fileList.value = val;
},
{ deep: true, immediate: true }
);
// #endregion
/* --------------- methods --------------- */
// #region
function fileChange(file, list) {
let formatReg = true;
if (props.format.length > 0) {
formatReg = props.format.includes(file.name.slice(file.name.lastIndexOf('.') + 1));
}
const fileSize = file.size / 1024 / 1024 < props.size;
if (!formatReg) {
ElMessage.error(`上传文件格式不正确,仅支持${props.format.join('/ ')}格式`);
delAttr(file.uid, list);
}
if (!fileSize) {
ElMessage.error(`上传文件大小不能超过${props.size}MB`);
delAttr(file.uid, list);
}
fileList.value = list;
}
function handleDelFile(file) {
delAttr(file.uid, fileList.value);
}
function delAttr(id, list = []) {
if (!id || !list) return false;
let i = list.findIndex((v) => v.uid == id);
if (i < 0) return;
else list.splice(i, 1);
}
// #endregion
</script>
<style lang="scss" scoped></style>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="_visible" title="案件登记处理" append-to="#app" @close="handleClose">
<el-dialog v-model="_visible" :width="800" title="案件登记处理" append-to="#app" @close="handleClose">
<h3>案件信息&gt;</h3>
<section class="case_info">
<div v-for="item in info" :key="`${item.key}_box`" :style="{ '--w': item.line ? '100%' : 'calc(50% - 12px)' }">
@ -7,14 +7,40 @@
<div class="text">{{ item.value }}</div>
</div>
</section>
<h3>案件处理&gt;</h3>
<el-form></el-form>
<h3>案件结果&gt;</h3>
<el-form :model="form" label-width="140px">
<h3>案件处理&gt;</h3>
<el-form-item label="案情记录:" prop="record">
<el-input v-model="form.record" type="textarea" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="现场取证:(照片、视频)" label-width="200px"></el-form-item>
<el-form-item label="执法文书:">
<section>
<el-radio-group v-model="form.document">
<el-radio :value="0" label="协助调查函"></el-radio>
<el-radio :value="1" label="抽样取样凭证"></el-radio>
<el-radio :value="2" label="检测报告"></el-radio>
<el-radio :value="3" label="其他文书"></el-radio>
</el-radio-group>
<FileUpload v-model:list="form.proof" />
</section>
</el-form-item>
<h3>案件结果&gt;</h3>
<el-form-item label="案件处理结果:" class="result_content">
<el-radio-group v-model="form.result">
<el-radio :value="0" label="正常营业,无违规行为"></el-radio>
<el-radio :value="1" label="简易程序,当场行政处罚"></el-radio>
<el-radio :value="2" label="普通程序,当场处罚立案审批"></el-radio>
<el-radio :value="3" label="移送程序,案件移送"></el-radio>
<el-radio :value="4" label="特殊程序,案件终止"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import FileUpload from './FileUpload.vue';
const emit = defineEmits(['update:visible']);
const props = defineProps({
@ -23,16 +49,21 @@ const props = defineProps({
default: false,
},
});
watch(
() => props.visible,
(val) => {
_visible.value = val;
}
);
/* --------------- data --------------- */
// #region
const _visible = ref(false);
watch(
() => props.visible,
(val) => {
_visible.value = val;
},
{
deep: true,
immediate: true,
}
);
const info = reactive([
{
label: '案件名称',
@ -76,7 +107,13 @@ const info = reactive([
line: true,
},
]);
const form = reactive({
record: '',
proof: [],
document: 1,
attrs: [],
result: 1,
});
// #endregion
/* --------------- methods --------------- */
@ -109,4 +146,12 @@ function handleClose() {
}
}
}
::v-deep() {
.result_content {
.el-radio-group {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
}
}
</style>

View File

@ -1,6 +1,7 @@
<template>
<CustomCard>
<avue-crud
ref="crudRef"
v-model:page="page"
v-model:search="searchData"
:option="option"
@ -10,29 +11,41 @@
@size-change="handleSizeChange"
@search-change="handleSearch"
@row-save="handleRowSave"
@row-update="handleExamine"
>
<template #menu="{ row }">
<el-button type="primary" @click="handleInfo(row)">登记处理</el-button>
</template>
<template #sceneProof-form>
<section>
<span>照片视频</span>
</section>
</template>
<template #attrs_-form>
<FileUpload v-model:list="examineForm.attrs" :format="['rar', 'zip', 'doc', 'docx', 'pdf']" />
</template>
</avue-crud>
</CustomCard>
<Register v-model:visible="caseInfo.visible" />
</template>
<script setup>
import { ref } from 'vue';
import { ref, reactive, h } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import CustomCard from '@/components/CustomCard.vue';
import { useUserStore } from '@/store/modules/user';
import Register from './common/Register.vue';
import FileUpload from './common/FileUpload.vue';
import { add } from 'lodash';
const { VITE_APP_BASE_API } = import.meta.env;
const UserStore = useUserStore();
/* --------------- data --------------- */
// #region
const crudRef = ref();
const searchData = ref({
code: '',
caseNum: '',
process: '',
});
const page = ref({
@ -43,8 +56,8 @@ const page = ref({
const _loading = ref(false);
const data = ref([
{
id: '1',
code: '111111111',
id_: '1',
caseNum: '111111111',
name: '张三',
unit: '123333333333333333333333333333',
owner: '张三',
@ -52,8 +65,8 @@ const data = ref([
date: '2020-02-02',
},
{
id: '2',
code: '222222222',
id_: '2',
caseNum: '222222222',
name: '张三',
unit: '123333333333333333333333333333',
owner: '张三',
@ -61,8 +74,8 @@ const data = ref([
date: '2020-02-02',
},
{
id: '3',
code: '333333333333',
id_: '3',
caseNum: '333333333333',
name: '张三',
unit: '123333333333333333333333333333',
owner: '张三',
@ -73,19 +86,27 @@ const data = ref([
const option = ref({
...CRUD_OPTIONS,
refreshBtn: false,
editTitle: '案件登记处理',
rowKey: 'id_',
column: [
{
label: '案件编号',
prop: 'code',
prop: 'caseNum',
search: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '案件名称',
prop: 'name',
editDisplay: false,
viewDisplay: false,
},
{
label: '关联单位',
prop: 'unit',
editDisplay: false,
viewDisplay: false,
},
{
label: '关联土地·',
@ -107,25 +128,31 @@ const option = ref({
authorization: UserStore.token,
},
dicFormatter: (res) => res?.data?.records ?? [],
editDisplay: false,
viewDisplay: false,
},
{
label: '联系电话',
prop: 'phone',
viewDisplay: true,
// addDisplay: true,
hide: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '法定代表人',
prop: 'onwer',
addDisplay: true,
hide: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '统一社会信用代码',
prop: 'code',
addDisplay: true,
hide: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '违法情况',
@ -135,12 +162,16 @@ const option = ref({
hide: true,
width: '100%',
span: 24,
editDisplay: false,
viewDisplay: false,
},
{
label: '负责人',
prop: 'owner',
display: false,
addDisplay: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '案件进度',
@ -148,12 +179,16 @@ const option = ref({
display: false,
search: true,
addDisplay: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '案件结果',
prop: 'result',
display: false,
addDisplay: true,
editDisplay: false,
viewDisplay: false,
},
{
label: '处理时间',
@ -161,12 +196,178 @@ const option = ref({
display: false,
addDisplay: false,
editDisplay: true,
viewDisplay: false,
},
],
group: [
{
label: '案件信息>',
prop: 'caseInfo',
addDisplay: false,
column: [
{
label: '案件名称',
prop: 'name',
render: ({ row }) => {
return h('span', {}, row.name);
},
},
{
label: '案件编号',
prop: 'caseNum',
render: ({ row }) => {
return h('span', {}, row.caseNum);
},
},
{
label: '关联单位',
prop: 'unit',
render: ({ row }) => {
return h('span', {}, row.unit);
},
},
{
label: '关联地块',
prop: 'land',
render: ({ row }) => {
return h('span', {}, row.land);
},
},
{
label: '法定代表人',
prop: 'onwer',
render: ({ row }) => {
return h('span', {}, row.onwer);
},
},
{
label: '联系电话',
prop: 'phone',
render: ({ row }) => {
return h('span', {}, row.phone);
},
},
{
label: '统一社会信用代码',
prop: 'code',
span: 24,
render: ({ row }) => {
return h('span', {}, row.code);
},
},
{
label: '违法情况',
prop: 'result',
span: 24,
render: ({ row }) => {
return h('span', {}, row.result);
},
},
],
},
{
label: '案件处理>',
prop: 'caseHandle',
addDisplay: false,
column: [
{
label: '案情记录',
prop: 'caseRecords',
type: 'textarea',
span: 24,
display: true,
editDisplay: true,
},
{
label: '现场取证',
prop: 'sceneProof',
span: 24,
display: true,
editDisplay: true,
},
{
label: '执法文书',
prop: 'document',
labelSuffix: '件',
span: 24,
display: true,
type: 'radio',
editDisplay: true,
dicData: [
{
label: '协助调查函',
value: 0,
},
{
label: '抽样取样凭证',
value: 1,
},
{
label: '检测报告',
value: 2,
},
{
label: '其他文书',
value: 3,
},
],
},
{
label: '',
prop: 'attrs_',
},
],
},
{
label: '处理结果>',
prop: 'caseResult_',
addDisplay: false,
column: [
{
label: '案件处理结果',
prop: 'caseResult',
type: 'radio',
value: 0,
dicData: [
{
label: '正常营业,无违规行为',
value: 0,
},
{
label: '简易程序,当场行政处罚',
value: 1,
},
{
label: '普通程序,当场处罚立案审批',
value: 2,
},
{
label: '移送程序,案件移送',
value: 3,
},
{
label: '特殊程序,案件终止',
value: 4,
},
],
span: 24,
display: true,
editDisplay: true,
},
],
},
],
});
const caseInfo = ref({
visible: false,
});
const examineForm = reactive({
record: '',
proof: [],
document: 1,
attrs: [],
result: 1,
});
// #endregion
/* --------------- methods --------------- */
@ -182,7 +383,9 @@ function handleSizeChange(val) {
}
function handleInfo(row) {
console.log('row', row);
caseInfo.value.visible = true;
// caseInfo.value.visible = true;
crudRef.value.rowEdit(row);
// crudRef.value.rowView(row);
}
function handleSearch(form, done) {
console.log('search', form);
@ -201,7 +404,22 @@ function handleRowSave(val, done, loading) {
done();
}, 1000);
}
function handleExamine(val, done, loading) {
console.log('val', val);
console.log('done', done);
console.log('loading', loading);
loading();
}
// #endregion
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
::v-deep() {
.attrs_ {
.el-form-item__label {
opacity: 0;
}
}
}
</style>

View File

@ -1,284 +0,0 @@
<template>
<el-drawer v-model="_visible" title="土地信息" :size="800" @close="handleClose">
<el-card>
<div class="title_">基础信息</div>
<el-form ref="baseForm" :model="baseInfo" :disabled="props.rowData.isDetails" class="base_form" label-width="120px" :rules="rules">
<el-form-item label="土地名称" prop="landName">
<el-input v-model="baseInfo.landName" placeholder="请输入名称"></el-input>
</el-form-item>
<el-form-item label="所属网格" prop="gridId">
<CustomSelect
v-model:value="baseInfo.gridId"
:set="{
url: '/land-resource/gridManage/page',
props: {
value: 'id',
label: 'gridName',
},
}"
placeholder="请选择所属网格"
/>
</el-form-item>
<el-form-item label="用地分类" prop="landClassificationType">
<LandClassificationType v-model:value="baseInfo.landClassificationType" placeholder="请选择用地分类" />
</el-form-item>
<el-form-item label="位置" prop="villageCode">
<el-input v-model="baseInfo.villageCode" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="是否土地流转" prop="isTransfer">
<LandIsTransfer v-model:value="baseInfo.isTransfer" />
</el-form-item>
<el-form-item label="面积" prop="area">
<el-input v-model="baseInfo.area" placeholder="请输入面积"></el-input>
</el-form-item>
<el-form-item label="产权人" prop="owner">
<el-input v-model="baseInfo.owner" placeholder="请输入产权人姓名"></el-input>
</el-form-item>
<el-form-item label="土壤类型">
<el-input v-model="baseInfo.soilType" placeholder="请输入土壤类型"></el-input>
</el-form-item>
</el-form>
</el-card>
<br />
<el-card>
<div class="title_">土地产权信息</div>
<el-form ref="propertyForm" :model="propertyInfo" :disabled="props.rowData.isDetails" class="property_form" label-width="120px">
<el-form-item label="产权人姓名" prop="propertyName">
<el-input v-model="propertyInfo.propertyName" placeholder="请输入联系人"></el-input>
</el-form-item>
<el-form-item label="产权人联系电话" prop="propertyPhone">
<el-input v-model="propertyInfo.propertyPhone" placeholder="请输入联系人"></el-input>
</el-form-item>
<el-form-item label="产权编号" prop="landCode" class="land_code">
<el-input v-model="propertyInfo.landCode" placeholder="请输入联系人"></el-input>
</el-form-item>
<el-form-item label="产权证书" prop="propertyCertificateUrl" class="attrs_content">
<el-upload class="custom-form__uploader" action="#" :show-file-list="false" accept="image/*" :limit="20" :http-request="rowUploadPicture">
<el-icon class="custom-form__uploader__icon"><Plus /></el-icon>
</el-upload>
<div v-for="item in propertyInfo.propertyCertificateUrl" :key="`attr_${item.id}`" class="attrs_content__item">
<img :src="item.url" :alt="item.name" style="width: 100%; height: 100%" />
<el-icon class="clear_btn" @click="handleClearAttr(item.id)"><CircleCloseFilled /></el-icon>
</div>
</el-form-item>
</el-form>
</el-card>
<section class="btns">
<el-button type="primary" :disabled="props.rowData.isDetails" @click="handleSubmit">保存并提交审核</el-button>
<!-- <el-button type="warning" :disabled="props.rowData.info" @click="handleSubmit">保存草稿</el-button> -->
<el-button @click="handleClose">取消</el-button>
</section>
</el-drawer>
</template>
<script setup>
import { reactive, ref, watch } from 'vue';
import LandClassificationType from '@/components/LandClassificationType.vue';
import CustomSelect from '@/components/CustomSelect.vue';
import LandIsTransfer from '@/components/LandIsTransfer.vue';
import { saveLand } from '@/apis/land';
import { ElMessage } from 'element-plus';
import { CommonUpload } from '@/apis';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
landType: {
type: String,
default: '0',
},
rowData: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['close']);
/* --------------- data --------------- */
// #region
const _visible = ref(false);
const editId = ref(null);
const baseInfo = reactive({
landName: '',
gridId: '',
landClassificationType: '',
villageCode: '',
isTransfer: '1',
area: '',
owner: '',
soilType: '',
});
const propertyInfo = reactive({
propertyName: '',
propertyPhone: '',
landCode: '',
propertyCertificateUrl: [],
});
watch(
() => props.visible,
() => {
_visible.value = props.visible;
if (!props.rowData.id) {
editId.value = props.rowData.id;
} else {
for (let key in baseInfo) {
baseInfo[key] = props.rowData[key];
}
baseInfo.isTransfer = props.rowData.landTransfer || '0';
for (let key in propertyInfo) {
propertyInfo[key] = props.rowData[key];
}
if (propertyInfo.propertyCertificateUrl) {
propertyInfo.propertyCertificateUrl = props.rowData.propertyCertificateUrl.map((item, i) => {
return {
url: item.url,
id: `id_${i}_${Date.now()}`,
name: item.name || `name_${i}_${Date.now()}`,
};
});
}
}
}
);
watch(
() => baseInfo,
() => {
console.log('---', baseInfo);
}
);
const baseForm = ref();
const propertyForm = ref();
const rules = reactive({
landName: [{ required: true, message: '请输入名称', trigger: 'blur' }],
gridId: [{ required: true, message: '请选择所属网格', trigger: 'blur' }],
landClassificationType: [{ required: true, message: '请选择用地分类', trigger: 'blur' }],
villageCode: [{ required: true, message: '请输入位置', trigger: 'blur' }],
});
// #endregion
/* --------------- methods --------------- */
// #region
const rowUploadPicture = async ({ file }) => {
const formData = new FormData();
formData.append('file', file);
const res = await CommonUpload(formData);
if (res.code === 200) {
propertyInfo.propertyCertificateUrl.push({
...res.data,
id: 'id_' + Date.now(),
});
console.log('---', res);
// state.form.productUrl = res.data.url;
// const base64 = await imageToBase64(file);
// state.form.base64 = base64;
}
};
function handleClearAttr(id) {
propertyInfo.propertyCertificateUrl = propertyInfo.propertyCertificateUrl.filter((item) => item.id !== id);
}
async function handleSubmit() {
baseForm.value.validate().then(async () => {
const data = {
...baseInfo,
...propertyInfo,
isDraftsSave: 0,
landType: props.landType,
};
let ids = [];
propertyInfo.propertyCertificateUrl.map((item) => ids.push(item.url));
data.propertyCertificateUrl = ids.join();
editId.value && (data.id = editId.value);
const res = await saveLand(data);
if (res.code == 200) {
ElMessage.success('保存成功');
resFrom();
}
});
}
function handleClose() {
resFrom();
emit('close');
}
function resFrom() {
baseForm.value.resetFields();
propertyForm.value.resetFields();
for (let key in baseInfo) {
baseInfo[key] = '';
}
for (let key in propertyInfo) {
propertyInfo[key] = '';
}
propertyInfo.propertyCertificateUrl = [];
}
// #endregion
</script>
<style lang="scss" scoped>
* {
box-sizing: border-box;
}
.title_ {
margin-bottom: 12px;
font-size: 16px;
font-weight: bold;
}
.base_form,
.property_form {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.btns {
margin-top: 12px;
display: grid;
justify-content: center;
grid-template-columns: auto auto auto;
gap: 12px;
}
.attrs_content__item {
position: relative;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
img {
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;
}
}
}
::v-deep() {
.land_code {
.el-form-item__content {
align-items: baseline;
}
}
.attrs_content {
.el-form-item__content {
display: flex;
flex-wrap: wrap;
gap: 10px;
> div {
width: 100px;
height: 100px;
}
}
}
}
</style>

View File

@ -13,6 +13,7 @@
:data="data"
:option="option"
:before-close="handleCloseFrom"
:table-loading="loading"
@current-change="handlePageChange"
@size-change="handleSizeChange"
@search-reset="handleResetSearch"
@ -61,9 +62,7 @@ import { useUserStore } from '@/store/modules/user';
import { getLandsList, exportLands, delLand, saveLand, importLands } from '@/apis/land.js';
import { ElMessage } from 'element-plus';
import useLandHook from './useLandHook';
import { CommonUpload } from '@/apis';
import Attrs from './common/Attrs.vue';
import { add } from 'lodash';
const { landType, landsType, landClassificationType, handleIficationType } = useLandHook();
const { VITE_APP_BASE_API } = import.meta.env;
@ -79,8 +78,9 @@ const local_ = ref([102.833669, 24.88149, '昆明市']);
const local = ref(JSON.parse(JSON.stringify(local_.value)));
/* --------------- data --------------- */
// #region
const loading = ref(false);
const crudRef = ref();
const pageData = reactive({
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
@ -88,8 +88,8 @@ const pageData = reactive({
const data = ref([]);
const option = reactive({
...CRUD_OPTIONS,
selection: false,
menuWidth: 120,
selection: false,
column: [
{
label: '地块名',
@ -375,30 +375,33 @@ const rowData = ref([]);
// #region
async function getList() {
loading.value = true;
const params = {
current: pageData.currentPage,
size: pageData.pageSize,
current: pageData.value.currentPage,
size: pageData.value.pageSize,
landType: landType.value,
...searchData,
};
let res = await getLandsList(params);
loading.value = false;
if (res.code == 200) {
data.value = res.data.records;
const { total, records } = res.data;
data.value = records;
data.value.forEach((v) => {
v.isTransfer = v.landTransfer || 1;
v.isTransferView = v.landTransfer == 1 ? '否' : '是';
v.landClassificationTypeView = handleIficationType(v.handleIficationType);
v.coordinateView = v.coordinate;
});
pageData.total = res.data.total;
pageData.value.total = total;
}
}
function handlePageChange(val) {
pageData.currentPage = val;
pageData.value.currentPage = val;
getList();
}
function handleSizeChange(val) {
pageData.pageSize = val;
pageData.value.pageSize = val;
getList();
}
async function handleSearch(form, done) {
@ -412,9 +415,9 @@ async function handleResetSearch() {
for (let key in searchData) {
searchData[key] = '';
}
pageData.currentPage = 1;
pageData.pageSize = 10;
pageData.total = 0;
pageData.value.currentPage = 1;
pageData.value.pageSize = 10;
pageData.value.total = 0;
await getList();
}
const attrNames = reactive(landsType.map((v) => v.label));

View File

@ -0,0 +1,185 @@
<template>
<div class="analysis">
<!-- <h3 class="analysis-title">土地利用与规划分析</h3> -->
<el-row :gutter="16" style="margin-bottom: 10px">
<el-col :span="12">
<el-card shadow="hover">
<custom-echart-pie :chart-data="state.landData" height="460px" :option="state.landOption" />
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover">
<custom-echart-bar :chart-data="state.cropData" height="460px" :option="state.cropOption" />
</el-card>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="24">
<el-card shadow="hover">
<custom-echart-line :chart-data="state.landTrendData" height="500px" :option="state.landTrendOption" type="line" />
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const state = reactive({
landOption: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: '土地用途分析',
textStyle: {
color: '#333',
},
},
legend: {
data: ['耕地', '林地', '建设用地'],
},
label: {
color: '#333',
},
series: [
{
type: 'pie',
radius: [80, 140],
roseType: 'area',
itemStyle: {
borderRadius: 20,
},
},
],
},
landData: [
{ value: 100, name: '耕地' },
{ value: 105, name: '林地' },
{ value: 217, name: '建设用地' },
],
cropOption: {
title: {
text: '作物种植结构',
textStyle: {
color: '#333',
},
},
xAxis: {
type: 'category',
data: ['土豆', '西红柿', '玉米', '花生', '水稻'],
},
yAxis: {
type: 'value',
name: '面积(亩)',
// splitLine: {
// show: false,
// },
},
barStyle: {
// barWidth: 50,
showBackground: true,
itemStyle: {
borderRadius: 10,
},
},
},
cropData: [
{ value: 230, name: '土豆' },
{ value: 165, name: '西红柿' },
{ value: 217, name: '玉米' },
{ value: 200, name: '花生' },
{ value: 305, name: '水稻' },
],
landTrendOption: {
color: ['#3685fe', '#41b879', '#ffd500'],
title: {
text: '土地变更趋势',
textStyle: {
color: '#333',
},
},
xAxis: {
type: 'category',
name: '年份',
},
yAxis: {
type: 'value',
name: '面积(亩)',
// splitLine: {
// show: false,
// },
},
},
landTrendData: [
{
type: '耕地',
value: 40,
name: '2022',
},
{
type: '林地',
value: 146,
name: '2022',
},
{
type: '建设用地',
value: 81,
name: '2022',
},
{
type: '耕地',
value: 60,
name: '2023',
},
{
type: '林地',
value: 186,
name: '2023',
},
{
type: '建设用地',
value: 101,
name: '2023',
},
{
type: '耕地',
value: 230,
name: '2024',
},
{
type: '林地',
value: 256,
name: '2024',
},
{
type: '建设用地',
value: 301,
name: '2024',
},
{
type: '耕地',
value: 160,
name: '2025',
},
{
type: '林地',
value: 286,
name: '2025',
},
{
type: '建设用地',
value: 161,
name: '2025',
},
],
});
</script>
<style lang="scss" scoped>
.analysis {
&-title {
font-size: 20px;
color: #000;
margin-bottom: 40px;
}
}
</style>