农户档案、种植基地档案、种植批次档案、农事作业档案

This commit is contained in:
沈鸿 2025-07-01 10:40:29 +08:00
parent a72183b3ac
commit 43b61e29e2
11 changed files with 2657 additions and 172 deletions

View File

@ -1,5 +1,6 @@
<template>
<div class="custom-page">
<!-- 搜索区域 -->
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
@ -23,50 +24,194 @@
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<avue-crud
ref="crudRef"
v-model:page="pagination"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<!-- <template #menu="scope">
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template> -->
</avue-crud>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">种植信息</p>
<el-row :gutter="20">
<el-col :span="4">
<!-- 作物图片 -->
<FileUploader v-model="formData.cropUrl" :limit="1" />
</el-col>
<el-col :span="10">
<!-- 种植作物 -->
<el-form-item label="种植作物" prop="cropName">
<el-input v-model="formData.cropName" placeholder="请输入种植作物名称" clearable />
</el-form-item>
<!-- 地块编号 -->
<el-form-item label="地块编号" prop="plotCode">
<el-input v-model="formData.plotCode" placeholder="请输入地块编号" clearable />
</el-form-item>
<!-- 种植基地编号 -->
<el-form-item label="种植基地编号" prop="baseCode">
<el-input v-model="formData.baseCode" placeholder="请输入种植基地编号" clearable />
</el-form-item>
<!-- 种植批次编号 -->
<el-form-item label="种植批次编号" prop="batchCode">
<el-input v-model="formData.batchCode" placeholder="请输入种植批次编号" clearable />
</el-form-item>
</el-col>
<el-col :span="10">
<!-- 作物品种 -->
<el-form-item label="作物品种" prop="cropVariety">
<el-select v-model="formData.cropVariety" placeholder="请选择作物品种" clearable filterable>
<el-option v-for="item in cropVarieties" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 地块名称 -->
<el-form-item label="地块名称" prop="plotName">
<el-input v-model="formData.plotName" placeholder="请输入地块名称" clearable />
</el-form-item>
<!-- 种植基地 -->
<el-form-item label="种植基地" prop="baseName">
<el-input v-model="formData.baseName" placeholder="请输入种植基地名称" clearable />
</el-form-item>
<!-- 种植批次 -->
<el-form-item label="种植批次" prop="batchName">
<el-input v-model="formData.batchName" placeholder="请输入种植批次名称" clearable />
</el-form-item>
</el-col>
</el-row>
<p class="form-title">农事作业管理</p>
<el-form-item label="农事作业列表">
<el-card v-for="(task, index) in formData.farmingTasks" :key="index" class="mb-4">
<el-row :gutter="20">
<el-col :span="4">
<!-- 作业图片占位可加上传组件 -->
<div
style="
width: 100px;
height: 100px;
background-color: #f0f0f0;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
"
>
<FileUploader v-model="formData.cropUrl" :limit="1" />
</div>
</el-col>
<el-col :span="20">
<el-row>
<el-col :span="12">
<el-form-item label="作业类型">
<el-input v-model="task.type" placeholder="如:种子处理、播种等" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人">
<el-input v-model="task.responsible" placeholder="请输入负责人" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="开始时间">
<el-date-picker v-model="task.startDate" type="date" placeholder="选择开始日期" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间">
<el-date-picker v-model="task.endDate" type="date" placeholder="选择结束日期" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="作业内容">
<el-input v-model="task.content" type="textarea" rows="3" placeholder="请输入具体作业内容" />
</el-form-item>
<el-form-item label="实际作业情况">
<el-input v-model="task.actual" type="textarea" rows="2" placeholder="如肥料使用量XX 公斤/亩" />
</el-form-item>
<el-button type="danger" @click="removeTask(index)">删除此作业</el-button>
</el-col>
</el-row>
</el-card>
<el-button type="primary" plain @click="addTask">新增作业</el-button>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">确定</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { mockData } from './mockData';
onMounted(() => {
getData();
});
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
crudData.value = mockData;
pagination.value.total = crudData.value.length;
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '农事作业详情',
EDIT: '编辑农事作业信息',
ADD: '新增农事作业',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
// const activeFormTab = ref('basic');
//
const searchForm = ref({});
const pagination = ref({
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const crudData = ref([]);
const crudOptions = ref({
...CRUD_OPTIONS,
header: false,
menu: false,
height: 'calc(100vh - 330px)',
column: [
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '地块编号', prop: 'landNumber', width: 160 },
{ label: '地块名称', prop: 'landName', width: 170 },
{ label: '面积', prop: 'planArea', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}` },
@ -87,19 +232,93 @@ const crudOptions = ref({
{ label: '账号(手机号)', prop: 'account', width: 130 },
{ label: '信息填报时间', prop: 'fillTime', width: 110 },
{ label: '信息审核时间', prop: 'approvalTime', width: 110 },
],
{ label: '操作', prop: 'action', slotName: 'action', fixed: 'right' },
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
const handleCurrentChange = (val) => {
pagination.value.currentPage = val;
// ==================== ====================
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
};
const handleSizeChange = (val) => {
pagination.value.pageSize = val;
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
const getActions = (row) => {
return [{ name: '查看', icon: 'view', event: () => handleView(row) }];
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = (row) => {
console.log(row);
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = {
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
};
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>

View File

@ -1,5 +1,6 @@
<template>
<div class="custom-page">
<!-- 搜索区域 -->
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
@ -9,77 +10,431 @@
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<avue-crud
ref="crudRef"
v-model:page="pagination"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<!-- <template #menu="scope">
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template> -->
</avue-crud>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-tabs v-model="activeFormTab" class="tabs-wrapper">
<!-- 基本信息标签页 -->
<el-tab-pane label="基地基本信息" name="basic">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">基地信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 基地编码 -->
<el-form-item label="基地编码" prop="baseCode">
<el-input v-model="formData.baseCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 所属行政区域 -->
<el-form-item label="所属行政区域" prop="regionName">
<el-cascader
v-model="formData.regionName"
:options="regionOptions"
:props="{ checkStrictly: true }"
:disabled="isReadonly"
clearable
/>
</el-form-item>
<!-- 具体位置 -->
<el-form-item label="具体位置" prop="address">
<el-input v-model="formData.address" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 气候条件 -->
<el-form-item label="气候条件" prop="weather">
<el-select v-model="formData.weather" :disabled="isReadonly" clearable>
<el-option v-for="item in weatherOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 面积 -->
<el-form-item label="面积(亩)" prop="area">
<el-input-number v-model="formData.area" :min="0" :precision="2" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 投入使用时间 -->
<el-form-item label="投入使用时间" prop="commissioningTime">
<el-date-picker
v-model="formData.commissioningTime"
type="date"
:disabled="isReadonly"
value-format="YYYY-MM-DD"
placeholder="选择日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 基地名称 -->
<el-form-item label="基地名称" prop="baseName">
<el-input v-model="formData.baseName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 所属网格 -->
<el-form-item label="所属网格" prop="gridName">
<el-input v-model="formData.gridName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 经纬度 -->
<el-form-item label="经纬度">
<div class="coordinate-container">
<el-input v-model="formData.longitude" placeholder="经度" :readonly="isReadonly" style="width: 45%" />
<span style="margin: 0 5px">,</span>
<el-input v-model="formData.latitude" placeholder="纬度" :readonly="isReadonly" style="width: 45%" />
<el-button v-if="!isReadonly" type="primary" link @click="handleSelectOnMap"> 地图选取 </el-button>
</div>
</el-form-item>
<!-- 海拔 -->
<el-form-item label="海拔(米)" prop="altitude">
<el-input-number v-model="formData.altitude" :min="0" :precision="0" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 基地使用状态 -->
<el-form-item label="使用状态" prop="status">
<el-select v-model="formData.status" :disabled="isReadonly" clearable>
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 基地照片 -->
<el-form-item label="基地照片">
<el-upload
action="#"
list-type="picture-card"
:file-list="formData.baseImages"
:disabled="isReadonly"
:auto-upload="false"
:limit="3"
:on-change="handleImageChange"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<p class="form-title">经营主体信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 经营主体代码 -->
<el-form-item label="经营主体代码" prop="businessCode">
<el-input v-model="formData.businessCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 经营主体名称 -->
<el-form-item label="经营主体名称" prop="businessSubjectName">
<el-input v-model="formData.businessSubjectName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 联系电话 -->
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="formData.contactPhone" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 经营主体类型 -->
<el-form-item label="经营主体类型" prop="businessType">
<el-select v-model="formData.businessType" :disabled="isReadonly" clearable>
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="formData.contactPerson" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
<!-- 种植信息标签页 -->
<el-tab-pane label="基地种植信息" name="plantInfo">
<el-form ref="propertyFormRef" :model="formData" :disabled="isReadonly" label-width="150px">
<el-row v-for="(item, index) in formData.plantInfoList" :key="index" :gutter="20">
<el-col :span="12">
<!-- 地块编号 -->
<el-form-item label="地块编号" :prop="`plantInfoList.${index}.plotCode`">
<el-input v-model="item.plotCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 面积 -->
<el-form-item label="面积(亩)" :prop="`plantInfoList.${index}.area`">
<el-input-number v-model="item.area" :min="0" :precision="2" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 品种名称 -->
<el-form-item label="品种名称" :prop="`plantInfoList.${index}.varietyName`">
<el-input v-model="item.varietyName" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 地块名称 -->
<el-form-item label="地块名称" :prop="`plantInfoList.${index}.plotName`">
<el-input v-model="item.plotName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 作物名称 -->
<el-form-item label="作物名称" :prop="`plantInfoList.${index}.cropName`">
<el-select v-model="item.cropName" :disabled="isReadonly" clearable>
<el-option v-for="crop in cropOptions" :key="crop.value" :label="crop.label" :value="crop.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">修改</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { ref, onMounted } from 'vue';
import { mockData } from './mockData';
onMounted(() => {
getData();
});
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
crudData.value = mockData;
pagination.value.total = crudData.value.length;
};
const loading = ref(false);
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '查看基地信息',
EDIT: '编辑基地信息',
ADD: '新增基地信息',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
const activeFormTab = ref('basic');
//
const searchForm = ref({});
const pagination = ref({
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const crudData = ref([]);
const crudOptions = ref({
...CRUD_OPTIONS,
header: false,
menu: false,
height: 'calc(100vh - 330px)',
column: [
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '基地编码', prop: 'baseCode' },
{ label: '基地名称', prop: 'baseName' },
{ label: '所属行政区域', prop: 'regionName' },
{ label: '所属网格', prop: 'gridName' },
{ label: '具体位置', prop: 'adress' },
{ label: '经纬度', prop: 'coordinate', formatter: (row) => `${row.longitude}, ${row.latitude}` },
{
label: '经纬度',
prop: 'coordinate',
formatter: (row) => `${row.longitude}, ${row.latitude}`,
},
{ label: '气候条件', prop: 'weather' },
{ label: '海拔(米)', prop: 'altitude' },
{ label: '面积(亩)', prop: 'area', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}` },
{
label: '面积(亩)',
prop: 'area',
formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}`,
},
{ label: '使用状态', prop: 'status' },
{ label: '经营主体代码', prop: 'businessCode' },
{ label: '经营主体类型', prop: 'businessType' },
{ label: '经营主体名称', prop: 'businessSubjectName' },
{ label: '创建时间', prop: 'createTime' },
],
{
label: '操作',
prop: 'action',
slotName: 'action',
fixed: 'right',
},
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
const handleCurrentChange = (val) => {
pagination.value.currentPage = val;
// ==================== ====================
const getData = async () => {
loading.value = true;
try {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
} finally {
loading.value = false;
}
};
const handleSizeChange = (val) => {
pagination.value.pageSize = val;
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
const getActions = (row) => {
return [{ name: '查看', icon: 'view', event: () => handleView(row) }];
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = (row) => {
console.log(row);
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = { ...initialFormData };
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.custom-page {
.search-bar {
margin-bottom: 20px;
}
.tabs-wrapper {
width: 100%;
display: flex;
align-items: center;
.el-tabs__header {
width: 100%;
.el-tabs__nav-scroll {
display: flex;
justify-content: center;
}
.el-tabs__item {
font-size: 16px;
color: #555555;
font-weight: 500;
}
}
:deep(.el-tabs__content) {
padding: 20px;
border-radius: 4px;
width: 80%;
height: calc(100vh - 400px);
overflow-y: auto;
.el-tab-pane {
margin: 0 auto;
width: 100%;
}
}
}
.form-title {
font-size: 16px;
font-weight: 500;
margin: 30px 0;
color: #333333;
}
.dialog-footer {
display: block;
text-align: center;
}
}
</style>

View File

@ -4,54 +4,151 @@
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
</el-form-item>
<!-- 分类蔬菜种苗蔬菜种子 -->
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<avue-crud
ref="crudRef"
v-model:page="pagination"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<!-- <template #menu="scope">
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template> -->
</avue-crud>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">基地信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 种植批次编码 -->
<el-form-item label="种植批次编码" prop="plantingBatchCode">
<el-input v-model="formData.plantingBatchCode" placeholder="请输入种植批次编码" />
</el-form-item>
<!-- 种植作物 -->
<el-form-item label="种植作物" prop="cropsName">
<el-input v-model="formData.cropsName" placeholder="请输入种植作物" />
</el-form-item>
<!-- 种植基地名称 -->
<el-form-item label="种植基地" prop="plantingBase">
<el-input v-model="formData.plantingBase" placeholder="请输入种植基地名称" />
</el-form-item>
<!-- 所属网格 -->
<el-form-item label="所属网格" prop="gridName">
<el-input v-model="formData.gridName" placeholder="请输入所属网格" />
</el-form-item>
<!-- 种植结束时间 -->
<el-form-item label="种植结束时间" prop="plantingEndTime">
<el-date-picker v-model="formData.plantingEndTime" type="datetime" placeholder="选择种植结束时间" value-format="yyyy-MM-dd HH:mm:ss" />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 种植批次名称 -->
<el-form-item label="种植批次名称" prop="plantingBatchName">
<el-input v-model="formData.plantingBatchName" placeholder="请输入种植批次名称" />
</el-form-item>
<!-- 种植基地编码 -->
<el-form-item label="种植基地编码" prop="plantingBaseCode">
<el-input v-model="formData.plantingBaseCode" placeholder="请输入种植基地编码" />
</el-form-item>
<!-- 所属区域 -->
<el-form-item label="所属行政区域" prop="district">
<el-input v-model="formData.district" placeholder="请输入所属行政区域" />
</el-form-item>
<!-- 种植开始时间 -->
<el-form-item label="种植开始时间" prop="plantingStartTime">
<el-date-picker
v-model="formData.plantingStartTime"
type="datetime"
placeholder="选择种植开始时间"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
<!-- 账号手机号 -->
<el-form-item label="账号(手机号)" prop="account">
<el-input v-model="formData.account" placeholder="请输入账号(手机号)" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">确定</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { mockData } from './mockData';
onMounted(() => {
getData();
});
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
crudData.value = mockData;
pagination.value.total = crudData.value.length;
};
const loading = ref(false);
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '查看批次信息',
EDIT: '编辑批次信息',
ADD: '新增批次信息',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
// const activeFormTab = ref('basic');
//
const searchForm = ref({});
const pagination = ref({
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const crudData = ref([]);
const crudOptions = ref({
...CRUD_OPTIONS,
header: false,
menu: false,
height: 'calc(100vh - 330px)',
column: [
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '基地编码', prop: 'baseCode' },
{ label: '种植批次编码', prop: 'plantingBatchCode' },
{ label: '种植批次名称', prop: 'plantingBatchName' },
@ -63,19 +160,93 @@ const crudOptions = ref({
{ label: '种植开始时间', prop: 'plantingStartTime' },
{ label: '种植结束时间', prop: 'plantingEndTime' },
{ label: '账号(手机号)', prop: 'account' },
],
{ label: '操作', prop: 'action', slotName: 'action', fixed: 'right' },
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
const handleCurrentChange = (val) => {
pagination.value.currentPage = val;
// ==================== ====================
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
};
const handleSizeChange = (val) => {
pagination.value.pageSize = val;
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
const getActions = (row) => {
return [{ name: '查看', icon: 'view', event: () => handleView(row) }];
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = (row) => {
console.log(row);
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = {
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
};
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>

View File

@ -16,9 +16,9 @@
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
<!-- <template #menu="scope">
<template #menu="scope">
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template> -->
</template>
</avue-crud>
<el-dialog v-model="visible" title="查看" width="60%" align-center :draggable="true">
<RecordForm ref="formRef" v-model="formData" :disabled="mode === 'view'" />
@ -61,7 +61,7 @@ const crudData = ref([]);
const crudOptions = reactive({
...CRUD_OPTIONS,
header: false,
menu: false,
// menu: false,
height: 'calc(100vh - 330px)',
column: [
{ label: '地块编号', prop: 'landNumber', width: 160 },

View File

@ -1,9 +1,440 @@
<template>
<div></div>
<div class="custom-page">
<!-- 搜索区域 -->
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-tabs v-model="activeFormTab" class="tabs-wrapper">
<!-- 基本信息标签页 -->
<el-tab-pane label="基地基本信息" name="basic">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">基地信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 基地编码 -->
<el-form-item label="基地编码" prop="baseCode">
<el-input v-model="formData.baseCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 所属行政区域 -->
<el-form-item label="所属行政区域" prop="regionName">
<el-cascader
v-model="formData.regionName"
:options="regionOptions"
:props="{ checkStrictly: true }"
:disabled="isReadonly"
clearable
/>
</el-form-item>
<!-- 具体位置 -->
<el-form-item label="具体位置" prop="address">
<el-input v-model="formData.address" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 气候条件 -->
<el-form-item label="气候条件" prop="weather">
<el-select v-model="formData.weather" :disabled="isReadonly" clearable>
<el-option v-for="item in weatherOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 面积 -->
<el-form-item label="面积(亩)" prop="area">
<el-input-number v-model="formData.area" :min="0" :precision="2" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 投入使用时间 -->
<el-form-item label="投入使用时间" prop="commissioningTime">
<el-date-picker
v-model="formData.commissioningTime"
type="date"
:disabled="isReadonly"
value-format="YYYY-MM-DD"
placeholder="选择日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 基地名称 -->
<el-form-item label="基地名称" prop="baseName">
<el-input v-model="formData.baseName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 所属网格 -->
<el-form-item label="所属网格" prop="gridName">
<el-input v-model="formData.gridName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 经纬度 -->
<el-form-item label="经纬度">
<div class="coordinate-container">
<el-input v-model="formData.longitude" placeholder="经度" :readonly="isReadonly" style="width: 45%" />
<span style="margin: 0 5px">,</span>
<el-input v-model="formData.latitude" placeholder="纬度" :readonly="isReadonly" style="width: 45%" />
<el-button v-if="!isReadonly" type="primary" link @click="handleSelectOnMap"> 地图选取 </el-button>
</div>
</el-form-item>
<!-- 海拔 -->
<el-form-item label="海拔(米)" prop="altitude">
<el-input-number v-model="formData.altitude" :min="0" :precision="0" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 基地使用状态 -->
<el-form-item label="使用状态" prop="status">
<el-select v-model="formData.status" :disabled="isReadonly" clearable>
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 基地照片 -->
<el-form-item label="基地照片">
<el-upload
action="#"
list-type="picture-card"
:file-list="formData.baseImages"
:disabled="isReadonly"
:auto-upload="false"
:limit="3"
:on-change="handleImageChange"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<p class="form-title">经营主体信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 经营主体代码 -->
<el-form-item label="经营主体代码" prop="businessCode">
<el-input v-model="formData.businessCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 经营主体名称 -->
<el-form-item label="经营主体名称" prop="businessSubjectName">
<el-input v-model="formData.businessSubjectName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 联系电话 -->
<el-form-item label="联系电话" prop="contactPhone">
<el-input v-model="formData.contactPhone" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 经营主体类型 -->
<el-form-item label="经营主体类型" prop="businessType">
<el-select v-model="formData.businessType" :disabled="isReadonly" clearable>
<el-option v-for="item in businessTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item label="联系人" prop="contactPerson">
<el-input v-model="formData.contactPerson" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
<!-- 种植信息标签页 -->
<el-tab-pane label="基地种植信息" name="plantInfo">
<el-form ref="propertyFormRef" :model="formData" :disabled="isReadonly" label-width="150px">
<el-row v-for="(item, index) in formData.plantInfoList" :key="index" :gutter="20">
<el-col :span="12">
<!-- 地块编号 -->
<el-form-item label="地块编号" :prop="`plantInfoList.${index}.plotCode`">
<el-input v-model="item.plotCode" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 面积 -->
<el-form-item label="面积(亩)" :prop="`plantInfoList.${index}.area`">
<el-input-number v-model="item.area" :min="0" :precision="2" :disabled="isReadonly" controls-position="right" />
</el-form-item>
<!-- 品种名称 -->
<el-form-item label="品种名称" :prop="`plantInfoList.${index}.varietyName`">
<el-input v-model="item.varietyName" :readonly="isReadonly" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 地块名称 -->
<el-form-item label="地块名称" :prop="`plantInfoList.${index}.plotName`">
<el-input v-model="item.plotName" :readonly="isReadonly" clearable />
</el-form-item>
<!-- 作物名称 -->
<el-form-item label="作物名称" :prop="`plantInfoList.${index}.cropName`">
<el-select v-model="item.cropName" :disabled="isReadonly" clearable>
<el-option v-for="crop in cropOptions" :key="crop.value" :label="crop.label" :value="crop.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">修改</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { ref, onMounted } from 'vue';
import { mockData } from './mockData';
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '查看基地信息',
EDIT: '编辑基地信息',
ADD: '新增基地信息',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
const activeFormTab = ref('basic');
//
const searchForm = ref({});
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '基地编码', prop: 'baseCode' },
{ label: '基地名称', prop: 'baseName' },
{ label: '所属行政区域', prop: 'regionName' },
{ label: '所属网格', prop: 'gridName' },
{ label: '具体位置', prop: 'adress' },
{
label: '经纬度',
prop: 'coordinate',
formatter: (row) => `${row.longitude}, ${row.latitude}`,
},
{ label: '气候条件', prop: 'weather' },
{ label: '海拔(米)', prop: 'altitude' },
{
label: '面积(亩)',
prop: 'area',
formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}`,
},
{ label: '使用状态', prop: 'status' },
{ label: '经营主体代码', prop: 'businessCode' },
{ label: '经营主体类型', prop: 'businessType' },
{ label: '经营主体名称', prop: 'businessSubjectName' },
{ label: '创建时间', prop: 'createTime' },
{
label: '操作',
prop: 'action',
slotName: 'action',
fixed: 'right',
},
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
// ==================== ====================
const getData = async () => {
loading.value = true;
try {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
} finally {
loading.value = false;
}
};
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = { ...initialFormData };
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.custom-page {
.search-bar {
margin-bottom: 20px;
}
.tabs-wrapper {
width: 100%;
display: flex;
align-items: center;
.el-tabs__header {
width: 100%;
.el-tabs__nav-scroll {
display: flex;
justify-content: center;
}
.el-tabs__item {
font-size: 16px;
color: #555555;
font-weight: 500;
}
}
:deep(.el-tabs__content) {
padding: 20px;
border-radius: 4px;
width: 80%;
height: calc(100vh - 400px);
overflow-y: auto;
.el-tab-pane {
margin: 0 auto;
width: 100%;
}
}
}
.form-title {
font-size: 16px;
font-weight: 500;
margin: 30px 0;
color: #333333;
}
.dialog-footer {
display: block;
text-align: center;
}
}
</style>

View File

@ -0,0 +1,39 @@
export const mockData = [
// 第一条:耿马县孟定镇咖啡示范基地
{
baseCode: 'JD5325262023KM01', // 耿马县+2023+咖啡(KM)+01
baseName: '孟定精品咖啡示范园',
regionName: ' 耿马县孟定镇',
gridName: '孟定镇河西片区第3网格',
adress: '孟定镇河西村班幸组南汀河西岸',
longitude: '99.0832°E', // 实际经纬度(孟定镇中心区域)
latitude: '23.5521°N',
weather: '热带季风气候', // 年均温21.5℃年降水量1600mm
altitude: 850, // 孟定坝区典型海拔
area: 286.5, // 示范基地面积
status: '使用中',
businessCode: 'QY532526001', // 统一社会信用代码前段
businessType: '合作社',
businessSubjectName: '耿马滇咖农业科技有限公司', // 实际注册企业
createTime: '2021-03-15 09:00:00',
},
// 第二条:耿马县四排山乡坚果基地
{
baseCode: 'JD5325262023JG02', // 耿马县+2023+坚果(JG)+02
baseName: '四排山澳洲坚果产业基地',
regionName: ' 耿马县四排山乡',
gridName: '四排山乡翁达村第5网格',
adress: '翁达村至芒关村公路东侧山坡地',
longitude: '99.2178°E', // 四排山乡中心区域坐标
latitude: '23.7865°N',
weather: '南亚热带气候', // 年均温19.8℃年降水量1400mm
altitude: 1200, // 山地种植区典型海拔
area: 580, // 连片种植面积
status: '使用中',
businessCode: 'HZ532526001', // 合作社注册代码
businessType: '合作社',
businessSubjectName: '耿马四排山坚果种植专业合作社', // 真实合作社
createTime: '2018-05-20 14:30:00',
},
];

View File

@ -0,0 +1,253 @@
<template>
<div class="custom-page">
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
</el-form-item>
<!-- 分类蔬菜种苗蔬菜种子 -->
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">基地信息</p>
<el-row :gutter="20">
<el-col :span="12">
<!-- 种植批次编码 -->
<el-form-item label="种植批次编码" prop="plantingBatchCode">
<el-input v-model="formData.plantingBatchCode" placeholder="请输入种植批次编码" />
</el-form-item>
<!-- 种植作物 -->
<el-form-item label="种植作物" prop="cropsName">
<el-input v-model="formData.cropsName" placeholder="请输入种植作物" />
</el-form-item>
<!-- 种植基地名称 -->
<el-form-item label="种植基地" prop="plantingBase">
<el-input v-model="formData.plantingBase" placeholder="请输入种植基地名称" />
</el-form-item>
<!-- 所属网格 -->
<el-form-item label="所属网格" prop="gridName">
<el-input v-model="formData.gridName" placeholder="请输入所属网格" />
</el-form-item>
<!-- 种植结束时间 -->
<el-form-item label="种植结束时间" prop="plantingEndTime">
<el-date-picker v-model="formData.plantingEndTime" type="datetime" placeholder="选择种植结束时间" value-format="yyyy-MM-dd HH:mm:ss" />
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 种植批次名称 -->
<el-form-item label="种植批次名称" prop="plantingBatchName">
<el-input v-model="formData.plantingBatchName" placeholder="请输入种植批次名称" />
</el-form-item>
<!-- 种植基地编码 -->
<el-form-item label="种植基地编码" prop="plantingBaseCode">
<el-input v-model="formData.plantingBaseCode" placeholder="请输入种植基地编码" />
</el-form-item>
<!-- 所属区域 -->
<el-form-item label="所属行政区域" prop="district">
<el-input v-model="formData.district" placeholder="请输入所属行政区域" />
</el-form-item>
<!-- 种植开始时间 -->
<el-form-item label="种植开始时间" prop="plantingStartTime">
<el-date-picker
v-model="formData.plantingStartTime"
type="datetime"
placeholder="选择种植开始时间"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
<!-- 账号手机号 -->
<el-form-item label="账号(手机号)" prop="account">
<el-input v-model="formData.account" placeholder="请输入账号(手机号)" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">确定</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { mockData } from './mockData';
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '查看批次信息',
EDIT: '编辑批次信息',
ADD: '新增批次信息',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
// const activeFormTab = ref('basic');
//
const searchForm = ref({});
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '基地编码', prop: 'baseCode' },
{ label: '种植批次编码', prop: 'plantingBatchCode' },
{ label: '种植批次名称', prop: 'plantingBatchName' },
{ label: '种植作物', prop: 'cropsName' },
{ label: '种植基地编码', prop: 'plantingBaseCode' },
{ label: '种植基地', prop: 'plantingBase' },
{ label: '所属行政区域', prop: 'district' },
{ label: '所属网格', prop: 'gridName' },
{ label: '种植开始时间', prop: 'plantingStartTime' },
{ label: '种植结束时间', prop: 'plantingEndTime' },
{ label: '账号(手机号)', prop: 'account' },
{ label: '操作', prop: 'action', slotName: 'action', fixed: 'right' },
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
// ==================== ====================
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
};
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = {
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
};
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,31 @@
export const mockData = [
// 第一条:耿马县咖啡种植数据(孟定镇特色产业)
{
baseCode: 'JD5325262023KM01', // 基地编码:耿马县+2023+咖啡(KM)+01
plantingBatchCode: 'KM-PC20231001', // 种植批次编码:咖啡-批次2023年10月第1批
plantingBatchName: '2023年精品咖啡秋植批次',
cropsName: '咖啡(阿拉比卡)', // 孟定镇主要种植品种
plantingBaseCode: 'JD-MD-ZJ01', // 种植基地编码:孟定-精品01
plantingBase: '耿马孟定精品咖啡示范园', // 实际存在的示范基地
district: '耿马县孟定镇', // 精确到镇级
gridName: '孟定镇河西片区第3网格', // 按当地网格划分
plantingStartTime: '2023-06-15', // 咖啡最佳种植期
plantingEndTime: '2024-11-30', // 咖啡采收周期
account: '159****3098', // 匹配原始数据中咖啡种植户联系方式
},
// 第二条:耿马县澳洲坚果数据(四排山乡主导产业)
{
baseCode: 'JD5325262023JG02', // 基地编码:耿马县+2023+坚果(JG)+02
plantingBatchCode: 'JG-PC20190502', // 种植批次编码:坚果-批次2019年5月第2批
plantingBatchName: '2019年澳洲坚果丰产林项目',
cropsName: '澳洲坚果', // 四排山乡支柱产业
plantingBaseCode: 'JD-SPS-FC01', // 种植基地编码:四排山-丰产01
plantingBase: '耿马四排山澳洲坚果标准化基地', // 政府扶持项目
district: '耿马县四排山乡', // 精确到乡级
gridName: '四排山乡翁达村第5网格', // 对应原始数据中的网格
plantingStartTime: '2019-05-20', // 坚果最佳定植时间
plantingEndTime: '2026-09-30', // 坚果丰产期开始时间
account: '158****3367', // 匹配原始数据中坚果种植户联系方式
},
];

