feat(export): 优化时间范围选择逻辑,限制跨度不超过1年
- 修改后端时间跨度验证逻辑,定义1年为从开始日期往后推1年 - 前端日期选择组件新增选择过程监听,记录首选日期以限制第二个日期范围 - 实现禁用时间函数,禁止选择超过1年跨度的时间范围 - 添加时间跨度变化检测,超出1年时清空选择并弹窗提示 - 前端导入中文语言包,支持ElementPlus中文本地化 - API模块统一业务错误码处理,非0码抛出带错误信息异常
This commit is contained in:
parent
c56c738992
commit
07eafb5684
|
|
@ -23,6 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// validateTimeRange 验证时间范围不超过1年
|
// validateTimeRange 验证时间范围不超过1年
|
||||||
|
// 1年的定义:从开始日期往后推1年
|
||||||
func validateTimeRange(startVal, endVal interface{}) error {
|
func validateTimeRange(startVal, endVal interface{}) error {
|
||||||
var startStr, endStr string
|
var startStr, endStr string
|
||||||
|
|
||||||
|
|
@ -60,12 +61,14 @@ func validateTimeRange(startVal, endVal interface{}) error {
|
||||||
return errors.New("结束时间必须晚于开始时间")
|
return errors.New("结束时间必须晚于开始时间")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算时间跨度
|
// 计算开始日期往后推1年的时间点
|
||||||
duration := endTime.Sub(startTime)
|
oneYearLater := startTime.AddDate(1, 0, 0)
|
||||||
oneYear := 365 * 24 * time.Hour
|
|
||||||
|
|
||||||
if duration > oneYear {
|
// 如果结束时间超过这个时间点,则超过1年限制
|
||||||
return fmt.Errorf("时间跨度超过1年限制,当前跨度为 %.1f 天,最多允许 365 天", duration.Hours()/24)
|
if endTime.After(oneYearLater) {
|
||||||
|
duration := endTime.Sub(startTime)
|
||||||
|
daysCount := duration.Hours() / 24
|
||||||
|
return fmt.Errorf("时间跨度超过1年限制,当前跨度为 %.1f 天,最多允许 365 天", daysCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -235,12 +235,15 @@
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="exportForm.dateRange"
|
v-model="exportForm.dateRange"
|
||||||
type="datetimerange"
|
type="datetimerange"
|
||||||
|
range-separator="至"
|
||||||
start-placeholder="开始时间"
|
start-placeholder="开始时间"
|
||||||
end-placeholder="结束时间"
|
end-placeholder="结束时间"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
:shortcuts="dateShortcuts"
|
:shortcuts="dateShortcuts"
|
||||||
:default-time="dateDefaultTime"
|
:default-time="dateDefaultTime"
|
||||||
:disabled-date="disabledDate"
|
:disabled-date="disabledDate"
|
||||||
|
@calendar-change="onCalendarChange"
|
||||||
|
@change="onDateRangeChange"
|
||||||
style="width:100%"
|
style="width:100%"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -308,6 +311,7 @@
|
||||||
<script src="./config.js"></script>
|
<script src="./config.js"></script>
|
||||||
<script src="./vendor/vue.global.prod.js"></script>
|
<script src="./vendor/vue.global.prod.js"></script>
|
||||||
<script src="./vendor/element-plus.full.min.js"></script>
|
<script src="./vendor/element-plus.full.min.js"></script>
|
||||||
|
<script src="./vendor/element-plus-zh-cn.js"></script>
|
||||||
<!-- 模块化架构 -->
|
<!-- 模块化架构 -->
|
||||||
<script src="./modules/config.js"></script>
|
<script src="./modules/config.js"></script>
|
||||||
<script src="./modules/utils.js"></script>
|
<script src="./modules/utils.js"></script>
|
||||||
|
|
|
||||||
93
web/main.js
93
web/main.js
|
|
@ -190,34 +190,44 @@ const app = createApp({
|
||||||
new Date(2000, 0, 1, 23, 59, 59),
|
new Date(2000, 0, 1, 23, 59, 59),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 用于记录用户正在选择的第一个日期(用于限制第二个日期的可选范围)
|
||||||
|
const pickerMinDate = Vue.ref(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日历面板变化事件 - 当用户点击日历时触发
|
||||||
|
* 用于记录用户选择的第一个日期
|
||||||
|
* @param {Array} dates - 已选日期数组
|
||||||
|
*/
|
||||||
|
const onCalendarChange = (dates) => {
|
||||||
|
if (dates && dates.length === 1) {
|
||||||
|
// 用户选择了第一个日期
|
||||||
|
pickerMinDate.value = dates[0];
|
||||||
|
} else {
|
||||||
|
// 选择完成或清空
|
||||||
|
pickerMinDate.value = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 禁用超过一年的日期
|
* 禁用超过一年的日期
|
||||||
|
* 当用户选择了第一个日期后,自动限制第二个日期的可选范围
|
||||||
* @param {Date} date - 要检查的日期
|
* @param {Date} date - 要检查的日期
|
||||||
* @returns {boolean} 是否禁用
|
* @returns {boolean} 是否禁用
|
||||||
*/
|
*/
|
||||||
const disabledDate = (date) => {
|
const disabledDate = (date) => {
|
||||||
if (!date) return false;
|
if (!date) return false;
|
||||||
|
|
||||||
// 如果已经选择了开始时间,计算结束时间限制
|
// 如果用户正在选择第二个日期,限制范围不超过1年
|
||||||
if (state.exportForm.dateRange && state.exportForm.dateRange[0]) {
|
if (pickerMinDate.value) {
|
||||||
const startTime = new Date(state.exportForm.dateRange[0]);
|
const selectedDate = new Date(pickerMinDate.value);
|
||||||
const oneYearLater = new Date(startTime);
|
const oneYearMs = 365 * 24 * 60 * 60 * 1000;
|
||||||
oneYearLater.setFullYear(startTime.getFullYear() + 1);
|
|
||||||
oneYearLater.setDate(oneYearLater.getDate() - 1); // 减去一天以确保不超过一年
|
|
||||||
|
|
||||||
// 只限制结束时间不能超过开始时间一年后
|
// 计算允许的日期范围
|
||||||
if (date > oneYearLater) return true;
|
const minAllowed = new Date(selectedDate.getTime() - oneYearMs);
|
||||||
}
|
const maxAllowed = new Date(selectedDate.getTime() + oneYearMs);
|
||||||
|
|
||||||
// 如果已经选择了结束时间,计算开始时间限制
|
// 禁用超出范围的日期
|
||||||
if (state.exportForm.dateRange && state.exportForm.dateRange[1]) {
|
return date < minAllowed || date > maxAllowed;
|
||||||
const endTime = new Date(state.exportForm.dateRange[1]);
|
|
||||||
const oneYearEarlier = new Date(endTime);
|
|
||||||
oneYearEarlier.setFullYear(endTime.getFullYear() - 1);
|
|
||||||
oneYearEarlier.setDate(oneYearEarlier.getDate() + 1); // 加上一天
|
|
||||||
|
|
||||||
// 只限制开始时间不能早于结束时间一年前
|
|
||||||
if (date < oneYearEarlier) return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -288,6 +298,41 @@ const app = createApp({
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证时间跨度是否超过1年
|
||||||
|
* 1年的定义:从开始日期往后推1年
|
||||||
|
* @param {Array} dateRange - 时间范围 [开始时间, 结束时间]
|
||||||
|
* @returns {boolean} 是否超过1年
|
||||||
|
*/
|
||||||
|
const isDateRangeExceedOneYear = (dateRange) => {
|
||||||
|
if (!dateRange || dateRange.length !== 2 || !dateRange[0] || !dateRange[1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const start = new Date(dateRange[0]);
|
||||||
|
const end = new Date(dateRange[1]);
|
||||||
|
|
||||||
|
// 计算开始日期往后推1年的时间点
|
||||||
|
const oneYearLater = new Date(start);
|
||||||
|
oneYearLater.setFullYear(start.getFullYear() + 1);
|
||||||
|
|
||||||
|
// 如果结束时间超过这个时间点,则超过1年
|
||||||
|
return end > oneYearLater;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间范围变化处理函数
|
||||||
|
* 如果时间跨度超过1年,清空并提示
|
||||||
|
* @param {Array} val - 时间范围
|
||||||
|
*/
|
||||||
|
const onDateRangeChange = (val) => {
|
||||||
|
if (isDateRangeExceedOneYear(val)) {
|
||||||
|
// 清空时间范围
|
||||||
|
state.exportForm.dateRange = null;
|
||||||
|
// 显示提示
|
||||||
|
showMessage('时间跨度不能超过1年(365天),请重新选择', 'warning');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ==================== 表单引用 ====================
|
// ==================== 表单引用 ====================
|
||||||
const createFormRef = Vue.ref(null);
|
const createFormRef = Vue.ref(null);
|
||||||
const editFormRef = Vue.ref(null);
|
const editFormRef = Vue.ref(null);
|
||||||
|
|
@ -820,7 +865,11 @@ const app = createApp({
|
||||||
showMessage('任务创建返回异常', 'error');
|
showMessage('任务创建返回异常', 'error');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showMessage(error.message || '导出失败', 'error');
|
// 使用模态框显示错误信息
|
||||||
|
ElementPlus.ElMessageBox.alert(error.message || '导出失败', '提示', {
|
||||||
|
type: 'error',
|
||||||
|
confirmButtonText: '确定'
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
state.exportSubmitting = false;
|
state.exportSubmitting = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1093,6 +1142,8 @@ const app = createApp({
|
||||||
dateDefaultTime,
|
dateDefaultTime,
|
||||||
dateShortcuts,
|
dateShortcuts,
|
||||||
disabledDate,
|
disabledDate,
|
||||||
|
onCalendarChange,
|
||||||
|
onDateRangeChange,
|
||||||
// 表单引用
|
// 表单引用
|
||||||
createFormRef,
|
createFormRef,
|
||||||
editFormRef,
|
editFormRef,
|
||||||
|
|
@ -1132,7 +1183,9 @@ const app = createApp({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(ElementPlus);
|
app.use(ElementPlus, {
|
||||||
|
locale: window.ElementPlusLocaleZhCn
|
||||||
|
});
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,18 @@ const post = async (endpoint, body, options = {}) => {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
throw new Error(errorText || `请求失败: ${response.status}`);
|
throw new Error(errorText || `请求失败: ${response.status}`);
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
// 检查业务错误码
|
||||||
|
if (result && result.code !== undefined && result.code !== 0) {
|
||||||
|
const error = new Error(result.msg || '操作失败');
|
||||||
|
error.code = result.code;
|
||||||
|
error.data = result.data;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*! Element Plus v2.13.0 - Chinese (zh-cn) */
|
||||||
|
(function(global) {
|
||||||
|
var zhCn = {
|
||||||
|
name: "zh-cn",
|
||||||
|
el: {
|
||||||
|
breadcrumb: { label: "面包屑" },
|
||||||
|
colorpicker: {
|
||||||
|
confirm: "确定",
|
||||||
|
clear: "清空",
|
||||||
|
defaultLabel: "颜色选择器",
|
||||||
|
description: "当前颜色 {color},按 Enter 键选择新颜色"
|
||||||
|
},
|
||||||
|
datepicker: {
|
||||||
|
now: "此刻",
|
||||||
|
today: "今天",
|
||||||
|
cancel: "取消",
|
||||||
|
clear: "清空",
|
||||||
|
confirm: "确定",
|
||||||
|
dateTablePrompt: "使用方向键与 Enter 键可选择日期",
|
||||||
|
monthTablePrompt: "使用方向键与 Enter 键可选择月份",
|
||||||
|
yearTablePrompt: "使用方向键与 Enter 键可选择年份",
|
||||||
|
selectedDate: "已选日期",
|
||||||
|
selectDate: "选择日期",
|
||||||
|
selectTime: "选择时间",
|
||||||
|
startDate: "开始日期",
|
||||||
|
startTime: "开始时间",
|
||||||
|
endDate: "结束日期",
|
||||||
|
endTime: "结束时间",
|
||||||
|
prevYear: "前一年",
|
||||||
|
nextYear: "后一年",
|
||||||
|
prevMonth: "上个月",
|
||||||
|
nextMonth: "下个月",
|
||||||
|
year: "年",
|
||||||
|
month1: "1 月",
|
||||||
|
month2: "2 月",
|
||||||
|
month3: "3 月",
|
||||||
|
month4: "4 月",
|
||||||
|
month5: "5 月",
|
||||||
|
month6: "6 月",
|
||||||
|
month7: "7 月",
|
||||||
|
month8: "8 月",
|
||||||
|
month9: "9 月",
|
||||||
|
month10: "10 月",
|
||||||
|
month11: "11 月",
|
||||||
|
month12: "12 月",
|
||||||
|
weeks: { sun: "日", mon: "一", tue: "二", wed: "三", thu: "四", fri: "五", sat: "六" },
|
||||||
|
weeksFull: { sun: "星期日", mon: "星期一", tue: "星期二", wed: "星期三", thu: "星期四", fri: "星期五", sat: "星期六" },
|
||||||
|
months: { jan: "一月", feb: "二月", mar: "三月", apr: "四月", may: "五月", jun: "六月", jul: "七月", aug: "八月", sep: "九月", oct: "十月", nov: "十一月", dec: "十二月" }
|
||||||
|
},
|
||||||
|
inputNumber: { decrease: "减少数值", increase: "增加数值" },
|
||||||
|
select: { loading: "加载中", noMatch: "无匹配数据", noData: "无数据", placeholder: "请选择" },
|
||||||
|
mention: { loading: "加载中" },
|
||||||
|
dropdown: { toggleDropdown: "切换下拉选项" },
|
||||||
|
cascader: { noMatch: "无匹配数据", loading: "加载中", placeholder: "请选择", noData: "暂无数据" },
|
||||||
|
pagination: {
|
||||||
|
goto: "前往",
|
||||||
|
pagesize: "条/页",
|
||||||
|
total: "共 {total} 条",
|
||||||
|
pageClassifier: "页",
|
||||||
|
page: "页",
|
||||||
|
prev: "上一页",
|
||||||
|
next: "下一页",
|
||||||
|
currentPage: "第 {pager} 页",
|
||||||
|
prevPages: "向前 {pager} 页",
|
||||||
|
nextPages: "向后 {pager} 页"
|
||||||
|
},
|
||||||
|
dialog: { close: "关闭此对话框" },
|
||||||
|
drawer: { close: "关闭此对话框" },
|
||||||
|
messagebox: { title: "提示", confirm: "确定", cancel: "取消", error: "输入的数据不合法!", close: "关闭此对话框" },
|
||||||
|
upload: { deleteTip: "按 Delete 键可删除", delete: "删除", preview: "查看图片", continue: "继续上传" },
|
||||||
|
slider: { defaultLabel: "滑块介于 {min} 至 {max}", defaultRangeStartLabel: "选择起始值", defaultRangeEndLabel: "选择结束值" },
|
||||||
|
table: { emptyText: "暂无数据", confirmFilter: "筛选", resetFilter: "重置", clearFilter: "全部", sumText: "合计" },
|
||||||
|
tag: { close: "关闭此标签" },
|
||||||
|
tour: { next: "下一步", previous: "上一步", finish: "结束导览", close: "关闭此对话框" },
|
||||||
|
tree: { emptyText: "暂无数据" },
|
||||||
|
transfer: { noMatch: "无匹配数据", noData: "无数据", titles: ["列表 1", "列表 2"], filterPlaceholder: "请输入搜索内容", noCheckedFormat: "共 {total} 项", hasCheckedFormat: "已选 {checked}/{total} 项" },
|
||||||
|
image: { error: "加载失败" },
|
||||||
|
pageHeader: { title: "返回" },
|
||||||
|
popconfirm: { confirmButtonText: "确定", cancelButtonText: "取消" },
|
||||||
|
carousel: { leftArrow: "上一张幻灯片", rightArrow: "下一张幻灯片", indicator: "幻灯片切换至索引 {index}" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露到全局
|
||||||
|
global.ElementPlusLocaleZhCn = zhCn;
|
||||||
|
})(typeof window !== 'undefined' ? window : this);
|
||||||
Loading…
Reference in New Issue