Compare commits

..

2 Commits

Author SHA1 Message Date
14a536bd1c feat(Calendar): 添加休息天数输入并改进营期设置逻辑
- 在营期设置弹窗中添加休息天数输入字段
- 修改营期结束判断逻辑,不再仅依赖休息日
- 改进用户参数获取逻辑,优先使用路由参数
- 添加测试数据以便在没有营期数据时测试功能
- 优化API请求参数处理,确保总是传递必要参数
2025-08-27 18:13:46 +08:00
787703fa12 feat(Calendar): 添加结束营期确认和下一营期设置弹窗
添加结束营期确认弹窗和强制设置下一营期弹窗,确保结束当前营期前必须设置下一营期
2025-08-27 17:38:48 +08:00
2 changed files with 297 additions and 26 deletions

View File

@@ -109,6 +109,18 @@
placeholder="请输入天数" placeholder="请输入天数"
/> />
</div> </div>
<div class="form-group">
<label for="restDays">休息天数</label>
<input
type="number"
id="restDays"
v-model="restDays"
min="1"
max="365"
class="form-input"
placeholder="请输入休息天数"
/>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button @click="showCampModal = false" class="cancel-btn">取消</button> <button @click="showCampModal = false" class="cancel-btn">取消</button>
@@ -116,6 +128,74 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 结束营期确认弹窗 -->
<div v-if="showFinishConfirmModal" class="modal-overlay">
<div class="modal-content confirm-modal" @click.stop>
<div class="modal-header">
<h3>确认结束营期</h3>
</div>
<div class="modal-body">
<p class="confirm-text">确定要结束当前营期吗结束后需要立即设置下一营期</p>
</div>
<div class="modal-footer">
<button @click="cancelFinishCamp" class="cancel-btn">取消</button>
<button @click="confirmFinishCamp" class="confirm-btn">确认结束</button>
</div>
</div>
</div>
<!-- 强制设置下一营期弹窗 -->
<div v-if="showNextCampModal" class="modal-overlay">
<div class="modal-content force-modal" @click.stop>
<div class="modal-header">
<h3>设置下一营期</h3>
<span class="required-text">*必须完成设置才能结束当前营期</span>
</div>
<div class="modal-body">
<div class="form-group">
<label for="nextCampStartDate">下一营期开始时间<span class="required">*</span></label>
<input
type="date"
id="nextCampStartDate"
v-model="nextCampStartDate"
class="form-input"
required
/>
</div>
<div class="form-group">
<label for="nextCampDataDays">接数据时间天数<span class="required">*</span></label>
<input
type="number"
id="nextCampDataDays"
v-model="nextCampDataDays"
min="1"
max="365"
class="form-input"
placeholder="请输入接数据天数"
required
/>
</div>
<div class="form-group">
<label for="nextCampRestDays">休息时间天数<span class="required">*</span></label>
<input
type="number"
id="nextCampRestDays"
v-model="nextCampRestDays"
min="1"
max="365"
class="form-input"
placeholder="请输入休息天数"
required
/>
</div>
</div>
<div class="modal-footer">
<button @click="forceNextCampSetting" class="cancel-btn disabled">不能取消</button>
<button @click="saveNextCampSettings" class="save-btn">完成设置</button>
</div>
</div>
</div>
</div> </div>
</template> </template>
@@ -137,8 +217,16 @@ const tooltipPosition = ref({ x: 0, y: 0 });
const showCampModal = ref(false); const showCampModal = ref(false);
const campStartDate = ref(''); const campStartDate = ref('');
const campDays = ref(); const campDays = ref();
const restDays = ref();
const isCampFinished = ref(false); const isCampFinished = ref(false);
// 结束营期确认弹框相关
const showFinishConfirmModal = ref(false);
const showNextCampModal = ref(false);
const nextCampStartDate = ref('');
const nextCampDataDays = ref();
const nextCampRestDays = ref();
// 获取本地时区的今日日期字符串 // 获取本地时区的今日日期字符串
const getTodayString = () => { const getTodayString = () => {
const now = new Date(); const now = new Date();
@@ -253,7 +341,11 @@ const saveCampSettings = async () => {
return; return;
} }
if (!campDays.value || campDays.value < 1) { if (!campDays.value || campDays.value < 1) {
alert('请输入有效的天数'); alert('请输入有效的接数据天数');
return;
}
if (!restDays.value || restDays.value < 1) {
alert('请输入有效的休息天数');
return; return;
} }
@@ -261,14 +353,21 @@ const saveCampSettings = async () => {
// 调用API设置营期参数并获取营期安排 // 调用API设置营期参数并获取营期安排
const result = await CenterCampPeriodAdmin({ const result = await CenterCampPeriodAdmin({
receipt_data_start_time: campStartDate.value, receipt_data_start_time: campStartDate.value,
receipt_data_time: campDays.value.toString() receipt_data_time: campDays.value.toString(),
rest_days: restDays.value.toString()
}); });
if (result && result.data) { if (result && result.data) {
showCampModal.value = false; showCampModal.value = false;
// 重置表单数据
campStartDate.value = '';
campDays.value = null;
restDays.value = null;
alert('营期设置成功!');
} }
} catch (error) { } catch (error) {
console.error('保存营期设置失败:', error); console.error('保存营期设置失败:', error);
alert('保存营期设置失败,请重试');
} }
}; };
@@ -355,40 +454,74 @@ const isRestDay = (dateStr) => {
return dayEvents.length > 0; return dayEvents.length > 0;
}; };
// 方法:检查营期是否应该结束(休息日已过) // 方法:检查营期是否应该结束
const shouldShowFinishCamp = () => { const shouldShowFinishCamp = () => {
if (isCampFinished.value) return false; if (isCampFinished.value) return false;
const today = formatDateToString(new Date()); // 检查是否有营期相关的事件(接数据、课程、休息日等)
const restDayEvents = events.value.filter(event => event.isCampEvent && event.type === 'rest'); const campEvents = events.value.filter(event => event.isCampEvent);
if (restDayEvents.length === 0) return false; // 如果有营期事件,就显示结束营期按钮
return campEvents.length > 0;
// 检查是否有休息日已经过去
const todayDate = new Date(today);
for (const restEvent of restDayEvents) {
const restDate = new Date(restEvent.date);
if (todayDate > restDate) {
return true;
}
}
return false;
}; };
// 方法:结束营期 // 方法:结束营期
const finishCamp = async () => { const finishCamp = async () => {
// 显示确认弹框
showFinishConfirmModal.value = true;
};
// 确认结束营期并进入下一营期设置
const confirmFinishCamp = () => {
showFinishConfirmModal.value = false;
showNextCampModal.value = true;
};
// 取消结束营期
const cancelFinishCamp = () => {
showFinishConfirmModal.value = false;
};
// 保存下一营期设置并结束当前营期
const saveNextCampSettings = async () => {
if (!nextCampStartDate.value || !nextCampDataDays.value || !nextCampRestDays.value) {
alert('请填写完整的下一营期信息');
return;
}
try { try {
// 先结束当前营期
isCampFinished.value = true; isCampFinished.value = true;
await CenterCampPeriodAdmin({ await CenterCampPeriodAdmin({
is_camp_finish: "11" is_camp_finish: "11"
}); });
// 设置下一营期
await CenterCampPeriodAdmin({
receipt_data_start_time: nextCampStartDate.value,
receipt_data_time: nextCampDataDays.value.toString(),
rest_days: nextCampRestDays.value.toString()
});
// 关闭弹框并重置数据
showNextCampModal.value = false;
nextCampStartDate.value = '';
nextCampDataDays.value = null;
nextCampRestDays.value = null;
alert('营期设置成功!');
} catch (error) { } catch (error) {
console.error('结束营期失败:', error); console.error('营期设置失败:', error);
isCampFinished.value = false; isCampFinished.value = false;
alert('营期设置失败,请重试');
} }
}; };
// 强制要求设置下一营期(不允许取消)
const forceNextCampSetting = () => {
alert('必须设置下一营期才能结束当前营期');
};
// 路由实例 // 路由实例
const router = useRouter(); const router = useRouter();
@@ -398,15 +531,19 @@ const userStore = useUserStore();
// 获取通用请求参数的函数 // 获取通用请求参数的函数
const getRequestParams = () => { const getRequestParams = () => {
const params = {} const params = {}
// 从路由参数获取 // 优先从路由参数获取
const routeUserLevel = router.currentRoute.value.query.user_level || router.currentRoute.value.params.user_level const routeUserLevel = router.currentRoute.value.query.user_level || router.currentRoute.value.params.user_level
const routeUserName = router.currentRoute.value.query.user_name || router.currentRoute.value.params.user_name const routeUserName = router.currentRoute.value.query.user_name || router.currentRoute.value.params.user_name
// 如果路由有参数,使用路由参数
if (routeUserLevel) { if (routeUserLevel && routeUserName) {
params.user_level = routeUserLevel.toString() params.user_level = routeUserLevel.toString()
}
if (routeUserName) {
params.user_name = routeUserName params.user_name = routeUserName
} else if (userStore.userInfo && userStore.userInfo.username) {
// 如果路由没有参数从用户store获取
params.user_name = userStore.userInfo.username
if (userStore.userInfo.user_level) {
params.user_level = userStore.userInfo.user_level.toString()
}
} }
return params return params
@@ -431,6 +568,9 @@ async function CenterCampPeriodAdmin(data = {}) {
// 设置营期时,传递开始时间和天数参数 // 设置营期时,传递开始时间和天数参数
Finsh.receipt_data_start_time = data.receipt_data_start_time Finsh.receipt_data_start_time = data.receipt_data_start_time
Finsh.receipt_data_time = data.receipt_data_time Finsh.receipt_data_time = data.receipt_data_time
if (data.rest_days) {
Finsh.rest_days = data.rest_days
}
} else { } else {
// 兼容原有逻辑,使用全局变量 // 兼容原有逻辑,使用全局变量
if (isCampFinished.value) { if (isCampFinished.value) {
@@ -439,12 +579,18 @@ async function CenterCampPeriodAdmin(data = {}) {
// 只有在有营期设置数据时才传递参数 // 只有在有营期设置数据时才传递参数
Finsh.receipt_data_start_time = campStartDate.value Finsh.receipt_data_start_time = campStartDate.value
Finsh.receipt_data_time = campDays.value.toString() Finsh.receipt_data_time = campDays.value.toString()
if (restDays.value) {
Finsh.rest_days = restDays.value.toString()
}
} }
// 如果没有营期设置数据Finsh 保持为空对象,用于获取现有数据 // 如果没有营期设置数据Finsh 保持为空对象,用于获取现有数据
} }
console.log('Finsh', Finsh) console.log('Finsh', Finsh)
console.log('params', params) console.log('params', params)
const res = await getCampPeriodAdmin(hasParams ? {...params, ...Finsh} : Finsh)
// 确保总是传递用户参数,即使是获取数据时
const finalParams = hasParams ? {...params, ...Finsh} : (Object.keys(Finsh).length > 0 ? Finsh : {})
const res = await getCampPeriodAdmin(finalParams)
// 如果获取到营期数据,映射到日历中 // 如果获取到营期数据,映射到日历中
if (res && res.data && res.data.camp_period) { if (res && res.data && res.data.camp_period) {
@@ -539,6 +685,7 @@ const parseDateRange = (dateRangeStr) => {
// 组件挂载时选中今天并获取营期数据 // 组件挂载时选中今天并获取营期数据
onMounted(async () => { onMounted(async () => {
console.log('Calendar组件挂载开始初始化...');
const todayDate = calendarDates.value.find(date => date.isToday); const todayDate = calendarDates.value.find(date => date.isToday);
if (todayDate) { if (todayDate) {
selectedDate.value = todayDate; selectedDate.value = todayDate;
@@ -546,9 +693,36 @@ onMounted(async () => {
// 获取现有的营期数据 // 获取现有的营期数据
try { try {
await CenterCampPeriodAdmin(); console.log('开始获取营期数据...');
const result = await CenterCampPeriodAdmin();
console.log('获取营期数据结果:', result);
// 如果没有获取到营期数据,添加一些测试数据以便测试结束营期功能
if (!result || !result.data || !result.data.camp_period) {
console.log('没有获取到营期数据,添加测试数据...');
// 添加一些测试营期事件
events.value.push({
id: 999,
date: formatDateToString(new Date()),
title: '测试营期',
description: '测试营期数据',
isCampEvent: true,
type: 'data'
});
console.log('已添加测试营期数据events:', events.value);
}
} catch (error) { } catch (error) {
console.error('获取营期数据失败:', error); console.error('获取营期数据失败:', error);
// 即使API失败也添加测试数据
console.log('API失败添加测试数据...');
events.value.push({
id: 999,
date: formatDateToString(new Date()),
title: '测试营期',
description: '测试营期数据',
isCampEvent: true,
type: 'data'
});
} }
}); });
</script> </script>
@@ -964,4 +1138,101 @@ onMounted(async () => {
padding: 1px 3px; padding: 1px 3px;
line-height: 1; line-height: 1;
} }
/* 确认弹框样式 */
.confirm-modal {
max-width: 420px;
}
.confirm-text {
font-size: 16px;
color: #495057;
text-align: center;
margin: 0;
line-height: 1.5;
}
.confirm-btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
background: linear-gradient(135deg, #e74c3c, #c0392b);
color: white;
}
.confirm-btn:hover {
background: linear-gradient(135deg, #c0392b, #a93226);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(231, 76, 60, 0.3);
}
/* 强制设置弹框样式 */
.force-modal {
max-width: 500px;
}
.force-modal .modal-header {
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.force-modal .modal-header h3 {
color: white;
margin: 0;
}
.required-text {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
font-weight: 400;
background: rgba(255, 255, 255, 0.2);
padding: 4px 8px;
border-radius: 4px;
}
.required {
color: #e74c3c;
margin-left: 2px;
}
.cancel-btn.disabled {
background: #adb5bd;
color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
.cancel-btn.disabled:hover {
background: #adb5bd;
transform: none;
box-shadow: none;
}
/* 表单验证样式 */
.form-input:invalid {
border-color: #e74c3c;
}
.form-input:invalid:focus {
border-color: #e74c3c;
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.1);
}
/* 强制弹框的保存按钮 */
.force-modal .save-btn {
background: linear-gradient(135deg, #28a745, #20c997);
}
.force-modal .save-btn:hover {
background: linear-gradient(135deg, #218838, #1ea085);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(40, 167, 69, 0.3);
}
</style> </style>

View File

@@ -929,7 +929,7 @@ const excellentRecord = ref({});
await CenterConversionRateVsAverage() await CenterConversionRateVsAverage()
await CenterSeniorManagerList() await CenterSeniorManagerList()
// 获取优秀录音 // 获取优秀录音
await CentergetGoodRecord() // await CentergetGoodRecord()
await CenterGroupList('all') // 初始化加载全部高级经理数据 await CenterGroupList('all') // 初始化加载全部高级经理数据
// 输出缓存信息 // 输出缓存信息