mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/learning-system-portal.git
synced 2025-12-18 07:16:44 +08:00
feat: 增强报名管理功能,新增删除课程学习记录API,优化受众选择弹窗的多选逻辑和搜索功能
This commit is contained in:
@@ -17,7 +17,7 @@ const hasSignup = function(courseId) {
|
|||||||
* 课程报名,微课,录播课
|
* 课程报名,微课,录播课
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* {
|
* {
|
||||||
courseId:
|
courseId:
|
||||||
courseName:
|
courseName:
|
||||||
courseType: 课程类型,对应微课,录播课
|
courseType: 课程类型,对应微课,录播课
|
||||||
signType:1,后名方式,默认是1自主报名,可以不传
|
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}`);
|
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,
|
signup,
|
||||||
findSignup,
|
findSignup,
|
||||||
deleteSignup,
|
deleteSignup,
|
||||||
|
deleteNewSignUp,
|
||||||
importSignup,
|
importSignup,
|
||||||
countSignup,
|
countSignup,
|
||||||
studyIndex,
|
studyIndex,
|
||||||
|
|||||||
@@ -5,16 +5,37 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div class="tab-search">
|
<div class="tab-search">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="keyword"
|
class="member-select"
|
||||||
placeholder="姓名"
|
ref="memberSelect"
|
||||||
size="small"
|
v-model="memberSelected"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
clearable
|
clearable
|
||||||
class="input"
|
reserve-keyword
|
||||||
@keyup.enter.native="onSearch"
|
placeholder="姓名"
|
||||||
/>
|
:multiple-limit="5"
|
||||||
<el-button type="primary" size="small" @click="onSearch">查询</el-button>
|
:remote-method="remoteSearchMember"
|
||||||
<el-button type="primary" size="small" @click="onReset">重置</el-button>
|
: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>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
class="use-table"
|
class="use-table"
|
||||||
@@ -51,6 +72,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { fetchAudienceList, saveStu } from "@/api/signup/commonStudent";
|
import { fetchAudienceList, saveStu } from "@/api/signup/commonStudent";
|
||||||
|
import apiUserbasic from "@/api/boe/userbasic.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AudienceModal",
|
name: "AudienceModal",
|
||||||
@@ -74,6 +96,10 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
selectedRows: [],
|
selectedRows: [],
|
||||||
|
// 成员远程搜索多选
|
||||||
|
memberSelected: [],
|
||||||
|
memberOptions: [],
|
||||||
|
memberLoading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -89,15 +115,18 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetAndFetch() {
|
resetAndFetch() {
|
||||||
this.keyword = "";
|
|
||||||
this.pageNo = 1;
|
this.pageNo = 1;
|
||||||
this.selectedRows = [];
|
this.selectedRows = [];
|
||||||
|
this.keyword = "";
|
||||||
|
this.memberSelected = [];
|
||||||
|
this.memberOptions = [];
|
||||||
this.fetchList();
|
this.fetchList();
|
||||||
},
|
},
|
||||||
fetchList() {
|
fetchList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
fetchAudienceList({
|
fetchAudienceList({
|
||||||
keyword: this.keyword,
|
// memberList 为多选成员 ID 列表
|
||||||
|
memberList: this.memberSelected || [],
|
||||||
pageNo: this.pageNo,
|
pageNo: this.pageNo,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
audienceIdList: this.audienceIds || [],
|
audienceIdList: this.audienceIds || [],
|
||||||
@@ -117,6 +146,8 @@ export default {
|
|||||||
},
|
},
|
||||||
onReset() {
|
onReset() {
|
||||||
this.keyword = "";
|
this.keyword = "";
|
||||||
|
this.memberSelected = [];
|
||||||
|
this.memberOptions = [];
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
},
|
},
|
||||||
onPageChange(page) {
|
onPageChange(page) {
|
||||||
@@ -126,6 +157,79 @@ export default {
|
|||||||
onSelectionChange(list) {
|
onSelectionChange(list) {
|
||||||
this.selectedRows = 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() {
|
handleClose() {
|
||||||
this.visibleSync = false;
|
this.visibleSync = false;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<el-button type="primary" size="small" @click="onSearchStu">
|
<el-button type="primary" size="small" @click="onSearchStu">
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" size="small" @click="resetStu">重置</el-button>
|
<el-button size="small" @click="resetStu">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="split">
|
<div class="split">
|
||||||
<div class="left-tree">
|
<div class="left-tree">
|
||||||
@@ -43,8 +43,15 @@
|
|||||||
@node-click="onOrgSelect" />
|
@node-click="onOrgSelect" />
|
||||||
</div>
|
</div>
|
||||||
<div class="table-area">
|
<div class="table-area">
|
||||||
<el-table class="use-table" border :data="stuTable.list"
|
<el-table
|
||||||
@selection-change="onStuSelectionChange" :row-key="row => row.id || row.userId">
|
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 type="selection" width="50" />
|
||||||
<el-table-column prop="realName" label="姓名" width="120" />
|
<el-table-column prop="realName" label="姓名" width="120" />
|
||||||
<el-table-column prop="userNo" label="工号" width="120" />
|
<el-table-column prop="userNo" label="工号" width="120" />
|
||||||
@@ -83,8 +90,15 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<el-button size="small" @click="resetAudienceInfo">重置</el-button>
|
<el-button size="small" @click="resetAudienceInfo">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table class="use-table" border :data="audienceTable.list" @selection-change="onAudienceSelectionChange"
|
<el-table
|
||||||
:row-key="row => row.id">
|
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 type="selection" width="50" />
|
||||||
<el-table-column prop="audienceName" label="受众名称" min-width="220" />
|
<el-table-column prop="audienceName" label="受众名称" min-width="220" />
|
||||||
<el-table-column prop="totalMember" label="总人数" width="100" />
|
<el-table-column prop="totalMember" label="总人数" width="100" />
|
||||||
@@ -309,6 +323,8 @@ export default {
|
|||||||
visible(val) {
|
visible(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
this.initData();
|
this.initData();
|
||||||
|
} else {
|
||||||
|
this.resetState();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -318,6 +334,30 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
initData() {
|
||||||
this.courseDetail = JSON.parse(sessionStorage.getItem("courseDetail") || "{}");
|
this.courseDetail = JSON.parse(sessionStorage.getItem("courseDetail") || "{}");
|
||||||
this.projectParams = {
|
this.projectParams = {
|
||||||
@@ -374,6 +414,10 @@ export default {
|
|||||||
console.log("res", res);
|
console.log("res", res);
|
||||||
this.stuTable.list = res.data?.list || [];
|
this.stuTable.list = res.data?.list || [];
|
||||||
this.stuTable.total = res.data?.total || 0;
|
this.stuTable.total = res.data?.total || 0;
|
||||||
|
// 根据右侧已选,恢复当前页的勾选状态
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncStuTableSelection();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onStuPageChange(page) {
|
onStuPageChange(page) {
|
||||||
@@ -381,7 +425,22 @@ export default {
|
|||||||
this.onSearchStu();
|
this.onSearchStu();
|
||||||
},
|
},
|
||||||
onStuSelectionChange(list) {
|
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() {
|
resetStu() {
|
||||||
this.nameSearch = { keyword: "", departId: "" };
|
this.nameSearch = { keyword: "", departId: "" };
|
||||||
@@ -444,6 +503,10 @@ export default {
|
|||||||
console.log('searchAudience', res);
|
console.log('searchAudience', res);
|
||||||
this.audienceTable.list = res.data?.list || [];
|
this.audienceTable.list = res.data?.list || [];
|
||||||
this.audienceTable.total = res.data?.total || 0;
|
this.audienceTable.total = res.data?.total || 0;
|
||||||
|
// 根据右侧已选,恢复当前页的受众勾选状态
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncAudienceTableSelection();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onAudiencePageChange(page) {
|
onAudiencePageChange(page) {
|
||||||
@@ -451,7 +514,50 @@ export default {
|
|||||||
this.searchAudience();
|
this.searchAudience();
|
||||||
},
|
},
|
||||||
onAudienceSelectionChange(list) {
|
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() {
|
resetAudienceInfo() {
|
||||||
this.audienceName.keyword = "";
|
this.audienceName.keyword = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user