Files
threeonecheck_web/pages/editchecklist/editchecklist.vue

2202 lines
56 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<page-meta :page-style="pageScrollLockStyle" />
<view class="page">
<view class="form-card">
<!-- 检查表名称 -->
<view class="form-item">
<view class="form-label">
<text>检查表名称</text>
<text class="required">*</text>
</view>
<up-input v-model="formData.name" placeholder="请输入检查表名称" border="surround"></up-input>
</view>
<!-- 分派单位 -->
<view class="form-item">
<view class="form-label">
<text>分派单位</text>
<text class="required">*</text>
</view>
<view class="picker-input" @click="openDeptPopup">
<text :class="formData.deptName ? 'picker-value' : 'picker-placeholder'">
{{ formData.deptName || '请选择分派单位' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
</view>
<!-- 补充说明 -->
<view class="form-item">
<view class="form-label">
<text>补充说明</text>
</view>
<up-textarea v-model="formData.remark" placeholder="请输入补充说明"></up-textarea>
</view>
<!-- 检查表类型 -->
<view class="form-item">
<view class="form-label">
<text>检查表类型</text>
</view>
<view class="picker-input" @click="showTypePicker = true">
<text :class="formData.typeName ? 'picker-value' : 'picker-placeholder'">
{{ formData.typeName || '请选择检查表类型' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
<up-picker
:show="showTypePicker"
:columns="typeColumns"
@confirm="onTypeConfirm"
@cancel="showTypePicker = false"
@close="showTypePicker = false"
></up-picker>
</view>
<!-- 模式 -->
<view class="form-item">
<view class="form-label">
<text>模式</text>
</view>
<view class="picker-input" @click="showModePicker = true">
<text :class="formData.modeName ? 'picker-value' : 'picker-placeholder'">
{{ formData.modeName || '请选择模式' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
<up-picker
:show="showModePicker"
:columns="modeColumns"
@confirm="onModeConfirm"
@cancel="showModePicker = false"
@close="showModePicker = false"
></up-picker>
</view>
<!-- 执行人员单人完成 / 排班模式 -->
<view class="form-item" v-if="formData.runMode === 1 || formData.runMode === 4">
<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.deptId ? '请选择执行人员' : '请先选择分派单位') }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
</view>
<!-- 周期 -->
<view class="form-item">
<view class="form-label">
<text>周期</text>
</view>
<view class="picker-input" @click="showCyclePicker = true">
<text :class="formData.cycleName ? 'picker-value' : 'picker-placeholder'">
{{ formData.cycleName || '请选择周期' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
<up-picker
:show="showCyclePicker"
:columns="cycleColumns"
@confirm="onCycleConfirm"
@cancel="showCyclePicker = false"
@close="showCyclePicker = false"
></up-picker>
</view>
<!-- 仅工作日 -->
<view class="form-item">
<view class="form-label">
<text>仅工作日</text>
</view>
<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 class="required">*</text>
</view>
<view class="picker-input" @click="openStartDatePicker">
<text :class="formData.startDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.startDate || '请选择计划开始日期' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
<up-datetime-picker
:show="showStartDatePicker"
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 class="required">*</text>
</view>
<view class="picker-input" @click="openEndDatePicker">
<text :class="formData.endDate ? 'picker-value' : 'picker-placeholder'">
{{ formData.endDate || '请选择计划结束日期' }}
</text>
<text class="cuIcon-unfold picker-arrow"></text>
</view>
<up-datetime-picker
:show="showEndDatePicker"
mode="date"
v-model="endDateValue"
:minDate="endDateMinDate"
:filter="endDateFilter"
@change="onEndDatePickerChange"
@confirm="onEndDateConfirm"
@cancel="showEndDatePicker = false"
@close="showEndDatePicker = false"
></up-datetime-picker>
</view>
<!-- 已添加的检查项 -->
<view class="form-item">
<view class="form-label label-blue">
<text>已添加的检查项{{ checkItemCount }}</text>
</view>
<!-- 空状态提示 -->
<view v-if="checkItemCount === 0" class="empty-check-tip">
<text class="text-gray">暂无检查项目请添加</text>
</view>
<!-- 手动添加的检查项卡片 -->
<view class="check-card manual-card" v-for="(item, index) in manualCheckItems" :key="'manual-' + index">
<view class="card-tag">{{ index + 1 }}</view>
<view class="check-info">
<view class="info-row">
<text class="info-label">检查项名称</text>
<text class="info-value">{{ item.name }}</text>
</view>
<view class="info-row">
<text class="info-label">检查内容</text>
<text class="info-value">{{ item.point }}</text>
</view>
<view class="info-row">
<text class="info-label">参考法规</text>
<text class="info-value">{{ item.regulationName || '-' }}</text>
</view>
</view>
<view class="check-action">
<button class="btn-delete" @click="deleteManualCheckItem(item, index)">删除</button>
</view>
</view>
<!-- 从检查库添加的检查项卡片 -->
<view class="check-card library-card" v-for="(item, index) in libraryCheckItems" :key="'lib-' + item.pointId">
<view class="card-tag library-tag">检查库</view>
<view class="check-info">
<view class="info-row">
<text class="info-label">来源检查库</text>
<text class="info-value">{{ item.sourceLibraryName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">检查项名称</text>
<text class="info-value">{{ item.name }}</text>
</view>
<view class="info-row">
<text class="info-label">检查内容</text>
<text class="info-value">{{ item.point }}</text>
</view>
<view class="info-row">
<text class="info-label">参考法规</text>
<text class="info-value">{{ item.regulation || '-' }}</text>
</view>
</view>
<view class="check-action">
<button class="btn-delete" @click="deleteLibraryCheckItem(index)">删除</button>
</view>
</view>
</view>
<!-- 添加检查项 -->
<view class="form-item">
<view class="form-label label-blue">
<text>添加检查项</text>
</view>
<view class="add-btns">
<button class="btn-add" @click="showAddPopup = true">手动添加</button>
<button class="btn-add" @click="openLibraryPopup">从检查库添加</button>
</view>
</view>
<!-- <view style="height: 20px;"></view> -->
</view>
<!-- 保存按钮 -->
<view class="footer-btn">
<button class="btn-save" @click="handleSave">保存</button>
</view>
<!-- 手动添加检查项弹出框 -->
<u-popup :show="showAddPopup" mode="center" round="20" @close="showAddPopup = false">
<view class="popup-content">
<view class="popup-header">
<view class="popup-title">手动添加检查项</view>
<view class="popup-close" @click="showAddPopup = false">×</view>
</view>
<view class="popup-body">
<view class="popup-form-item" v-if="showFullForm">
<view class="popup-form-label">行业类型<text class="required">*</text></view>
<view class="popup-select" @click="showIndustryPicker = true">
<text class="regulation-text" :class="checkForm.industryName ? '' : 'text-gray'">{{ checkForm.industryName || '请选择行业类型' }}</text>
<text class="cuIcon-unfold select-icon"></text>
</view>
<up-picker
:show="showIndustryPicker"
:columns="industryColumns"
@confirm="onIndustryConfirm"
@cancel="showIndustryPicker = false"
@close="showIndustryPicker = false"
></up-picker>
</view>
<view class="popup-form-item">
<view class="popup-form-label">检查名称<text class="required">*</text></view>
<up-input v-model="checkForm.name" placeholder="请输入检查名称" border="surround"></up-input>
</view>
<view class="popup-form-item" v-if="showFullForm">
<view class="popup-form-label">牵头部门<text class="required">*</text></view>
<up-input v-model="checkForm.leadDept" placeholder="请输入牵头部门名称" border="surround"></up-input>
</view>
<view class="popup-form-item">
<view class="popup-form-label">检查内容<text class="required">*</text></view>
<up-textarea v-model="checkForm.point" placeholder="请输入检查内容" :height="150"></up-textarea>
</view>
<view class="popup-form-item" v-if="showFullForm">
<view class="popup-form-label">参考法规</view>
<view class="popup-select regulation-select" @click="openLawPopup">
<text class="regulation-text" :class="checkForm.regulationName ? '' : 'text-gray'">{{ checkForm.regulationName || '选择法规' }}</text>
<text class="cuIcon-unfold select-icon"></text>
</view>
</view>
<view class="popup-form-item" v-if="showFullForm">
<view class="popup-form-label">常见隐患</view>
<up-textarea v-model="checkForm.commonProblem" placeholder="请输入常见隐患" :height="120"></up-textarea>
</view>
<view class="popup-form-item" v-if="showFullForm">
<view class="popup-form-label">重大隐患</view>
<view class="popup-select" @click="showSeriousPicker = true">
<text class="regulation-text">{{ seriousName }}</text>
<text class="cuIcon-unfold select-icon"></text>
</view>
<up-picker
:show="showSeriousPicker"
:columns="seriousColumns"
@confirm="onSeriousConfirm"
@cancel="showSeriousPicker = false"
@close="showSeriousPicker = false"
></up-picker>
</view>
</view>
<view class="popup-footer">
<button class="btn-cancel" @click="showAddPopup = false">取消</button>
<button class="btn-confirm" @click="handleAddCheck">确定</button>
</view>
</view>
</u-popup>
<!-- 选择法规弹出框 -->
<u-popup :show="showLawPopup" mode="center" round="20" @close="showLawPopup = false">
<view class="law-popup">
<view class="popup-header">
<view class="popup-title">选择参考法规</view>
<view class="popup-close" @click="showLawPopup = false">×</view>
</view>
<view class="search-box">
<text class="cuIcon-search search-icon"></text>
<input class="search-input" v-model="lawKeyword" placeholder="请输入关键词搜索" @confirm="searchRegulation" />
<text class="search-btn" @click="searchRegulation">搜索</text>
</view>
<scroll-view class="law-list" scroll-y @scrolltolower="loadMoreLaw">
<view v-if="lawLoading && lawList.length === 0" class="loading-tip">加载中...</view>
<view v-else-if="!lawLoading && lawList.length === 0" class="empty-tip">暂无数据</view>
<template v-else>
<view
class="law-item"
:class="{ 'law-item-active': selectedLawId === item.id }"
v-for="item in lawList"
:key="item.id"
@click="selectLaw(item)"
>
<view class="law-title">{{ item.depict }}</view>
<view class="law-basis text-gray">{{ item.legalBasis }}</view>
</view>
<view v-if="lawLoading" class="loading-tip">加载中...</view>
</template>
</scroll-view>
<view class="popup-footer">
<button class="btn-cancel" @click="showLawPopup = false">取消</button>
<button class="btn-confirm" @click="confirmLaw">确定</button>
</view>
</view>
</u-popup>
<!-- 从检查库添加弹出框 -->
<u-popup :show="showLibraryPopup" mode="center" round="20" @close="closeLibraryPopup">
<view class="library-popup">
<view class="popup-header">
<view class="popup-title">选择检查库</view>
<view class="popup-close" @click="closeLibraryPopup">×</view>
</view>
<view class="search-box">
<text class="cuIcon-search search-icon"></text>
<input class="search-input" v-model="libraryKeyword" placeholder="请输入关键词搜索" @confirm="searchLibrary" />
<text class="search-btn" @click="searchLibrary">搜索</text>
</view>
<scroll-view class="library-list" scroll-y @scrolltolower="loadMoreLibrary">
<view v-if="libraryLoading && libraryList.length === 0" class="loading-tip">加载中...</view>
<view v-else-if="!libraryLoading && libraryList.length === 0" class="empty-tip">暂无数据</view>
<template v-else>
<view
class="library-item"
v-for="item in libraryList"
:key="item.id"
@click="toggleLibrarySelect(item)"
>
<view class="library-checkbox" :class="{ 'library-checkbox-active': selectedLibraries.includes(item.id) }">
<text v-if="selectedLibraries.includes(item.id)" class="cuIcon-check"></text>
</view>
<view class="library-info">
<view class="library-name">关联表名{{ item.name }}</view>
<view class="library-count">{{ item.pointCount }}</view>
</view>
</view>
<view v-if="libraryLoading" class="loading-tip">加载中...</view>
</template>
</scroll-view>
<button class="btn-add-library" @click="addSelectedLibrary">添加选中项</button>
</view>
</u-popup>
<!-- 分派单位选择弹窗 -->
<u-popup :show="showDeptPicker" mode="center" round="20" :safeAreaInsetBottom="false" @close="cancelDeptSelect">
<view class="dept-popup">
<view class="popup-header">
<view class="popup-title">选择分派单位</view>
<view class="popup-close" @click="cancelDeptSelect">×</view>
</view>
<view v-if="showDeptPicker" class="dept-tree-body">
<xq-tree
v-if="deptTree.length"
ref="deptTreeRef"
:data="deptTree"
node-key="deptId"
label-key="deptName"
children-key="children"
show-checkbox
check-strictly
default-expand-all
:height="500"
:width="560"
:default-checked-keys="deptDefaultCheckedKeys"
empty-text="暂无部门数据"
@check="onDeptCheck"
@node-click="onDeptNodeClick"
/>
<view v-else class="empty-tip">暂无部门数据</view>
</view>
<view class="popup-footer">
<button class="btn-cancel" @click="cancelDeptSelect">取消</button>
<button class="btn-confirm" @click="confirmDeptSelect">确定</button>
</view>
</view>
</u-popup>
<!-- 执行人员选择弹窗 -->
<u-popup :show="showExecutorPopup" mode="center" round="20" @close="showExecutorPopup = false">
<view class="executor-popup">
<view class="popup-header">
<view class="popup-title">选择执行人员</view>
<view class="popup-close" @click="showExecutorPopup = false">×</view>
</view>
<scroll-view class="executor-list" scroll-y>
<view v-if="executorList.length === 0" class="empty-tip">暂无人员数据</view>
<view
v-else
class="executor-item"
v-for="item in executorList"
:key="item.identityId || item.userId"
@click="formData.runMode === 4 ? toggleRosterExecutor(item) : selectExecutor(item)"
>
<view
v-if="formData.runMode === 4"
class="executor-checkbox"
:class="{ 'executor-checkbox-active': selectedRosterIdentityIds.includes(item.identityId) }"
>
<text v-if="selectedRosterIdentityIds.includes(item.identityId)" class="cuIcon-check"></text>
</view>
<view
v-else
class="executor-radio"
:class="{ 'executor-radio-active': selectedExecutorIdentityId === item.identityId }"
>
<view v-if="selectedExecutorIdentityId === item.identityId" class="executor-radio-dot"></view>
</view>
<view class="executor-info">
<text class="executor-name">{{ item.nickName }}</text>
</view>
</view>
</scroll-view>
<view class="popup-footer">
<button class="btn-cancel" @click="showExecutorPopup = false">取消</button>
<button class="btn-confirm" @click="confirmExecutorSelect">确定</button>
</view>
</view>
</u-popup>
</view>
</template>
<script setup>
import { ref, reactive, computed, onMounted, nextTick, watch } from 'vue';
import { getRegulationList, addCheckPoint, detailcheckPoint, deleteCheckPoint, getCheckItemList, getCheckItemListDetail, getDeptUsers, getDeptChildren, addCheckTable, getindustry } from '@/request/api.js';
import { getCurrentIdentityFromStorage } from '@/utils/userInfo.js';
// organizationEntityType: 0/1 机关单位展示全部2/3 企业仅展示检查名称和检查内容
const showFullForm = computed(() => {
const identity = getCurrentIdentityFromStorage();
const type = Number(identity?.organizationEntityType);
return type === 0 || type === 1;
});
const MODE_OPTIONS = [
{ label: '单人完成', value: 1 },
{ label: '全员', value: 2 },
{ label: '排班模式', value: 4 }
]
// 表单数据
const formData = reactive({
name: '',
deptId: '',
deptName: '',
remark: '',
type: '', // 检查表类型1-日常检查2-专项检查3-设备检查
typeName: '',
runMode: undefined,
modeName: '',
selectDeptId: '',
selectDeptName: '',
executorIdentityId: '',
executorNames: '',
rosterIdentityIds: [],
cycleId: '',
cycleName: '',
isWeekend: 1,
startDate: '',
endDate: ''
});
// 日期工具(需放在使用它们的逻辑之前)
const getTodayTimestamp = () => {
const now = new Date()
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
}
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()
}
return getTodayTimestamp()
});
const startDateValue = ref(getTodayTimestamp());
const endDateValue = ref(getTodayTimestamp());
// 选择器显示控制
const showDeptPicker = ref(false);
const showTypePicker = ref(false);
const showModePicker = ref(false);
const showCyclePicker = ref(false);
const showStartDatePicker = ref(false);
const showEndDatePicker = ref(false);
// 选择器数据
const typeColumns = ref([['日常检查', '专项检查', '设备检查']]);
const modeColumns = ref([MODE_OPTIONS.map((item) => item.label)]);
const cycleColumns = ref([['每天一次', '每周一次', '每月一次', '每季度一次']]);
// 执行人员选择器
const showExecutorPopup = ref(false);
const executorList = ref([]);
const selectedExecutorIdentityId = ref(null);
const selectedRosterIdentityIds = ref([]);
const pageScrollLockStyle = computed(() => {
if (showDeptPicker.value || showExecutorPopup.value) {
return 'overflow: hidden; height: 100%;'
}
return ''
})
// 分派单位树形选择器
const deptTree = ref([]);
const deptTreeRef = ref(null);
const selectedDeptId = ref(null);
const deptDefaultCheckedKeys = ref([]);
const getDeptNodeId = (node) => node?.deptId ?? node?.raw?.deptId ?? null;
const syncDeptTreeChecked = (deptId) => {
nextTick(() => {
if (deptId) {
deptTreeRef.value?.setCheckedKeys?.([deptId]);
} else {
deptTreeRef.value?.clearChecked?.();
}
});
};
const findDeptPath = (tree, targetId, path = []) => {
for (const node of tree) {
const currentPath = [...path, node];
if (String(node.deptId) === String(targetId)) {
return currentPath;
}
if (node.children?.length) {
const found = findDeptPath(node.children, targetId, currentPath);
if (found) return found;
}
}
return null;
};
const openDeptPopup = () => {
selectedDeptId.value = formData.deptId || null;
deptDefaultCheckedKeys.value = formData.deptId ? [formData.deptId] : [];
showDeptPicker.value = true;
syncDeptTreeChecked(formData.deptId);
};
const onDeptNodeClick = (node) => {
const deptId = getDeptNodeId(node);
selectedDeptId.value = deptId;
syncDeptTreeChecked(deptId);
};
const onDeptCheck = (node, { checkedKeys = [] } = {}) => {
const deptId = getDeptNodeId(node);
const isChecked = checkedKeys.some((key) => String(key) === String(deptId));
if (isChecked) {
selectedDeptId.value = deptId;
syncDeptTreeChecked(deptId);
return;
}
selectedDeptId.value = null;
syncDeptTreeChecked(null);
};
const cancelDeptSelect = () => {
showDeptPicker.value = false;
};
const confirmDeptSelect = () => {
if (!selectedDeptId.value) {
uni.showToast({ title: '请选择分派单位', icon: 'none' });
return;
}
const path = findDeptPath(deptTree.value, selectedDeptId.value);
if (!path?.length) {
uni.showToast({ title: '请选择分派单位', icon: 'none' });
return;
}
const selected = path[path.length - 1];
formData.deptId = selected.deptId;
formData.deptName = path.map((d) => d.deptName).join(' / ');
clearExecutorSelection();
fetchDeptUsers(selected.deptId);
showDeptPicker.value = false;
};
// 检查表类型映射:日常检查->1, 专项检查->2, 设备检查->3
const typeMap = {
'日常检查': 1,
'专项检查': 2,
'设备检查': 3
};
const onTypeConfirm = (e) => {
if (e.value && e.value.length > 0) {
formData.typeName = e.value[0];
formData.type = typeMap[e.value[0]];
}
showTypePicker.value = false;
};
const onModeConfirm = (e) => {
const index = Number(e.indexs?.[0] ?? 0)
const option = MODE_OPTIONS[index]
if (!option) {
showModePicker.value = false
return
}
formData.modeName = option.label
formData.runMode = option.value
clearExecutorSelection()
if (formData.deptId && option.value !== 2) {
fetchDeptUsers(formData.deptId)
}
showModePicker.value = false
}
const clearExecutorSelection = () => {
formData.executorIdentityId = ''
formData.executorNames = ''
formData.rosterIdentityIds = []
selectedExecutorIdentityId.value = null
selectedRosterIdentityIds.value = []
}
const getDeptUsersQueryParams = () => {
if (formData.runMode === 4) {
return { type: 4 }
}
if (formData.runMode === 1) {
return { type: 5 }
}
return {}
}
const syncSingleExecutorSelection = (rawValue) => {
if (formData.runMode === 4 || rawValue === undefined || rawValue === null || rawValue === '') {
return
}
const num = Number(rawValue)
if (Number.isNaN(num)) return
const byIdentity = executorList.value.find((user) => Number(user.identityId) === num)
if (byIdentity) {
selectedExecutorIdentityId.value = byIdentity.identityId
formData.executorIdentityId = byIdentity.identityId
return
}
const byUser = executorList.value.find((user) => Number(user.userId) === num)
if (byUser) {
selectedExecutorIdentityId.value = byUser.identityId
formData.executorIdentityId = byUser.identityId
}
}
const fetchDeptUsers = async (deptId) => {
if (!deptId) {
executorList.value = []
return
}
try {
const params = getDeptUsersQueryParams()
const res = await getDeptUsers(deptId, params)
if (res.code === 0 && res.data) {
executorList.value = res.data || []
if (formData.runMode === 1) {
syncSingleExecutorSelection(formData.executorIdentityId)
}
} else {
executorList.value = []
}
} catch (error) {
console.error('获取部门用户失败:', error)
executorList.value = []
}
}
const selectExecutor = (item) => {
selectedExecutorIdentityId.value = item.identityId
}
const toggleRosterExecutor = (item) => {
const identityId = item.identityId
if (identityId == null) return
const index = selectedRosterIdentityIds.value.indexOf(identityId)
if (index > -1) {
selectedRosterIdentityIds.value.splice(index, 1)
} else {
selectedRosterIdentityIds.value.push(identityId)
}
}
const confirmExecutorSelect = () => {
if (formData.runMode === 4) {
if (selectedRosterIdentityIds.value.length === 0) {
uni.showToast({ title: '请选择执行人员', icon: 'none' })
return
}
formData.rosterIdentityIds = [...selectedRosterIdentityIds.value]
const names = executorList.value
.filter((user) => selectedRosterIdentityIds.value.includes(user.identityId))
.map((user) => user.nickName || user.nickname)
formData.executorNames = names.join('、')
showExecutorPopup.value = false
return
}
if (!selectedExecutorIdentityId.value) {
uni.showToast({ title: '请选择执行人员', icon: 'none' })
return
}
const selectedUser = executorList.value.find((user) => user.identityId === selectedExecutorIdentityId.value)
formData.executorIdentityId = selectedExecutorIdentityId.value
formData.executorNames = selectedUser?.nickName || selectedUser?.nickname || ''
showExecutorPopup.value = false
}
const openExecutorPopup = () => {
if (!formData.deptId) {
uni.showToast({ title: '请先选择分派单位', icon: 'none' })
return
}
if (formData.runMode === 4) {
selectedRosterIdentityIds.value = [...(formData.rosterIdentityIds || [])]
} else {
selectedExecutorIdentityId.value = formData.executorIdentityId || null
}
fetchDeptUsers(formData.deptId)
showExecutorPopup.value = true
}
const fetchDeptChildren = async () => {
try {
const res = await getDeptChildren()
if (res.code === 0 && res.data) {
const list = Array.isArray(res.data) ? res.data : []
deptTree.value = handleTree(list, 'deptId')
}
} catch (error) {
console.error('获取部门树失败:', error)
}
}
const onCycleConfirm = (e) => {
if (e.value && e.value.length > 0) {
formData.cycleName = e.value[0];
}
showCyclePicker.value = false;
};
const parseDateValue = (dateStr) => {
if (!dateStr) return 0;
const [datePart] = dateStr.split(' ');
const [year, month, day] = datePart.split('-').map(Number);
return new Date(year, month - 1, day).getTime();
};
const openStartDatePicker = () => {
const candidate = formData.startDate
? parseDateValue(formData.startDate)
: getTodayTimestamp()
startDateValue.value = getNextAvailableTimestamp(candidate)
startPickerContext.value = startDateValue.value
showStartDatePicker.value = true
}
const openEndDatePicker = () => {
if (!formData.startDate) {
uni.showToast({ title: '请先选择计划开始日期', icon: 'none' })
return
}
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)
if (isDateDisabled(selectedDate)) {
uni.showToast({
title: Number(formData.isWeekend) === 2 ? '不能选择周末或今天之前的日期' : '开始日期不能早于今天',
icon: 'none'
})
return
}
if (formData.endDate && parseDateValue(selectedDate) > parseDateValue(formData.endDate)) {
formData.endDate = ''
}
formData.startDate = selectedDate
showStartDatePicker.value = false
}
const onEndDateConfirm = (e) => {
const selectedDate = formatDate(e.value)
if (isDateDisabled(selectedDate)) {
uni.showToast({
title: Number(formData.isWeekend) === 2 ? '不能选择周末或今天之前的日期' : '结束日期不能早于今天',
icon: 'none'
})
return
}
if (formData.startDate && parseDateValue(selectedDate) < parseDateValue(formData.startDate)) {
uni.showToast({ title: '计划结束日期不能早于计划开始日期', icon: 'none' })
return
}
formData.endDate = selectedDate
showEndDatePicker.value = false
}
// 手动添加的检查项列表
const manualCheckItems = ref([]);
// 从检查库展开添加的检查项(每一条检查点一项)
const libraryCheckItems = ref([]);
// 检查项数量(手动添加 + 检查库展开项)
const checkItemCount = computed(() => {
return manualCheckItems.value.length + libraryCheckItems.value.length;
});
const getExistingPointIds = () => {
const ids = new Set();
manualCheckItems.value.forEach((item) => {
if (item.id) ids.add(Number(item.id));
});
libraryCheckItems.value.forEach((item) => {
if (item.pointId) ids.add(Number(item.pointId));
});
return ids;
};
// 删除手动添加的检查项(调用接口删除)
const deleteManualCheckItem = (item, index) => {
uni.showModal({
title: '提示',
content: '确定要删除该检查项吗?',
confirmColor: '#F56C6C',
success: async (res) => {
if (res.confirm) {
// 手动添加的检查项有id需要调用接口删除
if (item.id) {
try {
uni.showLoading({ title: '删除中...' });
const result = await deleteCheckPoint({ id: item.id });
uni.hideLoading();
if (result.code === 0) {
manualCheckItems.value.splice(index, 1);
uni.showToast({ title: '删除成功', icon: 'success' });
} else {
uni.showToast({ title: result.msg || '删除失败', icon: 'none' });
}
} catch (error) {
uni.hideLoading();
console.error('删除检查项失败:', error);
uni.showToast({ title: '删除失败', icon: 'none' });
}
} else {
// 没有id的情况理论上不应该出现直接从本地移除
manualCheckItems.value.splice(index, 1);
uni.showToast({ title: '删除成功', icon: 'success' });
}
}
}
});
};
// 删除检查库展开的检查项(只从本地暂存删除,不调用接口)
const deleteLibraryCheckItem = (index) => {
uni.showModal({
title: '提示',
content: '确定要删除该检查项吗?',
confirmColor: '#F56C6C',
success: (res) => {
if (res.confirm) {
libraryCheckItems.value.splice(index, 1);
uni.showToast({ title: '删除成功', icon: 'success' });
}
}
});
};
// 手动添加检查项弹窗
const showAddPopup = ref(false);
const checkForm = reactive({
name: '',
point: '',
industryId: null,
industryName: '',
leadDept: '',
commonProblem: '',
isSerious: 2,
regulationId: null,
regulationName: ''
});
const industryOptions = ref([]);
const showIndustryPicker = ref(false);
const industryColumns = computed(() => [industryOptions.value.map((item) => item.name || item.industryName || '')]);
const showSeriousPicker = ref(false);
const seriousColumns = ref([['是', '否']]);
const seriousName = computed(() => (checkForm.isSerious === 1 ? '是' : '否'));
const resetCheckForm = () => {
checkForm.name = '';
checkForm.point = '';
checkForm.industryId = null;
checkForm.industryName = '';
checkForm.leadDept = '';
checkForm.commonProblem = '';
checkForm.isSerious = 2;
checkForm.regulationId = null;
checkForm.regulationName = '';
};
const fetchIndustryOptions = async () => {
try {
const res = await getindustry();
if (res.code === 0) {
industryOptions.value = res.data || [];
}
} catch (error) {
console.error('获取行业类型失败:', error);
}
};
watch(showAddPopup, (val) => {
if (val && showFullForm.value) {
fetchIndustryOptions();
}
});
const onIndustryConfirm = (e) => {
const selectedName = e.value?.[0];
const selected = industryOptions.value.find((item) => (item.name || item.industryName) === selectedName);
if (selected) {
checkForm.industryId = selected.id;
checkForm.industryName = selected.name || selected.industryName || '';
}
showIndustryPicker.value = false;
};
const onSeriousConfirm = (e) => {
const selectedName = e.value?.[0];
checkForm.isSerious = selectedName === '是' ? 1 : 2;
showSeriousPicker.value = false;
};
// 选择法规弹窗
const showLawPopup = ref(false);
const lawKeyword = ref('');
const selectedLawId = ref(null);
const selectedLawName = ref('');
const lawList = ref([]);
const lawLoading = ref(false);
const lawPageNum = ref(1);
const lawPageSize = ref(10);
const hasMoreLaw = ref(true);
// 打开法规选择弹窗
const openLawPopup = () => {
if (!showFullForm.value) {
return;
}
showLawPopup.value = true;
// 如果列表为空,加载数据
if (lawList.value.length === 0) {
fetchRegulationList();
}
};
// 获取法规列表
const fetchRegulationList = async (isLoadMore = false) => {
if (lawLoading.value) return;
lawLoading.value = true;
try {
// 构建参数,只传有值的字段
const params = {
pageNum: lawPageNum.value,
pageSize: lawPageSize.value,
status: 1 // 启用状态
};
// keyword 有值时才传
if (lawKeyword.value && lawKeyword.value.trim()) {
params.keyword = lawKeyword.value.trim();
}
const res = await getRegulationList(params);
if (res.code === 0) {
const records = res.data.records || res.data || [];
if (isLoadMore) {
lawList.value = [...lawList.value, ...records];
} else {
lawList.value = records;
}
// 判断是否还有更多
const total = res.data.total || 0;
hasMoreLaw.value = lawList.value.length < total;
}
} catch (error) {
console.error('获取法规列表失败:', error);
} finally {
lawLoading.value = false;
}
};
// 搜索法规
const searchRegulation = () => {
lawPageNum.value = 1;
lawList.value = [];
hasMoreLaw.value = true;
fetchRegulationList();
};
// 加载更多法规
const loadMoreLaw = () => {
if (!hasMoreLaw.value || lawLoading.value) return;
lawPageNum.value++;
fetchRegulationList(true);
};
// 选择法规
const selectLaw = (item) => {
selectedLawId.value = item.id;
// 用 depict描述作为显示名称
selectedLawName.value = item.depict || item.keyword || '';
};
// 确认选择法规
const confirmLaw = () => {
if (selectedLawId.value) {
checkForm.regulationId = selectedLawId.value;
checkForm.regulationName = selectedLawName.value;
}
showLawPopup.value = false;
};
// 提交手动添加的检查项
const handleAddCheck = async () => {
if (!checkForm.name) {
uni.showToast({ title: '请输入检查名称', icon: 'none' });
return;
}
if (!checkForm.point) {
uni.showToast({ title: '请输入检查内容', icon: 'none' });
return;
}
if (showFullForm.value) {
if (!checkForm.industryId) {
uni.showToast({ title: '请选择行业类型', icon: 'none' });
return;
}
if (!checkForm.leadDept) {
uni.showToast({ title: '请输入牵头部门', icon: 'none' });
return;
}
}
try {
uni.showLoading({ title: '添加中...' });
const params = showFullForm.value
? {
name: checkForm.name,
point: checkForm.point,
industryId: checkForm.industryId,
leadDept: checkForm.leadDept,
regulationId: checkForm.regulationId || undefined,
commonProblem: checkForm.commonProblem || undefined,
isSerious: checkForm.isSerious
}
: {
name: checkForm.name,
point: checkForm.point
};
const res = await addCheckPoint(params);
if (res.code === 0) {
// 新增成功后,获取详情
const checkPointId = res.data?.id || res.data;
if (checkPointId) {
const detailRes = await detailcheckPoint(checkPointId);
if (detailRes.code === 0 && detailRes.data) {
// 添加到手动检查项列表
manualCheckItems.value.push({
id: detailRes.data.id,
name: detailRes.data.name,
point: detailRes.data.point,
regulationId: detailRes.data.regulationId,
regulationName: detailRes.data.regulationName || checkForm.regulationName
});
}
} else {
// 如果没有返回ID直接用表单数据添加
manualCheckItems.value.push({
name: checkForm.name,
point: checkForm.point,
regulationId: checkForm.regulationId,
regulationName: checkForm.regulationName
});
}
uni.hideLoading();
uni.showToast({ title: '添加成功', icon: 'success' });
showAddPopup.value = false;
resetCheckForm();
} else {
uni.hideLoading();
uni.showToast({ title: res.msg || '添加失败', icon: 'none' });
}
} catch (error) {
uni.hideLoading();
console.error('添加检查项失败:', error);
uni.showToast({ title: '添加失败', icon: 'none' });
}
};
// 从检查库添加弹窗
const showLibraryPopup = ref(false);
const libraryKeyword = ref('');
const selectedLibraries = ref([]);
const libraryList = ref([]);
const libraryLoading = ref(false);
const libraryPageNum = ref(1);
const libraryPageSize = ref(10);
const hasMoreLibrary = ref(true);
// 打开检查库弹窗时获取数据
const openLibraryPopup = () => {
showLibraryPopup.value = true;
libraryKeyword.value = '';
libraryPageNum.value = 1;
libraryList.value = [];
hasMoreLibrary.value = true;
selectedLibraries.value = [];
fetchLibraryList();
};
// 关闭检查库弹窗
const closeLibraryPopup = () => {
showLibraryPopup.value = false;
selectedLibraries.value = [];
};
// 获取检查库列表
const fetchLibraryList = async (isLoadMore = false) => {
if (libraryLoading.value) return;
libraryLoading.value = true;
try {
const params = {
pageNum: libraryPageNum.value,
pageSize: libraryPageSize.value
};
// name 有值时才传
if (libraryKeyword.value && libraryKeyword.value.trim()) {
params.name = libraryKeyword.value.trim();
}
const res = await getCheckItemList(params);
if (res.code === 0) {
const records = res.data.records || [];
if (isLoadMore) {
libraryList.value = [...libraryList.value, ...records];
} else {
libraryList.value = records;
}
// 判断是否还有更多
const total = res.data.total || 0;
hasMoreLibrary.value = libraryList.value.length < total;
}
} catch (error) {
console.error('获取检查库列表失败:', error);
} finally {
libraryLoading.value = false;
}
};
// 搜索检查库
const searchLibrary = () => {
libraryPageNum.value = 1;
libraryList.value = [];
hasMoreLibrary.value = true;
fetchLibraryList();
};
// 加载更多检查库
const loadMoreLibrary = () => {
if (!hasMoreLibrary.value || libraryLoading.value) return;
libraryPageNum.value++;
fetchLibraryList(true);
};
const toggleLibrarySelect = (item) => {
const index = selectedLibraries.value.indexOf(item.id);
if (index > -1) {
selectedLibraries.value.splice(index, 1);
} else {
selectedLibraries.value.push(item.id);
}
};
// 拉取检查库详情全部分页数据
const fetchLibraryDetailAll = async (libraryId) => {
const pageSize = 50;
let pageNum = 1;
let allRecords = [];
let total = 0;
while (true) {
const res = await getCheckItemListDetail({
id: libraryId,
pageNum,
pageSize
});
if (res.code !== 0 || !res.data) break;
const records = res.data.records || [];
total = Number(res.data.total) || 0;
allRecords = [...allRecords, ...records];
if (records.length === 0 || allRecords.length >= total) break;
pageNum += 1;
}
return allRecords;
};
const getLibraryDisplayName = (libraryId, records) => {
const library = libraryList.value.find((item) => String(item.id) === String(libraryId));
if (library?.name) return library.name;
if (records.length > 0) return records[0].name || '';
return '';
};
const appendLibraryRecords = (libraryId, records, sourceLibraryName) => {
const existingPointIds = getExistingPointIds();
let addedCount = 0;
records.forEach((record) => {
if (!record.pointId) return;
const pointId = Number(record.pointId);
if (existingPointIds.has(pointId)) return;
existingPointIds.add(pointId);
libraryCheckItems.value.push({
pointId,
itemId: record.itemId,
sourceLibraryId: libraryId,
sourceLibraryName,
name: record.name || '',
point: record.point || '',
industry: record.industry || '',
leadDept: record.leadDept || '',
regulation: record.regulation || ''
});
addedCount += 1;
});
return addedCount;
};
// 添加选中的检查库
const addSelectedLibrary = async () => {
if (selectedLibraries.value.length === 0) {
uni.showToast({ title: '请选择检查库', icon: 'none' });
return;
}
uni.showLoading({ title: '加载中...' });
try {
let totalAdded = 0;
for (const id of selectedLibraries.value) {
const records = await fetchLibraryDetailAll(id);
const sourceLibraryName = getLibraryDisplayName(id, records);
totalAdded += appendLibraryRecords(id, records, sourceLibraryName);
}
uni.hideLoading();
if (totalAdded === 0) {
uni.showToast({ title: '没有可添加的新检查项', icon: 'none' });
return;
}
uni.showToast({ title: `已添加${totalAdded}`, icon: 'success' });
showLibraryPopup.value = false;
selectedLibraries.value = [];
} catch (error) {
uni.hideLoading();
console.error('获取检查库详情失败:', error);
uni.showToast({ title: '添加失败', icon: 'none' });
}
};
// 保存
const handleSave = async () => {
// 表单验证
if (!formData.name) {
uni.showToast({ title: '请输入检查表名称', icon: 'none' });
return;
}
if (!formData.deptId) {
uni.showToast({ title: '请选择分派单位', icon: 'none' });
return;
}
if (!formData.type) {
uni.showToast({ title: '请选择检查表类型', icon: 'none' });
return;
}
if (!formData.runMode) {
uni.showToast({ title: '请选择模式', icon: 'none' });
return;
}
if (formData.runMode === 1 && !formData.executorIdentityId) {
uni.showToast({ title: '请选择执行人员', icon: 'none' });
return;
}
if (formData.runMode === 4 && (!formData.rosterIdentityIds || formData.rosterIdentityIds.length === 0)) {
uni.showToast({ title: '请选择执行人员', icon: 'none' });
return;
}
if (!formData.cycleName) {
uni.showToast({ title: '请选择周期', icon: 'none' });
return;
}
if (!formData.startDate || !formData.endDate) {
uni.showToast({ title: '请选择计划日期', icon: 'none' });
return;
}
if (parseDateValue(formData.endDate) < parseDateValue(formData.startDate)) {
uni.showToast({ title: '计划结束日期不能早于计划开始日期', icon: 'none' });
return;
}
// 构建 checkPointIds 数组(检查点 id 列表)
const items = [];
manualCheckItems.value.forEach((item) => {
if (item.id) {
items.push(Number(item.id));
}
});
libraryCheckItems.value.forEach((item) => {
if (item.pointId) {
items.push(Number(item.pointId));
}
});
if (items.length === 0) {
uni.showToast({ title: '请添加检查项', icon: 'none' });
return;
}
// 周期映射:每天=1每周=2每月=3每季度=4
const cycleMap = {
'每天一次': 1,
'每周一次': 2,
'每月一次': 3,
'每季度一次': 4
};
// 构建 itemIds 数组(从检查库选择的检查库 id 列表,去重)
const itemIds = [...new Set(
libraryCheckItems.value
.map((item) => item.sourceLibraryId)
.filter(Boolean)
.map((id) => Number(id))
)];
// 构建提交参数
const params = {
name: formData.name,
deptId: formData.deptId,
description: formData.remark || '',
type: formData.type, // 检查表类型1-日常检查2-专项检查3-设备检查
runMode: formData.runMode,
checkPointIds: items,
itemIds: itemIds, // 从检查库选择的库id数组
cycle: cycleMap[formData.cycleName] || 1,
isWeekend: formData.isWeekend,
planStartTime: `${formData.startDate} 00:00:00`,
planEndTime: `${formData.endDate} 00:00:00`
};
if (formData.runMode === 1 && formData.executorIdentityId) {
params.executorIdentityId = formData.executorIdentityId;
}
if (formData.runMode === 4 && formData.rosterIdentityIds?.length) {
params.rosterIdentityIds = formData.rosterIdentityIds;
}
try {
uni.showLoading({ title: '保存中...' });
const res = await addCheckTable(params);
uni.hideLoading();
if (res.code === 0) {
uni.showToast({ title: '保存成功', icon: 'success' });
setTimeout(() => {
uni.navigateBack();
}, 1500);
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' });
}
} catch (error) {
uni.hideLoading();
console.error('保存失败:', error);
uni.showToast({ title: '保存失败', icon: 'none' });
}
};
// 页面加载时获取数据
onMounted(() => {
fetchDeptChildren();
});
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #EBF2FC;
padding: 24rpx;
padding-bottom: 140rpx;
}
// 表单卡片
.form-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 80rpx;
}
// 表单项
.form-item {
margin-bottom: 32rpx;
&:last-child {
margin-bottom: 0;
}
}
// 表单标题
.form-label {
font-size: 28rpx;
color: #999;
margin-bottom: 16rpx;
display: flex;
align-items: center;
.required {
color: #F56C6C;
margin-left: 4rpx;
}
}
// 蓝色标题
.label-blue {
color: #2667E9;
}
// 选择器输入框
.picker-input {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
background: #fff;
border: 2rpx solid #E5E5E5;
border-radius: 8rpx;
padding: 0 24rpx;
.picker-value {
color: #333;
font-size: 28rpx;
}
.picker-placeholder {
color: #C0C4CC;
font-size: 28rpx;
}
.picker-arrow {
color: #C0C4CC;
font-size: 24rpx;
}
}
// 开关行(保留兼容)
.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;
padding: 60rpx 0;
font-size: 28rpx;
}
// 检查项卡片
.check-card {
background: #F8FAFF;
border-left: 6rpx solid #2667E9;
border-radius: 8rpx;
padding: 24rpx;
padding-bottom: 80rpx;
margin-bottom: 20rpx;
position: relative;
.check-info {
.info-row {
display: flex;
margin-bottom: 16rpx;
font-size: 26rpx;
line-height: 1.5;
&:last-child {
margin-bottom: 0;
}
.info-label {
color: #666;
flex-shrink: 0;
}
.info-label-bold {
font-weight: bold;
}
.info-value {
color: #333;
flex: 1;
}
}
}
.check-action {
position: absolute;
right: 20rpx;
bottom: 20rpx;
.btn-delete {
width: 120rpx;
height: 56rpx;
line-height: 56rpx;
background: #F56C6C;
color: #fff;
font-size: 26rpx;
border-radius: 8rpx;
padding: 0;
&::after {
border: none;
}
}
}
}
// 手动添加的检查项卡片带第x项标签
.manual-card,
.library-card {
padding-top: 50rpx;
.card-tag {
position: absolute;
left: 0;
top: 0;
background: #2667E9;
color: #fff;
font-size: 24rpx;
padding: 6rpx 20rpx;
border-radius: 0 0 12rpx 0;
}
}
.library-card .library-tag {
background: #67C23A;
}
// 添加按钮组
.add-btns {
display: flex;
justify-content: space-between;
gap: 24rpx;
.btn-add {
flex: 1;
height: 72rpx;
line-height: 72rpx;
background: #fff;
border: 2rpx solid #2667E9;
border-radius: 8rpx;
color: #2667E9;
font-size: 28rpx;
padding: 0;
&::after {
border: none;
}
}
}
// 底部保存按钮
.footer-btn {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 40rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: #EBF2FC;
.btn-save {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #2667E9;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
&::after {
border: none;
}
}
}
// 弹出框样式
.popup-content {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.popup-title {
font-size: 32rpx;
color: #333;
font-weight: bold;
}
.popup-close {
font-size: 48rpx;
color: #999;
line-height: 1;
}
.popup-body {
padding: 30rpx;
max-height: 700rpx;
overflow-y: auto;
}
.popup-form-item {
margin-bottom: 24rpx;
}
.popup-form-label {
font-size: 28rpx;
color: #333;
margin-bottom: 12rpx;
display: flex;
align-items: center;
.required {
color: #F56C6C;
margin-left: 4rpx;
}
}
.popup-select {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 80rpx;
border: 2rpx solid #E5E5E5;
border-radius: 8rpx;
padding: 0 24rpx;
font-size: 28rpx;
}
.regulation-select {
align-items: flex-start;
padding: 20rpx 24rpx;
.regulation-text {
flex: 1;
line-height: 1.5;
word-break: break-all;
}
.select-icon {
flex-shrink: 0;
margin-left: 16rpx;
margin-top: 4rpx;
}
}
.popup-footer {
display: flex;
border-top: 1rpx solid #eee;
button {
flex: 1;
height: 90rpx;
line-height: 90rpx;
border-radius: 0;
font-size: 30rpx;
&::after {
border: none;
}
}
.btn-cancel {
background: #fff;
color: #666;
}
.btn-confirm {
background: #2667E9;
color: #fff;
}
}
// 选择法规弹窗
.law-popup {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
max-height: 80vh;
}
.search-box {
display: flex;
align-items: center;
background: #F5F5F5;
border-radius: 40rpx;
padding: 16rpx 24rpx;
margin: 20rpx 30rpx;
.search-icon {
font-size: 28rpx;
color: #999;
margin-right: 12rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
background: transparent;
}
.search-btn {
color: #2667E9;
font-size: 26rpx;
margin-left: 16rpx;
}
}
.loading-tip, .empty-tip {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 26rpx;
}
.law-list {
max-height: 400rpx;
padding: 0 30rpx;
}
.law-item {
padding: 24rpx;
border: 2rpx solid #E5E5E5;
border-radius: 12rpx;
margin-bottom: 16rpx;
font-size: 28rpx;
color: #333;
.law-title {
line-height: 1.5;
margin-bottom: 8rpx;
}
.law-basis {
font-size: 24rpx;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.law-item-active {
border-color: #2667E9;
background: #F0F6FF;
}
.load-more {
text-align: center;
padding: 20rpx;
margin: 0 30rpx;
border: 2rpx solid #2667E9;
border-radius: 40rpx;
}
// 检查库弹窗样式
.library-popup {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
max-height: 80vh;
}
.library-list {
max-height: 400rpx;
padding: 0 30rpx;
}
.library-item {
display: flex;
align-items: flex-start;
padding: 24rpx;
border: 2rpx solid #E5E5E5;
border-radius: 12rpx;
margin-bottom: 16rpx;
}
.library-checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ccc;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 4rpx;
font-size: 24rpx;
}
.library-checkbox-active {
background: #2667E9;
border-color: #2667E9;
color: #fff;
}
.library-info {
flex: 1;
}
.library-name {
font-size: 28rpx;
color: #333;
line-height: 1.5;
}
.library-count {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
.btn-add-library {
width: calc(100% - 60rpx);
height: 80rpx;
line-height: 80rpx;
background: #2667E9;
border-radius: 40rpx;
color: #fff;
font-size: 30rpx;
margin: 20rpx 30rpx;
&::after {
border: none;
}
}
// 分派单位弹窗样式
.dept-popup {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
.popup-footer {
button {
margin: 0;
}
}
}
.dept-tree-body {
height: 500rpx;
overflow: hidden;
:deep(.checkbox-custom.checked),
:deep(.checkbox-custom.indeterminate) {
background-color: #2667E9;
border-color: #2667E9;
}
}
// 执行人员弹窗样式
.executor-popup {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
max-height: 80vh;
}
.executor-list {
max-height: 500rpx;
padding: 0 30rpx;
}
.executor-item {
display: flex;
align-items: center;
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.executor-radio {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ccc;
border-radius: 50%;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.executor-radio-active {
border-color: #2667E9;
}
.executor-radio-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background: #2667E9;
}
.executor-checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ccc;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 24rpx;
}
.executor-checkbox-active {
background: #2667E9;
border-color: #2667E9;
color: #fff;
}
.executor-info {
flex: 1;
}
.executor-name {
font-size: 28rpx;
color: #333;
}
</style>