609 lines
20 KiB
Vue
Raw Normal View History

2025-02-25 17:30:50 +08:00
<template>
2025-03-04 16:24:17 +08:00
<div class="custom-page">
<avue-crud
ref="crudRef"
2025-06-12 13:11:18 +08:00
v-model:page="pageData"
:data="crudData"
:option="crudOptions"
:table-loading="loading"
@refresh-change="handleRefresh"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
2025-03-04 16:24:17 +08:00
>
<template #menu-left>
2025-06-12 13:11:18 +08:00
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="success" icon="download" @click="handleExport">导出</el-button>
2025-02-26 17:03:50 +08:00
</template>
2025-03-04 16:24:17 +08:00
<template #menu="scope">
2025-06-12 17:06:06 +08:00
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
2025-03-06 16:20:26 +08:00
</template>
2025-03-04 16:24:17 +08:00
</avue-crud>
2025-06-12 13:11:18 +08:00
<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">
2025-03-04 16:24:17 +08:00
<el-row :gutter="20">
<el-col :span="12">
2025-06-12 13:11:18 +08:00
<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="请输入注意事项" />
2025-03-04 16:24:17 +08:00
</el-form-item>
</el-col>
<el-col :span="12">
2025-06-12 13:11:18 +08:00
<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="请输入巡查对象" />
2025-03-04 16:24:17 +08:00
</el-form-item>
</el-col>
</el-row>
</el-form>
2025-06-12 13:11:18 +08:00
<!-- 巡查信息登记只有进行过结果登记的才显示 -->
<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>
2025-06-12 17:06:06 +08:00
<el-button v-if="!isReadonlyRegist" type="danger" icon="el-icon-delete" circle size="mini" @click="handleRemoveForm(index)" />
2025-06-12 13:11:18 +08:00
</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">
2025-06-12 17:06:06 +08:00
<el-radio value="1"></el-radio>
<el-radio value="0"></el-radio>
2025-06-12 13:11:18 +08:00
</el-radio-group>
</el-form-item>
<!-- 违法时间 -->
2025-06-12 17:06:06 +08:00
<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"
/>
2025-06-12 13:11:18 +08:00
</el-form-item>
<!-- 违法图片 -->
2025-06-12 17:06:06 +08:00
<el-form-item v-if="form.illegalFlag === '1'" label="违法图片" prop="illegalImages">
<file-uploader v-model="form.illegalImages" :limit="1" :readonly="isReadonlyRegist || form.illegalFlag === 0" />
2025-06-12 13:11:18 +08:00
</el-form-item>
</el-col>
2025-06-12 17:06:06 +08:00
<el-col v-if="form.illegalFlag === '1'" :span="12">
2025-06-12 13:11:18 +08:00
<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"
2025-06-12 17:06:06 +08:00
:disabled="isReadonlyRegist || form.illegalFlag === 0"
2025-06-12 13:11:18 +08:00
/>
</el-form-item>
2025-06-12 17:06:06 +08:00
<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"
/>
2025-06-12 13:11:18 +08:00
</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>
2025-03-04 16:24:17 +08:00
<template #footer>
<div class="dialog-footer">
2025-06-12 13:11:18 +08:00
<!-- <el-button @click="infoCancel">取消</el-button> -->
<el-button type="primary" @click="handleSubmit()"> 确认 </el-button>
2025-03-04 16:24:17 +08:00
</div>
</template>
</el-dialog>
</div>
2025-02-25 17:30:50 +08:00
</template>
2025-06-12 13:11:18 +08:00
2025-03-04 16:24:17 +08:00
<script setup>
2025-06-12 13:11:18 +08:00
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
2025-03-04 16:24:17 +08:00
import { CRUD_OPTIONS } from '@/config';
2025-06-12 13:11:18 +08:00
import { ElMessage } from 'element-plus';
import { useActionPermissions } from '@/hooks/useActionPermissions';
2025-06-12 17:06:06 +08:00
import { useUserStore } from '@/store/modules/user';
2025-06-12 13:11:18 +08:00
import {
createLandInspection,
deleteLandInspection,
updateLandInspection,
updateLandInspectionStatus,
getLandInspectionDetail,
fetchLandInspectionList,
exportLandInspection,
createInspectionResult,
2025-06-12 17:06:06 +08:00
createInspectionResultBatch,
2025-06-12 13:11:18 +08:00
deleteInspectionResult,
getInspectionResultDetail,
} from '@/apis/landResourceManagement/landInspection/index';
2025-02-25 17:30:50 +08:00
2025-06-12 13:11:18 +08:00
const dialogTitle = ref('新增');
const visible = ref(false);
const isReadonlyInfo = ref(false);
const isReadonlyRegist = ref(false);
2025-06-12 17:06:06 +08:00
const UserStore = useUserStore();
const user = UserStore.getUserInfo();
console.log('admin 属性:', user.admin);
2025-03-04 16:24:17 +08:00
2025-06-12 13:11:18 +08:00
const pageData = ref({
currentPage: 1,
pageSize: 10,
total: 0,
2025-02-26 17:03:50 +08:00
});
2025-06-12 13:11:18 +08:00
const searchForm = ref({
taskCode: '',
taskName: '',
userId: '',
inspectionTypeCode: '',
gridId: '',
2025-02-26 17:03:50 +08:00
});
2025-03-04 16:24:17 +08:00
2025-06-12 13:11:18 +08:00
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: '', // 违法描述
2025-06-12 17:06:06 +08:00
img: '', // 违法图片
2025-06-12 13:11:18 +08:00
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' },
2025-06-12 17:06:06 +08:00
{ label: '状态', prop: 'inspectionStatusName' },
2025-06-12 13:11:18 +08:00
],
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),
},
],
2025-02-26 17:22:57 +08:00
});
2025-02-26 17:03:50 +08:00
2025-06-12 13:11:18 +08:00
onMounted(() => {
getData();
});
const getData = async () => {
loading.value = true;
try {
const response = await fetchLandInspectionList({
current: pageData.value.currentPage,
size: pageData.value.pageSize,
...searchForm.value,
2025-03-05 17:30:38 +08:00
});
2025-06-12 13:11:18 +08:00
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;
}
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
const handleAdd = () => {
isReadonlyInfo.value = false;
// resetForm();
dialogTitle.value = '新增';
visible.value = true;
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
const handleEdit = async (id) => {
console.log('编辑行:', id);
isReadonlyInfo.value = false;
const response = await getLandInspectionDetail(id);
taskForm.value = response.data;
// 处理返回的任务成员数组inspectionUsers[]数据为选择器需要的id数组taskUserIds[]
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;
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
const handleView = async (id) => {
console.log('查看行:', id);
const response = await getLandInspectionDetail(id);
taskForm.value = response.data;
const arr = response.data.inspectionResults;
2025-06-12 17:06:06 +08:00
illegalForms.value = arr ? arr : [];
illegalForms.value.forEach((item) => {
// 如果后端只给了一个 img 字符串
if (item.img) {
// 直接包成数组
item.illegalImages = [item.img];
} else {
// 没图时给空数组
item.illegalImages = [];
}
});
2025-06-12 13:11:18 +08:00
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;
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
const handleDelete = async (id) => {
console.log('删除ID:', id);
const response = await deleteLandInspection(id);
if (response?.code === 200) {
ElMessage.success(response?.msg || '删除成功');
getData(); // 刷新数据
}
2025-03-04 16:24:17 +08:00
};
2025-02-25 17:30:50 +08:00
2025-06-12 13:11:18 +08:00
const handleResult = async (id) => {
console.log('结果登记ID:', id);
await handleView(id);
isReadonlyRegist.value = false;
dialogTitle.value = '结果登记';
illegalForm.value.inspectionId = id;
nextTick(() => {
visible.value = true;
});
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
const handleBack = async (id) => {
console.log('收回任务ID:', id);
const response = await updateLandInspectionStatus({
id,
status: '02',
});
ElMessage.success(response?.msg || '发布成功');
await getData();
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
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;
});
2025-03-05 17:30:38 +08:00
};
2025-06-12 13:11:18 +08:00
// 删除表单
2025-06-12 17:06:06 +08:00
const handleRemoveForm = async (index) => {
if (illegalForms.value[index].id) {
await deleteInspectionResult(illegalForms.value[index].id);
} else {
illegalForms.value.splice(index, 1);
}
2025-03-04 16:24:17 +08:00
};
2025-06-12 13:11:18 +08:00
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 === '结果登记') {
2025-06-12 17:06:06 +08:00
// 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);
2025-06-12 13:11:18 +08:00
ElMessage.success(response?.msg || '结果登记成功');
2025-06-12 17:06:06 +08:00
// 4. 完成后重新拉列表、关闭弹窗
2025-06-12 13:11:18 +08:00
await getData();
}
// 可刷新表格数据
} catch (e) {
ElMessage.error('保存失败');
} finally {
loading.value = false;
2025-03-04 16:24:17 +08:00
}
2025-02-26 17:03:50 +08:00
};
2025-06-12 13:11:18 +08:00
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('导出成功');
2025-02-26 17:03:50 +08:00
} else {
2025-06-12 13:11:18 +08:00
ElMessage.error(response?.msg || '导出失败');
2025-02-26 17:03:50 +08:00
}
2025-06-12 13:11:18 +08:00
} catch (error) {
ElMessage.error('导出失败,请稍后重试');
}
2025-02-26 17:03:50 +08:00
};
2025-06-12 13:11:18 +08:00
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);
}
// ---------------------------------------------------------------------
// 页面权限控制相关
// ---------------------------------------------------------------------
2025-02-26 17:03:50 +08:00
2025-06-12 17:06:06 +08:00
// 如果user.admin是true就是operator否则是inspector
2025-06-12 13:11:18 +08:00
const role = ref('operator');
2025-06-12 17:06:06 +08:00
if (user.admin) {
role.value = 'operator';
} else {
role.value = 'inspector';
}
2025-06-12 13:11:18 +08:00
// 所有操作项
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 的权限
2025-06-12 17:06:06 +08:00
operator: ['view', 'edit', 'delete', 'publish', 'back', 'rePublish', 'viewResult'],
2025-06-12 13:11:18 +08:00
inspector: ['result', 'viewResult'],
2025-02-26 17:03:50 +08:00
};
2025-06-12 13:11:18 +08:00
// 状态控制表(状态码 -> 可操作项)
const statusMap = {
'-1': ['view', 'edit', 'delete', 'publish'],
'00': ['result', 'back', 'viewResult'],
'01': ['viewResult'],
'02': ['view', 'edit', 'delete', 'rePublish'],
2025-02-26 17:03:50 +08:00
};
2025-06-12 17:06:06 +08:00
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));
}
2025-02-25 17:30:50 +08:00
</script>
2025-03-10 09:05:01 +08:00
2025-06-12 13:11:18 +08:00
<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;
2025-03-10 09:05:01 +08:00
}
</style>