This commit is contained in:
13713575202 2025-06-13 10:33:49 +08:00
commit 0fbc726e8e
38 changed files with 3479 additions and 2468 deletions

View File

@ -4,7 +4,7 @@
基于 qiankun + vue2&vue3 + Vite + vite-plugin-qiankun 技术栈实现的前端微应用架构,实现了主子应用切换。
1、主应用介绍
1、主应用介绍
技术栈 Vite+Vue3 + Element-plus + qiankun + Pinia

View File

@ -16,6 +16,6 @@ VITE_APP_UPLOAD_API = '/uploadApis'
# VITE_APP_BASE_URL = 'http://47.109.205.240:8080'
# VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300'
# 内网接口地址
VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:8080'
VITE_APP_VIST_URL = 'http://192.168.18.99'
VITE_APP_BASE_URL = 'http://192.168.18.74:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'
# VITE_APP_VIST_URL = 'http://192.168.18.99'

View File

@ -13,5 +13,5 @@ VITE_APP_UPLOAD_API = '/uploadApis'
# VITE_APP_UPLOAD_URL = 'http://47.109.205.240:9300'
# 内网接口地址
VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:8080'
VITE_APP_BASE_URL = 'http://192.168.18.74:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'

View File

@ -59,7 +59,7 @@ export function fetchFarmerById(id) {
}
/**
* 删除农户接口严格匹配文档规范
* 农户-删除
* @param {string|string[]} ids - 单个ID或ID数组会自动转为逗号分隔字符串
* @returns {Promise} 请求Promise
*/
@ -75,29 +75,10 @@ export function deleteFarmers(ids) {
}
/**
* 生产经营主体 - 审核
* @param {Object} data 审核数据
*/
export function checkBusinessSubject(data) {
return request({
url: '/product-business/business/businessCheck',
method: 'put',
data,
});
}
/**
* 经营主体审核 - 分页查询
* 企业管理 - 列表查询
* @param {Object} params 查询参数
* @returns {Promise} 请求Promise
*/
export function fetchBusinessCheckList(params) {
return request({
url: '/product-business/business/businessCheckPage',
method: 'get',
params,
});
}
// 农企合作社-列表查询/product-business/business/enter/businessPage
export function getEnterList(params) {
return request({
url: '/product-business/business/enter/businessPage',
@ -105,3 +86,115 @@ export function getEnterList(params) {
params,
});
}
/**
* 企业管理 - 详情查询
* @param {string} id 企业I
* @returns {Promise} 请求Promise
*/
export function getEnterById(id) {
return request({
url: `/product-business/business/enter/businessInfo/${id}`,
method: 'get',
});
}
/**
* 企业管理 - 新增
* @param {Object} data 企业数据
* @returns {Promise} 请求Promise
*/
export function addEnter(data) {
return request({
url: '/product-business/business/enter/businessSave',
method: 'post',
data,
});
}
/**
* 企业管理 - 编辑
* @param {Object} data 企业数据
* @returns {Promise} 请求Promise
*/
export function updateEnter(data) {
return request({
url: '/product-business/business/enter/businessEdit',
method: 'put',
data,
});
}
/**
* 企业管理 - 审核
* @param {Object} data 审核数据
*/
export function approvalEnter(data) {
return request({
url: '/product-business/business/enter/businessApproval',
method: 'put',
data,
});
}
/**
* 企业管理 - 删除
* @param {string|string[]} ids - 单个ID或ID数组会自动转为逗号分隔字符串
* @returns {Promise} 请求Promise
*/
export function deleteEnter(ids) {
// 统一参数格式:数组转逗号分隔字符串,非数组直接使用
const idStr = Array.isArray(ids) ? ids.join(',') : ids;
return request({
url: '/product-business/business/enter/deleteBusiness',
method: 'delete',
params: { businessId: idStr },
});
}
/**
* 社员列表查询
* @param {Object} params 查询参数
*/
export function getMemberList(params) {
return request({
url: '/product-business/business/member/memberPage',
method: 'get',
params,
});
}
/**
* 社员新增
* @param {Object} data 社员数据
*/
export function addMember(data) {
return request({
url: '/product-business/business/member/businessSave',
method: 'post',
data,
});
}
/**
* 社员编辑
* @param {Object} data 社员数据
*/
export function updateMember(data) {
return request({
url: '/product-business/business/member/memberEdit',
method: 'put',
data,
});
}
/**
* 社员删除
* @param {string|string[]} ids - 单个ID或ID数组会自动转为逗号分隔字符串
* @returns {Promise} 请求Promise
*/
export function deleteMembers(ids) {
// 统一参数格式:数组转逗号分隔字符串,非数组直接使用
const idStr = Array.isArray(ids) ? ids.join(',') : ids;
return request({
url: '/product-business/business/member/deleteMember',
method: 'delete',
params: { ids: idStr },
});
}

View File

@ -0,0 +1,43 @@
import request from '@/utils/axios';
// 新增POST
export function createGrid(data = {}) {
return request('/land-resource/gridManage/save', {
method: 'POST',
data,
});
}
// 删除DELETE
export function deleteGrid(id) {
return request(`/land-resource/gridManage/${id}`, {
method: 'DELETE',
});
}
// 修改PUT
export function updateGrid(data = {}) {
return request('/land-resource/gridManage/edit', {
method: 'PUT',
data,
});
}
// 查询列表GET
export function fetchGridList(params) {
return request('/land-resource/gridManage/page', {
method: 'GET',
params,
});
}
// 获取详情GET
export function getGridDetail(id) {
return request(`/land-resource/gridManage/${id}`, {
method: 'GET',
});
}
// 导出GET + Blob
export function exportGrid(params = {}) {
return request('/land-resource/gridManage/export', {
method: 'GET',
params,
responseType: 'blob',
});
}

View File

@ -0,0 +1,29 @@
import request from '@/utils/axios';
/* 查询土地违法处理 */
export function getLandIllegal(params = {}) {
return request('land-resource/landViolation/page', {
method: 'GET',
params,
});
}
/* 新增土地违法处理 */
export function createLandIllegal(data = {}) {
return request('land-resource/landViolation/save', {
method: 'POST',
data,
});
}
/* 登记处理 */
export function registerLandIllegal(data = {}) {
return request('land-resource/landViolation/registResult', {
method: 'PUT',
data,
});
}
/* 违法详情 */
export function illegalInfo(id = '') {
return request('land-resource/landViolation/detail/' + id, {
method: 'GET',
});
}

View File

@ -0,0 +1,87 @@
// api/landInspection.js
import request from '@/utils/axios';
// 新增POST
export function createLandInspection(data) {
return request('land-resource/landInspection/save', {
method: 'POST',
data,
});
}
// 删除DELETE
export function deleteLandInspection(id) {
return request(`land-resource/landInspection/${id}`, {
method: 'DELETE',
});
}
// 修改PUT
export function updateLandInspection(data = {}) {
return request('/land-resource/landInspection/update', {
method: 'PUT',
data,
});
}
// 修改状态PUT
export function updateLandInspectionStatus(data = {}) {
return request('/land-resource/landInspection/updateStatus', {
method: 'PUT',
data,
});
}
// 查询详情GET
export function getLandInspectionDetail(id) {
return request(`land-resource/landInspection/${id}`, {
method: 'GET',
});
}
// 查询列表 admin GET
export function fetchLandInspectionList(params = {}) {
return request('land-resource/landInspection/page', {
method: 'GET',
params,
});
}
// 查询列表 执行人员 GET
export function fetchLandInspectionGen(params = {}) {
return request('/land-resource/landInspection/page/general', {
method: 'GET',
params,
});
}
// 导出GET + Blob
export function exportLandInspection(params = {}) {
return request('/land-resource/landInspection/export', {
method: 'GET',
params,
responseType: 'blob',
});
}
// 巡查结果-新增POST
export function createInspectionResult(data) {
return request('/land-resource/inspection/result/save', {
method: 'POST',
data,
});
}
// 巡查结果-批量新增POST
export function createInspectionResultBatch(data) {
return request('/land-resource/inspection/result/saveBatch', {
method: 'POST',
data,
});
}
// 巡查结果-删除DELETE
export function deleteInspectionResult(id) {
return request(`/land-resource/inspection/result/${id}`, {
method: 'DELETE',
});
}
// 巡查结果-违法事件查看GET
export function getInspectionResultDetail(id) {
return request(`/land-resource/inspection/result/${id}`, {
method: 'GET',
});
}

View File

@ -1,40 +1,27 @@
<template>
<div class="area-cascader-container" :style="{ width: width + 'px' }">
<div class="area-cascader-container" :style="containerStyle">
<!-- 一行显示模式 -->
<template v-if="!splitRows">
<div v-if="label" class="area-cascader-label">{{ label }}</div>
<div style="display: flex; gap: 8px; flex: 1">
<el-cascader
v-model="selectedAreaCode"
:options="areaOptions"
:props="cascaderProps"
:placeholder="areaPlaceholder"
style="flex: 1"
clearable
/>
<div class="controls">
<el-cascader v-model="regionModel" :options="areaOptions" :props="cascaderProps" :placeholder="areaPlaceholder" style="flex: 1" clearable />
<span v-if="showSeparator" class="area-cascader-separator">{{ separator }}</span>
<el-select v-model="selectedGridId" :placeholder="gridPlaceholder" style="flex: 1" clearable :disabled="!selectedAreaCode">
<el-option v-for="item in gridOptions" :key="item.gridName" :label="item.gridName" :value="item.id" />
<el-select v-model="gridModel" :placeholder="gridPlaceholder" style="flex: 1" :disabled="!regionModel" clearable>
<el-option v-for="item in gridOptions" :key="item.id" :label="item.gridName" :value="item.id" />
</el-select>
</div>
</template>
<!-- 两行显示模式 -->
<template v-else>
<div class="area-item">
<div class="area-cascader-label">所属行政区域</div>
<el-cascader
v-model="selectedAreaCode"
:options="areaOptions"
:props="cascaderProps"
:placeholder="areaPlaceholder"
style="flex: 1"
clearable
/>
<el-cascader v-model="regionModel" style="flex: 1" :options="areaOptions" :props="cascaderProps" :placeholder="areaPlaceholder" clearable />
</div>
<div class="area-item">
<div class="area-cascader-label">网格</div>
<el-select v-model="selectedGridId" :placeholder="gridPlaceholder" style="flex: 1" clearable :disabled="!selectedAreaCode">
<el-option v-for="item in gridOptions" :key="item.gridName" :label="item.gridName" :value="item.id" />
<el-select v-model="gridModel" style="flex: 1" :placeholder="gridPlaceholder" :disabled="!regionModel" clearable>
<el-option v-for="item in gridOptions" :key="item.id" :label="item.gridName" :value="item.id" />
</el-select>
</div>
</template>
@ -42,152 +29,49 @@
</template>
<script setup>
import { ref, watch, onMounted, computed } from 'vue';
import { ElCascader, ElSelect, ElOption } from 'element-plus';
import { ref, onMounted, computed } from 'vue';
import request from '@/utils/axios';
import { useUserStore } from '@/store/modules/user';
const props = defineProps({
regionCode: {
type: String,
default: '',
},
gridId: {
type: [String, Number],
default: '',
},
label: {
type: String,
default: '行政区域-网格:',
},
areaPlaceholder: {
type: String,
default: '请选择区域',
},
gridPlaceholder: {
type: String,
default: '请选择网格',
},
width: {
type: [Number, String],
default: 500,
},
showSeparator: {
type: Boolean,
default: false,
},
separator: {
type: String,
default: '-',
},
splitRows: {
type: Boolean,
default: false,
},
regionCode: { type: String, default: '' },
gridId: { type: [String, Number], default: '' },
label: { type: String, default: '行政区域-网格:' },
areaPlaceholder: { type: String, default: '请选择区域' },
gridPlaceholder: { type: String, default: '请选择网格' },
width: { type: [Number, String], default: 500 },
showSeparator: { type: Boolean, default: false },
separator: { type: String, default: '-' },
splitRows: { type: Boolean, default: false },
});
const emit = defineEmits(['update:regionCode', 'update:gridId']);
const userStore = useUserStore();
const areaOptions = ref([]);
const gridOptions = ref([]);
//
const selectedAreaCode = ref(props.regionCode);
const selectedGridId = ref(props.gridId);
//
const fetchAreaData = async () => {
try {
// 使 request params
const res = await request.get('/system/area/region', {
params: {
areaCode: '530000',
},
});
areaOptions.value = res.data ?? [];
} catch (err) {
console.error('区域数据加载失败', err);
}
};
//
const fetchGridList = async (regionCode) => {
if (!regionCode) return;
try {
// 使 request params
const res = await request.get('/land-resource/gridManage/page', {
params: {
regionCode: regionCode,
},
});
gridOptions.value = res.data?.records ?? [];
} catch (err) {
console.error('网格数据加载失败', err);
}
};
const selectedAreaLabel = computed(() => {
const findLabel = (options, code) => {
for (const item of options) {
if (item.areaCode === code) return item.areaName;
if (item.areaChildVOS?.length) {
const res = findLabel(item.areaChildVOS, code);
if (res) return res;
}
}
return '';
};
return findLabel(areaOptions.value, selectedAreaCode.value);
// computed regionModel & gridModel
const regionModel = computed({
get() {
return props.regionCode;
},
set(val) {
emit('update:regionCode', val);
// reset grid when region changes
emit('update:gridId', '');
gridOptions.value = [];
fetchGridList(val);
},
});
const gridModel = computed({
get() {
return props.gridId;
},
set(val) {
emit('update:gridId', val);
},
});
const selectedGridLabel = computed(() => {
const item = gridOptions.value.find((g) => g.id === selectedGridId.value);
return item?.gridName || '';
});
//
watch(
() => props.regionCode,
(val) => {
selectedAreaCode.value = val;
}
);
watch(
() => props.gridId,
(val) => {
selectedGridId.value = val;
}
);
//
watch(selectedAreaCode, (val) => {
emit('update:regionCode', val);
selectedGridId.value = ''; // gridId
emit('update:gridId', '');
fetchGridList(val);
});
watch(selectedGridId, (val) => {
emit('update:gridId', val);
});
// v-model:value
// const updateValue = () => {
// console.log('update:value', selectedAreaCode.value, selectedGridName.value, selectedGridId.value);
// emit('update:value', {
// regionCode: selectedAreaCode.value,
// gridName: selectedGridName.value,
// gridId: selectedGridId.value,
// });
// };
onMounted(() => {
fetchAreaData();
});
//
const cascaderProps = computed(() => ({
label: 'areaName',
value: 'areaCode',
@ -195,37 +79,62 @@ const cascaderProps = computed(() => ({
emitPath: false,
expandTrigger: 'hover',
}));
async function fetchAreaData() {
try {
const res = await request.get('/system/area/region', { params: { areaCode: '530000' } });
areaOptions.value = res.data || [];
} catch (err) {
console.error('区域数据加载失败', err);
}
}
async function fetchGridList(regionCode) {
if (!regionCode) return;
try {
const res = await request.get('/land-resource/gridManage/page', { params: { regionCode } });
gridOptions.value = res.data?.records || [];
} catch (err) {
console.error('网格数据加载失败', err);
}
}
onMounted(fetchAreaData);
//
const containerStyle = computed(() => ({
width: typeof props.width === 'number' ? `${props.width}px` : props.width,
display: 'flex',
gap: '18px',
flexDirection: props.splitRows ? 'column' : 'row',
}));
</script>
<style scoped>
.area-cascader-container {
display: flex;
gap: 18px;
margin: 0;
padding: 0;
/* 分行显示时垂直排列 */
flex-direction: v-bind('splitRows ? "column" : "row"');
}
.area-cascader-label {
font-size: 14px;
color: #606266;
text-align: right;
line-height: 32px;
box-sizing: border-box;
width: 120px;
padding-right: v-bind('splitRows ? "12px" : "0"');
}
.area-cascader-separator {
align-self: center;
font-size: 16px;
color: #606266;
margin: 0;
padding: 0;
.controls {
display: flex;
gap: 8px;
flex: 1;
}
.area-item {
display: flex;
gap: 0;
align-items: center;
/* background-color: antiquewhite; */
}
.area-cascader-separator {
align-self: center;
font-size: 16px;
color: #606266;
}
</style>

View File

@ -24,7 +24,7 @@ const props = defineProps({
default: false,
},
modelValue: {
type: Array,
type: [Array, String],
default: () => [],
},
label: {
@ -39,6 +39,10 @@ const props = defineProps({
type: [Number, String],
default: 500,
},
emitPath: {
type: Boolean,
default: true,
},
});
const emit = defineEmits(['update:modelValue']);
@ -46,14 +50,35 @@ const emit = defineEmits(['update:modelValue']);
const userStore = useUserStore();
const areaOptions = ref([]);
const selectedAreaPath = ref([...props.modelValue]);
//
// const selectedAreaPath = props.emitPath ? ref([].concat(props.modelValue)) : ref(props.modelValue);
const selectedAreaPath = computed({
get() {
// &
if (props.emitPath) {
return Array.isArray(props.modelValue) ? props.modelValue : [];
} else {
return typeof props.modelValue === 'string' ? props.modelValue : '';
}
},
set(val) {
//
if (props.emitPath) {
emit('update:modelValue', Array.isArray(val) ? val : []);
} else {
emit('update:modelValue', typeof val === 'string' ? val : '');
}
},
});
//
const cascaderProps = computed(() => ({
label: 'areaName',
value: 'areaCode',
children: 'areaChildVOS',
emitPath: true,
emitPath: props.emitPath,
expandTrigger: 'hover',
}));
@ -65,23 +90,34 @@ const fetchAreaData = async () => {
},
});
areaOptions.value = res.data ?? [];
console.log('AreaSelect区域数据', areaOptions.value);
} catch (err) {
console.error('加载行政区域失败', err);
}
};
// =>
watch(
() => props.modelValue,
(val) => {
selectedAreaPath.value = [...val];
}
);
// 使
// // =>
// watch(
// () => props.modelValue,
// (val) => {
// selectedAreaPath.value = [...val];
// }
// );
// =>
watch(selectedAreaPath, (val) => {
emit('update:modelValue', val);
});
watch(
selectedAreaPath,
(val) => {
if (props.emitPath) {
//
emit('update:modelValue', Array.isArray(val) ? val : []);
} else {
//
emit('update:modelValue', typeof val === 'string' ? val : '');
}
},
{ deep: true }
);
onMounted(() => {
fetchAreaData();

View File

@ -0,0 +1,110 @@
<template>
<div class="file-uploader">
<el-upload
class="file-uploader__upload"
:http-request="customUploadRequest"
:on-success="handleUploadSuccess"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture-card"
:limit="limit"
:show-file-list="true"
:auto-upload="true"
:disabled="readonly"
:accept="accept"
@preview="handlePreview"
>
<el-icon v-if="fileList.length < limit"><Plus /></el-icon>
</el-upload>
<el-image-viewer v-if="previewShow" :url-list="previewList" :initial-index="previewIndex" @close="previewShow = false" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import { CommonUpload } from '@/apis/index';
// 1. props & emit
const props = defineProps({
modelValue: { type: [Array, String], default: () => [] },
ossUrl: { type: String, default: 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/' },
limit: { type: Number, default: 5 },
accept: { type: String, default: 'image/*' },
readonly: { type: Boolean, default: false },
});
const emit = defineEmits(['update:modelValue']);
// 2. computed limit
const selectedFiles = computed({
get() {
// limit===1 1
if (props.limit === 1 && typeof props.modelValue === 'string' && props.modelValue) {
return [props.modelValue];
}
//
return Array.isArray(props.modelValue) ? props.modelValue : [];
},
set(val) {
//
if (props.limit === 1) {
emit('update:modelValue', val.length ? val[0] : '');
} else {
emit('update:modelValue', val);
}
},
});
// 3. fileList & previewList selectedFiles
const fileList = computed(() =>
selectedFiles.value.map((path, idx) => ({
name: `file_${idx}`,
url: props.ossUrl + path,
uid: `${idx}`,
}))
);
const previewShow = ref(false);
const previewIndex = ref(0);
const previewList = computed(() => fileList.value.map((f) => f.url));
// 4. &
const customUploadRequest = async ({ file, onSuccess, onError }) => {
const formData = new FormData();
formData.append('file', file);
try {
const res = await CommonUpload(formData);
onSuccess(res, file);
} catch (err) {
onError(err);
}
};
function handleUploadSuccess(res) {
const relative = res.data?.url;
if (!relative) return;
//
selectedFiles.value = [...selectedFiles.value, relative];
}
function handleRemove(file) {
const rel = file.url.replace(props.ossUrl, '');
selectedFiles.value = selectedFiles.value.filter((p) => p !== rel);
}
// 5.
function handlePreview(file) {
const idx = fileList.value.findIndex((item) => item.uid === file.uid);
if (idx >= 0) {
previewIndex.value = idx;
previewShow.value = true;
}
}
</script>
<style scoped>
.file-uploader {
display: flex;
flex-wrap: wrap;
}
.file-uploader__upload {
margin-right: 16px;
}
</style>

View File

@ -54,6 +54,10 @@ const props = defineProps({
type: String,
default: 'value',
},
responseParser: {
type: Function,
default: (res) => res.data.records, //
},
});
// -------------- Events --------------
@ -83,13 +87,13 @@ async function fetchOptions() {
if (!props.url) return;
const res = await request.get(props.url, { params: props.params });
const records = res.data.records;
const records = props.responseParser(res);
if (Array.isArray(records)) {
options.value = records;
// console.log('option', options.value);
} else {
options.value = [];
console.log('UrlSelect接口返回数据格式不是数组无法解析成 options');
console.error('UrlSelect解析后的数据不是数组');
}
}

View File

@ -0,0 +1,35 @@
// src/hooks/useActionPermissions.js
import { computed } from 'vue';
function getBaseRoles(role, permissionMap) {
// 允许角色继承(如 admin 自动继承其他角色权限)
const inherited = permissionMap[role];
return inherited && Array.isArray(inherited) ? inherited : [role];
}
function getAllowedKeys(role, status, permissionMap, statusMap) {
const baseRoles = getBaseRoles(role, permissionMap);
const roleKeys = new Set(baseRoles.flatMap((r) => permissionMap[r] || []));
const statusKeys = new Set(statusMap[status] || []);
return [...roleKeys].filter((key) => statusKeys.has(key));
}
/**
* 通用权限控制 Hook
* @param {Ref<String>} roleRef 用户角色 ref
* @param {Ref<String>} statusRef 当前状态 ref
* @param {Array} actions 操作项列表必须含 key 字段
* @param {Object} permissionMap 角色权限表{ operator: ['edit', ...] }
* @param {Object} statusMap 状态权限表{ '00': ['edit', ...] }
*/
export function useActionPermissions(roleRef, statusRef, actions, permissionMap, statusMap) {
const filteredActions = computed(() => {
const allowedKeys = getAllowedKeys(roleRef.value, statusRef.value, permissionMap, statusMap);
return actions.filter((action) => allowedKeys.includes(action.key));
});
return {
actions: filteredActions,
};
}

View File

@ -1,26 +1,26 @@
import Views from '@/layouts/Views.vue';
// import Views from '@/layouts/Views.vue';
const annualplanRoutes = [
{
path: '/sub-government-affairs-service/annualPlan',
name: 'annualPlan',
component: Views,
redirect: '/sub-government-affairs-service/annualPlans',
meta: { title: '种植进度网格化管理', icon: '' },
children: [
// {
// path: '/sub-government-affairs-service/annualPlans',
// name: 'annualPlans',
// component: () => import('@/views/annualPlan/component/annualPlans/index.vue'),
// meta: { title: '年度种植计划', icon: '' },
// },
{
path: '/sub-government-affairs-service/annualPlans',
name: 'annualPlans',
component: () => import('@/views/annualPlan/component/annualPlans/index.vue'),
meta: { title: '种植进度网格化管理', icon: 'Memo' },
},
],
},
];
export default annualplanRoutes;
// const annualplanRoutes = [
// {
// path: '/sub-government-affairs-service/annualPlan',
// name: 'annualPlan',
// component: Views,
// redirect: '/sub-government-affairs-service/annualPlans',
// meta: { title: '种植进度网格化管理', icon: '' },
// children: [
// {
// path: '/sub-government-affairs-service/annualPlans',
// name: 'annualPlans',
// component: () => import('@/views/annualPlan/component/annualPlans/index.vue'),
// meta: { title: '年度种植计划', icon: '' },
// },
// {
// path: '/sub-government-affairs-service/annualPlans',
// name: 'annualPlans',
// component: () => import('@/views/annualPlan/component/annualPlans/index.vue'),
// meta: { title: '种植进度网格化管理', icon: 'Memo' },
// },
// ],
// },
// ];
// export default annualplanRoutes;

View File

@ -1,8 +1,8 @@
import Layout from '@/layouts/index.vue';
import annualplanRouters from './annualplan';
// import annualplanRouters from './annualplan';
import statisticsRoutes from './statisticAnalysis';
import landsRoutes from './lands';
import dictRoutes from './dict';
// import dictRoutes from './dict';
export default [
{
@ -29,13 +29,13 @@ export default [
path: '/sub-government-affairs-service/add-grid',
component: () => import('@/views/resource/grid/AddGrid.vue'),
name: 'add',
meta: { title: '新增网格', icon: '' },
meta: { title: '网格管理', icon: '' },
},
{
path: '/sub-government-affairs-service/add--grid-member',
component: () => import('@/views/resource/grid/GridMember.vue'),
name: 'member',
meta: { title: '新增网格员', icon: '' },
meta: { title: '网格员管理', icon: '' },
},
// {
// path: '/sub-government-affairs-service/grid--management',
@ -48,7 +48,7 @@ export default [
// ...annualplanRouters,
...landsRoutes,
// ...statisticsRoutes,
...dictRoutes,
// ...dictRoutes,
],
},
];

View File

@ -1,659 +0,0 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
>
<template #menu-left>
<el-button type="primary" icon="Plus" @click="onAdd">新增</el-button>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #menu="{ row }">
<custom-table-operate :actions="state.options.actions" :data="{ row }" />
</template>
<template #search>
<div class="custom-search">
<div class="search-fields">
<div class="search-item">
<AreaCascader
v-model:region-code="areaQuery.regionCode"
v-model:grid-id="areaQuery.gridId"
placeholder="选择行政区域与网格"
:show-separator="true"
:width="500"
/>
</div>
<div class="search-item">
<span>种植作物</span>
<el-select v-model="state.query.cropsId" placeholder="种植作物" :clearable="true">
<el-option v-for="item in cropsOptions" :key="item.id" :label="item.cropsName" :value="item.id" />
</el-select>
</div>
<div class="search-item">
<span>计划编号</span>
<el-input v-model="state.query.id" placeholder="计划编号" :clearable="true" />
</div>
<div class="search-item">
<span>计划名称</span>
<el-input v-model="state.query.planName" placeholder="计划名称" :clearable="true" />
</div>
</div>
<div class="search-buttons">
<el-button type="primary" @click="searchChange"> 查询 </el-button>
<el-button @click="resetSearch"> 重置 </el-button>
</div>
</div>
</template>
</avue-crud>
<el-dialog v-model="detailDialogVisible" title="年度种植计划详情页" width="800" class="detail-dialog">
<el-tabs v-model="activeTab">
<el-tab-pane label="年度种植计划" name="basic">
<h3>{{ currentDetailRow.planName }}</h3>
<div>所属行政区域{{ currentDetailRow.regionName }}</div>
<div>所属网格{{ currentDetailRow.gridName }}</div>
<div>计划编号{{ currentDetailRow.id }}</div>
<div>计划名称{{ currentDetailRow.planName }}</div>
<div>种植作物{{ currentDetailRow.cropsName }}</div>
<div>种植面积{{ currentDetailRow.planParameters?.plantingArea }}</div>
<div>种植月份{{ currentDetailRow.planParameters?.plantingMonths }}</div>
<div>
生长周期{{ currentDetailRow.planParameters?.growthCycle }}
{{ getGrowthCycleUnitName(currentDetailRow.planParameters?.growthCycleUnit) }}
</div>
</el-tab-pane>
<el-tab-pane label="实际种植情况" name="planting">
<h3>{{ currentDetailRow.planName }}</h3>
<div>所属行政区域{{ currentDetailRow.regionName }}</div>
<div>所属网格{{ currentDetailRow.gridName }}</div>
<div>计划编号{{ currentDetailRow.id }}</div>
<div>计划名称{{ currentDetailRow.planName }}</div>
<div>种植作物{{ currentDetailRow.cropsName }}</div>
<div>种植面积{{ currentDetailRow.actualParameters?.plantingArea }}</div>
<div>种植月份{{ currentDetailRow.actualParameters?.plantingMonths }}</div>
<div>
生长周期{{ currentDetailRow.actualParameters?.growthCycle }}
{{ getGrowthCycleUnitName(currentDetailRow.actualParameters?.growthCycleUnit) }}
</div>
<div>当前进度{{ currentDetailRow.currentProgress }}%</div>
</el-tab-pane>
</el-tabs>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
<!-- 新增重新制定计划和填写实际种植信息操作的共用弹窗 -->
<el-dialog v-model="commonDialogVisible" :title="dialogTitle">
<template #default>
<el-form ref="planForm" :model="formData" :rules="formRules" label-width="120px" class="common-dialog">
<el-form-item label="" label-width="0px">
<AreaCascader v-model:value="areaFormData" split-rows label="所属行政区域-网格" :width="500" />
</el-form-item>
<!-- <AreaCascader v-model:value="areaFormData" split-rows label="所属行政区域-网格" :width="500" /> -->
<el-form-item label="计划名称">
<el-input v-model="formData.planName" style="width: 380px" />
</el-form-item>
<el-form-item label="种植作物" prop="cropsName">
<el-select v-model="formData.cropsId" placeholder="种植作物" style="width: 380px" :clearable="true">
<el-option v-for="item in cropsOptions" :key="item.id" :label="item.cropsName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="种植面积" prop="plantingArea">
<el-input v-model="formData.plantingArea" style="width: 380px" />
</el-form-item>
<el-form-item label="种植月份" prop="plantingMonths">
<el-select v-model="formData.plantingMonths" placeholder="请选择月份" style="width: 380px" :clearable="true">
<el-option v-for="item in monthsOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="生长周期" prop="growthCycle">
<el-input-number v-model="formData.growthCycle" :min="1" style="width: 300px" />
<el-select v-model="formData.growthCycleUnit" disabled style="width: 68px; margin: 0; padding: 0">
<el-option label="天" value="1" />
<el-option label="周" value="2" />
<el-option label="月" value="3" />
<el-option label="年" value="4" />
</el-select>
</el-form-item>
</el-form>
</template>
<template #footer>
<span class="dialog-footer">
<el-button @click="commonDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, computed, onMounted, watch } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { getAnnualList, examineAnnual, delAnnual, exportAnnua } from '@/apis/land.js';
import { pageCropsList } from '@/apis/landResourceManagement/cropsManagement/index.js';
import { saveAnnual, editAnnual, saveActualProgress, getAnnualDetail } from '@/apis/landResourceManagement/plantingPlan/index.js';
const app = useApp();
const crudRef = ref(null);
const userStore = useUserStore();
// AreaCascader
const areaFormData = ref({
regionCode: '',
gridName: '',
gridId: '',
});
watch(
areaFormData,
(newVal) => {
formData.value.regionCode = newVal.regionCode;
formData.value.gridName = newVal.gridName;
formData.value.gridId = newVal.gridId;
},
{ deep: true }
);
const formRules = reactive({
// planName: [{ required: true, message: '', trigger: 'blur' }],
// cropsId: [{ required: true, message: '', trigger: 'change' }],
// plantingArea: [
// { required: true, message: '', trigger: 'blur' },
// { type: 'number', message: '', trigger: 'blur' },
// ],
// plantingMonths: [{ required: true, message: '', trigger: 'change' }],
// growthCycle: [
// { required: true, message: '', trigger: 'blur' },
// { type: 'number', message: '', trigger: 'blur' },
// ],
});
//
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) {
app.$message.error('获取种植作物列表失败');
}
};
onMounted(() => {
fetchCropsList();
});
//
const getGrowthCycleUnitName = (unit) => {
const unitMap = {
1: '天',
2: '周',
3: '月',
4: '年',
};
return unitMap[unit] || '';
};
//
const fetchDetailData = async (id) => {
try {
const res = await getAnnualDetail(id);
if (res.code === 200) {
currentDetailRow.value = res.data;
} else {
app.$message.error(res.msg || '获取详情数据失败');
}
} catch (error) {
console.error('获取详情数据出错:', error);
app.$message.error('获取详情数据失败,请稍后重试');
}
};
const openDetailDialog = async (row) => {
detailDialogVisible.value = true;
await fetchDetailData(row.id);
};
//
const detailDialogVisible = ref(false);
//
const activeTab = ref('basic');
const currentDetailRow = ref({});
//
const commonDialogVisible = ref(false);
//
const currentAction = ref('');
//
const dialogTitle = computed(() => {
if (currentAction.value === 'reCreate') {
return '重新制定计划';
} else if (currentAction.value === 'fillActual') {
return '填写实际种植信息';
} else if (currentAction.value === 'add') {
return '新增年度种植计划';
}
return '';
});
const monthsOptions = ref([
{ value: 1, label: '1月' },
{ value: 2, label: '2月' },
{ value: 3, label: '3月' },
{ value: 4, label: '4月' },
{ value: 5, label: '5月' },
{ value: 6, label: '6月' },
{ value: 7, label: '7月' },
{ value: 8, label: '8月' },
{ value: 9, label: '9月' },
{ value: 10, label: '10月' },
{ value: 11, label: '11月' },
{ value: 12, label: '12月' },
]);
//
const planForm = ref(null);
//
const isGridMember = computed(() => {
const userRoles = userStore.getUserInfo().roles;
console.log('当前用户角色:', userRoles);
return userRoles.some((role) => role.roleKey === 'gridMember');
// return true;
});
// 使 ref 使 v-model:value
const areaQuery = ref({
regionCode: '',
gridName: '',
gridId: '',
});
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
id: '',
planName: '',
cropsName: '',
cropsId: '',
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '',
addBtn: false,
searchBtn: false,
emptyBtn: false,
fit: true,
column: [
{ label: '计划编号', prop: 'id', minWidth: 100 },
{ label: '计划名称', prop: 'planName' },
{ label: '种植作物', prop: 'cropsName' },
{ label: '种植面积', prop: 'plantingArea', formatter: (row, column, cellValue) => `${cellValue}` },
{ label: '种植月份', prop: 'plantingMonths', formatter: (row, column, cellValue) => `${cellValue}` },
{
label: '生长周期',
prop: 'growthCycle',
formatter: (row, column, cellValue) => {
const unitMap = { 1: '天', 2: '周', 3: '月', 4: '年' };
const unit = unitMap[row.growthCycleUnit] || '';
return `${cellValue} ${unit}`;
},
},
{ label: '所属行政区域', prop: 'regionName' },
{ label: '所属网格', prop: 'gridName' },
{ label: '当前进度', prop: 'currentProgress' },
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => {
openDetailDialog(row);
},
},
{
name: '重新制定计划',
icon: 'edit',
event: ({ row }) => {
currentAction.value = 'reCreate';
formData.value = { ...row };
if (isGridMember.value) {
formData.value.regionName = row.regionName;
formData.value.gridName = row.gridName;
}
formData.value.growthCycleUnit = row.growthCycleUnit || '1';
commonDialogVisible.value = true;
},
},
{
name: '填写实际种植信息',
icon: 'edit',
event: ({ row }) => {
currentAction.value = 'fillActual';
formData.value = { ...row };
if (isGridMember.value) {
formData.value.regionName = row.regionName;
formData.value.gridName = row.gridName;
}
formData.value.growthCycleUnit = row.growthCycleUnit || '1';
commonDialogVisible.value = true;
},
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
const formData = ref({
year: '',
regionCode: '',
gridId: '',
regionName: '',
gridName: '',
planName: '', //
cropsId: null,
cropsName: '',
plantingArea: null,
plantingMonths: '',
growthCycle: '',
growthCycleUnit: '1', //
note: '',
});
//
const submitForm = async () => {
try {
//
await planForm.value.validate();
//
console.log('表单数据:', formData.value);
if (currentAction.value === 'reCreate') {
//
await editAnnual(formData.value);
app.$message.success('重新制定计划成功');
} else if (currentAction.value === 'fillActual') {
//
await saveActualProgress(formData.value);
app.$message.success('填写实际种植信息成功');
} else if (currentAction.value === 'add') {
//
await saveAnnual(formData.value);
app.$message.success('新增年度种植计划成功');
}
commonDialogVisible.value = false;
loadData();
} catch (error) {
console.error('表单提交失败:', error);
if (error.errors) {
//
app.$message.error('请填写完整且正确的表单信息');
} else {
// API
app.$message.error('操作失败,请稍后重试');
}
}
};
const onAdd = () => {
currentAction.value = 'add';
formData.value = {
regionName: '',
gridName: '',
planName: '',
cropsName: '',
plantingArea: '',
plantingMonths: '',
growthCycle: '',
growthCycleUnit: '1',
};
commonDialogVisible.value = true;
};
//
const loadData = () => {
state.loading = true;
getAnnualList(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = () => {
state.query = {
...state.query,
...areaQuery.value,
current: 1,
};
console.log('搜索参数', state.query);
loadData();
};
//
const resetSearch = () => {
state.query = {
current: 1,
size: 10,
id: '',
planName: '',
cropsName: '',
cropsId: '',
};
areaQuery.value.regionCode = '';
areaQuery.value.gridName = '';
areaQuery.value.gridId = '';
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const rowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`删除后信息将不可查看,确认要删除吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
console.info('删除');
delAnnual({ id: row.id })
.then((res) => {
if (res.code === 200) {
app.$message.success('删除成功!');
// done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
});
})
.catch(() => {});
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
}
state.loading = true;
const fileName = '年度计划明细表';
exportAnnua(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})
.catch((err) => {
app.$message.error('导出失败!');
})
.finally(() => {
state.loading = false;
});
};
</script>
<style scoped lang="scss">
.custom-page {
/* 详情弹窗样式 */
/* 表单弹窗样式 */
.el-dialog {
.el-form {
padding: 0 20px;
width: 100%;
// background-color: aqua;
display: flex;
flex-wrap: wrap;
justify-content: center;
&-item {
margin-bottom: 18px;
width: 60%;
// background-color: #606266;
&__label {
font-weight: 500;
color: #606266;
}
/* 输入框和选择器统一宽度 */
.el-input,
.el-select,
.el-cascader {
width: 100%;
max-width: 400px;
}
/* 数字输入框和单位选择器组合 */
.el-input-number {
margin-right: 10px;
& + .el-select {
width: 100px;
}
}
}
}
}
/* 按钮样式微调 */
.el-button {
padding: 8px 16px;
border-radius: 4px;
& + .el-button {
margin-left: 12px;
}
}
/* 搜索区域样式 */
.custom-search {
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 16px;
border-radius: 8px;
// background-color: #f5f7fa;
.search-fields {
display: flex;
flex-wrap: wrap;
gap: 16px;
width: 100%;
margin-bottom: 16px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
.el-input,
.el-select,
.el-cascader {
width: 200px;
}
span {
font-size: 14px;
color: #606266;
align-self: center;
}
}
.search-buttons {
display: flex;
align-self: center;
gap: 12px;
margin-left: 8px;
}
}
}
</style>

View File

@ -1,37 +0,0 @@
<template>
<div class="grid-records"></div>
</template>
<script setup>
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
const records = ref([
{
createTime: '2025-05-26 09:58:29',
createUser: '1',
updateTime: '2025-05-26T10:02:02.000+08:00',
updateUser: '1',
tenantId: 0,
id: '1926820200675004417', //
year: null,
regionCode: null, //
cropsId: null, // id
gridId: null, // id
planName: '大兴乡2025水稻种植计划', //
plantingArea: 120, //
plantingAreaActual: 335, //
plantingMonths: '1', //
growthCycle: '20', //
growthCycleUnit: '1', // 1 2 3 4
note: '暂无备注', //
deleteFlag: '0', // 0 1
cropsName: '', //
gridName: null, //
regionName: null, //
currentProgress: 279.17, //
actualFlag: '1', // 0: 1
},
]);
</script>
<style scoped></style>

View File

@ -1,166 +0,0 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
>
<template #menu-left>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { compact } from 'lodash';
import { GetEntityList, AddEntity, UpdateEntity, DeleteEntity, ExportEntity } from '@/apis/resource/grid';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const handleLandChange = async (value, form, done) => {
if (!value) return; //
state.form.address = value.item?.address || '';
};
const JobTypeChange = async (value, form, done) => {
if (!value) return;
state.form.productName = value.item?.productName || '';
};
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '',
addBtn: false,
column: [
{ label: '网格名称', prop: 'gridManager' },
{ label: '网格管理员', prop: 'gridManager' },
{ label: '联系方式', prop: 'gridManager' },
{ label: '网格名称', prop: 'gridManager' },
{ label: '网格名称', prop: 'gridManager' },
{ label: '种植总面积(亩)', prop: 'gridManager' },
{ label: '已种植总面积(亩)', prop: 'gridManager' },
{ label: '进度(%)', prop: 'gridManager' },
],
actions: [],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
//
const loadData = () => {
// state.loading = true;
// GetEntityList(state.query)
// .then((res) => {
// if (res.code === 200) {
// const { current, size, total, records } = res.data;
// state.data = records;
// state.pageData = {
// currentPage: current || 1,
// pageSize: size || 10,
// total: total,
// };
// }
// })
// .catch((err) => {
// app.$message.error(err.msg);
// state.data = [];
// })
// .finally(() => {
// state.loading = false;
// });
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
}
state.loading = true;
const fileName = '网格种植记录明细表';
// ExportEntity(state.query)
// .then((res) => {
// if (res.status === 200) {
// downloadFile(res.data, `${fileName}.xlsx`, 'blob');
// app.$message.success('');
// }
// })
// .catch((err) => {
// app.$message.error('');
// })
// .finally(() => {
// state.loading = false;
// });
};
</script>

View File

@ -99,7 +99,13 @@
<template #default>
<el-form ref="planForm" :model="formData" :rules="formRules" label-width="120px" class="common-dialog">
<el-form-item label="" label-width="0px">
<AreaCascader v-model:value="areaFormData" split-rows label="所属行政区域-网格" :width="500" />
<AreaCascader
v-model:region-code="formData.regionCode"
v-model:grid-id="formData.gridId"
split-rows
label="所属行政区域-网格"
:width="500"
/>
</el-form-item>
<!-- <AreaCascader v-model:value="areaFormData" split-rows label="所属行政区域-网格" :width="500" /> -->
<el-form-item label="计划名称">
@ -215,6 +221,7 @@ const fetchDetailData = async (id) => {
const res = await getAnnualDetail(id);
if (res.code === 200) {
currentDetailRow.value = res.data;
formData.value = res.data;
} else {
app.$message.error(res.msg || '获取详情数据失败');
}
@ -332,8 +339,8 @@ const state = reactive({
currentAction.value = 'reCreate';
formData.value = { ...row };
if (isGridMember.value) {
formData.value.regionName = row.regionName;
formData.value.gridName = row.gridName;
formData.value.regionCode = row.regionCode;
formData.value.gridId = row.gridId;
}
formData.value.growthCycleUnit = row.growthCycleUnit || '1';
commonDialogVisible.value = true;
@ -349,6 +356,7 @@ const state = reactive({
formData.value.regionName = row.regionName;
formData.value.gridName = row.gridName;
}
formData.value.plantingArea = row.plantingAreaActual;
formData.value.growthCycleUnit = row.growthCycleUnit || '1';
commonDialogVisible.value = true;
},
@ -399,6 +407,7 @@ const submitForm = async () => {
app.$message.success('重新制定计划成功');
} else if (currentAction.value === 'fillActual') {
//
formData.value.planId = formData.value.id;
await saveActualProgress(formData.value);
app.$message.success('填写实际种植信息成功');
} else if (currentAction.value === 'add') {

View File

@ -2,361 +2,607 @@
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
v-model:page="pageData"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@refresh-change="handleRefresh"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
<template #menu-left>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="success" icon="download" @click="handleExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
<template #isIllegal="{ row }">
<el-tag v-if="row.isIllegal == '1'" type="danger"></el-tag>
<el-tag v-if="row.isIllegal == '0'" type="success"></el-tag>
</template>
<template #inspectionStatus="{ row }">
<el-tag v-if="row.inspectionStatus == '1'" type="success">已结束</el-tag>
<el-tag v-if="row.inspectionStatus == '0'">进行中</el-tag>
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template>
</avue-crud>
<el-dialog v-model="infoVisible" title="巡查任务" width="800" center>
<el-form ref="infoRef" :model="infoData" :rules="infoRules">
<el-descriptions :title="infoData.planName || ''" :column="2">
<el-descriptions-item label="任务编号">{{ infoData.id || '--' }}</el-descriptions-item>
<el-descriptions-item label="任务名称">{{ infoData.planName || '--' }}</el-descriptions-item>
<el-descriptions-item label="任务成员">{{ infoData.plantingArea || '--' }}</el-descriptions-item>
<el-descriptions-item label="巡查类型">{{ infoData.plantingMonths || '--' }}</el-descriptions-item>
<el-descriptions-item label="注意事项">{{ infoData.growthCycle || '--' }}</el-descriptions-item>
<el-descriptions-item label="巡查对象">{{ infoData.note || '--' }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :title="'巡查信息登记'"> </el-descriptions>
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<p class="form-group">任务信息</p>
<el-form :model="taskForm" :disabled="isReadonlyInfo" label-width="100px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否违法:" prop="num">
<el-radio-group v-model="radio1">
<el-radio value="1" size="large"></el-radio>
<el-radio value="2" size="large"></el-radio>
</el-radio-group>
<el-form-item label="任务编号" prop="taskCode">
<el-input v-model="taskForm.taskCode" placeholder="请输入任务编号" />
</el-form-item>
<el-form-item label="任务成员" prop="taskUserIds">
<!-- /land-resource/grid-member/current-user -->
<url-select
v-model="taskForm.taskUserIds"
url="/land-resource/grid-member/current-user"
placeholder="请选择任务成员"
multiple="true"
label-key="memberName"
value-key="id"
:response-parser="(res) => res.data.memberList"
/>
</el-form-item>
<el-form-item label="注意事项" prop="notes">
<el-input v-model="taskForm.notes" type="textarea" :autosize="{ minRows: 2, maxRows: 6 }" placeholder="请输入注意事项" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="巡查情况" prop="gridName">
<el-input
v-model="infoData.mark"
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
laceholder="请输入巡查情况"
style="width: 240px"
></el-input>
<el-form-item label="任务名称" prop="taskName">
<el-input v-model="taskForm.taskName" placeholder="请输入任务名称" />
</el-form-item>
<el-form-item label="巡查类型" prop="inspectionTypeCode">
<url-select
v-model="taskForm.inspectionTypeCode"
url="/system/dict/data/type/land_res_patrol_type"
placeholder="请选择巡查类型"
label-key="dictLabel"
value-key="dictValue"
:response-parser="(res) => res.data"
/>
</el-form-item>
<el-form-item label="巡查对象" prop="inspectionTarget">
<el-input v-model="taskForm.inspectionTarget" placeholder="请输入巡查对象" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 巡查信息登记只有进行过结果登记的才显示 -->
<template v-if="dialogTitle !== '查看' && dialogTitle !== '详情' && dialogTitle !== '新增' && dialogTitle !== '编辑'">
<p class="form-group">巡查信息登记</p>
<!-- 循环渲染多个表单 -->
<div v-for="(form, index) in illegalForms" :key="index" class="form-container">
<div class="form-header">
<span>记录 #{{ index + 1 }}</span>
<el-button v-if="!isReadonlyRegist" type="danger" icon="el-icon-delete" circle size="mini" @click="handleRemoveForm(index)" />
</div>
<el-form :model="form" :disabled="isReadonlyRegist" label-width="100px" class="form-item">
<el-row :gutter="20">
<el-col :span="12">
<!-- 选择地块 -->
<el-form-item label="选择地块" prop="landId">
<url-select
v-model="form.landId"
url="/land-resource/landManage/page"
placeholder="请选择地块"
label-key="landName"
value-key="id"
:params="{ current: 1, size: 999 }"
/>
</el-form-item>
<el-form-item label="是否违法" prop="illegalFlag">
<el-radio-group v-model="form.illegalFlag">
<el-radio value="1"></el-radio>
<el-radio value="0"></el-radio>
</el-radio-group>
</el-form-item>
<!-- 违法时间 -->
<el-form-item v-if="form.illegalFlag === '1'" label="违法时间" prop="illegalDate">
<el-date-picker
v-model="form.illegalDate"
type="datetime"
placeholder="选择违法时间"
format="YYYY年MM月DD日"
value-format="YYYY-MM-DD"
:disabled="isReadonlyRegist || form.illegalFlag === 0"
/>
</el-form-item>
<!-- 违法图片 -->
<el-form-item v-if="form.illegalFlag === '1'" label="违法图片" prop="illegalImages">
<file-uploader v-model="form.illegalImages" :limit="1" :readonly="isReadonlyRegist || form.illegalFlag === 0" />
</el-form-item>
</el-col>
<el-col v-if="form.illegalFlag === '1'" :span="12">
<el-form-item label="违法类型" prop="illegalType">
<url-select
v-model="form.illegalTypeCode"
url="/system/dict/data/type/land_inspection_illegal_type"
placeholder="请选择违法类型"
label-key="dictLabel"
value-key="dictValue"
:response-parser="(res) => res.data"
:disabled="isReadonlyRegist || form.illegalFlag === 0"
/>
</el-form-item>
<el-form-item v-if="form.illegalFlag === '1'" label="违法行为描述" prop="desc">
<el-input
v-model="form.desc"
:autosize="{ minRows: 2, maxRows: 6 }"
type="textarea"
placeholder="请输入违法行为描述"
:disabled="isReadonlyRegist || form.illegalFlag === 0"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div v-if="!isReadonlyRegist" class="form-footer">
<el-button type="primary" @click="handleAddForm"> 添加记录 </el-button>
</div>
</template>
<template #footer>
<div class="dialog-footer">
<el-button @click="infoCancel">取消</el-button>
<el-button type="primary" @click="subMitInfo(infoRef)"> 确认 </el-button>
<!-- <el-button @click="infoCancel">取消</el-button> -->
<el-button type="primary" @click="handleSubmit()"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, h } from 'vue';
import { useApp } from '@/hooks';
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { ElMessage } from 'element-plus';
import { useActionPermissions } from '@/hooks/useActionPermissions';
import { useUserStore } from '@/store/modules/user';
import { getlandInspection, savelandInspection, enrolllandInspection, exportlandInspection, getAddrCropByLand } from '@/apis/land';
import { progressProps } from 'element-plus';
import {
createLandInspection,
deleteLandInspection,
updateLandInspection,
updateLandInspectionStatus,
getLandInspectionDetail,
fetchLandInspectionList,
exportLandInspection,
createInspectionResult,
createInspectionResultBatch,
deleteInspectionResult,
getInspectionResultDetail,
} from '@/apis/landResourceManagement/landInspection/index';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const dialogTitle = ref('新增');
const visible = ref(false);
const isReadonlyInfo = ref(false);
const isReadonlyRegist = ref(false);
const UserStore = useUserStore();
const crudRef = ref(null);
const handleLandChange = async (value, form, done) => {
if (!value || !value.item || !value.item.id) return; //
let val = {};
getAddrCropByLand(value.item?.id || '')
.then((res) => {
if (res.code === 200) {
val = res.data || {};
}
})
.catch((err) => {
val = {};
})
.finally(() => {});
state.form.crop = val?.crop || value.item?.crop;
state.form.address = val?.county + val?.town + val?.village || value.item?.address;
};
const user = UserStore.getUserInfo();
console.log('admin 属性:', user.admin);
const jobTypeOptions = reactive([
{ label: '施肥', value: '0' },
{ label: '杀虫', value: '1' },
{ label: '灌溉', value: '2' },
]);
const isDisabled = ref(false);
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
taskCode: '',
taskName: '',
taskMembers: '',
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '新增',
updateBtnText: '确定',
searchSpan: 6,
searchGutter: 80,
searchMenuPosition: 'center',
index: true,
column: [
{
label: '任务编号',
prop: 'taskCode',
},
{
label: '任务名称',
prop: 'taskName',
},
{
label: '任务成员',
prop: 'taskMembers',
},
{
label: '巡查类型',
prop: 'inspectionType',
},
{
label: '巡查对象',
prop: 'inspectionTarget',
},
{
label: '注意事项',
prop: 'notes',
},
{
label: '是否违法',
prop: 'isIllegal',
},
{
label: '状态',
prop: 'inspectionStatus',
},
],
actions: [
{
name: '登记结果',
icon: 'edit',
event: ({ row }) => doEnroll(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const infoVisible = ref(false);
const infoRef = ref();
const infoData = reactive({
num: '',
name: '',
member: [],
type: '',
mark: '',
target: '',
const searchForm = ref({
taskCode: '',
taskName: '',
userId: '',
inspectionTypeCode: '',
gridId: '',
});
const infoRules = reactive({
num: [{ required: true, message: '请选择是否违法', trigger: 'blur' }],
mark: [{ required: true, message: '请输入巡查情况', trigger: 'blur' }],
const taskForm = ref({
id: '', // ID
taskCode: '', //
taskName: '', //
taskUserIds: [], // ID
taskMembers: '', //
inspectionTypeCode: '', //
inspectionType: '', //
inspectionTarget: '', //
inspectionStatus: '', //
notes: '', //
inspectionResults: [], //
});
const illegalForm = ref({
id: '', // ID
inspectionId: '', // ID
inspectionTaskName: '', //
landId: '', // ID
landName: '', //
illegalFlag: 0, //
illegalDate: '', //
illegalTypeCode: '', //
illegalTypeName: '', //
desc: '', //
img: '', //
status: '', //
});
const illegalForms = ref([{ ...illegalForm.value }]);
const crudData = ref([]);
const loading = ref(false);
const crudOptions = reactive({
...CRUD_OPTIONS,
addBtn: false,
searchBtn: false,
emptyBtn: false,
column: [
{ label: '任务编号', prop: 'taskCode' },
{ label: '任务名称', prop: 'taskName' },
{ label: '任务成员', prop: 'taskMembers' },
{ label: '巡查类型', prop: 'inspectionType' },
{ label: '巡查对象', prop: 'inspectionTarget' },
{ label: '注意事项', prop: 'notes' },
{ label: '是否违法', prop: 'isIllegal' },
{ label: '状态', prop: 'inspectionStatusName' },
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row.id),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row.id),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
//
{
name: '结果登记',
icon: 'result',
event: ({ row }) => handleResult(row.id),
},
//
{
name: '收回任务',
icon: 'back',
event: ({ row }) => handleBack(row.id),
},
//
{
name: '发布任务',
icon: 'publish',
event: ({ row }) => handlePublish(row.id),
},
//
{
name: '重新发布',
icon: 're-publish',
event: ({ row }) => handleRePublish(row.id),
},
//
{
name: '查看登记结果',
icon: 'view-result',
event: ({ row }) => handleViewResult(row),
},
],
});
//
const loadData = () => {
state.loading = true;
getlandInspection(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
onMounted(() => {
getData();
});
const getData = async () => {
loading.value = true;
try {
const response = await fetchLandInspectionList({
current: pageData.value.currentPage,
size: pageData.value.pageSize,
...searchForm.value,
});
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const rowSave = (row, done, loading) => {
// console.info('', row);
savelandInspection(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const doEnroll = (row) => {
console.info('doEnroll', row);
// infoVisible.value = true;
isDisabled.value = row.inspectionStatus == '1';
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
console.info('登记结果');
let parmer = {
id: row.id || '',
isIllegal: row.isIllegal || '0', //0 1
inspectionSituation: row.inspectionSituation || '', //
};
enrolllandInspection(parmer)
.then((res) => {
if (res.code === 200) {
app.$message.success('登记结果成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
crudData.value = response.data.records;
pageData.value.total = response.data.total;
pageData.value.currentPage = response.data.current;
pageData.value.pageSize = response.data.size;
} catch (error) {
ElMessage.error('加载数据失败');
} finally {
loading.value = false;
}
};
const handleAdd = () => {
isReadonlyInfo.value = false;
// resetForm();
dialogTitle.value = '新增';
visible.value = true;
};
const handleEdit = async (id) => {
console.log('编辑行:', id);
isReadonlyInfo.value = false;
const response = await getLandInspectionDetail(id);
taskForm.value = response.data;
// inspectionUsers[]idtaskUserIds[]
if (taskForm.value.inspectionUsers && Array.isArray(taskForm.value.inspectionUsers)) {
taskForm.value.taskUserIds = taskForm.value.inspectionUsers.map((user) => user.userId);
} else {
taskForm.value.taskUserIds = [];
}
dialogTitle.value = '编辑';
visible.value = true;
};
const handleView = async (id) => {
console.log('查看行:', id);
const response = await getLandInspectionDetail(id);
taskForm.value = response.data;
const arr = response.data.inspectionResults;
illegalForms.value = arr ? arr : [];
illegalForms.value.forEach((item) => {
// img
if (item.img) {
//
item.illegalImages = [item.img];
} else {
//
item.illegalImages = [];
}
});
isReadonlyInfo.value = true;
if (taskForm.value.inspectionUsers && Array.isArray(taskForm.value.inspectionUsers)) {
taskForm.value.taskUserIds = taskForm.value.inspectionUsers.map((user) => user.userId);
} else {
taskForm.value.taskUserIds = [];
}
dialogTitle.value = '查看';
visible.value = true;
};
const handleDelete = async (id) => {
console.log('删除ID:', id);
const response = await deleteLandInspection(id);
if (response?.code === 200) {
ElMessage.success(response?.msg || '删除成功');
getData(); //
}
state.loading = true;
const fileName = '土地巡查明细表';
exportlandInspection(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})
.catch((err) => {
app.$message.error('导出失败!');
})
.finally(() => {
state.loading = false;
});
};
const subMitInfo = (formEl) => {
if (!formEl) return;
formEl.validate((valid) => {
if (valid) {
infoHide();
console.log('submit!');
} else {
console.log('error submit!');
}
const handleResult = async (id) => {
console.log('结果登记ID:', id);
await handleView(id);
isReadonlyRegist.value = false;
dialogTitle.value = '结果登记';
illegalForm.value.inspectionId = id;
nextTick(() => {
visible.value = true;
});
};
const handleBack = async (id) => {
console.log('收回任务ID:', id);
const response = await updateLandInspectionStatus({
id,
status: '02',
});
ElMessage.success(response?.msg || '发布成功');
await getData();
};
const handlePublish = async (id) => {
console.log('发布任务ID:', id);
// updateLandInspectionStatus : status -1 => 00
const response = await updateLandInspectionStatus({
id,
status: '00',
});
ElMessage.success(response?.msg || '发布成功');
await getData();
};
const handleRePublish = async (id) => {
console.log('重新发布ID:', id);
const response = await updateLandInspectionStatus({
id,
status: '00',
});
ElMessage.success(response?.msg || '发布成功');
await getData();
};
const handleViewResult = async (row) => {
console.log('查看登记结果ID:', row.id);
await handleView(row.id);
isReadonlyRegist.value = true;
// const response = await getInspectionResultDetail(row.id);
// illegalForm.value = response.data;
dialogTitle.value = '查看登记结果';
nextTick(() => {
visible.value = true;
});
};
const infoCancel = () => {
infoHide();
//
const handleRemoveForm = async (index) => {
if (illegalForms.value[index].id) {
await deleteInspectionResult(illegalForms.value[index].id);
} else {
illegalForms.value.splice(index, 1);
}
};
const infoHide = () => {
infoRef.value && infoRef.value.resetFields();
infoVisible.value = false;
const handleSubmit = async () => {
try {
console.log('表单数据:', taskForm.value);
console.log('表单数据:', illegalForm.value);
// 1. refName
loading.value = true;
let response;
// 2.
if (dialogTitle.value === '新增') {
taskForm.value.id = '';
response = await createLandInspection(taskForm.value);
// 3. 200
ElMessage.success(response?.msg || '新增成功');
visible.value = false;
await getData();
} else if (dialogTitle.value === '编辑') {
response = await updateLandInspection(taskForm.value);
// 3. 200
ElMessage.success(response?.msg || '编辑成功');
visible.value = false;
await getData();
} else if (dialogTitle.value === '结果登记') {
// 1. id img
const payload = illegalForms.value
.filter((item) => !item.id)
.map((item) => {
//
const firstImage = Array.isArray(item.illegalImages) && item.illegalImages.length ? item.illegalImages[0] : '';
return {
...item,
img: firstImage,
};
});
// 2.
if (payload.length === 0) {
ElMessage.warning('没有需要登记的新违法记录');
return;
}
// 3.
response = await createInspectionResultBatch(payload);
ElMessage.success(response?.msg || '结果登记成功');
// 4.
await getData();
}
//
} catch (e) {
ElMessage.error('保存失败');
} finally {
loading.value = false;
}
};
</script>
<style scoped>
/* 使用 ::v-deep 深度选择器来穿透 scoped 样式 */
::v-deep .el-table .el-table__row {
height: 50px; /* 调整行高,间接控制行间距 */
const handleExport = async () => {
try {
const response = await exportLandInspection(searchForm.value);
if (response?.code === 200) {
//
const link = document.createElement('a');
link.href = response.data; // data
link.download = 'land_inspection_export.xlsx'; //
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
ElMessage.success('导出成功');
} else {
ElMessage.error(response?.msg || '导出失败');
}
} catch (error) {
ElMessage.error('导出失败,请稍后重试');
}
};
function handleRefresh() {
console.log('刷新数据');
}
function handleAddForm() {
console.log('添加记录');
illegalForms.value.push({ ...illegalForm.value });
}
function handleCurrentChange(val) {
console.log('当前页:', val);
}
function handleSizeChange(val) {
console.log('每页显示条数:', val);
}
// ---------------------------------------------------------------------
//
// ---------------------------------------------------------------------
::v-deep .el-table .el-table__row td {
padding: 8px 0; /* 调整单元格内边距,进一步微调行间距 */
// user.admintrueoperatorinspector
const role = ref('operator');
if (user.admin) {
role.value = 'operator';
} else {
role.value = 'inspector';
}
//
const allActions = [
{ name: '查看', icon: 'view', key: 'view', event: ({ row }) => handleView(row.id) },
{ name: '编辑', icon: 'edit', key: 'edit', event: ({ row }) => handleEdit(row.id) },
{ name: '删除', icon: 'delete', key: 'delete', type: 'danger', event: ({ row }) => handleDelete(row.id) },
{ name: '结果登记', icon: 'result', key: 'result', event: ({ row }) => handleResult(row.id) },
{ name: '收回任务', icon: 'back', key: 'back', event: ({ row }) => handleBack(row.id) },
{ name: '发布任务', icon: 'publish', key: 'publish', event: ({ row }) => handlePublish(row.id) },
{ name: '重新发布', icon: 're-publish', key: 'rePublish', event: ({ row }) => handleRePublish(row.id) },
{ name: '查看登记结果', icon: 'view-result', key: 'viewResult', event: ({ row }) => handleViewResult(row) },
];
// ->
const permissionMap = {
admin: ['operator', 'inspector'], // operator inspector
operator: ['view', 'edit', 'delete', 'publish', 'back', 'rePublish', 'viewResult'],
inspector: ['result', 'viewResult'],
};
// ->
const statusMap = {
'-1': ['view', 'edit', 'delete', 'publish'],
'00': ['result', 'back', 'viewResult'],
'01': ['viewResult'],
'02': ['view', 'edit', 'delete', 'rePublish'],
};
function getActions(row) {
// 1. action.key
let raw = permissionMap[role.value] || [];
// 2. raw permissionMap key
const expanded = raw.reduce((acc, key) => {
if (permissionMap[key]) {
// key action.key
acc.push(...permissionMap[key]);
} else {
// key action.key
acc.push(key);
}
return acc;
}, []);
//
const roleAllowed = Array.from(new Set(expanded));
// 3. action.key
// row.inspectionStatus row.status
const statusKey = row.inspectionStatus ?? row.status;
const statusAllowed = statusMap[statusKey] || [];
// 4. key
const finalKeys = roleAllowed.filter((key) => statusAllowed.includes(key));
// 5. finalKeys allActions
return allActions.filter((a) => finalKeys.includes(a.key));
}
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 20px;
height: calc(100vh - 300px);
overflow-y: auto;
}
.form-container {
position: relative;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.form-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.form-footer {
margin-top: 20px;
text-align: center;
}
.form-group {
font-size: 16px;
font-weight: 500;
margin: 30px 0;
color: #333333;
}
</style>

View File

@ -0,0 +1,362 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
>
<template #menu-left>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
<template #isIllegal="{ row }">
<el-tag v-if="row.isIllegal == '1'" type="danger"></el-tag>
<el-tag v-if="row.isIllegal == '0'" type="success"></el-tag>
</template>
<template #inspectionStatus="{ row }">
<el-tag v-if="row.inspectionStatus == '1'" type="success">已结束</el-tag>
<el-tag v-if="row.inspectionStatus == '0'">进行中</el-tag>
</template>
</avue-crud>
<el-dialog v-model="infoVisible" title="巡查任务" width="800" center>
<el-form ref="infoRef" :model="infoData" :rules="infoRules">
<el-descriptions :title="infoData.planName || ''" :column="2">
<el-descriptions-item label="任务编号">{{ infoData.id || '--' }}</el-descriptions-item>
<el-descriptions-item label="任务名称">{{ infoData.planName || '--' }}</el-descriptions-item>
<el-descriptions-item label="任务成员">{{ infoData.plantingArea || '--' }}</el-descriptions-item>
<el-descriptions-item label="巡查类型">{{ infoData.plantingMonths || '--' }}</el-descriptions-item>
<el-descriptions-item label="注意事项">{{ infoData.growthCycle || '--' }}</el-descriptions-item>
<el-descriptions-item label="巡查对象">{{ infoData.note || '--' }}</el-descriptions-item>
</el-descriptions>
<el-descriptions :title="'巡查信息登记'"> </el-descriptions>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否违法:" prop="num">
<el-radio-group v-model="radio1">
<el-radio value="1" size="large"></el-radio>
<el-radio value="2" size="large"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="巡查情况" prop="gridName">
<el-input
v-model="infoData.mark"
:autosize="{ minRows: 2, maxRows: 4 }"
type="textarea"
laceholder="请输入巡查情况"
style="width: 240px"
></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="infoCancel">取消</el-button>
<el-button type="primary" @click="subMitInfo(infoRef)"> 确认 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, h } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { getlandInspection, savelandInspection, enrolllandInspection, exportlandInspection, getAddrCropByLand } from '@/apis/land';
import { progressProps } from 'element-plus';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const handleLandChange = async (value, form, done) => {
if (!value || !value.item || !value.item.id) return; //
let val = {};
getAddrCropByLand(value.item?.id || '')
.then((res) => {
if (res.code === 200) {
val = res.data || {};
}
})
.catch((err) => {
val = {};
})
.finally(() => {});
state.form.crop = val?.crop || value.item?.crop;
state.form.address = val?.county + val?.town + val?.village || value.item?.address;
};
const jobTypeOptions = reactive([
{ label: '施肥', value: '0' },
{ label: '杀虫', value: '1' },
{ label: '灌溉', value: '2' },
]);
const isDisabled = ref(false);
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
taskCode: '',
taskName: '',
taskMembers: '',
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '新增',
updateBtnText: '确定',
searchSpan: 6,
searchGutter: 80,
searchMenuPosition: 'center',
index: true,
column: [
{
label: '任务编号',
prop: 'taskCode',
},
{
label: '任务名称',
prop: 'taskName',
},
{
label: '任务成员',
prop: 'taskMembers',
},
{
label: '巡查类型',
prop: 'inspectionType',
},
{
label: '巡查对象',
prop: 'inspectionTarget',
},
{
label: '注意事项',
prop: 'notes',
},
{
label: '是否违法',
prop: 'isIllegal',
},
{
label: '状态',
prop: 'inspectionStatus',
},
],
actions: [
{
name: '登记结果',
icon: 'edit',
event: ({ row }) => doEnroll(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
const infoVisible = ref(false);
const infoRef = ref();
const infoData = reactive({
num: '',
name: '',
member: [],
type: '',
mark: '',
target: '',
});
const infoRules = reactive({
num: [{ required: true, message: '请选择是否违法', trigger: 'blur' }],
mark: [{ required: true, message: '请输入巡查情况', trigger: 'blur' }],
});
//
const loadData = () => {
state.loading = true;
getlandInspection(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const rowSave = (row, done, loading) => {
// console.info('', row);
savelandInspection(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const doEnroll = (row) => {
console.info('doEnroll', row);
// infoVisible.value = true;
isDisabled.value = row.inspectionStatus == '1';
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
console.info('登记结果');
let parmer = {
id: row.id || '',
isIllegal: row.isIllegal || '0', //0 1
inspectionSituation: row.inspectionSituation || '', //
};
enrolllandInspection(parmer)
.then((res) => {
if (res.code === 200) {
app.$message.success('登记结果成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
}
state.loading = true;
const fileName = '土地巡查明细表';
exportlandInspection(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})
.catch((err) => {
app.$message.error('导出失败!');
})
.finally(() => {
state.loading = false;
});
};
const subMitInfo = (formEl) => {
if (!formEl) return;
formEl.validate((valid) => {
if (valid) {
infoHide();
console.log('submit!');
} else {
console.log('error submit!');
}
});
};
const infoCancel = () => {
infoHide();
};
const infoHide = () => {
infoRef.value && infoRef.value.resetFields();
infoVisible.value = false;
};
</script>
<style scoped>
/* 使用 ::v-deep 深度选择器来穿透 scoped 样式 */
::v-deep .el-table .el-table__row {
height: 50px; /* 调整行高,间接控制行间距 */
}
::v-deep .el-table .el-table__row td {
padding: 8px 0; /* 调整单元格内边距,进一步微调行间距 */
}
</style>

View File

@ -81,14 +81,7 @@
</el-form>
</el-tab-pane>
<el-tab-pane label="土地产权信息" name="property">
<el-form
ref="propertyFormRef"
:model="formDataProperty"
:rules="propertyRules"
:disabled="disabledProperty"
label-width="120px"
class="form-container"
>
<el-form ref="propertyFormRef" :model="formDataProperty" :rules="propertyRules" label-width="120px" class="form-container">
<el-form-item label="地块名称">
<el-input v-model="formDataProperty.landName" disabled />
</el-form-item>
@ -102,16 +95,7 @@
<el-input v-model="formDataProperty.landCode" placeholder="请输入产权编号" />
</el-form-item>
<el-form-item label="产权证书" prop="propertyCertificateUrl">
<el-upload
:http-request="customUploadRequest"
:on-success="(res, file) => handleUploadSuccess(res, file, 'property')"
multiple
:show-file-list="true"
>
<template #trigger>
<el-button type="primary">点击上传</el-button>
</template>
</el-upload>
<FileUploader v-model="formDataProperty.propertyCertificateUrl" :limit="1" />
</el-form-item>
</el-form>
</el-tab-pane>
@ -120,7 +104,7 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleAddSubmit('basic')">提交</el-button>
<el-button v-if="activeTab === 'basic'" type="primary" @click="handleAddSubmit('basic')">提交</el-button>
<el-button v-if="activeTab === 'basic'" :disabled="disabledProperty" @click="activeTab = 'property'">下一步</el-button>
<el-button v-else @click="activeTab = 'basic'">上一步</el-button>
<el-button v-if="activeTab === 'property'" type="primary" @click="handleAddSubmit('property')"> 提交 </el-button>
@ -154,7 +138,7 @@
<el-input v-model="viewEditFormData.address" />
</el-form-item>
<el-form-item label="所属网格" prop="gridId">
<AreaCascader v-model:grid-id="viewEditFormData.gridId" :width="500" label="" />
<AreaCascader v-model="viewEditFormData.regionCode" v-model:grid-id="viewEditFormData.gridId" :width="500" label="" />
</el-form-item>
<el-form-item label="土壤类型" prop="soilTypeId">
<url-select
@ -167,20 +151,7 @@
/>
</el-form-item>
<el-form-item label="土地照片" prop="landUrl">
<template v-if="viewEditFormData.landUrl">
<img :src="`${ossUrl}${viewEditFormData.landUrl}`" alt="土地照片" class="preview-image" />
</template>
<el-upload
v-if="!isView"
:http-request="customUploadRequest"
:on-success="(res, file) => handleUploadSuccess(res, file, 'viewEditBasic')"
multiple
:show-file-list="true"
>
<template #trigger>
<el-button type="primary">点击上传</el-button>
</template>
</el-upload>
<FileUploader v-model="viewEditFormData.landUrl" :limit="1" />
</el-form-item>
<el-form-item label="土地范围" prop="scope">
<el-input v-model="viewEditFormData.scope" />
@ -197,20 +168,7 @@
<el-input v-model="viewEditFormData.landCode" />
</el-form-item>
<el-form-item label="产权证书" prop="propertyCertificateUrl">
<template v-if="viewEditFormData.propertyCertificateUrl">
<img :src="`${ossUrl}${viewEditFormData.propertyCertificateUrl}`" alt="产权证书" class="preview-image" />
</template>
<el-upload
v-if="!isView"
:http-request="customUploadRequest"
:on-success="(res, file) => handleUploadSuccess(res, file, 'viewEditProperty')"
multiple
:show-file-list="true"
>
<template #trigger>
<el-button type="primary">点击上传</el-button>
</template>
</el-upload>
<FileUploader v-model="viewEditFormData.propertyCertificateUrl" :limit="1" />
</el-form-item>
</el-form>
@ -395,7 +353,7 @@ const option = reactive({
column: [
{ label: '地块名称', prop: 'landName' },
{ label: '所属网格', prop: 'gridName' },
{ label: '面积', prop: 'area' },
{ label: '面积', prop: 'area', formatter: (row, column, cellValue) => `${cellValue}` },
{ label: '土地类型', prop: 'landTypeName' },
{ label: '所属行政区域', prop: 'fullRegionName' },
{ label: '具体位置', prop: 'address' },

View File

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

View File

@ -1,12 +1,10 @@
<template>
<el-form :model="localForm" label-width="120px" :disabled="readonly">
<h3>经营主体信息</h3>
<el-row :gutter="20">
<el-col :span="24">
<h3>经营主体信息</h3>
</el-col>
<el-col :span="12">
<el-form-item label="农企/合作社名称">
<el-input v-model="localForm.name" placeholder="请输入" />
<el-input v-model="localForm.businessName" placeholder="请输入" />
</el-form-item>
<el-form-item label="面积">
<el-input v-model="localForm.area" placeholder="请输入" />
@ -15,29 +13,52 @@
<el-input v-model="localForm.contactPerson" placeholder="请输入" />
</el-form-item>
<el-form-item label="农企/合作社照片">
<el-upload action="#" list-type="picture-card" :disabled="readonly" :auto-upload="false">
<el-upload
action="#"
:http-request="customUploadRequest"
:on-success="(res, file) => handleUploadSuccess(res, file, 'cooperativePhoto')"
:file-list="cooperativePhotoList"
list-type="picture-card"
:limit="1"
:on-exceed="handleExceed"
:on-remove="() => handleRemove('cooperativePhoto')"
>
<el-icon><Plus /></el-icon>
</el-upload>
<!-- <FileUploader v-model="localForm.cooperativePhoto" :limit="1" /> -->
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地址">
<el-cascader v-model="localForm.address" :options="regionOptions" placeholder="请选择" />
<area-select v-model="localForm.addressArr" :disabled="readonly" :label="null" />
</el-form-item>
<el-form-item label="经营产品">
<el-input v-model="localForm.product" placeholder="请输入" />
<el-input v-model="localForm.primaryProduct" placeholder="请输入" />
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="localForm.phone" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<h3>证件资料</h3>
<el-row :gutter="20">
<el-col :span="24">
<h3>证件资料</h3>
<el-form-item label="营业执照">
<el-upload action="#" list-type="picture-card" :disabled="readonly" :auto-upload="false">
<!-- <el-upload
action="#"
:http-request="customUploadRequest"
:on-success="(res, file) => handleUploadSuccess(res, file, 'businessLicence')"
:file-list="businessLicenceList"
list-type="picture-card"
:limit="1"
:on-exceed="handleExceed"
:on-remove="() => handleRemove('businessLicence')"
>
<el-icon><Plus /></el-icon>
</el-upload>
</el-upload> -->
<FileUploader v-model="localForm.businessLicence" :limit="1" />
</el-form-item>
</el-col>
</el-row>
@ -45,8 +66,12 @@
</template>
<script setup>
import { reactive, watch } from 'vue';
import { ref, reactive, watch } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import cloneDeep from 'lodash/cloneDeep';
import { CommonUpload } from '@/apis/index';
import { ElMessage } from 'element-plus';
import Attrs from './Attrs.vue';
const props = defineProps({
modelValue: {
@ -57,10 +82,14 @@ const props = defineProps({
readonly: Boolean,
});
const ossUrl = 'http://gov-cloud.oss-cn-chengdu.aliyuncs.com/';
const cooperativePhotoList = ref([]);
const businessLicenceList = ref([]);
const emit = defineEmits(['update:modelValue']);
// props
const localForm = reactive({ ...props.modelValue });
const localForm = ref({ ...props.modelValue });
//
watch(
@ -70,18 +99,87 @@ watch(
},
{ deep: true }
);
// watch(
// () => props.modelValue,
// (newVal) => {
// Object.assign(localForm, cloneDeep(newVal));
// },
// { deep: true }
// );
const regionOptions = [
{
label: '耿马县',
value: '耿马县',
children: [
{
label: '耿马镇',
value: '耿马镇',
children: [{ label: '新城村', value: '新城村' }],
},
],
},
];
const customUploadRequest = async (options) => {
const formData = new FormData();
formData.append('file', options.file);
try {
const response = await CommonUpload(formData);
options.onSuccess(response, options.file);
return response;
} catch (err) {
console.error('上传失败', err);
options.onError(err);
throw err;
}
};
const handleUploadSuccess = (res, file, fieldName) => {
localForm[fieldName] = res.data.url;
//
const previewItem = {
name: res.data.name,
url: ossUrl + res.data.url, // 访URL
status: 'success',
};
if (fieldName === 'cooperativePhoto') {
cooperativePhotoList.value = [previewItem];
} else {
businessLicenceList.value = [previewItem];
}
};
//
const handleRemove = (fieldName) => {
localForm[fieldName] = '';
if (fieldName === 'cooperativePhoto') {
cooperativePhotoList.value = [];
} else {
businessLicenceList.value = [];
}
};
//
const handleExceed = () => {
ElMessage.warning('只能上传一张图片,请先删除当前图片');
};
//
if (localForm.value.cooperativePhoto) {
cooperativePhotoList.value = [
{
name: '合作社照片',
url: ossUrl + localForm.value.cooperativePhoto,
status: 'success',
},
];
}
if (localForm.value.businessLicence) {
businessLicenceList.value = [
{
name: '营业执照',
url: ossUrl + localForm.value.businessLicence,
status: 'success',
},
];
}
</script>
<style scoped lang="scss">
h3 {
margin-top: 20px;
margin-bottom: 20px;
font-size: 16px;
font-weight: 500;
color: #333333;
width: 110px;
text-align: right;
}
</style>

View File

@ -1,71 +1,76 @@
<template>
<div class="tab-business">
<el-form :model="localForm" label-width="140px">
<el-form :model="localForm" label-width="0">
<el-row :gutter="24">
<!-- 负债表 -->
<el-col :span="24">
<el-form-item label="负债表">
<el-button type="text" @click="downloadTemplate('debt')"> 下载模板 </el-button>
<el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.debtFiles"
:before-upload="(file) => handleImport('debt', file)"
:disabled="readonly"
>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload>
</el-form-item>
<el-col :span="8">
<h3>负债表</h3>
<!-- <el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.debtFiles"
:before-upload="(file) => handleImport('debt', file)"
:disabled="readonly"
>
<el-icon><Plus /></el-icon>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload> -->
<div class="view-file"></div>
<div class="upload-btn">
<el-button type="primary" :disabled="readonly">导入表格</el-button>
<el-button type="success" @click="downloadTemplate('debt')"> 下载模板 </el-button>
</div>
</el-col>
<!-- 利润表 -->
<el-col :span="24">
<el-form-item label="利润表">
<el-button type="text" @click="downloadTemplate('profit')"> 下载模板 </el-button>
<el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.profitFiles"
:before-upload="(file) => handleImport('profit', file)"
:disabled="readonly"
>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload>
</el-form-item>
<el-col :span="8">
<h3>利润表</h3>
<!-- <el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.profitFiles"
:before-upload="(file) => handleImport('profit', file)"
:disabled="readonly"
>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload> -->
<div class="view-file"></div>
<div class="upload-btn">
<el-button type="primary" :disabled="readonly">导入表格</el-button>
<el-button type="success" @click="downloadTemplate('profit')"> 下载模板 </el-button>
</div>
</el-col>
<!-- 现金流量表 -->
<el-col :span="24">
<el-form-item label="现金流量表">
<el-button type="text" @click="downloadTemplate('cashflow')"> 下载模板 </el-button>
<el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.cashflowFiles"
:before-upload="(file) => handleImport('cashflow', file)"
:disabled="readonly"
>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload>
</el-form-item>
<el-col :span="8">
<h3>现金流量表</h3>
<!-- <el-upload
class="upload-btn"
action="#"
:auto-upload="false"
:file-list="localForm.cashflowFiles"
:before-upload="(file) => handleImport('cashflow', file)"
:disabled="readonly"
>
<el-button size="small" :disabled="readonly">导入表格</el-button>
</el-upload> -->
<div class="view-file"></div>
<div class="upload-btn">
<el-button type="primary" :disabled="readonly">导入表格</el-button>
<el-button type="success" @click="downloadTemplate('cashflow')"> 下载模板 </el-button>
</div>
</el-col>
</el-row>
</el-form>
<!-- 底部按钮 -->
<div class="business-footer" style="text-align: right; margin-top: 20px">
<el-button :disabled="readonly" @click="$emit('prev')">上一步</el-button>
<el-button v-if="!readonly" type="primary" @click="$emit('next', localForm)"> 下一步 </el-button>
<el-button v-if="readonly" type="warning" @click="$emit('edit')"> 修改 </el-button>
</div>
</div>
</template>
<script setup>
import { reactive, watch } from 'vue';
import { Plus } from '@element-plus/icons-vue';
import cloneDeep from 'lodash/cloneDeep';
const props = defineProps({
modelValue: {
@ -78,19 +83,10 @@ const props = defineProps({
},
});
const emit = defineEmits([
'update:modelValue',
'prev', //
'next', //
'edit', //
]);
const emit = defineEmits(['update:modelValue']);
//
const localForm = reactive({
debtFiles: props.modelValue.debtFiles || [],
profitFiles: props.modelValue.profitFiles || [],
cashflowFiles: props.modelValue.cashflowFiles || [],
});
const localForm = reactive(cloneDeep(props.modelValue));
// watch
watch(
@ -100,6 +96,13 @@ watch(
},
{ deep: true }
);
watch(
() => props.modelValue,
(newVal) => {
Object.assign(localForm, cloneDeep(newVal));
},
{ deep: true }
);
//
function downloadTemplate(type) {
@ -130,10 +133,31 @@ function handleImport(type, file) {
</script>
<style scoped>
h3 {
margin-top: 20px;
margin-bottom: 20px;
font-size: 16px;
font-weight: 500;
color: #606266;
width: 100%;
text-align: center;
}
.upload-btn {
margin-left: 16px;
margin: 20px 0;
display: flex;
justify-content: space-around;
}
.business-footer el-button + el-button {
margin-left: 8px;
}
.view-file {
height: 400px;
border: 1px solid #dcdfe6;
border-radius: 4px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
}
</style>

View File

@ -1,35 +1,33 @@
<template>
<el-form :model="localForm" label-width="160px" :disabled="readonly">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="信用评价">
<el-rate v-model="localForm.creditRating" allow-half show-score score-template="{value} 分" />
</el-form-item>
<div class="credit-evaluation">
<h3>信用评级</h3>
<el-form-item label="信用评价">
<el-rate v-model="localForm.creditEvaluation" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-form-item label="带动周边农户">
<el-rate v-model="localForm.farmersSupport" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-form-item label="带动周边农户">
<el-rate v-model="localForm.supportedFarmers" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-form-item label="社会效益">
<el-rate v-model="localForm.socialBenefit" allow-half show-score score-template="{value} 分" />
</el-form-item>
</el-col>
<el-form-item label="社会效益">
<el-rate v-model="localForm.socialImpact" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-col :span="12">
<el-form-item label="高新技术应用">
<el-rate v-model="localForm.techApplication" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-form-item label="高新技术应用">
<el-rate v-model="localForm.techApplication" allow-half show-score score-template="{value} 分" />
</el-form-item>
<el-form-item label="产品质量及服务保障">
<el-rate v-model="localForm.qualityService" allow-half show-score score-template="{value} 分" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="产品质量及服务保障">
<el-rate v-model="localForm.productQuality" allow-half show-score score-template="{value} 分" />
</el-form-item>
</div>
</el-form>
</template>
<script setup>
import { reactive, watch } from 'vue';
import cloneDeep from 'lodash/cloneDeep';
const props = defineProps({
modelValue: {
@ -42,13 +40,43 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']);
const localForm = reactive({ ...props.modelValue });
const localForm = reactive(cloneDeep(props.modelValue));
watch(
localForm,
(val) => {
emit('update:modelValue', { ...val });
(newVal) => {
emit('update:modelValue', { ...newVal });
},
{ deep: true }
);
watch(
() => props.modelValue,
(newVal) => {
Object.assign(localForm, cloneDeep(newVal));
},
{ deep: true }
);
</script>
<style scoped lang="scss">
.credit-evaluation {
width: 600px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 20px;
.el-rate {
font-size: 16px;
color: #f90;
}
}
h3 {
margin-top: 20px;
margin-bottom: 20px;
font-size: 16px;
font-weight: 500;
color: #333333;
width: 110px;
text-align: right;
}
</style>

View File

@ -0,0 +1,323 @@
<template>
<div>
<avue-crud ref="crudRef" v-model:page="pageData" :data="crudData" :option="crudOptions" :table-loading="loading">
<template #menu-left>
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
</template>
</avue-crud>
<el-dialog :key="dialogTitle" v-model="dialogVisible" :title="dialogTitle" width="80%">
<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 label="1"></el-radio>
<el-radio label="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, reactive, watch, onMounted, computed } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { ElMessageBox, ElMessage } from 'element-plus';
import { getMemberList, addMember, updateMember, deleteMembers } from '@/apis/businessEntity';
const dialogTitle = ref('新增');
const dialogVisible = ref(false);
const isReadonly = ref(false);
const loading = ref(false);
const crudRef = ref();
//
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const searchForm = ref({
entId: '',
userId: '',
});
const crudData = ref([]);
//
const defaultFormData = {
id: '',
entId: '',
name: '',
idType: '101',
idCard: '',
sex: '1',
age: '',
phone: '',
provinceCode: '', //
cityCode: '', //
countyCode: '', //
townCode: '', //
street: '', //
addressArr: [],
detailAddress: '',
area: '',
planCrop: '',
planCropName: '',
address: '',
createTime: '',
createUser: '',
updateTime: '',
updateUser: '',
};
// 使
const formData = ref({ ...defaultFormData });
const resetForm = () => {
formData.value = { ...defaultFormData };
};
const crudOptions = reactive({
...CRUD_OPTIONS,
addBtn: false,
searchBtn: false,
emptyBtn: false,
refreshBtn: false,
column: [
{ label: '姓名', prop: 'name' },
{
label: '证件类型',
prop: 'idType',
formatter: (row, column, cellValue) => {
return cellValue === '101' ? '身份证' : cellValue === '2' ? '护照' : cellValue === '3' ? '港澳身份证' : '';
},
},
{ label: '证件号码', prop: 'idCardEncrypt' },
{
label: '性别',
prop: 'sex',
formatter: (row, column, cellValue) => {
return cellValue === '1' ? '男' : cellValue === '0' ? '女' : '';
},
},
{ label: '年龄', prop: 'age' },
{ label: '联系方式', prop: 'phone' },
{ label: '居住地行政区划', prop: 'address' },
{ label: '种植作物', prop: 'planCrop' },
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row),
},
],
});
async function getData() {
loading.value = true;
try {
const params = {
...searchForm.value,
current: pageData.value.currentPage,
size: pageData.value.pageSize,
};
const response = await getMemberList(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;
}
}
onMounted(() => {
getData();
});
const handleSave = async () => {
try {
let response;
if (dialogTitle.value === '新增') {
// saveFarmerList
response = await addMember(formData.value);
if (response.code === 200) {
ElMessage.success('新增成功');
}
} else if (dialogTitle.value === '编辑') {
// editFarmer
response = await updateMember(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('编辑失败,请稍后重试');
}
}
};
watch(
() => formData.value.addressArr,
(newValue) => {
if (newValue.length <= 3) {
formData.value.provinceCode = '530000';
formData.value.cityCode = '530900';
formData.value.countyCode = newValue[0];
formData.value.townCode = newValue[1];
formData.value.street = newValue[2];
}
}
);
const handleAdd = () => {
isReadonly.value = false; //
resetForm();
dialogTitle.value = '新增';
dialogVisible.value = true;
};
function handleView(row) {
console.log('查看', row);
dialogTitle.value = '查看';
isReadonly.value = true; //
dialogVisible.value = true;
const addressArr = [row.countyCode, row.townCode, row.street].filter(Boolean);
formData.value = {
...row,
addressArr,
};
//
}
async function handleEdit(row) {
dialogTitle.value = '编辑';
isReadonly.value = false;
dialogVisible.value = true;
const addressArr = [row.countyCode, row.townCode, row.street].filter(Boolean);
formData.value = {
...row,
addressArr,
};
}
async function handleDelete(row) {
try {
await ElMessageBox.confirm('确定删除该条记录?', '删除提示');
console.log(`删除 ID=${row.id}`);
await deleteMembers(row.id); // ID
await getData();
ElMessage.success('已删除');
} catch (error) {
if (error !== 'cancel') {
ElMessage.error(`删除失败: ${error.message || '请稍后重试'}`);
}
}
}
</script>
<style scoped lang="scss"></style>

View File

@ -1,46 +1,58 @@
<template>
<el-form :model="localForm" label-width="150px" :disabled="readonly">
<h3>登记注册信息</h3>
<el-row :gutter="20">
<!-- 左列 -->
<el-col :span="12">
<el-form-item label="企业名称">
<el-input v-model="localForm.companyName" placeholder="请输入" />
<el-input v-model="localForm.businessName" placeholder="请输入" />
</el-form-item>
<el-form-item label="法定代表人">
<el-input v-model="localForm.legalPerson" placeholder="请输入" />
<el-input v-model="localForm.legalRep" placeholder="请输入" />
</el-form-item>
<el-form-item label="企业类型">
<el-select v-model="localForm.companyType" placeholder="请选择">
<el-select v-model="localForm.comType" placeholder="请选择">
<el-option label="农民专业合作社" value="农民专业合作社" />
<el-option label="农业公司" value="农业公司" />
<el-option label="个体工商户" value="个体工商户" />
</el-select>
</el-form-item>
<el-form-item label="登记机关">
<el-input v-model="localForm.registerOrg" placeholder="请输入" />
<el-input v-model="localForm.regAuthority" placeholder="请输入" />
</el-form-item>
<el-form-item label="核准日期">
<el-date-picker v-model="localForm.approvalDate" type="date" placeholder="请选择" format="YYYY年MM月DD日" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item label="经营范围">
<el-input v-model="localForm.businessScope" type="textarea" placeholder="请输入" :rows="4" />
<el-date-picker
v-model="localForm.approveDate"
style="width: 100%"
type="date"
placeholder="请选择"
format="YYYY年MM月DD日"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<!-- 右列 -->
<el-col :span="12">
<el-form-item label="统一社会信用代码">
<el-input v-model="localForm.creditCode" placeholder="请输入" />
<el-input v-model="localForm.uscc" placeholder="请输入" />
</el-form-item>
<el-form-item label="登记状态">
<el-select v-model="localForm.registerStatus" placeholder="请选择">
<el-select v-model="localForm.registrationStatus" placeholder="请选择">
<el-option label="存续" value="存续" />
<el-option label="注销" value="注销" />
<el-option label="吊销" value="吊销" />
</el-select>
</el-form-item>
<el-form-item label="成立日期">
<el-date-picker v-model="localForm.establishDate" type="date" placeholder="请选择" format="YYYY年MM月DD日" value-format="YYYY-MM-DD" />
<el-date-picker
v-model="localForm.estDate"
style="width: 100%"
type="date"
placeholder="请选择"
format="YYYY年MM月DD日"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="成员出资总额">
<el-input v-model="localForm.totalCapital" placeholder="请输入" />
@ -49,12 +61,18 @@
<el-input v-model="localForm.address" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="经营范围">
<el-input v-model="localForm.businessScope" type="businessScope" placeholder="请输入" :rows="4" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script setup>
import { reactive, watch } from 'vue';
import cloneDeep from 'lodash/cloneDeep';
const props = defineProps({
modelValue: {
@ -68,7 +86,7 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']);
// props
const localForm = reactive({ ...props.modelValue });
const localForm = reactive(cloneDeep(props.modelValue));
//
watch(
@ -78,4 +96,23 @@ watch(
},
{ deep: true }
);
watch(
() => props.modelValue,
(newVal) => {
Object.assign(localForm, cloneDeep(newVal));
},
{ deep: true }
);
</script>
<style scoped lang="scss">
h3 {
margin-top: 20px;
margin-bottom: 20px;
font-size: 16px;
font-weight: 500;
color: #333333;
width: 110px;
text-align: right;
}
</style>

View File

@ -1,35 +1,47 @@
<template>
<section class="custom-page">
<!-- 四个固定 Tabs -->
<el-tabs v-model="activeCrudTab" @tab-click="handleTabChange">
<el-tab-pane label="待提交" name="0" />
<el-tab-pane label="待审核" name="1" />
<el-tab-pane label="已通过" name="2" />
<el-tab-pane label="已驳回" name="3" />
</el-tabs>
<!-- 表格 -->
<avue-crud ref="crudRef" v-model:page="pageData" :data="crudData" :option="crudOptions" :table-loading="loading">
<template v-if="activeCrudTab === '0'" #menu-left>
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
</template>
</avue-crud>
<el-dialog v-model="visible" title="农企合作社详情" width="80%" :draggable="true">
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-tabs v-model="activeTab" class="tabs-wrapper">
<el-tab-pane label="1. 基础信息" name="basic">
<TabBasicInfo v-model="formData.basicInfo" :readonly="isReadonly" />
<el-tab-pane label="基础信息" name="basic">
<TabBasicInfo v-model="formData" :readonly="isReadonly" />
</el-tab-pane>
<el-tab-pane label="2. 登记注册信息" name="register">
<TabRegister v-model="formData.registerInfo" />
<el-tab-pane label="登记注册信息" name="register">
<TabRegister v-model="formData" />
</el-tab-pane>
<el-tab-pane label="3. 经营信息" name="business">
<TabBusinessInfo
v-model="formData.business"
:readonly="isReadonly"
@prev="activeTab = 'register'"
@next="handleNext"
@edit="isReadonly = false"
/>
<el-tab-pane label="经营信息" name="business">
<TabBusinessInfo v-model="formData" :readonly="isReadonly" />
</el-tab-pane>
<el-tab-pane label="4. 信用评级" name="credit">
<TabCreditEvaluation v-model="formData.credit" :readonly="isReadonly" />
<el-tab-pane label="信用评级" name="credit">
<TabCreditEvaluation v-model="formData" :readonly="isReadonly" />
</el-tab-pane>
<el-tab-pane v-if="dialogTitle === '查看'" label="职工/社员管理" name="employee">
<TabMember :readonly="isReadonly" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="visible = false">关闭</el-button>
<el-button v-if="!isReadonly" type="primary" @click="handleSubmit">保存</el-button>
<template v-if="dialogTitle !== '查看'" #footer>
<el-button v-if="!isFirstTab" type="primary" @click="handlePrev">上一步</el-button>
<el-button v-if="!isLastTab" type="primary" @click="handleNext">下一步</el-button>
<el-button v-if="!isFirstTab && !isLastTab" type="info" @click="handleSkip">跳过</el-button>
<el-button v-if="isLastTab" type="primary" @click="handleSubmit">保存</el-button>
<el-button v-if="isLastTab" type="info" @click="handleSkipSave">跳过并保存</el-button>
</template>
</el-dialog>
</section>
@ -43,65 +55,86 @@ import TabBasicInfo from './components/TabBasicInfo.vue';
import TabRegister from './components/TabRegister.vue';
import TabBusinessInfo from './components/TabBusinessInfo.vue';
import TabCreditEvaluation from './components/TabCreditEvaluation.vue';
import { getEnterList } from '@/apis/businessEntity';
import TabMember from './components/TabMember.vue';
import { getEnterList, getEnterById, addEnter, updateEnter, approvalEnter, deleteEnter } from '@/apis/businessEntity';
import { cloneDeep } from 'lodash';
//
const visible = ref(false);
// tab
const activeCrudTab = ref('0');
const activeTab = ref('basic');
const dialogTitle = ref('新增');
const isReadonly = ref(false);
//
const searchForm = ref({
businessName: '',
uscc: '',
productType: '',
primaryProduct: '',
});
//
const formData = ref({
//
basicInfo: {
name: '',
area: '',
contactPerson: '',
address: [],
product: '',
phone: '',
photoUrl: '',
businessLicenseUrl: '',
},
//
registerInfo: {
companyName: '',
legalPerson: '',
companyType: '',
registerOrg: '',
approvalDate: '',
creditCode: '',
registerStatus: '',
establishDate: '',
totalCapital: '',
address: '',
businessScope: '',
},
business: {
debtFiles: [],
profitFiles: [],
cashflowFiles: [],
},
credit: {
creditRating: 5,
farmersSupport: 4,
socialBenefit: 3,
techApplication: 2,
qualityService: 1,
},
id: '',
// basicInfo
businessName: '',
area: '',
contactPerson: '',
cooperativePhoto: '', //
addressArr: [],
primaryProduct: '',
phone: '',
businessLicence: '', //
provinceCode: '',
cityCode: '',
countyCode: '',
townCode: '',
villageCode: '',
// registerInfo
// businessName: '',
legalRep: '',
comType: '',
regAuthority: '',
approveDate: '',
uscc: '',
registrationStatus: '',
estDate: '',
totalCapital: '',
address: '',
businessScope: '',
//
debtFiles: [],
profitFiles: [],
cashflowFiles: [],
//
creditEvaluation: 5,
supportedFarmers: 4,
socialImpact: 3,
techApplication: 2,
productQuality: 1,
});
//
const isReadonly = ref(false);
//
const handleSubmit = async () => {
try {
loading.value = true;
// ID formData.value.id
await api.updateById(formData.value.id, formData.value);
ElMessage.success('保存成功');
visible.value = false;
let response;
if (dialogTitle.value === '新增') {
formData.value.id = '';
response = await addEnter(formData.value);
if (response.code === 200) {
ElMessage.success('新增成功');
visible.value = false;
getData(); //
}
} else if (dialogTitle.value === '编辑') {
response = await updateEnter(formData.value);
if (response.code === 200) {
ElMessage.success('编辑成功');
visible.value = false;
getData(); //
}
}
//
} catch (e) {
ElMessage.error('保存失败');
@ -116,68 +149,7 @@ const pageData = ref({
total: 0,
});
const crudData = ref([
{
cooperativeName: '华祥种植专业合作社',
location: '耿马县/耿马镇/新城村',
area: '300亩',
products: '小葱、大白菜',
contactPerson: '张强',
contactPhone: '13384041642',
employeeCount: '50人',
status: '待审核',
createTime: '2024年8月21日16:20:41',
updateTime: '2024年8月21日16:20:51',
},
{
cooperativeName: '春林农业合作社',
location: '耿马县/耿马镇/和平村',
area: '200亩',
products: '西红柿、黄瓜',
contactPerson: '李梅',
contactPhone: '13812345678',
employeeCount: '30人',
status: '审核通过',
createTime: '2024年7月15日09:30:12',
updateTime: '2024年7月15日10:02:00',
},
{
cooperativeName: '绿源蔬菜种植基地',
location: '耿马县/芒洪乡/新村',
area: '150亩',
products: '辣椒、豆角',
contactPerson: '王伟',
contactPhone: '13987654321',
employeeCount: '25人',
status: '待审核',
createTime: '2024年9月01日14:45:20',
updateTime: '2024年9月01日14:46:30',
},
{
cooperativeName: '金丰农作物专业合作社',
location: '耿马县/勐简乡/丰收村',
area: '400亩',
products: '玉米、大豆',
contactPerson: '赵敏',
contactPhone: '13699887766',
employeeCount: '60人',
status: '待审核',
createTime: '2024年6月10日08:10:50',
updateTime: '2024年6月10日08:12:20',
},
{
cooperativeName: '恒泰水果种植农场',
location: '耿马县/勐永镇/红星村',
area: '500亩',
products: '香蕉、芒果',
contactPerson: '周强',
contactPhone: '13711223344',
employeeCount: '80人',
status: '审核不通过',
createTime: '2024年5月25日11:20:15',
updateTime: '2024年5月25日11:22:00',
},
]);
const crudData = ref([]);
const loading = ref(false);
@ -187,13 +159,13 @@ const crudOptions = reactive({
searchBtn: false,
emptyBtn: false,
column: [
{ label: '农企/合作社名称', prop: 'cooperativeName' },
{ label: '地点', prop: 'location' },
{ label: '农企/合作社名称', prop: 'businessName' },
{ label: '地点', prop: 'regAddress' },
{ label: '面积', prop: 'area' },
{ label: '经营产品', prop: 'products' },
{ label: '经营产品', prop: 'businessScope' },
{ label: '联系人', prop: 'contactPerson' },
{ label: '联系电话', prop: 'contactPhone' },
{ label: '聘工人数', prop: 'employeeCount' },
{ label: '联系电话', prop: 'phone' },
{ label: '聘工人数', prop: 'villageCount' },
{ label: '状态', prop: 'status' },
{ label: '信息录入时间', prop: 'createTime' },
{ label: '信息更新时间', prop: 'updateTime' },
@ -215,16 +187,94 @@ const crudOptions = reactive({
icon: 'delete',
event: ({ row }) => handleDelete(row.id),
},
// showRejectReason
{
name: '驳回原因',
icon: 'warning',
event: ({ row }) => {
showRejectReason(row);
},
},
// handleSubmit
{
name: '提交审核',
icon: 'check',
event: ({ row }) => {
if (row.status === '待提交') {
handleSubmit();
} else {
ElMessage.warning('当前状态不允许提交审核');
}
},
},
// handleWithdraw
{
name: '撤销',
icon: 'undo',
event: ({ row }) => {
handleWithdraw(row);
},
},
// handleResubmit
{
name: '重新提交',
icon: 'resubmit',
event: (row) => {
handleResubmit(row);
},
},
// handleApprove
{
name: '通过',
icon: 'check-circle',
event: ({ row }) => {
handleApprove(row);
},
},
// handleReject
{
name: '驳回',
icon: 'close-circle',
event: ({ row }) => {
handleReject(row);
},
},
],
});
// 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('');
// }
// }
// );
onMounted(() => {
getData();
});
//
const getData = async () => {
loading.value = true;
try {
const response = await getEnterList(pageData.value.currentPage, pageData.value.pageSize);
crudData.value = response.data;
pageData.value.total = response.total;
const response = await getEnterList({
...searchForm.value,
status: activeCrudTab.value,
page: pageData.value.currentPage,
size: pageData.value.pageSize,
});
console.log('response', response);
crudData.value = response.data.records;
pageData.value.total = response.data.total;
pageData.value.currentPage = response.data.current;
pageData.value.pageSize = response.data.size;
} catch (error) {
ElMessage.error('加载数据失败');
} finally {
@ -232,12 +282,72 @@ const getData = async () => {
}
};
const getEnterDetail = async (id) => {
try {
const response = await getEnterById(id);
if (response?.code === 200 && response.data) {
// 使
formData.value = {
...cloneDeep(response.data),
// addressArr5
addressArr: [
response.data.provinceCode || '',
response.data.cityCode || '',
response.data.countyCode || '',
response.data.townCode || '',
response.data.villageCode || '',
],
};
// //
// if (response.data.cooperativePhoto) {
// cooperativePhotoList.value = [
// {
// name: '',
// url: response.data.cooperativePhoto,
// status: 'success',
// },
// ];
// }
// if (response.data.businessLicence) {
// businessLicenceList.value = [
// {
// name: '',
// url: response.data.businessLicence,
// status: 'success',
// },
// ];
// }
console.log('详情数据加载完成', formData.value);
} else {
ElMessage.error(response?.msg || '获取详情失败:服务器未返回有效数据');
}
} catch (error) {
console.error('获取详情失败:', error);
ElMessage.error(`获取详情失败:${error.message || '请稍后重试'}`);
}
};
function handleSearch() {
getData();
}
function handleTabChange(tab) {
handleSearch();
}
const handleAdd = () => {
isReadonly.value = false;
// resetForm();
dialogTitle.value = '新增';
visible.value = true;
};
//
const handleView = async (row) => {
loading.value = true;
dialogTitle.value = '查看';
formData.value = null;
try {
const data = await api.getDetailById(row.id);
formData.value = data;
await getEnterDetail(row.id);
isReadonly.value = true;
activeTab.value = 'basic';
visible.value = true;
@ -249,9 +359,9 @@ const handleView = async (row) => {
//
const handleEdit = async (row) => {
loading.value = true;
dialogTitle.value = '编辑';
try {
const data = await api.getDetailById(row.id);
formData.value = data;
await getEnterById(row.id);
isReadonly.value = false;
activeTab.value = 'basic';
visible.value = true;
@ -259,7 +369,106 @@ const handleEdit = async (row) => {
loading.value = false;
}
};
//
function handleResubmit(row) {
ElMessageBox.confirm('确认重新提交吗?', '重新提交').then(() => {
const params = {
id: row.id,
status: '1', //
reason: '重新提交审核',
};
approvalEnter(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: '用户主动撤销',
};
approvalEnter(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: '审核通过',
};
approvalEnter(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 approvalEnter(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 || '无驳回原因', '驳回原因');
}
//
const handleDelete = async (id) => {
try {
@ -267,9 +476,13 @@ const handleDelete = async (id) => {
type: 'warning',
});
loading.value = true;
await api.deleteById(id);
ElMessage.success('删除成功');
//
const res = await deleteEnter(id);
if (res.code === 200) {
ElMessage.success('删除成功');
getData(); //
} else {
ElMessage.error(res.msg || '删除失败');
}
} catch (e) {
if (e !== 'cancel') {
ElMessage.error('删除失败');
@ -278,63 +491,35 @@ const handleDelete = async (id) => {
loading.value = false;
}
};
// tabs
const tabOrder = ['basic', 'register', 'business', 'credit'];
// API
const api = {
//
getDetailById: async (id) => {
// formData
return {
basicInfo: {
name: '绿源蔬菜种植基地',
area: '150亩',
contactPerson: '王伟',
address: ['耿马县', '芒洪乡', '新村'],
product: '辣椒、豆角',
phone: '13987654321',
photoUrl: '',
businessLicenseUrl: '',
},
registerInfo: {
companyName: '绿源蔬菜种植基地',
legalPerson: '王伟',
companyType: '合作社',
registerOrg: '耿马县工商局',
approvalDate: '2022-03-01',
creditCode: '123456789000000000',
registerStatus: '存续',
establishDate: '2020-01-01',
totalCapital: '200万',
address: '耿马县芒洪乡新村',
businessScope: '蔬菜种植销售',
},
business: {
debtFiles: [],
profitFiles: [],
cashflowFiles: [],
},
credit: {
creditRating: 4,
farmersSupport: 3,
socialBenefit: 4,
techApplication: 3,
qualityService: 4,
},
};
},
const currentTabIndex = computed(() => tabOrder.indexOf(activeTab.value));
const isFirstTab = computed(() => currentTabIndex.value === 0);
const isLastTab = computed(() => currentTabIndex.value === tabOrder.length - 1);
/** 跳转到下一 Tab */
function handleNext() {
if (currentTabIndex.value < tabOrder.length - 1) {
activeTab.value = tabOrder[currentTabIndex.value + 1];
}
}
//
updateById: async (id, data) => {
console.log('更新请求', id, data);
return true;
},
/** 跳转到上一 Tab */
function handlePrev() {
if (currentTabIndex.value > 0) {
activeTab.value = tabOrder[currentTabIndex.value - 1];
}
}
//
deleteById: async (id) => {
console.log('删除请求', id);
return true;
},
};
/** 跳过当前步骤 */
function handleSkip() {
handleNext();
}
/** 跳过并保存 */
function handleSkipSave() {
handleSkip();
handleSubmit();
}
</script>
<style scoped lang="scss">
@ -342,12 +527,29 @@ const api = {
width: 100%;
display: flex;
align-items: center;
background-color: #fff;
// background-color: #7daaaa;
:deep(.el-tabs__item) {
font-size: 24px;
font-weight: 700;
font-size: 16px;
color: #555555;
font-weight: 500;
// border: 1 solid #f000;
}
:deep(.el-tabs__content) {
padding: 20px;
width: 100%;
// background-color: #f5f5f5;
border-radius: 4px;
height: calc(100vh - 300px);
overflow-y: auto;
}
:deep(.el-tab-pane) {
margin: 0 auto;
width: 80%;
}
:deep(.el-tab-pane:nth-child(5)) {
margin: 0 auto;
width: 100%;
}
}
</style>

View File

@ -20,7 +20,7 @@
</el-tabs>
<!-- 表格 -->
<avue-crud ref="crudRef" v-model:page="pageData" :data="filteredData" :option="crudOptions" :table-loading="loading">
<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>
@ -30,7 +30,7 @@
</avue-crud>
<!-- 新增弹窗 -->
<el-dialog :key="dialogTitle" v-model="dialogVisible" :title="dialogTitle" width="80%">
<el-dialog :key="dialogTitle" v-model="dialogVisible" :title="dialogTitle" width="80%" align-center :draggable="true">
<el-form :model="formData" label-width="120px" class="custom-form" :disabled="isReadonly">
<el-row :gutter="20">
<el-col :span="12">
@ -57,8 +57,8 @@
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
<el-radio value="1"></el-radio>
<el-radio value="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -77,8 +77,8 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="居住地行政区划" prop="address">
<area-select v-model="formData.address" :label="null" />
<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>
@ -122,7 +122,7 @@
</template>
<script setup>
import { ref, computed, reactive, onMounted, watch } 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';
@ -161,7 +161,7 @@ const pageData = ref({
total: 0,
});
//
const allData = ref([]);
const crudData = ref([]);
//
const dialogVisible = ref(false);
@ -177,8 +177,9 @@ const defaultFormData = {
cityCode: '', //
countyCode: '', //
townCode: '', //
street: '', //
address: [],
villageCode: '', //
address: '',
addressArr: [],
detailAddress: '',
area: '',
planCrop: '',
@ -192,20 +193,6 @@ const resetForm = () => {
formData.value = { ...defaultFormData };
};
// tab +
const filteredData = computed(() => {
return allData.value.filter((row) => {
//
if (String(row.status) !== activeTab.value) return false;
// //
const kw = searchForm.value.name.trim();
if (kw) {
return row.name.includes(kw) || row.idCard.includes(kw) || row.phone.includes(kw);
}
return true;
});
});
// ==============================
// CRUD
// ==============================
@ -255,8 +242,8 @@ async function getData() {
};
const response = await fetchFarmerList(params);
if (response.code === 200 && response.data) {
allData.value = response.data.records;
console.log('获取数据成功:', allData.value);
crudData.value = response.data.records;
console.log('获取数据成功:', crudData.value);
pageData.value = {
currentPage: response.data.current,
pageSize: response.data.size,
@ -288,9 +275,6 @@ const fetchCropsList = async () => {
}
};
function handleSearch() {
// filteredData
// activeTab + keyword
console.log('搜索关键词:', searchForm.value.keyword);
getData();
}
function handleReset() {
@ -309,14 +293,16 @@ const handleAdd = () => {
};
// address
watch(
() => formData.value.address,
() => formData.value.addressArr,
(newValue) => {
if (newValue.length <= 3) {
formData.value.provinceCode = '530000';
formData.value.cityCode = '530900';
formData.value.countyCode = newValue[0];
formData.value.townCode = newValue[1];
formData.value.street = newValue[2];
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('行政区划数据错误');
}
}
);
@ -326,6 +312,7 @@ 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) {
@ -366,26 +353,29 @@ const getFarmerById = async (id) => {
}
};
//
function handleView(row) {
async function handleView(row) {
console.log('查看', row);
dialogTitle.value = '查看';
isReadonly.value = true; //
dialogVisible.value = true;
getFarmerById(row.id).then((data) => {
if (data) {
const address = [data.countyCode, data.townCode, data.street].filter(Boolean);
formData.value = {
...data,
address,
};
}
const data = await getFarmerById(row.id);
if (data) {
console.log('data :>> ', data);
const addressArr = [data.provinceCode, data.cityCode, data.countyCode, data.townCode, data.villageCode].filter(Boolean);
console.log('addressArr :>> ', addressArr);
formData.value = {
...data,
addressArr: addressArr,
};
}
console.log('cc formData.value :>> ', formData.value.addressArr);
nextTick(() => {
dialogVisible.value = true;
});
//
}
// /
async function handleEdit(row) {
//
if (row.status === '2') {
try {
await ElMessageBox.confirm('编辑后数据将需要重新审核,是否继续?', '确认编辑', {
@ -394,7 +384,7 @@ async function handleEdit(row) {
type: 'warning',
});
} catch {
return; //
return;
}
}
dialogTitle.value = '编辑';
@ -402,10 +392,8 @@ async function handleEdit(row) {
dialogVisible.value = true;
getFarmerById(row.id).then((data) => {
if (data) {
const address = [data.countyCode, data.townCode, data.street].filter(Boolean);
formData.value = {
...data,
address,
};
}
});
@ -431,7 +419,6 @@ function handleSubmit(row) {
});
}
//
//
function handleResubmit(row) {
ElMessageBox.confirm('确认重新提交吗?', '重新提交').then(() => {
@ -601,7 +588,7 @@ function getActions(row) {
}
// ---------------------------------------------------------------------
// 4. allData
// 4. crudData
// ---------------------------------------------------------------------
onMounted(() => {
getData();
@ -622,6 +609,11 @@ onMounted(() => {
margin-bottom: 20px;
}
}
:deep(.el-dialog__body) {
padding: 20px;
height: calc(100vh - 300px);
overflow-y: auto;
}
.custom-form {
padding: 20px;

View File

@ -1,254 +0,0 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
>
<template #menu-left>
<!-- <el-button type="primary" icon="Upload" @click="onImport">导入</el-button> -->
<!-- <el-button type="danger" icon="Delete" @click="onBatchDel">批量删除</el-button> -->
</template>
<!-- <template #operationDate-search>
<el-date-picker v-model="timeVal" type="daterange" style="width: 230px" start-placeholder="开始" end-placeholder="结束" />
</template> -->
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import {
fetchBusinessSubjectList,
saveBusinessSubject,
editBusinessSubject,
deleteBusinessSubject,
fetchBusinessSubjectInfo,
} from '@/apis/businessEntity';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: { current: 1, size: 10, businessType: 0 },
form: {},
selection: [],
pageData: { currentPage: 1, pageSize: 10, total: 0 },
data: [],
options: {
...CRUD_OPTIONS,
addBtnText: '添加',
column: [
{ label: '主体代码', prop: 'id' },
{ label: '主体名称', prop: 'businessName' },
{
label: '经营产品种类',
prop: 'productType',
type: 'select',
dicData: [
{ label: '蔬菜', value: '0' },
{ label: '水果', value: '1' },
{ label: '畜产品', value: '2' },
{ label: '水产品', value: '3' },
{ label: '谷物', value: '4' },
{ label: '农资', value: '5' },
{ label: '种源', value: '6' },
{ label: '农产品加工', value: '7' },
{ label: '其他', value: '8' },
],
},
{ label: '主要经营产品', prop: 'primaryProduct' },
{ label: '户主身份证号', prop: 'idCard' },
{
label: '联系地址',
prop: 'address',
type: 'cascader',
props: { label: 'areaName', value: 'areaCode', children: 'areaChildVOS' },
dicUrl: `${VITE_APP_BASE_API}/system/area/region?areaCode=530000`,
dicHeaders: { authorization: UserStore.token },
},
{ label: '详细地址', prop: 'detailAddress' },
{ label: '联系电话', prop: 'phone' },
{
label: '审核状态',
prop: 'status',
type: 'select',
dicData: [
{ label: '未审核', value: 0 },
{ label: '通过', value: 1 },
{ label: '拒绝', value: 2 },
],
},
{ label: '审核意见', prop: 'reviewSuggestion' },
{ label: '创建时间', prop: 'createTime', type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss' },
],
actions: [
{ name: '详情', event: ({ row }) => viewRow(row) },
{ name: '编辑', event: ({ row }) => editRow(row) },
{ type: 'danger', name: '删除', event: ({ row }) => deleteRow(row) },
],
},
});
//
const loadData = () => {
state.loading = true;
fetchBusinessSubjectList(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
loadData();
const editRow = (row) => {
fetchBusinessSubjectInfo(row.id).then((res) => {
crudRef.value.rowEdit(res.data);
});
};
const viewRow = (row) => {
fetchBusinessSubjectInfo(row.id).then((res) => {
crudRef.value.rowView(res.data);
});
};
const deleteRow = (row) => {
app
.$confirm('确认删除?', '删除', { type: 'warning' })
.then(() =>
deleteBusinessSubject(row.id).then(() => {
app.$message.success('删除成功');
loadData();
})
)
.catch(() => {});
};
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
const handleIds = () => {
let datalist = state.selection.map((m) => {
return { landId: m.landId, landName: m.landName };
});
let selectIdlist = uniqueObjects(datalist, 'landId');
let selectIdsVal = selectIdlist.map((n) => {
return n.landId;
});
return selectIdsVal.toString() || '';
};
function uniqueObjects(arr, key) {
return arr.reduce((acc, current) => {
const duplicate = acc.find((element) => element[key] === current[key]);
if (!duplicate) {
acc.push(current);
}
return acc;
}, []);
}
//
const rowSave = (row, done, loading) => {
saveBusinessSubject(row)
.then(() => {
app.$message.success('添加成功');
done();
loadData();
})
.catch((e) => app.$message.error(e.msg))
.finally(() => loading());
};
const rowUpdate = (row, index, done, loading) => {
console.info('更新');
editBusinessSubject(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('更新成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
const onBatchDel = () => {
let ids = handleIds();
if (!ids.length || ids.length <= 0) {
return app.$message.error('请先选择要删除的数据');
}
};
</script>

View File

@ -2,389 +2,266 @@
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
v-model:page="pageData"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@refresh-change="handleRefresh"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>
<template #search="{ size }">
<AreaCascader v-model:value="state.query" placeholder="选择行政区域与网格" :width="400" />
<template #search>
<div class="custom-search">
<AreaCascader v-model:region-code="searchForm.regionCode" v-model:grid-id="searchForm.id" :width="600" />
<el-button type="primary" @click="handleSearch"> 搜索 </el-button>
<el-button @click="resetSearch"> 重置 </el-button>
</div>
</template>
<template #menu-left>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
<el-button type="primary" icon="Plus" @click="handleAdd">新增网格</el-button>
<!-- <el-button type="success" icon="download" @click="handleExport">导出</el-button> -->
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
<template #detail="scope">
<el-tabs type="border-card">
<el-tab-pane label="基本信息">
<avue-detail :option="baseDetailOption" :data="scope.row"></avue-detail>
</el-tab-pane>
<el-tab-pane label="网格地图">
<div v-if="scope.row.mapUrl" style="height: 400px">
<img :src="scope.row.mapUrl" style="max-width: 100%; max-height: 100%" />
</div>
<el-empty v-else description="暂无地图数据"></el-empty>
</el-tab-pane>
<el-tab-pane label="其他信息">
<avue-detail :option="otherDetailOption" :data="scope.row"></avue-detail>
</el-tab-pane>
</el-tabs>
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
</template>
</avue-crud>
<el-dialog :key="dialogTitle" v-model="visible" :title="dialogTitle" width="60%" align-center :draggable="true">
<el-form ref="form" :model="formData" :rules="rules" :disabled="isReadonly" label-width="100px" class="form-item">
<p class="form-title">填写网格信息</p>
<el-form-item label="网格名称" prop="gridName">
<el-input v-model="formData.gridName" placeholder="请输入网格名称" />
</el-form-item>
<el-form-item label="所属行政区域" prop="gridAreaCode">
<AreaSelect v-model="formData.gridAreaCode" :label="null" :emit-path="false" />
</el-form-item>
<el-form-item label="网格化地图" prop="scopeImg">
<FileUploader v-model="formData.scopeImg" :limit="1" />
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="formData.note" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button v-if="!isReadonly" type="primary" @click="handleSubmit()"> 保存 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
// ---------------------------------------------------------------------
// avue-crud
// ---------------------------------------------------------------------
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { ElMessage, ElMessageBox } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
import { compact } from 'lodash';
import { GetEntityList, AddEntity, UpdateEntity, DeleteEntity, ExportEntity } from '@/apis/resource/grid';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
gridName: '',
regionCode: '',
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '添加网格',
// detail: true,
// detailTitle: '',
column: [
{
label: '网格编号',
prop: 'id',
},
{
label: '网格名称',
prop: 'gridName',
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '所属行政区域',
prop: 'gridAreaName',
addDisplay: false,
viewDisplay: true,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '所属行政区域',
prop: 'cities',
type: 'cascader',
hide: true,
span: 24,
width: 300,
addDisplay: true,
editDisplay: true,
viewDisplay: false,
emitPath: false,
props: {
label: 'areaName',
value: 'areaCode',
children: 'areaChildVOS',
},
dicUrl: `${VITE_APP_BASE_API}/system/area/region?areaCode=530000`,
dicHeaders: {
authorization: UserStore.token,
},
dicFormatter: (res) => res.data ?? [],
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{
label: '网格地图',
prop: 'mapUrl',
type: 'upload',
hide: true,
// action: `${VITE_APP_BASE_API}/system/file/upload`,
},
// {
// label: '',
// prop: 'gridName',
// type: 'select',
// addDisplay: false,
// hide: true,
// // search: true,
// searchLabelWidth: 100,
// dicUrl: `${VITE_APP_BASE_API}/land-resource/gridManage/page?regionCode={{key}}`,
// props: {
// label: 'gridName',
// value: 'gridName',
// },
// dicHeaders: {
// authorization: UserStore.token,
// },
// dicFormatter: (res) => res.data?.records,
// },
{
label: '备注',
prop: 'note',
type: 'textarea',
span: 24,
rows: 4,
overHidden: true,
// width: 200,
},
{
label: '创建时间',
prop: 'createTime',
width: 200,
hide: true,
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
const user = UserStore.getUserInfo();
console.log('admin 属性:', user.admin);
const loading = ref(false);
const visible = ref(false);
const isReadonly = ref(false);
const dialogTitle = ref();
const formData = ref({
gridName: '',
gridAreaCode: '',
scope: '',
scopeImg: '',
note: '',
});
const baseDetailOption = {
const initialFormData = { ...formData.value };
const resetForm = () => {
formData.value = { ...initialFormData };
};
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
});
const searchForm = ref({
gridName: '',
keyword: '',
regionCode: '',
id: '',
status: -1,
});
const initialSearchForm = { ...searchForm.value };
const resetSearch = () => {
searchForm.value = { ...initialSearchForm };
};
//
const filterObject = (obj) => {
const newObj = {};
Object.keys(obj).forEach((key) => {
const value = obj[key];
// null undefined
if (value !== '' && value !== null && value !== undefined) {
newObj[key] = value;
}
});
return newObj;
};
const crudData = ref([]);
const crudOptions = reactive({
...CRUD_OPTIONS,
addBtn: false,
searchBtn: false,
emptyBtn: false,
column: [
{ label: '网格编号', prop: 'id' },
{ label: '网格名称', prop: 'gridName' },
{ label: '所属行政区域', prop: 'gridAreaName' },
{ label: '备注', prop: 'note' },
],
actions: [
{
label: '网格名称',
prop: 'gridName',
name: '查看',
icon: 'view',
event: ({ row }) => handleView(row),
},
{
label: '所属行政区域',
prop: 'gridAreaName',
name: '编辑',
icon: 'edit',
event: ({ row }) => handleEdit(row),
},
{
label: '备注',
prop: 'note',
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => handleDelete(row),
},
],
});
const handleRefresh = async () => {
searchForm.value = { ...initialSearchForm };
getData();
};
const otherDetailOption = {
column: [
{
label: '创建时间',
prop: 'createTime',
},
// ...
],
const handleCurrentChange = (val) => {
pageData.value.currentPage = val;
};
//
const loadData = () => {
state.loading = true;
GetEntityList(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
const handleSizeChange = (val) => {
pageData.value.pageSize = val;
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
const handleView = (row) => {
isReadonly.value = true;
formData.value = { ...row };
dialogTitle.value = '查看网格';
visible.value = true;
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
const handleEdit = (row) => {
isReadonly.value = false;
formData.value = { ...row };
dialogTitle.value = '编辑网格';
visible.value = true;
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const rowView = (row) => {
crudRef.value.rowView(row);
};
const setCity = (row) => {
if (!isEmpty(row.cities)) {
row.provinceCode = row?.cities[0] ?? null;
row.cityCode = row?.cities[1] ?? null;
row.gridAreaCode = row?.cities[4] ?? null;
row.townCode = row?.cities[3] ?? null;
row.village = row?.cities[4] ?? null;
// row.village = row?.cities.join(',');
}
};
//
const rowSave = (row, done, loading) => {
setCity(row);
AddEntity(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const rowEdit = (row) => {
const village = !isEmpty(row.village) ? row.village : [];
row.cities = compact([row.provinceCode, row.cityCode, row.gridAreaCode ?? '', row.townCode ?? '', ...village]);
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
setCity(row);
UpdateEntity(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('更新成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const rowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`删除后信息将不可查看,确认要删除吗?`, '确定删除', {
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确认删除该网格吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
DeleteEntity({ id: row.id })
.then((res) => {
if (res.code === 200) {
app.$message.success('删除成功!');
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
});
})
.catch(() => {});
});
const response = await deleteGrid(row.id);
ElMessage.success('删除成功');
getData();
} catch (error) {
if (error === 'cancel') {
ElMessage.info('已取消删除');
} else {
ElMessage.error('删除失败');
console.error('删除异常:', error);
}
}
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
const handleSubmit = async () => {
console.log('提交表单:', formData.value);
try {
if (dialogTitle.value === '新增网格') {
await createGrid(formData.value);
ElMessage.success('新增成功');
resetForm();
visible.value = false;
getData();
} else {
await updateGrid(formData.value);
ElMessage.success('更新成功');
resetForm();
visible.value = false;
getData();
}
} catch (error) {
ElMessage.error(error.message || '新增失败,请重试');
}
state.loading = true;
const fileName = '网格明细表';
ExportEntity(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})
.catch((err) => {
app.$message.error('导出失败!');
})
.finally(() => {
state.loading = false;
});
};
// ---------------------------------------------------------------------
//
// ---------------------------------------------------------------------
import { createGrid, updateGrid, deleteGrid, fetchGridList, getGridDetail, exportGrid } from '@/apis/landResourceManagement/gridManagement';
onMounted(() => {
getData();
});
const getData = async () => {
const filteredParams = filterObject(searchForm.value);
const response = await fetchGridList(filteredParams);
crudData.value = Array.isArray(response.data.records) ? response.data.records : [];
};
const handleAdd = () => {
console.log('handleAdd');
resetForm();
isReadonly.value = false;
dialogTitle.value = '新增网格';
visible.value = true;
};
const handleSearch = () => {
getData();
};
const handleCancel = () => {
visible.value = false;
};
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 20px;
height: calc(100vh - 300px);
overflow-y: auto;
}
.form-title {
font-size: 16px;
font-weight: 500;
margin: 30px 0;
color: #333333;
}
.form-item {
width: 500px;
margin: 0 auto;
}
.dialog-footer {
text-align: center;
}
.custom-search {
display: flex;
align-items: center;
margin-bottom: 16px;
.el-button {
margin-left: 12px;
}
}
</style>

View File

@ -0,0 +1,392 @@
<template>
<div class="custom-page">
<avue-crud
ref="crudRef"
v-model="state.form"
v-model:search="state.query"
v-model:page="state.pageData"
:table-loading="state.loading"
:data="state.data"
:option="state.options"
@refresh-change="refreshChange"
@search-reset="searchChange"
@search-change="searchChange"
@selection-change="selectionChange"
@current-change="currentChange"
@size-change="sizeChange"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
>
<template #search="{ size }">
<AreaCascader v-model:value="state.query" placeholder="选择行政区域与网格" :width="400" />
</template>
<template #menu-left>
<el-button type="success" icon="download" @click="onExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="state.options.actions" :data="scope" />
</template>
<template #detail="scope">
<el-tabs type="border-card">
<el-tab-pane label="基本信息">
<avue-detail :option="baseDetailOption" :data="scope.row"></avue-detail>
</el-tab-pane>
<el-tab-pane label="网格地图">
<div v-if="scope.row.mapUrl" style="height: 400px">
<img :src="scope.row.mapUrl" style="max-width: 100%; max-height: 100%" />
</div>
<el-empty v-else description="暂无地图数据"></el-empty>
</el-tab-pane>
<el-tab-pane label="其他信息">
<avue-detail :option="otherDetailOption" :data="scope.row"></avue-detail>
</el-tab-pane>
</el-tabs>
</template>
</avue-crud>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { useApp } from '@/hooks';
import { CRUD_OPTIONS } from '@/config';
import { isEmpty, downloadFile } from '@/utils';
import { useUserStore } from '@/store/modules/user';
import { compact } from 'lodash';
import { GetEntityList, AddEntity, UpdateEntity, DeleteEntity, ExportEntity } from '@/apis/resource/grid';
const { VITE_APP_BASE_API } = import.meta.env;
const app = useApp();
const UserStore = useUserStore();
const crudRef = ref(null);
const state = reactive({
loading: false,
query: {
current: 1,
size: 10,
gridName: '',
regionCode: '',
},
form: {},
selection: [],
options: {
...CRUD_OPTIONS,
addBtnText: '添加网格',
// detail: true,
// detailTitle: '',
column: [
{
label: '网格编号',
prop: 'id',
addDisplay: false,
},
{
label: '网格名称',
prop: 'gridName',
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '所属行政区域',
prop: 'gridAreaName',
addDisplay: false,
viewDisplay: true,
rules: {
required: true,
message: '请输入',
trigger: 'blur',
},
},
{
label: '所属行政区域',
prop: 'cities',
type: 'cascader',
hide: true,
span: 24,
width: 300,
addDisplay: true,
editDisplay: true,
viewDisplay: false,
emitPath: false,
props: {
label: 'areaName',
value: 'areaCode',
children: 'areaChildVOS',
},
dicUrl: `${VITE_APP_BASE_API}/system/area/region?areaCode=530000`,
dicHeaders: {
authorization: UserStore.token,
},
dicFormatter: (res) => res.data ?? [],
rules: {
required: true,
message: '请选择',
trigger: 'blur',
},
},
{
label: '网格地图',
prop: 'mapUrl',
type: 'upload',
hide: true,
// action: `${VITE_APP_BASE_API}/system/file/upload`,
},
// {
// label: '',
// prop: 'gridName',
// type: 'select',
// addDisplay: false,
// hide: true,
// // search: true,
// searchLabelWidth: 100,
// dicUrl: `${VITE_APP_BASE_API}/land-resource/gridManage/page?regionCode={{key}}`,
// props: {
// label: 'gridName',
// value: 'gridName',
// },
// dicHeaders: {
// authorization: UserStore.token,
// },
// dicFormatter: (res) => res.data?.records,
// },
{
label: '备注',
prop: 'note',
type: 'textarea',
span: 24,
rows: 4,
overHidden: true,
// width: 200,
},
{
label: '创建时间',
prop: 'createTime',
width: 200,
hide: true,
display: false,
},
],
actions: [
{
name: '查看',
icon: 'view',
event: ({ row }) => rowView(row),
},
{
name: '编辑',
icon: 'edit',
event: ({ row }) => rowEdit(row),
},
{
type: 'danger',
name: '删除',
icon: 'delete',
event: ({ row }) => rowDel(row),
},
],
},
pageData: {
total: 0,
currentPage: 1,
pageSize: 10,
},
data: [],
currentRow: {},
});
const baseDetailOption = {
column: [
{
label: '网格名称',
prop: 'gridName',
},
{
label: '所属行政区域',
prop: 'gridAreaName',
},
{
label: '备注',
prop: 'note',
},
],
};
const otherDetailOption = {
column: [
{
label: '创建时间',
prop: 'createTime',
},
// ...
],
};
//
const loadData = () => {
state.loading = true;
GetEntityList(state.query)
.then((res) => {
if (res.code === 200) {
const { current, size, total, records } = res.data;
state.data = records;
state.pageData = {
currentPage: current || 1,
pageSize: size || 10,
total: total,
};
}
})
.catch((err) => {
app.$message.error(err.msg);
state.data = [];
})
.finally(() => {
state.loading = false;
});
};
loadData();
//
const currentChange = (current) => {
state.query.current = current;
loadData();
};
//
const sizeChange = (size) => {
state.query.size = size;
loadData();
};
//
const searchChange = (params, done) => {
if (done) done();
state.query = params;
state.query.current = 1;
loadData();
};
//
const refreshChange = () => {
loadData();
app.$message.success('刷新成功');
};
//
const selectionChange = (rows) => {
state.selection = rows;
};
//
const rowView = (row) => {
crudRef.value.rowView(row);
};
const setCity = (row) => {
if (!isEmpty(row.cities)) {
row.provinceCode = row?.cities[0] ?? null;
row.cityCode = row?.cities[1] ?? null;
row.gridAreaCode = row?.cities[4] ?? null;
row.townCode = row?.cities[3] ?? null;
row.village = row?.cities[4] ?? null;
// row.village = row?.cities.join(',');
}
};
//
const rowSave = (row, done, loading) => {
setCity(row);
row.gridAreaCode = row.cities;
AddEntity(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('添加成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const rowEdit = (row) => {
const village = !isEmpty(row.village) ? row.village : [];
row.cities = compact([row.provinceCode, row.cityCode, row.gridAreaCode ?? '', row.townCode ?? '', ...village]);
crudRef.value.rowEdit(row);
};
const rowUpdate = (row, index, done, loading) => {
setCity(row);
UpdateEntity(row)
.then((res) => {
if (res.code === 200) {
app.$message.success('更新成功!');
done();
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
})
.finally(() => {
loading();
});
};
//
const rowDel = (row, index, done) => {
if (isEmpty(row)) return;
app
.$confirm(`删除后信息将不可查看,确认要删除吗?`, '确定删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
DeleteEntity({ id: row.id })
.then((res) => {
if (res.code === 200) {
app.$message.success('删除成功!');
loadData();
}
})
.catch((err) => {
app.$message.error(err.msg);
});
})
.catch(() => {});
};
//
const onExport = () => {
if (isEmpty(state.data)) {
app.$message.error('当前暂时没有可供导出的数据!');
return;
}
state.loading = true;
const fileName = '网格明细表';
ExportEntity(state.query)
.then((res) => {
if (res.status === 200) {
downloadFile(res.data, `${fileName}.xlsx`, 'blob');
app.$message.success('导出成功!');
}
})
.catch((err) => {
app.$message.error('导出失败!');
})
.finally(() => {
state.loading = false;
});
};
</script>

View File

@ -48,26 +48,27 @@
</template>
</avue-crud>
<el-dialog v-model="addDialogVisible" :title="isEdit ? '编辑网格员' : '新增网格员'" width="600px">
<el-form ref="addFormRef" :model="addForm" :rules="addFormRules" label-width="120px">
<el-dialog :key="dialogTitle" v-model="addDialogVisible" :title="isEdit ? '编辑网格员' : '新增网格员'" width="60%" align-center :draggable="true">
<el-form ref="addFormRef" :model="addForm" :rules="addFormRules" label-width="120px" class="form-item">
<p class="form-title">填写网格员信息</p>
<el-form-item label="网格员姓名" prop="memberName">
<el-input v-model="addForm.memberName" style="width: 380px" />
<el-input v-model="addForm.memberName" />
</el-form-item>
<el-form-item label="" prop="gridId" label-width="0px">
<!-- 假设 AreaCascader 是行政区域-网格的级联选择组件 -->
<AreaCascader v-model:region-code="addForm.gridAreaCode" v-model:grid-id="addForm.gridId" split-rows label="" />
</el-form-item>
<el-form-item label="电话号码" prop="phone">
<el-input v-model="addForm.phone" style="width: 380px" />
<el-input v-model="addForm.phone" />
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="addForm.note" type="textarea" style="width: 380px" />
<el-input v-model="addForm.note" type="textarea" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="addDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm">确定</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</span>
</template>
</el-dialog>
@ -270,6 +271,7 @@ const selectionChange = (rows) => {
};
const rowView = (row) => {
isEdit.value = false;
crudRef.value.rowView(row);
};
@ -280,6 +282,7 @@ const rowEdit = (row) => {
};
//
const onAdd = () => {
isEdit.value = false;
addDialogVisible.value = true;
//
Object.keys(addForm).forEach((key) => {
@ -351,6 +354,11 @@ const onExport = () => {
</script>
<style scoped lang="scss">
:deep(.el-dialog__body) {
padding: 20px;
height: calc(100vh - 300px);
overflow-y: auto;
}
.custom-page {
padding: 20px;
.custom-search {
@ -394,4 +402,23 @@ const onExport = () => {
}
}
}
.form-title {
font-size: 16px;
font-weight: 500;
margin: 30px 0;
color: #333333;
}
.form-item {
width: 500px;
margin: 0 auto;
}
.dialog-footer {
text-align: center;
}
:deep(.area-cascader-label) {
padding: 0 12px 0 0;
margin: 0;
width: 120px;
box-sizing: border-box;
}
</style>

View File

@ -38,7 +38,7 @@ const res = [
name: `1号基地`,
area: 100,
status: 1,
location: '东经 92°45至 99°23北纬 20°27至 18°35F',
location: '99°24\'31.280"E, 23°31\'39.990"N',
type: 1,
p1: 1,
p2: 100,
@ -52,7 +52,7 @@ const res = [
name: `2号基地`,
area: 211,
status: 1,
location: '东经 92°44至 99°24北纬 20°23至 18°31F',
location: '99°23\'56.224"E, 23°32\'8.225"N',
type: 1,
p1: 1,
p2: 120,
@ -66,7 +66,7 @@ const res = [
name: `3号基地`,
area: 121,
status: 1,
location: '东经 92°43至 99°25北纬 20°21至 18°31F',
location: '99°25\'32.574"E, 23°38\'23.701"N',
type: 1,
p1: 1,
p2: 56,
@ -80,7 +80,7 @@ const res = [
name: `4号基地`,
area: 231,
status: 2,
location: '东经 92°42至 99°26北纬 21°20至 18°32F',
location: '99°25\'19.027"E, 23°31\'59.693"N',
type: 2,
p1: 2,
p2: 145,
@ -94,7 +94,7 @@ const res = [
name: `5号基地`,
area: 231,
status: 2,
location: '东经 92°43至 99°26北纬 21°21至 18°22F',
location: '99°30\'50.886"E, 23°37\'14.542"N',
type: 2,
p1: 2,
p2: 145,
@ -177,6 +177,9 @@ const state = reactive({
message: '请输入',
trigger: 'blur',
},
formatter: (row) => {
return row.location.replace(',', '\n'); //
},
},
{
label: '土壤类型',

View File

@ -45,6 +45,12 @@ export function authDetail(id, params) {
params,
});
}
export function getStatisticsData(id, params) {
return request('brand/applicationrecord/getStatisticsData', {
method: 'GET',
params,
});
}
export function saveRecords(data) {
return request('brand/applicationrecord/save', {
@ -52,3 +58,10 @@ export function saveRecords(data) {
data,
});
}
export function cancelAuth(data) {
return request('brand/applicationrecord/cancel', {
method: 'POST',
data,
});
}

View File

@ -7,13 +7,21 @@
<div class="card-left">
<img :src="getAssetsFile('images/brand/1532.png')" alt="" />
<div class="card-content flex-1 flex-column">
<div class="stat-number">817 <span></span></div>
<div class="stat-number">{{ statisticsData.advent }} <span></span></div>
<div class="stat-label">授权产品</div>
</div>
</div>
<div class="card-right">
<div v-if="statisticsData.adventNum >= 0" class="card-right">
<img :src="getAssetsFile('images/brand/cardLeft.png')" alt="" />
<p>较上月上涨 <span>13</span> </p>
<p>
较上月上涨 <span>{{ statisticsData.adventNum }}</span>
</p>
</div>
<div v-else class="card-right">
<img :src="getAssetsFile('images/brand/cardRight.png')" alt="" />
<p>
较上月下降 <span>{{ -statisticsData.adventNum }}</span>
</p>
</div>
</el-card>
</el-col>
@ -22,13 +30,21 @@
<div class="card-left">
<img :src="getAssetsFile('images/brand/1533.png')" alt="" />
<div class="card-content flex-1 flex-column">
<div class="stat-number">125 <span></span></div>
<div class="stat-number">{{ statisticsData.total }} <span></span></div>
<div class="stat-label">临期产品</div>
</div>
</div>
<div class="card-right">
<div v-if="statisticsData.totalNum >= 0" class="card-right">
<img :src="getAssetsFile('images/brand/cardLeft.png')" alt="" />
<p>
较上月上涨 <span>{{ statisticsData.totalNum }}</span>
</p>
</div>
<div v-else class="card-right">
<img :src="getAssetsFile('images/brand/cardRight.png')" alt="" />
<p>较上月下降 <span>5</span> </p>
<p>
较上月下降 <span>{{ -statisticsData.totalNum }}</span>
</p>
</div>
</el-card>
</el-col>
@ -186,6 +202,9 @@ import { ref, computed, onMounted } from 'vue';
import { getAssetsFile } from '@/utils/index.js';
import { authList, getProducts } from '@/apis/brand';
import { Edit } from '@element-plus/icons-vue';
import { getStatisticsData } from '@/apis/brand.js';
import { ElMessage, ElMessageBox } from 'element-plus';
import { cancelAuth } from '@/apis/brand.js';
const dialogVisible = ref(false);
const traceData = ref(null);
@ -255,8 +274,36 @@ const products = ref([
},
]);
const statisticsData = ref({
advent: 817,
adventNum: 13,
total: 125,
totalNum: -5,
});
const onRevoke = (p) => {
console.log('取消授权', p);
ElMessageBox.confirm('是否确认取消该商品的授权?', '注意!', {
confirmButtonText: '确认',
cancelButtonText: '放弃',
type: 'error',
})
.then(() => {
cancelAuth({ id: p.id }).then((res) => {
if (res.code === 200) {
ElMessage({
type: 'success',
message: '已取消授权',
});
getAuthList(2);
}
});
})
.catch(() => {
// ElMessage({
// type: 'info',
// message: 'Delete canceled',
// });
});
};
function onInspect(item) {
@ -338,8 +385,15 @@ const getAuthList = (status) => {
});
};
const getStatusData = () => {
getStatisticsData().then((res) => {
console.log(res);
});
};
onMounted(() => {
getAuthList(2);
getStatusData();
});
</script>

View File

@ -79,15 +79,15 @@
<div>{{ item.maxMoney }}</div>
</div>
<div style="display: flex">
<div class="infoTitle">最长期限</div>
<div class="infoTitle">申请期限</div>
<div>{{ item.maxDate }}</div>
</div>
<div style="display: flex">
<div class="infoTitle">利率</div>
<div class="infoTitle">参考利率</div>
<div>{{ item.rate }}</div>
</div>
<div style="display: flex">
<div class="infoTitle">保方式</div>
<div class="infoTitle">保方式</div>
<div>{{ item.ways }}</div>
</div>
</div>

View File

@ -300,7 +300,6 @@ onMounted(() => {
const fertilizer = getAssetsFile('images/mockPic/fertilizerReport.png');
const seed = getAssetsFile('images/mockPic/seedReport.png');
const Irrigation = getAssetsFile('images/mockPic/irrigationReport.png');
console.log(fertilizer);
initChart();
if (props.title.indexOf('灌溉') !== -1) {
Pics.value = [Irrigation];