View File

@ -0,0 +1,325 @@
<template>
<div class="custom-page">
<!-- 搜索区域 -->
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
</el-form-item>
<el-form-item label="" label-width="0">
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
</el-form-item>
<el-form-item label="种植作物" prop="planCrop">
<url-select
v-model="searchForm.planCrop"
url="/land-resource/crops/page"
:params="{ status: '0' }"
label-key="cropsName"
value-key="id"
placeholder="请选择种植作物"
style="width: 200px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 功能区域 -->
<section class="function-bar">
<el-button type="primary" @click="handleAdd">新增</el-button>
</section>
<!-- 表格区域 -->
<TableComponent
:loading="loading"
:columns="columns"
:table-data="tableData"
:current-page="pageData.currentPage"
:page-size="pageData.pageSize"
:total="pageData.total"
:show-pagination="true"
:show-border="true"
:show-sort="true"
style="max-height: calc(100vh - 220px)"
@page-change="handlePageChange"
>
<template #action="scope">
<custom-table-operate :actions="actions" :data="scope" />
</template>
</TableComponent>
<!-- 详情/编辑对话框 -->
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form ref="basicFormRef" :model="formData" :disabled="isReadonly" label-width="120px">
<p class="form-title">种植信息</p>
<el-row :gutter="20">
<el-col :span="4">
<!-- 作物图片 -->
<FileUploader v-model="formData.cropUrl" :limit="1" />
</el-col>
<el-col :span="10">
<!-- 种植作物 -->
<el-form-item label="种植作物" prop="cropName">
<el-input v-model="formData.cropName" placeholder="请输入种植作物名称" clearable />
</el-form-item>
<!-- 地块编号 -->
<el-form-item label="地块编号" prop="plotCode">
<el-input v-model="formData.plotCode" placeholder="请输入地块编号" clearable />
</el-form-item>
<!-- 种植基地编号 -->
<el-form-item label="种植基地编号" prop="baseCode">
<el-input v-model="formData.baseCode" placeholder="请输入种植基地编号" clearable />
</el-form-item>
<!-- 种植批次编号 -->
<el-form-item label="种植批次编号" prop="batchCode">
<el-input v-model="formData.batchCode" placeholder="请输入种植批次编号" clearable />
</el-form-item>
</el-col>
<el-col :span="10">
<!-- 作物品种 -->
<el-form-item label="作物品种" prop="cropVariety">
<el-select v-model="formData.cropVariety" placeholder="请选择作物品种" clearable filterable>
<el-option v-for="item in cropVarieties" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 地块名称 -->
<el-form-item label="地块名称" prop="plotName">
<el-input v-model="formData.plotName" placeholder="请输入地块名称" clearable />
</el-form-item>
<!-- 种植基地 -->
<el-form-item label="种植基地" prop="baseName">
<el-input v-model="formData.baseName" placeholder="请输入种植基地名称" clearable />
</el-form-item>
<!-- 种植批次 -->
<el-form-item label="种植批次" prop="batchName">
<el-input v-model="formData.batchName" placeholder="请输入种植批次名称" clearable />
</el-form-item>
</el-col>
</el-row>
<p class="form-title">农事作业管理</p>
<el-form-item label="农事作业列表">
<el-card v-for="(task, index) in formData.farmingTasks" :key="index" class="mb-4">
<el-row :gutter="20">
<el-col :span="4">
<!-- 作业图片占位可加上传组件 -->
<div
style="
width: 100px;
height: 100px;
background-color: #f0f0f0;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
"
>
<FileUploader v-model="formData.cropUrl" :limit="1" />
</div>
</el-col>
<el-col :span="20">
<el-row>
<el-col :span="12">
<el-form-item label="作业类型">
<el-input v-model="task.type" placeholder="如:种子处理、播种等" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人">
<el-input v-model="task.responsible" placeholder="请输入负责人" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="开始时间">
<el-date-picker v-model="task.startDate" type="date" placeholder="选择开始日期" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束时间">
<el-date-picker v-model="task.endDate" type="date" placeholder="选择结束日期" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="作业内容">
<el-input v-model="task.content" type="textarea" rows="3" placeholder="请输入具体作业内容" />
</el-form-item>
<el-form-item label="实际作业情况">
<el-input v-model="task.actual" type="textarea" rows="2" placeholder="如肥料使用量XX 公斤/亩" />
</el-form-item>
<el-button type="danger" @click="removeTask(index)">删除此作业</el-button>
</el-col>
</el-row>
</el-card>
<el-button type="primary" plain @click="addTask">新增作业</el-button>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<template v-if="!isReadonly">
<el-button type="primary" @click="submitAll">确定</el-button>
</template>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { mockData } from './mockData';
// ==================== ====================
const DIALOG_TITLE = {
VIEW: '农事作业详情',
EDIT: '编辑农事作业信息',
ADD: '新增农事作业',
};
// ==================== ====================
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref('');
// const activeFormTab = ref('basic');
//
const searchForm = ref({});
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const initialFormData = { ...formData.value };
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
//
const tableData = ref([]);
// ==================== ====================
const columns = ref([
{ label: '地块编号', prop: 'landNumber', width: 160 },
{ label: '地块名称', prop: 'landName', width: 170 },
{ label: '面积', prop: 'planArea', formatter: (row, column, cellValue) => `${Number(cellValue).toFixed(2)}` },
{ label: '所属行政区域', prop: 'belongRegion', width: 160 },
{ label: '所属网格', prop: 'belongGrid', width: 90 },
{ label: '具体位置', prop: 'address', width: 160 },
{ label: '基地编码', prop: 'baseCode' },
{ label: '基地名称', prop: 'baseName' },
{ label: '种植批次编码', prop: 'batchCode' },
{ label: '种植批次名称', prop: 'batchName' },
{ label: '种植作物', prop: 'crop' },
{ label: '作物品种', prop: 'cropBrand' },
{ label: '种植开始时间', prop: 'startDate' },
{ label: '种植结束时间', prop: 'endDate' },
{ label: '经营主体代码', prop: 'businessEntityCode', width: 130 },
{ label: '经营主体类型', prop: 'businessEntityType', width: 130 },
{ label: '经营主体名称', prop: 'businessEntityName', width: 130 },
{ label: '账号(手机号)', prop: 'account', width: 130 },
{ label: '信息填报时间', prop: 'fillTime', width: 110 },
{ label: '信息审核时间', prop: 'approvalTime', width: 110 },
{ label: '操作', prop: 'action', slotName: 'action', fixed: 'right' },
]);
const actions = [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
];
// ==================== ====================
onMounted(() => {
getData();
});
// ==================== ====================
const getData = async () => {
await new Promise((resolve) => setTimeout(resolve, 300));
tableData.value = mockData;
pageData.value.total = tableData.value.length;
};
// ==================== ====================
const handlePageChange = ({ page, pageSize }) => {
pageData.value.currentPage = page;
pageData.value.pageSize = pageSize;
getData();
};
// ==================== ====================
const handleAdd = () => {
resetForm();
dialogTitle.value = DIALOG_TITLE.ADD;
visible.value = true;
};
const handleView = async (row) => {
isReadonly.value = true;
dialogTitle.value = DIALOG_TITLE.VIEW;
formData.value = { ...row };
visible.value = true;
};
const handleEdit = (row) => {
isReadonly.value = false;
dialogTitle.value = DIALOG_TITLE.EDIT;
formData.value = { ...row };
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除', id);
//
};
const resetForm = () => {
formData.value = {
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
};
};
const handleSearch = () => {
pageData.value.currentPage = 1;
getData();
};
const handleReset = () => {
searchForm.value = {};
resetForm();
handleSearch();
};
const submitAll = () => {
console.log('提交表单', formData.value);
visible.value = false;
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,46 @@
export const mockData = [
{
landNumber: 'DN532526001', // 耿马县行政区划代码开头
landName: '四排山坚果标准化种植园', // 耿马县重点坚果产区
planArea: 68.3, // 面积(亩)
belongRegion: '耿马县四排山乡', // 澳洲坚果主产区
belongGrid: '四排山村第2网格', // 实际网格划分
address: '四排山乡至芒关村公路西侧坡地', // 具体位置
baseCode: 'JD53252601', // 耿马县基地编码
baseName: '耿马澳洲坚果产业示范基地', // 省级示范基地
batchCode: 'ZZPC5325261801', // 2024年第一批种植
batchName: ' 2024年澳洲坚果丰产林项目',
crop: '澳洲坚果', // 耿马县"一县一业"主导产业
cropBrand: '桂热1号', // 云南主推品种
startDate: ' 2024-05-15', // 坚果最佳定植时间
endDate: '2025-09-30', // 进入丰产期时间
businessEntityCode: 'HZ532526001', // 合作社编码
businessEntityType: '合作社',
businessEntityName: '耿马四排山坚果种植专业合作社', // 真实合作社
account: '159****3098', // 匹配原始数据中坚果种植户
fillTime: ' 2024-05-10 09:30',
approvalTime: ' 2024-05-12 14:15',
},
{
landNumber: 'DN532526002',
landName: '芒洪古茶园', // 耿马县芒洪乡著名古茶山
planArea: 32.6,
belongRegion: ' 耿马县芒洪乡',
belongGrid: '芒洪村第1网格',
address: '芒洪村后山海拔1800米处',
baseCode: 'JD53252602',
baseName: '耿马古树茶保护基地',
batchCode: 'ZZPC5325262302',
batchName: '2023年春茶采摘批次',
crop: '茶树',
cropBrand: '勐库大叶种', // 临沧市代表性茶树品种
startDate: '2023-03-20', // 春茶采摘时间
endDate: '2023-04-15',
businessEntityCode: 'HZ532526001',
businessEntityType: '合作社',
businessEntityName: '耿马芒洪茶叶专业合作社', // 真实存在的合作社
account: '138****4671', // 匹配原始数据
fillTime: '2023-03-15 14:10',
approvalTime: '2023-03-16 16:30',
},
];

View File

@ -1,9 +1,624 @@
<template>
<div></div>
<div class="custom-page">
<!-- 关键词搜索 -->
<el-form :inline="true" :model="searchForm" class="search-bar">
<el-form-item label="关键词">
<el-input v-model="searchForm.name" placeholder="请输入关键词" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 表格 -->
<avue-crud ref="crudRef" v-model:page="pageData" :data="crudData" :option="crudOptions" :table-loading="loading">
<template v-if="activeTab === '0'" #menu-left>
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template>
</avue-crud>
<!-- 新增弹窗 -->
<el-dialog :key="dialogTitle" v-model="dialogVisible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form :model="formData" label-width="120px" class="custom-form" :disabled="isReadonly">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证件类型" prop="idType">
<el-select v-model="formData.idType" placeholder="请选择证件类型" disabled>
<el-option label="身份证" value="101" />
<el-option label="护照" value="2" />
<el-option label="港澳身份证" value="3" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="证件号码" prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入证件号码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio value="1"></el-radio>
<el-radio value="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="年龄" prop="age">
<el-input v-model="formData.age" placeholder="请输入年龄" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="phone">
<el-input v-model="formData.phone" placeholder="请输入联系方式" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="居住地行政区划" prop="addressArr">
<area-select v-model="formData.addressArr" :label="null" />
<!-- <el-input v-model="formData.detailAddress" placeholder="请选择居住地行政区划" /> -->
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="具体地址" prop="detailAddress">
<el-input v-model="formData.detailAddress" placeholder="请输入具体地址" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="种植面积" prop="area">
<el-input v-model="formData.area" placeholder="请输入种植面积" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="种植作物" prop="planCrop">
<!-- <el-select v-model="formData.planCrop" placeholder="种植作物" style="width: 380px" :clearable="true">
<el-option v-for="item in cropsOptions" :key="item.id" :label="item.cropsName" :value="item.id" />
</el-select> -->
<url-select
v-model="formData.planCrop"
url="/land-resource/crops/page"
:params="{ status: '0' }"
label-key="cropsName"
value-key="id"
placeholder="请选择种植作物"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button v-if="!isReadonly" type="primary" @click="handleSave">保存</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { ref, computed, reactive, onMounted, watch, nextTick } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { CRUD_OPTIONS } from '@/config';
import { fetchFarmerList, fetchFarmerById, saveFarmerList, editFarmer, approveFarmer, deleteFarmers } from '@/apis/businessEntity';
import { pageCropsList } from '@/apis/landResourceManagement/cropsManagement/index.js';
// ---------------------------------------------------------------------
// 1.
// 'superadmin''auditor''submitter'
// ---------------------------------------------------------------------
const role = ref('superadmin'); // 'superadmin' / 'auditor' / 'submitter'
//
const isSuperAdmin = computed(() => role.value === 'superadmin');
const isAuditor = computed(() => role.value === 'auditor');
// ---------------------------------------------------------------------
// 2. Tab
// ---------------------------------------------------------------------
const activeTab = ref('2');
const dialogTitle = ref('新增');
const isReadonly = ref(false);
//
const searchForm = ref({
name: '',
idCard: '',
phone: '',
status: '',
userId: '',
});
const loading = ref(false);
const crudRef = ref();
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
//
const crudData = ref([]);
//
const dialogVisible = ref(false);
//
const defaultFormData = {
name: '',
idType: '101',
idCard: '',
sex: '1',
age: '',
phone: '',
provinceCode: '', //
cityCode: '', //
countyCode: '', //
townCode: '', //
villageCode: '', //
address: '',
addressArr: [],
detailAddress: '',
area: '',
planCrop: '',
reason: '',
};
// 使
const formData = ref({ ...defaultFormData });
//
const resetForm = () => {
formData.value = { ...defaultFormData };
};
// ==============================
// CRUD
// ==============================
const crudOptions = reactive({
...CRUD_OPTIONS,
addBtn: false,
searchBtn: false,
emptyBtn: false,
refreshBtn: false,
column: [
{ label: '主体编码', prop: 'id' },
{ label: '姓名', prop: 'name' },
{
label: '证件类型',
prop: 'idType',
formatter: (row, column, cellValue) => {
return cellValue === '101' ? '身份证' : '其它';
},
},
{ label: '证件号码', prop: 'idCard' },
{
label: '性别',
prop: 'sex',
formatter: (row, column, cellValue) => {
return cellValue === '1' ? '男' : '女';
},
},
{ label: '年龄', prop: 'age' },
{ label: '联系方式', prop: 'phone' },
{ label: '所属行政区划', prop: 'address' },
{ label: '种植作物', prop: 'planCropName' },
{ label: '数据来源', prop: 'dataSource' },
{ label: '创建时间', prop: 'createTime' },
],
});
// ---------------------------------------------------------------------
// 3. API/
// ---------------------------------------------------------------------
async function getData() {
loading.value = true;
try {
const params = {
...searchForm.value,
status: activeTab.value,
current: pageData.value.currentPage,
size: pageData.value.pageSize,
};
const response = await fetchFarmerList(params);
if (response.code === 200 && response.data) {
crudData.value = response.data.records;
console.log('获取数据成功:', crudData.value);
pageData.value = {
currentPage: response.data.current,
pageSize: response.data.size,
total: response.data.total,
};
} else {
ElMessage.error(response.msg || '获取数据失败');
}
} catch (error) {
ElMessage.error('获取数据失败,请稍后重试');
} finally {
loading.value = false;
}
}
//
const cropsOptions = ref([]);
//
const fetchCropsList = async () => {
try {
// pageCropsList
const res = await pageCropsList({ status: '0' });
if (res.code === 200) {
console.log('res :>> ', res.data.records);
cropsOptions.value = res.data.records;
console.log('object :>> ', cropsOptions.value);
}
} catch (error) {
console.error('获取种植作物列表失败:', error);
}
};
function handleSearch() {
getData();
}
function handleReset() {
searchForm.value.keyword = '';
handleSearch();
}
function handleTabChange(tab) {
handleSearch();
}
//
const handleAdd = () => {
isReadonly.value = false; //
resetForm();
dialogTitle.value = '新增';
dialogVisible.value = true;
};
// address
watch(
() => formData.value.addressArr,
(newValue) => {
if (newValue.length === 5) {
formData.value.provinceCode = newValue[0] || '';
formData.value.cityCode = newValue[1] || '';
formData.value.countyCode = newValue[2] || '';
formData.value.townCode = newValue[3] || '';
formData.value.villageCode = newValue[4] || '';
} else {
ElMessageBox.alert('行政区划数据错误');
}
}
);
// saveFarmerList
const handleSave = async () => {
try {
let response;
if (dialogTitle.value === '新增') {
console.log('新增formData.value.arr :>> ', formData.value.addressArr);
// saveFarmerList
response = await saveFarmerList(formData.value);
if (response.code === 200) {
ElMessage.success('新增成功');
}
} else if (dialogTitle.value === '编辑') {
// editFarmer
response = await editFarmer(formData.value);
if (response.code === 200) {
ElMessage.success('编辑成功');
}
}
if (response && response.code === 200) {
dialogVisible.value = false;
getData(); //
}
} catch (error) {
if (dialogTitle.value === '新增') {
ElMessage.error('新增失败,请稍后重试');
} else {
ElMessage.error('编辑失败,请稍后重试');
}
}
};
const getFarmerById = async (id) => {
try {
const response = await fetchFarmerById(id);
if (response.code === 200 && response.data) {
return response.data;
} else {
ElMessage.error(response.msg || '获取数据失败');
return null;
}
} catch (error) {
ElMessage.error('获取数据失败,请稍后重试');
return null;
}
};
//
async function handleView(row) {
dialogTitle.value = '查看';
isReadonly.value = true; //
const data = await getFarmerById(row.id);
if (data) {
const addressArr = [data.provinceCode, data.cityCode, data.countyCode, data.townCode, data.villageCode].filter(Boolean);
formData.value = {
...data,
addressArr: addressArr,
};
}
nextTick(() => {
dialogVisible.value = true;
});
}
// /
async function handleEdit(row) {
if (row.status === '2') {
try {
await ElMessageBox.confirm('编辑后数据将需要重新审核,是否继续?', '确认编辑', {
confirmButtonText: '继续编辑',
cancelButtonText: '取消',
type: 'warning',
});
} catch {
return;
}
}
const data = await getFarmerById(row.id);
if (data) {
const addressArr = [data.provinceCode, data.cityCode, data.countyCode, data.townCode, data.villageCode].filter(Boolean);
formData.value = {
...data,
addressArr: addressArr,
};
}
dialogTitle.value = '编辑';
isReadonly.value = false;
nextTick(() => {
dialogVisible.value = true;
});
}
//
function handleSubmit(row) {
ElMessageBox.confirm('确定提交审核吗?', '提交审核').then(() => {
const params = {
id: row.id,
status: '1',
reason: row.reason || '',
};
approveFarmer(params)
.then(() => {
row.rejectReason = ''; //
getData(); //
})
.catch(() => {
ElMessage.error('提交审核失败,请稍后重试');
});
});
}
//
function handleResubmit(row) {
ElMessageBox.confirm('确认重新提交吗?', '重新提交').then(() => {
const params = {
id: row.id,
status: '1', //
reason: '重新提交审核',
};
approveFarmer(params)
.then(() => {
console.log(`ID=${row.id} 重新提交审核`);
row.status = '1';
row.rejectReason = '';
getData();
ElMessage.success('已重新提交,状态已变为"待审核"');
})
.catch((error) => {
console.error('重新提交失败:', error);
ElMessage.error(error.response?.data?.msg || '重新提交失败');
});
});
}
//
function handleWithdraw(row) {
ElMessageBox.confirm('确认撤销本次审核吗?', '撤销').then(() => {
const params = {
id: row.id,
status: '0', //
reason: '用户主动撤销',
};
approveFarmer(params)
.then(() => {
console.log(`ID=${row.id} 撤销审核`);
row.status = '0';
getData();
ElMessage.success('已撤销,状态已变为"待提交"');
})
.catch((error) => {
console.error('撤销失败:', error);
ElMessage.error(error.response?.data?.msg || '撤销失败');
});
});
}
//
function handleApprove(row) {
ElMessageBox.confirm('确认通过审核?', '审核通过').then(() => {
const params = {
id: row.id,
status: '2', //
reason: '审核通过',
};
approveFarmer(params)
.then(() => {
console.log(`ID=${row.id} 审核通过`);
row.status = '2';
getData();
ElMessage.success('审核已通过');
})
.catch((error) => {
console.error('审核通过操作失败:', error);
ElMessage.error(error.response?.data?.msg || '审核操作失败');
});
});
}
//
function handleReject(row) {
ElMessageBox.prompt('请输入驳回原因', '审核驳回', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /.+/, //
inputErrorMessage: '驳回原因不能为空',
})
.then(({ value }) => {
const params = {
id: row.id,
status: '3', //
reason: value.trim(),
};
return approveFarmer(params).then(() => {
console.log(`ID=${row.id} 驳回,原因:${value.trim()}`);
row.status = '3';
row.rejectReason = value.trim();
getData();
ElMessage.success('已驳回');
});
})
.catch((error) => {
if (error !== 'cancel') {
console.error('驳回操作失败:', error);
ElMessage.error(error.response?.data?.msg || '驳回操作失败');
}
});
}
// /
function showRejectReason(row) {
ElMessageBox.alert(row.reason || '无驳回原因', '驳回原因');
}
async function handleDelete(row) {
try {
await ElMessageBox.confirm('确定删除该条记录?', '删除提示');
console.log(`删除 ID=${row.id}`);
await deleteFarmers(row.id); // ID
await getData();
ElMessage.success('已删除');
} catch (error) {
if (error !== 'cancel') {
ElMessage.error(`删除失败: ${error.message || '请稍后重试'}`);
}
}
}
//
const ACTION_MAP = {
view: { name: '查看', icon: 'view', handler: handleView },
edit: { name: '编辑', icon: 'edit', handler: handleEdit },
submit: { name: '提交审核', icon: 'submit', handler: handleSubmit },
delete: { type: 'danger', name: '删除', icon: 'delete', handler: handleDelete },
approve: { name: '通过', icon: 'approve', handler: handleApprove },
reject: { name: '驳回', icon: 'reject', handler: handleReject },
withdraw: { name: '撤销', icon: 'withdraw', handler: handleWithdraw },
resubmit: { name: '重新提交', icon: 'resubmit', handler: handleResubmit },
reason: { name: '驳回原因', icon: 'reason', handler: showRejectReason },
modify: { name: '修改', icon: 'edit', handler: handleEdit },
};
//
const ROLE_STATUS_ACTIONS = {
superadmin: {
0: ['view', 'edit', 'submit', 'delete'],
1: ['view', 'approve', 'reject', 'withdraw', 'delete'],
2: ['view', 'modify', 'delete'],
3: ['view', 'edit', 'reason', 'resubmit', 'delete'],
},
auditor: {
0: [],
1: ['view', 'approve', 'reject'],
2: ['view', 'delete'],
3: ['view', 'reason', 'delete'],
},
submitter: {
0: ['view', 'edit', 'submit', 'delete'],
1: ['view', 'withdraw', 'delete'],
2: ['view', 'modify', 'delete'],
3: ['view', 'edit', 'reason', 'resubmit', 'delete'],
},
};
//
function getActions(row) {
const currentRole = role.value;
const status = row.status;
const actionKeys = ROLE_STATUS_ACTIONS[currentRole]?.[status] || [];
return actionKeys.map((key) => {
const action = ACTION_MAP[key];
return {
...action,
event: () => action.handler(row),
};
});
}
// ---------------------------------------------------------------------
// 4. crudData
// ---------------------------------------------------------------------
onMounted(() => {
getData();
fetchCropsList(); //
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
// :deep(.avue-crud__header) {
// display: none;
// }
.custom-page {
padding: 20px;
height: calc(100vh - 150px);
overflow-y: auto;
.search-bar {
margin-bottom: 20px;
}
}
:deep(.el-dialog__body) {
padding: 20px;
height: calc(100vh - 300px);
overflow-y: auto;
}
.custom-form {
padding: 20px;
.el-form-item {
margin-bottom: 22px;
}
.el-input,
.el-select,
.el-cascader,
.el-date-picker {
width: 500px;
max-width: 100%; //
box-sizing: border-box; // padding/border
}
}
</style>