新增教研室和教员的时候,同步创建部门和教员账号
This commit is contained in:
@@ -100,3 +100,16 @@ export function endPublishTeachingTask(bh) {
|
||||
params: { bh }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出教学任务书 Excel(该任务下全部教研室任务书课程明细)
|
||||
* GET /teachingTask/exportTaskBook?bh=
|
||||
*/
|
||||
export function exportTaskBook(bh) {
|
||||
return request({
|
||||
url: '/teachingTask/exportTaskBook',
|
||||
method: 'get',
|
||||
params: { bh },
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,30 +9,32 @@ export function listOffice(query) {
|
||||
})
|
||||
}
|
||||
|
||||
// 新增教研室
|
||||
export function addOffice(data) {
|
||||
// 新增教研室(syncDept=true 时同步创建组织部门)
|
||||
export function addOffice(data, syncDept) {
|
||||
return request({
|
||||
url: '/jys/office/add',
|
||||
method: 'post',
|
||||
params: syncDept === undefined ? {} : { syncDept },
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新教研室
|
||||
export function updateOffice(data) {
|
||||
// 更新教研室(syncDeptName=true 时将名称/序号同步到关联部门)
|
||||
export function updateOffice(data, syncDeptName) {
|
||||
return request({
|
||||
url: '/jys/office/update',
|
||||
method: 'post',
|
||||
params: syncDeptName === undefined ? {} : { syncDeptName },
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 停用教研室(jysdh 必传,query 参数)
|
||||
export function disableOffice(jysdh) {
|
||||
// 停用教研室(jysdh 必传,query 参数;syncDept=true 时联动停用部门)
|
||||
export function disableOffice(jysdh, syncDept) {
|
||||
return request({
|
||||
url: '/jys/office/disable',
|
||||
method: 'post',
|
||||
params: { jysdh: jysdh }
|
||||
params: syncDept === undefined ? { jysdh } : { jysdh, syncDept }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,3 +46,31 @@ export function downloadOfficeTemplate() {
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 单个教研室补建/对齐组织部门
|
||||
export function syncOfficeDept(jysdh) {
|
||||
return request({
|
||||
url: '/jys/office/sync-dept',
|
||||
method: 'post',
|
||||
params: { jysdh }
|
||||
})
|
||||
}
|
||||
|
||||
// 对齐组织部门:批量创建/复用(ids 不传 = 全部教研室)
|
||||
export function alignDepts(ids) {
|
||||
return request({
|
||||
url: '/jys/org/align-dept',
|
||||
method: 'post',
|
||||
data: ids ? { ids } : {},
|
||||
timeout: 120000
|
||||
})
|
||||
}
|
||||
|
||||
// 一键对齐:先部门后账号(幂等,返回 { dept, user } 两环节结果)
|
||||
export function alignApply() {
|
||||
return request({
|
||||
url: '/jys/org/align-apply',
|
||||
method: 'post',
|
||||
timeout: 180000
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,3 +73,57 @@ export function updateTeacherAttribute(data) {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/** 下载教员导入模板 GET /download/jy */
|
||||
export function downloadTeacherTemplate() {
|
||||
return request({
|
||||
url: '/download/jy',
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
/** 导出教员 Excel GET /jys/teacher/export */
|
||||
export function exportTeacher(params) {
|
||||
return request({
|
||||
url: '/jys/teacher/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
/** 导入教员 Excel POST /jys/teacher/import */
|
||||
export function importTeacher(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/jys/teacher/import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
timeout: 60000
|
||||
})
|
||||
}
|
||||
|
||||
/** 单个教员补建/对齐系统账号 POST /jys/teacher/sync-user */
|
||||
export function syncTeacherUser(jybh, params) {
|
||||
return request({
|
||||
url: '/jys/teacher/sync-user',
|
||||
method: 'post',
|
||||
params: Object.assign({ jybh }, params || {})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对齐到用户表:为无账号教员批量建账号
|
||||
* POST /jys/teacher/align-user body: { ids }(不传 = 全部无账号教员)
|
||||
*/
|
||||
export function alignTeacherUsers(ids) {
|
||||
return request({
|
||||
url: '/jys/teacher/align-user',
|
||||
method: 'post',
|
||||
data: ids && ids.length ? { ids } : {},
|
||||
timeout: 180000
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
width="640px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="align-summary">
|
||||
<span>新建 <b class="ok">{{ result.createCount || 0 }}</b></span>
|
||||
<span>复用 <b>{{ result.reuseCount || 0 }}</b></span>
|
||||
<span>跳过 <b class="warn">{{ result.skipCount || 0 }}</b></span>
|
||||
<span>失败 <b class="err">{{ result.failCount || 0 }}</b></span>
|
||||
</div>
|
||||
<div class="align-detail">
|
||||
<template v-for="sec in sections">
|
||||
<div v-if="sec.list && sec.list.length" :key="sec.key" class="align-sec">
|
||||
<div class="sec-title" :class="sec.cls">{{ sec.label }}({{ sec.list.length }})</div>
|
||||
<div class="sec-body">
|
||||
<div v-for="(item, idx) in sec.list" :key="sec.key + idx">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-if="isEmpty" description="无需处理" :image-size="60" />
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" @click="visible = false">关 闭</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 组织/账号对齐结果清单弹窗。
|
||||
* result: { createCount, reuseCount, skipCount, failCount, createList, reuseList, skipList, failList, message }
|
||||
*/
|
||||
export default {
|
||||
name: 'OrgSyncResultDialog',
|
||||
props: {
|
||||
title: { type: String, default: '对齐结果' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
result: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sections() {
|
||||
const r = this.result || {}
|
||||
return [
|
||||
{ key: 'create', label: '新建', cls: 'ok', list: r.createList },
|
||||
{ key: 'reuse', label: '复用', cls: '', list: r.reuseList },
|
||||
{ key: 'skip', label: '跳过', cls: 'warn', list: r.skipList },
|
||||
{ key: 'fail', label: '失败', cls: 'err', list: r.failList }
|
||||
]
|
||||
},
|
||||
isEmpty() {
|
||||
const r = this.result || {}
|
||||
return !(r.createCount || r.reuseCount || r.skipCount || r.failCount)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(result) {
|
||||
this.result = result || {}
|
||||
this.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.align-summary {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
|
||||
b { color: #409eff; }
|
||||
b.ok { color: #67c23a; }
|
||||
b.warn { color: #e6a23c; }
|
||||
b.err { color: #f56c6c; }
|
||||
}
|
||||
|
||||
.align-detail {
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.align-sec {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.sec-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
color: #303133;
|
||||
|
||||
&.ok { color: #67c23a; }
|
||||
&.warn { color: #e6a23c; }
|
||||
&.err { color: #f56c6c; }
|
||||
}
|
||||
|
||||
.sec-body {
|
||||
padding: 8px 10px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -75,9 +75,16 @@
|
||||
<el-table-column prop="jssj" label="结束时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||
<el-table-column prop="cjsj" label="创建时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||
<el-table-column prop="jcxqscsj" label="教材需求生成时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="small" @click="handleDetail(row)">详情</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
icon="el-icon-download"
|
||||
:loading="exportingBh === row.bh"
|
||||
@click="handleExport(row)"
|
||||
>导出</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -127,8 +134,9 @@
|
||||
* 仅提供 分页查询 list + 详情 get?bh=,不做增删改发(避免与教学任务计划管理页重复操作同一实体)
|
||||
* 增删改发统一在 src/views/teachOffice/taskPlan/index.vue(教学任务计划管理)完成
|
||||
*/
|
||||
import { listTeachingTask, getTeachingTask } from '@/api/teachBusiness/teachingTask'
|
||||
import { listTeachingTask, getTeachingTask, exportTaskBook } from '@/api/teachBusiness/teachingTask'
|
||||
import { listAllSemester } from '@/api/teachBusiness/semester'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
export default {
|
||||
name: 'TeachingTask',
|
||||
@@ -156,7 +164,10 @@ export default {
|
||||
// ==================== 3. 详情 ====================
|
||||
detailVisible: false,
|
||||
detailLoading: false,
|
||||
detailData: {}
|
||||
detailData: {},
|
||||
|
||||
// ==================== 4. 导出 ====================
|
||||
exportingBh: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -242,6 +253,18 @@ export default {
|
||||
}).catch(() => {
|
||||
this.detailLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/* ---------- 导出教学任务书 ---------- */
|
||||
handleExport(row) {
|
||||
if (this.exportingBh) return
|
||||
this.exportingBh = row.bh
|
||||
exportTaskBook(row.bh).then(blob => {
|
||||
saveAs(blob, `教学任务书_${row.rwmc || row.bh}.xlsx`)
|
||||
this.$message.success('导出成功')
|
||||
}).catch(() => {}).finally(() => {
|
||||
this.exportingBh = ''
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleDownloadTemplate">模板下载</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="el-icon-refresh" size="mini" :loading="alignLoading" @click="handleAlignDepts">对齐组织部门</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
@@ -117,6 +120,22 @@
|
||||
<el-form-item label="备注" prop="bz">
|
||||
<el-input v-model="form.bz" type="textarea" placeholder="请输入备注" :rows="3" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-divider content-position="left">组织同步</el-divider>
|
||||
<template v-if="isAdd">
|
||||
<el-form-item label="同步创建部门">
|
||||
<el-checkbox v-model="form.syncDept">在「学员综合管理 › 教务处」下创建同名组织部门</el-checkbox>
|
||||
<div class="sync-tip">部门名称跟随教研室名称(超30字用简称);创建后可在组织管理中调整负责人/排序</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-form-item label="组织部门">
|
||||
<el-tag v-if="form.bmbh" type="success" size="small">已关联(教务处下)</el-tag>
|
||||
<span v-else class="sync-tip">未关联,保存后可用「对齐组织部门」补建</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.bmbh" label="重命名同步">
|
||||
<el-checkbox v-model="form.syncDeptName">保存时将教研室名称/序号同步到组织部门</el-checkbox>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
@@ -143,21 +162,27 @@
|
||||
<span>仅允许导入 xls、xlsx 格式文件。</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
<el-checkbox v-model="importSyncDept" class="import-sync">同时创建组织部门(挂在教务处下)</el-checkbox>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitImport">确 定</el-button>
|
||||
<el-button @click="importOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 对齐结果清单弹窗 -->
|
||||
<org-sync-result-dialog ref="alignResult" title="对齐组织部门结果" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listOffice, addOffice, updateOffice, disableOffice, downloadOfficeTemplate } from "@/api/teachOffice/office"
|
||||
import { listOffice, addOffice, updateOffice, disableOffice, downloadOfficeTemplate, alignDepts } from "@/api/teachOffice/office"
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { saveAs } from 'file-saver'
|
||||
import OrgSyncResultDialog from '@/components/OrgSyncResultDialog'
|
||||
|
||||
export default {
|
||||
name: "Office",
|
||||
components: { OrgSyncResultDialog },
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
@@ -178,6 +203,10 @@ export default {
|
||||
isAdd: true,
|
||||
// 是否显示导入弹出层
|
||||
importOpen: false,
|
||||
// 导入时是否同步创建组织部门
|
||||
importSyncDept: true,
|
||||
// 对齐执行中
|
||||
alignLoading: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -202,7 +231,8 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
importUrl() {
|
||||
return process.env.VUE_APP_BASE_API + '/jys/office/import'
|
||||
const base = process.env.VUE_APP_BASE_API + '/jys/office/import'
|
||||
return this.importSyncDept ? base + '?syncDept=true' : base
|
||||
},
|
||||
uploadHeaders() {
|
||||
return { Authorization: 'Bearer ' + getToken() }
|
||||
@@ -255,33 +285,56 @@ export default {
|
||||
jc: row.jc,
|
||||
bx: row.bx,
|
||||
xh: row.xh,
|
||||
bz: row.bz
|
||||
bz: row.bz,
|
||||
bmbh: row.bmbh,
|
||||
syncDeptName: true
|
||||
}
|
||||
this.open = true
|
||||
this.title = "编辑教研室"
|
||||
},
|
||||
/** 停用按钮 */
|
||||
/** 停用按钮:已关联部门时询问是否联动停用 */
|
||||
handleDisable(row) {
|
||||
const jysdh = row.jysdh
|
||||
this.$modal.confirm('确认停用教研室【' + row.jysmc + '】吗?').then(() => {
|
||||
return disableOffice(jysdh)
|
||||
}).then(() => {
|
||||
if (!row.bmbh) return false
|
||||
return this.$modal.confirm('该教研室已关联组织部门,是否一并停用?')
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
}).then(syncDept => {
|
||||
if (syncDept === undefined) return
|
||||
return disableOffice(jysdh, syncDept === true)
|
||||
}).then(res => {
|
||||
if (res === undefined) return
|
||||
this.getList()
|
||||
this.$modal.msgSuccess("停用成功")
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 批量对齐组织部门(直接执行,幂等) */
|
||||
handleAlignDepts() {
|
||||
this.$modal.confirm('将为全部教研室创建/复用「教务处」下的同名组织部门,已关联的自动跳过。是否继续?').then(() => {
|
||||
this.alignLoading = true
|
||||
return alignDepts()
|
||||
}).then(response => {
|
||||
this.alignLoading = false
|
||||
if (response === undefined) return
|
||||
this.$refs.alignResult.open(response.data)
|
||||
this.getList()
|
||||
}).catch(() => {
|
||||
this.alignLoading = false
|
||||
})
|
||||
},
|
||||
/** 提交表单 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
if (this.isAdd) {
|
||||
addOffice(this.form).then(response => {
|
||||
addOffice(this.form, this.form.syncDept).then(response => {
|
||||
this.$modal.msgSuccess("新增成功")
|
||||
this.open = false
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
} else {
|
||||
updateOffice(this.form).then(response => {
|
||||
updateOffice(this.form, this.form.bmbh ? this.form.syncDeptName : undefined).then(response => {
|
||||
this.$modal.msgSuccess("修改成功")
|
||||
this.open = false
|
||||
this.getList()
|
||||
@@ -298,7 +351,9 @@ export default {
|
||||
jc: undefined,
|
||||
bx: undefined,
|
||||
xh: undefined,
|
||||
bz: undefined
|
||||
bz: undefined,
|
||||
syncDept: true,
|
||||
syncDeptName: true
|
||||
}
|
||||
this.resetForm("form")
|
||||
},
|
||||
@@ -360,5 +415,15 @@ export default {
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sync-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.import-sync {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleNew">新建</el-button>
|
||||
<el-button type="danger" plain icon="el-icon-delete" @click="handleDeleteSelected">删除所选</el-button>
|
||||
</div>
|
||||
<div class="right-group">
|
||||
<el-button icon="el-icon-upload2" @click="openImportDialog">导入</el-button>
|
||||
<el-button icon="el-icon-download" @click="handleExport">导出</el-button>
|
||||
<el-button icon="el-icon-document" @click="handleDownloadTemplate">下载模板</el-button>
|
||||
<el-button type="success" plain icon="el-icon-refresh" :loading="alignLoading" @click="handleAlignUsers">对齐到用户表</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card shadow="never" class="table-card">
|
||||
@@ -65,7 +71,14 @@
|
||||
<el-table-column label="虚实类型" width="80" align="center">
|
||||
<template slot-scope="{ row }">{{ fmtXslx(row.xslx) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<el-table-column label="账号" width="130" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.yhbh" type="success" size="mini">{{ row.jybh }}</el-tag>
|
||||
<el-button v-else type="text" size="small" icon="el-icon-link"
|
||||
@click="handleCreateAccount(row)">创建账号</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="small" icon="el-icon-view" @click="handleViewAttribute(row)">查看属性</el-button>
|
||||
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
|
||||
@@ -347,6 +360,42 @@
|
||||
<el-form-item label="发表论文"><el-input v-model="addForm.attributes.fblw" placeholder="选填" /></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">登录账号</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="同时创建账号">
|
||||
<el-checkbox v-model="addForm.createUser">创建系统账号(登录名默认=教员工号,角色=教员)</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template v-if="addForm.createUser">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="登录名">
|
||||
<el-input v-model="addForm.loginName" placeholder="留空则取教员工号" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="初始密码">
|
||||
<el-input v-model="addForm.password" placeholder="默认 Jw@123456" show-password maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="addForm.roleId" style="width:100%" :loading="roleLoading">
|
||||
<el-option v-for="r in roleOptions" :key="r.roleId" :label="r.roleName" :value="r.roleId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属组织">
|
||||
<span class="org-path">{{ orgPathText }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="addDialogVisible = false">取消</el-button>
|
||||
@@ -621,6 +670,36 @@
|
||||
</template>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 教员数据导入弹窗 -->
|
||||
<el-dialog title="教员数据导入" :visible.sync="importDialog.visible" width="560px" append-to-body
|
||||
:close-on-click-modal="false">
|
||||
<el-alert type="info" :closable="false" show-icon class="import-tip"
|
||||
title="导入只写教员档案,不创建登录账号;需要账号时回列表点【对齐到用户表】。" />
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="导入模板">
|
||||
<el-button icon="el-icon-download" :loading="importDialog.downloading" @click="handleDownloadTemplate">
|
||||
下载导入模板
|
||||
</el-button>
|
||||
<span class="tip-text">红色表头列为必填,详见模板「填写说明」sheet</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据文件">
|
||||
<input ref="importFileInput" type="file" accept=".xls,.xlsx" style="display: none"
|
||||
@change="handleImportFileChange" />
|
||||
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
|
||||
<span class="file-name">{{ importDialog.fileName || '未选择任何文件' }}</span>
|
||||
<el-button v-if="importDialog.file" type="text" class="danger-text" @click="clearImportFile">清除</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="importDialog.visible = false">关 闭</el-button>
|
||||
<el-button type="primary" :loading="importDialog.importing" :disabled="!importDialog.file"
|
||||
@click="handleImport">开始导入</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 对齐结果清单弹窗 -->
|
||||
<org-sync-result-dialog ref="alignResult" title="对齐结果" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -632,13 +711,22 @@ import {
|
||||
updateTeacher,
|
||||
disableTeacher,
|
||||
getTeacherAttribute,
|
||||
updateTeacherAttribute
|
||||
updateTeacherAttribute,
|
||||
downloadTeacherTemplate,
|
||||
exportTeacher,
|
||||
importTeacher,
|
||||
syncTeacherUser,
|
||||
alignTeacherUsers
|
||||
} from '@/api/teachOffice/teacher'
|
||||
import { listOffice } from '@/api/teachOffice/office'
|
||||
import { getDicts } from '@/api/system/dict/data'
|
||||
import { listRole } from '@/api/system/role'
|
||||
import { saveAs } from 'file-saver'
|
||||
import OrgSyncResultDialog from '@/components/OrgSyncResultDialog'
|
||||
|
||||
export default {
|
||||
name: 'Teacher',
|
||||
components: { OrgSyncResultDialog },
|
||||
data() {
|
||||
return {
|
||||
// 查询条件(仅后端 JYBMapper.xml 支持的字段:jyxm 模糊 / jysdh 等值 / zc 模糊 / jylb 等值)
|
||||
@@ -711,13 +799,36 @@ export default {
|
||||
dslbOptions: [],
|
||||
// 教研室下拉(新增教员时选择所属教研室)
|
||||
officeOptions: [],
|
||||
officeLoading: false
|
||||
officeLoading: false,
|
||||
// 角色下拉(建账号时可选角色,默认教员 101)
|
||||
roleOptions: [],
|
||||
roleLoading: false,
|
||||
// 导入弹窗
|
||||
importDialog: {
|
||||
visible: false,
|
||||
downloading: false,
|
||||
importing: false,
|
||||
file: null,
|
||||
fileName: ''
|
||||
},
|
||||
// 对齐执行中
|
||||
alignLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 新增弹窗里所选教研室对应的组织路径展示 */
|
||||
orgPathText() {
|
||||
const office = this.officeOptions.find(o => o.jysdh === this.addForm.teacher.jysdh)
|
||||
if (!office) return '请选择教研室后自动显示'
|
||||
const suffix = office.bmbh ? '' : '(尚未建部门,保存时自动创建)'
|
||||
return '学员综合管理 › 教务处 › ' + office.jysmc + suffix
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchList()
|
||||
this.loadDicts()
|
||||
this.loadOfficeOptions()
|
||||
this.loadRoleOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 统一格式化后端 LocalDateTime(去除 T、截断到秒) */
|
||||
@@ -772,7 +883,7 @@ export default {
|
||||
// 已停用(ty=1)的教研室不再参与新建教员
|
||||
if (!jysdh || seen[jysdh] || Number(row.ty) === 1) return
|
||||
seen[jysdh] = true
|
||||
options.push({ jysdh, jysmc: row.jysmc || jysdh })
|
||||
options.push({ jysdh, jysmc: row.jysmc || jysdh, bmbh: row.bmbh })
|
||||
})
|
||||
this.officeOptions = options
|
||||
}).catch(() => {
|
||||
@@ -806,7 +917,11 @@ export default {
|
||||
xl: '', xw: '', jl: 0, jljsrq: '', zr: 0, zw: '', zwsj: '', jszw: '',
|
||||
jszwsj: '', jsdj: '', jsdjsj: '', jxwz: '', jxwzsj: '', cssj: '',
|
||||
rwgzsj: '', rdzjl: '', fblw: ''
|
||||
}
|
||||
},
|
||||
createUser: true,
|
||||
loginName: '',
|
||||
password: '',
|
||||
roleId: 101
|
||||
}
|
||||
},
|
||||
|
||||
@@ -872,6 +987,11 @@ export default {
|
||||
this.$message.info('后端暂未提供该接口')
|
||||
},
|
||||
|
||||
// ==================== 导入弹窗 ====================
|
||||
openImportDialog() {
|
||||
this.importDialog.visible = true
|
||||
},
|
||||
|
||||
// ==================== 新增 ====================
|
||||
handleNew() {
|
||||
this.addForm = this.createEmptyAddForm()
|
||||
@@ -886,7 +1006,11 @@ export default {
|
||||
if (!valid) return
|
||||
const dto = {
|
||||
teacher: this.buildTeacherPayload(this.addForm.teacher),
|
||||
attributes: this.buildAttributePayload(this.addForm.attributes)
|
||||
attributes: this.buildAttributePayload(this.addForm.attributes),
|
||||
createUser: this.addForm.createUser,
|
||||
loginName: this.addForm.loginName,
|
||||
password: this.addForm.password,
|
||||
roleId: this.addForm.roleId
|
||||
}
|
||||
this.addSaving = true
|
||||
addTeacher(dto).then(() => {
|
||||
@@ -1062,6 +1186,135 @@ export default {
|
||||
}).finally(() => {
|
||||
this.attributeSaving = false
|
||||
})
|
||||
},
|
||||
|
||||
// ==================== 建账号 / 对齐到用户表 ====================
|
||||
handleCreateAccount(row) {
|
||||
const h = this.$createElement
|
||||
this.$msgbox({
|
||||
title: '为教员建账号',
|
||||
message: h('div', null, [
|
||||
h('p', null, '教员:' + row.jyxm + '(' + row.jybh + ')'),
|
||||
h('p', null, '用户名 = 教员工号,手机号 = 联系方式,角色 = 教员(101)'),
|
||||
h('p', { style: 'color:#E6A23C' }, '初始密码将生成随机密码,请到用户管理重置后发本人')
|
||||
]),
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '创建',
|
||||
cancelButtonText: '取消'
|
||||
}).then(() => {
|
||||
syncTeacherUser(row.jybh).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success('账号创建成功(用户编号:' + res.data + ')')
|
||||
this.fetchList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '创建失败')
|
||||
}
|
||||
}).catch(() => {})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
handleAlignUsers() {
|
||||
this.$confirm('扫描在职且未挂账号的教员,自动创建登录账号并回写用户编号?(已建账号的教员跳过,不重复建号)', '对齐到用户表', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '开始对齐',
|
||||
cancelButtonText: '取消'
|
||||
}).then(() => {
|
||||
this.alignLoading = true
|
||||
alignTeacherUsers().then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
this.$refs.alignResult.open(res.data)
|
||||
this.fetchList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '对齐失败')
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error('对齐失败', e)
|
||||
this.$message.error(e.message || '对齐失败')
|
||||
}).finally(() => {
|
||||
this.alignLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
// ==================== 导入 / 导出 / 模板 ====================
|
||||
handleDownloadTemplate() {
|
||||
this.importDialog.downloading = true
|
||||
downloadTeacherTemplate().then(res => {
|
||||
const blob = res instanceof Blob ? res : new Blob([res])
|
||||
saveAs(blob, '教员导入模板.xlsx')
|
||||
}).catch(e => {
|
||||
console.error('模板下载失败', e)
|
||||
this.$message.error(e.message || '模板下载失败')
|
||||
}).finally(() => {
|
||||
this.importDialog.downloading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleExport() {
|
||||
exportTeacher({ ...this.searchForm }).then(res => {
|
||||
const blob = res instanceof Blob ? res : new Blob([res])
|
||||
saveAs(blob, '教员档案.xlsx')
|
||||
}).catch(e => {
|
||||
console.error('导出失败', e)
|
||||
this.$message.error(e.message || '导出失败')
|
||||
})
|
||||
},
|
||||
|
||||
handleChooseFile() {
|
||||
this.$refs.importFileInput.click()
|
||||
},
|
||||
|
||||
handleImportFileChange(e) {
|
||||
const file = e.target.files && e.target.files[0]
|
||||
if (!file) return
|
||||
if (!/\.(xls|xlsx)$/i.test(file.name)) {
|
||||
this.$message.error('请选择 xls/xlsx 文件')
|
||||
e.target.value = ''
|
||||
return
|
||||
}
|
||||
this.importDialog.file = file
|
||||
this.importDialog.fileName = file.name
|
||||
e.target.value = ''
|
||||
},
|
||||
|
||||
clearImportFile() {
|
||||
this.importDialog.file = null
|
||||
this.importDialog.fileName = ''
|
||||
},
|
||||
|
||||
handleImport() {
|
||||
if (!this.importDialog.file) {
|
||||
this.$message.warning('请选择数据文件')
|
||||
return
|
||||
}
|
||||
this.importDialog.importing = true
|
||||
importTeacher(this.importDialog.file).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success(res.msg || '导入完成')
|
||||
this.importDialog.visible = false
|
||||
this.clearImportFile()
|
||||
this.fetchList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '导入失败')
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error('导入失败', e)
|
||||
this.$message.error(e.message || '导入失败')
|
||||
}).finally(() => {
|
||||
this.importDialog.importing = false
|
||||
})
|
||||
},
|
||||
|
||||
// ==================== 角色下拉 ====================
|
||||
loadRoleOptions() {
|
||||
this.roleLoading = true
|
||||
listRole({ pageNum: 1, pageSize: 200, status: '0' }).then(res => {
|
||||
this.roleOptions = res.rows || res.data || []
|
||||
}).catch(e => {
|
||||
console.error('加载角色列表失败', e)
|
||||
}).finally(() => {
|
||||
this.roleLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1128,6 +1381,42 @@ export default {
|
||||
.danger-text {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.account-section {
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.account-tip {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin: -6px 0 8px;
|
||||
}
|
||||
|
||||
.org-path {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.import-tip {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
margin-left: 8px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user