|
|
|
@ -12,7 +12,7 @@ |
|
|
|
<van-notice-bar |
|
|
|
v-if="showNotice" |
|
|
|
left-icon="info-o" |
|
|
|
text="请各执法单位,于本月25日前提交次月的计划" |
|
|
|
:text="noticeText" |
|
|
|
:scrollable="false" |
|
|
|
background="#fff9dc" |
|
|
|
color="#7d661c" |
|
|
|
@ -80,9 +80,30 @@ |
|
|
|
<van-field |
|
|
|
v-model="form.enterpriseName" |
|
|
|
label="企业名称" |
|
|
|
placeholder="请输入执法企业" |
|
|
|
placeholder="输入≥3个字符检索企业" |
|
|
|
required |
|
|
|
@update:model-value="onEnterpriseNameInput" |
|
|
|
/> |
|
|
|
<!-- 企业检索结果下拉 --> |
|
|
|
<div v-if="showEnterpriseSuggest" class="enterprise-suggest"> |
|
|
|
<div v-if="enterpriseSearching" class="suggest-empty"> |
|
|
|
<van-loading size="16" /> 检索中... |
|
|
|
</div> |
|
|
|
<template v-else> |
|
|
|
<div |
|
|
|
v-for="(item, idx) in enterpriseOptions" |
|
|
|
:key="idx" |
|
|
|
class="suggest-item" |
|
|
|
@click="onSelectEnterprise(item)" |
|
|
|
> |
|
|
|
<div class="suggest-name">{{ item.enterpriseName }}</div> |
|
|
|
<div class="suggest-number">{{ item.enterpriseNumber }}</div> |
|
|
|
</div> |
|
|
|
<div v-if="!enterpriseOptions.length" class="suggest-empty"> |
|
|
|
未检索到匹配企业 |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 执法人员 --> |
|
|
|
<van-field label="执法人员" required> |
|
|
|
@ -152,6 +173,7 @@ |
|
|
|
v-model="datePickerValue" |
|
|
|
title="选择计划时间" |
|
|
|
:min-date="minDate" |
|
|
|
:max-date="maxDate" |
|
|
|
@confirm="onDateConfirm" |
|
|
|
@cancel="showDatePicker = false" |
|
|
|
/> |
|
|
|
@ -162,16 +184,50 @@ |
|
|
|
<script setup name="cpPlanEdit"> |
|
|
|
import { useRouter, useRoute } from 'vue-router'; |
|
|
|
import { showToast } from 'vant'; |
|
|
|
import { addPlan, editPlan, getPlanDetail } from '@/api/enforcement/plan'; |
|
|
|
import { addPlan, editPlan, getPlanDetail, getSubmitDeadline, searchUnionByEnterpriseName } from '@/api/enforcement/plan'; |
|
|
|
|
|
|
|
const router = useRouter(); |
|
|
|
const route = useRoute(); |
|
|
|
const {useCommon} = $globalStore; |
|
|
|
|
|
|
|
const isEdit = computed(() => !!route.query.id); |
|
|
|
const showNotice = ref(true); |
|
|
|
const submitting = ref(false); |
|
|
|
const showDatePicker = ref(false); |
|
|
|
const minDate = new Date(); |
|
|
|
|
|
|
|
// 下阶段计划提交最晚时间(每月几号前需提交下阶段计划) |
|
|
|
const submitDeadline = ref(26); |
|
|
|
|
|
|
|
// 计算日期范围:根据计划类别、提交截止日返回 min/max |
|
|
|
const dateRange = computed(() => { |
|
|
|
const today = new Date(); |
|
|
|
const currentYear = today.getFullYear(); |
|
|
|
const currentMonth = today.getMonth(); // 0-based |
|
|
|
const currentDay = today.getDate(); |
|
|
|
|
|
|
|
// 专项计划:从明天开始,可选未来任意时间 |
|
|
|
if (form.value.planType !== 1) { |
|
|
|
const tomorrow = new Date(currentYear, currentMonth, currentDay + 1); |
|
|
|
return { min: tomorrow, max: null }; |
|
|
|
} |
|
|
|
|
|
|
|
// 日常计划:默认从下个月开始;如果当前日 >= deadline 则从下下个月开始 |
|
|
|
// 上限不限制,未来年、未来月仅受 minDate 约束 |
|
|
|
const startMonthOffset = currentDay >= submitDeadline.value ? 2 : 1; |
|
|
|
const minDateValue = new Date(currentYear, currentMonth + startMonthOffset, 1); |
|
|
|
return { min: minDateValue, max: null }; |
|
|
|
}); |
|
|
|
|
|
|
|
const minDate = computed(() => dateRange.value.min); |
|
|
|
const maxDate = computed(() => dateRange.value.max || new Date(2099, 11, 31)); |
|
|
|
|
|
|
|
// 动态提示文案 |
|
|
|
const noticeText = computed(() => { |
|
|
|
if (form.value.planType === 1) { |
|
|
|
return `请各执法单位,于每月${submitDeadline.value}日前提交次月的日常计划`; |
|
|
|
} |
|
|
|
return '专项计划时间不受提交截止日限制'; |
|
|
|
}); |
|
|
|
|
|
|
|
// 表单数据 |
|
|
|
const form = ref({ |
|
|
|
@ -205,6 +261,13 @@ const form = ref({ |
|
|
|
// 人员列表 |
|
|
|
const personList = ref([]); |
|
|
|
|
|
|
|
// 企业搜索状态 |
|
|
|
const enterpriseOptions = ref([]); |
|
|
|
const showEnterpriseSuggest = ref(false); |
|
|
|
const enterpriseSearching = ref(false); |
|
|
|
let enterpriseSearchTimer = null; |
|
|
|
let enterpriseSearchSeq = 0; |
|
|
|
|
|
|
|
// 日期选择值 |
|
|
|
const datePickerValue = ref([ |
|
|
|
String(new Date().getFullYear()), |
|
|
|
@ -221,11 +284,57 @@ const planDateDisplay = computed(() => { |
|
|
|
|
|
|
|
// 如果是编辑模式,加载详情 |
|
|
|
onMounted(() => { |
|
|
|
// 执法单位默认绑定为当前登录用户所属执法单位 |
|
|
|
form.value.bureauDeptId = useCommon.bureauUnitId || ''; |
|
|
|
form.value.bureauDeptName = useCommon.bureauUnitName || ''; |
|
|
|
// 获取下阶段计划提交最晚时间设置 |
|
|
|
loadSubmitDeadline(); |
|
|
|
if (isEdit.value) { |
|
|
|
loadDetail(route.query.id); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 拉取提交截止日设置 |
|
|
|
const loadSubmitDeadline = () => { |
|
|
|
getSubmitDeadline((res) => { |
|
|
|
if (res && res.code === 200 && res.data != null) { |
|
|
|
const val = Number(res.data); |
|
|
|
if (!isNaN(val) && val > 0) { |
|
|
|
submitDeadline.value = val; |
|
|
|
} |
|
|
|
} |
|
|
|
// 初始化日期选择器默认值为可选范围的起始月 |
|
|
|
syncDatePickerToMinDate(); |
|
|
|
}, () => { |
|
|
|
// 请求失败使用默认值 26 |
|
|
|
syncDatePickerToMinDate(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
// 将日期选择器的默认值同步为当前可选范围的最小日期 |
|
|
|
const syncDatePickerToMinDate = () => { |
|
|
|
if (isEdit.value) return; // 编辑模式不覆盖 |
|
|
|
const d = minDate.value; |
|
|
|
datePickerValue.value = [ |
|
|
|
String(d.getFullYear()), |
|
|
|
String(d.getMonth() + 1).padStart(2, '0'), |
|
|
|
String(d.getDate()).padStart(2, '0'), |
|
|
|
]; |
|
|
|
}; |
|
|
|
|
|
|
|
// 监听计划类别变化,重置已选日期为有效范围 |
|
|
|
watch(() => form.value.planType, () => { |
|
|
|
syncDatePickerToMinDate(); |
|
|
|
// 若已选日期不在新范围内,清空 |
|
|
|
if (form.value.planDate) { |
|
|
|
const selected = new Date(form.value.planDate); |
|
|
|
if (selected < minDate.value || (dateRange.value.max && selected > dateRange.value.max)) { |
|
|
|
form.value.planDate = ''; |
|
|
|
form.value.planActualDate = ''; |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
const loadDetail = (id) => { |
|
|
|
getPlanDetail(id, (res) => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
@ -251,6 +360,49 @@ const onDateConfirm = ({ selectedValues }) => { |
|
|
|
showDatePicker.value = false; |
|
|
|
}; |
|
|
|
|
|
|
|
// 企业名称输入(防抖检索) |
|
|
|
const onEnterpriseNameInput = (val) => { |
|
|
|
// 手动修改名称后,原先绑定的 enterpriseNumber/enterpriseId 失效,需重新选择 |
|
|
|
form.value.enterpriseNumber = ''; |
|
|
|
form.value.enterpriseId = ''; |
|
|
|
if (enterpriseSearchTimer) { |
|
|
|
clearTimeout(enterpriseSearchTimer); |
|
|
|
} |
|
|
|
const keyword = (val || '').trim(); |
|
|
|
if (keyword.length < 3) { |
|
|
|
showEnterpriseSuggest.value = false; |
|
|
|
enterpriseOptions.value = []; |
|
|
|
return; |
|
|
|
} |
|
|
|
enterpriseSearchTimer = setTimeout(() => { |
|
|
|
const seq = ++enterpriseSearchSeq; |
|
|
|
showEnterpriseSuggest.value = true; |
|
|
|
enterpriseSearching.value = true; |
|
|
|
searchUnionByEnterpriseName(keyword, (res) => { |
|
|
|
// 仅处理最新一次请求的结果 |
|
|
|
if (seq !== enterpriseSearchSeq) return; |
|
|
|
enterpriseSearching.value = false; |
|
|
|
if (res && res.code === 200 && Array.isArray(res.data)) { |
|
|
|
enterpriseOptions.value = res.data; |
|
|
|
} else { |
|
|
|
enterpriseOptions.value = []; |
|
|
|
} |
|
|
|
}, () => { |
|
|
|
if (seq !== enterpriseSearchSeq) return; |
|
|
|
enterpriseSearching.value = false; |
|
|
|
enterpriseOptions.value = []; |
|
|
|
}); |
|
|
|
}, 350); |
|
|
|
}; |
|
|
|
|
|
|
|
// 选择某个企业 |
|
|
|
const onSelectEnterprise = (item) => { |
|
|
|
form.value.enterpriseName = item.enterpriseName || ''; |
|
|
|
form.value.enterpriseNumber = item.enterpriseNumber || ''; |
|
|
|
showEnterpriseSuggest.value = false; |
|
|
|
enterpriseOptions.value = []; |
|
|
|
}; |
|
|
|
|
|
|
|
// 添加人员(暂时简单实现) |
|
|
|
const onAddPerson = () => { |
|
|
|
// TODO: 后续对接人员选择器 |
|
|
|
@ -289,6 +441,10 @@ const validate = () => { |
|
|
|
showToast('请输入企业名称'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (!form.value.enterpriseNumber) { |
|
|
|
showToast('请从检索结果中选择企业'); |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -422,6 +578,52 @@ const onSubmit = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.enterprise-suggest { |
|
|
|
background: #fff; |
|
|
|
margin: -4px 14px 8px; |
|
|
|
border: 1px solid #e6edf5; |
|
|
|
border-radius: 8px; |
|
|
|
max-height: 220px; |
|
|
|
overflow-y: auto; |
|
|
|
box-shadow: 0 4px 10px rgba(44, 116, 190, 0.08); |
|
|
|
|
|
|
|
.suggest-item { |
|
|
|
padding: 10px 14px; |
|
|
|
border-bottom: 1px solid #f0f2f5; |
|
|
|
cursor: pointer; |
|
|
|
|
|
|
|
&:last-child { |
|
|
|
border-bottom: none; |
|
|
|
} |
|
|
|
|
|
|
|
&:active { |
|
|
|
background: #f5faff; |
|
|
|
} |
|
|
|
|
|
|
|
.suggest-name { |
|
|
|
font-size: 14px; |
|
|
|
color: #333; |
|
|
|
line-height: 1.4; |
|
|
|
} |
|
|
|
|
|
|
|
.suggest-number { |
|
|
|
font-size: 12px; |
|
|
|
color: #999; |
|
|
|
margin-top: 2px; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.suggest-empty { |
|
|
|
padding: 14px; |
|
|
|
text-align: center; |
|
|
|
color: #999; |
|
|
|
font-size: 13px; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.submit-wrap { |
|
|
|
padding: 22px 0 0; |
|
|
|
|
|
|
|
|