This commit is contained in:
2026-06-10 14:38:57 +08:00
parent 06b8a74f41
commit 351806c339
10 changed files with 397 additions and 518 deletions
@@ -130,6 +130,21 @@ layout("/layouts/platform.html"){
:visible.sync="notifyDialogVisible"
:close-on-click-modal="false"
width="50%">
<el-select
v-model="notifyCourseIds"
multiple
filterable
clearable
:loading="notifyCourseLoading"
placeholder="请选择要通知的分活动"
style="width: 100%; margin-bottom: 15px">
<el-option
v-for="item in notifyCourseList"
:key="item.id"
:label="item.courseName"
:value="item.id">
</el-option>
</el-select>
<el-input
type="textarea"
:rows="6"
@@ -138,7 +153,7 @@ layout("/layouts/platform.html"){
</el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="notifyDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="sendNotify">发 送</el-button>
<el-button type="primary" :loading="notifyLoading" @click="sendNotify">发 送</el-button>
</span>
</el-dialog>
</guava>
@@ -175,6 +190,10 @@ layout("/layouts/platform.html"){
notifyDialogVisible: false,
notifyContent: '',
notifyActivityId: '',
notifyCourseIds: [],
notifyCourseList: [],
notifyCourseLoading: false,
notifyLoading: false,
}
},
methods: {
@@ -206,28 +225,51 @@ layout("/layouts/platform.html"){
openNotifyDialog(row) {
this.notifyActivityId = row.id
this.notifyContent = ''
this.notifyCourseIds = []
this.notifyCourseList = []
this.notifyDialogVisible = true
this.notifyCourseLoading = true
this.$axios.post("/platform/trainSignUp/manage/findOne", { id: row.id })
.then((resp) => {
if (resp.code === 0) {
this.notifyCourseList = resp.data.courseList || []
} else {
this.$message.warning(resp.msg)
}
})
.finally(() => {
this.notifyCourseLoading = false
})
},
async sendNotify() {
sendNotify() {
if (this.notifyCourseIds.length === 0) {
this.$message.warning('请选择要通知的分活动')
return
}
if (!this.notifyContent.trim()) {
this.$message.warning('请输入通知内容')
return
}
this.$confirm("确认发送通知给所报名人员吗?", "提示", {
this.$confirm("确认发送通知给所选分活动的报名人员吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(async () => {
const resp = await this.$axios.post(loc() + "/sendNotify", {
}).then(() => {
this.notifyLoading = true
this.$axios.post(loc() + "/sendNotify", {
activityId: this.notifyActivityId,
courseIds: JSON.stringify(this.notifyCourseIds),
content: this.notifyContent
}).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.notifyDialogVisible = false
} else {
this.$message.warning(resp.msg)
}
}).finally(() => {
this.notifyLoading = false
})
if (resp.code === 0) {
this.$message.success(resp.msg)
this.notifyDialogVisible = false
} else {
this.$message.warning(resp.msg)
}
}).catch(() => {})
},
async activityStatusChange(notice, val, id) {
@@ -8,6 +8,7 @@ layout("/layouts/platform.html"){
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
@change="doSearch"
placeholder="请选择年度"
type="year"
style="width: 100%"
@@ -15,31 +16,17 @@ layout("/layouts/platform.html"){
value-format="yyyy"
></el-date-picker>
</search-item>
<search-item label="活动名称">
<el-select @change="activityChange" style="width: 100%" v-model="pageForm.activityId" filterable>
<el-option :label="item.activityName" :value="item.id" v-for="item in activityList" :key="item.id"></el-option>
</el-select>
</search-item>
<search-item :label="trainType + ''">
<el-select @change="courseChange" :placeholder="'请选择' + trainType" style="width: 100%" v-model="pageForm.courseId" filterable>
<el-option :label="item.courseName" :value="item.id" v-for="item in courseList" :key="item.id"></el-option>
</el-select>
</search-item>
<search-item label="人员信息">
<el-input clearable placeholder="输入姓名或者工号搜索" v-model="pageForm.userKeyWord"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool ref="tool" :label="trainType + '(温馨提示:如需补充人员,请在上面选择具体的' + trainType + ''">
<el-button @click="openReserve" type="primary" v-if="reserveMode === 2" size="small">补充人员</el-button>
<el-button @click="exportSignPerson" type="primary" size="small">导出签到名单</el-button>
<el-button @click="exportGiftPerson" type="primary" size="small">导出领取名单</el-button>
</table-tool>
<table-tool ref="tool" label="年度人员黑名单管理"></table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder">
<el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column>
<el-table-column
:label="column.prop === 'courseNames' ? trainType : column.label"
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
align="center"
@@ -48,59 +35,47 @@ layout("/layouts/platform.html"){
v-for="(column,index) in tableColumns"
:key="index"
>
<template v-slot:header>
<span>{{ column.label }}</span>
<el-tooltip
:content="column.help"
effect="dark"
placement="top"
v-if="column.help"
>
<i class="el-icon-question text-info ml5" style="cursor: pointer"></i>
</el-tooltip>
</template>
<template v-slot="{row}" v-if="column.prop==='isDisabled'">
<span class="text-danger" v-if="row.isDisabled"></span>
<span class="text-success" v-else></span>
</template>
<template v-slot="{row}" v-else-if="column.prop==='state'">
<span class="text-success" v-if="row.state === 1">正常报名</span>
<span class="text-warning" v-else-if="row.state === 2">候补报名</span>
<span class="text-success" v-else-if="row.state === 3">正常报名(候补)</span>
<span class="text-info" v-else-if="row.state === 4">无效报名(缺席)</span>
<template v-slot="{row}" v-else-if="column.prop==='lastUnblockTime'">
<span v-if="row.lastUnblockTime">{{ formatDateTime(row.lastUnblockTime) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="150px">
<template v-slot="{row}">
<el-button @click="handleUser(row.userId)" size="mini" type="danger" v-if="!row.isDisabled">拉黑</el-button>
<el-button @click="handleUser(row.userId)" size="mini" type="success" v-if="row.isDisabled">解封</el-button>
<el-button @click="handleUser(row)" size="mini" type="danger" v-if="!row.isDisabled">拉黑</el-button>
<el-button @click="handleUser(row)" size="mini" type="success" v-if="row.isDisabled">解封</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog title="补充人员" :visible.sync="reserveDialogVisible" width="55%" top="3%">
<vi-title title="以下为候补人员,已为您按照报名时间降序排列"></vi-title>
<el-table :data="reserveTableData" ref="multipleTable" @selection-change="handleSelectionChange">
<el-table-column :selectable="(row, index) => {return row.state === 2}" type="selection" width="55"></el-table-column>
<el-table-column prop="username" label="姓名"></el-table-column>
<el-table-column prop="loginname" label="工号"></el-table-column>
<el-table-column prop="mobile" label="联系方式"></el-table-column>
<el-table-column prop="unitName" label="单位"></el-table-column>
<el-table-column prop="unionName" label="工会"></el-table-column>
<el-table-column prop="signUpTime" label="报名时间"></el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="reserveDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="reserveDo">确 定</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
dicts: ["TRAIN_SIGNUP_TYPE"],
mixins: [initTableMixins],
data() {
return {
reserveDialogVisible: false,
reserveTableData: [],
activityList: [],
pageForm: {
year: new Date().getFullYear().toString(),
activityId: null
userKeyWord: ""
},
tableColumns: [
{ label: "姓名", prop: "username" },
@@ -108,131 +83,66 @@ layout("/layouts/platform.html"){
{ label: "联系方式", prop: "mobile" },
{ label: "单位", prop: "unitName", sortable: true },
{ label: "分工会", prop: "unionName", sortable: true },
{ label: "课程", prop: "courseNames", sortable: true },
{ label: "总次数", prop: "totalCount", sortable: true },
{ label: "签到次数", prop: "signCount", sortable: true },
{ label: "缺席次数", prop: "absentCount", sortable: true },
{ label: "是否黑名单", prop: "isDisabled", sortable: true }
],
trainType: "培训班",
courseList: [],
course: {},
reserveMode: 1,
multipleSelection: [],
{
label: "年度总次数",
prop: "totalCount",
sortable: true,
help: "所选年度内,非候补报名、课程已结束、开启手机签到的报名活动/课程次数。同一报名下多个签到时间段只算一次。"
},
{
label: "签到次数",
prop: "signCount",
sortable: true,
help: "所选年度内,课程已结束、开启手机签到,并且人员至少完成一次签到的报名活动/课程次数。"
},
{
label: "缺席次数",
prop: "absentCount",
sortable: true,
help: "所选年度内,课程已结束、开启手机签到,并且人员没有任何签到记录的报名活动/课程次数。候补报名不计入。"
},
{
label: "是否黑名单",
prop: "isDisabled",
sortable: true,
help: "是表示当前人员已被限制报名香樟工会讲堂;否表示当前未被限制。"
},
{
label: "最近解封时间",
prop: "lastUnblockTime",
sortable: true,
help: "管理员最近一次手动解封时间。解封后,自动拉黑的缺席次数会从该时间之后重新累计。"
}
]
}
},
methods: {
handleSelectionChange(val) {
this.multipleSelection = val
formatDateTime(val) {
return this.$moment(val).format("YYYY-MM-DD HH:mm:ss")
},
async openReserve() {
const activity = this.activityList.find((o) => o.id === this.pageForm.activityId)
if (this.$moment().unix() < this.$moment(activity.activityEndTime).unix()) {
this.$alert("此活动还未结束,无法补充", "提示", {
confirmButtonText: "确定"
})
return
}
const resp = await this.$axios.post("/platform/trainSignUp/userManage/getReserveUser", { courseId: this.course.id })
this.reserveTableData = resp.data
this.reserveDialogVisible = true
},
reserveDo() {
if (this.multipleSelection.length === 0) {
this.$message.warning("请先在左侧多选框中选择要补充的人员")
return
}
this.$confirm("您选择了" + this.multipleSelection.length + "位教职工,确定要进行补充操作吗?", "提示", {
handleUser(row) {
const actionName = row.isDisabled ? "解封" : "拉黑"
const userName = row.username + "" + row.loginname + ""
this.$confirm("确定要" + actionName + userName + "吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(async () => {
const ids = this.multipleSelection.map((o) => o.userId)
const resp = await this.$axios.post("/platform/trainSignUp/userManage/reserveSingUp", {
ids: JSON.stringify(ids),
courseId: this.course.id
})
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
this.multipleSelection = []
this.$refs.multipleTable.clearSelection()
this.reserveDialogVisible = false
}
.then(() => {
$.post(loc() + "/doHandleUser", { userId: row.userId })
.then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
}
})
.always(() => {})
})
.catch(() => {})
},
courseChange(val) {
const course = this.courseList.find((o) => o.id === val)
this.course = course
this.reserveMode = course.reserveMode
if (this.reserveMode === 2) {
this.tableColumns.push({ label: "报名状态", prop: "state", sortable: true })
} else {
const o = this.tableColumns.find((o) => o.label === "报名状态")
if (o !== null && o !== undefined) {
this.tableColumns.splice(this.tableColumns.length - 1, 1)
}
}
this.$refs.tool.app = this
this.doSearch()
},
exportSignPerson() {
this.exportPro(loc() + "/exportSignPerson", { activityId: this.pageForm.activityId })
},
exportGiftPerson() {
this.exportPro(loc() + "/exportGiftPerson", { activityId: this.pageForm.activityId })
},
exportPro(url, param) {
if (param.activityId === null) {
this.$message.warning("请选择活动")
return
}
this.$downLoad(url, param)
},
async activityChange(val) {
this.$nextTick(async () => {
const activity = this.activityList.find((o) => o.id === val)
const type = this.dict.type.TRAIN_SIGNUP_TYPE?.find(o => o.code === activity.trainType)
this.trainType = type?.name || "培训班"
const resp = await this.$axios.post(loc() + "/getCourseByActivityId", { activityId: val })
this.courseList = resp.data
if(this.courseList.length > 0) {
this.$set(this.pageForm, "courseId", this.courseList[0].id)
} else {
this.pageForm.courseId = ""
}
this.doSearch()
})
},
async handleUser(userId) {
const resp = await $.post(loc() + "/doHandleUser", { userId })
if (resp.code === 0) {
this.$message.success(resp.msg)
this.doSearch()
}
},
async yearChange() {
this.pageForm.activityId = null
await this.getActivityList()
await this.doSearch()
},
async getActivityList() {
const resp = await this.$axios.post("/platform/trainSignUp/statistics/activityList", { year: this.pageForm.year })
this.activityList = resp.data
if (this.activityList && this.activityList.length > 0) {
this.pageForm.activityId = this.activityList[0].id
await this.activityChange(this.activityList[0].id)
}
},
async initData() {
await this.getActivityList()
},
}
},
async created() {
await this.initData()
await this.pageData()
created() {
this.pageData()
}
})
</script>