Compare commits

...

157 Commits

Author SHA1 Message Date
71357f9621 完成基础信息页面开发 2026-03-19 15:55:37 +08:00
RISE
8d8a7db748 aa 2026-03-16 10:59:51 +08:00
吴红兵
98bc6585c8 采购申请增加指定勾选导出 2026-03-15 20:01:01 +08:00
吴红兵
01ef25b70a 招标文件 2026-03-15 19:45:54 +08:00
吴红兵
98799a2555 招标文件 2026-03-15 18:56:27 +08:00
吴红兵
7e0371134b 招标文件修改 2026-03-15 17:39:50 +08:00
吴红兵
9b60b7f29c 招标文件修改 2026-03-15 17:29:31 +08:00
吴红兵
74596fd81f 招标文件修改 2026-03-15 16:59:02 +08:00
吴红兵
055ff1f1e9 招标文件修改 2026-03-15 15:10:52 +08:00
吴红兵
2b7ea0754d 修改代理机构 2026-03-15 14:27:13 +08:00
吴红兵
33929c0f39 修改代理机构 2026-03-15 14:21:17 +08:00
吴红兵
129a83510f 修改代理机构 2026-03-15 14:00:12 +08:00
吴红兵
832eb3916b 修改代理机构 2026-03-15 13:56:40 +08:00
吴红兵
eec90d963d 修改实施采购 2026-03-15 13:08:38 +08:00
吴红兵
1ca0e3c452 修改实施采购 2026-03-15 12:18:05 +08:00
吴红兵
b273c44104 修改履约验收 2026-03-15 11:56:08 +08:00
吴红兵
7497986573 fix(purchase): 修复合同流程状态判断问题
- 将 contractFlowStatus 从独立 ref 改为 form 对象中的属性
- 确保通过 v-model 传递时状态正确同步
- 更新所有判断条件使用 form.contractFlowStatus
2026-03-15 11:35:22 +08:00
吴红兵
451004d84e fix(purchase): 修复合同信息未正确传递的问题
- loadData中始终传递合同相关字段(不再依赖batches是否存在)
- 添加contractFlowStatus、contractName、contractNo、contractMoney字段传递
- 更新DEFAULT_COMMON_FORM包含新字段
2026-03-15 11:26:05 +08:00
吴红兵
8bc778345b feat(purchase): 履约验收优化界面,移除是否签订合同选择
- 移除是否签订合同手动选择,由系统自动判断
- 合同信息单独分栏显示,展示合同名称/编号/金额/供应商
- 已签订合同以绿色卡片展示
- 未签订合同显示警告提示
- 合同审批中显示错误提示,禁止操作
- 供应商名称合并到合同信息中展示
- 未签订合同时才显示成交金额和供应商输入
2026-03-15 11:13:10 +08:00
吴红兵
90ae916b93 feat(purchase): 履约验收自动显示合同信息和供应商信息
- 选择“是否签订合同”为“是”时,自动查询采购合同
- 合同流程已完成时,自动显示合同信息和供应商信息
- 合同不存在或流程未完成时,提示无法进行履约操作
- 移除合同下拉选择,改为只读显示
- 添加合同状态常量(运行中/已完成/已作废)
2026-03-15 10:53:43 +08:00
吴红兵
2114f79fb1 采购合同修改 2026-03-15 00:36:59 +08:00
吴红兵
53c46d0fc7 采购合同修改 2026-03-15 00:32:02 +08:00
吴红兵
697c48e5a8 更新采购合同供应商 2026-03-15 00:23:45 +08:00
吴红兵
d1170b73b2 更新采购合同供应商 2026-03-15 00:21:01 +08:00
吴红兵
0b609732d4 更新采购合同供应商 2026-03-15 00:16:05 +08:00
吴红兵
2d6b2c56a4 更新采购合同供应商 2026-03-14 23:19:12 +08:00
吴红兵
1c49821117 更新采购列表界面 2026-03-14 23:12:22 +08:00
吴红兵
65f99ff477 Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-14 20:59:30 +08:00
yaojian
ff1d38752d 1 2026-03-13 18:36:27 +08:00
yaojian
c611c3e720 1 2026-03-13 11:41:26 +08:00
吴红兵
d8a3302786 Merge branch 'developer' 2026-03-12 22:22:41 +08:00
吴红兵
ae72e88d78 1 2026-03-12 22:22:24 +08:00
吴红兵
ad808230ce Merge branch 'developer' 2026-03-12 21:48:40 +08:00
吴红兵
817a73d079 Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-12 21:47:48 +08:00
吴红兵
9ccbe8b524 采购更新 2026-03-12 21:47:41 +08:00
yaojian
d9f7f85422 每周计划日卫生月卫生新增统计 2026-03-12 18:32:54 +08:00
yaojian
ff2023709b 1 2026-03-12 17:39:13 +08:00
yaojian
065ceff8de Merge branch 'developer' of http://code.cyweb.top/scj/zhxy/v3/cloud-ui into developer 2026-03-12 17:15:43 +08:00
yaojian
bf1c61dae1 学生信息详情保存 2026-03-12 17:15:29 +08:00
吴红兵
533fd7c952 修改采购申请部门筛选问题 2026-03-12 17:14:46 +08:00
yaojian
c54a8d05bb 活动子项导入 2026-03-12 10:45:02 +08:00
吴红兵
e633a25b0a Merge branch 'developer' 2026-03-11 22:22:50 +08:00
吴红兵
6dc986144c Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-11 22:14:47 +08:00
吴红兵
ad78c6c70d 采购更新 2026-03-11 22:14:38 +08:00
yaojian
d4f140e488 学年考核学期考核 2026-03-11 17:51:17 +08:00
yaojian
cb2280406e 班费管理页面修复 2026-03-11 16:57:19 +08:00
吴红兵
4fe8453fdf Merge branch 'feature-purchase' into developer 2026-03-11 16:28:46 +08:00
吴红兵
594a58cae4 调整采购申请查询及增加导出功能 2026-03-11 16:28:31 +08:00
yaojian
4af5660df2 工学交替 2026-03-11 16:26:14 +08:00
yaojian
e33ac977d4 宿舍水电月明细 2026-03-11 15:43:12 +08:00
yaojian
9003ef9c1c 转班异动规则+学生巡检 2026-03-11 14:52:27 +08:00
yaojian
9acb919fdc Merge branch 'developer' of http://code.cyweb.top/scj/zhxy/v3/cloud-ui into developer 2026-03-11 11:31:04 +08:00
yaojian
553dbe5137 食堂问卷调查 2026-03-11 11:30:51 +08:00
吴红兵
4910a9a50e Merge branch 'developer' 2026-03-11 11:04:56 +08:00
吴红兵
30edd38fee 紧急情况 需要上传校党委会议纪要 2026-03-11 11:04:23 +08:00
吴红兵
b35de9dce8 Merge branch 'developer' 2026-03-10 22:20:33 +08:00
吴红兵
a7da30f6c4 采购 2026-03-10 22:18:41 +08:00
吴红兵
3710ca2fd5 修改实施采购途径 2026-03-10 21:41:40 +08:00
吴红兵
ddbdbecba6 Merge branch 'developer' into feature-purchase 2026-03-10 21:11:51 +08:00
吴红兵
f3c2c0f4ea fix 2026-03-10 21:11:49 +08:00
吴红兵
7f45ca074f Merge branch 'feature-purchase-fix' into developer 2026-03-10 21:02:41 +08:00
吴红兵
63034bd182 修改采购途径 2026-03-10 21:02:11 +08:00
yaojian
15d9ca27eb 学生信息页面完善 2026-03-10 18:32:48 +08:00
yaojian
33b228da9a 导出头像 学籍卡导出 顶岗申请 2026-03-10 17:50:48 +08:00
yaojian
7e4f5e1e9b 学生页面补全 2026-03-10 17:09:43 +08:00
yaojian
d92cb8634b 1 2026-03-10 14:27:36 +08:00
Administrator
26eef99007 Merge branch 'developer' into 'master'
1

