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

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

@@ -2,7 +2,17 @@
<view class="page padding">
<!-- 检查表列表 -->
<view class="checklist-card" v-for="item in list" :key="item.id">
<view class="card-name">{{ item.name }}</view>
<view class="card-main">
<view class="card-info">
<view class="card-name">{{ item.name }}</view>
<view class="card-meta" v-if="item.deptName || item.pointCount">
<text v-if="item.deptName">{{ item.deptName }}</text>
<text v-if="item.deptName && item.pointCount" class="meta-dot">·</text>
<text v-if="item.pointCount">检查项 {{ item.pointCount }} </text>
</view>
</view>
<button class="detail-btn" @click.stop="goToDetail(item)">详情</button>
</view>
</view>
<!-- 空状态 -->
@@ -12,7 +22,6 @@
<!-- 新增按钮 -->
<button class="add-btn" @click="goToAdd">
<!-- <text class="cuIcon-add"></text> -->
<text>新增检查表</text>
</button>
</view>
@@ -25,28 +34,32 @@ import { getCheckTableList } from '@/request/api.js'
const list = ref([])
// 获取检查表列表
const fetchList = async () => {
try {
const res = await getCheckTableList({ pageNum: 1, pageSize: 100 });
const res = await getCheckTableList({ pageNum: 1, pageSize: 100 })
if (res.code === 0) {
list.value = res.data.records || [];
list.value = res.data.records || []
}
} catch (error) {
console.error('获取检查表列表失败:', error);
console.error('获取检查表列表失败:', error)
}
}
// 跳转到新增页面
const goToAdd = () => {
uni.navigateTo({
url: '/pages/editchecklist/editchecklist'
})
}
// 每次显示页面时刷新数据
const goToDetail = (item) => {
if (!item?.id) return
uni.navigateTo({
url: `/pages/checklist/detail?id=${item.id}`
})
}
onShow(() => {
fetchList();
fetchList()
})
</script>
@@ -60,14 +73,64 @@ onShow(() => {
.checklist-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
padding: 24rpx 28rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.card-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
.card-main {
display: flex;
align-items: center;
gap: 20rpx;
}
.card-info {
flex: 1;
min-width: 0;
}
.card-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
}
.card-meta {
margin-top: 10rpx;
font-size: 24rpx;
color: #999;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.meta-dot {
margin: 0 8rpx;
}
.detail-btn {
flex-shrink: 0;
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
padding: 0;
margin: 0;
background: #fff;
border: 2rpx solid #2667E9;
border-radius: 30rpx;
color: #2667E9;
font-size: 26rpx;
&::after {
border: none;
}
}
@@ -92,10 +155,9 @@ onShow(() => {
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(102, 126, 234, 0.4);
.cuIcon-add {
margin-right: 10rpx;
font-size: 36rpx;
&::after {
border: none;
}
}
</style>
</style>

321
pages/checklist/detail.vue Normal file
View File

@@ -0,0 +1,321 @@
<template>
<view class="page">
<view v-if="loading" class="loading-wrap">
<text class="loading-text">加载中...</text>
</view>
<template v-else>
<!-- 基本信息 -->
<view class="section-card">
<view class="section-title">基本信息</view>
<view class="info-row">
<text class="info-label">检查表名称</text>
<text class="info-value">{{ detail.name || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">分派单位</text>
<text class="info-value">{{ detail.deptName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">检查表类型</text>
<text class="info-value">{{ typeLabel }}</text>
</view>
<view class="info-row">
<text class="info-label">模式</text>
<text class="info-value">{{ runModeLabel }}</text>
</view>
<view class="info-row">
<text class="info-label">执行人员</text>
<text class="info-value">{{ executorLabel }}</text>
</view>
<view class="info-row">
<text class="info-label">循环周期</text>
<text class="info-value">{{ cycleLabel }}</text>
</view>
<view class="info-row column">
<text class="info-label">检查说明</text>
<view class="info-block">
<rich-text v-if="detail.description" :nodes="detail.description"></rich-text>
<text v-else class="info-empty">-</text>
</view>
</view>
</view>
<!-- 关联表项 -->
<view class="section-card">
<view class="section-title">关联表项</view>
<view class="related-box">
<image class="related-icon" src="/static/jianchabiao/biaodan.svg" mode="aspectFit"></image>
<text class="related-text">{{ detail.itemNames || '-' }}</text>
</view>
</view>
<!-- 检查项明细 -->
<view class="section-card">
<view class="section-title">
检查项明细
<text class="item-count">{{ checkItems.length }}</text>
</view>
<view v-if="checkItems.length === 0" class="empty-block">暂无检查项</view>
<view v-else class="item-list">
<view
v-for="(item, index) in checkItems"
:key="item.pointId || index"
class="item-row"
>
<text class="item-no">{{ index + 1 }}</text>
<view class="item-content">
<text class="item-name">{{ item.checkItemName || '-' }}</text>
<text v-if="item.industryName" class="item-industry">{{ item.industryName }}</text>
</view>
</view>
</view>
</view>
<!-- 计划安排 -->
<view class="section-card">
<view class="section-title">计划安排</view>
<view class="info-row">
<text class="info-label">仅工作日</text>
<text class="info-value">{{ weekendLabel }}</text>
</view>
<view class="info-row">
<text class="info-label">计划开始日期</text>
<text class="info-value">{{ formatDate(detail.planStartTime) }}</text>
</view>
<view class="info-row">
<text class="info-label">计划结束日期</text>
<text class="info-value">{{ formatDate(detail.planEndTime) }}</text>
</view>
</view>
</template>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getOneTableInfo } from '@/request/api.js'
const loading = ref(true)
const oneTableId = ref('')
const detail = ref({})
const checkItems = ref([])
const typeMap = { 1: '日常检查', 2: '专项检查', 3: '设备检查' }
const cycleMap = { 1: '每天一次', 2: '每周', 3: '每月', 4: '每季度' }
const runModeMap = { 1: '单人完成', 2: '全员', 3: '指定分组' }
const weekendMap = { 1: '包含周末', 2: '不包含周末' }
const typeLabel = computed(() => detail.value.typeName || typeMap[detail.value.type] || '-')
const cycleLabel = computed(() => detail.value.cycleName || cycleMap[detail.value.cycle] || '-')
const runModeLabel = computed(() => detail.value.runModeName || runModeMap[detail.value.runMode] || '-')
const weekendLabel = computed(() => detail.value.isWeekendName || weekendMap[detail.value.isWeekend] || '-')
const executorLabel = computed(() => {
if (Number(detail.value.runMode) === 2) return '全员'
return detail.value.executorName || '-'
})
const formatDate = (value) => {
if (!value) return '-'
return String(value).split(' ')[0]
}
const fetchDetail = async () => {
if (!oneTableId.value) return
loading.value = true
try {
const res = await getOneTableInfo(oneTableId.value)
if (res.code === 0) {
const data = res.data || {}
detail.value = data
checkItems.value = data.itemDetailList || []
} else {
uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' })
}
} catch (error) {
console.error('获取检查计划详情失败:', error)
uni.showToast({ title: '获取详情失败', icon: 'none' })
} finally {
loading.value = false
}
}
onLoad((options) => {
oneTableId.value = options.id || ''
if (!oneTableId.value) {
uni.showToast({ title: '缺少检查表ID', icon: 'none' })
loading.value = false
return
}
fetchDetail()
})
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #EBF2FC;
padding: 24rpx;
padding-bottom: 40rpx;
box-sizing: border-box;
}
.loading-wrap {
padding: 120rpx 0;
text-align: center;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
.section-card {
background: #fff;
border-radius: 16rpx;
padding: 28rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.04);
}
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
padding-left: 16rpx;
border-left: 6rpx solid #2667E9;
}
.item-count {
font-size: 26rpx;
font-weight: 400;
color: #999;
}
.info-row {
display: flex;
align-items: flex-start;
padding: 18rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&.column {
flex-direction: column;
}
}
.info-label {
width: 180rpx;
flex-shrink: 0;
font-size: 26rpx;
color: #999;
line-height: 1.5;
}
.info-value {
flex: 1;
font-size: 28rpx;
color: #333;
line-height: 1.5;
word-break: break-all;
}
.info-block {
width: 100%;
margin-top: 12rpx;
padding: 20rpx;
background: #f8f9fb;
border: 1rpx solid #ebeef5;
border-radius: 8rpx;
font-size: 28rpx;
color: #606266;
line-height: 1.6;
box-sizing: border-box;
}
.info-empty {
color: #c0c4cc;
}
.related-box {
display: flex;
align-items: flex-start;
gap: 12rpx;
padding: 20rpx;
background: #f8f9fb;
border: 1rpx solid #ebeef5;
border-radius: 8rpx;
}
.related-icon {
flex-shrink: 0;
width: 36rpx;
height: 36rpx;
margin-top: 4rpx;
}
.related-text {
flex: 1;
font-size: 28rpx;
color: #333;
line-height: 1.5;
word-break: break-all;
}
.empty-block {
text-align: center;
padding: 40rpx 0;
font-size: 26rpx;
color: #999;
}
.item-list {
border-top: 1rpx solid #f0f0f0;
}
.item-row {
display: flex;
align-items: flex-start;
gap: 16rpx;
padding: 24rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
padding-bottom: 0;
}
}
.item-no {
width: 36rpx;
font-size: 26rpx;
color: #999;
line-height: 1.5;
flex-shrink: 0;
}
.item-content {
flex: 1;
min-width: 0;
}
.item-name {
display: block;
font-size: 28rpx;
color: #333;
line-height: 1.5;
word-break: break-all;
}
.item-industry {
display: inline-block;
margin-top: 8rpx;
font-size: 24rpx;
color: #999;
}
</style>

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 {

View File

@@ -160,7 +160,7 @@
const details = (item) => {
uni.navigateTo({
url: `/pages/hiddendanger/view?hazardId=${item.hazardId}&assignId=${item.assignId}`
url: `/pages/hiddendanger/detail2?hazardId=${item.hazardId}&assignId=${item.assignId || ''}`
})
}
@@ -169,6 +169,12 @@
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 })
}

View File

@@ -76,7 +76,7 @@
<up-datetime-picker
:show="showDatePicker"
v-model="dateValue"
mode="datetime"
mode="date"
@confirm="onDateConfirm"
@cancel="showDatePicker = false"
@close="showDatePicker = false"
@@ -204,17 +204,13 @@
}
};
// 日期时间选择确认
// 日期选择确认
const onDateConfirm = (e) => {
console.log('选择的日期时间:', e);
const date = new Date(e.value);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
selectedDate.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
selectedDate.value = `${year}-${month}-${day}`;
showDatePicker.value = false;
};

