mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-17 23:06:45 +08:00
feat: 增强报名管理功能,新增删除课程学习记录API,优化受众选择弹窗的多选逻辑和搜索功能
This commit is contained in:
@@ -17,7 +17,7 @@ const hasSignup = function(courseId) {
|
||||
* 课程报名,微课,录播课
|
||||
* @param {Object} data
|
||||
* {
|
||||
courseId:
|
||||
courseId:
|
||||
courseName:
|
||||
courseType: 课程类型,对应微课,录播课
|
||||
signType:1,后名方式,默认是1自主报名,可以不传
|
||||
@@ -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,
|
||||
|
||||
@@ -5,16 +5,37 @@
|
||||
</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;
|
||||
},
|
||||
|
||||
@@ -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" />
|
||||
@@ -83,8 +90,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" />
|
||||
@@ -309,6 +323,8 @@ export default {
|
||||
visible(val) {
|
||||
if (val) {
|
||||
this.initData();
|
||||
} else {
|
||||
this.resetState();
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -318,6 +334,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 +414,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 +425,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 +503,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 +514,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 = "";
|
||||
|
||||
Reference in New Issue
Block a user