See merge request scj/zhxy/v3/cloud-ui!7
2026-03-10 03:53:40 +00:00
zhoutianchi
f53c65224b 1 2026-03-10 11:33:59 +08:00
吴红兵
4a76ec3e26 rebuild all 2026-03-10 00:59:05 +08:00
吴红兵
b527ddc277 Merge branch 'feature-purchase' into developer 2026-03-09 23:35:56 +08:00
吴红兵
99baffe114 采购合同 2026-03-09 23:35:38 +08:00
吴红兵
346bd73202 Merge branch 'developer' 2026-03-09 23:31:31 +08:00
吴红兵
8c149c86dd 采购暂存逻辑修改 2026-03-09 23:15:00 +08:00
吴红兵
482f513cdb 采购暂存逻辑修改 2026-03-09 23:09:39 +08:00
吴红兵
4eaf05e3a0 采购暂存逻辑修改 2026-03-09 23:05:27 +08:00
吴红兵
3ee82accbd 更新 2026-03-09 23:00:42 +08:00
吴红兵
0a32e457a5 采购暂存逻辑修改 2026-03-09 21:42:42 +08:00
吴红兵
3fa24f558a 采购暂存逻辑修改 2026-03-09 21:30:32 +08:00
吴红兵
19c3ac20fa 采购暂存逻辑修改 2026-03-09 21:13:16 +08:00
吴红兵
3ce63a6c5a 采购暂存逻辑修改 2026-03-09 21:09:53 +08:00
吴红兵
67891627f8 fix: 预算金额>=2000时自动清空直接采购方式 2026-03-09 17:57:29 +08:00
吴红兵
77a0c38a7a 调整自行采购直接采购 2026-03-09 17:41:08 +08:00
吴红兵
10227b512d 调整暂存逻辑 2026-03-09 16:57:48 +08:00
吴红兵
cbc23f6b5a Merge branch 'developer' 2026-03-09 15:28:46 +08:00
吴红兵
03f10180dc 更新采购申请文件模版下载 2026-03-09 15:22:19 +08:00
吴红兵
5963bbafd6 Merge remote-tracking branch 'origin/developer' into developer 2026-03-09 13:23:02 +08:00
吴红兵
92aa64f1dd 新增管理员模拟登录 2026-03-09 13:22:55 +08:00
yaojian
338ed20da2 导入导出 2026-03-09 12:03:19 +08:00
yaojian
328bc38e9d Merge branch 'developer' of http://code.cyweb.top/scj/zhxy/v3/cloud-ui into developer 2026-03-09 10:51:13 +08:00
yaojian
f7dee0da5e 1 2026-03-09 10:38:05 +08:00
Administrator
bd856df1c2 Merge branch 'developer' into 'master'
1

See merge request scj/zhxy/v3/cloud-ui!6
2026-03-08 18:19:49 +00:00
zhoutc
af08f469fb 1 2026-03-09 02:19:11 +08:00
Administrator
0425faf220 Merge branch 'developer' into 'master'
Developer

See merge request scj/zhxy/v3/cloud-ui!5
2026-03-08 17:44:53 +00:00
zhoutc
bfd864bb2c Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-09 01:43:52 +08:00
zhoutc
e70362c905 1 2026-03-09 01:43:50 +08:00
Administrator
8e859ecc8a Merge branch 'developer' into 'master'
Developer

See merge request scj/zhxy/v3/cloud-ui!4
2026-03-08 17:35:50 +00:00
吴红兵
35b5a2f84e Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-09 01:33:20 +08:00
吴红兵
91bf640eff 更新 2026-03-09 01:33:11 +08:00
Administrator
8e62636281 Merge branch 'developer' into 'master'
1

See merge request scj/zhxy/v3/cloud-ui!3
2026-03-08 17:32:46 +00:00
zhoutc
8575b5e0b1 1 2026-03-09 01:31:30 +08:00
吴红兵
afb93bf333 Merge branch 'developer' 2026-03-09 00:43:58 +08:00
吴红兵
236e5f5594 更新 2026-03-09 00:18:42 +08:00
吴红兵
4b33dc9aab 更新 2026-03-08 23:23:02 +08:00
吴红兵
42ab802371 fix 2026-03-08 20:44:22 +08:00
吴红兵
7200f8fe5f 招标文件审批记录 2026-03-08 20:39:03 +08:00
吴红兵
a8b3b9fb20 招标文件审批记录 2026-03-08 20:28:46 +08:00
吴红兵
0ef35bf5fd Merge branch 'developer' 2026-03-08 18:13:18 +08:00
吴红兵
d0b7d6875c rebuild all 2026-03-08 18:12:31 +08:00
吴红兵
bff955c49a Merge branch 'developer' 2026-03-08 17:39:18 +08:00
吴红兵
35d179b1b9 fix 2026-03-08 17:37:57 +08:00
吴红兵
877d9f0848 fix 2026-03-08 17:10:23 +08:00
吴红兵
ec690ecab5 fix 2026-03-08 15:18:12 +08:00
吴红兵
5a53648ee7 fix 2026-03-08 15:17:13 +08:00
吴红兵
a0218f1c8f fix 2026-03-07 21:20:45 +08:00
吴红兵
57dd7026e6 fix 2026-03-07 18:29:36 +08:00
吴红兵
0e54f887cb fix 2026-03-07 18:23:23 +08:00
吴红兵
15b3efe51e fix 2026-03-07 17:32:17 +08:00
吴红兵
1cf529cead fix 2026-03-07 13:46:22 +08:00
吴红兵
e87c9ac3f8 fix(purchase): 表单验证提示信息改为中文
添加以下字段的中文验证提示:
- meetingMinutesSingle: 校党委会议纪要不能为空
- singleSourceProof: 单一来源论专家证附件不能为空
- purchaseRequirement: 需求文件不能为空
- feasibilityReport: 项目可行性论证报告不能为空
- deptSelfMeetingMinutes: 部门采购会议纪要不能为空
- importApplication: 进口产品申请及专家论证意见表不能为空
2026-03-07 13:25:38 +08:00
吴红兵
68a02f2a43 fix(purchase): 修复模版匹配字段名错误
- 将 t.type 改为 t.templateType,与后端返回字段保持一致
- 影响 downloadTemplate 和 getTemplateName 两个函数
2026-03-07 13:17:43 +08:00
吴红兵
c571c78788 feat(purchase): 所有模版下载按钮使用动态名称
- 将所有硬编码的模版名称替换为动态获取
- 模版名称从后台API获取,支持后台维护后前端自动更新
- 统一格式为:下载《{模版名称}》模版
- 保留后备名称确保兼容性
2026-03-07 13:03:45 +08:00
吴红兵
f92612c394 feat(purchase): 政府采购意向表添加模版下载功能
- 添加政府采购意向表模版下载按钮(类型编码: gov_pur_int)
- 模版名称从后台动态获取,支持后台维护后前端自动更新
- 页面加载时并行获取模版列表
- downloadTemplate 函数优化:优先使用后台模版名称,后备使用本地硬编码
2026-03-07 12:48:43 +08:00
吴红兵
b997b3ba48 fix 2026-03-07 12:35:45 +08:00
吴红兵
271710e870 feat(purchase): 采购申请选择服务商城品目时显示提示信息
在品目编码选择器下方添加条件提示:
- 当选择的品目为特殊品目(服务商城品目)时显示黄色提示
- 提示文案:当前选择品目为服务商城品目
2026-03-07 12:25:00 +08:00
吴红兵
94c3473958 fix 2026-03-07 01:34:48 +08:00
吴红兵
adc511cfdc fix 2026-03-06 22:48:58 +08:00
吴红兵
33bc94fce5 Merge branch 'feature-purchase' into developer 2026-03-06 18:35:08 +08:00
吴红兵
81c5665094 跟新 2026-03-06 18:34:55 +08:00
吴红兵
fde9d3555e Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-06 16:11:19 +08:00
yaojian
b352145e4f 1 2026-03-06 16:03:27 +08:00
吴红兵
9fe1acb177 fix 2026-03-06 15:05:12 +08:00
yaojian
40024afcc4 Merge branch 'developer' of http://code.cyweb.top/scj/zhxy/v3/cloud-ui into developer 2026-03-06 11:49:08 +08:00
yaojian
c70f302247 导入导出 2026-03-06 11:49:01 +08:00
吴红兵
99282b3c1b rebuild all 2026-03-06 11:01:24 +08:00
吴红兵
dad11c8131 fix 2026-03-06 10:40:33 +08:00
吴红兵
9da3a92b69 跟新 2026-03-06 10:04:47 +08:00
吴红兵
ac4df43795 恢复更新 2026-03-06 03:38:01 +08:00
吴红兵
a5a77b509b fix 2026-03-06 02:50:46 +08:00
吴红兵
9a4490b2c6 fix 2026-03-06 01:50:07 +08:00
吴红兵
3a86e24722 fix 2026-03-06 01:33:49 +08:00
吴红兵
e637d19037 Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-06 01:15:48 +08:00
吴红兵
83ed9dad05 跟新 2026-03-06 01:15:40 +08:00
wangdongjun
b088900886 rebuild all 2026-03-05 23:26:43 +08:00
王栋俊
fdabc8efcc Merge branch 'developer' into 'master'
rebuild all