View File

@@ -446,6 +446,22 @@
}
};
const applyAssigneeFromOptions = (assigneeId, assigneeName) => {
if (!assigneeId) return;
const id = String(assigneeId);
const name = assigneeName ? decodeURIComponent(String(assigneeName)).trim() : '';
const exists = detailPersonPool.value.some((user) => String(user.userId) === id);
if (!exists) {
detailPersonPool.value.push({
userId: assigneeId,
nickName: name,
deptName: ''
});
}
selectedUserIds.value = [id];
syncSelectedUsersFromIds(selectedUserIds.value);
};
const parseIdList = (raw) => {
if (raw === null || raw === undefined || raw === '') return [];
if (Array.isArray(raw)) {
@@ -1212,6 +1228,10 @@
if (options.assignId) {
assignId.value = options.assignId;
}
if (!options.rectifyId && options.assigneeId) {
applyAssigneeFromOptions(options.assigneeId, options.assigneeName);
}
// 在hazardId赋值后调用确保有值
if (!options.rectifyId) fetchPersonnelLists();

View File

@@ -199,7 +199,7 @@
import { toImageUrl } from '@/request/request.js';
const loading = ref(true);
const defaultAvatar = 'https://ossweb-img.qq.com/images/lol/web201310/skin/big99008.jpg';
const defaultAvatar = '/static/my/default_avater.png';
// 用户信息
const userInfo = reactive({
@@ -541,6 +541,12 @@
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 })
}

View File

@@ -107,7 +107,7 @@
import { toImageUrl } from '@/request/request.js';
import { getProfileDetail } from '@/request/three_one_api/info.js';
const defaultAvatar = 'https://ossweb-img.qq.com/images/lol/web201310/skin/big99008.jpg';
const defaultAvatar = '/static/my/default_avater.png';
// 用户信息
const userInfo = reactive({