mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-23 17:55:37 +08:00
Compare commits
25 Commits
251114-fea
...
251114-fea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2e188c0e6 | ||
|
|
a9b36884c3 | ||
|
|
e176769174 | ||
|
|
5f518b713a | ||
|
|
b2395fd3a2 | ||
|
|
100ad78c9b | ||
|
|
4831da0c47 | ||
|
|
6bee4e8e87 | ||
|
|
4ebd8f5225 | ||
|
|
79f068198d | ||
|
|
6cfd0e8720 | ||
|
|
ed672905f2 | ||
|
|
2f1ea68c70 | ||
|
|
7a28e362f7 | ||
|
|
e12437484b | ||
|
|
78be53013b | ||
|
|
c9c714ddfa | ||
|
|
ffeb5bf0b6 | ||
|
|
f680e1c394 | ||
|
|
ef926e6418 | ||
|
|
2d06dde984 | ||
|
|
5cd85a3e15 | ||
|
|
89364b1aef | ||
|
|
a34f87410f | ||
|
|
361675c369 |
@@ -46,6 +46,14 @@ const getUserInfoById = function(id) {
|
||||
return ajax.postJson(baseURL,'/user/list',{id});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全量机构树
|
||||
* GET /userbasic/organization/all/tree
|
||||
*/
|
||||
const getAllOrgTree = function () {
|
||||
return ajax.get(baseURL, '/organization/all/tree');
|
||||
};
|
||||
|
||||
/**
|
||||
* https://u-pre.boe.com/userbasic/audience/userAudiences
|
||||
* 【当前代码中未查询到】获取当前用户受众信息
|
||||
@@ -111,6 +119,7 @@ const selectUser = function(keyword = '') {
|
||||
return ajax.postJson(baseURL,'/user/selectuser',{ keyword });
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
userParentOrg,
|
||||
findOrgsByKeyword,
|
||||
@@ -125,5 +134,6 @@ export default {
|
||||
getUsersByIds,
|
||||
updateUser,
|
||||
logout,
|
||||
getAllOrgTree,
|
||||
selectUser
|
||||
}
|
||||
|
||||
@@ -367,6 +367,15 @@ const deleteSignUp=function(id,courseId){
|
||||
return ajax.post(`/xboe/school/study/course/delete-signup?id=${id}&couserId=${courseId}`);
|
||||
}
|
||||
|
||||
/*
|
||||
删除课程学习记录
|
||||
@param id 学习id
|
||||
@param courseId 课程id 用于删除课程报名信息和修改课程学习人数
|
||||
*/
|
||||
const deleteNewSignUp=function(data){
|
||||
return ajax.post(`/xboe/school/study/course/delete-signup`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 二次查询 用于个人主页/他人主页
|
||||
* {
|
||||
@@ -432,6 +441,7 @@ export default {
|
||||
signup,
|
||||
findSignup,
|
||||
deleteSignup,
|
||||
deleteNewSignUp,
|
||||
importSignup,
|
||||
countSignup,
|
||||
studyIndex,
|
||||
|
||||
@@ -402,7 +402,7 @@ li{
|
||||
|
||||
.el-message-box__header {
|
||||
// padding: 0 0 16px 0;
|
||||
padding: 2px 8px 2px 16px;
|
||||
padding: 8px 8px 2px 16px;
|
||||
display: block;
|
||||
|
||||
.el-message-box__title {
|
||||
@@ -412,7 +412,7 @@ li{
|
||||
}
|
||||
|
||||
.el-message-box__headerbtn {
|
||||
top: 8px;
|
||||
top: 14px;
|
||||
right: 10px;
|
||||
.el-message-box__close {
|
||||
font-size: 22px;
|
||||
@@ -461,17 +461,47 @@ li{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
margin-top: 0px;
|
||||
padding-bottom: 20px;
|
||||
margin-top: 8px;
|
||||
|
||||
.el-button {
|
||||
width: 70px;
|
||||
width: 78px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
padding: 0 18px;
|
||||
&:nth-child(2){
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-button--default {
|
||||
color: rgba(0, 0, 0, .3);
|
||||
border-color: rgba(0, 0, 0, .3);
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
background-color: #3b7cff;
|
||||
border-color: #3b7cff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.common-course-dialog {
|
||||
.el-dialog__footer {
|
||||
padding: 10px 20px 16px;
|
||||
.el-button {
|
||||
width: 78px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
padding: 0 18px;
|
||||
&:nth-child(2){
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
}
|
||||
.el-button--default {
|
||||
color: rgba(0, 0, 0, .3);
|
||||
border-color: rgba(0, 0, 0, .3);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
class="common-course-dialog"
|
||||
title="置顶排序"
|
||||
:visible.sync="dialogVisible"
|
||||
custom-class="g-dialog top-course-sorter-dialog"
|
||||
width="820px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleClosed"
|
||||
@@ -13,10 +13,9 @@
|
||||
v-if="topList.length"
|
||||
ref="sortTable"
|
||||
:data="topList"
|
||||
class="top-course-sorter__table"
|
||||
class="top-course-sorter__table use-table"
|
||||
row-key="id"
|
||||
:row-class-name="getRowClassName"
|
||||
:header-cell-style="{ background: '#f5f7fa', color: '#303133', fontWeight: '600' }"
|
||||
>
|
||||
<el-table-column width="60" align="center">
|
||||
<template slot-scope="scope">
|
||||
@@ -262,39 +261,8 @@ export default {
|
||||
min-height: 200px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.top-course-sorter__table {
|
||||
::v-deep .el-table__row {
|
||||
cursor: move;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: #f9fbff;
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
opacity: 0.7;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
&.drag-over {
|
||||
border-top: 2px solid #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-table__body-wrapper {
|
||||
.el-table__row {
|
||||
&:last-child {
|
||||
.el-table__cell {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-table__cell {
|
||||
padding: 12px 0;
|
||||
}
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 10px 20px 20px;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
|
||||
@@ -1,20 +1,41 @@
|
||||
<template>
|
||||
<el-dialog title="添加报名" :visible.sync="visibleSync" width="1200px" top="8vh" append-to-body @close="handleClose">
|
||||
<el-dialog title="添加报名" class="common-course-dialog" :visible.sync="visibleSync" width="1000px" top="8vh" append-to-body @close="handleClose">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="从受众中选择" name="quick">
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="tab-search">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
placeholder="姓名"
|
||||
size="small"
|
||||
<el-select
|
||||
class="member-select"
|
||||
ref="memberSelect"
|
||||
v-model="memberSelected"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
class="input"
|
||||
@keyup.enter.native="onSearch"
|
||||
/>
|
||||
<el-button type="primary" size="small" @click="onSearch">查询</el-button>
|
||||
<el-button type="primary" size="small" @click="onReset">重置</el-button>
|
||||
reserve-keyword
|
||||
placeholder="姓名"
|
||||
:multiple-limit="5"
|
||||
:remote-method="remoteSearchMember"
|
||||
:loading="memberLoading"
|
||||
@input.native="limitMemberInput"
|
||||
@visible-change="handleMemberVisibleChange"
|
||||
@change="handleMemberChange"
|
||||
@clear="handleMemberClear"
|
||||
size="small"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in memberOptions"
|
||||
:key="item.userId"
|
||||
:label="item.name"
|
||||
:value="item.userId"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
<span v-if="item.code" class="option-code">({{ item.code }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" size="small" @click="onSearch" style="margin-left: 8px;">查询</el-button>
|
||||
<el-button size="small" @click="onReset" style="margin-left: 4px;">重置</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
class="use-table"
|
||||
@@ -51,6 +72,7 @@
|
||||
|
||||
<script>
|
||||
import { fetchAudienceList, saveStu } from "@/api/signup/commonStudent";
|
||||
import apiUserbasic from "@/api/boe/userbasic.js";
|
||||
|
||||
export default {
|
||||
name: "AudienceModal",
|
||||
@@ -74,6 +96,10 @@ export default {
|
||||
total: 0,
|
||||
pageNo: 1,
|
||||
selectedRows: [],
|
||||
// 成员远程搜索多选
|
||||
memberSelected: [],
|
||||
memberOptions: [],
|
||||
memberLoading: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -89,15 +115,18 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
resetAndFetch() {
|
||||
this.keyword = "";
|
||||
this.pageNo = 1;
|
||||
this.selectedRows = [];
|
||||
this.keyword = "";
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
this.fetchList();
|
||||
},
|
||||
fetchList() {
|
||||
this.loading = true;
|
||||
fetchAudienceList({
|
||||
keyword: this.keyword,
|
||||
// memberList 为多选成员 ID 列表
|
||||
memberList: this.memberSelected || [],
|
||||
pageNo: this.pageNo,
|
||||
pageSize: this.pageSize,
|
||||
audienceIdList: this.audienceIds || [],
|
||||
@@ -117,6 +146,8 @@ export default {
|
||||
},
|
||||
onReset() {
|
||||
this.keyword = "";
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
this.onSearch();
|
||||
},
|
||||
onPageChange(page) {
|
||||
@@ -126,6 +157,79 @@ export default {
|
||||
onSelectionChange(list) {
|
||||
this.selectedRows = list;
|
||||
},
|
||||
// ===== 成员远程搜索,多选逻辑(参考 ManageListRemote.vue 的创建人筛选) =====
|
||||
async remoteSearchMember(keyword) {
|
||||
const limited = (keyword || "").slice(0, 50);
|
||||
if (this.$refs.memberSelect && this.$refs.memberSelect.query !== limited) {
|
||||
this.$refs.memberSelect.query = limited;
|
||||
this.$nextTick(() => {
|
||||
if (
|
||||
this.$refs.memberSelect &&
|
||||
this.$refs.memberSelect.$refs &&
|
||||
this.$refs.memberSelect.$refs.input
|
||||
) {
|
||||
this.$refs.memberSelect.$refs.input.value = limited;
|
||||
}
|
||||
});
|
||||
}
|
||||
const query = limited.trim();
|
||||
if (!query || query.length <= 1) {
|
||||
this.memberOptions = [];
|
||||
return;
|
||||
}
|
||||
this.memberLoading = true;
|
||||
try {
|
||||
const res = await apiUserbasic.selectUser(query);
|
||||
if (res && res.status === 200) {
|
||||
const resultList = res.result || [];
|
||||
this.memberOptions = resultList
|
||||
.map((item) => this.formatMemberItem(item))
|
||||
.filter((item) => item.userId);
|
||||
} else {
|
||||
this.memberOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
this.memberOptions = [];
|
||||
} finally {
|
||||
this.memberLoading = false;
|
||||
}
|
||||
},
|
||||
formatMemberItem(item = {}) {
|
||||
return {
|
||||
userId: item.id,
|
||||
name: item.realName,
|
||||
code: item.userNo,
|
||||
};
|
||||
},
|
||||
handleMemberChange(value = []) {
|
||||
// 限制最多 5 个,保持与 ManageListRemote 中创建人筛选一致
|
||||
this.memberSelected = (value || []).slice(0, 5);
|
||||
},
|
||||
handleMemberClear() {
|
||||
this.memberSelected = [];
|
||||
this.memberOptions = [];
|
||||
},
|
||||
handleMemberVisibleChange(visible) {
|
||||
if (!visible) return;
|
||||
const select = this.$refs.memberSelect;
|
||||
const query = (select && select.query) || "";
|
||||
if (!query) {
|
||||
this.memberOptions = [];
|
||||
}
|
||||
},
|
||||
limitMemberInput(event) {
|
||||
const limited =
|
||||
(event && event.target && event.target.value
|
||||
? event.target.value
|
||||
: ""
|
||||
).slice(0, 50);
|
||||
if (event && event.target && event.target.value !== limited) {
|
||||
event.target.value = limited;
|
||||
}
|
||||
if (this.$refs.memberSelect) {
|
||||
this.$refs.memberSelect.query = limited;
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.visibleSync = false;
|
||||
},
|
||||
@@ -181,29 +285,5 @@ export default {
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
::v-deep .el-dialog__footer {
|
||||
|
||||
.el-button {
|
||||
min-width: 120px;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.el-button--default {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
background-color: #3b7cff;
|
||||
border-color: #3b7cff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="visible">
|
||||
<el-dialog :visible="visible" :title="dialogTitle" width="1200px" top="8vh" append-to-body destroy-on-close
|
||||
class="signup-dialog" @close="handleClose">
|
||||
<el-dialog class="signup-dialog common-course-dialog" :visible="visible" :title="dialogTitle" width="1200px" top="8vh" append-to-body destroy-on-close
|
||||
@close="handleClose">
|
||||
<div class="signup-wrap">
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- <el-tab-pane v-if="infoType" label="项目内学员" name="project">
|
||||
@@ -35,7 +35,7 @@
|
||||
<el-button type="primary" size="small" @click="onSearchStu">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" @click="resetStu">重置</el-button>
|
||||
<el-button size="small" @click="resetStu">重置</el-button>
|
||||
</div>
|
||||
<div class="split">
|
||||
<div class="left-tree">
|
||||
@@ -43,8 +43,15 @@
|
||||
@node-click="onOrgSelect" />
|
||||
</div>
|
||||
<div class="table-area">
|
||||
<el-table class="use-table" border :data="stuTable.list"
|
||||
@selection-change="onStuSelectionChange" :row-key="row => row.id || row.userId">
|
||||
<el-table
|
||||
ref="stuTable"
|
||||
class="use-table"
|
||||
border
|
||||
:data="stuTable.list"
|
||||
@selection-change="onStuSelectionChange"
|
||||
:row-key="row => row.id || row.userId"
|
||||
:reserve-selection="true"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column prop="realName" label="姓名" width="120" />
|
||||
<el-table-column prop="userNo" label="工号" width="120" />
|
||||
@@ -70,7 +77,14 @@
|
||||
</el-button>
|
||||
<el-button size="small" @click="resetOrg">重置</el-button>
|
||||
</div>
|
||||
<el-tree class="org-tree" show-checkbox node-key="id" :data="orgList" :props="orgTreeProps" lazy
|
||||
<el-tree
|
||||
ref="orgTree"
|
||||
class="org-tree"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:data="orgList"
|
||||
:props="orgTreeProps"
|
||||
lazy
|
||||
:load="loadOrgNodeFull" @check-change="onOrgCheckChange" :default-checked-keys="selectedOrgKeys" />
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -83,8 +97,15 @@
|
||||
</el-button>
|
||||
<el-button size="small" @click="resetAudienceInfo">重置</el-button>
|
||||
</div>
|
||||
<el-table class="use-table" border :data="audienceTable.list" @selection-change="onAudienceSelectionChange"
|
||||
:row-key="row => row.id">
|
||||
<el-table
|
||||
ref="audienceTable"
|
||||
class="use-table"
|
||||
border
|
||||
:data="audienceTable.list"
|
||||
@selection-change="onAudienceSelectionChange"
|
||||
:row-key="row => row.id"
|
||||
:reserve-selection="true"
|
||||
>
|
||||
<el-table-column type="selection" width="50" />
|
||||
<el-table-column prop="audienceName" label="受众名称" min-width="220" />
|
||||
<el-table-column prop="totalMember" label="总人数" width="100" />
|
||||
@@ -104,7 +125,7 @@
|
||||
<div class="already">已选</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="{ 'max-height': screenHeight - 235 + 'px' }" style="overflow-y: auto">
|
||||
<div class="selected-area" :style="{ 'max-height': screenHeight - 235 + 'px' }" style="overflow-y: auto">
|
||||
<div class="selecteds" v-if="infoType">
|
||||
<div class="person">项目内学员</div>
|
||||
<div v-for="(item, i) in projectSelectRows" :key="item.id || item.studentId">
|
||||
@@ -309,6 +330,8 @@ export default {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.initData();
|
||||
} else {
|
||||
this.resetState();
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -318,6 +341,30 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 参考 CommonStudent.vue,在关闭弹窗时清空各种选中与搜索条件
|
||||
resetState() {
|
||||
this.projectParams.studentName = "";
|
||||
this.nameSearch = { keyword: "", departId: "" };
|
||||
this.audienceName = { keyword: "" };
|
||||
this.searchOrgName = { keyword: "", pageNo: 1, pageSize: 200 };
|
||||
this.projectSelectRows = [];
|
||||
this.stuSelectRows = [];
|
||||
this.auditSelectRows = [];
|
||||
this.deptList = [];
|
||||
this.selectedOrgKeys = [];
|
||||
this.member = false;
|
||||
this.dept = false;
|
||||
this.person = false;
|
||||
this.group = false;
|
||||
|
||||
// 清空表格勾选
|
||||
if (this.$refs.stuTable && this.$refs.stuTable.clearSelection) {
|
||||
this.$refs.stuTable.clearSelection();
|
||||
}
|
||||
if (this.$refs.audienceTable && this.$refs.audienceTable.clearSelection) {
|
||||
this.$refs.audienceTable.clearSelection();
|
||||
}
|
||||
},
|
||||
initData() {
|
||||
this.courseDetail = JSON.parse(sessionStorage.getItem("courseDetail") || "{}");
|
||||
this.projectParams = {
|
||||
@@ -374,6 +421,10 @@ export default {
|
||||
console.log("res", res);
|
||||
this.stuTable.list = res.data?.list || [];
|
||||
this.stuTable.total = res.data?.total || 0;
|
||||
// 根据右侧已选,恢复当前页的勾选状态
|
||||
this.$nextTick(() => {
|
||||
this.syncStuTableSelection();
|
||||
});
|
||||
});
|
||||
},
|
||||
onStuPageChange(page) {
|
||||
@@ -381,7 +432,22 @@ export default {
|
||||
this.onSearchStu();
|
||||
},
|
||||
onStuSelectionChange(list) {
|
||||
this.stuSelectRows = list;
|
||||
// Element 表格在翻页时会把当前页的 selection 通过 list 传进来
|
||||
// 为了保留其他页已选,这里做合并而不是直接覆盖
|
||||
const currentPageIds = this.stuTable.list.map((r) => r.id || r.userId);
|
||||
// 先保留非当前页的已选
|
||||
const otherPageSelected = this.stuSelectRows.filter(
|
||||
(r) => !currentPageIds.includes(r.id || r.userId)
|
||||
);
|
||||
const merged = [...otherPageSelected, ...list];
|
||||
// 根据 id/userId 去重
|
||||
const seen = new Set();
|
||||
this.stuSelectRows = merged.filter((r) => {
|
||||
const key = r.id || r.userId;
|
||||
if (!key || seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
},
|
||||
resetStu() {
|
||||
this.nameSearch = { keyword: "", departId: "" };
|
||||
@@ -444,6 +510,10 @@ export default {
|
||||
console.log('searchAudience', res);
|
||||
this.audienceTable.list = res.data?.list || [];
|
||||
this.audienceTable.total = res.data?.total || 0;
|
||||
// 根据右侧已选,恢复当前页的受众勾选状态
|
||||
this.$nextTick(() => {
|
||||
this.syncAudienceTableSelection();
|
||||
});
|
||||
});
|
||||
},
|
||||
onAudiencePageChange(page) {
|
||||
@@ -451,7 +521,50 @@ export default {
|
||||
this.searchAudience();
|
||||
},
|
||||
onAudienceSelectionChange(list) {
|
||||
this.auditSelectRows = list;
|
||||
const currentPageIds = this.audienceTable.list.map((r) => r.id);
|
||||
const otherPageSelected = this.auditSelectRows.filter(
|
||||
(r) => !currentPageIds.includes(r.id)
|
||||
);
|
||||
const merged = [...otherPageSelected, ...list];
|
||||
const seen = new Set();
|
||||
this.auditSelectRows = merged.filter((r) => {
|
||||
const key = r.id;
|
||||
if (!key || seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
},
|
||||
// 根据 stuSelectRows 恢复当前页表格里的勾选
|
||||
syncStuTableSelection() {
|
||||
if (!this.$refs.stuTable) return;
|
||||
const table = this.$refs.stuTable;
|
||||
if (!table.clearSelection || !table.toggleRowSelection) return;
|
||||
const selectedMap = new Set(
|
||||
(this.stuSelectRows || []).map((r) => r.id || r.userId)
|
||||
);
|
||||
table.clearSelection();
|
||||
this.stuTable.list.forEach((row) => {
|
||||
const key = row.id || row.userId;
|
||||
if (key && selectedMap.has(key)) {
|
||||
table.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 根据 auditSelectRows 恢复当前页受众表格里的勾选
|
||||
syncAudienceTableSelection() {
|
||||
if (!this.$refs.audienceTable) return;
|
||||
const table = this.$refs.audienceTable;
|
||||
if (!table.clearSelection || !table.toggleRowSelection) return;
|
||||
const selectedMap = new Set(
|
||||
(this.auditSelectRows || []).map((r) => r.id)
|
||||
);
|
||||
table.clearSelection();
|
||||
this.audienceTable.list.forEach((row) => {
|
||||
const key = row.id;
|
||||
if (key && selectedMap.has(key)) {
|
||||
table.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
resetAudienceInfo() {
|
||||
this.audienceName.keyword = "";
|
||||
@@ -467,13 +580,25 @@ export default {
|
||||
this.stuSelectRows = this.stuSelectRows.filter(
|
||||
(i) => (i.id || i.userId) !== (item.id || item.userId)
|
||||
);
|
||||
// 同步左侧快速选人表格的勾选状态
|
||||
this.$nextTick(() => {
|
||||
this.syncStuTableSelection();
|
||||
});
|
||||
},
|
||||
removeOrg(item) {
|
||||
this.deptList = this.deptList.filter((i) => i.id !== item.id);
|
||||
this.selectedOrgKeys = this.deptList.map((d) => d.id);
|
||||
// 同步左侧组织树的勾选状态
|
||||
if (this.$refs.orgTree && this.$refs.orgTree.setCheckedKeys) {
|
||||
this.$refs.orgTree.setCheckedKeys(this.selectedOrgKeys);
|
||||
}
|
||||
},
|
||||
removeAudience(item) {
|
||||
this.auditSelectRows = this.auditSelectRows.filter((i) => i.id !== item.id);
|
||||
// 同步左侧受众表格的勾选状态
|
||||
this.$nextTick(() => {
|
||||
this.syncAudienceTableSelection();
|
||||
});
|
||||
},
|
||||
submitAuth() {
|
||||
if (this.type === 2) {
|
||||
@@ -524,28 +649,6 @@ export default {
|
||||
background-color: #3b7cff;
|
||||
}
|
||||
}
|
||||
.signup-dialog ::v-deep .el-dialog__footer {
|
||||
|
||||
.el-button {
|
||||
min-width: 120px;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.el-button--default {
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
background-color: #3b7cff;
|
||||
border-color: #3b7cff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.signup-wrap {
|
||||
display: flex;
|
||||
@@ -596,8 +699,23 @@ export default {
|
||||
.right1 {
|
||||
border-left: 1px solid #f2f6fe;
|
||||
margin-left: 20px;
|
||||
max-width: 200px;
|
||||
|
||||
width: 200px;
|
||||
.selected-area {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background-color: #4284F7;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color:rgba(0, 0, 0, .1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 4px;
|
||||
background-color: #4284F7
|
||||
}
|
||||
}
|
||||
.onerow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -722,14 +722,18 @@ export default {
|
||||
this.getSignupList();
|
||||
},
|
||||
handleDeleteSignup(row) {
|
||||
this.$confirm(`<i class="el-icon-warning-outline"></i>确认删除${row.name || ''}的报名记录吗?`, '删除确认', {
|
||||
this.$confirm(`<i class="el-icon-warning-outline"></i>确定删除${row.name || ''}的报名记录吗?`, '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'warning',
|
||||
customClass: 'custom-confirm-dialog'
|
||||
}).then(() => {
|
||||
apicourseStudy.deleteSignUp(row.id, this.courseDetail.id)
|
||||
apicourseStudy.deleteNewSignUp({
|
||||
id: row.id,
|
||||
courseId: this.courseDetail.id,
|
||||
studentId: row.aid
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.status === 200) {
|
||||
this.$showMessage("删除成功", 'success');
|
||||
|
||||
@@ -100,9 +100,12 @@
|
||||
placeholder="资源归属"
|
||||
clearable
|
||||
:props="resOwnerCascaderProps"
|
||||
:options="resOwnerOptions"
|
||||
:show-all-levels="false"
|
||||
@change="handleResOwnerChange"
|
||||
@input.native="limitResOwnerInput"
|
||||
@clear="handleResOwnerClear"
|
||||
@visible-change="handleResOwnerVisibleChange"
|
||||
filterable
|
||||
:filter-method="resOwnerFilterMethod"
|
||||
></el-cascader>
|
||||
@@ -227,7 +230,7 @@
|
||||
<span class="common-cell common-cell-right">{{ scope.row.openCourse == 1 ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="资源归属" prop="orgName" min-width="220" align="center" sortable="custom">
|
||||
<el-table-column label="资源归属" prop="orgName" min-width="220" align="center" sortable="custom" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip :content="scope.row.orgFullName || scope.row.orgName" placement="top" effect="dark">
|
||||
<span class="common-cell common-cell-right">{{ scope.row.orgName }}</span>
|
||||
@@ -464,14 +467,23 @@ export default {
|
||||
computed: {
|
||||
...mapGetters(['resOwnerMap', 'sysTypeMap', 'userInfo', 'identity']),
|
||||
resOwnerCascaderProps() {
|
||||
// 搜索模式:关闭懒加载,直接使用全量 options
|
||||
if (this.resOwnerSearchMode) {
|
||||
return {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
checkStrictly: true, // 允许选择任意一级选项
|
||||
};
|
||||
}
|
||||
// 非搜索模式:开启前端“伪懒加载”,数据从本地缓存树中按需取
|
||||
return {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
lazy: true,
|
||||
lazyLoad: this.loadResOwnerNode,
|
||||
leaf: 'leaf',
|
||||
checkStrictly: true // 允许选择任意一级选项
|
||||
lazyLoad: this.loadResOwnerNodeFromCache,
|
||||
checkStrictly: true, // 允许选择任意一级选项
|
||||
};
|
||||
},
|
||||
// 动态计算“授课教师”列的最小宽度,避免固定 260px
|
||||
@@ -496,7 +508,11 @@ export default {
|
||||
sysTypeList: [],
|
||||
resOwnerListMap: [],
|
||||
resOwnerSelected: [],
|
||||
resOwnerCascaderOptions: [],
|
||||
// 资源归属全量树 & 级联选项
|
||||
resOwnerTreeAll: [],
|
||||
resOwnerOptions: [],
|
||||
// 资源归属搜索模式开关(有关键字时为 true)
|
||||
resOwnerSearchMode: false,
|
||||
showSetTopFeature: false,
|
||||
page: {
|
||||
pageIndex: 1,//第几页
|
||||
@@ -628,6 +644,8 @@ export default {
|
||||
//已经加载tree的情况下,不需要再单独的加载一次
|
||||
this.loadResOwners();
|
||||
this.loadSysTypes();
|
||||
// 加载资源归属全量树,供级联和搜索使用
|
||||
this.loadAllResOwnerTree();
|
||||
document.querySelector('#app').style.overflowX = 'hidden';
|
||||
this.applyAppScrollbarStyle();
|
||||
|
||||
@@ -838,8 +856,36 @@ export default {
|
||||
if (event && event.target && event.target.value !== limited) {
|
||||
event.target.value = limited;
|
||||
}
|
||||
if (this.$refs.resOwnerCascader) {
|
||||
this.$refs.resOwnerCascader.inputValue = limited;
|
||||
const keyword = (limited || '').trim();
|
||||
// 根据是否有关键字切换搜索模式
|
||||
this.resOwnerSearchMode = !!keyword;
|
||||
if (this.resOwnerSearchMode) {
|
||||
// 搜索模式:使用全量树作为 options,交给 filter-method 过滤
|
||||
this.resOwnerOptions = this.resOwnerTreeAll;
|
||||
} else {
|
||||
// 非搜索模式:清空 options,交给 lazyLoad 从缓存树按需“懒加载”
|
||||
this.resOwnerOptions = [];
|
||||
}
|
||||
},
|
||||
// 清空选择时,恢复到懒加载模式
|
||||
handleResOwnerClear() {
|
||||
this.resOwnerSearchMode = false;
|
||||
this.resOwnerOptions = [];
|
||||
// 清空输入框的文字,避免残留关键字
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.resOwnerCascader && this.$refs.resOwnerCascader.inputValue !== undefined) {
|
||||
this.$refs.resOwnerCascader.inputValue = '';
|
||||
}
|
||||
});
|
||||
},
|
||||
// 下拉面板打开时,如果当前没有关键字,也确保是懒加载模式
|
||||
handleResOwnerVisibleChange(visible) {
|
||||
if (!visible) return;
|
||||
// 如果没有关键字,就强制回到懒加载模式
|
||||
const keyword = (this.$refs.resOwnerCascader && this.$refs.resOwnerCascader.inputValue) || '';
|
||||
if (!keyword) {
|
||||
this.resOwnerSearchMode = false;
|
||||
this.resOwnerOptions = [];
|
||||
}
|
||||
},
|
||||
resOwnerFilterMethod(node, keyword) {
|
||||
@@ -1460,60 +1506,50 @@ export default {
|
||||
sessionStorage.setItem('courseDetail', JSON.stringify(row));
|
||||
this.$router.push({ path: '/iframe/course/coursemanage-remote' });
|
||||
},
|
||||
async loadResOwnerNode(node, resolve) {
|
||||
const { level } = node;
|
||||
let parentId = '';
|
||||
// 前端“伪懒加载”:根据本地全量机构树按需返回子节点
|
||||
// 为了让 el-cascader 的 loading 动画有可见效果,这里故意加入一个很小的延时再 resolve
|
||||
async loadResOwnerNodeFromCache(node, resolve) {
|
||||
try {
|
||||
const delayResolve = (nodes) => {
|
||||
// 适当延时 150ms,让 loading 动画可见,但又不会让用户感觉明显卡顿
|
||||
setTimeout(() => resolve(nodes), 150);
|
||||
};
|
||||
|
||||
if (level === 0) {
|
||||
// 第一层:获取根节点
|
||||
try {
|
||||
const res = await apiUserbasic.findOrgsByKeyword('');
|
||||
if (res && res.result) {
|
||||
const nodes = res.result.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
hrbpId: item.hrbpId,
|
||||
leaf: false, // 懒加载模式下,先假设所有节点都可能有子节点
|
||||
children: [] // 空数组表示需要懒加载
|
||||
}));
|
||||
resolve(nodes);
|
||||
// 根节点(level === 0):返回所有一级机构
|
||||
if (node.level === 0) {
|
||||
return delayResolve(this.resOwnerTreeAll || []);
|
||||
}
|
||||
|
||||
// 其他层级:根据当前节点 id 在树中查找对应节点,再返回其 children
|
||||
const currentId = node.value || (node.data && node.data.id);
|
||||
if (!currentId) {
|
||||
return delayResolve([]);
|
||||
}
|
||||
|
||||
const findNodeById = (list = [], id) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i];
|
||||
if (item.id === id) {
|
||||
return item;
|
||||
}
|
||||
if (Array.isArray(item.children) && item.children.length > 0) {
|
||||
const found = findNodeById(item.children, id);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const target = findNodeById(this.resOwnerTreeAll || [], currentId);
|
||||
if (target && Array.isArray(target.children)) {
|
||||
delayResolve(target.children);
|
||||
} else {
|
||||
delayResolve([]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('本地懒加载资源归属节点失败:', e);
|
||||
resolve([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载资源归属失败:', error);
|
||||
resolve([]);
|
||||
}
|
||||
} else {
|
||||
// 子节点:根据父节点ID加载
|
||||
parentId = node.value || (node.data && node.data.id);
|
||||
if (!parentId) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await apiUserbasic.getOrgInfo(parentId);
|
||||
if (res && res.status === 200 && res.result) {
|
||||
let treeList = [];
|
||||
if (res.result.directChildList && res.result.directChildList.length > 0) {
|
||||
treeList = res.result.directChildList.map(item => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
hrbpId: item.hrbpId,
|
||||
leaf: false, // 懒加载模式下,先假设所有节点都可能有子节点
|
||||
children: [] // 空数组表示需要懒加载
|
||||
}));
|
||||
}
|
||||
resolve(treeList);
|
||||
} else {
|
||||
// 如果没有子节点,返回空数组,级联选择器会自动将其视为叶子节点
|
||||
resolve([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载资源归属子节点失败:', error);
|
||||
resolve([]);
|
||||
}
|
||||
}
|
||||
},
|
||||
handleResOwnerChange(value) {
|
||||
if (!value || value.length === 0) {
|
||||
@@ -1536,6 +1572,39 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
// 将后端机构节点转换为级联组件所需结构
|
||||
mapOrgToCascaderNode(node = {}) {
|
||||
const children = Array.isArray(node.childList)
|
||||
? node.childList.map(child => this.mapOrgToCascaderNode(child))
|
||||
: [];
|
||||
return {
|
||||
id: node.organizationId,
|
||||
name: node.orgName,
|
||||
children,
|
||||
};
|
||||
},
|
||||
// 加载资源归属全量机构树
|
||||
async loadAllResOwnerTree() {
|
||||
try {
|
||||
const res = await apiUserbasic.getAllOrgTree();
|
||||
if (res && res.status === 200 && res.result && Array.isArray(res.result.orgTreeList)) {
|
||||
this.resOwnerTreeAll = res.result.orgTreeList.map(item => this.mapOrgToCascaderNode(item));
|
||||
// 默认进入非搜索模式,由懒加载从本地树按需返回节点
|
||||
this.resOwnerOptions = [];
|
||||
} else if (res && res.result && Array.isArray(res.result.orgTreeList)) {
|
||||
// 兼容没有 status 字段但有 result 的情况
|
||||
this.resOwnerTreeAll = res.result.orgTreeList.map(item => this.mapOrgToCascaderNode(item));
|
||||
this.resOwnerOptions = [];
|
||||
} else {
|
||||
this.resOwnerTreeAll = [];
|
||||
this.resOwnerOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载资源归属全量树失败:', error);
|
||||
this.resOwnerTreeAll = [];
|
||||
this.resOwnerOptions = [];
|
||||
}
|
||||
},
|
||||
showChooseCourse() {
|
||||
this.courseChooseShow = true;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user