See merge request scj/zhxy/v3/cloud-ui!2
2026-03-05 14:43:23 +00:00
wangdongjun
57085824b6 rebuild all 2026-03-05 22:43:04 +08:00
王栋俊
8767ec8593 Merge branch 'developer' into 'master'
Developer

See merge request scj/zhxy/v3/cloud-ui!1
2026-03-05 13:30:22 +00:00
wangdongjun
ca3092c685 rebuild all 2026-03-05 21:26:33 +08:00
wangdongjun
618fee859a Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer 2026-03-05 21:25:57 +08:00
吴红兵
25f31978e5 跟新 2026-03-05 20:02:15 +08:00
吴红兵
450e1be7bd 跟新 2026-03-05 19:45:43 +08:00
yaojian
9bbfc6e736 学籍比对 2026-03-05 18:16:08 +08:00
吴红兵
44256b55cc 跟新 2026-03-05 18:15:31 +08:00
吴红兵
ea7e48fc49 跟新 2026-03-05 17:34:02 +08:00
yaojian
f63ce726c1 班主任考核学期统计 2026-03-05 15:37:17 +08:00
吴红兵
700c2e6a83 Merge remote-tracking branch 'origin/developer' into developer 2026-03-05 15:00:25 +08:00
吴红兵
2317909261 fix 2026-03-05 15:00:19 +08:00
yaojian
5125ea04c9 宿舍月卫生 2026-03-05 14:46:55 +08:00
23 changed files with 5041 additions and 2521 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -146,6 +146,32 @@ export const putObj = (obj: any) => {
}); });
}; };
/**
* 按文档接口修改部门
* POST /basic/basicdept/edit
* @param obj
*/
export const editObj = (obj: any) => {
return request({
url: '/basic/basicdept/edit',
method: 'post',
data: obj,
});
};
/**
* 按文档接口批量删除部门
* POST /basic/basicdept/delete
* @param ids
*/
export const delObjs = (ids: string[]) => {
return request({
url: '/basic/basicdept/delete',
method: 'post',
data: ids,
});
};
/** /**
* 根据部门代码获取培训部门 * 根据部门代码获取培训部门
* @param obj * @param obj

View File

@@ -59,8 +59,9 @@ export const makeHoliday = (obj: any) => {
*/ */
export const getObj = (id: string | number) => { export const getObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicholiday/${id}`, url: '/basic/basicholiday/detail',
method: 'get', method: 'get',
params: { id },
}); });
}; };
@@ -70,8 +71,9 @@ export const getObj = (id: string | number) => {
*/ */
export const delObj = (id: string | number) => { export const delObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicholiday/${id}`, url: '/basic/basicholiday/delete',
method: 'delete', method: 'post',
data: [id],
}); });
}; };
@@ -81,8 +83,8 @@ export const delObj = (id: string | number) => {
*/ */
export const putObj = (obj: any) => { export const putObj = (obj: any) => {
return request({ return request({
url: '/basic/basicholiday', url: '/basic/basicholiday/edit',
method: 'put', method: 'post',
data: obj, data: obj,
}); });
}; };
@@ -110,3 +112,20 @@ export const getHolidayDayList = (query?: any) => {
}); });
}; };
/**
* 学年学期列表
*/
export const getAllYearAndTerm = () => {
return request({
url: '/basic/basicholiday/getAllYearAndTerm',
method: 'get',
});
};
/**
* 学年列表(兼容页面调用)
*/
export const queryAllSchoolYear = () => {
return getAllYearAndTerm();
};

View File

