- ToolParam / FormField 新增控件能力: radio(单选按钮组)、switch(勾选框+开关值映射)、quickPeriods(快捷时段)、 dailyExpand(按天展开)、autoPickFirst(默认选中第一项)、 hideIfSingleOption(单选项隐藏)、placeholder(照搬模块页原文案) - LeaveTools: * leave.submit / leave.register 的 leaveType 改单选按钮组并默认选中第一个 * 请假时间补齐快捷时段与「连续请假」勾选框(勾上展开连续天数 + 逐天时段) * 事由 / 附件对齐模块页文案;学生端 classId 单班隐藏、教师端恒显示 - ToolExecutor.expandDailyArgs 认前端显式发来的 continuousLeaveTime (用户在逐天时段里做过微调就以他改的为准,不再按基准覆盖) - 只读查询因下游 302 失败时也回传 landingPath,让「去业务系统查看」按钮有着落
This commit is contained in:
@@ -252,8 +252,12 @@ public class AgentOrchestrator {
|
||||
ToolCallTrace trace = buildTrace(def, args, tr);
|
||||
resp.getToolCalls().add(trace);
|
||||
|
||||
if (!def.isReadOnly()) {
|
||||
resp.setWroteSomething(true);
|
||||
// 写操作 → 去系统里确认结果;只读**失败**(下游 302 / 会话没建起来)→
|
||||
// 同样要给「去系统里自己查看」入口,否则模型嘴上说「点下方按钮」而界面没有按钮。
|
||||
if (!def.isReadOnly() || !tr.isSuccess()) {
|
||||
if (!def.isReadOnly()) {
|
||||
resp.setWroteSomething(true);
|
||||
}
|
||||
if (StringUtils.isNotBlank(def.getLandingPath())) {
|
||||
resp.setLandingPath(def.getLandingPath());
|
||||
}
|
||||
@@ -361,8 +365,12 @@ public class AgentOrchestrator {
|
||||
}
|
||||
auditService.record(session.getSessionId(), caller, def, args, tr);
|
||||
resp.getToolCalls().add(buildTrace(def, args, tr));
|
||||
if (!def.isReadOnly()) {
|
||||
resp.setWroteSomething(true);
|
||||
// 写操作 → 去系统里确认结果;只读**失败**(如下游 302 / 会话没建起来)→
|
||||
// 同样要给「去系统里自己查看」入口,否则模型嘴上说「点下方按钮」而界面没有按钮。
|
||||
if (!def.isReadOnly() || !tr.isSuccess()) {
|
||||
if (!def.isReadOnly()) {
|
||||
resp.setWroteSomething(true);
|
||||
}
|
||||
if (StringUtils.isNotBlank(def.getLandingPath())) {
|
||||
resp.setLandingPath(def.getLandingPath());
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class FormField {
|
||||
/** 中文标签,如「开始时间」 */
|
||||
private String label;
|
||||
|
||||
/** 控件类型:text / textarea / number / date / datetime / select / multiselect / file */
|
||||
/** 控件类型:text / textarea / number / date / datetime / select / multiselect / file / scopes / flow / radio / switch */
|
||||
private String widget;
|
||||
|
||||
/** 数据类型:string / integer / number / boolean / array */
|
||||
@@ -86,6 +86,38 @@ public class FormField {
|
||||
*/
|
||||
private boolean fileAsObject;
|
||||
|
||||
/**
|
||||
* 单选控件({@code widget=radio})在选项就绪后自动选中第一项。
|
||||
* <p>对齐模块页:{@code LeaveApply.vue} 的「请假类型」一进页面就已选中第一个类型。
|
||||
*/
|
||||
private boolean autoPickFirst;
|
||||
|
||||
/**
|
||||
* 选项只有一个时自动填入并隐藏本字段。
|
||||
* <p>对齐模块页:{@code LeaveApply.vue} 的「班级」用 {@code v-if="myClasses.length > 1"}`,
|
||||
* 单班时不渲染、值直接带上。
|
||||
*/
|
||||
private boolean hideWhenSingleOption;
|
||||
|
||||
/**
|
||||
* 快捷时段按钮组:{@code {start: 开始字段名, end: 结束字段名,
|
||||
* items: [{label, start, end}]}};{@code start/end} 为时刻({@code HH:mm:ss})。
|
||||
* <p>对齐模块页 {@code LeaveTimeSection.quickSet()}:沿用已选日期,只换时分秒。
|
||||
*/
|
||||
private Map<String, Object> quickPeriods;
|
||||
|
||||
/** {@code widget=switch} 勾选时提交的值 */
|
||||
private String switchOn;
|
||||
|
||||
/** {@code widget=switch} 未勾选时提交的值 */
|
||||
private String switchOff;
|
||||
|
||||
/** 多行文本框显示行数 */
|
||||
private Integer rows;
|
||||
|
||||
/** 文本最大长度(前端显示字数统计) */
|
||||
private Integer maxLength;
|
||||
|
||||
private boolean required;
|
||||
|
||||
/** 说明(来自工具定义,写清格式与含义) */
|
||||
@@ -94,6 +126,13 @@ public class FormField {
|
||||
/** 格式示例,前端用作 placeholder,如 2026-10-11 08:00:00 */
|
||||
private String example;
|
||||
|
||||
/**
|
||||
* 显式的占位提示(优先于 {@link #example})。
|
||||
* <p>用于对齐业务模块页的原文案,如请假事由的「请输入请假原因.....」——
|
||||
* 这里不该塞它的示例值(「发烧就医,需在家休息」),那是给模型看的参考,不是给用户看的提示。
|
||||
*/
|
||||
private String placeholder;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
|
||||
@@ -619,9 +619,29 @@ public class ToolExecutor {
|
||||
*
|
||||
* <p>与模块页 {@code LeaveTimeSection.regenerate()} 口径一致(每天一段,时分沿用基准)。
|
||||
* 放在执行器里做,浏览器代执行与服务端直调共用同一份逻辑。
|
||||
*
|
||||
* <p>★ 若入参里**已经有** {@code continuousLeaveTime}(助手表单让用户逐天微调过,
|
||||
* 前端会显式发来),则以它为准、不再自行展开 —— 否则用户改好的个别天会被覆盖掉。
|
||||
*/
|
||||
private Map<String, Object> expandDailyArgs(ToolDef def, Map<String, Object> args) {
|
||||
Map<String, Object> extra = new LinkedHashMap<>();
|
||||
|
||||
// ① 前端已经算好逐天时段(用户可单天微调)→ 原样采用,只把基准对齐到第一天
|
||||
Object explicit = args.get("continuousLeaveTime");
|
||||
if (explicit instanceof List && !((List<?>) explicit).isEmpty()) {
|
||||
Object first = ((List<?>) explicit).get(0);
|
||||
if (first instanceof Map) {
|
||||
Object s0 = ((Map<?, ?>) first).get("startDate");
|
||||
Object e0 = ((Map<?, ?>) first).get("endDate");
|
||||
if (s0 != null && e0 != null) {
|
||||
extra.put("startDate", String.valueOf(s0));
|
||||
extra.put("endDate", String.valueOf(e0));
|
||||
}
|
||||
}
|
||||
return extra;
|
||||
}
|
||||
|
||||
// ② 否则按「基准时间 + 天数」自行展开(模型直接调用 / 没走表单时的路径)
|
||||
for (ToolParam p : def.getParams()) {
|
||||
Map<String, String> spec = p.getDailyExpandSpec();
|
||||
if (spec == null || spec.isEmpty()) continue;
|
||||
|
||||
@@ -62,6 +62,9 @@ public class ToolParam {
|
||||
/** 示例值(表单 placeholder,如 2026-10-11 08:00:00),也会进入给模型的描述 */
|
||||
private String example;
|
||||
|
||||
/** 显式占位提示(优先于 {@link #example}),用于照搬业务模块页的原文案 */
|
||||
private String placeholderText;
|
||||
|
||||
/**
|
||||
* 控件类型:text / textarea / number / date / datetime / select / multiselect / file;
|
||||
* 缺省按参数名与类型自动推断。
|
||||
@@ -148,8 +151,52 @@ public class ToolParam {
|
||||
* <p>作用有两个:① 生成表单时把依赖名带给前端,前端据此清空/重新拉取选项;
|
||||
* ② 生成「浏览器代取选项」的地址时,把依赖字段位置留成占位符({@code ?classId={{classId}}})。
|
||||
*/
|
||||
/**
|
||||
* 依赖的先序字段名(用于联动下拉,如「学生」依赖「班级」)
|
||||
*/
|
||||
private List<String> dependsOn;
|
||||
|
||||
/**
|
||||
* 单选控件({@code widget=radio})在选项就绪后**自动选中第一项**。
|
||||
*
|
||||
* <p>★ 对齐模块页行为:HomeLeave 的 {@code LeaveApply.vue} 里「请假类型」是单选按钮组,
|
||||
* 且 {@code watch(leaveTypeOptions, opts => { if (formData.leaveType == null && opts.length)
|
||||
* formData.leaveType = opts[0].id })} —— 一进页面就已经选中第一个类型。
|
||||
* 助手的表单若不自动选中,用户就得比模块页多点一步,且容易漏填导致必填校验卡住。
|
||||
*/
|
||||
private boolean autoPickFirst;
|
||||
|
||||
/**
|
||||
* 选项只有一个时**自动填入并隐藏本字段**。
|
||||
*
|
||||
* <p>★ 对齐模块页行为:{@code LeaveApply.vue} 的「班级」是
|
||||
* {@code v-if="myClasses.length > 1"} —— 教师只有 1 个班时页面上根本没有这个控件,
|
||||
* 值在 {@code onMounted} 里直接带上。助手表单同理:单班自动带上、不显示,
|
||||
* 多班(或选项没取到)才让用户选。
|
||||
*/
|
||||
private boolean hideWhenSingleOption;
|
||||
|
||||
/**
|
||||
* 快捷时段按钮组(对齐 {@code LeaveTimeSection.vue} 的全天/上午/下午)。
|
||||
*
|
||||
* <p>结构:{@code {start: 开始字段名, end: 结束字段名,
|
||||
* items: [{label, start, end}, ...]}};{@code start/end} 是**时刻**({@code HH:mm:ss}),
|
||||
* 点按钮时套用到当前已选日期上(未选日期则用今天)。
|
||||
*/
|
||||
private Map<String, Object> quickPeriods;
|
||||
|
||||
/** {@code widget=switch} 的「开」值(勾选时提交的值),如请假 continuousLeave 的 {@code 1} */
|
||||
private String switchOn;
|
||||
|
||||
/** {@code widget=switch} 的「关」值(未勾选时提交的值),如 {@code 0} */
|
||||
private String switchOff;
|
||||
|
||||
/** 多行文本框显示行数(缺省 3,对齐模块页「请假事由」的 4 行) */
|
||||
private Integer rows;
|
||||
|
||||
/** 文本最大长度(对齐模块页 {@code maxlength} + 字数统计) */
|
||||
private Integer maxLength;
|
||||
|
||||
public ToolParam() {
|
||||
}
|
||||
|
||||
@@ -207,6 +254,15 @@ public class ToolParam {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显式占位提示,照搬业务模块页的原文案(如请假事由的「请输入请假原因.....」)。
|
||||
* <p>与 {@link #eg} 的区别:{@code eg} 是给模型看的**示例值**,这里纯粹是给用户看的提示。
|
||||
*/
|
||||
public ToolParam placeholder(String value) {
|
||||
this.placeholderText = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 指定控件类型 */
|
||||
public ToolParam as(String value) {
|
||||
this.widget = value;
|
||||
@@ -323,6 +379,88 @@ public class ToolParam {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单选按钮组(对齐模块页的 {@code RadioGroup})。
|
||||
* <p>取值仍走 {@link #options}/{@link #optionsFrom},只是渲染成平铺的单选按钮而不是下拉。
|
||||
*/
|
||||
public ToolParam radio() {
|
||||
this.widget = "radio";
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 勾选框 / 开关控件。
|
||||
*
|
||||
* @param onValue 勾选时提交的值(如 {@code "1"})
|
||||
* @param offValue 未勾选时提交的值(如 {@code "0"})
|
||||
*/
|
||||
public ToolParam switchOf(String onValue, String offValue) {
|
||||
this.widget = "switch";
|
||||
this.switchOn = onValue;
|
||||
this.switchOff = offValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 选项就绪后自动选中第一项(模块页「请假类型」的默认行为) */
|
||||
public ToolParam autoPickFirst() {
|
||||
this.autoPickFirst = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 选项只有一个时自动填入并隐藏本字段(模块页「班级」:只教一个班时不显示) */
|
||||
public ToolParam hideIfSingleOption() {
|
||||
this.hideWhenSingleOption = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷时段按钮组(全天 / 上午 / 下午 …),点一下同时填好开始与结束**时刻**。
|
||||
*
|
||||
* <pre>
|
||||
* ToolParam.required("startDate", "string", "...")
|
||||
* .as("datetime")
|
||||
* .quickPeriods("startDate", "endDate",
|
||||
* "全天", "08:00:00", "18:00:00",
|
||||
* "上午", "08:00:00", "12:00:00",
|
||||
* "下午", "12:00:00", "18:00:00")
|
||||
* </pre>
|
||||
*
|
||||
* <p>三元组按「标签、开始时刻、结束时刻」顺序依次传入(个数必须为 3 的倍数)。
|
||||
* 与模块页 {@code LeaveTimeSection.quickSet()} 口径一致:**沿用已选的日期**,
|
||||
* 只把时分秒换掉;还没选日期就按今天。
|
||||
*/
|
||||
public ToolParam quickPeriods(String startField, String endField, String... labelStartEnd) {
|
||||
if (labelStartEnd == null || labelStartEnd.length < 3 || labelStartEnd.length % 3 != 0) {
|
||||
throw new IllegalArgumentException("quickPeriods 需要「标签, 开始时刻, 结束时刻」三元组");
|
||||
}
|
||||
List<Map<String, String>> items = new ArrayList<>();
|
||||
for (int i = 0; i + 2 < labelStartEnd.length; i += 3) {
|
||||
Map<String, String> it = new LinkedHashMap<>();
|
||||
it.put("label", labelStartEnd[i]);
|
||||
it.put("start", labelStartEnd[i + 1]);
|
||||
it.put("end", labelStartEnd[i + 2]);
|
||||
items.add(it);
|
||||
}
|
||||
Map<String, Object> spec = new LinkedHashMap<>();
|
||||
spec.put("start", startField);
|
||||
spec.put("end", endField);
|
||||
spec.put("items", items);
|
||||
this.quickPeriods = spec;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 多行文本框行数 */
|
||||
public ToolParam rows(int value) {
|
||||
this.rows = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 文本最大长度(前端显示字数统计,超出时截断提示) */
|
||||
public ToolParam maxLength(int value) {
|
||||
this.maxLength = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** 覆盖附件上传地址(缺省由编排层按 {@code /<模块>/api/base/FileUpload} 兜底) */
|
||||
public ToolParam upload(String path) {
|
||||
this.uploadPathOverride = path;
|
||||
@@ -407,6 +545,7 @@ public class ToolParam {
|
||||
f.setRequired(required);
|
||||
f.setDescription(description);
|
||||
f.setExample(example);
|
||||
f.setPlaceholder(placeholderText);
|
||||
f.setDefaultValue(defaultValue);
|
||||
f.setOptions(optionList());
|
||||
// 把「动态选项来源」一并下发:前端据此区分「本来就没有选项」和「字典没取到」
|
||||
@@ -422,6 +561,14 @@ public class ToolParam {
|
||||
f.setMirrorOf(mirrorLabelOf);
|
||||
f.setDailyExpand(dailyExpandSpec);
|
||||
f.setFileAsObject(fileObject);
|
||||
// ★ 表单呈现细节(对齐模块页的控件形态)
|
||||
f.setAutoPickFirst(autoPickFirst);
|
||||
f.setHideWhenSingleOption(hideWhenSingleOption);
|
||||
f.setQuickPeriods(quickPeriods);
|
||||
f.setSwitchOn(switchOn);
|
||||
f.setSwitchOff(switchOff);
|
||||
f.setRows(rows);
|
||||
f.setMaxLength(maxLength);
|
||||
// 选项是内置兜底(不是学校实时字典)时如实告知,前端可提示用户
|
||||
f.setOptionsFallback(isUsingFallbackOptions());
|
||||
return f;
|
||||
|
||||
@@ -35,15 +35,34 @@ public final class LeaveTools {
|
||||
private static final String DT_HINT = "格式 yyyy-MM-dd HH:mm:ss(如 2026-10-12 08:00:00)";
|
||||
|
||||
/**
|
||||
* 请假类型内置兜底(与 HomeLeave 前端 {@code LEAVE_TYPE_OPTIONS}、后端字典种子一致)。
|
||||
* <p>权威来源是字典接口 {@code GET /api/dict/LeaveTypeOptions}(学校可在「应用设置」里自定义),
|
||||
* 该接口需登录会话、服务端取不到 —— 表单会带上下拉并交给浏览器用用户登录态取实时字典,
|
||||
* 这份兜底保证「取不到时下拉里也还有标准项」,不会退化成让用户填「类型编号」的数字框。
|
||||
* 请假类型内置兜底(与 HomeLeave 前端 {@code LEAVE_TYPE_OPTIONS} 的种子一致)。
|
||||
*
|
||||
* <p>★ 权威来源是字典接口 {@code GET /api/dict/LeaveTypeOptions}(学校可在「应用设置」里
|
||||
* 增删改,种子就是下面两项)。该接口需登录会话、服务端取不到 —— 表单会带上下拉并交给
|
||||
* 浏览器用用户登录态取实时字典,**取到就以学校的为准**(`.optionsFrom` 全部覆盖)。
|
||||
* 这份兜底只保证「实时字典没取到时下拉不是空的」,避免退化成让用户填「类型编号」的数字框。
|
||||
*/
|
||||
private static final String[] LEAVE_TYPE_VALUES = {"1", "2"};
|
||||
|
||||
private static final String[] LEAVE_TYPE_LABELS = {"病假", "事假及其他"};
|
||||
|
||||
/**
|
||||
* 给模型看的请假类型说明。
|
||||
*
|
||||
* <p>★ 这里**不能**写死「1 病假、2 事假及其他」的编号含义 —— 编号是学校自定义的,
|
||||
* 换一所学校含义就变了。模型也不需要知道编号:表单的「请假类型」下拉会带出实时字典。
|
||||
*/
|
||||
private static final String LEAVE_TYPE_HINT =
|
||||
"请假类型编号(来自学校「应用设置」里配置的字典,不同学校编号含义可能不同);"
|
||||
+ "不确定就省略,表单会列出当前学校可选类型让你选";
|
||||
|
||||
/** 请假时间快捷时段(对齐模块页 LeaveTimeSection 的全天 / 上午 / 下午按钮) */
|
||||
private static final String[] QUICK_PERIODS = {
|
||||
"全天", "08:00:00", "18:00:00",
|
||||
"上午", "08:00:00", "12:00:00",
|
||||
"下午", "12:00:00", "18:00:00",
|
||||
};
|
||||
|
||||
private LeaveTools() {
|
||||
}
|
||||
|
||||
@@ -84,8 +103,10 @@ public final class LeaveTools {
|
||||
.p(ToolParam.optional("pageIndex", "integer", "页码,从 1 开始").dft("1"),
|
||||
ToolParam.optional("pageSize", "integer", "每页条数,默认 10").dft("10"),
|
||||
ToolParam.optional("status", "integer", "按状态筛选:0待审核 1已通过 2待撤销 3已撤销 4撤销拒绝 5待销假 6已销假 7销假拒绝 8已拒绝"),
|
||||
ToolParam.optional("leaveType", "integer", "按请假类型筛选:1 病假、2 事假及其他")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS),
|
||||
ToolParam.optional("leaveType", "integer", "按请假类型筛选(编号含义由学校的「应用设置」定义,表单会列出可选项)")
|
||||
.label("请假类型")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS)
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v"),
|
||||
ToolParam.optional("startDate", "string", "开始日期(含),yyyy-MM-dd"),
|
||||
ToolParam.optional("endDate", "string", "结束日期(含),yyyy-MM-dd"),
|
||||
ToolParam.optional("keyword", "string", "关键词(学生姓名/事由)"))
|
||||
@@ -103,8 +124,10 @@ public final class LeaveTools {
|
||||
.p(ToolParam.optional("startDate", "string", "开始日期 yyyy-MM-dd"),
|
||||
ToolParam.optional("endDate", "string", "结束日期 yyyy-MM-dd"),
|
||||
ToolParam.optional("classId", "integer", "班级 id"),
|
||||
ToolParam.optional("leaveType", "integer", "请假类型:1 病假、2 事假及其他")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS))
|
||||
ToolParam.optional("leaveType", "integer", "请假类型(编号含义由学校的「应用设置」定义,表单会列出可选项)")
|
||||
.label("请假类型")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS)
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v"))
|
||||
.readAt("/stats")
|
||||
.modId(6));
|
||||
|
||||
@@ -115,30 +138,25 @@ public final class LeaveTools {
|
||||
+ "尽量确认:请假类型 leaveType、开始时间 startDate、结束时间 endDate、事由 remarks;"
|
||||
+ "不确定的字段直接省略,系统会弹表单让用户补齐,不要用文字反复追问。",
|
||||
"POST_JSON", "/api/leave/Save")
|
||||
.p(ToolParam.required("leaveType", "integer", "请假类型:1 病假、2 事假及其他")
|
||||
// 字段顺序与模块页「请假申请」完全一致:班级 → 请假类型 → 请假时间 → 事由 → 佐证材料
|
||||
.p(ToolParam.optional("classId", "integer", "班级 id;只有一个班级时可不传(后端会取默认班级)")
|
||||
.label("班级")
|
||||
.optionsFrom("leave.myClasses").optionsKeys("classId", "className")
|
||||
// 模块页:只有多个班级时才显示「班级」,单班自动带上
|
||||
.hideIfSingleOption(),
|
||||
ToolParam.required("leaveType", "integer", LEAVE_TYPE_HINT)
|
||||
.label("请假类型")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS)
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v"),
|
||||
ToolParam.required("startDate", "string", "请假开始时间," + DT_HINT)
|
||||
.label("开始时间").eg("2026-10-12 08:00:00"),
|
||||
ToolParam.required("endDate", "string", "请假结束时间," + DT_HINT)
|
||||
.label("结束时间").eg("2026-10-12 17:00:00"),
|
||||
ToolParam.required("remarks", "string", "请假事由")
|
||||
.label("请假事由").as("textarea").eg("发烧就医,需在家休息"),
|
||||
ToolParam.optional("classId", "integer", "班级 id;只有一个班级时可不传(后端会取默认班级)")
|
||||
.label("班级")
|
||||
.optionsFrom("leave.myClasses").optionsKeys("classId", "className"),
|
||||
ToolParam.optional("continuousLeave", "integer", "是否连续请假(同一时段连续多天)")
|
||||
.label("连续请假").dft("0")
|
||||
.options(new String[]{"0", "1"}, new String[]{"否", "是"})
|
||||
// 选「是」时按下面的连续天数把起止时间复制成逐天时间段,
|
||||
// 生成模块要的 continuousLeaveTime(与模块页 LeaveTimeSection 一致)
|
||||
.expandDaily("continuousLeaveNum", "startDate", "endDate"),
|
||||
ToolParam.optional("continuousLeaveNum", "integer", "连续天数(continuousLeave=1 时有效,1~30)")
|
||||
.label("连续天数").dft("1"),
|
||||
// ★ 模块传的是 number[](JSON 数组),不是逗号串,故不声明 join
|
||||
ToolParam.optional("fileIds", "array", "佐证材料附件 id 列表(由表单的附件上传得到,可多份)")
|
||||
.label("佐证材料").as("file").multiple(true))
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v")
|
||||
// 模块页是单选按钮组,且一进页面就已选中第一个类型
|
||||
.radio().autoPickFirst())
|
||||
.p(leaveTimeParams())
|
||||
.p(ToolParam.required("remarks", "string", "请假事由")
|
||||
.label("请假事由").as("textarea").rows(4).maxLength(1000)
|
||||
// placeholder 照搬模块页原文案(eg 只给模型当填写参考)
|
||||
.placeholder("请输入请假原因.....")
|
||||
.eg("发烧就医,需在家休息"),
|
||||
evidenceParam())
|
||||
.fix("type", 2)
|
||||
.fullForm()
|
||||
.write("/my")
|
||||
@@ -151,6 +169,7 @@ public final class LeaveTools {
|
||||
+ "学生自己请假请改用「提交请假申请」,不要用本工具。"
|
||||
+ "不确定的字段直接省略,系统会弹表单让用户补齐,不要用文字反复追问。",
|
||||
"POST_JSON", "/api/leave/Save")
|
||||
// 字段顺序与模块页「请假登记」一致:班级 → 学生 → 请假类型 → 请假时间 → 事由 → 佐证材料
|
||||
.p(ToolParam.required("classId", "integer", "学生所在班级 id(决定下面「学生」下拉的可选项)")
|
||||
.label("班级")
|
||||
.optionsFrom("leave.myClasses").optionsKeys("classId", "className"),
|
||||
@@ -158,25 +177,17 @@ public final class LeaveTools {
|
||||
.label("学生")
|
||||
.optionsFrom("leave.classStudents").optionsKeys("stuId", "stuName")
|
||||
.dependsOn("classId"),
|
||||
ToolParam.required("leaveType", "integer", "请假类型:1 病假、2 事假及其他")
|
||||
ToolParam.required("leaveType", "integer", LEAVE_TYPE_HINT)
|
||||
.label("请假类型")
|
||||
.fallbackOptions(LEAVE_TYPE_VALUES, LEAVE_TYPE_LABELS)
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v"),
|
||||
ToolParam.required("startDate", "string", "请假开始时间," + DT_HINT)
|
||||
.label("开始时间").eg("2026-10-12 08:00:00"),
|
||||
ToolParam.required("endDate", "string", "请假结束时间," + DT_HINT)
|
||||
.label("结束时间").eg("2026-10-12 17:00:00"),
|
||||
ToolParam.required("remarks", "string", "请假事由")
|
||||
.label("请假事由").as("textarea").eg("家中有事,需请假一天"),
|
||||
ToolParam.optional("continuousLeave", "integer", "是否连续请假(同一时段连续多天)")
|
||||
.label("连续请假").dft("0")
|
||||
.options(new String[]{"0", "1"}, new String[]{"否", "是"})
|
||||
.expandDaily("continuousLeaveNum", "startDate", "endDate"),
|
||||
ToolParam.optional("continuousLeaveNum", "integer", "连续天数(continuousLeave=1 时有效,1~30)")
|
||||
.label("连续天数").dft("1"),
|
||||
// ★ 模块传的是 number[](JSON 数组),不是逗号串,故不声明 join
|
||||
ToolParam.optional("fileIds", "array", "佐证材料附件 id 列表(由表单的附件上传得到,可多份)")
|
||||
.label("佐证材料").as("file").multiple(true))
|
||||
.optionsFrom("leave.types").optionsKeys("k", "v")
|
||||
.radio().autoPickFirst())
|
||||
.p(leaveTimeParams())
|
||||
.p(ToolParam.required("remarks", "string", "请假事由")
|
||||
.label("请假事由").as("textarea").rows(4).maxLength(1000)
|
||||
.placeholder("请输入请假原因.....")
|
||||
.eg("家中有事,需请假一天"),
|
||||
evidenceParam())
|
||||
.fix("type", 1)
|
||||
.fullForm()
|
||||
.write("/register")
|
||||
@@ -229,6 +240,39 @@ public final class LeaveTools {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 「请假时间」区(两个写工具共用,保证与模块页 {@code LeaveTimeSection} 口径一致):
|
||||
* 起止时间 + <b>快捷时段按钮</b>(全天/上午/下午)+ <b>连续请假勾选框</b> + 连续天数。
|
||||
*/
|
||||
private static ToolParam[] leaveTimeParams() {
|
||||
return new ToolParam[]{
|
||||
ToolParam.required("startDate", "string", "请假开始时间," + DT_HINT)
|
||||
.label("开始时间").eg("2026-10-12 08:00:00")
|
||||
.as("datetime")
|
||||
// 与模块页一致:点「全天 / 上午 / 下午」直接填好时段的时分秒(日期沿用已选的)
|
||||
.quickPeriods("startDate", "endDate", QUICK_PERIODS),
|
||||
ToolParam.required("endDate", "string", "请假结束时间," + DT_HINT)
|
||||
.label("结束时间").eg("2026-10-12 17:00:00")
|
||||
.as("datetime"),
|
||||
ToolParam.optional("continuousLeave", "integer", "是否连续请假(同一时段连续多天)")
|
||||
.label("连续请假").dft("0")
|
||||
// 模块页是一个勾选框(勾上才展开天数与逐天时段),不是「是/否」下拉
|
||||
.switchOf("1", "0")
|
||||
// 勾选时按天数把起止时间复制成逐天时间段,生成 continuousLeaveTime
|
||||
.expandDaily("continuousLeaveNum", "startDate", "endDate"),
|
||||
ToolParam.optional("continuousLeaveNum", "integer", "连续天数(1~30,仅勾选「连续请假」时有效)")
|
||||
.label("连续天数").dft("1"),
|
||||
};
|
||||
}
|
||||
|
||||
/** 佐证材料(附件):模块传的是 {@code number[]}(JSON 数组),不是逗号串,故不声明 join */
|
||||
private static ToolParam evidenceParam() {
|
||||
return ToolParam.optional("fileIds", "array", "佐证材料附件 id 列表(由表单的附件上传得到,可多份)")
|
||||
.label("佐证材料").as("file").multiple(true)
|
||||
// 与模块页附件区下方的说明一致
|
||||
.placeholder("可选,大小不超过 200MB");
|
||||
}
|
||||
|
||||
/** 供文档/目录使用 */
|
||||
public static List<String> moduleAlias() {
|
||||
return Arrays.asList("请假", "学生请假", "homeleave");
|
||||
|
||||
Reference in New Issue
Block a user