交办时间年月日,整改时回显整改人

This commit is contained in:
王利强
2026-06-23 16:58:40 +08:00
parent cc94d3e3e9
commit d4897284e8
588 changed files with 1551 additions and 1393 deletions

View File

@@ -61,14 +61,14 @@
></up-picker>
</view>
<!-- 运行模式 -->
<!-- 模式 -->
<view class="form-item">
<view class="form-label">
<text>运行模式</text>
<text>模式</text>
</view>
<view class="picker-input" @click="showModePicker = true">
<text :class="formData.modeName ? 'picker-value' : 'picker-placeholder'">
{{ formData.modeName || '请选择运行模式' }}
{{ formData.modeName || '请选择模式' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
@@ -81,14 +81,15 @@
></up-picker>
</view>
<!-- 执行人员仅当运行模式为"指定人员"时显示 -->
<view class="form-item" v-if="formData.modeName === '指定人员'">
<!-- 执行人员单人完成模式 -->
<view class="form-item" v-if="formData.modeName === '单人完成'">
<view class="form-label">
<text>执行人员</text>
<text class="required">*</text>
</view>
<view class="picker-input" @click="openExecutorPopup">
<text :class="formData.executorNames ? 'picker-value' : 'picker-placeholder'">
{{ formData.executorNames || '请选择执行人员' }}
{{ formData.executorNames || (formData.deptId ? '请选择执行人员' : '请先选择分派单位') }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
@@ -114,24 +115,34 @@
></up-picker>
</view>
<!-- 工作日开关 -->
<!-- 工作日 -->
<view class="form-item">
<view class="form-label">
<text>工作日开关</text>
<text>工作日</text>
</view>
<view class="switch-row">
<switch :checked="workdaySwitch" @change="onSwitchChange" color="#07C160" />
<view class="weekend-options">
<view
class="weekend-option"
:class="{ active: formData.isWeekend === 1 }"
@click="setIsWeekend(1)"
>包含周末</view>
<view
class="weekend-option"
:class="{ active: formData.isWeekend === 2 }"
@click="setIsWeekend(2)"
>不包含周末</view>
</view>
</view>
<!-- 开始时间 -->
<!-- 计划开始日期 -->
<view class="form-item">
<view class="form-label">
<text>开始时间</text>
<text>计划开始日期</text>
<text class="required">*</text>
</view>
<view class="picker-input" @click="openStartDatePicker">
<text :class="formData.startDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.startDate || '请选择开始时间' }}
{{ formData.startDate || '请选择计划开始日期' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
@@ -140,20 +151,23 @@
mode="date"
v-model="startDateValue"
:minDate="todayMinDate"
:filter="startDateFilter"
@change="onStartDatePickerChange"
@confirm="onStartDateConfirm"
@cancel="showStartDatePicker = false"
@close="showStartDatePicker = false"
></up-datetime-picker>
</view>
<!-- 结束时间 -->
<!-- 计划结束日期 -->
<view class="form-item">
<view class="form-label">
<text>结束时间</text>
<text>计划结束日期</text>
<text class="required">*</text>
</view>
<view class="picker-input" @click="openEndDatePicker">
<text :class="formData.endDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.endDate || '请选择结束时间' }}
{{ formData.endDate || '请选择计划结束日期' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
@@ -162,6 +176,8 @@
mode="date"
v-model="endDateValue"
:minDate="endDateMinDate"
:filter="endDateFilter"
@change="onEndDatePickerChange"
@confirm="onEndDateConfirm"
@cancel="showEndDatePicker = false"
@close="showEndDatePicker = false"
@@ -376,10 +392,10 @@
class="executor-item"
v-for="item in executorList"
:key="item.userId"
@click="toggleExecutorSelect(item)"
@click="selectExecutor(item)"
>
<view class="executor-checkbox" :class="{ 'executor-checkbox-active': selectedExecutorIds.includes(item.userId) }">
<text v-if="selectedExecutorIds.includes(item.userId)" class="cuIcon-check"></text>
<view class="executor-radio" :class="{ 'executor-radio-active': selectedExecutorId === item.userId }">
<view v-if="selectedExecutorId === item.userId" class="executor-radio-dot"></view>
</view>
<view class="executor-info">
<text class="executor-name">{{ item.nickName }}</text>
@@ -398,7 +414,7 @@
<script setup>
import { ref, reactive, computed } from 'vue';
import { getRegulationList, addCheckPoint, detailcheckPoint, deleteCheckPoint, getCheckItemList, getCheckItemListDetail, getDeptUsers, getParentDepts, addCheckTable } from '@/request/api.js';
import { getRegulationList, addCheckPoint, detailcheckPoint, deleteCheckPoint, getCheckItemList, getCheckItemListDetail, getDeptUsers, getDeptChildren, addCheckTable } from '@/request/api.js';
import { onMounted } from 'vue';
// 表单数据
@@ -413,34 +429,128 @@ const formData = reactive({
modeName: '',
selectDeptId: '',
selectDeptName: '',
executorIds: [], // 执行人员ID数组
executorNames: '', // 执行人员名称(显示用)
executorId: '',
executorNames: '',
cycleId: '',
cycleName: '',
isWeekend: 1,
startDate: '',
endDate: ''
});
// 工作日开关(单独定义避免页面闪烁
const workdaySwitch = ref(false);
const onSwitchChange = (e) => {
workdaySwitch.value = e.detail.value;
};
// 日期选择器值
// 日期工具(需放在使用它们的逻辑之前
const getTodayTimestamp = () => {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
};
const now = new Date()
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
}
const todayMinDate = computed(() => getTodayTimestamp());
const formatDate = (timestamp) => {
const date = new Date(timestamp)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// 扁平数据转部门树
const handleTree = (data, id = 'deptId', parentId = 'parentId', children = 'children') => {
const childrenListMap = {}
const tree = []
data.forEach((item) => {
childrenListMap[item[id]] = { ...item, [children]: item[children] || [] }
})
data.forEach((item) => {
const node = childrenListMap[item[id]]
const parentObj = childrenListMap[item[parentId]]
if (!parentObj) {
tree.push(node)
} else {
parentObj[children].push(node)
}
})
return tree
}
const isWeekendDate = (date) => {
const day = date.getDay()
return day === 0 || day === 6
}
const isWeekendDateString = (dateStr) => {
if (!dateStr) return false
const [year, month, day] = String(dateStr).split(' ')[0].split('-').map(Number)
return isWeekendDate(new Date(year, month - 1, day))
}
const isDateDisabled = (dateStr) => {
const todayStr = formatDate(getTodayTimestamp())
if (dateStr < todayStr) return true
if (Number(formData.isWeekend) === 2 && isWeekendDateString(dateStr)) return true
return false
}
const setIsWeekend = (value) => {
formData.isWeekend = value
if (Number(value) === 2) {
if (isWeekendDateString(formData.startDate)) {
formData.startDate = ''
}
if (isWeekendDateString(formData.endDate)) {
formData.endDate = ''
}
}
}
const startPickerContext = ref(getTodayTimestamp())
const endPickerContext = ref(getTodayTimestamp())
const buildWeekendDayFilter = (contextRef) => {
return (type, values) => {
if (Number(formData.isWeekend) !== 2 || type !== 'day') {
return values
}
const current = new Date(contextRef.value)
const year = current.getFullYear()
const month = current.getMonth()
const filtered = values.filter((dayStr) => {
const date = new Date(year, month, Number(dayStr))
return !isWeekendDate(date)
})
return filtered.length > 0 ? filtered : values
}
}
const startDateFilter = computed(() => {
return Number(formData.isWeekend) === 2 ? buildWeekendDayFilter(startPickerContext) : null
})
const endDateFilter = computed(() => {
return Number(formData.isWeekend) === 2 ? buildWeekendDayFilter(endPickerContext) : null
})
const getNextAvailableTimestamp = (timestamp, minTimestamp = getTodayTimestamp()) => {
let date = new Date(Math.max(timestamp, minTimestamp))
let guard = 0
while (guard < 366) {
const isPast = date.getTime() < minTimestamp
const isWeekendBlocked = Number(formData.isWeekend) === 2 && isWeekendDate(date)
if (!isPast && !isWeekendBlocked) {
return date.getTime()
}
date.setDate(date.getDate() + 1)
guard++
}
return date.getTime()
}
const todayMinDate = computed(() => getTodayTimestamp())
const endDateMinDate = computed(() => {
if (formData.startDate) {
const [year, month, day] = formData.startDate.split('-').map(Number);
return new Date(year, month - 1, day).getTime();
const [year, month, day] = formData.startDate.split('-').map(Number)
return new Date(year, month - 1, day + 1).getTime()
}
return getTodayTimestamp();
return getTodayTimestamp()
});
const startDateValue = ref(getTodayTimestamp());
@@ -458,13 +568,13 @@ const showEndDatePicker = ref(false);
// 选择器数据
const deptColumns = ref([['湘西自治州和谐网络科技有限公司', '湘西自治州和谐云大数据科技有限公司', '湘西网络有限公司']]);
const typeColumns = ref([['日常检查', '专项检查', '设备检查']]);
const modeColumns = ref([['全员', '指定人员']]);
const modeColumns = ref([['单人完成', '员']]);
const cycleColumns = ref([['每天一次', '每周一次', '每月一次', '每季度一次']]);
// 执行人员选择器
const showExecutorPopup = ref(false);
const executorList = ref([]);
const selectedExecutorIds = ref([]);
const selectedExecutorId = ref(null);
// 分派单位级联选择器
const deptTree = ref([]);
@@ -473,15 +583,16 @@ const deptCascaderIndexs = ref([0]);
const selectedDeptPath = ref([]); // 选中的路径
// 选择器确认回调
const onDeptConfirm = (e) => {
// 获取最后选中的部门
const lastSelected = selectedDeptPath.value[selectedDeptPath.value.length - 1];
const onDeptConfirm = () => {
const lastSelected = selectedDeptPath.value[selectedDeptPath.value.length - 1]
if (lastSelected) {
formData.deptId = lastSelected.deptId;
formData.deptName = selectedDeptPath.value.map(d => d.deptName).join(' / ');
formData.deptId = lastSelected.deptId
formData.deptName = selectedDeptPath.value.map(d => d.deptName).join(' / ')
clearExecutorSelection()
fetchDeptUsers(lastSelected.deptId)
}
showDeptPicker.value = false;
};
showDeptPicker.value = false
}
// 检查表类型映射:日常检查->1, 专项检查->2, 设备检查->3
const typeMap = {
@@ -500,95 +611,95 @@ const onTypeConfirm = (e) => {
const onModeConfirm = (e) => {
if (e.value && e.value.length > 0) {
formData.modeName = e.value[0];
// 如果切换到全员,清空执行人员选择
formData.modeName = e.value[0]
if (e.value[0] === '全员') {
formData.executorIds = [];
formData.executorNames = '';
selectedExecutorIds.value = [];
clearExecutorSelection()
}
}
showModePicker.value = false;
};
showModePicker.value = false
}
// 获取当前部门用户列表
const fetchDeptUsers = async () => {
const clearExecutorSelection = () => {
formData.executorId = ''
formData.executorNames = ''
selectedExecutorId.value = null
}
const fetchDeptUsers = async (deptId) => {
if (!deptId) {
executorList.value = []
return
}
try {
const res = await getDeptUsers();
const res = await getDeptUsers(deptId)
if (res.code === 0 && res.data) {
executorList.value = res.data || [];
executorList.value = res.data || []
} else {
executorList.value = []
}
} catch (error) {
console.error('获取部门用户失败:', error);
console.error('获取部门用户失败:', error)
executorList.value = []
}
};
}
// 切换执行人员选择
const toggleExecutorSelect = (item) => {
const index = selectedExecutorIds.value.indexOf(item.userId);
if (index > -1) {
selectedExecutorIds.value.splice(index, 1);
} else {
selectedExecutorIds.value.push(item.userId);
}
};
const selectExecutor = (item) => {
selectedExecutorId.value = item.userId
}
// 确认执行人员选择
const confirmExecutorSelect = () => {
formData.executorIds = [...selectedExecutorIds.value];
const selectedUsers = executorList.value.filter(u => selectedExecutorIds.value.includes(u.userId));
formData.executorNames = selectedUsers.map(u => u.nickName).join('、');
showExecutorPopup.value = false;
};
if (!selectedExecutorId.value) {
uni.showToast({ title: '请选择执行人员', icon: 'none' })
return
}
const selectedUser = executorList.value.find(u => u.userId === selectedExecutorId.value)
formData.executorId = selectedExecutorId.value
formData.executorNames = selectedUser?.nickName || selectedUser?.nickname || ''
showExecutorPopup.value = false
}
// 打开执行人员选择弹窗
const openExecutorPopup = () => {
selectedExecutorIds.value = [...formData.executorIds];
showExecutorPopup.value = true;
};
if (!formData.deptId) {
uni.showToast({ title: '请先选择分派单位', icon: 'none' })
return
}
selectedExecutorId.value = formData.executorId || null
fetchDeptUsers(formData.deptId)
showExecutorPopup.value = true
}
// 获取父级部门列表(树形结构)
const fetchParentDepts = async () => {
const fetchDeptChildren = async () => {
try {
const res = await getParentDepts();
const res = await getDeptChildren()
if (res.code === 0 && res.data) {
deptTree.value = res.data;
// 初始化级联选择器第一列
initDeptCascader(res.data);
const list = Array.isArray(res.data) ? res.data : []
deptTree.value = handleTree(list, 'deptId')
initDeptCascader(deptTree.value)
}
} catch (error) {
console.error('获取部门树失败:', error);
console.error('获取部门树失败:', error)
}
};
}
// 初始化部门级联选择器
const initDeptCascader = (data) => {
if (!data) return;
// 第一列数据
const firstColumn = buildDeptColumn(data);
deptCascaderColumns.value = [firstColumn];
deptCascaderIndexs.value = [0];
selectedDeptPath.value = [data];
// 如果有子级,添加下一列
if (data.children && data.children.length > 0) {
const secondColumn = data.children.map(item => ({ text: item.deptName, ...item }));
deptCascaderColumns.value.push(secondColumn);
deptCascaderIndexs.value.push(0);
selectedDeptPath.value.push(data.children[0]);
// 检查是否还有下一级
if (data.children[0].children && data.children[0].children.length > 0) {
const thirdColumn = data.children[0].children.map(item => ({ text: item.deptName, ...item }));
deptCascaderColumns.value.push(thirdColumn);
deptCascaderIndexs.value.push(0);
selectedDeptPath.value.push(data.children[0].children[0]);
}
}
};
const initDeptCascader = (treeData) => {
const roots = Array.isArray(treeData) ? treeData : (treeData ? [treeData] : [])
if (roots.length === 0) return
// 构建部门列数据
const buildDeptColumn = (data) => {
return [{ text: data.deptName, ...data }];
};
const firstColumn = roots.map(item => ({ text: item.deptName, ...item }))
deptCascaderColumns.value = [firstColumn]
deptCascaderIndexs.value = [0]
selectedDeptPath.value = [roots[0]]
let current = roots[0]
while (current.children && current.children.length > 0) {
const nextColumn = current.children.map(item => ({ text: item.deptName, ...item }))
deptCascaderColumns.value.push(nextColumn)
deptCascaderIndexs.value.push(0)
selectedDeptPath.value.push(current.children[0])
current = current.children[0]
}
}
// 部门级联选择器列改变
const onDeptCascaderChange = (e) => {
@@ -636,15 +747,6 @@ const onCycleConfirm = (e) => {
showCyclePicker.value = false;
};
// 日期格式化(年月日)
const formatDate = (timestamp) => {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
const parseDateValue = (dateStr) => {
if (!dateStr) return 0;
const [datePart] = dateStr.split(' ');
@@ -653,45 +755,70 @@ const parseDateValue = (dateStr) => {
};
const openStartDatePicker = () => {
const today = getTodayTimestamp();
if (startDateValue.value < today) {
startDateValue.value = today;
}
showStartDatePicker.value = true;
};
const candidate = formData.startDate
? parseDateValue(formData.startDate)
: getTodayTimestamp()
startDateValue.value = getNextAvailableTimestamp(candidate)
startPickerContext.value = startDateValue.value
showStartDatePicker.value = true
}
const openEndDatePicker = () => {
const minDate = endDateMinDate.value;
if (endDateValue.value < minDate) {
endDateValue.value = minDate;
if (!formData.startDate) {
uni.showToast({ title: '请先选择计划开始日期', icon: 'none' })
return
}
showEndDatePicker.value = true;
};
const minDate = endDateMinDate.value
const candidate = formData.endDate
? parseDateValue(formData.endDate)
: minDate
endDateValue.value = getNextAvailableTimestamp(candidate, minDate)
endPickerContext.value = endDateValue.value
showEndDatePicker.value = true
}
const onStartDatePickerChange = (e) => {
startPickerContext.value = e.value
startDateValue.value = e.value
}
const onEndDatePickerChange = (e) => {
endPickerContext.value = e.value
endDateValue.value = e.value
}
const onStartDateConfirm = (e) => {
const selectedDate = formatDate(e.value);
const todayStr = formatDate(getTodayTimestamp());
if (selectedDate < todayStr) {
uni.showToast({ title: '开始时间不能早于今天', icon: 'none' });
return;
const selectedDate = formatDate(e.value)
if (isDateDisabled(selectedDate)) {
uni.showToast({
title: Number(formData.isWeekend) === 2 ? '不能选择周末或今天之前的日期' : '开始日期不能早于今天',
icon: 'none'
})
return
}
if (formData.endDate && parseDateValue(selectedDate) > parseDateValue(formData.endDate)) {
uni.showToast({ title: '开始时间不能晚于结束时间', icon: 'none' });
return;
if (formData.endDate && parseDateValue(selectedDate) >= parseDateValue(formData.endDate)) {
formData.endDate = ''
}
formData.startDate = selectedDate;
showStartDatePicker.value = false;
};
formData.startDate = selectedDate
showStartDatePicker.value = false
}
const onEndDateConfirm = (e) => {
const selectedDate = formatDate(e.value);
if (formData.startDate && parseDateValue(selectedDate) < parseDateValue(formData.startDate)) {
uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' });
return;
const selectedDate = formatDate(e.value)
if (isDateDisabled(selectedDate)) {
uni.showToast({
title: Number(formData.isWeekend) === 2 ? '不能选择周末或今天之前的日期' : '结束日期不能早于今天',
icon: 'none'
})
return
}
formData.endDate = selectedDate;
showEndDatePicker.value = false;
};
if (formData.startDate && parseDateValue(selectedDate) <= parseDateValue(formData.startDate)) {
uni.showToast({ title: '计划结束日期必须晚于计划开始日期', icon: 'none' })
return
}
formData.endDate = selectedDate
showEndDatePicker.value = false
}
// 手动添加的检查项列表
const manualCheckItems = ref([]);
@@ -1125,11 +1252,10 @@ const handleSave = async () => {
return;
}
if (!formData.modeName) {
uni.showToast({ title: '请选择运行模式', icon: 'none' });
uni.showToast({ title: '请选择模式', icon: 'none' });
return;
}
// 如果选择指定人员,需要选择执行人员
if (formData.modeName === '指定人员' && formData.executorIds.length === 0) {
if (formData.modeName === '单人完成' && !formData.executorId) {
uni.showToast({ title: '请选择执行人员', icon: 'none' });
return;
}
@@ -1138,11 +1264,11 @@ const handleSave = async () => {
return;
}
if (!formData.startDate || !formData.endDate) {
uni.showToast({ title: '请选择计划时间', icon: 'none' });
uni.showToast({ title: '请选择计划日期', icon: 'none' });
return;
}
if (parseDateValue(formData.endDate) < parseDateValue(formData.startDate)) {
uni.showToast({ title: '结束时间不能早于开始时间', icon: 'none' });
if (parseDateValue(formData.endDate) <= parseDateValue(formData.startDate)) {
uni.showToast({ title: '计划结束日期必须晚于计划开始日期', icon: 'none' });
return;
}
@@ -1164,9 +1290,9 @@ const handleSave = async () => {
return;
}
// 运行模式映射:指定人员=1全员=2
// 运行模式映射:单人完成=1全员=2
const runModeMap = {
'指定人员': 1,
'单人完成': 1,
'全员': 2
};
@@ -1196,15 +1322,13 @@ const handleSave = async () => {
checkPointIds: items,
itemIds: itemIds, // 从检查库选择的库id数组
cycle: cycleMap[formData.cycleName] || 1,
isWeekend: workdaySwitch.value ? 1 : 2,
isWeekend: formData.isWeekend,
planStartTime: `${formData.startDate} 00:00:00`,
planEndTime: `${formData.endDate} 23:59:59`
planEndTime: `${formData.endDate} 00:00:00`
};
// 如果是指定人员模式添加执行人员id
if (formData.modeName === '指定人员' && formData.executorIds.length > 0) {
params.executorId = formData.executorIds[0]; // 如果只能选一个执行人员
// 如果支持多个执行人员可以改为params.executorIds = formData.executorIds;
if (formData.modeName === '单人完成' && formData.executorId) {
params.executorId = formData.executorId;
}
try {
@@ -1229,8 +1353,7 @@ const handleSave = async () => {
// 页面加载时获取数据
onMounted(() => {
fetchDeptUsers();
fetchParentDepts();
fetchDeptChildren();
});
</script>
@@ -1305,13 +1428,36 @@ onMounted(() => {
}
}
// 开关行
// 开关行(保留兼容)
.switch-row {
display: flex;
align-items: center;
gap: 30rpx;
}
.weekend-options {
display: flex;
gap: 20rpx;
}
.weekend-option {
flex: 1;
height: 72rpx;
line-height: 72rpx;
text-align: center;
font-size: 28rpx;
color: #666;
background: #fff;
border: 2rpx solid #E5E5E5;
border-radius: 8rpx;
&.active {
color: #2667E9;
border-color: #2667E9;
background: #F0F6FF;
}
}
// 空状态提示
.empty-check-tip {
text-align: center;
@@ -1732,11 +1878,11 @@ onMounted(() => {
border-bottom: 1rpx solid #f0f0f0;
}
.executor-checkbox {
.executor-radio {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ccc;
border-radius: 8rpx;
border-radius: 50%;
margin-right: 20rpx;
display: flex;
align-items: center;
@@ -1744,10 +1890,15 @@ onMounted(() => {
flex-shrink: 0;
}
.executor-checkbox-active {
background: #2667E9;
.executor-radio-active {
border-color: #2667E9;
color: #fff;
}
.executor-radio-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background: #2667E9;
}
.executor-info {