@@ -47,8 +47,9 @@ export const addObj = (obj: any) => {
*/ */
export const getObj = (id: string | number) => { export const getObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicnation/${id}`, url: '/basic/basicnation/detail',
method: 'get', method: 'get',
params: { id },
}); });
}; };
@@ -58,8 +59,9 @@ export const getObj = (id: string | number) => {
*/ */
export const delObj = (id: string | number) => { export const delObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicnation/${id}`, url: '/basic/basicnation/delete',
method: 'delete', method: 'post',
data: [id],
}); });
}; };
@@ -69,8 +71,8 @@ export const delObj = (id: string | number) => {
*/ */
export const putObj = (obj: any) => { export const putObj = (obj: any) => {
return request({ return request({
url: '/basic/basicnation', url: '/basic/basicnation/edit',
method: 'put', method: 'post',
data: obj, data: obj,
}); });
}; };

View File

@@ -47,8 +47,9 @@ export const addObj = (obj: any) => {
*/ */
export const getObj = (id: string | number) => { export const getObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicyear/${id}`, url: '/basic/basicyear/detail',
method: 'get', method: 'get',
params: { id },
}); });
}; };
@@ -58,8 +59,9 @@ export const getObj = (id: string | number) => {
*/ */
export const delObj = (id: string | number) => { export const delObj = (id: string | number) => {
return request({ return request({
url: `/basic/basicyear/${id}`, url: '/basic/basicyear/delete',
method: 'delete', method: 'post',
data: [id],
}); });
}; };
@@ -69,8 +71,8 @@ export const delObj = (id: string | number) => {
*/ */
export const putObj = (obj: any) => { export const putObj = (obj: any) => {
return request({ return request({
url: '/basic/basicyear', url: '/basic/basicyear/edit',
method: 'put', method: 'post',
data: obj, data: obj,
}); });
}; };

View File

@@ -47,8 +47,9 @@ export const addObj = (obj: any) => {
*/ */
export const getObj = (id: string | number) => { export const getObj = (id: string | number) => {
return request({ return request({
url: `/basic/major/${id}`, url: '/basic/major/detail',
method: 'get', method: 'get',
params: { id },
}); });
}; };
@@ -58,8 +59,9 @@ export const getObj = (id: string | number) => {
*/ */
export const delObj = (id: string | number) => { export const delObj = (id: string | number) => {
return request({ return request({
url: `/basic/major/${id}`, url: '/basic/major/delete',
method: 'delete', method: 'post',
data: [id],
}); });
}; };
@@ -69,8 +71,8 @@ export const delObj = (id: string | number) => {
*/ */
export const putObj = (obj: any) => { export const putObj = (obj: any) => {
return request({ return request({
url: '/basic/major', url: '/basic/major/edit',
method: 'put', method: 'post',
data: obj, data: obj,
}); });
}; };

View File

@@ -47,8 +47,9 @@ export const addObj = (obj: any) => {
*/ */
export const getObj = (id: string | number) => { export const getObj = (id: string | number) => {
return request({ return request({
url: `/basic/schoolnews/${id}`, url: '/basic/schoolnews/detail',
method: 'get', method: 'get',
params: { id },
}); });
}; };
@@ -58,8 +59,9 @@ export const getObj = (id: string | number) => {
*/ */
export const delObj = (id: string | number) => { export const delObj = (id: string | number) => {
return request({ return request({
url: `/basic/schoolnews/${id}`, url: '/basic/schoolnews/delete',
method: 'delete', method: 'post',
data: [id],
}); });
}; };
@@ -69,8 +71,8 @@ export const delObj = (id: string | number) => {
*/ */
export const putObj = (obj: any) => { export const putObj = (obj: any) => {
return request({ return request({
url: '/basic/schoolnews', url: '/basic/schoolnews/edit',
method: 'put', method: 'post',
data: obj, data: obj,
}); });
}; };

View File

@@ -68,9 +68,9 @@ export function putObj(obj?: Object) {
/** /**
* 统计班主任查看情况 * 统计班主任查看情况
* GET /api/stuwork/weekPlan/readStatistics * GET /api/stuwork/weekPlan/readStatistics
* @param params 包含 deptCode, weekPlanId * @param params 包含 deptCode, weekPlanId(可选)
*/ */
export function readStatistics(params: { deptCode: string; weekPlanId: string }) { export function readStatistics(params: { deptCode: string; weekPlanId?: string }) {
return request({ return request({
url: '/stuwork/weekPlan/readStatistics', url: '/stuwork/weekPlan/readStatistics',
method: 'get', method: 'get',

View File

@@ -0,0 +1,204 @@
<template>
<el-dialog
:title="form.id ? '编辑部门' : '新增部门'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="760px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="110px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="20">
<el-col :span="12" class="mb20">
<el-form-item label="部门编码" prop="deptCode">
<el-input v-model="form.deptCode" placeholder="请输入部门编码" clearable :disabled="!!form.id" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="部门名称" prop="deptName">
<el-input v-model="form.deptName" placeholder="请输入部门名称" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="父级部门" prop="parentCode">
<el-tree-select
v-model="form.parentCode"
:data="treeData"
:props="{ value: 'deptCode', label: 'deptName', children: 'children' }"
node-key="deptCode"
check-strictly
clearable
filterable
style="width: 100%"
placeholder="请选择父级部门" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="部门层级" prop="deptLevel">
<el-select v-model="form.deptLevel" placeholder="请选择层级" clearable style="width: 100%">
<el-option label="一级" value="1" />
<el-option label="二级" value="2" />
<el-option label="三级" value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :step="1" :precision="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="二级学院" prop="secondFlag">
<el-radio-group v-model="form.secondFlag">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="教学部门" prop="teachFlag">
<el-radio-group v-model="form.teachFlag">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="培训部门" prop="trainFlag">
<el-radio-group v-model="form.trainFlag">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BasicDeptDialog">
import { nextTick, reactive, ref } from 'vue'
import { addObj, editObj, fetchTree } from '/@/api/basic/basicdept'
import { useMessage } from '/@/hooks/message'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const treeData = ref<any[]>([])
const form = reactive({
id: '',
deptCode: '',
deptName: '',
parentCode: '',
deptLevel: '1',
sort: 0,
secondFlag: '0',
teachFlag: '0',
trainFlag: '0',
remarks: ''
})
const rules = ref({
deptCode: [{ required: true, message: '部门编码不能为空', trigger: 'blur' }],
deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
parentCode: [{ required: true, message: '父级部门不能为空', trigger: 'change' }],
deptLevel: [{ required: true, message: '部门层级不能为空', trigger: 'change' }]
})
const resetForm = () => {
Object.assign(form, {
id: '',
deptCode: '',
deptName: '',
parentCode: '',
deptLevel: '1',
sort: 0,
secondFlag: '0',
teachFlag: '0',
trainFlag: '0',
remarks: ''
})
}
const loadTreeData = async () => {
try {
const res = await fetchTree()
treeData.value = Array.isArray(res.data) ? res.data : []
} catch {
treeData.value = []
}
}
const openDialog = async (row?: any) => {
visible.value = true
resetForm()
await loadTreeData()
if (row) {
Object.assign(form, {
id: row.id || '',
deptCode: row.deptCode || '',
deptName: row.deptName || '',
parentCode: row.parentCode || '',
deptLevel: row.deptLevel || '1',
sort: typeof row.sort === 'number' ? row.sort : Number(row.sort || 0),
secondFlag: row.secondFlag ?? '0',
teachFlag: row.teachFlag ?? '0',
trainFlag: row.trainFlag ?? '0',
remarks: row.remarks || ''
})
}
nextTick(() => {
formRef.value?.clearValidate()
})
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
try {
loading.value = true
const payload = {
...form,
sort: Number(form.sort || 0)
}
if (form.id) {
await editObj(payload)
useMessage().success('编辑成功')
} else {
await addObj(payload)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
} finally {
loading.value = false
}
}
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,185 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="部门编码" prop="deptCode">
<el-input v-model="searchForm.deptCode" placeholder="请输入部门编码" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="部门名称" prop="deptName">
<el-input v-model="searchForm.deptName" placeholder="请输入部门名称" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="部门层级" prop="deptLevel">
<el-select v-model="searchForm.deptLevel" placeholder="请选择层级" clearable style="width: 160px">
<el-option label="一级" value="1" />
<el-option label="二级" value="2" />
<el-option label="三级" value="3" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
部门管理
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="deptCode" label="部门编码" min-width="130" show-overflow-tooltip align="center" />
<el-table-column prop="deptName" label="部门名称" min-width="180" show-overflow-tooltip align="center" />
<el-table-column prop="parentCode" label="父级编码" min-width="130" show-overflow-tooltip align="center" />
<el-table-column prop="deptLevel" label="层级" width="90" align="center" />
<el-table-column prop="sort" label="排序" width="90" align="center" />
<el-table-column label="二级学院" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.secondFlag === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.secondFlag === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="教学部门" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.teachFlag === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.teachFlag === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="培训部门" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.trainFlag === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.trainFlag === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row)">编辑</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="BasicDept">
import { defineAsyncComponent, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObjs, fetchList } from '/@/api/basic/basicdept'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const searchForm = reactive({
deptCode: '',
deptName: '',
deptLevel: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.deptCode = ''
searchForm.deptName = ''
searchForm.deptLevel = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到部门ID')
return
}
try {
await useMessageBox().confirm('确定要删除该部门吗?')
} catch {
return
}
try {
await delObjs([id])
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,169 @@
<template>
<el-dialog
v-model="visible"
:title="form.id ? '编辑节假日' : '新增节假日'"
width="640px"
destroy-on-close
:close-on-click-modal="false">
<el-form ref="formRef" :model="form" :rules="rules" label-width="105px">
<el-form-item label="日期范围" prop="dateRange">
<el-date-picker
v-model="form.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 100%" />
</el-form-item>
<el-form-item label="节假日类型" prop="holidayType">
<el-radio-group v-model="form.holidayType">
<el-radio label="0">周六周日</el-radio>
<el-radio label="1">节假日</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="学年学期" prop="year">
<el-select v-model="form.year" placeholder="请选择学年学期" clearable style="width: 100%">
<el-option
v-for="item in schoolYearTermList"
:key="item.id || item.year"
:label="buildYearTermLabel(item)"
:value="item.id || item.year" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" maxlength="255" show-word-limit placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<span>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="loading" @click="handleSubmit">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BasicHolidayForm">
import { computed, onMounted, reactive, ref } from 'vue'
import { addObj, getAllYearAndTerm, getObj, putObj } from '/@/api/basic/basicholiday'
import { useMessage } from '/@/hooks/message'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const schoolYearTermList = ref<any[]>([])
const createForm = () => ({
id: '',
dateRange: [] as string[],
holidayType: '1',
year: '',
remarks: ''
})
const form = reactive(createForm())
const rules = computed(() => ({
dateRange: [{ required: true, message: '请选择日期范围', trigger: 'change' }],
holidayType: [{ required: true, message: '请选择节假日类型', trigger: 'change' }],
year: [{ required: true, message: '请选择学年学期', trigger: 'change' }]
}))
const resetForm = () => {
Object.assign(form, createForm())
formRef.value?.clearValidate()
}
const buildYearTermLabel = (item: any) => {
if (item?.year && item?.yearTerm) {
return `${item.year}${item.yearTerm}学期`
}
return item?.year || item?.id || '-'
}
const getYearAndTermList = async () => {
try {
const res = await getAllYearAndTerm()
schoolYearTermList.value = Array.isArray(res?.data) ? res.data : []
} catch {
schoolYearTermList.value = []
}
}
const parseDateRange = (row: any) => {
if (Array.isArray(row?.dateRange) && row.dateRange.length === 2) {
return row.dateRange
}
if (row?.holidayDate) {
return [row.holidayDate, row.holidayDate]
}
return []
}
const openDialog = async (id?: string) => {
visible.value = true
resetForm()
if (!id) return
try {
const res: any = await getObj(id)
const row = res?.data || {}
form.id = row.id || ''
form.dateRange = parseDateRange(row)
form.holidayType = row.holidayType?.toString?.() || '1'
form.year = row.yearId || row.year || ''
form.remarks = row.remarks || ''
} catch (err: any) {
useMessage().error(err.msg || '获取详情失败')
}
}
const handleSubmit = async () => {
try {
await formRef.value?.validate()
} catch {
return
}
loading.value = true
const payload: any = {
id: form.id || undefined,
dateRange: form.dateRange,
holidayType: form.holidayType,
year: form.year,
remarks: form.remarks
}
// 编辑场景补齐单日字段,避免后端仅识别 holidayDate 导致更新失败
if (form.id && form.dateRange?.length) {
payload.holidayDate = form.dateRange[0]
}
try {
if (form.id) {
await putObj(payload)
useMessage().success('修改成功')
} else {
await addObj(payload)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '修改失败' : '新增失败'))
} finally {
loading.value = false
}
}
onMounted(() => {
getYearAndTermList()
})
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,198 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="节假日类型" prop="holidayType">
<el-select v-model="searchForm.holidayType" placeholder="请选择类型" clearable style="width: 180px">
<el-option label="周六周日" value="0" />
<el-option label="节假日" value="1" />
</el-select>
</el-form-item>
<el-form-item label="学年" prop="year">
<el-select v-model="searchForm.year" placeholder="请选择学年" clearable filterable style="width: 200px">
<el-option
v-for="item in schoolYearList"
:key="item.id || item.year"
:label="item.year"
:value="item.year" />
</el-select>
</el-form-item>
<el-form-item label="学期" prop="yearTerm">
<el-select v-model="searchForm.yearTerm" placeholder="请选择学期" clearable style="width: 160px">
<el-option label="第一学期" value="1" />
<el-option label="第二学期" value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
节假日维护
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="holidayDate" label="日期" width="130" show-overflow-tooltip align="center" />
<el-table-column prop="holidayType" label="类型" width="110" align="center">
<template #default="scope">
<el-tag :type="scope.row.holidayType === '1' ? 'danger' : 'info'" size="small" effect="plain">
{{ scope.row.holidayType === '1' ? '节假日' : '周六周日' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="year" label="学年" min-width="130" show-overflow-tooltip align="center" />
<el-table-column prop="yearTerm" label="学期" width="100" align="center">
<template #default="scope">
<el-tag size="small" type="primary" effect="plain">
{{ scope.row.yearTerm === '1' ? '第一学期' : scope.row.yearTerm === '2' ? '第二学期' : (scope.row.yearTerm || '-') }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remarks" label="备注" min-width="180" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="BasicHoliday">
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObj, fetchList, queryAllSchoolYear } from '/@/api/basic/basicholiday'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Document, List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const schoolYearList = ref<any[]>([])
const searchForm = reactive({
holidayType: '',
year: '',
yearTerm: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.holidayType = ''
searchForm.year = ''
searchForm.yearTerm = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到节假日ID')
return
}
try {
await useMessageBox().confirm('确定要删除该节假日记录吗?')
} catch {
return
}
try {
await delObj(id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
schoolYearList.value = Array.isArray(res?.data) ? res.data : []
} catch {
schoolYearList.value = []
}
}
onMounted(() => {
getSchoolYearList()
})
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,130 @@
<template>
<el-dialog
:title="form.id ? '编辑民族' : '新增民族'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="620px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
:validate-on-rule-change="false"
v-loading="loading">
<el-form-item label="民族编码" prop="nationCode">
<el-input v-model="form.nationCode" placeholder="请输入民族编码" clearable />
</el-form-item>
<el-form-item label="民族名称" prop="nationName">
<el-input v-model="form.nationName" placeholder="请输入民族名称" clearable />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :step="1" :precision="0" style="width: 100%" />
</el-form-item>
<el-form-item label="10万以下民族" prop="isLower">
<el-radio-group v-model="form.isLower">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BasicNationDialog">
import { nextTick, reactive, ref } from 'vue'
import { addObj, getObj, putObj } from '/@/api/basic/basicnation'
import { useMessage } from '/@/hooks/message'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const form = reactive({
id: '',
nationCode: '',
nationName: '',
sort: 0,
isLower: '0'
})
const rules = ref({
nationCode: [{ required: true, message: '民族编码不能为空', trigger: 'blur' }],
nationName: [{ required: true, message: '民族名称不能为空', trigger: 'blur' }]
})
const resetForm = () => {
Object.assign(form, {
id: '',
nationCode: '',
nationName: '',
sort: 0,
isLower: '0'
})
}
const openDialog = async (id?: string) => {
visible.value = true
resetForm()
nextTick(() => {
formRef.value?.clearValidate()
})
if (!id) return
form.id = id
loading.value = true
try {
const res = await getObj(id)
const row = res?.data || {}
Object.assign(form, {
id: row.id || '',
nationCode: row.nationCode || '',
nationName: row.nationName || '',
sort: typeof row.sort === 'number' ? row.sort : Number(row.sort || 0),
isLower: row.isLower ?? '0'
})
} catch (err: any) {
useMessage().error(err.msg || '获取详情失败')
} finally {
loading.value = false
}
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
try {
loading.value = true
const payload = {
...form,
sort: Number(form.sort || 0)
}
if (form.id) {
await putObj(payload)
useMessage().success('编辑成功')
} else {
await addObj(payload)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
} finally {
loading.value = false
}
}
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,164 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="民族编码" prop="nationCode">
<el-input v-model="searchForm.nationCode" placeholder="请输入民族编码" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="民族名称" prop="nationName">
<el-input v-model="searchForm.nationName" placeholder="请输入民族名称" clearable style="width: 220px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
民族管理
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="nationCode" label="民族编码" min-width="180" show-overflow-tooltip align="center" />
<el-table-column prop="nationName" label="民族名称" min-width="180" show-overflow-tooltip align="center" />
<el-table-column prop="sort" label="排序" width="100" align="center" />
<el-table-column label="10万以下民族" width="130" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.isLower === '1' ? 'warning' : 'info'" effect="plain">
{{ scope.row.isLower === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row.id)">
编辑
</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="BasicNation">
import { defineAsyncComponent, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObj, fetchList } from '/@/api/basic/basicnation'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Document, List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const searchForm = reactive({
nationCode: '',
nationName: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.nationCode = ''
searchForm.nationName = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到民族ID')
return
}
try {
await useMessageBox().confirm('确定要删除该民族吗?')
} catch {
return
}
try {
await delObj(id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,219 @@
<template>
<el-dialog
:title="form.id ? '编辑学期维护' : '新增学期维护'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="780px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="130px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="20">
<el-col :span="12" class="mb20">
<el-form-item label="开始年份" prop="startYear">
<el-input v-model="form.startYear" maxlength="4" placeholder="例如2025" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="结束年份" prop="endYear">
<el-input v-model="form.endYear" maxlength="4" placeholder="例如2026" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="学期" prop="yearTerm">
<el-select v-model="form.yearTerm" placeholder="请选择学期" clearable style="width: 100%">
<el-option label="第一学期" value="1" />
<el-option label="第二学期" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="当前学年" prop="isCurrent">
<el-radio-group v-model="form.isCurrent">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="教学学年" prop="isNextTeach">
<el-radio-group v-model="form.isNextTeach">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="水电开始时间" prop="waterStartTime">
<el-date-picker
v-model="form.waterStartTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择时间"
clearable
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="水电结束时间" prop="waterEndTime">
<el-date-picker
v-model="form.waterEndTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择时间"
clearable
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="查询开始时间" prop="waterSearchStartTime">
<el-date-picker
v-model="form.waterSearchStartTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择时间"
clearable
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="查询结束时间" prop="waterSearchEndTime">
<el-date-picker
v-model="form.waterSearchEndTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择时间"
clearable
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BasicYearDialog">
import { nextTick, reactive, ref } from 'vue'
import { addObj, getObj, putObj } from '/@/api/basic/basicyear'
import { useMessage } from '/@/hooks/message'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const form = reactive({
id: '',
startYear: '',
endYear: '',
yearTerm: '',
isCurrent: '0',
isNextTeach: '0',
remarks: '',
waterStartTime: '',
waterEndTime: '',
waterSearchStartTime: '',
waterSearchEndTime: ''
})
const rules = ref({
startYear: [{ required: true, message: '开始年份不能为空', trigger: 'blur' }],
endYear: [{ required: true, message: '结束年份不能为空', trigger: 'blur' }],
yearTerm: [{ required: true, message: '学期不能为空', trigger: 'change' }]
})
const resetForm = () => {
Object.assign(form, {
id: '',
startYear: '',
endYear: '',
yearTerm: '',
isCurrent: '0',
isNextTeach: '0',
remarks: '',
waterStartTime: '',
waterEndTime: '',
waterSearchStartTime: '',
waterSearchEndTime: ''
})
}
const openDialog = async (id?: string) => {
visible.value = true
resetForm()
nextTick(() => {
formRef.value?.clearValidate()
})
if (!id) return
form.id = id
loading.value = true
try {
const res = await getObj(id)
const row = res?.data || {}
Object.assign(form, {
id: row.id || '',
startYear: row.startYear || '',
endYear: row.endYear || '',
yearTerm: row.yearTerm || '',
isCurrent: row.isCurrent ?? '0',
isNextTeach: row.isNextTeach ?? '0',
remarks: row.remarks || '',
waterStartTime: row.waterStartTime || '',
waterEndTime: row.waterEndTime || '',
waterSearchStartTime: row.waterSearchStartTime || '',
waterSearchEndTime: row.waterSearchEndTime || ''
})
} catch (err: any) {
useMessage().error(err.msg || '获取详情失败')
} finally {
loading.value = false
}
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
try {
loading.value = true
if (form.id) {
await putObj(form)
useMessage().success('编辑成功')
} else {
await addObj(form)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
} finally {
loading.value = false
}
}
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,185 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="开始年份" prop="startYear">
<el-input v-model="searchForm.startYear" placeholder="请输入开始年份" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="结束年份" prop="endYear">
<el-input v-model="searchForm.endYear" placeholder="请输入结束年份" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="学期" prop="yearTerm">
<el-select v-model="searchForm.yearTerm" placeholder="请选择学期" clearable style="width: 160px">
<el-option label="第一学期" value="1" />
<el-option label="第二学期" value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
学期维护
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="year" label="学年" min-width="130" show-overflow-tooltip align="center" />
<el-table-column prop="yearTerm" label="学期" width="100" align="center">
<template #default="scope">
<el-tag size="small" type="primary" effect="plain">
{{ scope.row.yearTerm === '1' ? '第一学期' : scope.row.yearTerm === '2' ? '第二学期' : (scope.row.yearTerm || '-') }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="isCurrent" label="当前学年" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.isCurrent === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.isCurrent === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="isNextTeach" label="教学学年" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.isNextTeach === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.isNextTeach === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="waterStartTime" label="水电开始时间" min-width="170" show-overflow-tooltip align="center" />
<el-table-column prop="waterEndTime" label="水电结束时间" min-width="170" show-overflow-tooltip align="center" />
<el-table-column prop="waterSearchStartTime" label="查询开始时间" min-width="170" show-overflow-tooltip align="center" />
<el-table-column prop="waterSearchEndTime" label="查询结束时间" min-width="170" show-overflow-tooltip align="center" />
<el-table-column prop="remarks" label="备注" min-width="150" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="BasicYear">
import { defineAsyncComponent, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObj, fetchList } from '/@/api/basic/basicyear'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Document, List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const searchForm = reactive({
startYear: '',
endYear: '',
yearTerm: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.startYear = ''
searchForm.endYear = ''
searchForm.yearTerm = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到学年学期ID')
return
}
try {
await useMessageBox().confirm('确定要删除该学期维护记录吗?')
} catch {
return
}
try {
await delObj(id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,233 @@
<template>
<el-dialog
:title="form.id ? '编辑专业' : '新增专业'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="760px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="20">
<el-col :span="12" class="mb20">
<el-form-item label="专业代码" prop="majorCode">
<el-input v-model="form.majorCode" placeholder="请输入专业代码" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="专业名称" prop="majorName">
<el-input v-model="form.majorName" placeholder="请输入专业名称" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="所属学院" prop="deptCode">
<el-select v-model="form.deptCode" placeholder="请选择学院" clearable filterable style="width: 100%">
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="学制" prop="majorYears">
<el-select v-model="form.majorYears" placeholder="请选择学制" clearable filterable style="width: 100%">
<el-option
v-for="item in majorYearsList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="培养层次" prop="majorLevel">
<el-select v-model="form.majorLevel" placeholder="请选择培养层次" clearable filterable style="width: 100%">
<el-option
v-for="item in majorLevelList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="中德班" prop="isGerman">
<el-radio-group v-model="form.isGerman">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="专业规范名称" prop="majorProName">
<el-input v-model="form.majorProName" placeholder="请输入专业规范名称" clearable />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="国际专业名称" prop="countryMajorName">
<el-input v-model="form.countryMajorName" placeholder="请输入国际专业名称" clearable />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="BasicMajorDialog">
import { nextTick, onMounted, reactive, ref } from 'vue'
import { addObj, getObj, putObj } from '/@/api/basic/major'
import { getDeptList } from '/@/api/basic/basicclass'
import { getDictsByTypes } from '/@/api/admin/dict'
import { useMessage } from '/@/hooks/message'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const deptList = ref<any[]>([])
const majorYearsList = ref<any[]>([])
const majorLevelList = ref<any[]>([])
const form = reactive({
id: '',
majorCode: '',
majorName: '',
deptCode: '',
majorYears: '',
majorProName: '',
majorLevel: '',
isGerman: '0',
countryMajorName: '',
remarks: ''
})
const rules = ref({
majorCode: [{ required: true, message: '专业代码不能为空', trigger: 'blur' }],
majorName: [{ required: true, message: '专业名称不能为空', trigger: 'blur' }],
deptCode: [{ required: true, message: '所属学院不能为空', trigger: 'change' }],
majorYears: [{ required: true, message: '学制不能为空', trigger: 'change' }],
majorLevel: [{ required: true, message: '培养层次不能为空', trigger: 'change' }]
})
const resetForm = () => {
Object.assign(form, {
id: '',
majorCode: '',
majorName: '',
deptCode: '',
majorYears: '',
majorProName: '',
majorLevel: '',
isGerman: '0',
countryMajorName: '',
remarks: ''
})
}
const parseDictList = (list: any[]) => {
if (!Array.isArray(list)) return []
return list.map((item: any) => ({
label: item.label || item.dictLabel || item.name || item.value || '',
value: item.value || item.dictValue || item.code || item.label || ''
}))
}
const loadOptions = async () => {
try {
const [deptRes, dictRes] = await Promise.all([
getDeptList(),
getDictsByTypes(['basic_major_years', 'basic_major_level'])
])
deptList.value = Array.isArray(deptRes?.data) ? deptRes.data : []
majorYearsList.value = parseDictList(dictRes?.data?.basic_major_years || [])
majorLevelList.value = parseDictList(dictRes?.data?.basic_major_level || [])
} catch {
deptList.value = []
majorYearsList.value = []
majorLevelList.value = []
}
}
const openDialog = async (id?: string) => {
visible.value = true
resetForm()
if (!deptList.value.length) {
await loadOptions()
}
nextTick(() => {
formRef.value?.clearValidate()
})
if (!id) return
form.id = id
loading.value = true
try {
const res = await getObj(id)
const row = res?.data || {}
Object.assign(form, {
id: row.id || '',
majorCode: row.majorCode || '',
majorName: row.majorName || '',
deptCode: row.deptCode || '',
majorYears: row.majorYears || '',
majorProName: row.majorProName || '',
majorLevel: row.majorLevel || '',
isGerman: row.isGerman ?? '0',
countryMajorName: row.countryMajorName || '',
remarks: row.remarks || ''
})
} catch (err: any) {
useMessage().error(err.msg || '获取详情失败')
} finally {
loading.value = false
}
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
try {
loading.value = true
if (form.id) {
await putObj(form)
useMessage().success('编辑成功')
} else {
await addObj(form)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
} finally {
loading.value = false
}
}
onMounted(() => {
loadOptions()
})
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,191 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="专业代码" prop="majorCode">
<el-input v-model="searchForm.majorCode" placeholder="请输入专业代码" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="专业名称" prop="majorName">
<el-input v-model="searchForm.majorName" placeholder="请输入专业名称" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="所属学院" prop="deptCode">
<el-select v-model="searchForm.deptCode" placeholder="请选择学院" clearable filterable style="width: 220px">
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
专业管理
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="majorCode" label="专业代码" min-width="120" show-overflow-tooltip align="center" />
<el-table-column prop="majorName" label="专业名称" min-width="160" show-overflow-tooltip align="center" />
<el-table-column prop="majorProName" label="专业规范名称" min-width="200" show-overflow-tooltip align="center" />
<el-table-column prop="deptCode" label="所属学院编码" min-width="130" show-overflow-tooltip align="center" />
<el-table-column prop="majorYears" label="学制" width="90" align="center" />
<el-table-column prop="majorLevel" label="培养层次" width="100" align="center" />
<el-table-column label="中德班" width="100" align="center">
<template #default="scope">
<el-tag size="small" :type="scope.row.isGerman === '1' ? 'success' : 'info'" effect="plain">
{{ scope.row.isGerman === '1' ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="countryMajorName" label="国际专业名称" min-width="160" show-overflow-tooltip align="center" />
<el-table-column prop="remarks" label="备注" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="180" align="center" fixed="right">
<template #default="scope">
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="BasicMajor">
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObj, fetchList } from '/@/api/basic/major'
import { getDeptList } from '/@/api/basic/basicclass'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Document, List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const deptList = ref<any[]>([])
const searchForm = reactive({
majorCode: '',
majorName: '',
deptCode: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.majorCode = ''
searchForm.majorName = ''
searchForm.deptCode = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到专业ID')
return
}
try {
await useMessageBox().confirm('确定要删除该专业吗?')
} catch {
return
}
try {
await delObj(id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
const getDeptListData = async () => {
try {
const res = await getDeptList()
deptList.value = Array.isArray(res?.data) ? res.data : []
} catch {
deptList.value = []
}
}
onMounted(() => {
getDeptListData()
})
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,133 @@
<template>
<el-dialog
title="校园新闻预览"
v-model="visible"
:close-on-click-modal="false"
draggable
width="980px">
<div v-loading="loading" class="article-container">
<div class="article-header">
<h1 class="article-title">{{ detailData.title || '-' }}</h1>
<div class="article-meta">
<span>发布时间{{ detailData.pubTime || '-' }}</span>
</div>
</div>
<div v-if="detailData.thumb" class="thumb-wrapper">
<el-image :src="detailData.thumb" :preview-src-list="[detailData.thumb]" fit="cover" class="thumb-image" />
</div>
<div class="article-content" v-html="detailData.content || '-'"></div>
<div v-if="detailData.remarks" class="article-remarks">备注{{ detailData.remarks }}</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="SchoolNewsDetailDialog">
import { reactive, ref } from 'vue'
import { getObj } from '/@/api/basic/schoolnews'
import { useMessage } from '/@/hooks/message'
const visible = ref(false)
const loading = ref(false)
const detailData = reactive({
id: '',
title: '',
content: '',
thumb: '',
remarks: '',
pubTime: ''
})
const openDialog = async (id: string) => {
if (!id) {
useMessage().warning('未获取到新闻ID')
return
}
visible.value = true
loading.value = true
try {
const res = await getObj(id)
const data = res?.data || {}
Object.assign(detailData, {
id: data.id || '',
title: data.title || '',
content: data.content || '',
thumb: data.thumb || '',
remarks: data.remarks || '',
pubTime: data.pubTime || ''
})
} catch (err: any) {
useMessage().error(err.msg || '获取新闻详情失败')
} finally {
loading.value = false
}
}
defineExpose({
openDialog
})
</script>
<style scoped lang="scss">
.article-container {
padding: 12px 20px;
}
.article-header {
border-bottom: 1px solid #ebeef5;
margin-bottom: 16px;
padding-bottom: 12px;
}
.article-title {
margin: 0 0 10px;
font-size: 26px;
font-weight: 700;
line-height: 1.4;
text-align: center;
}
.article-meta {
color: #909399;
text-align: center;
font-size: 13px;
}
.thumb-wrapper {
margin: 10px 0 16px;
text-align: center;
}
.thumb-image {
width: 360px;
max-width: 100%;
height: auto;
border-radius: 4px;
}
.article-content {
font-size: 16px;
line-height: 1.8;
color: #303133;
word-break: break-word;
}
.article-content :deep(img) {
max-width: 100%;
height: auto;
}
.article-remarks {
margin-top: 16px;
font-size: 13px;
color: #606266;
padding: 10px 12px;
background: #f5f7fa;
border-radius: 4px;
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<el-dialog
:title="form.id ? '编辑校园新闻' : '新增校园新闻'"
v-model="visible"
:close-on-click-modal="false"
draggable
width="820px">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
:validate-on-rule-change="false"
v-loading="loading">
<el-row :gutter="20">
<el-col :span="24" class="mb20">
<el-form-item label="新闻标题" prop="title">
<el-input v-model="form.title" maxlength="120" show-word-limit placeholder="请输入新闻标题" clearable />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="发布时间" prop="pubTime">
<el-date-picker
v-model="form.pubTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择发布时间"
clearable
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="缩略图" prop="thumb">
<UploadImg
v-model="form.thumb"
:width="'180px'"
:height="'110px'"
upload-file-url="/admin/sys-file/upload" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="新闻内容" prop="content">
<Editor v-model:get-html="form.content" height="360" placeholder="请输入新闻内容" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="备注" prop="remarks">
<el-input v-model="form.remarks" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="SchoolNewsDialog">
import { nextTick, reactive, ref } from 'vue'
import { addObj, getObj, putObj } from '/@/api/basic/schoolnews'
import { useMessage } from '/@/hooks/message'
import Editor from '/@/components/Editor/index.vue'
import UploadImg from '/@/components/Upload/Image.vue'
const emit = defineEmits(['refresh'])
const visible = ref(false)
const loading = ref(false)
const formRef = ref()
const form = reactive({
id: '',
title: '',
content: '',
thumb: '',
remarks: '',
pubTime: ''
})
const rules = ref({
title: [{ required: true, message: '新闻标题不能为空', trigger: 'blur' }],
content: [{ required: true, message: '新闻内容不能为空', trigger: 'blur' }],
pubTime: [{ required: true, message: '发布时间不能为空', trigger: 'change' }],
thumb: [{ required: true, message: '请上传缩略图', trigger: 'change' }]
})
const resetForm = () => {
Object.assign(form, {
id: '',
title: '',
content: '',
thumb: '',
remarks: '',
pubTime: ''
})
}
const openDialog = async (id?: string) => {
visible.value = true
resetForm()
nextTick(() => {
formRef.value?.clearValidate()
})
if (!id) return
form.id = id
loading.value = true
try {
const res = await getObj(id)
const row = res?.data || {}
Object.assign(form, {
id: row.id || '',
title: row.title || '',
content: row.content || '',
thumb: row.thumb || '',
remarks: row.remarks || '',
pubTime: row.pubTime || ''
})
} catch (err: any) {
useMessage().error(err.msg || '获取新闻详情失败')
} finally {
loading.value = false
}
}
const handleSubmit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
try {
loading.value = true
if (form.id) {
await putObj(form)
useMessage().success('编辑成功')
} else {
await addObj(form)
useMessage().success('新增成功')
}
visible.value = false
emit('refresh')
} catch (err: any) {
useMessage().error(err.msg || (form.id ? '编辑失败' : '新增失败'))
} finally {
loading.value = false
}
}
defineExpose({
openDialog
})
</script>

View File

@@ -0,0 +1,189 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="新闻标题" prop="title">
<el-input v-model="searchForm.title" placeholder="请输入新闻标题" clearable style="width: 220px" />
</el-form-item>
<el-form-item label="发布时间" prop="pubTime">
<el-date-picker
v-model="searchForm.pubTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择发布时间"
clearable
style="width: 220px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
校园新闻列表
</span>
<div class="header-actions">
<el-button icon="FolderAdd" type="primary" @click="formDialogRef.openDialog()">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
@sort-change="sortChangeHandle">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<el-table-column prop="title" label="新闻标题" min-width="220" show-overflow-tooltip align="center" />
<el-table-column prop="thumb" label="缩略图" width="120" align="center">
<template #default="scope">
<el-image
v-if="scope.row.thumb"
:src="scope.row.thumb"
:preview-src-list="[scope.row.thumb]"
fit="cover"
style="width: 70px; height: 45px">
<template #error>
<span>-</span>
</template>
</el-image>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="pubTime" label="发布时间" width="180" show-overflow-tooltip align="center" />
<el-table-column prop="remarks" label="备注" min-width="180" show-overflow-tooltip align="center" />
<el-table-column label="操作" width="240" align="center" fixed="right">
<template #default="scope">
<el-button icon="View" link type="primary" @click="handlePreview(scope.row.id)">
预览
</el-button>
<el-button icon="EditPen" link type="primary" @click="formDialogRef.openDialog(scope.row.id)">
编辑
</el-button>
<el-button icon="Delete" link type="danger" @click="handleDelete(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<div class="pagination-wrapper">
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
</div>
</el-card>
</div>
<FormDialog ref="formDialogRef" @refresh="getDataList(false)" />
<DetailDialog ref="detailDialogRef" />
</div>
</template>
<script setup lang="ts" name="SchoolNews">
import { defineAsyncComponent, reactive, ref } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { delObj, fetchList } from '/@/api/basic/schoolnews'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Document, List, Search } from '@element-plus/icons-vue'
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'))
const formDialogRef = ref()
const detailDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
const searchForm = reactive({
title: '',
pubTime: ''
})
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: searchForm,
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true
})
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
tableStyle
} = useTable(state)
const handleSearch = () => {
getDataList()
}
const handleReset = () => {
searchFormRef.value?.resetFields()
searchForm.title = ''
searchForm.pubTime = ''
getDataList()
}
const handleDelete = async (id: string) => {
if (!id) {
useMessage().warning('未获取到新闻ID')
return
}
try {
await useMessageBox().confirm('确定要删除该校园新闻吗?')
} catch {
return
}
try {
await delObj(id)
useMessage().success('删除成功')
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '删除失败')
}
}
const handlePreview = (id: string) => {
detailDialogRef.value?.openDialog(id)
}
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -68,6 +68,13 @@
@click="formDialogRef.openDialog()"> @click="formDialogRef.openDialog()">
新增 新增
</el-button> </el-button>
<el-button
icon="Histogram"
type="success"
class="ml10"
@click="handleReadStatistics()">
统计班主任查看情况
</el-button>
<right-toolbar <right-toolbar
v-model:showSearch="showSearch" v-model:showSearch="showSearch"
:export="'stuwork_weekPlan_export'" :export="'stuwork_weekPlan_export'"
@@ -152,13 +159,6 @@
@click="formDialogRef.openDialog(scope.row.id)"> @click="formDialogRef.openDialog(scope.row.id)">
编辑 编辑
</el-button> </el-button>
<el-button
icon="Histogram"
link
type="success"
@click="handleReadStatistics(scope.row)">
查看班主任查看情况
</el-button>
<el-button <el-button
icon="Delete" icon="Delete"
link link
@@ -305,13 +305,9 @@ const handleViewDetail = async (id: string) => {
} }
// 查看班主任查看情况统计 // 查看班主任查看情况统计
const handleReadStatistics = async (row: any) => { const handleReadStatistics = async (row?: any) => {
if (!row?.id) {
useMessage().warning('未获取到每周工作ID')
return
}
await nextTick() await nextTick()
readStatisticsDialogRef.value?.openDialog(row.id) readStatisticsDialogRef.value?.openDialog(row?.id || '')
} }
// 删除操作 // 删除操作

View File

@@ -6,6 +6,31 @@
width="900px" width="900px"
draggable draggable
> >
<!-- 查询条件学院选择 -->
<el-form :inline="true" class="filter-form">
<el-form-item label="学院">
<el-select
v-model="selectedDeptCode"
placeholder="请选择学院"
clearable
filterable
style="width: 220px"
>
<el-option
v-for="item in deptList"
:key="item.deptCode"
:label="item.deptName"
:value="item.deptCode"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">
查询
</el-button>
</el-form-item>
</el-form>
<el-table <el-table
:data="dataList" :data="dataList"
v-loading="loading" v-loading="loading"
@@ -40,42 +65,38 @@
</template> </template>
<script setup lang="ts" name="WeekPlanReadStatisticsDialog"> <script setup lang="ts" name="WeekPlanReadStatisticsDialog">
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import { useMessage } from '/@/hooks/message' import { useMessage } from '/@/hooks/message'
import { readStatistics } from '/@/api/stuwork/weekplan' import { readStatistics } from '/@/api/stuwork/weekplan'
import { useUserInfo } from '/@/stores/userInfo' import { getDeptList } from '/@/api/basic/basicclass'
const visible = ref(false) const visible = ref(false)
const loading = ref(false) const loading = ref(false)
const dataList = ref<any[]>([]) const dataList = ref<any[]>([])
const deptList = ref<any[]>([])
const selectedDeptCode = ref('')
const currentWeekPlanId = ref('')
const openDialog = async (weekPlanId: string) => { const openDialog = async (weekPlanId: string) => {
if (!weekPlanId) {
useMessage().warning('未获取到每周工作ID')
return
}
visible.value = true visible.value = true
dataList.value = [] dataList.value = []
await fetchData(weekPlanId) currentWeekPlanId.value = weekPlanId || ''
} }
const fetchData = async (weekPlanId: string) => { const handleQuery = async () => {
try { if (!selectedDeptCode.value) {
loading.value = true useMessage().warning('请选择学院')
const userStore = useUserInfo()
const deptCode =
(userStore.userInfos as any)?.user?.deptCode ||
(userStore.userInfos as any)?.user?.deptCodeList?.[0] ||
''
if (!deptCode) {
useMessage().error('未获取到部门编码,无法统计班主任查看情况')
return return
} }
await fetchData()
}
const fetchData = async () => {
try {
loading.value = true
const res: any = await readStatistics({ const res: any = await readStatistics({
deptCode, deptCode: selectedDeptCode.value,
weekPlanId, weekPlanId: currentWeekPlanId.value || undefined,
}) })
if (Array.isArray(res?.data)) { if (Array.isArray(res?.data)) {
dataList.value = res.data dataList.value = res.data
@@ -93,11 +114,31 @@ const fetchData = async (weekPlanId: string) => {
} }
} }
const getDeptListData = async () => {
try {
const res = await getDeptList()
if (res.data && Array.isArray(res.data)) {
deptList.value = res.data
} else {
deptList.value = []
}
} catch (err) {
deptList.value = []
}
}
onMounted(() => {
getDeptListData()
})
defineExpose({ defineExpose({
openDialog, openDialog,
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.filter-form {
margin-bottom: 15px;
}
</style> </style>