合并 #2

Merged
shenhong merged 4 commits from fea into dev 2025-06-12 17:27:00 +08:00
6 changed files with 235 additions and 37 deletions
Showing only changes of commit a461a5e488 - Show all commits

View File

@ -16,5 +16,5 @@ 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.74:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'
VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:8080'

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.74:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.74:8080'
VITE_APP_BASE_URL = 'http://192.168.18.99:8080'
VITE_APP_UPLOAD_URL = 'http://192.168.18.99:8080'

View File

@ -66,6 +66,13 @@ export function createInspectionResult(data) {
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}`, {

View File

@ -0,0 +1,113 @@
<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"
>
<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';
const props = defineProps({
modelValue: {
type: Array,
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']);
// el-upload file-list
const fileList = computed(() => {
return props.modelValue.map((path, idx) => ({
name: `image_${idx}`,
url: props.ossUrl + path,
uid: `${idx}`,
}));
});
//
const previewShow = ref(false);
const previewList = computed(() => fileList.value.map((item) => item.url));
const previewIndex = ref(0);
//
const customUploadRequest = async ({ file, onSuccess, onError }) => {
const formData = new FormData();
formData.append('file', file);
try {
const res = await CommonUpload(formData);
// { code:200, data: { url: 'relative/path.jpg' } }
onSuccess(res, file);
} catch (err) {
onError(err);
}
};
// modelValue
function handleUploadSuccess(res) {
const relative = res.data?.url;
if (relative) {
const newArr = [...props.modelValue, relative];
emit('update:modelValue', newArr);
}
}
//
function handleRemove(file) {
const fullUrl = file.url;
const relative = fullUrl.replace(props.ossUrl, '');
const newArr = props.modelValue.filter((path) => path !== relative);
emit('update:modelValue', newArr);
}
//
function handlePreview(file) {
const idx = fileList.value.findIndex((item) => item.uid === file.uid);
if (idx !== -1) {
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

@ -2,7 +2,7 @@ import Layout from '@/layouts/index.vue';
// import annualplanRouters from './annualplan';
import statisticsRoutes from './statisticAnalysis';
import landsRoutes from './lands';
import dictRoutes from './dict';
// import dictRoutes from './dict';
export default [
{
@ -48,7 +48,7 @@ export default [
// ...annualplanRouters,
...landsRoutes,
// ...statisticsRoutes,
...dictRoutes,
// ...dictRoutes,
],
},
];

View File

@ -15,7 +15,7 @@
<el-button type="success" icon="download" @click="handleExport">导出</el-button>
</template>
<template #menu="scope">
<custom-table-operate :actions="crudOptions.actions" :data="scope" />
<custom-table-operate :actions="getActions(scope.row)" :data="scope" />
</template>
</avue-crud>
@ -71,7 +71,7 @@
<div v-for="(form, index) in illegalForms" :key="index" class="form-container">
<div class="form-header">
<span>记录 #{{ index + 1 }}</span>
<el-button v-if="illegalForms.length > 1" type="danger" icon="el-icon-delete" circle size="mini" @click="handleRemoveForm(index)" />
<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">
@ -90,29 +90,27 @@
</el-form-item>
<el-form-item label="是否违法" prop="illegalFlag">
<el-radio-group v-model="form.illegalFlag">
<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-form-item label="违法时间" prop="illegalDate">
<el-date-picker v-model="form.illegalDate" type="datetime" placeholder="选择违法时间" />
<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 label="违法图片" prop="illegalImages">
<el-upload
action="/upload"
list-type="picture-card"
:on-success="(res, file) => handleUploadSuccess(res, file, index)"
:on-error="handleUploadError"
:on-preview="handlePreview"
:on-remove="(file, fileList) => handleRemove(file, fileList, index)"
>
<i class="el-icon-plus"></i>
</el-upload>
<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 :span="12">
<el-col v-if="form.illegalFlag === '1'" :span="12">
<el-form-item label="违法类型" prop="illegalType">
<url-select
v-model="form.illegalTypeCode"
@ -121,10 +119,17 @@
label-key="dictLabel"
value-key="dictValue"
:response-parser="(res) => res.data"
:disabled="isReadonlyRegist || form.illegalFlag === 0"
/>
</el-form-item>
<el-form-item label="违法行为描述" prop="desc">
<el-input v-model="form.desc" :autosize="{ minRows: 2, maxRows: 6 }" type="textarea" placeholder="请输入违法行为描述" />
<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>
@ -150,6 +155,7 @@ import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
import { CRUD_OPTIONS } from '@/config';
import { ElMessage } from 'element-plus';
import { useActionPermissions } from '@/hooks/useActionPermissions';
import { useUserStore } from '@/store/modules/user';
import {
createLandInspection,
deleteLandInspection,
@ -159,6 +165,7 @@ import {
fetchLandInspectionList,
exportLandInspection,
createInspectionResult,
createInspectionResultBatch,
deleteInspectionResult,
getInspectionResultDetail,
} from '@/apis/landResourceManagement/landInspection/index';
@ -167,6 +174,9 @@ const dialogTitle = ref('新增');
const visible = ref(false);
const isReadonlyInfo = ref(false);
const isReadonlyRegist = ref(false);
const UserStore = useUserStore();
const user = UserStore.getUserInfo();
console.log('admin 属性:', user.admin);
const pageData = ref({
currentPage: 1,
@ -205,7 +215,7 @@ const illegalForm = ref({
illegalTypeCode: '', //
illegalTypeName: '', //
desc: '', //
img: '111', //
img: '', //
status: '', //
});
const illegalForms = ref([{ ...illegalForm.value }]);
@ -224,7 +234,7 @@ const crudOptions = reactive({
{ label: '巡查对象', prop: 'inspectionTarget' },
{ label: '注意事项', prop: 'notes' },
{ label: '是否违法', prop: 'isIllegal' },
{ label: '状态', prop: 'inspectionStatus' },
{ label: '状态', prop: 'inspectionStatusName' },
],
actions: [
{
@ -322,9 +332,17 @@ const handleView = async (id) => {
const response = await getLandInspectionDetail(id);
taskForm.value = response.data;
const arr = response.data.inspectionResults;
if (arr && Array.isArray(arr)) {
illegalForms.value = arr;
}
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);
@ -394,8 +412,12 @@ const handleViewResult = async (row) => {
};
//
const handleRemoveForm = (index) => {
illegalForms.value.splice(index, 1);
const handleRemoveForm = async (index) => {
if (illegalForms.value[index].id) {
await deleteInspectionResult(illegalForms.value[index].id);
} else {
illegalForms.value.splice(index, 1);
}
};
const handleSubmit = async () => {
@ -420,8 +442,30 @@ const handleSubmit = async () => {
visible.value = false;
await getData();
} else if (dialogTitle.value === '结果登记') {
response = await createInspectionResult(illegalForm.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();
}
//
@ -467,7 +511,13 @@ function handleSizeChange(val) {
//
// ---------------------------------------------------------------------
// 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) },
@ -482,7 +532,7 @@ const allActions = [
// ->
const permissionMap = {
admin: ['operator', 'inspector'], // operator inspector
operator: ['view', 'edit', 'delete', 'publish', 'back', 'rePublish', 'result', 'viewResult'],
operator: ['view', 'edit', 'delete', 'publish', 'back', 'rePublish', 'viewResult'],
inspector: ['result', 'viewResult'],
};
// ->
@ -492,8 +542,36 @@ const statusMap = {
'01': ['viewResult'],
'02': ['view', 'edit', 'delete', 'rePublish'],
};
// 使 Hook
// const { actions } = useActionPermissions(role, status, allActions, permissionMap, statusMap);
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">