Compare commits
No commits in common. "4023e9ab37d41c33ddcd598b383f96f83ce82252" and "a11329c5022c7a4c78dc1c2517d1e42a303b3984" have entirely different histories.
4023e9ab37
...
a11329c502
|
|
@ -34,7 +34,7 @@
|
|||
"/docs": true,
|
||||
"**/dist/**": true,
|
||||
"**/node_modules": true,
|
||||
"node_modules/**": true
|
||||
"node_modules/**": true,
|
||||
},
|
||||
"cSpell.words": ["Axios", "tinymce"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,8 +302,6 @@ const local: App.I18n.Schema = {
|
|||
tool: 'System Tools',
|
||||
tool_gen: 'Code Generation',
|
||||
art: 'Art',
|
||||
'art_history-score-control-line': 'History Score Control Line',
|
||||
art_major: 'Art Major Library',
|
||||
art_school: 'School',
|
||||
art_school_modules: 'School Modules',
|
||||
'art_school_modules_school-campus': 'School Campus',
|
||||
|
|
@ -316,8 +314,6 @@ const local: App.I18n.Schema = {
|
|||
'art_school_modules_school-media': 'School Media',
|
||||
'art_school_modules_school-name': 'School Name',
|
||||
'art_school_modules_school-tag': 'School Tag',
|
||||
'art_school-recruit-major': 'School Recruit Major',
|
||||
'art_school-recruit-major-history': 'School Recruit Major History',
|
||||
about: 'About'
|
||||
},
|
||||
menu: {
|
||||
|
|
|
|||
|
|
@ -298,8 +298,6 @@ const local: App.I18n.Schema = {
|
|||
tool: '系统工具',
|
||||
tool_gen: '代码生成',
|
||||
art: '艺术院校',
|
||||
'art_history-score-control-line': '历年省控线',
|
||||
art_major: '艺术专业库',
|
||||
art_school: '院校管理',
|
||||
art_school_modules: '院校子模块',
|
||||
'art_school_modules_school-campus': '校区管理',
|
||||
|
|
@ -311,9 +309,7 @@ const local: App.I18n.Schema = {
|
|||
'art_school_modules_school-major-tag': '专业标签',
|
||||
'art_school_modules_school-media': '媒体管理',
|
||||
'art_school_modules_school-name': '院校名称',
|
||||
'art_school_modules_school-tag': '学校标签',
|
||||
'art_school-recruit-major': '院校招录专业',
|
||||
'art_school-recruit-major-history': '院校招录专业历年录取',
|
||||
'art_school_modules_school-tag': '院校标签',
|
||||
about: '关于'
|
||||
},
|
||||
menu: {
|
||||
|
|
|
|||
|
|
@ -23,10 +23,6 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||
"social-callback": () => import("@/views/_builtin/social-callback/index.vue"),
|
||||
"user-center": () => import("@/views/_builtin/user-center/index.vue"),
|
||||
about: () => import("@/views/about/index.vue"),
|
||||
"art_history-score-control-line": () => import("@/views/art/history-score-control-line/index.vue"),
|
||||
art_major: () => import("@/views/art/major/index.vue"),
|
||||
"art_school-recruit-major-history": () => import("@/views/art/school-recruit-major-history/index.vue"),
|
||||
"art_school-recruit-major": () => import("@/views/art/school-recruit-major/index.vue"),
|
||||
art_school: () => import("@/views/art/school/index.vue"),
|
||||
"art_school_modules_school-campus": () => import("@/views/art/school/modules/school-campus/index.vue"),
|
||||
"art_school_modules_school-college": () => import("@/views/art/school/modules/school-college/index.vue"),
|
||||
|
|
|
|||
|
|
@ -59,24 +59,6 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||
i18nKey: 'route.art'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'art_history-score-control-line',
|
||||
path: '/art/history-score-control-line',
|
||||
component: 'view.art_history-score-control-line',
|
||||
meta: {
|
||||
title: 'art_history-score-control-line',
|
||||
i18nKey: 'route.art_history-score-control-line'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'art_major',
|
||||
path: '/art/major',
|
||||
component: 'view.art_major',
|
||||
meta: {
|
||||
title: 'art_major',
|
||||
i18nKey: 'route.art_major'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'art_school',
|
||||
path: '/art/school',
|
||||
|
|
@ -187,24 +169,6 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'art_school-recruit-major',
|
||||
path: '/art/school-recruit-major',
|
||||
component: 'view.art_school-recruit-major',
|
||||
meta: {
|
||||
title: 'art_school-recruit-major',
|
||||
i18nKey: 'route.art_school-recruit-major'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'art_school-recruit-major-history',
|
||||
path: '/art/school-recruit-major-history',
|
||||
component: 'view.art_school-recruit-major-history',
|
||||
meta: {
|
||||
title: 'art_school-recruit-major-history',
|
||||
i18nKey: 'route.art_school-recruit-major-history'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -172,8 +172,6 @@ const routeMap: RouteMap = {
|
|||
"500": "/500",
|
||||
"about": "/about",
|
||||
"art": "/art",
|
||||
"art_history-score-control-line": "/art/history-score-control-line",
|
||||
"art_major": "/art/major",
|
||||
"art_school": "/art/school",
|
||||
"art_school_modules": "/art/school/modules",
|
||||
"art_school_modules_school-campus": "/art/school/modules/school-campus",
|
||||
|
|
@ -186,8 +184,6 @@ const routeMap: RouteMap = {
|
|||
"art_school_modules_school-media": "/art/school/modules/school-media",
|
||||
"art_school_modules_school-name": "/art/school/modules/school-name",
|
||||
"art_school_modules_school-tag": "/art/school/modules/school-tag",
|
||||
"art_school-recruit-major": "/art/school-recruit-major",
|
||||
"art_school-recruit-major-history": "/art/school-recruit-major-history",
|
||||
"demo": "/demo",
|
||||
"demo_demo": "/demo/demo",
|
||||
"demo_tree": "/demo/tree",
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { request } from '@/service/request';
|
||||
|
||||
/** 获取历年省控线列表 */
|
||||
export function fetchGetHistoryScoreControlLineList(params?: Api.Art.HistoryScoreControlLineSearchParams) {
|
||||
return request<Api.Art.HistoryScoreControlLineList>({
|
||||
url: '/art/historyScoreControlLine/list',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
/** 新增历年省控线 */
|
||||
export function fetchCreateHistoryScoreControlLine(data: Api.Art.HistoryScoreControlLineOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/historyScoreControlLine',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改历年省控线 */
|
||||
export function fetchUpdateHistoryScoreControlLine(data: Api.Art.HistoryScoreControlLineOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/historyScoreControlLine',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 批量删除历年省控线 */
|
||||
export function fetchBatchDeleteHistoryScoreControlLine(controlIds: CommonType.IdType[]) {
|
||||
return request<boolean>({
|
||||
url: `/art/historyScoreControlLine/${controlIds.join(',')}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { request } from '@/service/request';
|
||||
|
||||
/** 获取艺术专业库列表 */
|
||||
export function fetchGetMajorList(params?: Api.Art.MajorSearchParams) {
|
||||
return request<Api.Art.MajorList>({
|
||||
url: '/art/major/list',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
/** 新增艺术专业库 */
|
||||
export function fetchCreateMajor(data: Api.Art.MajorOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/major',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改艺术专业库 */
|
||||
export function fetchUpdateMajor(data: Api.Art.MajorOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/major',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 批量删除艺术专业库 */
|
||||
export function fetchBatchDeleteMajor(majorIds: CommonType.IdType[]) {
|
||||
return request<boolean>({
|
||||
url: `/art/major/${majorIds.join(',')}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { request } from '@/service/request';
|
||||
|
||||
/** 获取院校招录专业历年录取数据列表 */
|
||||
export function fetchGetSchoolRecruitMajorHistoryList(params?: Api.Art.SchoolRecruitMajorHistorySearchParams) {
|
||||
return request<Api.Art.SchoolRecruitMajorHistoryList>({
|
||||
url: '/art/schoolRecruitMajorHistory/list',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
/** 新增院校招录专业历年录取数据 */
|
||||
export function fetchCreateSchoolRecruitMajorHistory(data: Api.Art.SchoolRecruitMajorHistoryOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/schoolRecruitMajorHistory',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改院校招录专业历年录取数据 */
|
||||
export function fetchUpdateSchoolRecruitMajorHistory(data: Api.Art.SchoolRecruitMajorHistoryOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/schoolRecruitMajorHistory',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 批量删除院校招录专业历年录取数据 */
|
||||
export function fetchBatchDeleteSchoolRecruitMajorHistory(historyIds: CommonType.IdType[]) {
|
||||
return request<boolean>({
|
||||
url: `/art/schoolRecruitMajorHistory/${historyIds.join(',')}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { request } from '@/service/request';
|
||||
|
||||
/** 获取院校招录专业列表 */
|
||||
export function fetchGetSchoolRecruitMajorList(params?: Api.Art.SchoolRecruitMajorSearchParams) {
|
||||
return request<Api.Art.SchoolRecruitMajorList>({
|
||||
url: '/art/schoolRecruitMajor/list',
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
/** 新增院校招录专业 */
|
||||
export function fetchCreateSchoolRecruitMajor(data: Api.Art.SchoolRecruitMajorOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/schoolRecruitMajor',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改院校招录专业 */
|
||||
export function fetchUpdateSchoolRecruitMajor(data: Api.Art.SchoolRecruitMajorOperateParams) {
|
||||
return request<boolean>({
|
||||
url: '/art/schoolRecruitMajor',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 批量删除院校招录专业 */
|
||||
export function fetchBatchDeleteSchoolRecruitMajor(recruitMajorIds: CommonType.IdType[]) {
|
||||
return request<boolean>({
|
||||
url: `/art/schoolRecruitMajor/${recruitMajorIds.join(',')}`,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
|
@ -3,19 +3,3 @@
|
|||
--n-title-font-size: 15px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep school search form inputs readable across theme/custom overrides */
|
||||
.school-search-card {
|
||||
.n-input .n-input__input-el,
|
||||
.n-input .n-input__textarea-el,
|
||||
.n-base-selection .n-base-selection-label,
|
||||
.n-base-selection .n-base-selection-input__content {
|
||||
color: var(--n-text-color) !important;
|
||||
-webkit-text-fill-color: var(--n-text-color) !important;
|
||||
}
|
||||
|
||||
.n-input .n-input__placeholder,
|
||||
.n-base-selection .n-base-selection-placeholder {
|
||||
color: var(--n-placeholder-color) !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* Namespace Api
|
||||
*
|
||||
* All backend api type
|
||||
*/
|
||||
declare namespace Api {
|
||||
/**
|
||||
* namespace Art
|
||||
*
|
||||
* backend api module: "Art"
|
||||
*/
|
||||
namespace Art {
|
||||
/** history score control line */
|
||||
type HistoryScoreControlLine = Common.CommonRecord<{
|
||||
/** 省控线主键 */
|
||||
controlId: CommonType.IdType;
|
||||
/** 租户编号 */
|
||||
tenantId: CommonType.IdType;
|
||||
/** 删除标志(0存在 1删除) */
|
||||
delFlag: string;
|
||||
/** 省份行政区划代码 */
|
||||
provinceCode: string;
|
||||
/** 省份名称 */
|
||||
provinceName: string;
|
||||
/** 年份 */
|
||||
year: number;
|
||||
/** 专业类别 */
|
||||
majorCategory: string;
|
||||
/** 批次 */
|
||||
batchName: string;
|
||||
/** 科类(文/理) */
|
||||
subjectType: string;
|
||||
/** 文化成绩分数 */
|
||||
cultureScore: number;
|
||||
/** 专业成绩分数 */
|
||||
majorScore: number;
|
||||
/** 文化成绩校考分数 */
|
||||
cultureScoreExam: number;
|
||||
/** 专业成绩校考分数 */
|
||||
majorScoreExam: number;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
}>;
|
||||
|
||||
/** history score control line search params */
|
||||
type HistoryScoreControlLineSearchParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.HistoryScoreControlLine,
|
||||
| 'provinceCode'
|
||||
| 'provinceName'
|
||||
| 'year'
|
||||
| 'majorCategory'
|
||||
| 'batchName'
|
||||
| 'subjectType'
|
||||
| 'cultureScore'
|
||||
| 'majorScore'
|
||||
| 'cultureScoreExam'
|
||||
| 'majorScoreExam'
|
||||
> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
/** history score control line operate params */
|
||||
type HistoryScoreControlLineOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.HistoryScoreControlLine,
|
||||
| 'controlId'
|
||||
| 'provinceCode'
|
||||
| 'provinceName'
|
||||
| 'year'
|
||||
| 'majorCategory'
|
||||
| 'batchName'
|
||||
| 'subjectType'
|
||||
| 'cultureScore'
|
||||
| 'majorScore'
|
||||
| 'cultureScoreExam'
|
||||
| 'majorScoreExam'
|
||||
| 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
/** history score control line list */
|
||||
type HistoryScoreControlLineList = Api.Common.PaginatingQueryRecord<HistoryScoreControlLine>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* Namespace Api
|
||||
*
|
||||
* All backend api type
|
||||
*/
|
||||
declare namespace Api {
|
||||
/**
|
||||
* namespace Art
|
||||
*
|
||||
* backend api module: "Art"
|
||||
*/
|
||||
namespace Art {
|
||||
/** major */
|
||||
type Major = Common.CommonRecord<{
|
||||
/** 专业主键ID */
|
||||
majorId: CommonType.IdType;
|
||||
/** 租户编号 */
|
||||
tenantId: CommonType.IdType;
|
||||
/** 删除标志(0存在 1删除) */
|
||||
delFlag: string;
|
||||
/** 专业名称 */
|
||||
majorName: string;
|
||||
/** 学历层次 */
|
||||
educationLevel: string;
|
||||
/** 专业图标 */
|
||||
majorIcon: string;
|
||||
/** 学制(年) */
|
||||
schoolingYears: number;
|
||||
/** 所属一级学科 */
|
||||
disciplinePrimary: string;
|
||||
/** 所属二级学科 */
|
||||
disciplineSecondary: string;
|
||||
/** 授予学士学位 */
|
||||
degreeAwarded: string;
|
||||
/** 专业概括 */
|
||||
summary: string;
|
||||
/** 培养方向 */
|
||||
trainingDirection: string;
|
||||
/** 主要课程 */
|
||||
coreCourses: string;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
}>;
|
||||
|
||||
/** major search params */
|
||||
type MajorSearchParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.Major,
|
||||
| 'majorName'
|
||||
| 'educationLevel'
|
||||
| 'majorIcon'
|
||||
| 'schoolingYears'
|
||||
| 'disciplinePrimary'
|
||||
| 'disciplineSecondary'
|
||||
| 'degreeAwarded'
|
||||
| 'summary'
|
||||
| 'trainingDirection'
|
||||
| 'coreCourses'
|
||||
> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
/** major operate params */
|
||||
type MajorOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.Major,
|
||||
| 'majorId'
|
||||
| 'majorName'
|
||||
| 'educationLevel'
|
||||
| 'majorIcon'
|
||||
| 'schoolingYears'
|
||||
| 'disciplinePrimary'
|
||||
| 'disciplineSecondary'
|
||||
| 'degreeAwarded'
|
||||
| 'summary'
|
||||
| 'trainingDirection'
|
||||
| 'coreCourses'
|
||||
| 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
/** major list */
|
||||
type MajorList = Api.Common.PaginatingQueryRecord<Major>;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,8 +20,6 @@ declare namespace Api {
|
|||
schoolId: CommonType.IdType;
|
||||
/** 学校详细介绍(大文本) */
|
||||
introduction: string;
|
||||
/** 院校图标 */
|
||||
schoolIcon: string;
|
||||
/** 学校地址 */
|
||||
address: string;
|
||||
/** 联系电话 */
|
||||
|
|
@ -30,44 +28,6 @@ declare namespace Api {
|
|||
email: string;
|
||||
/** 官网地址 */
|
||||
website: string;
|
||||
/** 邮编 */
|
||||
postcode: string;
|
||||
/** 建校时间(年) */
|
||||
establishYear: number;
|
||||
/** 占地面积(亩) */
|
||||
campusAreaMu: number;
|
||||
/** 图书馆藏书量 */
|
||||
libraryCollection: number;
|
||||
/** 男生比例(%) */
|
||||
maleRatio: number;
|
||||
/** 女生比例(%) */
|
||||
femaleRatio: number;
|
||||
/** 是否985(0/1) */
|
||||
is985: number;
|
||||
/** 是否211(0/1) */
|
||||
is211: number;
|
||||
/** 是否双一流(0/1) */
|
||||
isDoubleFirstClass: number;
|
||||
/** 是否重点大学(0/1) */
|
||||
isKeyUniversity: number;
|
||||
/** 标签 */
|
||||
tags: string[];
|
||||
/** 学生人数 */
|
||||
studentCount: number;
|
||||
/** 教师人数 */
|
||||
teacherCount: number;
|
||||
/** 硕士点数量 */
|
||||
masterPoint: number;
|
||||
/** 博士点数量 */
|
||||
doctorPoint: number;
|
||||
/** 重点专业数量 */
|
||||
keyMajorCount: number;
|
||||
/** 就业率(%) */
|
||||
employmentRate: number;
|
||||
/** 满意度(%) */
|
||||
satisfactionRate: number;
|
||||
/** 外部学校ID */
|
||||
univId: number;
|
||||
/** 删除标志(0代表存在 1代表删除) */
|
||||
delFlag: string;
|
||||
/** 备注 */
|
||||
|
|
@ -76,36 +36,7 @@ declare namespace Api {
|
|||
|
||||
/** school detail search params */
|
||||
type SchoolDetailSearchParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolDetail,
|
||||
| 'detailId'
|
||||
| 'schoolId'
|
||||
| 'introduction'
|
||||
| 'schoolIcon'
|
||||
| 'address'
|
||||
| 'contact'
|
||||
| 'email'
|
||||
| 'website'
|
||||
| 'postcode'
|
||||
| 'establishYear'
|
||||
| 'campusAreaMu'
|
||||
| 'libraryCollection'
|
||||
| 'maleRatio'
|
||||
| 'femaleRatio'
|
||||
| 'is985'
|
||||
| 'is211'
|
||||
| 'isDoubleFirstClass'
|
||||
| 'isKeyUniversity'
|
||||
| 'tags'
|
||||
| 'studentCount'
|
||||
| 'teacherCount'
|
||||
| 'masterPoint'
|
||||
| 'doctorPoint'
|
||||
| 'keyMajorCount'
|
||||
| 'employmentRate'
|
||||
| 'satisfactionRate'
|
||||
| 'univId'
|
||||
> &
|
||||
Pick<Api.Art.SchoolDetail, 'schoolId' | 'introduction' | 'address' | 'contact' | 'email' | 'website'> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
|
|
@ -113,34 +44,7 @@ declare namespace Api {
|
|||
type SchoolDetailOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolDetail,
|
||||
| 'detailId'
|
||||
| 'schoolId'
|
||||
| 'introduction'
|
||||
| 'schoolIcon'
|
||||
| 'address'
|
||||
| 'contact'
|
||||
| 'email'
|
||||
| 'website'
|
||||
| 'postcode'
|
||||
| 'establishYear'
|
||||
| 'campusAreaMu'
|
||||
| 'libraryCollection'
|
||||
| 'maleRatio'
|
||||
| 'femaleRatio'
|
||||
| 'is985'
|
||||
| 'is211'
|
||||
| 'isDoubleFirstClass'
|
||||
| 'isKeyUniversity'
|
||||
| 'tags'
|
||||
| 'studentCount'
|
||||
| 'teacherCount'
|
||||
| 'masterPoint'
|
||||
| 'doctorPoint'
|
||||
| 'keyMajorCount'
|
||||
| 'employmentRate'
|
||||
| 'satisfactionRate'
|
||||
| 'univId'
|
||||
| 'remark'
|
||||
'detailId' | 'schoolId' | 'introduction' | 'address' | 'contact' | 'email' | 'website' | 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,145 +0,0 @@
|
|||
/**
|
||||
* Namespace Api
|
||||
*
|
||||
* All backend api type
|
||||
*/
|
||||
declare namespace Api {
|
||||
/**
|
||||
* namespace Art
|
||||
*
|
||||
* backend api module: "Art"
|
||||
*/
|
||||
namespace Art {
|
||||
/** school recruit major history */
|
||||
type SchoolRecruitMajorHistory = Common.CommonRecord<{
|
||||
/** 历年录取数据ID */
|
||||
historyId: CommonType.IdType;
|
||||
/** 租户编号 */
|
||||
tenantId: CommonType.IdType;
|
||||
/** 删除标志(0存在 1删除) */
|
||||
delFlag: string;
|
||||
/** 对应招录专业ID */
|
||||
recruitMajorId: CommonType.IdType;
|
||||
/** 学校ID */
|
||||
schoolId: CommonType.IdType;
|
||||
/** 学校代码 */
|
||||
schoolCode: string;
|
||||
/** 院校代码 */
|
||||
collegeCode: string;
|
||||
/** 学校名称 */
|
||||
schoolName: string;
|
||||
/** 专业ID */
|
||||
majorId: CommonType.IdType;
|
||||
/** 专业代码 */
|
||||
majorCode: string;
|
||||
/** 专业名称 */
|
||||
majorName: string;
|
||||
/** 招生代码 */
|
||||
enrollCode: string;
|
||||
/** 专业类型 */
|
||||
majorType: string;
|
||||
/** 专业类别子级 */
|
||||
majorTypeSub: string;
|
||||
/** 主考科目 */
|
||||
mainExamSubject: string;
|
||||
/** 年份 */
|
||||
year: number;
|
||||
/** 科类(文/理) */
|
||||
subjectType: string;
|
||||
/** 批次 */
|
||||
batchName: string;
|
||||
/** 录取方式(文*x+专*y) */
|
||||
admissionFormula: string;
|
||||
/** 录取概率规则运算符 */
|
||||
probabilityOperator: string;
|
||||
/** 省控线 */
|
||||
controlScore: number;
|
||||
/** 录取线 */
|
||||
admissionScore: number;
|
||||
/** 招生人数 */
|
||||
planEnroll: number;
|
||||
/** 实际投档人数 */
|
||||
filedAmount: number;
|
||||
/** 录取数 */
|
||||
admitAmount: number;
|
||||
/** 一志愿录取数 */
|
||||
firstChoiceAdmitAmount: number;
|
||||
/** 最低分数差 */
|
||||
minScoreDiff: number;
|
||||
/** 学费(元/年) */
|
||||
tuitionFee: number;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
}>;
|
||||
|
||||
/** school recruit major history search params */
|
||||
type SchoolRecruitMajorHistorySearchParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolRecruitMajorHistory,
|
||||
| 'recruitMajorId'
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'collegeCode'
|
||||
| 'schoolName'
|
||||
| 'majorId'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'enrollCode'
|
||||
| 'majorType'
|
||||
| 'majorTypeSub'
|
||||
| 'mainExamSubject'
|
||||
| 'year'
|
||||
| 'subjectType'
|
||||
| 'batchName'
|
||||
| 'admissionFormula'
|
||||
| 'probabilityOperator'
|
||||
| 'controlScore'
|
||||
| 'admissionScore'
|
||||
| 'planEnroll'
|
||||
| 'filedAmount'
|
||||
| 'admitAmount'
|
||||
| 'firstChoiceAdmitAmount'
|
||||
| 'minScoreDiff'
|
||||
| 'tuitionFee'
|
||||
> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
/** school recruit major history operate params */
|
||||
type SchoolRecruitMajorHistoryOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolRecruitMajorHistory,
|
||||
| 'historyId'
|
||||
| 'recruitMajorId'
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'collegeCode'
|
||||
| 'schoolName'
|
||||
| 'majorId'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'enrollCode'
|
||||
| 'majorType'
|
||||
| 'majorTypeSub'
|
||||
| 'mainExamSubject'
|
||||
| 'year'
|
||||
| 'subjectType'
|
||||
| 'batchName'
|
||||
| 'admissionFormula'
|
||||
| 'probabilityOperator'
|
||||
| 'controlScore'
|
||||
| 'admissionScore'
|
||||
| 'planEnroll'
|
||||
| 'filedAmount'
|
||||
| 'admitAmount'
|
||||
| 'firstChoiceAdmitAmount'
|
||||
| 'minScoreDiff'
|
||||
| 'tuitionFee'
|
||||
| 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
/** school recruit major history list */
|
||||
type SchoolRecruitMajorHistoryList = Api.Common.PaginatingQueryRecord<SchoolRecruitMajorHistory>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
/**
|
||||
* Namespace Api
|
||||
*
|
||||
* All backend api type
|
||||
*/
|
||||
declare namespace Api {
|
||||
/**
|
||||
* namespace Art
|
||||
*
|
||||
* backend api module: "Art"
|
||||
*/
|
||||
namespace Art {
|
||||
/** school recruit major */
|
||||
type SchoolRecruitMajor = Common.CommonRecord<{
|
||||
/** 院校招录专业ID */
|
||||
recruitMajorId: CommonType.IdType;
|
||||
/** 租户编号 */
|
||||
tenantId: CommonType.IdType;
|
||||
/** 删除标志(0存在 1删除) */
|
||||
delFlag: string;
|
||||
/** 学校ID */
|
||||
schoolId: CommonType.IdType;
|
||||
/** 学校代码 */
|
||||
schoolCode: string;
|
||||
/** 学校名称(冗余) */
|
||||
schoolName: string;
|
||||
/** 年份 */
|
||||
year: number;
|
||||
/** 专业ID */
|
||||
majorId: CommonType.IdType;
|
||||
/** 专业代码 */
|
||||
majorCode: string;
|
||||
/** 专业名称 */
|
||||
majorName: string;
|
||||
/** 招生代码(为空则存空串) */
|
||||
enrollCode: string;
|
||||
/** 数据状态(停招/新招/新增) */
|
||||
dataStatus: string;
|
||||
/** 批次 */
|
||||
batchName: string;
|
||||
/** 专业类型 */
|
||||
majorType: string;
|
||||
/** 二级专业类型 */
|
||||
majorTypeSub: string;
|
||||
/** 科类(文/理) */
|
||||
subjectType: string;
|
||||
/** 录取方式缩写 */
|
||||
admissionWayShort: string;
|
||||
/** 对外录取方式 */
|
||||
admissionWayExternal: string;
|
||||
/** 对外录取方式运算符 */
|
||||
admissionWayExternalOp: string;
|
||||
/** 内部录取方式 */
|
||||
admissionWayInternal: string;
|
||||
/** 内部录取方式运算符 */
|
||||
admissionWayInternalOp: string;
|
||||
/** 计划招生人数 */
|
||||
planEnroll: number;
|
||||
/** 主考科目 */
|
||||
mainExamSubject: string;
|
||||
/** 学制(年) */
|
||||
schoolingYears: number;
|
||||
/** 院校限制说明 */
|
||||
enrollLimitDesc: string;
|
||||
/** 学费(元/年) */
|
||||
tuitionFee: number;
|
||||
/** 文化分数限制 */
|
||||
cultureScoreLimit: number;
|
||||
/** 专业分数限制 */
|
||||
majorScoreLimit: number;
|
||||
/** 语文成绩限制 */
|
||||
chineseScoreLimit: number;
|
||||
/** 英语成绩限制 */
|
||||
englishScoreLimit: number;
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
}>;
|
||||
|
||||
/** school recruit major search params */
|
||||
type SchoolRecruitMajorSearchParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolRecruitMajor,
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'schoolName'
|
||||
| 'year'
|
||||
| 'majorId'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'enrollCode'
|
||||
| 'dataStatus'
|
||||
| 'batchName'
|
||||
| 'majorType'
|
||||
| 'majorTypeSub'
|
||||
| 'subjectType'
|
||||
| 'admissionWayShort'
|
||||
| 'admissionWayExternal'
|
||||
| 'admissionWayExternalOp'
|
||||
| 'admissionWayInternal'
|
||||
| 'admissionWayInternalOp'
|
||||
| 'planEnroll'
|
||||
| 'mainExamSubject'
|
||||
| 'schoolingYears'
|
||||
| 'enrollLimitDesc'
|
||||
| 'tuitionFee'
|
||||
| 'cultureScoreLimit'
|
||||
| 'majorScoreLimit'
|
||||
| 'chineseScoreLimit'
|
||||
| 'englishScoreLimit'
|
||||
> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
||||
/** school recruit major operate params */
|
||||
type SchoolRecruitMajorOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolRecruitMajor,
|
||||
| 'recruitMajorId'
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'schoolName'
|
||||
| 'year'
|
||||
| 'majorId'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'enrollCode'
|
||||
| 'dataStatus'
|
||||
| 'batchName'
|
||||
| 'majorType'
|
||||
| 'majorTypeSub'
|
||||
| 'subjectType'
|
||||
| 'admissionWayShort'
|
||||
| 'admissionWayExternal'
|
||||
| 'admissionWayExternalOp'
|
||||
| 'admissionWayInternal'
|
||||
| 'admissionWayInternalOp'
|
||||
| 'planEnroll'
|
||||
| 'mainExamSubject'
|
||||
| 'schoolingYears'
|
||||
| 'enrollLimitDesc'
|
||||
| 'tuitionFee'
|
||||
| 'cultureScoreLimit'
|
||||
| 'majorScoreLimit'
|
||||
| 'chineseScoreLimit'
|
||||
| 'englishScoreLimit'
|
||||
| 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
/** school recruit major list */
|
||||
type SchoolRecruitMajorList = Api.Common.PaginatingQueryRecord<SchoolRecruitMajor>;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@ declare namespace Api {
|
|||
enrollCodes: string[];
|
||||
/** 学校标签列表 */
|
||||
schoolTags: string[];
|
||||
/** 院校图标 */
|
||||
schoolIcon: string;
|
||||
/** 省份 */
|
||||
province: string;
|
||||
/** 城市 */
|
||||
|
|
@ -40,6 +42,14 @@ declare namespace Api {
|
|||
schoolNature: string;
|
||||
/** 主管部门:教育部/工信部/民委... */
|
||||
supervisorDept: string;
|
||||
/** 建校时间(年) */
|
||||
establishYear: number;
|
||||
/** 占地面积(亩) */
|
||||
campusAreaMu: number;
|
||||
/** 男生比例(%) */
|
||||
maleRatio: number;
|
||||
/** 女生比例(%) */
|
||||
femaleRatio: number;
|
||||
/** 删除标志(0代表存在 1代表删除) */
|
||||
delFlag: string;
|
||||
/** 备注 */
|
||||
|
|
@ -54,6 +64,7 @@ declare namespace Api {
|
|||
| 'mainCode'
|
||||
| 'mainName'
|
||||
| 'shortName'
|
||||
| 'schoolIcon'
|
||||
| 'province'
|
||||
| 'city'
|
||||
| 'district'
|
||||
|
|
@ -61,6 +72,10 @@ declare namespace Api {
|
|||
| 'educationLevel'
|
||||
| 'schoolNature'
|
||||
| 'supervisorDept'
|
||||
| 'establishYear'
|
||||
| 'campusAreaMu'
|
||||
| 'maleRatio'
|
||||
| 'femaleRatio'
|
||||
> &
|
||||
Api.Common.CommonSearchParams
|
||||
>;
|
||||
|
|
@ -73,6 +88,7 @@ declare namespace Api {
|
|||
| 'mainCode'
|
||||
| 'mainName'
|
||||
| 'shortName'
|
||||
| 'schoolIcon'
|
||||
| 'province'
|
||||
| 'city'
|
||||
| 'district'
|
||||
|
|
@ -80,6 +96,10 @@ declare namespace Api {
|
|||
| 'educationLevel'
|
||||
| 'schoolNature'
|
||||
| 'supervisorDept'
|
||||
| 'establishYear'
|
||||
| 'campusAreaMu'
|
||||
| 'maleRatio'
|
||||
| 'femaleRatio'
|
||||
| 'remark'
|
||||
>
|
||||
>;
|
||||
|
|
@ -88,34 +108,7 @@ declare namespace Api {
|
|||
type SchoolDetailInSchoolOperateParams = CommonType.RecordNullable<
|
||||
Pick<
|
||||
Api.Art.SchoolDetail,
|
||||
| 'detailId'
|
||||
| 'schoolId'
|
||||
| 'introduction'
|
||||
| 'schoolIcon'
|
||||
| 'address'
|
||||
| 'contact'
|
||||
| 'email'
|
||||
| 'website'
|
||||
| 'postcode'
|
||||
| 'establishYear'
|
||||
| 'campusAreaMu'
|
||||
| 'libraryCollection'
|
||||
| 'maleRatio'
|
||||
| 'femaleRatio'
|
||||
| 'is985'
|
||||
| 'is211'
|
||||
| 'isDoubleFirstClass'
|
||||
| 'isKeyUniversity'
|
||||
| 'tags'
|
||||
| 'studentCount'
|
||||
| 'teacherCount'
|
||||
| 'masterPoint'
|
||||
| 'doctorPoint'
|
||||
| 'keyMajorCount'
|
||||
| 'employmentRate'
|
||||
| 'satisfactionRate'
|
||||
| 'univId'
|
||||
| 'remark'
|
||||
'detailId' | 'schoolId' | 'introduction' | 'address' | 'contact' | 'email' | 'website' | 'remark'
|
||||
>
|
||||
>;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ declare module 'vue' {
|
|||
BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||
BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||
ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||
copy: typeof import('./../components/custom/dict-radio copy.vue')['default']
|
||||
CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||
DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||
DataTable: typeof import('./../components/common/data-table.vue')['default']
|
||||
|
|
@ -36,7 +37,6 @@ declare module 'vue' {
|
|||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconHugeiconsConfiguration01: typeof import('~icons/hugeicons/configuration01')['default']
|
||||
IconIcRoundOpenInFull: typeof import('~icons/ic/round-open-in-full')['default']
|
||||
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
|
|
@ -62,7 +62,6 @@ declare module 'vue' {
|
|||
IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
|
||||
IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
InlineExpandTextarea: typeof import('./../components/custom/inline-expand-textarea.vue')['default']
|
||||
JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
|
|
@ -169,6 +168,7 @@ declare global {
|
|||
const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
|
||||
const BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
|
||||
const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
|
||||
const copy: typeof import('./../components/custom/dict-radio copy.vue')['default']
|
||||
const CountTo: typeof import('./../components/custom/count-to.vue')['default']
|
||||
const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
|
||||
const DataTable: typeof import('./../components/common/data-table.vue')['default']
|
||||
|
|
@ -189,7 +189,6 @@ declare global {
|
|||
const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
const IconHugeiconsConfiguration01: typeof import('~icons/hugeicons/configuration01')['default']
|
||||
const IconIcRoundOpenInFull: typeof import('~icons/ic/round-open-in-full')['default']
|
||||
const IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
|
||||
const IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
|
||||
const IconLocalBanner: typeof import('~icons/local/banner')['default']
|
||||
|
|
@ -215,7 +214,6 @@ declare global {
|
|||
const IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
|
||||
const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
|
||||
const IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
const InlineExpandTextarea: typeof import('./../components/custom/inline-expand-textarea.vue')['default']
|
||||
const JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
|
||||
const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
const LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ declare module "@elegant-router/types" {
|
|||
"500": "/500";
|
||||
"about": "/about";
|
||||
"art": "/art";
|
||||
"art_history-score-control-line": "/art/history-score-control-line";
|
||||
"art_major": "/art/major";
|
||||
"art_school": "/art/school";
|
||||
"art_school_modules": "/art/school/modules";
|
||||
"art_school_modules_school-campus": "/art/school/modules/school-campus";
|
||||
|
|
@ -40,8 +38,6 @@ declare module "@elegant-router/types" {
|
|||
"art_school_modules_school-media": "/art/school/modules/school-media";
|
||||
"art_school_modules_school-name": "/art/school/modules/school-name";
|
||||
"art_school_modules_school-tag": "/art/school/modules/school-tag";
|
||||
"art_school-recruit-major": "/art/school-recruit-major";
|
||||
"art_school-recruit-major-history": "/art/school-recruit-major-history";
|
||||
"demo": "/demo";
|
||||
"demo_demo": "/demo/demo";
|
||||
"demo_tree": "/demo/tree";
|
||||
|
|
@ -145,10 +141,6 @@ declare module "@elegant-router/types" {
|
|||
| "social-callback"
|
||||
| "user-center"
|
||||
| "about"
|
||||
| "art_history-score-control-line"
|
||||
| "art_major"
|
||||
| "art_school-recruit-major-history"
|
||||
| "art_school-recruit-major"
|
||||
| "art_school"
|
||||
| "art_school_modules_school-campus"
|
||||
| "art_school_modules_school-college"
|
||||
|
|
|
|||
|
|
@ -1,478 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { computed, ref } from 'vue';
|
||||
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import {
|
||||
fetchBatchDeleteHistoryScoreControlLine,
|
||||
fetchCreateHistoryScoreControlLine,
|
||||
fetchGetHistoryScoreControlLineList,
|
||||
fetchUpdateHistoryScoreControlLine
|
||||
} from '@/service/api/art/history-score-control-line';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
import { useDownload } from '@/hooks/business/download';
|
||||
import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||
import InlineExpandTextarea from '@/components/custom/inline-expand-textarea.vue';
|
||||
import HistoryScoreControlLineSearch from './modules/history-score-control-line-search.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'HistoryScoreControlLineList'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { download } = useDownload();
|
||||
const { hasAuth } = useAuth();
|
||||
|
||||
const searchParams = ref<Api.Art.HistoryScoreControlLineSearchParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
provinceCode: null,
|
||||
provinceName: null,
|
||||
year: null,
|
||||
majorCategory: null,
|
||||
batchName: null,
|
||||
subjectType: null,
|
||||
cultureScore: null,
|
||||
majorScore: null,
|
||||
cultureScoreExam: null,
|
||||
majorScoreExam: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
type TableRow = Api.Art.HistoryScoreControlLine & { tempKey?: string };
|
||||
type Model = Api.Art.HistoryScoreControlLineOperateParams;
|
||||
type EditableField = Extract<keyof Model, keyof TableRow>;
|
||||
|
||||
const editingModel = ref<Model>(createDefaultModel());
|
||||
const editingSnapshot = ref<TableRow | null>(null);
|
||||
const editingMode = ref<NaiveUI.TableOperateType | null>(null);
|
||||
const editingRowKey = ref<string | null>(null);
|
||||
const savingRowKey = ref<string | null>(null);
|
||||
const tempRow = ref<TableRow | null>(null);
|
||||
const checkedRowKeys = ref<CommonType.IdType[]>([]);
|
||||
|
||||
const editableColumns: Array<{ key: EditableField; title: string; textarea?: boolean }> = [
|
||||
{ key: 'provinceCode', title: '省份行政区划代码' },
|
||||
{ key: 'provinceName', title: '省份名称' },
|
||||
{ key: 'year', title: '年份' },
|
||||
{ key: 'majorCategory', title: '专业类别' },
|
||||
{ key: 'batchName', title: '批次' },
|
||||
{ key: 'subjectType', title: '科类(文/理)' },
|
||||
{ key: 'cultureScore', title: '文化成绩分数' },
|
||||
{ key: 'majorScore', title: '专业成绩分数' },
|
||||
{ key: 'cultureScoreExam', title: '文化成绩校考分数' },
|
||||
{ key: 'majorScoreExam', title: '专业成绩校考分数' },
|
||||
{ key: 'remark', title: '备注', textarea: true }
|
||||
];
|
||||
|
||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
||||
useNaivePaginatedTable({
|
||||
api: () => fetchGetHistoryScoreControlLineList(searchParams.value),
|
||||
transform: response => defaultTransform(response),
|
||||
onPaginationParamsChange: params => {
|
||||
searchParams.value.pageNum = params.page;
|
||||
searchParams.value.pageSize = params.pageSize;
|
||||
},
|
||||
columns: () => createColumns()
|
||||
});
|
||||
|
||||
const tableData = computed<TableRow[]>(() => {
|
||||
const rows = data.value as TableRow[];
|
||||
return tempRow.value ? [tempRow.value, ...rows] : rows;
|
||||
});
|
||||
|
||||
function createColumns(): NaiveUI.TableColumn<TableRow>[] {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center',
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
title: $t('common.index'),
|
||||
align: 'center',
|
||||
width: 64,
|
||||
render: (_, index) => index + 1
|
||||
},
|
||||
{
|
||||
key: 'controlId',
|
||||
title: '省控线主键',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
...editableColumns.map(column => createEditableColumn(column)),
|
||||
createOperateColumn()
|
||||
];
|
||||
}
|
||||
|
||||
function createEditableColumn(column: { key: EditableField; title: string; textarea?: boolean }) {
|
||||
return {
|
||||
key: column.key,
|
||||
title: column.title,
|
||||
align: 'center',
|
||||
minWidth: 140,
|
||||
render: (row: TableRow) => renderEditableCell(row, column.key, column)
|
||||
} satisfies NaiveUI.TableColumn<TableRow>;
|
||||
}
|
||||
|
||||
function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
|
||||
return {
|
||||
key: 'operate',
|
||||
title: $t('common.operate'),
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
width: 180,
|
||||
render: (row: TableRow) => {
|
||||
const rowKey = resolveRowKey(row);
|
||||
const editing = editingRowKey.value === rowKey;
|
||||
const saving = savingRowKey.value === rowKey;
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<NSpace size={8} justify="center">
|
||||
<NButton size="tiny" quaternary disabled={saving} onClick={handleCancelEdit}>
|
||||
{$t('common.cancel')}
|
||||
</NButton>
|
||||
<NButton size="tiny" type="primary" loading={saving} onClick={handleSaveRow}>
|
||||
{$t('common.save')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
);
|
||||
}
|
||||
|
||||
const showEdit = hasAuth('art:historyScoreControlLine:edit');
|
||||
const showDelete = hasAuth('art:historyScoreControlLine:remove');
|
||||
|
||||
return (
|
||||
<div class="flex-center gap-8px">
|
||||
{showEdit ? (
|
||||
<NButton size="tiny" text type="primary" onClick={() => handleEditRow(row)}>
|
||||
{$t('common.edit')}
|
||||
</NButton>
|
||||
) : null}
|
||||
{showEdit && showDelete ? <NDivider vertical /> : null}
|
||||
{showDelete ? (
|
||||
<ButtonIcon
|
||||
text
|
||||
type="error"
|
||||
icon="material-symbols:delete-outline"
|
||||
tooltipContent={$t('common.delete')}
|
||||
popconfirmContent={$t('common.confirmDelete')}
|
||||
onPositiveClick={() => handleDelete(row.controlId)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getRowFieldValue(row: TableRow, field: EditableField) {
|
||||
return row[field as keyof TableRow];
|
||||
}
|
||||
|
||||
function formatDisplayValue(value: unknown) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return typeof value === 'number' ? value : String(value);
|
||||
}
|
||||
|
||||
function renderEditableCell(row: TableRow, field: EditableField, options?: { textarea?: boolean }) {
|
||||
if (!isEditingRow(row)) {
|
||||
return formatDisplayValue(getRowFieldValue(row, field));
|
||||
}
|
||||
|
||||
const inputValue = editingModel.value[field];
|
||||
const resolvedValue = inputValue === null || inputValue === undefined ? '' : String(inputValue);
|
||||
|
||||
if (options?.textarea) {
|
||||
return renderTextareaEditor(row, field, resolvedValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<NInput
|
||||
size="small"
|
||||
type={options?.textarea ? 'textarea' : 'text'}
|
||||
autosize={options?.textarea ? { minRows: 1, maxRows: 4 } : undefined}
|
||||
value={resolvedValue}
|
||||
onUpdateValue={value => updateEditingField(field, value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextareaEditor(row: TableRow, field: EditableField, value: string) {
|
||||
return <InlineExpandTextarea value={value} onUpdateValue={val => updateEditingField(field, val)} />;
|
||||
}
|
||||
|
||||
function updateEditingField(field: EditableField, value: string | number | null) {
|
||||
(editingModel.value as Record<string, string | number | null>)[field] = value;
|
||||
}
|
||||
|
||||
function resolveRowKey(row: TableRow) {
|
||||
if (row.controlId !== null && row.controlId !== undefined && row.controlId !== '') {
|
||||
return String(row.controlId);
|
||||
}
|
||||
return row.tempKey ?? '';
|
||||
}
|
||||
|
||||
function isEditingRow(row: TableRow) {
|
||||
return editingRowKey.value !== null && editingRowKey.value === resolveRowKey(row);
|
||||
}
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
controlId: null,
|
||||
provinceCode: '',
|
||||
provinceName: '',
|
||||
year: null,
|
||||
majorCategory: '',
|
||||
batchName: '',
|
||||
subjectType: '',
|
||||
cultureScore: null,
|
||||
majorScore: null,
|
||||
cultureScoreExam: null,
|
||||
majorScoreExam: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
function createTempKey() {
|
||||
return `temp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
function ensureEditingGuard(action: () => void | Promise<void>) {
|
||||
const executeAction = () => Promise.resolve(action());
|
||||
|
||||
if (!editingRowKey.value) {
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
if (!window.$dialog) {
|
||||
resetEditingState();
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
window.$dialog?.warning({
|
||||
title: '提示',
|
||||
content: '当前行尚未保存,确定放弃修改吗?',
|
||||
positiveText: '放弃',
|
||||
negativeText: '继续编辑',
|
||||
onPositiveClick: async () => {
|
||||
resetEditingState();
|
||||
try {
|
||||
await executeAction();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
onNegativeClick: () => resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resetEditingState() {
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
editingMode.value = null;
|
||||
editingRowKey.value = null;
|
||||
savingRowKey.value = null;
|
||||
tempRow.value = null;
|
||||
}
|
||||
|
||||
async function handleAddRow() {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'add';
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
const key = createTempKey();
|
||||
tempRow.value = { ...(editingModel.value as TableRow), tempKey: key };
|
||||
editingRowKey.value = key;
|
||||
checkedRowKeys.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
async function handleEditRow(row: TableRow) {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'edit';
|
||||
tempRow.value = null;
|
||||
editingRowKey.value = resolveRowKey(row);
|
||||
editingSnapshot.value = jsonClone(row);
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(row));
|
||||
});
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetEditingState();
|
||||
}
|
||||
|
||||
const requiredFields: Array<{ key: EditableField; label: string }> = [
|
||||
{ key: 'provinceCode', label: '省份行政区划代码' },
|
||||
{ key: 'provinceName', label: '省份名称' },
|
||||
{ key: 'year', label: '年份' },
|
||||
{ key: 'majorCategory', label: '专业类别' },
|
||||
{ key: 'batchName', label: '批次' },
|
||||
{ key: 'subjectType', label: '科类(文/理)' }
|
||||
];
|
||||
|
||||
function validateModel() {
|
||||
for (const item of requiredFields) {
|
||||
const value = editingModel.value[item.key];
|
||||
if (value === null || value === undefined || value === '') {
|
||||
window.$message?.warning(`${item.label}不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleSaveRow() {
|
||||
if (!editingMode.value) return;
|
||||
if (!validateModel()) return;
|
||||
|
||||
const {
|
||||
controlId,
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
} = editingModel.value;
|
||||
|
||||
savingRowKey.value = editingRowKey.value;
|
||||
|
||||
const requestResult =
|
||||
editingMode.value === 'add'
|
||||
? await fetchCreateHistoryScoreControlLine({
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
})
|
||||
: await fetchUpdateHistoryScoreControlLine({
|
||||
controlId,
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
});
|
||||
|
||||
savingRowKey.value = null;
|
||||
|
||||
if (requestResult.error) {
|
||||
window.$message?.error(requestResult.error.message || '保存失败');
|
||||
if (editingMode.value === 'edit' && editingSnapshot.value) {
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(editingSnapshot.value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
resetEditingState();
|
||||
await getData();
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteHistoryScoreControlLine(checkedRowKeys.value);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
checkedRowKeys.value = [];
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(controlId: CommonType.IdType) {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteHistoryScoreControlLine([controlId]);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
download('/art/historyScoreControlLine/export', searchParams.value, `历年省控线_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
|
||||
async function handleSearch() {
|
||||
await ensureEditingGuard(async () => {
|
||||
await getDataByPage();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRefresh() {
|
||||
await ensureEditingGuard(getData);
|
||||
}
|
||||
|
||||
function rowClassName(row: TableRow) {
|
||||
return isEditingRow(row) ? 'inline-edit-row' : '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||
<HistoryScoreControlLineSearch v-model:model="searchParams" @search="handleSearch" />
|
||||
<NCard title="历年省控线列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
v-model:columns="columnChecks"
|
||||
:disabled-delete="checkedRowKeys.length === 0"
|
||||
:loading="loading"
|
||||
:show-add="hasAuth('art:historyScoreControlLine:add')"
|
||||
:show-delete="hasAuth('art:historyScoreControlLine:remove')"
|
||||
:show-export="hasAuth('art:historyScoreControlLine:export')"
|
||||
@add="handleAddRow"
|
||||
@delete="handleBatchDelete"
|
||||
@export="handleExport"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
size="small"
|
||||
:flex-height="!appStore.isMobile"
|
||||
:scroll-x="scrollX"
|
||||
:loading="loading"
|
||||
remote
|
||||
:row-key="resolveRowKey"
|
||||
:row-class-name="rowClassName"
|
||||
:pagination="mobilePagination"
|
||||
class="sm:h-full"
|
||||
/>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.inline-edit-row .n-data-table-td) {
|
||||
background-color: rgba(24, 160, 88, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import {
|
||||
fetchCreateHistoryScoreControlLine,
|
||||
fetchUpdateHistoryScoreControlLine
|
||||
} from '@/service/api/art/history-score-control-line';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'HistoryScoreControlLineOperateDrawer'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
/** the type of operation */
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.Art.HistoryScoreControlLine | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
add: '新增历年省控线',
|
||||
edit: '编辑历年省控线'
|
||||
};
|
||||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
type Model = Api.Art.HistoryScoreControlLineOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
controlId: null,
|
||||
provinceCode: '',
|
||||
provinceName: '',
|
||||
year: null,
|
||||
majorCategory: '',
|
||||
batchName: '',
|
||||
subjectType: '',
|
||||
cultureScore: null,
|
||||
majorScore: null,
|
||||
cultureScoreExam: null,
|
||||
majorScoreExam: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Extract<
|
||||
keyof Model,
|
||||
| 'controlId'
|
||||
| 'tenantId'
|
||||
| 'delFlag'
|
||||
| 'provinceCode'
|
||||
| 'provinceName'
|
||||
| 'year'
|
||||
| 'majorCategory'
|
||||
| 'batchName'
|
||||
| 'subjectType'
|
||||
>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
controlId: createRequiredRule('省控线主键不能为空'),
|
||||
tenantId: createRequiredRule('租户编号不能为空'),
|
||||
delFlag: createRequiredRule('删除标志(0存在 1删除)不能为空'),
|
||||
provinceCode: createRequiredRule('省份行政区划代码不能为空'),
|
||||
provinceName: createRequiredRule('省份名称不能为空'),
|
||||
year: createRequiredRule('年份不能为空'),
|
||||
majorCategory: createRequiredRule('专业类别不能为空'),
|
||||
batchName: createRequiredRule('批次不能为空'),
|
||||
subjectType: createRequiredRule('科类(文/理)不能为空')
|
||||
};
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
model.value = createDefaultModel();
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
Object.assign(model.value, jsonClone(props.rowData));
|
||||
}
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
const {
|
||||
controlId,
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
} = model.value;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { error } = await fetchCreateHistoryScoreControlLine({
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { error } = await fetchUpdateHistoryScoreControlLine({
|
||||
controlId,
|
||||
provinceCode,
|
||||
provinceName,
|
||||
year,
|
||||
majorCategory,
|
||||
batchName,
|
||||
subjectType,
|
||||
cultureScore,
|
||||
majorScore,
|
||||
cultureScoreExam,
|
||||
majorScoreExam,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
closeDrawer();
|
||||
emit('submitted');
|
||||
}
|
||||
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleUpdateModelWhenEdit();
|
||||
restoreValidation();
|
||||
getTreeList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="省份行政区划代码" path="provinceCode">
|
||||
<NInput v-model:value="model.provinceCode" placeholder="请输入省份行政区划代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="省份名称" path="provinceName">
|
||||
<NInput v-model:value="model.provinceName" placeholder="请输入省份名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="年份" path="year">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业类别" path="majorCategory">
|
||||
<NInput v-model:value="model.majorCategory" placeholder="请输入专业类别" />
|
||||
</NFormItem>
|
||||
<NFormItem label="批次" path="batchName">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItem>
|
||||
<NFormItem label="科类(文/理)" path="subjectType">
|
||||
<NSelect
|
||||
v-model:value="model.subjectType"
|
||||
placeholder="请选择科类(文/理)"
|
||||
:options="[{ value: '0', label: '请选择字典生成' }]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="文化成绩分数" path="cultureScore">
|
||||
<NInput v-model:value="model.cultureScore" placeholder="请输入文化成绩分数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业成绩分数" path="majorScore">
|
||||
<NInput v-model:value="model.majorScore" placeholder="请输入专业成绩分数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="文化成绩校考分数" path="cultureScoreExam">
|
||||
<NInput v-model:value="model.cultureScoreExam" placeholder="请输入文化成绩校考分数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业成绩校考分数" path="majorScoreExam">
|
||||
<NInput v-model:value="model.majorScoreExam" placeholder="请输入专业成绩校考分数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'HistoryScoreControlLineSearch'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
|
||||
const model = defineModel<Api.Art.HistoryScoreControlLineSearchParams>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await restoreValidation();
|
||||
resetModel();
|
||||
emit('search');
|
||||
}
|
||||
|
||||
async function search() {
|
||||
await validate();
|
||||
emit('search');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-history-score-control-line-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="省份行政区划代码"
|
||||
label-width="auto"
|
||||
path="provinceCode"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.provinceCode" placeholder="请输入省份行政区划代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="省份名称" label-width="auto" path="provinceName" class="pr-24px">
|
||||
<NInput v-model:value="model.provinceName" placeholder="请输入省份名称" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="年份" label-width="auto" path="year" class="pr-24px">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业类别" label-width="auto" path="majorCategory" class="pr-24px">
|
||||
<NInput v-model:value="model.majorCategory" placeholder="请输入专业类别" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="批次" label-width="auto" path="batchName" class="pr-24px">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="科类(文/理)" label-width="auto" path="subjectType" class="pr-24px">
|
||||
<NSelect v-model:value="model.subjectType" placeholder="请选择科类(文/理)" :options="[]" clearable />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="文化成绩分数" label-width="auto" path="cultureScore" class="pr-24px">
|
||||
<NInput v-model:value="model.cultureScore" placeholder="请输入文化成绩分数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业成绩分数" label-width="auto" path="majorScore" class="pr-24px">
|
||||
<NInput v-model:value="model.majorScore" placeholder="请输入专业成绩分数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="文化成绩校考分数"
|
||||
label-width="auto"
|
||||
path="cultureScoreExam"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.cultureScoreExam" placeholder="请输入文化成绩校考分数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="专业成绩校考分数"
|
||||
label-width="auto"
|
||||
path="majorScoreExam"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.majorScoreExam" placeholder="请输入专业成绩校考分数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,465 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { computed, ref } from 'vue';
|
||||
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { fetchBatchDeleteMajor, fetchCreateMajor, fetchGetMajorList, fetchUpdateMajor } from '@/service/api/art/major';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
import { useDownload } from '@/hooks/business/download';
|
||||
import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||
import InlineExpandTextarea from '@/components/custom/inline-expand-textarea.vue';
|
||||
import MajorSearch from './modules/major-search.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'MajorList'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { download } = useDownload();
|
||||
const { hasAuth } = useAuth();
|
||||
|
||||
const searchParams = ref<Api.Art.MajorSearchParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
majorName: null,
|
||||
educationLevel: null,
|
||||
majorIcon: null,
|
||||
schoolingYears: null,
|
||||
disciplinePrimary: null,
|
||||
disciplineSecondary: null,
|
||||
degreeAwarded: null,
|
||||
summary: null,
|
||||
trainingDirection: null,
|
||||
coreCourses: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
type TableRow = Api.Art.Major & { tempKey?: string };
|
||||
type Model = Api.Art.MajorOperateParams;
|
||||
type EditableField = Extract<keyof Model, keyof TableRow>;
|
||||
|
||||
const editingModel = ref<Model>(createDefaultModel());
|
||||
const editingSnapshot = ref<TableRow | null>(null);
|
||||
const editingMode = ref<NaiveUI.TableOperateType | null>(null);
|
||||
const editingRowKey = ref<string | null>(null);
|
||||
const savingRowKey = ref<string | null>(null);
|
||||
const tempRow = ref<TableRow | null>(null);
|
||||
const checkedRowKeys = ref<CommonType.IdType[]>([]);
|
||||
|
||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
||||
useNaivePaginatedTable({
|
||||
api: () => fetchGetMajorList(searchParams.value),
|
||||
transform: response => defaultTransform(response),
|
||||
onPaginationParamsChange: params => {
|
||||
searchParams.value.pageNum = params.page;
|
||||
searchParams.value.pageSize = params.pageSize;
|
||||
},
|
||||
columns: () => createColumns()
|
||||
});
|
||||
|
||||
const tableData = computed<TableRow[]>(() => {
|
||||
const rows = data.value as TableRow[];
|
||||
return tempRow.value ? [tempRow.value, ...rows] : rows;
|
||||
});
|
||||
|
||||
function createColumns(): NaiveUI.TableColumn<TableRow>[] {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center',
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
title: $t('common.index'),
|
||||
align: 'center',
|
||||
width: 64,
|
||||
render: (_, index) => index + 1
|
||||
},
|
||||
{
|
||||
key: 'majorId',
|
||||
title: '专业主键ID',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
createEditableColumn('majorName', '专业名称'),
|
||||
createEditableColumn('educationLevel', '学历层次'),
|
||||
createEditableColumn('majorIcon', '专业图标'),
|
||||
createEditableColumn('schoolingYears', '学制(年)'),
|
||||
createEditableColumn('disciplinePrimary', '所属一级学科'),
|
||||
createEditableColumn('disciplineSecondary', '所属二级学科'),
|
||||
createEditableColumn('degreeAwarded', '授予学士学位'),
|
||||
createEditableColumn('summary', '专业概括', { textarea: true }),
|
||||
createEditableColumn('trainingDirection', '培养方向', { textarea: true }),
|
||||
createEditableColumn('coreCourses', '主要课程', { textarea: true }),
|
||||
createEditableColumn('remark', '备注', { textarea: true }),
|
||||
createOperateColumn()
|
||||
];
|
||||
}
|
||||
|
||||
function createEditableColumn(key: EditableField, title: string, options?: { textarea?: boolean; minWidth?: number }) {
|
||||
return {
|
||||
key,
|
||||
title,
|
||||
align: 'center',
|
||||
minWidth: options?.minWidth ?? 120,
|
||||
render: (row: TableRow) => renderEditableCell(row, key, options)
|
||||
} satisfies NaiveUI.TableColumn<TableRow>;
|
||||
}
|
||||
|
||||
function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
|
||||
return {
|
||||
key: 'operate',
|
||||
title: $t('common.operate'),
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
render: (row: TableRow) => {
|
||||
const rowKey = resolveRowKey(row);
|
||||
const editing = editingRowKey.value === rowKey;
|
||||
const saving = savingRowKey.value === rowKey;
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<NSpace size={8} justify="center">
|
||||
<NButton size="tiny" quaternary disabled={saving} onClick={handleCancelEdit}>
|
||||
{$t('common.cancel')}
|
||||
</NButton>
|
||||
<NButton size="tiny" type="primary" loading={saving} onClick={handleSaveRow}>
|
||||
{$t('common.save')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
);
|
||||
}
|
||||
|
||||
const showEdit = hasAuth('art:major:edit');
|
||||
const showDelete = hasAuth('art:major:remove');
|
||||
|
||||
return (
|
||||
<div class="flex-center gap-8px">
|
||||
{showEdit ? (
|
||||
<NButton size="tiny" text type="primary" onClick={() => handleEditRow(row)}>
|
||||
{$t('common.edit')}
|
||||
</NButton>
|
||||
) : null}
|
||||
{showEdit && showDelete ? <NDivider vertical /> : null}
|
||||
{showDelete ? (
|
||||
<ButtonIcon
|
||||
text
|
||||
type="error"
|
||||
icon="material-symbols:delete-outline"
|
||||
tooltipContent={$t('common.delete')}
|
||||
popconfirmContent={$t('common.confirmDelete')}
|
||||
onPositiveClick={() => handleDelete(row.majorId)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getRowFieldValue(row: TableRow, field: EditableField) {
|
||||
return row[field as keyof TableRow];
|
||||
}
|
||||
|
||||
function formatDisplayValue(value: unknown) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return typeof value === 'number' ? value : String(value);
|
||||
}
|
||||
|
||||
function renderEditableCell(row: TableRow, field: EditableField, options?: { textarea?: boolean }) {
|
||||
if (!isEditingRow(row)) {
|
||||
return formatDisplayValue(getRowFieldValue(row, field));
|
||||
}
|
||||
|
||||
const inputValue = editingModel.value[field];
|
||||
const resolvedValue = inputValue === null || inputValue === undefined ? '' : String(inputValue);
|
||||
|
||||
if (options?.textarea) {
|
||||
return renderTextareaEditor(row, field, resolvedValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<NInput
|
||||
size="small"
|
||||
type={options?.textarea ? 'textarea' : 'text'}
|
||||
autosize={options?.textarea ? { minRows: 1, maxRows: 4 } : undefined}
|
||||
value={resolvedValue}
|
||||
onUpdateValue={value => updateEditingField(field, value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextareaEditor(row: TableRow, field: EditableField, value: string) {
|
||||
return <InlineExpandTextarea value={value} onUpdateValue={val => updateEditingField(field, val)} />;
|
||||
}
|
||||
|
||||
function updateEditingField(field: EditableField, value: string | number | null) {
|
||||
(editingModel.value as Record<string, string | number | null>)[field] = value;
|
||||
}
|
||||
|
||||
function resolveRowKey(row: TableRow) {
|
||||
if (row.majorId !== null && row.majorId !== undefined && row.majorId !== '') {
|
||||
return String(row.majorId);
|
||||
}
|
||||
return row.tempKey ?? '';
|
||||
}
|
||||
|
||||
function isEditingRow(row: TableRow) {
|
||||
return editingRowKey.value !== null && editingRowKey.value === resolveRowKey(row);
|
||||
}
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
majorId: null,
|
||||
majorName: '',
|
||||
educationLevel: '',
|
||||
majorIcon: '',
|
||||
schoolingYears: null,
|
||||
disciplinePrimary: '',
|
||||
disciplineSecondary: '',
|
||||
degreeAwarded: '',
|
||||
summary: '',
|
||||
trainingDirection: '',
|
||||
coreCourses: '',
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
function createTempKey() {
|
||||
return `temp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
function ensureEditingGuard(action: () => void | Promise<void>) {
|
||||
const executeAction = () => Promise.resolve(action());
|
||||
|
||||
if (!editingRowKey.value) {
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
if (!window.$dialog) {
|
||||
resetEditingState();
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
window.$dialog?.warning({
|
||||
title: '提示',
|
||||
content: '当前行尚未保存,确定放弃修改吗?',
|
||||
positiveText: '放弃',
|
||||
negativeText: '继续编辑',
|
||||
onPositiveClick: async () => {
|
||||
resetEditingState();
|
||||
try {
|
||||
await executeAction();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
onNegativeClick: () => resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resetEditingState() {
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
editingMode.value = null;
|
||||
editingRowKey.value = null;
|
||||
savingRowKey.value = null;
|
||||
tempRow.value = null;
|
||||
}
|
||||
|
||||
async function handleAddRow() {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'add';
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
const key = createTempKey();
|
||||
tempRow.value = { ...(editingModel.value as TableRow), tempKey: key };
|
||||
editingRowKey.value = key;
|
||||
checkedRowKeys.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
async function handleEditRow(row: TableRow) {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'edit';
|
||||
tempRow.value = null;
|
||||
editingRowKey.value = resolveRowKey(row);
|
||||
editingSnapshot.value = jsonClone(row);
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(row));
|
||||
});
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetEditingState();
|
||||
}
|
||||
|
||||
const requiredFields: Array<{ key: EditableField; label: string }> = [
|
||||
{ key: 'majorName', label: '专业名称' },
|
||||
{ key: 'educationLevel', label: '学历层次' }
|
||||
];
|
||||
|
||||
function validateModel() {
|
||||
for (const item of requiredFields) {
|
||||
const value = editingModel.value[item.key];
|
||||
if (value === null || value === undefined || value === '') {
|
||||
window.$message?.warning(`${item.label}不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleSaveRow() {
|
||||
if (!editingMode.value) return;
|
||||
if (!validateModel()) return;
|
||||
|
||||
const {
|
||||
majorId,
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
} = editingModel.value;
|
||||
|
||||
savingRowKey.value = editingRowKey.value;
|
||||
|
||||
const requestResult =
|
||||
editingMode.value === 'add'
|
||||
? await fetchCreateMajor({
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
})
|
||||
: await fetchUpdateMajor({
|
||||
majorId,
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
});
|
||||
|
||||
savingRowKey.value = null;
|
||||
|
||||
if (requestResult.error) {
|
||||
window.$message?.error(requestResult.error.message || '保存失败');
|
||||
if (editingMode.value === 'edit' && editingSnapshot.value) {
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(editingSnapshot.value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
resetEditingState();
|
||||
await getData();
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteMajor(checkedRowKeys.value);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
checkedRowKeys.value = [];
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(majorId: CommonType.IdType) {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteMajor([majorId]);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
download('/art/major/export', searchParams.value, `艺术专业库_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
|
||||
async function handleSearch() {
|
||||
await ensureEditingGuard(async () => {
|
||||
await getDataByPage();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRefresh() {
|
||||
await ensureEditingGuard(getData);
|
||||
}
|
||||
|
||||
function rowClassName(row: TableRow) {
|
||||
return isEditingRow(row) ? 'inline-edit-row' : '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||
<MajorSearch v-model:model="searchParams" @search="handleSearch" />
|
||||
<NCard title="艺术专业库列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
v-model:columns="columnChecks"
|
||||
:disabled-delete="checkedRowKeys.length === 0"
|
||||
:loading="loading"
|
||||
:show-add="hasAuth('art:major:add')"
|
||||
:show-delete="hasAuth('art:major:remove')"
|
||||
:show-export="hasAuth('art:major:export')"
|
||||
@add="handleAddRow"
|
||||
@delete="handleBatchDelete"
|
||||
@export="handleExport"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
size="small"
|
||||
:flex-height="!appStore.isMobile"
|
||||
:scroll-x="scrollX"
|
||||
:loading="loading"
|
||||
remote
|
||||
:row-key="resolveRowKey"
|
||||
:row-class-name="rowClassName"
|
||||
:pagination="mobilePagination"
|
||||
class="sm:h-full"
|
||||
/>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.inline-edit-row .n-data-table-td) {
|
||||
background-color: rgba(24, 160, 88, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { fetchCreateMajor, fetchUpdateMajor } from '@/service/api/art/major';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'MajorOperateDrawer'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
/** the type of operation */
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.Art.Major | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
add: '新增艺术专业库',
|
||||
edit: '编辑艺术专业库'
|
||||
};
|
||||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
type Model = Api.Art.MajorOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
majorId: null,
|
||||
majorName: '',
|
||||
educationLevel: '',
|
||||
majorIcon: '',
|
||||
schoolingYears: null,
|
||||
disciplinePrimary: '',
|
||||
disciplineSecondary: '',
|
||||
degreeAwarded: '',
|
||||
summary: '',
|
||||
trainingDirection: '',
|
||||
coreCourses: '',
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Extract<keyof Model, 'majorId' | 'tenantId' | 'delFlag' | 'majorName' | 'educationLevel'>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
majorId: createRequiredRule('专业主键ID不能为空'),
|
||||
tenantId: createRequiredRule('租户编号不能为空'),
|
||||
delFlag: createRequiredRule('删除标志(0存在 1删除)不能为空'),
|
||||
majorName: createRequiredRule('专业名称不能为空'),
|
||||
educationLevel: createRequiredRule('学历层次不能为空')
|
||||
};
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
model.value = createDefaultModel();
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
Object.assign(model.value, jsonClone(props.rowData));
|
||||
}
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
const {
|
||||
majorId,
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
} = model.value;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { error } = await fetchCreateMajor({
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { error } = await fetchUpdateMajor({
|
||||
majorId,
|
||||
majorName,
|
||||
educationLevel,
|
||||
majorIcon,
|
||||
schoolingYears,
|
||||
disciplinePrimary,
|
||||
disciplineSecondary,
|
||||
degreeAwarded,
|
||||
summary,
|
||||
trainingDirection,
|
||||
coreCourses,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
closeDrawer();
|
||||
emit('submitted');
|
||||
}
|
||||
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleUpdateModelWhenEdit();
|
||||
restoreValidation();
|
||||
getTreeList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="专业名称" path="majorName">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学历层次" path="educationLevel">
|
||||
<NInput v-model:value="model.educationLevel" placeholder="请输入学历层次" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业图标" path="majorIcon">
|
||||
<NInput v-model:value="model.majorIcon" placeholder="请输入专业图标" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学制(年)" path="schoolingYears">
|
||||
<NInput v-model:value="model.schoolingYears" placeholder="请输入学制(年)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="所属一级学科" path="disciplinePrimary">
|
||||
<NInput v-model:value="model.disciplinePrimary" placeholder="请输入所属一级学科" />
|
||||
</NFormItem>
|
||||
<NFormItem label="所属二级学科" path="disciplineSecondary">
|
||||
<NInput v-model:value="model.disciplineSecondary" placeholder="请输入所属二级学科" />
|
||||
</NFormItem>
|
||||
<NFormItem label="授予学士学位" path="degreeAwarded">
|
||||
<NInput v-model:value="model.degreeAwarded" placeholder="请输入授予学士学位" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业概括" path="summary">
|
||||
<NInput v-model:value="model.summary" :rows="3" type="textarea" placeholder="请输入专业概括" />
|
||||
</NFormItem>
|
||||
<NFormItem label="培养方向" path="trainingDirection">
|
||||
<NInput v-model:value="model.trainingDirection" :rows="3" type="textarea" placeholder="请输入培养方向" />
|
||||
</NFormItem>
|
||||
<NFormItem label="主要课程" path="coreCourses">
|
||||
<NInput v-model:value="model.coreCourses" :rows="3" type="textarea" placeholder="请输入主要课程" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'MajorSearch'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
|
||||
const model = defineModel<Api.Art.MajorSearchParams>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await restoreValidation();
|
||||
resetModel();
|
||||
emit('search');
|
||||
}
|
||||
|
||||
async function search() {
|
||||
await validate();
|
||||
emit('search');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-major-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业名称" label-width="auto" path="majorName" class="pr-24px">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学历层次" label-width="auto" path="educationLevel" class="pr-24px">
|
||||
<NInput v-model:value="model.educationLevel" placeholder="请输入学历层次" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业图标" label-width="auto" path="majorIcon" class="pr-24px">
|
||||
<NInput v-model:value="model.majorIcon" placeholder="请输入专业图标" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学制(年)" label-width="auto" path="schoolingYears" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolingYears" placeholder="请输入学制(年)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="所属一级学科"
|
||||
label-width="auto"
|
||||
path="disciplinePrimary"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.disciplinePrimary" placeholder="请输入所属一级学科" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="所属二级学科"
|
||||
label-width="auto"
|
||||
path="disciplineSecondary"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.disciplineSecondary" placeholder="请输入所属二级学科" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="授予学士学位"
|
||||
label-width="auto"
|
||||
path="degreeAwarded"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.degreeAwarded" placeholder="请输入授予学士学位" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业概括" label-width="auto" path="summary" class="pr-24px">
|
||||
<NInput v-model:value="model.summary" placeholder="请输入专业概括" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="培养方向"
|
||||
label-width="auto"
|
||||
path="trainingDirection"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.trainingDirection" placeholder="请输入培养方向" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="主要课程" label-width="auto" path="coreCourses" class="pr-24px">
|
||||
<NInput v-model:value="model.coreCourses" placeholder="请输入主要课程" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,573 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { computed, ref } from 'vue';
|
||||
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import {
|
||||
fetchBatchDeleteSchoolRecruitMajorHistory,
|
||||
fetchCreateSchoolRecruitMajorHistory,
|
||||
fetchGetSchoolRecruitMajorHistoryList,
|
||||
fetchUpdateSchoolRecruitMajorHistory
|
||||
} from '@/service/api/art/school-recruit-major-history';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
import { useDownload } from '@/hooks/business/download';
|
||||
import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||
import InlineExpandTextarea from '@/components/custom/inline-expand-textarea.vue';
|
||||
import SchoolRecruitMajorHistorySearch from './modules/school-recruit-major-history-search.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorHistoryList'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { download } = useDownload();
|
||||
const { hasAuth } = useAuth();
|
||||
|
||||
const searchParams = ref<Api.Art.SchoolRecruitMajorHistorySearchParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
recruitMajorId: null,
|
||||
schoolId: null,
|
||||
schoolCode: null,
|
||||
collegeCode: null,
|
||||
schoolName: null,
|
||||
majorId: null,
|
||||
majorCode: null,
|
||||
majorName: null,
|
||||
enrollCode: null,
|
||||
majorType: null,
|
||||
majorTypeSub: null,
|
||||
mainExamSubject: null,
|
||||
year: null,
|
||||
subjectType: null,
|
||||
batchName: null,
|
||||
admissionFormula: null,
|
||||
probabilityOperator: null,
|
||||
controlScore: null,
|
||||
admissionScore: null,
|
||||
planEnroll: null,
|
||||
filedAmount: null,
|
||||
admitAmount: null,
|
||||
firstChoiceAdmitAmount: null,
|
||||
minScoreDiff: null,
|
||||
tuitionFee: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
type TableRow = Api.Art.SchoolRecruitMajorHistory & { tempKey?: string };
|
||||
type Model = Api.Art.SchoolRecruitMajorHistoryOperateParams;
|
||||
type EditableField = Extract<keyof Model, keyof TableRow>;
|
||||
|
||||
const editingModel = ref<Model>(createDefaultModel());
|
||||
const editingSnapshot = ref<TableRow | null>(null);
|
||||
const editingMode = ref<NaiveUI.TableOperateType | null>(null);
|
||||
const editingRowKey = ref<string | null>(null);
|
||||
const savingRowKey = ref<string | null>(null);
|
||||
const tempRow = ref<TableRow | null>(null);
|
||||
const checkedRowKeys = ref<CommonType.IdType[]>([]);
|
||||
|
||||
const editableColumns: Array<{ key: EditableField; title: string; textarea?: boolean; minWidth?: number }> = [
|
||||
{ key: 'recruitMajorId', title: '对应招录专业ID' },
|
||||
{ key: 'schoolId', title: '学校ID' },
|
||||
{ key: 'schoolCode', title: '学校代码' },
|
||||
{ key: 'collegeCode', title: '院校代码' },
|
||||
{ key: 'schoolName', title: '学校名称', minWidth: 160 },
|
||||
{ key: 'majorId', title: '专业ID' },
|
||||
{ key: 'majorCode', title: '专业代码' },
|
||||
{ key: 'majorName', title: '专业名称', minWidth: 160 },
|
||||
{ key: 'enrollCode', title: '招生代码' },
|
||||
{ key: 'majorType', title: '专业类型' },
|
||||
{ key: 'majorTypeSub', title: '专业类别子级' },
|
||||
{ key: 'mainExamSubject', title: '主考科目' },
|
||||
{ key: 'year', title: '年份' },
|
||||
{ key: 'subjectType', title: '科类(文/理)' },
|
||||
{ key: 'batchName', title: '批次' },
|
||||
{ key: 'admissionFormula', title: '录取方式', minWidth: 160 },
|
||||
{ key: 'probabilityOperator', title: '录取概率规则运算符', minWidth: 160 },
|
||||
{ key: 'controlScore', title: '省控线' },
|
||||
{ key: 'admissionScore', title: '录取线' },
|
||||
{ key: 'planEnroll', title: '招生人数' },
|
||||
{ key: 'filedAmount', title: '实际投档人数' },
|
||||
{ key: 'admitAmount', title: '录取数' },
|
||||
{ key: 'firstChoiceAdmitAmount', title: '一志愿录取数', minWidth: 160 },
|
||||
{ key: 'minScoreDiff', title: '最低分数差' },
|
||||
{ key: 'tuitionFee', title: '学费(元/年)' },
|
||||
{ key: 'remark', title: '备注', textarea: true, minWidth: 160 }
|
||||
];
|
||||
|
||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
||||
useNaivePaginatedTable({
|
||||
api: () => fetchGetSchoolRecruitMajorHistoryList(searchParams.value),
|
||||
transform: response => defaultTransform(response),
|
||||
onPaginationParamsChange: params => {
|
||||
searchParams.value.pageNum = params.page;
|
||||
searchParams.value.pageSize = params.pageSize;
|
||||
},
|
||||
columns: () => createColumns()
|
||||
});
|
||||
|
||||
const tableData = computed<TableRow[]>(() => {
|
||||
const rows = data.value as TableRow[];
|
||||
return tempRow.value ? [tempRow.value, ...rows] : rows;
|
||||
});
|
||||
|
||||
function createColumns(): NaiveUI.TableColumn<TableRow>[] {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center',
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
title: $t('common.index'),
|
||||
align: 'center',
|
||||
width: 64,
|
||||
render: (_, index) => index + 1
|
||||
},
|
||||
{
|
||||
key: 'historyId',
|
||||
title: '历年录取数据ID',
|
||||
align: 'center',
|
||||
minWidth: 160
|
||||
},
|
||||
...editableColumns.map(column => createEditableColumn(column)),
|
||||
createOperateColumn()
|
||||
];
|
||||
}
|
||||
|
||||
function createEditableColumn(column: { key: EditableField; title: string; textarea?: boolean; minWidth?: number }) {
|
||||
return {
|
||||
key: column.key,
|
||||
title: column.title,
|
||||
align: 'center',
|
||||
minWidth: column.minWidth ?? 140,
|
||||
render: (row: TableRow) => renderEditableCell(row, column.key, column)
|
||||
} satisfies NaiveUI.TableColumn<TableRow>;
|
||||
}
|
||||
|
||||
function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
|
||||
return {
|
||||
key: 'operate',
|
||||
title: $t('common.operate'),
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
render: (row: TableRow) => {
|
||||
const rowKey = resolveRowKey(row);
|
||||
const editing = editingRowKey.value === rowKey;
|
||||
const saving = savingRowKey.value === rowKey;
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<NSpace size={8} justify="center">
|
||||
<NButton size="tiny" quaternary disabled={saving} onClick={handleCancelEdit}>
|
||||
{$t('common.cancel')}
|
||||
</NButton>
|
||||
<NButton size="tiny" type="primary" loading={saving} onClick={handleSaveRow}>
|
||||
{$t('common.save')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
);
|
||||
}
|
||||
|
||||
const showEdit = hasAuth('art:schoolRecruitMajorHistory:edit');
|
||||
const showDelete = hasAuth('art:schoolRecruitMajorHistory:remove');
|
||||
|
||||
return (
|
||||
<div class="flex-center gap-8px">
|
||||
{showEdit ? (
|
||||
<NButton size="tiny" text type="primary" onClick={() => handleEditRow(row)}>
|
||||
{$t('common.edit')}
|
||||
</NButton>
|
||||
) : null}
|
||||
{showEdit && showDelete ? <NDivider vertical /> : null}
|
||||
{showDelete ? (
|
||||
<ButtonIcon
|
||||
text
|
||||
type="error"
|
||||
icon="material-symbols:delete-outline"
|
||||
tooltipContent={$t('common.delete')}
|
||||
popconfirmContent={$t('common.confirmDelete')}
|
||||
onPositiveClick={() => handleDelete(row.historyId)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getRowFieldValue(row: TableRow, field: EditableField) {
|
||||
return row[field as keyof TableRow];
|
||||
}
|
||||
|
||||
function formatDisplayValue(value: unknown) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return typeof value === 'number' ? value : String(value);
|
||||
}
|
||||
|
||||
function renderEditableCell(row: TableRow, field: EditableField, options?: { textarea?: boolean }) {
|
||||
if (!isEditingRow(row)) {
|
||||
return formatDisplayValue(getRowFieldValue(row, field));
|
||||
}
|
||||
|
||||
const inputValue = editingModel.value[field];
|
||||
const resolvedValue = inputValue === null || inputValue === undefined ? '' : String(inputValue);
|
||||
|
||||
if (options?.textarea) {
|
||||
return renderTextareaEditor(row, field, resolvedValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<NInput
|
||||
size="small"
|
||||
type={options?.textarea ? 'textarea' : 'text'}
|
||||
autosize={options?.textarea ? { minRows: 1, maxRows: 4 } : undefined}
|
||||
value={resolvedValue}
|
||||
onUpdateValue={value => updateEditingField(field, value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextareaEditor(row: TableRow, field: EditableField, value: string) {
|
||||
return <InlineExpandTextarea value={value} onUpdateValue={val => updateEditingField(field, val)} />;
|
||||
}
|
||||
|
||||
function updateEditingField(field: EditableField, value: string | number | null) {
|
||||
(editingModel.value as Record<string, string | number | null>)[field] = value;
|
||||
}
|
||||
|
||||
function resolveRowKey(row: TableRow) {
|
||||
if (row.historyId !== null && row.historyId !== undefined && row.historyId !== '') {
|
||||
return String(row.historyId);
|
||||
}
|
||||
return row.tempKey ?? '';
|
||||
}
|
||||
|
||||
function isEditingRow(row: TableRow) {
|
||||
return editingRowKey.value !== null && editingRowKey.value === resolveRowKey(row);
|
||||
}
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
historyId: null,
|
||||
recruitMajorId: null,
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
collegeCode: '',
|
||||
schoolName: '',
|
||||
majorId: null,
|
||||
majorCode: '',
|
||||
majorName: '',
|
||||
enrollCode: '',
|
||||
majorType: '',
|
||||
majorTypeSub: '',
|
||||
mainExamSubject: '',
|
||||
year: null,
|
||||
subjectType: '',
|
||||
batchName: '',
|
||||
admissionFormula: '',
|
||||
probabilityOperator: '',
|
||||
controlScore: null,
|
||||
admissionScore: null,
|
||||
planEnroll: null,
|
||||
filedAmount: null,
|
||||
admitAmount: null,
|
||||
firstChoiceAdmitAmount: null,
|
||||
minScoreDiff: null,
|
||||
tuitionFee: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
function createTempKey() {
|
||||
return `temp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
function ensureEditingGuard(action: () => void | Promise<void>) {
|
||||
const executeAction = () => Promise.resolve(action());
|
||||
|
||||
if (!editingRowKey.value) {
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
if (!window.$dialog) {
|
||||
resetEditingState();
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
window.$dialog?.warning({
|
||||
title: '提示',
|
||||
content: '当前行尚未保存,确定放弃修改吗?',
|
||||
positiveText: '放弃',
|
||||
negativeText: '继续编辑',
|
||||
onPositiveClick: async () => {
|
||||
resetEditingState();
|
||||
try {
|
||||
await executeAction();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
onNegativeClick: () => resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resetEditingState() {
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
editingMode.value = null;
|
||||
editingRowKey.value = null;
|
||||
savingRowKey.value = null;
|
||||
tempRow.value = null;
|
||||
}
|
||||
|
||||
async function handleAddRow() {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'add';
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
const key = createTempKey();
|
||||
tempRow.value = { ...(editingModel.value as TableRow), tempKey: key };
|
||||
editingRowKey.value = key;
|
||||
checkedRowKeys.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
async function handleEditRow(row: TableRow) {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'edit';
|
||||
tempRow.value = null;
|
||||
editingRowKey.value = resolveRowKey(row);
|
||||
editingSnapshot.value = jsonClone(row);
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(row));
|
||||
});
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetEditingState();
|
||||
}
|
||||
|
||||
const requiredFields: Array<{ key: EditableField; label: string }> = [
|
||||
{ key: 'recruitMajorId', label: '对应招录专业ID' },
|
||||
{ key: 'schoolId', label: '学校ID' },
|
||||
{ key: 'schoolCode', label: '学校代码' },
|
||||
{ key: 'schoolName', label: '学校名称' },
|
||||
{ key: 'majorCode', label: '专业代码' },
|
||||
{ key: 'majorName', label: '专业名称' },
|
||||
{ key: 'year', label: '年份' }
|
||||
];
|
||||
|
||||
function validateModel() {
|
||||
for (const item of requiredFields) {
|
||||
const value = editingModel.value[item.key];
|
||||
if (value === null || value === undefined || value === '') {
|
||||
window.$message?.warning(`${item.label}不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleSaveRow() {
|
||||
if (!editingMode.value) return;
|
||||
if (!validateModel()) return;
|
||||
|
||||
const {
|
||||
historyId,
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
} = editingModel.value;
|
||||
|
||||
savingRowKey.value = editingRowKey.value;
|
||||
|
||||
const requestResult =
|
||||
editingMode.value === 'add'
|
||||
? await fetchCreateSchoolRecruitMajorHistory({
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
})
|
||||
: await fetchUpdateSchoolRecruitMajorHistory({
|
||||
historyId,
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
});
|
||||
|
||||
savingRowKey.value = null;
|
||||
|
||||
if (requestResult.error) {
|
||||
window.$message?.error(requestResult.error.message || '保存失败');
|
||||
if (editingMode.value === 'edit' && editingSnapshot.value) {
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(editingSnapshot.value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
resetEditingState();
|
||||
await getData();
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteSchoolRecruitMajorHistory(checkedRowKeys.value);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
checkedRowKeys.value = [];
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(historyId: CommonType.IdType) {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteSchoolRecruitMajorHistory([historyId]);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
download(
|
||||
'/art/schoolRecruitMajorHistory/export',
|
||||
searchParams.value,
|
||||
`院校招录专业历年录取数据_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
|
||||
async function handleSearch() {
|
||||
await ensureEditingGuard(async () => {
|
||||
await getDataByPage();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRefresh() {
|
||||
await ensureEditingGuard(getData);
|
||||
}
|
||||
|
||||
function rowClassName(row: TableRow) {
|
||||
return isEditingRow(row) ? 'inline-edit-row' : '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||
<SchoolRecruitMajorHistorySearch v-model:model="searchParams" @search="handleSearch" />
|
||||
<NCard title="院校招录专业历年录取数据列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
v-model:columns="columnChecks"
|
||||
:disabled-delete="checkedRowKeys.length === 0"
|
||||
:loading="loading"
|
||||
:show-add="hasAuth('art:schoolRecruitMajorHistory:add')"
|
||||
:show-delete="hasAuth('art:schoolRecruitMajorHistory:remove')"
|
||||
:show-export="hasAuth('art:schoolRecruitMajorHistory:export')"
|
||||
@add="handleAddRow"
|
||||
@delete="handleBatchDelete"
|
||||
@export="handleExport"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
size="small"
|
||||
:flex-height="!appStore.isMobile"
|
||||
:scroll-x="scrollX"
|
||||
:loading="loading"
|
||||
remote
|
||||
:row-key="resolveRowKey"
|
||||
:row-class-name="rowClassName"
|
||||
:pagination="mobilePagination"
|
||||
class="sm:h-full"
|
||||
/>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.inline-edit-row .n-data-table-td) {
|
||||
background-color: rgba(24, 160, 88, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,336 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import {
|
||||
fetchCreateSchoolRecruitMajorHistory,
|
||||
fetchUpdateSchoolRecruitMajorHistory
|
||||
} from '@/service/api/art/school-recruit-major-history';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorHistoryOperateDrawer'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
/** the type of operation */
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.Art.SchoolRecruitMajorHistory | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
add: '新增院校招录专业历年录取数据',
|
||||
edit: '编辑院校招录专业历年录取数据'
|
||||
};
|
||||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
type Model = Api.Art.SchoolRecruitMajorHistoryOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
historyId: null,
|
||||
recruitMajorId: null,
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
collegeCode: '',
|
||||
schoolName: '',
|
||||
majorId: null,
|
||||
majorCode: '',
|
||||
majorName: '',
|
||||
enrollCode: '',
|
||||
majorType: '',
|
||||
majorTypeSub: '',
|
||||
mainExamSubject: '',
|
||||
year: null,
|
||||
subjectType: '',
|
||||
batchName: '',
|
||||
admissionFormula: '',
|
||||
probabilityOperator: '',
|
||||
controlScore: null,
|
||||
admissionScore: null,
|
||||
planEnroll: null,
|
||||
filedAmount: null,
|
||||
admitAmount: null,
|
||||
firstChoiceAdmitAmount: null,
|
||||
minScoreDiff: null,
|
||||
tuitionFee: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Extract<
|
||||
keyof Model,
|
||||
| 'historyId'
|
||||
| 'tenantId'
|
||||
| 'delFlag'
|
||||
| 'recruitMajorId'
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'schoolName'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'year'
|
||||
>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
historyId: createRequiredRule('历年录取数据ID不能为空'),
|
||||
tenantId: createRequiredRule('租户编号不能为空'),
|
||||
delFlag: createRequiredRule('删除标志(0存在 1删除)不能为空'),
|
||||
recruitMajorId: createRequiredRule('对应招录专业ID不能为空'),
|
||||
schoolId: createRequiredRule('学校ID不能为空'),
|
||||
schoolCode: createRequiredRule('学校代码不能为空'),
|
||||
schoolName: createRequiredRule('学校名称不能为空'),
|
||||
majorCode: createRequiredRule('专业代码不能为空'),
|
||||
majorName: createRequiredRule('专业名称不能为空'),
|
||||
year: createRequiredRule('年份不能为空')
|
||||
};
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
model.value = createDefaultModel();
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
Object.assign(model.value, jsonClone(props.rowData));
|
||||
}
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
const {
|
||||
historyId,
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
} = model.value;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { error } = await fetchCreateSchoolRecruitMajorHistory({
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { error } = await fetchUpdateSchoolRecruitMajorHistory({
|
||||
historyId,
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
collegeCode,
|
||||
schoolName,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
mainExamSubject,
|
||||
year,
|
||||
subjectType,
|
||||
batchName,
|
||||
admissionFormula,
|
||||
probabilityOperator,
|
||||
controlScore,
|
||||
admissionScore,
|
||||
planEnroll,
|
||||
filedAmount,
|
||||
admitAmount,
|
||||
firstChoiceAdmitAmount,
|
||||
minScoreDiff,
|
||||
tuitionFee,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
closeDrawer();
|
||||
emit('submitted');
|
||||
}
|
||||
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleUpdateModelWhenEdit();
|
||||
restoreValidation();
|
||||
getTreeList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="对应招录专业ID" path="recruitMajorId">
|
||||
<NInput v-model:value="model.recruitMajorId" placeholder="请输入对应招录专业ID" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学校ID" path="schoolId">
|
||||
<NInput v-model:value="model.schoolId" placeholder="请输入学校ID" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学校代码" path="schoolCode">
|
||||
<NInput v-model:value="model.schoolCode" placeholder="请输入学校代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="院校代码" path="collegeCode">
|
||||
<NInput v-model:value="model.collegeCode" placeholder="请输入院校代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学校名称" path="schoolName">
|
||||
<NInput v-model:value="model.schoolName" placeholder="请输入学校名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业ID" path="majorId">
|
||||
<NInput v-model:value="model.majorId" placeholder="请输入专业ID" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业代码" path="majorCode">
|
||||
<NInput v-model:value="model.majorCode" placeholder="请输入专业代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业名称" path="majorName">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="招生代码" path="enrollCode">
|
||||
<NInput v-model:value="model.enrollCode" placeholder="请输入招生代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业类型" path="majorType">
|
||||
<NSelect
|
||||
v-model:value="model.majorType"
|
||||
placeholder="请选择专业类型"
|
||||
:options="[{ value: '0', label: '请选择字典生成' }]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="专业类别子级" path="majorTypeSub">
|
||||
<NInput v-model:value="model.majorTypeSub" placeholder="请输入专业类别子级" />
|
||||
</NFormItem>
|
||||
<NFormItem label="主考科目" path="mainExamSubject">
|
||||
<NInput v-model:value="model.mainExamSubject" placeholder="请输入主考科目" />
|
||||
</NFormItem>
|
||||
<NFormItem label="年份" path="year">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItem>
|
||||
<NFormItem label="科类(文/理)" path="subjectType">
|
||||
<NSelect
|
||||
v-model:value="model.subjectType"
|
||||
placeholder="请选择科类(文/理)"
|
||||
:options="[{ value: '0', label: '请选择字典生成' }]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="批次" path="batchName">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItem>
|
||||
<NFormItem label="录取方式(文*x+专*y)" path="admissionFormula">
|
||||
<NInput v-model:value="model.admissionFormula" placeholder="请输入录取方式(文*x+专*y)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="录取概率规则运算符" path="probabilityOperator">
|
||||
<NInput v-model:value="model.probabilityOperator" placeholder="请输入录取概率规则运算符" />
|
||||
</NFormItem>
|
||||
<NFormItem label="省控线" path="controlScore">
|
||||
<NInput v-model:value="model.controlScore" placeholder="请输入省控线" />
|
||||
</NFormItem>
|
||||
<NFormItem label="录取线" path="admissionScore">
|
||||
<NInput v-model:value="model.admissionScore" placeholder="请输入录取线" />
|
||||
</NFormItem>
|
||||
<NFormItem label="招生人数" path="planEnroll">
|
||||
<NInput v-model:value="model.planEnroll" placeholder="请输入招生人数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="实际投档人数" path="filedAmount">
|
||||
<NInput v-model:value="model.filedAmount" placeholder="请输入实际投档人数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="录取数" path="admitAmount">
|
||||
<NInput v-model:value="model.admitAmount" placeholder="请输入录取数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="一志愿录取数" path="firstChoiceAdmitAmount">
|
||||
<NInput v-model:value="model.firstChoiceAdmitAmount" placeholder="请输入一志愿录取数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="最低分数差" path="minScoreDiff">
|
||||
<NInput v-model:value="model.minScoreDiff" placeholder="请输入最低分数差" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学费(元/年)" path="tuitionFee">
|
||||
<NInput v-model:value="model.tuitionFee" placeholder="请输入学费(元/年)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorHistorySearch'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
|
||||
const model = defineModel<Api.Art.SchoolRecruitMajorHistorySearchParams>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await restoreValidation();
|
||||
resetModel();
|
||||
emit('search');
|
||||
}
|
||||
|
||||
async function search() {
|
||||
await validate();
|
||||
emit('search');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-recruit-major-history-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="对应招录专业ID"
|
||||
label-width="auto"
|
||||
path="recruitMajorId"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.recruitMajorId" placeholder="请输入对应招录专业ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校ID" label-width="auto" path="schoolId" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolId" placeholder="请输入学校ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校代码" label-width="auto" path="schoolCode" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolCode" placeholder="请输入学校代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="院校代码" label-width="auto" path="collegeCode" class="pr-24px">
|
||||
<NInput v-model:value="model.collegeCode" placeholder="请输入院校代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校名称" label-width="auto" path="schoolName" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolName" placeholder="请输入学校名称" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业ID" label-width="auto" path="majorId" class="pr-24px">
|
||||
<NInput v-model:value="model.majorId" placeholder="请输入专业ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业代码" label-width="auto" path="majorCode" class="pr-24px">
|
||||
<NInput v-model:value="model.majorCode" placeholder="请输入专业代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业名称" label-width="auto" path="majorName" class="pr-24px">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="招生代码" label-width="auto" path="enrollCode" class="pr-24px">
|
||||
<NInput v-model:value="model.enrollCode" placeholder="请输入招生代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业类型" label-width="auto" path="majorType" class="pr-24px">
|
||||
<NSelect v-model:value="model.majorType" placeholder="请选择专业类型" :options="[]" clearable />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业类别子级" label-width="auto" path="majorTypeSub" class="pr-24px">
|
||||
<NInput v-model:value="model.majorTypeSub" placeholder="请输入专业类别子级" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="主考科目" label-width="auto" path="mainExamSubject" class="pr-24px">
|
||||
<NInput v-model:value="model.mainExamSubject" placeholder="请输入主考科目" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="年份" label-width="auto" path="year" class="pr-24px">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="科类(文/理)" label-width="auto" path="subjectType" class="pr-24px">
|
||||
<NSelect v-model:value="model.subjectType" placeholder="请选择科类(文/理)" :options="[]" clearable />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="批次" label-width="auto" path="batchName" class="pr-24px">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="录取方式(文*x+专*y)"
|
||||
label-width="auto"
|
||||
path="admissionFormula"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionFormula" placeholder="请输入录取方式(文*x+专*y)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="录取概率规则运算符"
|
||||
label-width="auto"
|
||||
path="probabilityOperator"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.probabilityOperator" placeholder="请输入录取概率规则运算符" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="省控线" label-width="auto" path="controlScore" class="pr-24px">
|
||||
<NInput v-model:value="model.controlScore" placeholder="请输入省控线" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="录取线" label-width="auto" path="admissionScore" class="pr-24px">
|
||||
<NInput v-model:value="model.admissionScore" placeholder="请输入录取线" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="招生人数" label-width="auto" path="planEnroll" class="pr-24px">
|
||||
<NInput v-model:value="model.planEnroll" placeholder="请输入招生人数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="实际投档人数" label-width="auto" path="filedAmount" class="pr-24px">
|
||||
<NInput v-model:value="model.filedAmount" placeholder="请输入实际投档人数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="录取数" label-width="auto" path="admitAmount" class="pr-24px">
|
||||
<NInput v-model:value="model.admitAmount" placeholder="请输入录取数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="一志愿录取数"
|
||||
label-width="auto"
|
||||
path="firstChoiceAdmitAmount"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.firstChoiceAdmitAmount" placeholder="请输入一志愿录取数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="最低分数差" label-width="auto" path="minScoreDiff" class="pr-24px">
|
||||
<NInput v-model:value="model.minScoreDiff" placeholder="请输入最低分数差" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学费(元/年)" label-width="auto" path="tuitionFee" class="pr-24px">
|
||||
<NInput v-model:value="model.tuitionFee" placeholder="请输入学费(元/年)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,580 +0,0 @@
|
|||
<script setup lang="tsx">
|
||||
import { computed, ref } from 'vue';
|
||||
import { NButton, NDivider, NInput, NSpace } from 'naive-ui';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import {
|
||||
fetchBatchDeleteSchoolRecruitMajor,
|
||||
fetchCreateSchoolRecruitMajor,
|
||||
fetchGetSchoolRecruitMajorList,
|
||||
fetchUpdateSchoolRecruitMajor
|
||||
} from '@/service/api/art/school-recruit-major';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
import { useDownload } from '@/hooks/business/download';
|
||||
import { defaultTransform, useNaivePaginatedTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||
import InlineExpandTextarea from '@/components/custom/inline-expand-textarea.vue';
|
||||
import SchoolRecruitMajorSearch from './modules/school-recruit-major-search.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorList'
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { download } = useDownload();
|
||||
const { hasAuth } = useAuth();
|
||||
|
||||
const searchParams = ref<Api.Art.SchoolRecruitMajorSearchParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
schoolId: null,
|
||||
schoolCode: null,
|
||||
schoolName: null,
|
||||
year: null,
|
||||
majorId: null,
|
||||
majorCode: null,
|
||||
majorName: null,
|
||||
enrollCode: null,
|
||||
dataStatus: null,
|
||||
batchName: null,
|
||||
majorType: null,
|
||||
majorTypeSub: null,
|
||||
subjectType: null,
|
||||
admissionWayShort: null,
|
||||
admissionWayExternal: null,
|
||||
admissionWayExternalOp: null,
|
||||
admissionWayInternal: null,
|
||||
admissionWayInternalOp: null,
|
||||
planEnroll: null,
|
||||
mainExamSubject: null,
|
||||
schoolingYears: null,
|
||||
enrollLimitDesc: null,
|
||||
tuitionFee: null,
|
||||
cultureScoreLimit: null,
|
||||
majorScoreLimit: null,
|
||||
chineseScoreLimit: null,
|
||||
englishScoreLimit: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
type TableRow = Api.Art.SchoolRecruitMajor & { tempKey?: string };
|
||||
type Model = Api.Art.SchoolRecruitMajorOperateParams;
|
||||
type EditableField = Extract<keyof Model, keyof TableRow>;
|
||||
|
||||
const editingModel = ref<Model>(createDefaultModel());
|
||||
const editingSnapshot = ref<TableRow | null>(null);
|
||||
const editingMode = ref<NaiveUI.TableOperateType | null>(null);
|
||||
const editingRowKey = ref<string | null>(null);
|
||||
const savingRowKey = ref<string | null>(null);
|
||||
const tempRow = ref<TableRow | null>(null);
|
||||
const checkedRowKeys = ref<CommonType.IdType[]>([]);
|
||||
|
||||
const editableColumns: Array<{ key: EditableField; title: string; textarea?: boolean; minWidth?: number }> = [
|
||||
{ key: 'schoolCode', title: '学校代码' },
|
||||
{ key: 'schoolName', title: '学校名称(冗余)', minWidth: 160 },
|
||||
{ key: 'year', title: '年份' },
|
||||
{ key: 'majorId', title: '专业ID' },
|
||||
{ key: 'majorCode', title: '专业代码' },
|
||||
{ key: 'majorName', title: '专业名称', minWidth: 160 },
|
||||
{ key: 'enrollCode', title: '招生代码' },
|
||||
{ key: 'dataStatus', title: '数据状态' },
|
||||
{ key: 'batchName', title: '批次' },
|
||||
{ key: 'majorType', title: '专业类型' },
|
||||
{ key: 'majorTypeSub', title: '二级专业类型' },
|
||||
{ key: 'subjectType', title: '科类(文/理)' },
|
||||
{ key: 'admissionWayShort', title: '录取方式缩写' },
|
||||
{ key: 'admissionWayExternal', title: '对外录取方式' },
|
||||
{ key: 'admissionWayExternalOp', title: '对外录取方式运算符' },
|
||||
{ key: 'admissionWayInternal', title: '内部录取方式' },
|
||||
{ key: 'admissionWayInternalOp', title: '内部录取方式运算符' },
|
||||
{ key: 'planEnroll', title: '计划招生人数' },
|
||||
{ key: 'mainExamSubject', title: '主考科目' },
|
||||
{ key: 'schoolingYears', title: '学制(年)' },
|
||||
{ key: 'enrollLimitDesc', title: '院校限制说明', textarea: true, minWidth: 180 },
|
||||
{ key: 'tuitionFee', title: '学费(元/年)' },
|
||||
{ key: 'cultureScoreLimit', title: '文化分数限制' },
|
||||
{ key: 'majorScoreLimit', title: '专业分数限制' },
|
||||
{ key: 'chineseScoreLimit', title: '语文成绩限制' },
|
||||
{ key: 'englishScoreLimit', title: '英语成绩限制' },
|
||||
{ key: 'remark', title: '备注', textarea: true, minWidth: 160 }
|
||||
];
|
||||
|
||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
||||
useNaivePaginatedTable({
|
||||
api: () => fetchGetSchoolRecruitMajorList(searchParams.value),
|
||||
transform: response => defaultTransform(response),
|
||||
onPaginationParamsChange: params => {
|
||||
searchParams.value.pageNum = params.page;
|
||||
searchParams.value.pageSize = params.pageSize;
|
||||
},
|
||||
columns: () => createColumns()
|
||||
});
|
||||
|
||||
const tableData = computed<TableRow[]>(() => {
|
||||
const rows = data.value as TableRow[];
|
||||
return tempRow.value ? [tempRow.value, ...rows] : rows;
|
||||
});
|
||||
|
||||
function createColumns(): NaiveUI.TableColumn<TableRow>[] {
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center',
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
key: 'index',
|
||||
title: $t('common.index'),
|
||||
align: 'center',
|
||||
width: 64,
|
||||
render: (_, index) => index + 1
|
||||
},
|
||||
{
|
||||
key: 'recruitMajorId',
|
||||
title: '院校招录专业ID',
|
||||
align: 'center',
|
||||
minWidth: 150
|
||||
},
|
||||
...editableColumns.map(column => createEditableColumn(column)),
|
||||
createOperateColumn()
|
||||
];
|
||||
}
|
||||
|
||||
function createEditableColumn(column: { key: EditableField; title: string; textarea?: boolean; minWidth?: number }) {
|
||||
return {
|
||||
key: column.key,
|
||||
title: column.title,
|
||||
align: 'center',
|
||||
minWidth: column.minWidth ?? 140,
|
||||
render: (row: TableRow) => renderEditableCell(row, column.key, column)
|
||||
} satisfies NaiveUI.TableColumn<TableRow>;
|
||||
}
|
||||
|
||||
function createOperateColumn(): NaiveUI.TableColumn<TableRow> {
|
||||
return {
|
||||
key: 'operate',
|
||||
title: $t('common.operate'),
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
width: 200,
|
||||
render: (row: TableRow) => {
|
||||
const rowKey = resolveRowKey(row);
|
||||
const editing = editingRowKey.value === rowKey;
|
||||
const saving = savingRowKey.value === rowKey;
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<NSpace size={8} justify="center">
|
||||
<NButton size="tiny" quaternary disabled={saving} onClick={handleCancelEdit}>
|
||||
{$t('common.cancel')}
|
||||
</NButton>
|
||||
<NButton size="tiny" type="primary" loading={saving} onClick={handleSaveRow}>
|
||||
{$t('common.save')}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
);
|
||||
}
|
||||
|
||||
const showEdit = hasAuth('art:schoolRecruitMajor:edit');
|
||||
const showDelete = hasAuth('art:schoolRecruitMajor:remove');
|
||||
|
||||
return (
|
||||
<div class="flex-center gap-8px">
|
||||
{showEdit ? (
|
||||
<NButton size="tiny" text type="primary" onClick={() => handleEditRow(row)}>
|
||||
{$t('common.edit')}
|
||||
</NButton>
|
||||
) : null}
|
||||
{showEdit && showDelete ? <NDivider vertical /> : null}
|
||||
{showDelete ? (
|
||||
<ButtonIcon
|
||||
text
|
||||
type="error"
|
||||
icon="material-symbols:delete-outline"
|
||||
tooltipContent={$t('common.delete')}
|
||||
popconfirmContent={$t('common.confirmDelete')}
|
||||
onPositiveClick={() => handleDelete(row.recruitMajorId)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getRowFieldValue(row: TableRow, field: EditableField) {
|
||||
return row[field as keyof TableRow];
|
||||
}
|
||||
|
||||
function formatDisplayValue(value: unknown) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
return typeof value === 'number' ? value : String(value);
|
||||
}
|
||||
|
||||
function renderEditableCell(row: TableRow, field: EditableField, options?: { textarea?: boolean }) {
|
||||
if (!isEditingRow(row)) {
|
||||
return formatDisplayValue(getRowFieldValue(row, field));
|
||||
}
|
||||
|
||||
const inputValue = editingModel.value[field];
|
||||
const resolvedValue = inputValue === null || inputValue === undefined ? '' : String(inputValue);
|
||||
|
||||
if (options?.textarea) {
|
||||
return renderTextareaEditor(row, field, resolvedValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<NInput
|
||||
size="small"
|
||||
type={options?.textarea ? 'textarea' : 'text'}
|
||||
autosize={options?.textarea ? { minRows: 1, maxRows: 4 } : undefined}
|
||||
value={resolvedValue}
|
||||
onUpdateValue={value => updateEditingField(field, value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextareaEditor(row: TableRow, field: EditableField, value: string) {
|
||||
return <InlineExpandTextarea value={value} onUpdateValue={val => updateEditingField(field, val)} />;
|
||||
}
|
||||
|
||||
function updateEditingField(field: EditableField, value: string | number | null) {
|
||||
(editingModel.value as Record<string, string | number | null>)[field] = value;
|
||||
}
|
||||
|
||||
function resolveRowKey(row: TableRow) {
|
||||
if (row.recruitMajorId !== null && row.recruitMajorId !== undefined && row.recruitMajorId !== '') {
|
||||
return String(row.recruitMajorId);
|
||||
}
|
||||
return row.tempKey ?? '';
|
||||
}
|
||||
|
||||
function isEditingRow(row: TableRow) {
|
||||
return editingRowKey.value !== null && editingRowKey.value === resolveRowKey(row);
|
||||
}
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
recruitMajorId: null,
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
schoolName: '',
|
||||
year: null,
|
||||
majorId: null,
|
||||
majorCode: '',
|
||||
majorName: '',
|
||||
enrollCode: '',
|
||||
dataStatus: '',
|
||||
batchName: '',
|
||||
majorType: '',
|
||||
majorTypeSub: '',
|
||||
subjectType: '',
|
||||
admissionWayShort: '',
|
||||
admissionWayExternal: '',
|
||||
admissionWayExternalOp: '',
|
||||
admissionWayInternal: '',
|
||||
admissionWayInternalOp: '',
|
||||
planEnroll: null,
|
||||
mainExamSubject: '',
|
||||
schoolingYears: null,
|
||||
enrollLimitDesc: '',
|
||||
tuitionFee: null,
|
||||
cultureScoreLimit: null,
|
||||
majorScoreLimit: null,
|
||||
chineseScoreLimit: null,
|
||||
englishScoreLimit: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
function createTempKey() {
|
||||
return `temp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
}
|
||||
|
||||
function ensureEditingGuard(action: () => void | Promise<void>) {
|
||||
const executeAction = () => Promise.resolve(action());
|
||||
|
||||
if (!editingRowKey.value) {
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
if (!window.$dialog) {
|
||||
resetEditingState();
|
||||
return executeAction();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
window.$dialog?.warning({
|
||||
title: '提示',
|
||||
content: '当前行尚未保存,确定放弃修改吗?',
|
||||
positiveText: '放弃',
|
||||
negativeText: '继续编辑',
|
||||
onPositiveClick: async () => {
|
||||
resetEditingState();
|
||||
try {
|
||||
await executeAction();
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
onNegativeClick: () => resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function resetEditingState() {
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
editingMode.value = null;
|
||||
editingRowKey.value = null;
|
||||
savingRowKey.value = null;
|
||||
tempRow.value = null;
|
||||
}
|
||||
|
||||
async function handleAddRow() {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'add';
|
||||
editingModel.value = createDefaultModel();
|
||||
editingSnapshot.value = null;
|
||||
const key = createTempKey();
|
||||
tempRow.value = { ...(editingModel.value as TableRow), tempKey: key };
|
||||
editingRowKey.value = key;
|
||||
checkedRowKeys.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
async function handleEditRow(row: TableRow) {
|
||||
await ensureEditingGuard(() => {
|
||||
editingMode.value = 'edit';
|
||||
tempRow.value = null;
|
||||
editingRowKey.value = resolveRowKey(row);
|
||||
editingSnapshot.value = jsonClone(row);
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(row));
|
||||
});
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetEditingState();
|
||||
}
|
||||
|
||||
const requiredFields: Array<{ key: EditableField; label: string }> = [
|
||||
{ key: 'schoolCode', label: '学校代码' },
|
||||
{ key: 'schoolName', label: '学校名称' },
|
||||
{ key: 'year', label: '年份' },
|
||||
{ key: 'majorCode', label: '专业代码' },
|
||||
{ key: 'majorName', label: '专业名称' },
|
||||
{ key: 'enrollCode', label: '招生代码' },
|
||||
{ key: 'dataStatus', label: '数据状态' }
|
||||
];
|
||||
|
||||
function validateModel() {
|
||||
for (const item of requiredFields) {
|
||||
const value = editingModel.value[item.key];
|
||||
if (value === null || value === undefined || value === '') {
|
||||
window.$message?.warning(`${item.label}不能为空`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleSaveRow() {
|
||||
if (!editingMode.value) return;
|
||||
if (!validateModel()) return;
|
||||
|
||||
const {
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
} = editingModel.value;
|
||||
|
||||
savingRowKey.value = editingRowKey.value;
|
||||
|
||||
const requestResult =
|
||||
editingMode.value === 'add'
|
||||
? await fetchCreateSchoolRecruitMajor({
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
})
|
||||
: await fetchUpdateSchoolRecruitMajor({
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
});
|
||||
|
||||
savingRowKey.value = null;
|
||||
|
||||
if (requestResult.error) {
|
||||
window.$message?.error(requestResult.error.message || '保存失败');
|
||||
if (editingMode.value === 'edit' && editingSnapshot.value) {
|
||||
editingModel.value = Object.assign(createDefaultModel(), jsonClone(editingSnapshot.value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
resetEditingState();
|
||||
await getData();
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteSchoolRecruitMajor(checkedRowKeys.value);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
checkedRowKeys.value = [];
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleDelete(recruitMajorId: CommonType.IdType) {
|
||||
await ensureEditingGuard(async () => {
|
||||
const { error } = await fetchBatchDeleteSchoolRecruitMajor([recruitMajorId]);
|
||||
if (error) return;
|
||||
window.$message?.success($t('common.deleteSuccess'));
|
||||
await getData();
|
||||
});
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
download('/art/schoolRecruitMajor/export', searchParams.value, `院校招录专业_${new Date().getTime()}.xlsx`);
|
||||
}
|
||||
|
||||
async function handleSearch() {
|
||||
await ensureEditingGuard(async () => {
|
||||
await getDataByPage();
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRefresh() {
|
||||
await ensureEditingGuard(getData);
|
||||
}
|
||||
|
||||
function rowClassName(row: TableRow) {
|
||||
return isEditingRow(row) ? 'inline-edit-row' : '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||
<SchoolRecruitMajorSearch v-model:model="searchParams" @search="handleSearch" />
|
||||
<NCard title="院校招录专业列表" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
v-model:columns="columnChecks"
|
||||
:disabled-delete="checkedRowKeys.length === 0"
|
||||
:loading="loading"
|
||||
:show-add="hasAuth('art:schoolRecruitMajor:add')"
|
||||
:show-delete="hasAuth('art:schoolRecruitMajor:remove')"
|
||||
:show-export="hasAuth('art:schoolRecruitMajor:export')"
|
||||
@add="handleAddRow"
|
||||
@delete="handleBatchDelete"
|
||||
@export="handleExport"
|
||||
@refresh="handleRefresh"
|
||||
/>
|
||||
</template>
|
||||
<NDataTable
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
size="small"
|
||||
:flex-height="!appStore.isMobile"
|
||||
:scroll-x="scrollX"
|
||||
:loading="loading"
|
||||
remote
|
||||
:row-key="resolveRowKey"
|
||||
:row-class-name="rowClassName"
|
||||
:pagination="mobilePagination"
|
||||
class="sm:h-full"
|
||||
/>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.inline-edit-row .n-data-table-td) {
|
||||
background-color: rgba(24, 160, 88, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,353 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { fetchCreateSchoolRecruitMajor, fetchUpdateSchoolRecruitMajor } from '@/service/api/art/school-recruit-major';
|
||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorOperateDrawer'
|
||||
});
|
||||
|
||||
interface Props {
|
||||
/** the type of operation */
|
||||
operateType: NaiveUI.TableOperateType;
|
||||
/** the edit row data */
|
||||
rowData?: Api.Art.SchoolRecruitMajor | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
interface Emits {
|
||||
(e: 'submitted'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false
|
||||
});
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
add: '新增院校招录专业',
|
||||
edit: '编辑院校招录专业'
|
||||
};
|
||||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
type Model = Api.Art.SchoolRecruitMajorOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
||||
function createDefaultModel(): Model {
|
||||
return {
|
||||
recruitMajorId: null,
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
schoolName: '',
|
||||
year: null,
|
||||
majorId: null,
|
||||
majorCode: '',
|
||||
majorName: '',
|
||||
enrollCode: '',
|
||||
dataStatus: '',
|
||||
batchName: '',
|
||||
majorType: '',
|
||||
majorTypeSub: '',
|
||||
subjectType: '',
|
||||
admissionWayShort: '',
|
||||
admissionWayExternal: '',
|
||||
admissionWayExternalOp: '',
|
||||
admissionWayInternal: '',
|
||||
admissionWayInternalOp: '',
|
||||
planEnroll: null,
|
||||
mainExamSubject: '',
|
||||
schoolingYears: null,
|
||||
enrollLimitDesc: '',
|
||||
tuitionFee: null,
|
||||
cultureScoreLimit: null,
|
||||
majorScoreLimit: null,
|
||||
chineseScoreLimit: null,
|
||||
englishScoreLimit: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = Extract<
|
||||
keyof Model,
|
||||
| 'recruitMajorId'
|
||||
| 'tenantId'
|
||||
| 'delFlag'
|
||||
| 'schoolId'
|
||||
| 'schoolCode'
|
||||
| 'schoolName'
|
||||
| 'year'
|
||||
| 'majorCode'
|
||||
| 'majorName'
|
||||
| 'enrollCode'
|
||||
| 'dataStatus'
|
||||
>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
recruitMajorId: createRequiredRule('院校招录专业ID不能为空'),
|
||||
tenantId: createRequiredRule('租户编号不能为空'),
|
||||
delFlag: createRequiredRule('删除标志(0存在 1删除)不能为空'),
|
||||
schoolId: createRequiredRule('学校ID不能为空'),
|
||||
schoolCode: createRequiredRule('学校代码不能为空'),
|
||||
schoolName: createRequiredRule('学校名称(冗余)不能为空'),
|
||||
year: createRequiredRule('年份不能为空'),
|
||||
majorCode: createRequiredRule('专业代码不能为空'),
|
||||
majorName: createRequiredRule('专业名称不能为空'),
|
||||
enrollCode: createRequiredRule('招生代码(为空则存空串)不能为空'),
|
||||
dataStatus: createRequiredRule('数据状态(停招/新招/新增)不能为空')
|
||||
};
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
model.value = createDefaultModel();
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
Object.assign(model.value, jsonClone(props.rowData));
|
||||
}
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
const {
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
} = model.value;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { error } = await fetchCreateSchoolRecruitMajor({
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { error } = await fetchUpdateSchoolRecruitMajor({
|
||||
recruitMajorId,
|
||||
schoolId,
|
||||
schoolCode,
|
||||
schoolName,
|
||||
year,
|
||||
majorId,
|
||||
majorCode,
|
||||
majorName,
|
||||
enrollCode,
|
||||
dataStatus,
|
||||
batchName,
|
||||
majorType,
|
||||
majorTypeSub,
|
||||
subjectType,
|
||||
admissionWayShort,
|
||||
admissionWayExternal,
|
||||
admissionWayExternalOp,
|
||||
admissionWayInternal,
|
||||
admissionWayInternalOp,
|
||||
planEnroll,
|
||||
mainExamSubject,
|
||||
schoolingYears,
|
||||
enrollLimitDesc,
|
||||
tuitionFee,
|
||||
cultureScoreLimit,
|
||||
majorScoreLimit,
|
||||
chineseScoreLimit,
|
||||
englishScoreLimit,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
closeDrawer();
|
||||
emit('submitted');
|
||||
}
|
||||
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleUpdateModelWhenEdit();
|
||||
restoreValidation();
|
||||
getTreeList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="学校ID" path="schoolId">
|
||||
<NInput v-model:value="model.schoolId" placeholder="请输入学校ID" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学校代码" path="schoolCode">
|
||||
<NInput v-model:value="model.schoolCode" placeholder="请输入学校代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学校名称(冗余)" path="schoolName">
|
||||
<NInput v-model:value="model.schoolName" placeholder="请输入学校名称(冗余)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="年份" path="year">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业ID" path="majorId">
|
||||
<NInput v-model:value="model.majorId" placeholder="请输入专业ID" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业代码" path="majorCode">
|
||||
<NInput v-model:value="model.majorCode" placeholder="请输入专业代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业名称" path="majorName">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="招生代码(为空则存空串)" path="enrollCode">
|
||||
<NInput v-model:value="model.enrollCode" placeholder="请输入招生代码(为空则存空串)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="数据状态(停招/新招/新增)" path="dataStatus">
|
||||
<NRadioGroup v-model:value="model.dataStatus">
|
||||
<NSpace>
|
||||
<NRadio value="0" label="请选择字典生成" />
|
||||
</NSpace>
|
||||
</NRadioGroup>
|
||||
</NFormItem>
|
||||
<NFormItem label="批次" path="batchName">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业类型" path="majorType">
|
||||
<NSelect
|
||||
v-model:value="model.majorType"
|
||||
placeholder="请选择专业类型"
|
||||
:options="[{ value: '0', label: '请选择字典生成' }]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="二级专业类型" path="majorTypeSub">
|
||||
<NInput v-model:value="model.majorTypeSub" placeholder="请输入二级专业类型" />
|
||||
</NFormItem>
|
||||
<NFormItem label="科类(文/理)" path="subjectType">
|
||||
<NSelect
|
||||
v-model:value="model.subjectType"
|
||||
placeholder="请选择科类(文/理)"
|
||||
:options="[{ value: '0', label: '请选择字典生成' }]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="录取方式缩写" path="admissionWayShort">
|
||||
<NInput v-model:value="model.admissionWayShort" placeholder="请输入录取方式缩写" />
|
||||
</NFormItem>
|
||||
<NFormItem label="对外录取方式" path="admissionWayExternal">
|
||||
<NInput v-model:value="model.admissionWayExternal" placeholder="请输入对外录取方式" />
|
||||
</NFormItem>
|
||||
<NFormItem label="对外录取方式运算符" path="admissionWayExternalOp">
|
||||
<NInput v-model:value="model.admissionWayExternalOp" placeholder="请输入对外录取方式运算符" />
|
||||
</NFormItem>
|
||||
<NFormItem label="内部录取方式" path="admissionWayInternal">
|
||||
<NInput v-model:value="model.admissionWayInternal" placeholder="请输入内部录取方式" />
|
||||
</NFormItem>
|
||||
<NFormItem label="内部录取方式运算符" path="admissionWayInternalOp">
|
||||
<NInput v-model:value="model.admissionWayInternalOp" placeholder="请输入内部录取方式运算符" />
|
||||
</NFormItem>
|
||||
<NFormItem label="计划招生人数" path="planEnroll">
|
||||
<NInput v-model:value="model.planEnroll" placeholder="请输入计划招生人数" />
|
||||
</NFormItem>
|
||||
<NFormItem label="主考科目" path="mainExamSubject">
|
||||
<NInput v-model:value="model.mainExamSubject" placeholder="请输入主考科目" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学制(年)" path="schoolingYears">
|
||||
<NInput v-model:value="model.schoolingYears" placeholder="请输入学制(年)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="院校限制说明" path="enrollLimitDesc">
|
||||
<NInput v-model:value="model.enrollLimitDesc" :rows="3" type="textarea" placeholder="请输入院校限制说明" />
|
||||
</NFormItem>
|
||||
<NFormItem label="学费(元/年)" path="tuitionFee">
|
||||
<NInput v-model:value="model.tuitionFee" placeholder="请输入学费(元/年)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="文化分数限制" path="cultureScoreLimit">
|
||||
<NInput v-model:value="model.cultureScoreLimit" placeholder="请输入文化分数限制" />
|
||||
</NFormItem>
|
||||
<NFormItem label="专业分数限制" path="majorScoreLimit">
|
||||
<NInput v-model:value="model.majorScoreLimit" placeholder="请输入专业分数限制" />
|
||||
</NFormItem>
|
||||
<NFormItem label="语文成绩限制" path="chineseScoreLimit">
|
||||
<NInput v-model:value="model.chineseScoreLimit" placeholder="请输入语文成绩限制" />
|
||||
</NFormItem>
|
||||
<NFormItem label="英语成绩限制" path="englishScoreLimit">
|
||||
<NInput v-model:value="model.englishScoreLimit" placeholder="请输入英语成绩限制" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NDrawerContent>
|
||||
</NDrawer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolRecruitMajorSearch'
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
|
||||
const model = defineModel<Api.Art.SchoolRecruitMajorSearchParams>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
await restoreValidation();
|
||||
resetModel();
|
||||
emit('search');
|
||||
}
|
||||
|
||||
async function search() {
|
||||
await validate();
|
||||
emit('search');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-recruit-major-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校ID" label-width="auto" path="schoolId" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolId" placeholder="请输入学校ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校代码" label-width="auto" path="schoolCode" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolCode" placeholder="请输入学校代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校名称(冗余)" label-width="auto" path="schoolName" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolName" placeholder="请输入学校名称(冗余)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="年份" label-width="auto" path="year" class="pr-24px">
|
||||
<NInput v-model:value="model.year" placeholder="请输入年份" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业ID" label-width="auto" path="majorId" class="pr-24px">
|
||||
<NInput v-model:value="model.majorId" placeholder="请输入专业ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业代码" label-width="auto" path="majorCode" class="pr-24px">
|
||||
<NInput v-model:value="model.majorCode" placeholder="请输入专业代码" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业名称" label-width="auto" path="majorName" class="pr-24px">
|
||||
<NInput v-model:value="model.majorName" placeholder="请输入专业名称" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="招生代码(为空则存空串)"
|
||||
label-width="auto"
|
||||
path="enrollCode"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.enrollCode" placeholder="请输入招生代码(为空则存空串)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="数据状态(停招/新招/新增)"
|
||||
label-width="auto"
|
||||
path="dataStatus"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NSelect
|
||||
v-model:value="model.dataStatus"
|
||||
placeholder="请选择数据状态(停招/新招/新增)"
|
||||
:options="[]"
|
||||
clearable
|
||||
/>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="批次" label-width="auto" path="batchName" class="pr-24px">
|
||||
<NInput v-model:value="model.batchName" placeholder="请输入批次" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="专业类型" label-width="auto" path="majorType" class="pr-24px">
|
||||
<NSelect v-model:value="model.majorType" placeholder="请选择专业类型" :options="[]" clearable />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="二级专业类型" label-width="auto" path="majorTypeSub" class="pr-24px">
|
||||
<NInput v-model:value="model.majorTypeSub" placeholder="请输入二级专业类型" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="科类(文/理)" label-width="auto" path="subjectType" class="pr-24px">
|
||||
<NSelect v-model:value="model.subjectType" placeholder="请选择科类(文/理)" :options="[]" clearable />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="录取方式缩写"
|
||||
label-width="auto"
|
||||
path="admissionWayShort"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionWayShort" placeholder="请输入录取方式缩写" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="对外录取方式"
|
||||
label-width="auto"
|
||||
path="admissionWayExternal"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionWayExternal" placeholder="请输入对外录取方式" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="对外录取方式运算符"
|
||||
label-width="auto"
|
||||
path="admissionWayExternalOp"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionWayExternalOp" placeholder="请输入对外录取方式运算符" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="内部录取方式"
|
||||
label-width="auto"
|
||||
path="admissionWayInternal"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionWayInternal" placeholder="请输入内部录取方式" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="内部录取方式运算符"
|
||||
label-width="auto"
|
||||
path="admissionWayInternalOp"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.admissionWayInternalOp" placeholder="请输入内部录取方式运算符" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="计划招生人数" label-width="auto" path="planEnroll" class="pr-24px">
|
||||
<NInput v-model:value="model.planEnroll" placeholder="请输入计划招生人数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="主考科目" label-width="auto" path="mainExamSubject" class="pr-24px">
|
||||
<NInput v-model:value="model.mainExamSubject" placeholder="请输入主考科目" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学制(年)" label-width="auto" path="schoolingYears" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolingYears" placeholder="请输入学制(年)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="院校限制说明"
|
||||
label-width="auto"
|
||||
path="enrollLimitDesc"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.enrollLimitDesc" placeholder="请输入院校限制说明" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学费(元/年)" label-width="auto" path="tuitionFee" class="pr-24px">
|
||||
<NInput v-model:value="model.tuitionFee" placeholder="请输入学费(元/年)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="文化分数限制"
|
||||
label-width="auto"
|
||||
path="cultureScoreLimit"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.cultureScoreLimit" placeholder="请输入文化分数限制" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="专业分数限制"
|
||||
label-width="auto"
|
||||
path="majorScoreLimit"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.majorScoreLimit" placeholder="请输入专业分数限制" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="语文成绩限制"
|
||||
label-width="auto"
|
||||
path="chineseScoreLimit"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.chineseScoreLimit" placeholder="请输入语文成绩限制" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="英语成绩限制"
|
||||
label-width="auto"
|
||||
path="englishScoreLimit"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.englishScoreLimit" placeholder="请输入英语成绩限制" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCollapseItem>
|
||||
</NCollapse>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
@ -25,7 +25,8 @@ type SchoolSubModuleType =
|
|||
| 'schoolMajor'
|
||||
| 'schoolEnrollPlan'
|
||||
| 'schoolDorm'
|
||||
| 'schoolMedia';
|
||||
| 'schoolMedia'
|
||||
| 'schoolTag';
|
||||
|
||||
type SchoolSubModuleButton = {
|
||||
key: SchoolSubModuleType;
|
||||
|
|
@ -49,7 +50,8 @@ const subModuleButtons: SchoolSubModuleButton[] = [
|
|||
{ key: 'schoolMajor', label: '专业管理' },
|
||||
{ key: 'schoolEnrollPlan', label: '招生计划' },
|
||||
{ key: 'schoolDorm', label: '宿舍管理' },
|
||||
{ key: 'schoolMedia', label: '媒体管理' }
|
||||
{ key: 'schoolMedia', label: '媒体管理' },
|
||||
{ key: 'schoolTag', label: '院校标签' }
|
||||
];
|
||||
|
||||
const searchParams = ref<Api.Art.SchoolSearchParams>({
|
||||
|
|
@ -58,6 +60,7 @@ const searchParams = ref<Api.Art.SchoolSearchParams>({
|
|||
mainCode: null,
|
||||
mainName: null,
|
||||
shortName: null,
|
||||
schoolIcon: null,
|
||||
province: null,
|
||||
city: null,
|
||||
district: null,
|
||||
|
|
@ -65,6 +68,10 @@ const searchParams = ref<Api.Art.SchoolSearchParams>({
|
|||
educationLevel: null,
|
||||
schoolNature: null,
|
||||
supervisorDept: null,
|
||||
establishYear: null,
|
||||
campusAreaMu: null,
|
||||
maleRatio: null,
|
||||
femaleRatio: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
|
|
@ -97,19 +104,25 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
|||
},
|
||||
{
|
||||
key: 'mainCode',
|
||||
title: '学校编码(国标代码)',
|
||||
title: '学校编码(唯一标识,如国标代码)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'mainName',
|
||||
title: '学校主名称',
|
||||
title: '学校主名称(官方全称)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'shortName',
|
||||
title: '学校简称',
|
||||
title: '学校简称(备用)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'schoolIcon',
|
||||
title: '院校图标',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
|
|
@ -133,25 +146,49 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
|||
},
|
||||
{
|
||||
key: 'universityType',
|
||||
title: '大学类型',
|
||||
title: '大学类型:综合/工科/财经/艺术...',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'educationLevel',
|
||||
title: '学历层次',
|
||||
title: '学历层次:本科/专科',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'schoolNature',
|
||||
title: '办学性质',
|
||||
title: '办学性质:公办/民办/中外合作',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'supervisorDept',
|
||||
title: '主管部门',
|
||||
title: '主管部门:教育部/工信部/民委...',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'establishYear',
|
||||
title: '建校时间(年)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'campusAreaMu',
|
||||
title: '占地面积(亩)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'maleRatio',
|
||||
title: '男生比例(%)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'femaleRatio',
|
||||
title: '女生比例(%)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-campus-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-college-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="tsx">
|
||||
import { computed, ref } from 'vue';
|
||||
import { NDivider, NTag } from 'naive-ui';
|
||||
import { NDivider } from 'naive-ui';
|
||||
import { fetchBatchDeleteSchoolDetail, fetchGetSchoolDetailList } from '@/service/api/art/school-detail';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useAuth } from '@/hooks/business/auth';
|
||||
|
|
@ -32,33 +32,12 @@ const { hasAuth } = useAuth();
|
|||
const searchParams = ref<Api.Art.SchoolDetailSearchParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
detailId: null,
|
||||
schoolId: props.schoolId,
|
||||
introduction: null,
|
||||
schoolIcon: null,
|
||||
address: null,
|
||||
contact: null,
|
||||
email: null,
|
||||
website: null,
|
||||
postcode: null,
|
||||
establishYear: null,
|
||||
campusAreaMu: null,
|
||||
libraryCollection: null,
|
||||
maleRatio: null,
|
||||
femaleRatio: null,
|
||||
is985: null,
|
||||
is211: null,
|
||||
isDoubleFirstClass: null,
|
||||
isKeyUniversity: null,
|
||||
tags: [],
|
||||
studentCount: null,
|
||||
teacherCount: null,
|
||||
masterPoint: null,
|
||||
doctorPoint: null,
|
||||
keyMajorCount: null,
|
||||
employmentRate: null,
|
||||
satisfactionRate: null,
|
||||
univId: null,
|
||||
params: {}
|
||||
});
|
||||
|
||||
|
|
@ -67,17 +46,6 @@ const requestParams = computed<Api.Art.SchoolDetailSearchParams>(() => ({
|
|||
schoolId: props.schoolId ?? searchParams.value.schoolId
|
||||
}));
|
||||
|
||||
function renderBooleanTag(value?: number | null) {
|
||||
if (value === null || value === undefined) return '-';
|
||||
|
||||
return <NTag type={value === 1 ? 'success' : 'default'}>{value === 1 ? '是' : '否'}</NTag>;
|
||||
}
|
||||
|
||||
function renderStringArray(values?: string[] | null) {
|
||||
if (!values?.length) return '-';
|
||||
return values.join(' / ');
|
||||
}
|
||||
|
||||
const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagination, scrollX } =
|
||||
useNaivePaginatedTable({
|
||||
api: () => fetchGetSchoolDetailList(requestParams.value),
|
||||
|
|
@ -117,12 +85,6 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
|||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'schoolIcon',
|
||||
title: '院校图标',
|
||||
align: 'center',
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '学校地址',
|
||||
|
|
@ -147,89 +109,6 @@ const { columns, columnChecks, data, getData, getDataByPage, loading, mobilePagi
|
|||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'postcode',
|
||||
title: '邮编',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'establishYear',
|
||||
title: '建校年份',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'campusAreaMu',
|
||||
title: '占地面积(亩)',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'libraryCollection',
|
||||
title: '图书馆藏书量',
|
||||
align: 'center',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
key: 'studentCount',
|
||||
title: '学生人数',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'teacherCount',
|
||||
title: '教师人数',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'employmentRate',
|
||||
title: '就业率(%)',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'satisfactionRate',
|
||||
title: '满意度(%)',
|
||||
align: 'center',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
key: 'is985',
|
||||
title: '985',
|
||||
align: 'center',
|
||||
minWidth: 80,
|
||||
render: row => renderBooleanTag(row.is985)
|
||||
},
|
||||
{
|
||||
key: 'is211',
|
||||
title: '211',
|
||||
align: 'center',
|
||||
minWidth: 80,
|
||||
render: row => renderBooleanTag(row.is211)
|
||||
},
|
||||
{
|
||||
key: 'isDoubleFirstClass',
|
||||
title: '双一流',
|
||||
align: 'center',
|
||||
minWidth: 90,
|
||||
render: row => renderBooleanTag(row.isDoubleFirstClass)
|
||||
},
|
||||
{
|
||||
key: 'isKeyUniversity',
|
||||
title: '重点大学',
|
||||
align: 'center',
|
||||
minWidth: 100,
|
||||
render: row => renderBooleanTag(row.isKeyUniversity)
|
||||
},
|
||||
{
|
||||
key: 'tags',
|
||||
title: '详情标签',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
render: row => renderStringArray(row.tags)
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
|
|
|
|||
|
|
@ -35,9 +35,6 @@ const visible = defineModel<boolean>('visible', {
|
|||
|
||||
const { formRef, validate, restoreValidation } = useNaiveForm();
|
||||
const { createRequiredRule } = useFormRules();
|
||||
const submitLoading = ref(false);
|
||||
const detailTagInputValue = ref('');
|
||||
const detailTagList = ref<string[]>([]);
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
|
|
@ -47,17 +44,6 @@ const title = computed(() => {
|
|||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
const booleanOptions = [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
];
|
||||
|
||||
function toNumberValue(value: CommonType.IdType | null | undefined) {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
|
||||
return typeof value === 'number' ? value : Number(value);
|
||||
}
|
||||
|
||||
type Model = Api.Art.SchoolDetailOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
|
@ -67,94 +53,31 @@ function createDefaultModel(): Model {
|
|||
detailId: null,
|
||||
schoolId: null,
|
||||
introduction: '',
|
||||
schoolIcon: '',
|
||||
address: '',
|
||||
contact: '',
|
||||
email: '',
|
||||
website: '',
|
||||
postcode: '',
|
||||
establishYear: null,
|
||||
campusAreaMu: null,
|
||||
libraryCollection: null,
|
||||
maleRatio: null,
|
||||
femaleRatio: null,
|
||||
is985: null,
|
||||
is211: null,
|
||||
isDoubleFirstClass: null,
|
||||
isKeyUniversity: null,
|
||||
tags: [],
|
||||
studentCount: null,
|
||||
teacherCount: null,
|
||||
masterPoint: null,
|
||||
doctorPoint: null,
|
||||
keyMajorCount: null,
|
||||
employmentRate: null,
|
||||
satisfactionRate: null,
|
||||
univId: null,
|
||||
remark: ''
|
||||
};
|
||||
}
|
||||
|
||||
type RuleKey = 'schoolId' | 'address' | 'contact';
|
||||
type RuleKey = Extract<keyof Model, 'detailId' | 'schoolId'>;
|
||||
|
||||
const rules: Record<RuleKey, App.Global.FormRule> = {
|
||||
schoolId: createRequiredRule('关联学校主表ID不能为空'),
|
||||
address: createRequiredRule('学校地址不能为空'),
|
||||
contact: createRequiredRule('联系电话不能为空')
|
||||
detailId: createRequiredRule('主键ID不能为空'),
|
||||
schoolId: createRequiredRule('关联学校主表ID不能为空')
|
||||
};
|
||||
|
||||
function normalizeStringList(values?: string[] | null) {
|
||||
if (!values?.length) return [];
|
||||
|
||||
const uniqueValues = new Set<string>();
|
||||
|
||||
values.forEach(value => {
|
||||
const normalizedValue = String(value).trim();
|
||||
if (normalizedValue) {
|
||||
uniqueValues.add(normalizedValue);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(uniqueValues);
|
||||
}
|
||||
|
||||
function isSameStringList(source: string[], target: string[]) {
|
||||
if (source.length !== target.length) return false;
|
||||
return source.every((value, index) => value === target[index]);
|
||||
}
|
||||
|
||||
function syncTagsToModel() {
|
||||
model.value.tags = normalizeStringList(detailTagList.value);
|
||||
}
|
||||
|
||||
function addDetailTags() {
|
||||
const values = detailTagInputValue.value
|
||||
.split(/[,,]/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!values.length) return;
|
||||
|
||||
detailTagList.value = normalizeStringList([...detailTagList.value, ...values]);
|
||||
detailTagInputValue.value = '';
|
||||
}
|
||||
|
||||
function handleUpdateModelWhenEdit() {
|
||||
model.value = createDefaultModel();
|
||||
detailTagInputValue.value = '';
|
||||
detailTagList.value = [];
|
||||
|
||||
if (props.operateType === 'add' && props.defaultSchoolId !== null) {
|
||||
model.value.schoolId = props.defaultSchoolId;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData) {
|
||||
const detailData = jsonClone(props.rowData);
|
||||
Object.assign(model.value, detailData);
|
||||
detailTagList.value = normalizeStringList(detailData.tags);
|
||||
Object.assign(model.value, jsonClone(props.rowData));
|
||||
}
|
||||
|
||||
syncTagsToModel();
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
|
|
@ -164,283 +87,88 @@ function closeDrawer() {
|
|||
async function handleSubmit() {
|
||||
await validate();
|
||||
|
||||
submitLoading.value = true;
|
||||
|
||||
const {
|
||||
detailId,
|
||||
schoolId,
|
||||
introduction,
|
||||
schoolIcon,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
postcode,
|
||||
establishYear,
|
||||
campusAreaMu,
|
||||
libraryCollection,
|
||||
maleRatio,
|
||||
femaleRatio,
|
||||
is985,
|
||||
is211,
|
||||
isDoubleFirstClass,
|
||||
isKeyUniversity,
|
||||
studentCount,
|
||||
teacherCount,
|
||||
masterPoint,
|
||||
doctorPoint,
|
||||
keyMajorCount,
|
||||
employmentRate,
|
||||
satisfactionRate,
|
||||
univId,
|
||||
remark
|
||||
} = model.value;
|
||||
|
||||
const payload: Api.Art.SchoolDetailOperateParams = {
|
||||
detailId,
|
||||
schoolId,
|
||||
introduction,
|
||||
schoolIcon,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
postcode,
|
||||
establishYear,
|
||||
campusAreaMu,
|
||||
libraryCollection,
|
||||
maleRatio,
|
||||
femaleRatio,
|
||||
is985,
|
||||
is211,
|
||||
isDoubleFirstClass,
|
||||
isKeyUniversity,
|
||||
tags: normalizeStringList(detailTagList.value),
|
||||
studentCount,
|
||||
teacherCount,
|
||||
masterPoint,
|
||||
doctorPoint,
|
||||
keyMajorCount,
|
||||
employmentRate,
|
||||
satisfactionRate,
|
||||
univId,
|
||||
remark
|
||||
};
|
||||
const { detailId, schoolId, introduction, address, contact, email, website, remark } = model.value;
|
||||
|
||||
// request
|
||||
if (props.operateType === 'add') {
|
||||
const { error } = await fetchCreateSchoolDetail(payload);
|
||||
submitLoading.value = false;
|
||||
const { error } = await fetchCreateSchoolDetail({
|
||||
schoolId,
|
||||
introduction,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
if (props.operateType === 'edit') {
|
||||
const { error } = await fetchUpdateSchoolDetail(payload);
|
||||
submitLoading.value = false;
|
||||
const { error } = await fetchUpdateSchoolDetail({
|
||||
detailId,
|
||||
schoolId,
|
||||
introduction,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
remark
|
||||
});
|
||||
if (error) return;
|
||||
}
|
||||
|
||||
window.$message?.success(props.operateType === 'add' ? $t('common.addSuccess') : $t('common.updateSuccess'));
|
||||
window.$message?.success($t('common.updateSuccess'));
|
||||
closeDrawer();
|
||||
emit('submitted');
|
||||
}
|
||||
|
||||
watch(visible, show => {
|
||||
if (show) {
|
||||
watch(visible, () => {
|
||||
if (visible.value) {
|
||||
handleUpdateModelWhenEdit();
|
||||
restoreValidation();
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
detailTagList,
|
||||
tags => {
|
||||
const normalizedTags = normalizeStringList(tags);
|
||||
|
||||
if (!isSameStringList(tags, normalizedTags)) {
|
||||
detailTagList.value = normalizedTags;
|
||||
return;
|
||||
}
|
||||
|
||||
syncTagsToModel();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="980" class="max-w-96%">
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<NGrid :cols="24" :x-gap="16">
|
||||
<NGi :span="8">
|
||||
<NFormItem label="关联学校主表ID" path="schoolId">
|
||||
<NInputNumber
|
||||
:value="toNumberValue(model.schoolId)"
|
||||
:disabled="props.defaultSchoolId !== null"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入关联学校主表ID"
|
||||
@update:value="value => (model.schoolId = value)"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="外部学校ID" path="univId">
|
||||
<NInputNumber v-model:value="model.univId" class="w-full" clearable placeholder="请输入外部学校ID" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="邮编" path="postcode">
|
||||
<NInput v-model:value="model.postcode" placeholder="请输入邮编" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
<NFormItem label="学校详细介绍" path="introduction">
|
||||
<NInput
|
||||
v-model:value="model.introduction"
|
||||
:rows="3"
|
||||
type="textarea"
|
||||
placeholder="请输入学校详细介绍(大文本)"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="院校图标" path="schoolIcon">
|
||||
<NInput v-model:value="model.schoolIcon" placeholder="请输入院校图标地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="官网地址" path="website">
|
||||
<NInput v-model:value="model.website" placeholder="请输入官网地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="学校地址" path="address">
|
||||
<NInput v-model:value="model.address" placeholder="请输入学校地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="联系电话" path="contact">
|
||||
<NInput v-model:value="model.contact" placeholder="请输入联系电话" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="model.email" placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="建校年份" path="establishYear">
|
||||
<NInputNumber v-model:value="model.establishYear" class="w-full" clearable placeholder="请输入建校年份" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="占地面积(亩)" path="campusAreaMu">
|
||||
<NInputNumber v-model:value="model.campusAreaMu" class="w-full" clearable placeholder="请输入占地面积" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="图书馆藏书量" path="libraryCollection">
|
||||
<NInputNumber
|
||||
v-model:value="model.libraryCollection"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入图书馆藏书量"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="学生人数" path="studentCount">
|
||||
<NInputNumber v-model:value="model.studentCount" class="w-full" clearable placeholder="请输入学生人数" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="教师人数" path="teacherCount">
|
||||
<NInputNumber v-model:value="model.teacherCount" class="w-full" clearable placeholder="请输入教师人数" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="硕士点数量" path="masterPoint">
|
||||
<NInputNumber v-model:value="model.masterPoint" class="w-full" clearable placeholder="请输入硕士点数量" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="博士点数量" path="doctorPoint">
|
||||
<NInputNumber v-model:value="model.doctorPoint" class="w-full" clearable placeholder="请输入博士点数量" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="重点专业数" path="keyMajorCount">
|
||||
<NInputNumber v-model:value="model.keyMajorCount" class="w-full" clearable placeholder="请输入重点专业数" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="男生比例(%)" path="maleRatio">
|
||||
<NInputNumber v-model:value="model.maleRatio" class="w-full" clearable placeholder="请输入男生比例" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="女生比例(%)" path="femaleRatio">
|
||||
<NInputNumber v-model:value="model.femaleRatio" class="w-full" clearable placeholder="请输入女生比例" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="就业率(%)" path="employmentRate">
|
||||
<NInputNumber v-model:value="model.employmentRate" class="w-full" clearable placeholder="请输入就业率" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="满意度(%)" path="satisfactionRate">
|
||||
<NInputNumber v-model:value="model.satisfactionRate" class="w-full" clearable placeholder="请输入满意度" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否985" path="is985">
|
||||
<NSelect v-model:value="model.is985" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否211" path="is211">
|
||||
<NSelect v-model:value="model.is211" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否双一流" path="isDoubleFirstClass">
|
||||
<NSelect v-model:value="model.isDoubleFirstClass" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否重点大学" path="isKeyUniversity">
|
||||
<NSelect v-model:value="model.isKeyUniversity" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
<NFormItem label="详情标签" path="tags">
|
||||
<NSpace vertical class="w-full">
|
||||
<NSpace class="w-full" :wrap-item="false">
|
||||
<NInput
|
||||
v-model:value="detailTagInputValue"
|
||||
class="flex-1"
|
||||
placeholder="输入详情标签后回车或点击确认,可用逗号一次输入多个"
|
||||
@keydown.enter.prevent="addDetailTags"
|
||||
/>
|
||||
<NButton type="primary" ghost @click="addDetailTags">确认</NButton>
|
||||
</NSpace>
|
||||
<NDynamicTags v-model:value="detailTagList" />
|
||||
</NSpace>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
<NForm ref="formRef" :model="model" :rules="rules">
|
||||
<NFormItem label="关联学校主表ID" path="schoolId">
|
||||
<NInput
|
||||
v-model:value="model.schoolId"
|
||||
:disabled="props.defaultSchoolId !== null"
|
||||
placeholder="请输入关联学校主表ID"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="学校详细介绍(大文本)" path="introduction">
|
||||
<NInput
|
||||
v-model:value="model.introduction"
|
||||
:rows="3"
|
||||
type="textarea"
|
||||
placeholder="请输入学校详细介绍(大文本)"
|
||||
/>
|
||||
</NFormItem>
|
||||
<NFormItem label="学校地址" path="address">
|
||||
<NInput v-model:value="model.address" placeholder="请输入学校地址" />
|
||||
</NFormItem>
|
||||
<NFormItem label="联系电话" path="contact">
|
||||
<NInput v-model:value="model.contact" placeholder="请输入联系电话" />
|
||||
</NFormItem>
|
||||
<NFormItem label="邮箱" path="email">
|
||||
<NInput v-model:value="model.email" placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
<NFormItem label="官网地址" path="website">
|
||||
<NInput v-model:value="model.website" placeholder="请输入官网地址" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="model.remark" :rows="3" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
<template #footer>
|
||||
<NSpace :size="16">
|
||||
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
|
||||
<NButton type="primary" :loading="submitLoading" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
|
||||
</NSpace>
|
||||
</template>
|
||||
</NDrawerContent>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, toRaw, watch } from 'vue';
|
||||
import { toRaw } from 'vue';
|
||||
import { jsonClone } from '@sa/utils';
|
||||
import { useNaiveForm } from '@/hooks/common/form';
|
||||
import { $t } from '@/locales';
|
||||
|
|
@ -19,60 +19,9 @@ const { formRef, validate, restoreValidation } = useNaiveForm();
|
|||
const model = defineModel<Api.Art.SchoolDetailSearchParams>('model', { required: true });
|
||||
|
||||
const defaultModel = jsonClone(toRaw(model.value));
|
||||
const detailTagInputValue = ref('');
|
||||
const detailTagList = ref<string[]>([]);
|
||||
const booleanOptions = [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
];
|
||||
|
||||
function toNumberValue(value: CommonType.IdType | null | undefined) {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
|
||||
return typeof value === 'number' ? value : Number(value);
|
||||
}
|
||||
|
||||
function normalizeStringList(values?: string[] | null) {
|
||||
if (!values?.length) return [];
|
||||
|
||||
const uniqueValues = new Set<string>();
|
||||
|
||||
values.forEach(value => {
|
||||
const normalizedValue = String(value).trim();
|
||||
if (normalizedValue) {
|
||||
uniqueValues.add(normalizedValue);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(uniqueValues);
|
||||
}
|
||||
|
||||
function isSameStringList(source: string[], target: string[]) {
|
||||
if (source.length !== target.length) return false;
|
||||
return source.every((value, index) => value === target[index]);
|
||||
}
|
||||
|
||||
function syncTagsToModel() {
|
||||
model.value.tags = normalizeStringList(detailTagList.value);
|
||||
}
|
||||
|
||||
function addDetailTags() {
|
||||
const values = detailTagInputValue.value
|
||||
.split(/[,,]/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!values.length) return;
|
||||
|
||||
detailTagList.value = normalizeStringList([...detailTagList.value, ...values]);
|
||||
detailTagInputValue.value = '';
|
||||
}
|
||||
|
||||
function resetModel() {
|
||||
Object.assign(model.value, jsonClone(defaultModel));
|
||||
detailTagInputValue.value = '';
|
||||
detailTagList.value = normalizeStringList(defaultModel.tags);
|
||||
syncTagsToModel();
|
||||
Object.assign(model.value, defaultModel);
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
|
|
@ -85,58 +34,25 @@ async function search() {
|
|||
await validate();
|
||||
emit('search');
|
||||
}
|
||||
|
||||
watch(
|
||||
detailTagList,
|
||||
tags => {
|
||||
const normalizedTags = normalizeStringList(tags);
|
||||
|
||||
if (!isSameStringList(tags, normalizedTags)) {
|
||||
detailTagList.value = normalizedTags;
|
||||
return;
|
||||
}
|
||||
|
||||
syncTagsToModel();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => model.value.tags,
|
||||
tags => {
|
||||
const normalizedTags = normalizeStringList(tags);
|
||||
|
||||
if (!isSameStringList(detailTagList.value, normalizedTags)) {
|
||||
detailTagList.value = normalizedTags;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-detail-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi span="24 s:12 m:6" label="详情ID" label-width="auto" path="detailId" class="pr-24px">
|
||||
<NInputNumber
|
||||
:value="toNumberValue(model.detailId)"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入详情ID"
|
||||
@update:value="value => (model.detailId = value)"
|
||||
/>
|
||||
<NFormItemGi span="24 s:12 m:6" label="关联学校主表ID" label-width="auto" path="schoolId" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolId" placeholder="请输入关联学校主表ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校ID" label-width="auto" path="schoolId" class="pr-24px">
|
||||
<NInputNumber
|
||||
:value="toNumberValue(model.schoolId)"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入学校ID"
|
||||
@update:value="value => (model.schoolId = value)"
|
||||
/>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="学校详细介绍(大文本)"
|
||||
label-width="auto"
|
||||
path="introduction"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.introduction" placeholder="请输入学校详细介绍(大文本)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校地址" label-width="auto" path="address" class="pr-24px">
|
||||
<NInput v-model:value="model.address" placeholder="请输入学校地址" />
|
||||
|
|
@ -144,97 +60,12 @@ watch(
|
|||
<NFormItemGi span="24 s:12 m:6" label="联系电话" label-width="auto" path="contact" class="pr-24px">
|
||||
<NInput v-model:value="model.contact" placeholder="请输入联系电话" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="邮箱" label-width="auto" path="email" class="pr-24px">
|
||||
<NInput v-model:value="model.email" placeholder="请输入邮箱" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="官网地址" label-width="auto" path="website" class="pr-24px">
|
||||
<NInput v-model:value="model.website" placeholder="请输入官网地址" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="院校图标" label-width="auto" path="schoolIcon" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolIcon" placeholder="请输入院校图标地址" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="邮编" label-width="auto" path="postcode" class="pr-24px">
|
||||
<NInput v-model:value="model.postcode" placeholder="请输入邮编" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="外部学校ID" label-width="auto" path="univId" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.univId" class="w-full" clearable placeholder="请输入外部学校ID" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="建校年份" label-width="auto" path="establishYear" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.establishYear" class="w-full" clearable placeholder="请输入建校年份" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="占地面积(亩)" label-width="auto" path="campusAreaMu" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.campusAreaMu" class="w-full" clearable placeholder="请输入占地面积" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="图书馆藏书量"
|
||||
label-width="auto"
|
||||
path="libraryCollection"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInputNumber
|
||||
v-model:value="model.libraryCollection"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入图书馆藏书量"
|
||||
/>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学生人数" label-width="auto" path="studentCount" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.studentCount" class="w-full" clearable placeholder="请输入学生人数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="教师人数" label-width="auto" path="teacherCount" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.teacherCount" class="w-full" clearable placeholder="请输入教师人数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="硕士点数量" label-width="auto" path="masterPoint" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.masterPoint" class="w-full" clearable placeholder="请输入硕士点数量" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="博士点数量" label-width="auto" path="doctorPoint" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.doctorPoint" class="w-full" clearable placeholder="请输入博士点数量" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="重点专业数" label-width="auto" path="keyMajorCount" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.keyMajorCount" class="w-full" clearable placeholder="请输入重点专业数" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="就业率(%)" label-width="auto" path="employmentRate" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.employmentRate" class="w-full" clearable placeholder="请输入就业率" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="满意度(%)" label-width="auto" path="satisfactionRate" class="pr-24px">
|
||||
<NInputNumber v-model:value="model.satisfactionRate" class="w-full" clearable placeholder="请输入满意度" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="是否985" label-width="auto" path="is985" class="pr-24px">
|
||||
<NSelect v-model:value="model.is985" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="是否211" label-width="auto" path="is211" class="pr-24px">
|
||||
<NSelect v-model:value="model.is211" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="是否双一流"
|
||||
label-width="auto"
|
||||
path="isDoubleFirstClass"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NSelect v-model:value="model.isDoubleFirstClass" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="是否重点大学"
|
||||
label-width="auto"
|
||||
path="isKeyUniversity"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NSelect v-model:value="model.isKeyUniversity" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24" label="详情标签" label-width="auto" path="tags" class="pr-24px">
|
||||
<NSpace vertical class="w-full">
|
||||
<NSpace class="w-full" :wrap-item="false">
|
||||
<NInput
|
||||
v-model:value="detailTagInputValue"
|
||||
class="flex-1"
|
||||
placeholder="输入详情标签后回车或点击确认,可用逗号一次输入多个"
|
||||
@keydown.enter.prevent="addDetailTags"
|
||||
/>
|
||||
<NButton type="primary" ghost @click="addDetailTags">确认</NButton>
|
||||
</NSpace>
|
||||
<NDynamicTags v-model:value="detailTagList" />
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-dorm-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-enroll-plan-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-major-tag-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ watch(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-major-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-media-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-name-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ const enrollCodeInputValue = ref('');
|
|||
const enrollCodeList = ref<string[]>([]);
|
||||
const schoolTagInputValue = ref('');
|
||||
const schoolTagList = ref<string[]>([]);
|
||||
const detailTagInputValue = ref('');
|
||||
const detailTagList = ref<string[]>([]);
|
||||
|
||||
const title = computed(() => {
|
||||
const titles: Record<NaiveUI.TableOperateType, string> = {
|
||||
|
|
@ -50,11 +48,6 @@ const title = computed(() => {
|
|||
return titles[props.operateType];
|
||||
});
|
||||
|
||||
const booleanOptions = [
|
||||
{ value: 1, label: '是' },
|
||||
{ value: 0, label: '否' }
|
||||
];
|
||||
|
||||
type Model = Api.Art.SchoolWithDetailOperateParams;
|
||||
|
||||
const model = ref<Model>(createDefaultModel());
|
||||
|
|
@ -66,6 +59,7 @@ function createDefaultModel(): Model {
|
|||
mainCode: '',
|
||||
mainName: '',
|
||||
shortName: '',
|
||||
schoolIcon: '',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
|
|
@ -73,36 +67,20 @@ function createDefaultModel(): Model {
|
|||
educationLevel: '',
|
||||
schoolNature: '',
|
||||
supervisorDept: '',
|
||||
establishYear: null,
|
||||
campusAreaMu: null,
|
||||
maleRatio: null,
|
||||
femaleRatio: null,
|
||||
remark: ''
|
||||
},
|
||||
detail: {
|
||||
detailId: null,
|
||||
schoolId: null,
|
||||
introduction: '',
|
||||
schoolIcon: '',
|
||||
address: '',
|
||||
contact: '',
|
||||
email: '',
|
||||
website: '',
|
||||
postcode: '',
|
||||
establishYear: null,
|
||||
campusAreaMu: null,
|
||||
libraryCollection: null,
|
||||
maleRatio: null,
|
||||
femaleRatio: null,
|
||||
is985: null,
|
||||
is211: null,
|
||||
isDoubleFirstClass: null,
|
||||
isKeyUniversity: null,
|
||||
tags: [],
|
||||
studentCount: null,
|
||||
teacherCount: null,
|
||||
masterPoint: null,
|
||||
doctorPoint: null,
|
||||
keyMajorCount: null,
|
||||
employmentRate: null,
|
||||
satisfactionRate: null,
|
||||
univId: null,
|
||||
remark: ''
|
||||
},
|
||||
enrollCodes: [],
|
||||
|
|
@ -136,33 +114,31 @@ function normalizeStringList(values?: string[] | null) {
|
|||
|
||||
function isSameStringList(source: string[], target: string[]) {
|
||||
if (source.length !== target.length) return false;
|
||||
return source.every((value, index) => value === target[index]);
|
||||
}
|
||||
|
||||
function appendValues(inputValue: string, target: string[]) {
|
||||
const values = inputValue
|
||||
.split(/[,,]/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!values.length) return target;
|
||||
|
||||
return normalizeStringList([...target, ...values]);
|
||||
return source.every((code, index) => code === target[index]);
|
||||
}
|
||||
|
||||
function addEnrollCodes() {
|
||||
enrollCodeList.value = appendValues(enrollCodeInputValue.value, enrollCodeList.value);
|
||||
const inputCodes = enrollCodeInputValue.value
|
||||
.split(/[,,]/)
|
||||
.map(code => code.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!inputCodes.length) return;
|
||||
|
||||
enrollCodeList.value = normalizeStringList([...enrollCodeList.value, ...inputCodes]);
|
||||
enrollCodeInputValue.value = '';
|
||||
}
|
||||
|
||||
function addSchoolTags() {
|
||||
schoolTagList.value = appendValues(schoolTagInputValue.value, schoolTagList.value);
|
||||
schoolTagInputValue.value = '';
|
||||
}
|
||||
const inputTags = schoolTagInputValue.value
|
||||
.split(/[,,]/)
|
||||
.map(tag => tag.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
function addDetailTags() {
|
||||
detailTagList.value = appendValues(detailTagInputValue.value, detailTagList.value);
|
||||
detailTagInputValue.value = '';
|
||||
if (!inputTags.length) return;
|
||||
|
||||
schoolTagList.value = normalizeStringList([...schoolTagList.value, ...inputTags]);
|
||||
schoolTagInputValue.value = '';
|
||||
}
|
||||
|
||||
function syncEnrollCodesToModel() {
|
||||
|
|
@ -173,10 +149,6 @@ function syncSchoolTagsToModel() {
|
|||
model.value.schoolTags = normalizeStringList(schoolTagList.value);
|
||||
}
|
||||
|
||||
function syncDetailTagsToModel() {
|
||||
model.value.detail.tags = normalizeStringList(detailTagList.value);
|
||||
}
|
||||
|
||||
async function loadEditFormData(schoolId: CommonType.IdType) {
|
||||
detailLoading.value = true;
|
||||
|
||||
|
|
@ -200,14 +172,11 @@ async function loadEditFormData(schoolId: CommonType.IdType) {
|
|||
}
|
||||
|
||||
if (!detailRes.error && detailRes.data) {
|
||||
const detailData = jsonClone(detailRes.data);
|
||||
Object.assign(model.value.detail, detailData);
|
||||
detailTagList.value = normalizeStringList(detailData.tags);
|
||||
Object.assign(model.value.detail, jsonClone(detailRes.data));
|
||||
}
|
||||
|
||||
syncEnrollCodesToModel();
|
||||
syncSchoolTagsToModel();
|
||||
syncDetailTagsToModel();
|
||||
model.value.detail.schoolId = model.value.school.schoolId;
|
||||
detailLoading.value = false;
|
||||
}
|
||||
|
|
@ -218,15 +187,12 @@ async function handleUpdateModelWhenOpen() {
|
|||
enrollCodeList.value = [];
|
||||
schoolTagInputValue.value = '';
|
||||
schoolTagList.value = [];
|
||||
detailTagInputValue.value = '';
|
||||
detailTagList.value = [];
|
||||
|
||||
if (props.operateType === 'edit' && props.rowData?.schoolId !== null && props.rowData?.schoolId !== undefined) {
|
||||
await loadEditFormData(props.rowData.schoolId);
|
||||
} else {
|
||||
syncEnrollCodesToModel();
|
||||
syncSchoolTagsToModel();
|
||||
syncDetailTagsToModel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,94 +216,13 @@ async function handleSubmit() {
|
|||
|
||||
submitLoading.value = true;
|
||||
|
||||
const {
|
||||
schoolId,
|
||||
mainCode,
|
||||
mainName,
|
||||
shortName,
|
||||
province,
|
||||
city,
|
||||
district,
|
||||
universityType,
|
||||
educationLevel,
|
||||
schoolNature,
|
||||
supervisorDept,
|
||||
remark: schoolRemark
|
||||
} = model.value.school;
|
||||
|
||||
const {
|
||||
detailId,
|
||||
introduction,
|
||||
schoolIcon,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
postcode,
|
||||
establishYear,
|
||||
campusAreaMu,
|
||||
libraryCollection,
|
||||
maleRatio,
|
||||
femaleRatio,
|
||||
is985,
|
||||
is211,
|
||||
isDoubleFirstClass,
|
||||
isKeyUniversity,
|
||||
studentCount,
|
||||
teacherCount,
|
||||
masterPoint,
|
||||
doctorPoint,
|
||||
keyMajorCount,
|
||||
employmentRate,
|
||||
satisfactionRate,
|
||||
univId,
|
||||
remark: detailRemark
|
||||
} = model.value.detail;
|
||||
|
||||
const payload: Api.Art.SchoolWithDetailOperateParams = {
|
||||
school: {
|
||||
schoolId,
|
||||
mainCode,
|
||||
mainName,
|
||||
shortName,
|
||||
province,
|
||||
city,
|
||||
district,
|
||||
universityType,
|
||||
educationLevel,
|
||||
schoolNature,
|
||||
supervisorDept,
|
||||
remark: schoolRemark
|
||||
...model.value.school
|
||||
},
|
||||
detail: {
|
||||
detailId,
|
||||
schoolId,
|
||||
introduction,
|
||||
schoolIcon,
|
||||
address,
|
||||
contact,
|
||||
email,
|
||||
website,
|
||||
postcode,
|
||||
establishYear,
|
||||
campusAreaMu,
|
||||
libraryCollection,
|
||||
maleRatio,
|
||||
femaleRatio,
|
||||
is985,
|
||||
is211,
|
||||
isDoubleFirstClass,
|
||||
isKeyUniversity,
|
||||
tags: normalizeStringList(detailTagList.value),
|
||||
studentCount,
|
||||
teacherCount,
|
||||
masterPoint,
|
||||
doctorPoint,
|
||||
keyMajorCount,
|
||||
employmentRate,
|
||||
satisfactionRate,
|
||||
univId,
|
||||
remark: detailRemark
|
||||
...model.value.detail,
|
||||
schoolId: model.value.school.schoolId
|
||||
},
|
||||
enrollCodes: normalizeStringList(enrollCodeList.value),
|
||||
schoolTags: normalizeStringList(schoolTagList.value)
|
||||
|
|
@ -403,25 +288,10 @@ watch(
|
|||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
detailTagList,
|
||||
tags => {
|
||||
const normalizedTags = normalizeStringList(tags);
|
||||
|
||||
if (!isSameStringList(tags, normalizedTags)) {
|
||||
detailTagList.value = normalizedTags;
|
||||
return;
|
||||
}
|
||||
|
||||
syncDetailTagsToModel();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="1120" class="max-w-96%">
|
||||
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="960" class="max-w-92%">
|
||||
<NDrawerContent :title="title" :native-scrollbar="false" closable>
|
||||
<NForm ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<NCard title="院校基础信息" size="small" :bordered="true" class="mb-16px">
|
||||
|
|
@ -473,6 +343,11 @@ watch(
|
|||
</NSpace>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="院校图标" path="school.schoolIcon">
|
||||
<NInput v-model:value="model.school.schoolIcon" placeholder="请输入院校图标地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="省份" path="school.province">
|
||||
<NInput v-model:value="model.school.province" placeholder="请输入省份" />
|
||||
|
|
@ -490,7 +365,7 @@ watch(
|
|||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="大学类型" path="school.universityType">
|
||||
<NInput v-model:value="model.school.universityType" placeholder="请输入大学类型,如综合/工科/艺术" />
|
||||
<NInput v-model:value="model.school.universityType" placeholder="如:综合/工科/财经/艺术" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
|
|
@ -499,6 +374,7 @@ watch(
|
|||
v-model:value="model.school.educationLevel"
|
||||
placeholder="请选择学历层次"
|
||||
:options="[
|
||||
{ value: '', label: '请选择' },
|
||||
{ value: '本科', label: '本科' },
|
||||
{ value: '专科', label: '专科' }
|
||||
]"
|
||||
|
|
@ -525,6 +401,26 @@ watch(
|
|||
<NInput v-model:value="model.school.supervisorDept" placeholder="请输入主管部门" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="建校时间(年)" path="school.establishYear">
|
||||
<NInput v-model:value="model.school.establishYear" placeholder="请输入建校年份" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="占地面积(亩)" path="school.campusAreaMu">
|
||||
<NInput v-model:value="model.school.campusAreaMu" placeholder="请输入占地面积(亩)" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="男生比例(%)" path="school.maleRatio">
|
||||
<NInput v-model:value="model.school.maleRatio" placeholder="请输入男生比例(%)" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="女生比例(%)" path="school.femaleRatio">
|
||||
<NInput v-model:value="model.school.femaleRatio" placeholder="请输入女生比例(%)" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
<NFormItem label="备注" path="school.remark">
|
||||
<NInput v-model:value="model.school.remark" :rows="2" type="textarea" placeholder="请输入备注" />
|
||||
|
|
@ -545,16 +441,6 @@ watch(
|
|||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="院校图标" path="detail.schoolIcon">
|
||||
<NInput v-model:value="model.detail.schoolIcon" placeholder="请输入院校图标地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="官网地址" path="detail.website">
|
||||
<NInput v-model:value="model.detail.website" placeholder="请输入官网地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="学校地址" path="detail.address">
|
||||
<NInput v-model:value="model.detail.address" placeholder="请输入学校地址" />
|
||||
|
|
@ -565,150 +451,14 @@ watch(
|
|||
<NInput v-model:value="model.detail.contact" placeholder="请输入联系电话" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NGi :span="12">
|
||||
<NFormItem label="邮箱" path="detail.email">
|
||||
<NInput v-model:value="model.detail.email" placeholder="请输入邮箱" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="邮编" path="detail.postcode">
|
||||
<NInput v-model:value="model.detail.postcode" placeholder="请输入邮编" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="外部学校ID" path="detail.univId">
|
||||
<NInputNumber v-model:value="model.detail.univId" class="w-full" clearable placeholder="请输入外部学校ID" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="建校年份" path="detail.establishYear">
|
||||
<NInputNumber v-model:value="model.detail.establishYear" class="w-full" clearable placeholder="请输入建校年份" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="占地面积(亩)" path="detail.campusAreaMu">
|
||||
<NInputNumber
|
||||
v-model:value="model.detail.campusAreaMu"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入占地面积(亩)"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="图书馆藏书量" path="detail.libraryCollection">
|
||||
<NInputNumber
|
||||
v-model:value="model.detail.libraryCollection"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入图书馆藏书量"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="男生比例(%)" path="detail.maleRatio">
|
||||
<NInputNumber v-model:value="model.detail.maleRatio" class="w-full" clearable placeholder="请输入男生比例" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="女生比例(%)" path="detail.femaleRatio">
|
||||
<NInputNumber v-model:value="model.detail.femaleRatio" class="w-full" clearable placeholder="请输入女生比例" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="就业率(%)" path="detail.employmentRate">
|
||||
<NInputNumber
|
||||
v-model:value="model.detail.employmentRate"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入就业率"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="满意度(%)" path="detail.satisfactionRate">
|
||||
<NInputNumber
|
||||
v-model:value="model.detail.satisfactionRate"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入满意度"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否985" path="detail.is985">
|
||||
<NSelect v-model:value="model.detail.is985" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否211" path="detail.is211">
|
||||
<NSelect v-model:value="model.detail.is211" :options="booleanOptions" clearable placeholder="请选择" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否双一流" path="detail.isDoubleFirstClass">
|
||||
<NSelect
|
||||
v-model:value="model.detail.isDoubleFirstClass"
|
||||
:options="booleanOptions"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="6">
|
||||
<NFormItem label="是否重点大学" path="detail.isKeyUniversity">
|
||||
<NSelect
|
||||
v-model:value="model.detail.isKeyUniversity"
|
||||
:options="booleanOptions"
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="学生人数" path="detail.studentCount">
|
||||
<NInputNumber v-model:value="model.detail.studentCount" class="w-full" clearable placeholder="请输入学生人数" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="教师人数" path="detail.teacherCount">
|
||||
<NInputNumber v-model:value="model.detail.teacherCount" class="w-full" clearable placeholder="请输入教师人数" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="重点专业数" path="detail.keyMajorCount">
|
||||
<NInputNumber
|
||||
v-model:value="model.detail.keyMajorCount"
|
||||
class="w-full"
|
||||
clearable
|
||||
placeholder="请输入重点专业数"
|
||||
/>
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="硕士点数量" path="detail.masterPoint">
|
||||
<NInputNumber v-model:value="model.detail.masterPoint" class="w-full" clearable placeholder="请输入硕士点数量" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="8">
|
||||
<NFormItem label="博士点数量" path="detail.doctorPoint">
|
||||
<NInputNumber v-model:value="model.detail.doctorPoint" class="w-full" clearable placeholder="请输入博士点数量" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
<NFormItem label="详情标签" path="detail.tags">
|
||||
<NSpace vertical class="w-full">
|
||||
<NSpace class="w-full" :wrap-item="false">
|
||||
<NInput
|
||||
v-model:value="detailTagInputValue"
|
||||
class="flex-1"
|
||||
placeholder="输入详情标签后回车或点击确认,可用逗号一次输入多个"
|
||||
@keydown.enter.prevent="addDetailTags"
|
||||
/>
|
||||
<NButton type="primary" ghost @click="addDetailTags">确认</NButton>
|
||||
</NSpace>
|
||||
<NDynamicTags v-model:value="detailTagList" />
|
||||
</NSpace>
|
||||
<NGi :span="12">
|
||||
<NFormItem label="官网地址" path="detail.website">
|
||||
<NInput v-model:value="model.detail.website" placeholder="请输入官网地址" />
|
||||
</NFormItem>
|
||||
</NGi>
|
||||
<NGi :span="24">
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
@ -51,7 +51,13 @@ async function search() {
|
|||
>
|
||||
<NInput v-model:value="model.mainCode" placeholder="请输入学校编码(唯一标识,如国标代码)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="学校主名称" label-width="auto" path="mainName" class="pr-24px">
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="学校主名称(官方全称)"
|
||||
label-width="auto"
|
||||
path="mainName"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.mainName" placeholder="请输入学校主名称(官方全称)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
|
|
@ -63,6 +69,9 @@ async function search() {
|
|||
>
|
||||
<NInput v-model:value="model.shortName" placeholder="请输入学校简称(备用)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="院校图标" label-width="auto" path="schoolIcon" class="pr-24px">
|
||||
<NInput v-model:value="model.schoolIcon" placeholder="请输入院校图标" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="省份" label-width="auto" path="province" class="pr-24px">
|
||||
<NInput v-model:value="model.province" placeholder="请输入省份" />
|
||||
</NFormItemGi>
|
||||
|
|
@ -113,6 +122,24 @@ async function search() {
|
|||
>
|
||||
<NInput v-model:value="model.supervisorDept" placeholder="请输入主管部门:教育部/工信部/民委..." />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi
|
||||
span="24 s:12 m:6"
|
||||
label="建校时间(年)"
|
||||
label-width="auto"
|
||||
path="establishYear"
|
||||
class="pr-24px"
|
||||
>
|
||||
<NInput v-model:value="model.establishYear" placeholder="请输入建校时间(年)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="占地面积(亩)" label-width="auto" path="campusAreaMu" class="pr-24px">
|
||||
<NInput v-model:value="model.campusAreaMu" placeholder="请输入占地面积(亩)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="男生比例(%)" label-width="auto" path="maleRatio" class="pr-24px">
|
||||
<NInput v-model:value="model.maleRatio" placeholder="请输入男生比例(%)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:12 m:6" label="女生比例(%)" label-width="auto" path="femaleRatio" class="pr-24px">
|
||||
<NInput v-model:value="model.femaleRatio" placeholder="请输入女生比例(%)" />
|
||||
</NFormItemGi>
|
||||
<NFormItemGi :show-feedback="false" span="24" class="pr-24px">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton @click="reset">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import SchoolEnrollPlanList from './school-enroll-plan/index.vue';
|
|||
import SchoolMajorList from './school-major/index.vue';
|
||||
import SchoolMediaList from './school-media/index.vue';
|
||||
import SchoolNameList from './school-name/index.vue';
|
||||
// import SchoolTagList from './school-tag/index.vue';
|
||||
import SchoolTagList from './school-tag/index.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'SchoolSubTableModal'
|
||||
|
|
@ -21,7 +21,8 @@ type SchoolSubModuleType =
|
|||
| 'schoolMajor'
|
||||
| 'schoolEnrollPlan'
|
||||
| 'schoolDorm'
|
||||
| 'schoolMedia';
|
||||
| 'schoolMedia'
|
||||
| 'schoolTag';
|
||||
|
||||
type SchoolSubModuleOption = {
|
||||
key: SchoolSubModuleType;
|
||||
|
|
@ -51,7 +52,8 @@ const subModuleOptions: SchoolSubModuleOption[] = [
|
|||
{ key: 'schoolMajor', label: '专业管理' },
|
||||
{ key: 'schoolEnrollPlan', label: '招生计划管理' },
|
||||
{ key: 'schoolDorm', label: '宿舍管理' },
|
||||
{ key: 'schoolMedia', label: '媒体管理' }
|
||||
{ key: 'schoolMedia', label: '媒体管理' },
|
||||
{ key: 'schoolTag', label: '院校标签管理' }
|
||||
];
|
||||
|
||||
const activeTab = ref<SchoolSubModuleType>(props.activeModule);
|
||||
|
|
@ -63,7 +65,8 @@ const componentMap = {
|
|||
schoolMajor: SchoolMajorList,
|
||||
schoolEnrollPlan: SchoolEnrollPlanList,
|
||||
schoolDorm: SchoolDormList,
|
||||
schoolMedia: SchoolMediaList
|
||||
schoolMedia: SchoolMediaList,
|
||||
schoolTag: SchoolTagList
|
||||
} as const;
|
||||
|
||||
const activeComponent = computed(() => componentMap[activeTab.value]);
|
||||
|
|
@ -158,7 +161,7 @@ watch(
|
|||
<NGi :span="8">办学性质:{{ formatFieldValue(schoolInfo?.schoolNature) }}</NGi>
|
||||
<NGi :span="8">学历层次:{{ formatFieldValue(schoolInfo?.educationLevel) }}</NGi>
|
||||
<NGi :span="8">主管部门:{{ formatFieldValue(schoolInfo?.supervisorDept) }}</NGi>
|
||||
<NGi :span="8">学校简称:{{ formatFieldValue(schoolInfo?.shortName) }}</NGi>
|
||||
<NGi :span="8">建校年份:{{ formatFieldValue(schoolInfo?.establishYear) }}</NGi>
|
||||
</NGrid>
|
||||
</NCard>
|
||||
<NDivider class="my-10px">子表管理区</NDivider>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function search() {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="school-search-card card-wrapper">
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NCollapse>
|
||||
<NCollapseItem :title="$t('common.search')" name="art-school-tag-search">
|
||||
<NForm ref="formRef" :model="model" label-placement="left" :label-width="80">
|
||||
|
|
|
|||
Loading…
Reference in New Issue