一单四制优化及加入工作流

This commit is contained in:
王利强
2026-07-27 09:27:22 +08:00
parent 455fd4fc07
commit 7c3285ed5a
725 changed files with 19668 additions and 2921 deletions

View File

@@ -25,17 +25,18 @@
<view class="user-info">
<view class="user-dept text-bold">{{ userInfo.deptName || '未知部门' }}</view>
<view class="user-phone">手机号{{ userInfo.phone || '未绑定' }}</view>
<view v-if="displayIdentityName" class="user-identity">当前身份{{ displayIdentityName }}</view>
</view>
<!-- <view class="switch-btn">
<view class="switch-btn" @click="goSwitchIdentity">
<text>切换</text>
<u-icon name="list" color="#285CE9" size="14"></u-icon>
</view> -->
</view>
</view>
</view>
<view class="padding page-content">
<!-- 功能菜单 -->
<view class="menu-card">
<!-- 功能菜单approval 角色不展示 -->
<view v-if="!isApprovalRole" class="menu-card">
<view class="menu-grid">
<view class="menu-item" v-for="(item, index) in infoList" :key="index" @click="handleMenuClick(item)">
<image class="menu-icon" :src="item.src"></image>
@@ -44,7 +45,7 @@
</view>
</view>
<!-- 我的检查计划 -->
<view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -30rpx; margin-right: -30rpx;">
<view v-if="!isApprovalRole" class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -30rpx; margin-right: -30rpx;">
<view class="flex margin-bottom-xl align-center justify-between">
<view class="text-bold margin-left-xs" style="font-size: 32rpx;">我的检查计划</view>
<!-- <button class="cu-btn round sm line-blue inspect-list-btn" @click="goInspectList">检查列表</button> -->
@@ -121,8 +122,8 @@
加载更多
</view>
</view>
<!-- 我的隐患 -->
<view class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -40rpx; margin-right: -40rpx;">
<!-- 我的隐患排查 -->
<view v-if="!isApprovalRole" class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -40rpx; margin-right: -40rpx;">
<view class="flex margin-bottom">
<!-- <view class="border-tite"></view> -->
<view class="text-bold margin-left-xs" style="font-size: 32rpx;">我的隐患排查</view>
@@ -140,11 +141,11 @@
</view>
</scroll-view>
<!-- 无数据提示 -->
<view v-if="filteredDangerData.length === 0" class="text-center text-gray padding">
<view v-if="hiddenDangerData.length === 0 && !hiddenDangerLoading" class="text-center text-gray padding">
暂无隐患数据
</view>
<!-- 隐患卡片列表 -->
<view class="danger-card margin-top" v-for="(item, index) in filteredDangerData" :key="item.hazardId">
<view class="danger-card margin-top" v-for="(item, index) in hiddenDangerData" :key="item.hazardId">
<!-- 标题行图标+标题 + 等级标签 -->
<view class="flex justify-between align-center">
<view class="flex align-center" style="flex: 1; min-width: 0; margin-right: 24rpx;">
@@ -183,10 +184,74 @@
class="round cu-btn bg-blue" @click.stop="goRectification(item)">立即整改</button>
<button v-if="item.statusName === '待验收' && item.canEdit"
class="round cu-btn light bg-blue" @click.stop="editRectification(item)">编辑整改信息</button>
<button v-if="item.statusName === '待验收' && canAcceptance"
<button v-if="canShowAcceptanceButton(item, currentRoleKey)"
class="round cu-btn bg-blue" @click.stop="goAcceptance(item)">立即验收</button>
<button v-if="item.statusName === '待交办'"
class="round cu-btn bg-blue" @click.stop="assignHazard(item)">隐患交办</button>
<button v-if="canShowWriteoffApplyButton(item)"
class="round cu-btn bg-blue" @click.stop="goWriteoffApply(item)">销号申请</button>
<button v-if="canShowWriteoffApprovalButton(item, currentRoleKey)"
class="round cu-btn bg-blue" @click.stop="goWriteoffApproval(item)">销号审批</button>
</view>
</view>
<u-loadmore
v-if="hiddenDangerData.length > 0"
:status="hiddenDangerLoadStatus"
style="margin-top: 20rpx; margin-bottom: 20rpx;"
/>
</view>
<!-- 我的工作台approval 角色专属 -->
<view v-if="isApprovalRole" class="bg-white margin-top radius" style="padding: 40rpx; margin-left: -40rpx; margin-right: -40rpx;">
<view class="flex margin-bottom">
<view class="text-bold margin-left-xs" style="font-size: 32rpx;">我的工作台</view>
</view>
<view class="danger-tab-list workbench-tab-list">
<view
class="danger-tab-item"
:class="{ 'danger-tab-active': activeWorkbenchTab === index }"
v-for="(tab, index) in workbenchTabs"
:key="tab.value"
@click="switchWorkbenchTab(index)"
>{{ tab.label }}</view>
</view>
<view v-if="workbenchLoading" class="text-center text-gray padding">加载中...</view>
<view v-else-if="workbenchList.length === 0" class="text-center text-gray padding">
暂无数据
</view>
<view class="danger-card margin-top" v-for="item in workbenchList" :key="getWorkbenchItemKey(item)">
<view class="flex justify-between align-center">
<view class="flex align-center" style="flex: 1; min-width: 0; margin-right: 24rpx;">
<text class="cuIcon-infofill text-blue" style="font-size: 36rpx; margin-right: 12rpx; flex-shrink: 0;"></text>
<view class="text-bold" style="font-size: 30rpx; word-break: break-all; flex: 1;">{{ item.title }}</view>
</view>
<view class="level-tag" :class="{
'level-minor': item.levelName === '轻微隐患',
'level-normal': item.levelName === '一般隐患',
'level-major': item.levelName === '重大隐患'
}">{{ item.levelName }}</view>
</view>
<view class="text-gray margin-top-sm" style="font-size: 26rpx; padding-left: 48rpx;">{{ item.address }}</view>
<view style="height: 1rpx; background: #EEEEEE; margin: 20rpx 0;"></view>
<view class="danger-info-row">
<text class="text-gray">隐患来源</text>
<text style="color: #333333;">{{ item.source }}</text>
</view>
<view class="danger-info-row">
<text class="text-gray">隐患状态</text>
<text style="color: #333333;">{{ item.statusName }}</text>
</view>
<view class="danger-info-row">
<text class="text-gray">发现时间</text>
<text style="color: #333333;">{{ item.createdAt }}</text>
</view>
<view class="flex justify-end danger-card-actions">
<button class="round cu-btn light bg-blue" @click.stop="viewHazardDetail(item)">查看详情</button>
<button
v-if="activeWorkbenchTab === 0"
class="round cu-btn bg-blue"
@click.stop="goLeaderApproval(item)"
>领导批示</button>
</view>
</view>
</view>
@@ -197,10 +262,24 @@
<script setup>
import { ref, reactive, computed } from 'vue';
// import { onLoad } from '@dcloudio/uni-app';
import {getCheckPlanList, getHiddenDangerList} from '@/request/api.js'
import {getCheckPlanList, getHiddenDangerList, getFlowTodoList, getFlowDoneList} from '@/request/api.js'
import { getProfileDetail } from '@/request/three_one_api/info.js';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { onLoad, onShow, onReachBottom } from '@dcloudio/uni-app';
import { toImageUrl } from '@/request/request.js';
import { applyProfileToUserInfo, applyStoredUserInfo, resolveIdentityName, resolveUserRoleKey } from '@/utils/userInfo.js';
import {
buildRectificationUrl,
buildEditRectificationUrl,
buildAssignmentUrl,
buildAcceptanceUrl,
buildAcceptanceApprovalUrl,
buildWriteoffApplyUrl,
buildWriteoffApprovalUrl,
buildLeaderWriteoffApprovalUrl,
canShowAcceptanceButton,
canShowWriteoffApplyButton,
canShowWriteoffApprovalButton
} from '@/utils/hazardNav.js';
const loading = ref(true);
const defaultAvatar = '/static/my/default_avater.png';
@@ -214,13 +293,31 @@
deptName: '',
role: '',
avatar: '',
phone: ''
phone: '',
identityName: '',
userIdentity: null
});
// 获取用户角色判断是否有验收权限admin或manage才能验收
const canAcceptance = computed(() => {
return userInfo.role === 'admin' || userInfo.role === 'manage';
// 当前身份角色(优先 userIdentity.roleKey
const currentRoleKey = computed(() => resolveUserRoleKey(userInfo));
const isApprovalRole = computed(() => currentRoleKey.value === 'approval');
// 展示用身份名称(兼容接口字段与本地缓存)
const displayIdentityName = computed(() => {
return resolveIdentityName(userInfo, userInfo.userIdentity);
});
const loadUserInfoFromStorage = () => {
try {
const storedUserInfo = uni.getStorageSync('userInfo');
if (storedUserInfo) {
applyStoredUserInfo(userInfo, JSON.parse(storedUserInfo));
}
} catch (storageError) {
console.error('从本地存储获取用户信息失败:', storageError);
}
};
// 获取图片完整URL用于显示
const getImageUrl = (path) => {
@@ -233,37 +330,11 @@
try {
const res = await getProfileDetail();
if (res.code === 0 && res.data) {
userInfo.userId = res.data.userId || '';
userInfo.username = res.data.userName || '';
userInfo.nickName = res.data.nickName || '';
userInfo.deptId = res.data.deptId || '';
userInfo.deptName = res.data.deptName || '';
userInfo.avatar = res.data.avatar || '';
userInfo.phone = res.data.phonenumber || res.data.phone || '';
// 获取角色信息
if (res.data.roles && res.data.roles.length > 0) {
userInfo.role = res.data.roles[0].roleKey || '';
}
applyProfileToUserInfo(userInfo, res.data);
}
} catch (e) {
console.error('获取用户信息失败:', e);
// 如果接口失败,尝试从本地存储获取
try {
const storedUserInfo = uni.getStorageSync('userInfo');
if (storedUserInfo) {
const info = JSON.parse(storedUserInfo);
userInfo.userId = info.userId || '';
userInfo.username = info.username || '';
userInfo.nickName = info.nickName || '';
userInfo.deptId = info.deptId || '';
userInfo.deptName = info.deptName || '';
userInfo.role = info.role || '';
userInfo.avatar = info.avatar || '';
userInfo.phone = info.phone || '';
}
} catch (storageError) {
console.error('从本地存储获取用户信息失败:', storageError);
}
loadUserInfoFromStorage();
}
};
// 全部菜单项
@@ -307,7 +378,10 @@
// 根据角色动态展示菜单
const infoList = computed(() => {
if (userInfo.role === 'common') {
if (currentRoleKey.value === 'approval') {
return [];
}
if (currentRoleKey.value === 'common') {
return allMenuList.filter(item => commonMenuNames.includes(item.name));
}
// admin、manage 及其他角色展示全部菜单
@@ -473,16 +547,22 @@
// });
// 页面每次显示时都会加载数据
onShow(() => {
getUserInfo();
getCheckPlanLists();
getHiddenDangerLists();
loadUserInfoFromStorage();
getUserInfo().then(() => {
if (isApprovalRole.value) {
fetchWorkbenchList();
} else {
getCheckPlanLists();
resetHiddenDangerList();
fetchHiddenDangerList();
}
});
});
//我的隐患排查
const hiddenDangerParams = ref({
pageNum: 1,
pageSize: 10,
name: ''
});
const HAZARD_PAGE_SIZE = 10;
const hiddenDangerPageNum = ref(1);
const hiddenDangerLoading = ref(false);
const hiddenDangerLoadStatus = ref('loadmore');
const hiddenDangerData = ref([]);
// 隐患排查 Tab 筛选
@@ -496,92 +576,224 @@
]);
const activeDangerTab = ref(0);
// 切换 Tab
const buildHiddenDangerListParams = () => {
const params = {
pageNum: hiddenDangerPageNum.value,
pageSize: HAZARD_PAGE_SIZE
};
const activeTab = dangerTabs.value[activeDangerTab.value];
if (activeTab?.value != null) {
params.status = activeTab.value;
}
return params;
};
const resetHiddenDangerList = () => {
hiddenDangerPageNum.value = 1;
hiddenDangerData.value = [];
hiddenDangerLoadStatus.value = 'loadmore';
};
// 切换 Tab带 status 重新请求接口
const switchDangerTab = (index) => {
if (activeDangerTab.value === index) return;
activeDangerTab.value = index;
resetHiddenDangerList();
fetchHiddenDangerList();
};
// 根据 Tab 过滤隐患数据
const filteredDangerData = computed(() => {
const activeTab = dangerTabs.value[activeDangerTab.value];
if (!activeTab || activeTab.value === null) {
return hiddenDangerData.value;
const fetchHiddenDangerList = async () => {
if (hiddenDangerLoading.value) return;
if (hiddenDangerPageNum.value > 1 && hiddenDangerLoadStatus.value === 'nomore') return;
hiddenDangerLoading.value = true;
if (hiddenDangerPageNum.value > 1) {
hiddenDangerLoadStatus.value = 'loading';
}
return hiddenDangerData.value.filter(item => item.status === activeTab.value);
});
const getHiddenDangerLists = async () => {
try {
const res = await getHiddenDangerList(hiddenDangerParams.value);
console.log(res);
const res = await getHiddenDangerList(buildHiddenDangerListParams());
if (res.code === 0) {
hiddenDangerData.value = res.data.records;
console.log(hiddenDangerData.value,1111);
const records = res.data?.records || [];
const total = Number(res.data?.total ?? 0);
if (hiddenDangerPageNum.value === 1) {
hiddenDangerData.value = records;
} else {
hiddenDangerData.value = [...hiddenDangerData.value, ...records];
}
if (hiddenDangerData.value.length >= total || records.length < HAZARD_PAGE_SIZE) {
hiddenDangerLoadStatus.value = 'nomore';
} else {
hiddenDangerLoadStatus.value = 'loadmore';
}
} else {
if (hiddenDangerPageNum.value === 1) {
hiddenDangerData.value = [];
}
hiddenDangerLoadStatus.value = 'nomore';
}
} catch (error) {
console.error(error);
if (hiddenDangerPageNum.value > 1) {
hiddenDangerPageNum.value--;
}
hiddenDangerLoadStatus.value = 'loadmore';
} finally {
hiddenDangerLoading.value = false;
loading.value = false;
}
};
const loadMoreHiddenDangerList = () => {
if (hiddenDangerLoadStatus.value !== 'loadmore' || hiddenDangerLoading.value) return;
hiddenDangerPageNum.value++;
fetchHiddenDangerList();
};
// 页面加载时调用接口
onLoad(() => {
getHiddenDangerLists();
if (!isApprovalRole.value) {
resetHiddenDangerList();
fetchHiddenDangerList();
}
});
onReachBottom(() => {
if (isApprovalRole.value) return;
loadMoreHiddenDangerList();
});
// ========== 我的工作台approval 角色) ==========
const workbenchTabs = [
{ label: '待办', value: 'todo' },
{ label: '已办', value: 'done' }
];
const activeWorkbenchTab = ref(0);
const workbenchList = ref([]);
const workbenchLoading = ref(false);
const normalizeWorkbenchItem = (item) => {
if (!item) return {};
const hazard = item.hazard || item.hazardInfo || {};
return {
...item,
hazardId: item.hazardId ?? hazard.hazardId ?? '',
assignId: item.assignId ?? hazard.assignId ?? '',
rectifyId: item.rectifyId ?? hazard.rectifyId ?? '',
taskId: item.taskId ?? item.flowTaskId ?? item.currentTaskId ?? '',
taskKey: item.taskKey ?? item.flowTaskKey ?? item.currentTaskKey ?? '',
title: item.title ?? hazard.title ?? item.hazardTitle ?? '',
address: item.address ?? hazard.address ?? '',
source: item.source ?? hazard.source ?? item.hazardSourceName ?? '',
statusName: item.statusName ?? hazard.statusName ?? item.hazardStatusName ?? hazard.hazardStatusName ?? item.taskName ?? '',
hazardStatus: item.hazardStatus ?? hazard.hazardStatus ?? item.status ?? hazard.status ?? '',
createdAt: item.discoveredAt ?? hazard.discoveredAt ?? '',
levelName: item.levelName ?? hazard.levelName ?? ''
};
};
const resolveWorkbenchRecords = (data) => {
if (!data) return [];
if (Array.isArray(data)) return data;
if (Array.isArray(data.records)) return data.records;
if (Array.isArray(data.list)) return data.list;
return [];
};
const getWorkbenchItemKey = (item) => {
const taskId = item.taskId || item.flowTaskId || '';
const hazardId = item.hazardId || '';
if (taskId && hazardId) return `${taskId}-${hazardId}`;
return taskId || hazardId || item.rectifyId || JSON.stringify(item);
};
const switchWorkbenchTab = (index) => {
if (activeWorkbenchTab.value === index) return;
activeWorkbenchTab.value = index;
fetchWorkbenchList();
};
const fetchWorkbenchList = async () => {
workbenchLoading.value = true;
try {
const isTodo = activeWorkbenchTab.value === 0;
const res = isTodo ? await getFlowTodoList() : await getFlowDoneList();
if (res.code === 0) {
workbenchList.value = resolveWorkbenchRecords(res.data).map(normalizeWorkbenchItem);
} else {
workbenchList.value = [];
uni.showToast({ title: res.msg || '获取工作台数据失败', icon: 'none' });
}
} catch (error) {
console.error('获取工作台数据失败:', error);
workbenchList.value = [];
uni.showToast({ title: '获取工作台数据失败', icon: 'none' });
} finally {
workbenchLoading.value = false;
loading.value = false;
}
};
const goLeaderApproval = (item) => {
const hazardStatus = Number(item.hazardStatus);
const statusName = item.statusName || item.hazardStatusName || '';
if (hazardStatus === 4 || statusName === '待销号') {
uni.navigateTo({ url: buildLeaderWriteoffApprovalUrl(item) });
return;
}
if (hazardStatus === 3 || statusName === '待验收') {
uni.navigateTo({ url: buildAcceptanceApprovalUrl(item) });
return;
}
uni.showToast({ title: '当前状态不支持领导审批', icon: 'none' });
};
// ========== 隐患排查相关跳转函数 ==========
// 查看隐患详情
// 查看隐患详情(流程链)
const viewHazardDetail = (item) => {
uni.navigateTo({
url: `/pages/hiddendanger/detail2?hazardId=${item.hazardId}&assignId=${item.assignId || ''}`
url: `/pages/hiddendanger/process-chain?hazardId=${item.hazardId}`
})
}
// 立即整改(待整改状态)
const goRectification = (item) => {
let url = `/pages/hiddendanger/rectification?hazardId=${item.hazardId}&assignId=${item.assignId}`
if (item.deadline) {
url += `&deadline=${encodeURIComponent(item.deadline)}`
}
if (item.assigneeId) {
url += `&assigneeId=${item.assigneeId}`
}
if (item.assigneeName) {
url += `&assigneeName=${encodeURIComponent(item.assigneeName)}`
}
uni.navigateTo({ url })
uni.navigateTo({ url: buildRectificationUrl(item) });
}
// 编辑整改信息(待验收状态)
const editRectification = (item) => {
uni.navigateTo({
url: `/pages/hiddendanger/rectification?rectifyId=${item.rectifyId}&isEdit=1`
})
uni.navigateTo({ url: buildEditRectificationUrl(item) });
}
// 立即验收
const goAcceptance = (item) => {
uni.navigateTo({
url: `/pages/hiddendanger/acceptance?hazardId=${item.hazardId}&assignId=${item.assignId}&rectifyId=${item.rectifyId}`
})
uni.navigateTo({ url: buildAcceptanceUrl(item) });
}
// 隐患交办
const assignHazard = (item) => {
uni.navigateTo({
url: `/pages/hiddendanger/assignment?hazardId=${item.hazardId}&assignId=${item.assignId}`
})
uni.navigateTo({ url: buildAssignmentUrl(item) });
}
// 销号申请
const goWriteoffApply = (item) => {
uni.navigateTo({ url: buildWriteoffApplyUrl(item) });
};
// 销号审批
const goWriteoffApproval = (item) => {
uni.navigateTo({ url: buildWriteoffApprovalUrl(item) });
};
// 切换账户
const switchAccount = () => {
uni.showToast({
title: '切换账户功能开发中',
icon: 'none'
// 跳转身份切换页
const goSwitchIdentity = () => {
uni.navigateTo({
url: '/pages/personalcenter/identity'
});
}
};
</script>
<style lang="scss" scoped>
@@ -642,13 +854,22 @@
color: rgba(255, 255, 255, 0.9);
margin-top: 10rpx;
}
.user-identity {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.85);
margin-top: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.switch-btn {
display: flex;
align-items: center;
background: #fff;
padding: 16rpx 24rpx;
padding: 8rpx 24rpx;
border-radius: 30rpx;
color: #285CE9;
font-size: 26rpx;
@@ -896,6 +1117,10 @@
margin-left: 10rpx;
}
.workbench-tab-list {
margin-bottom: 20rpx;
}
.danger-tab-item {
font-size: 28rpx;
color: #666;