index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <script setup>
  2. import { reactive, ref, watch } from "vue"
  3. import {
  4. getActivityPages,
  5. createInsertActivity,
  6. updateActivity,
  7. getClubPage,
  8. deleteActivityById,
  9. exportActivityExcel
  10. } from "@/api/activityManagement"
  11. import { getQueryCollegesApi, getQueryPeriodsApi, getQueryMajorsApi, getQueryClassesApi } from "@/api/alumniManager"
  12. import { ElMessage, ElMessageBox } from "element-plus"
  13. import { usePagination } from "@/hooks/usePagination"
  14. import { cloneDeep } from "lodash-es"
  15. const loading = ref(false)
  16. const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
  17. //#region 设置管理员
  18. const SET_ADMIN_DEFAULT_FORM_DATA = {
  19. list: []
  20. }
  21. const setAdminDialogVisible = ref(false)
  22. const setAdminFormRef = ref(null)
  23. const setAdminFormData = ref(cloneDeep(SET_ADMIN_DEFAULT_FORM_DATA))
  24. const setAdminFormRules = {
  25. list: [{ required: true, trigger: "blur", message: "必填" }]
  26. }
  27. const handleSetAdmin = () => {
  28. setAdminFormRef.value?.validate((valid, fields) => {
  29. if (!valid) return console.error("表单校验不通过", fields)
  30. // todo 调用设置管理员接口
  31. })
  32. }
  33. const resetSetAdminForm = () => {
  34. setAdminFormRef.value?.clearValidate()
  35. setAdminFormData.value = cloneDeep(SET_ADMIN_DEFAULT_FORM_DATA)
  36. }
  37. //#endregion
  38. //#region 增
  39. const DEFAULT_FORM_DATA = {
  40. id: undefined,
  41. orgId: "",
  42. orgName: "",
  43. organization: [],
  44. theme: "",
  45. themeDetail: "",
  46. poster: "",
  47. activityTime: null,
  48. address: "",
  49. lng: "",
  50. lat: "",
  51. describes: "",
  52. registrationTime: null,
  53. totalNumber: undefined,
  54. scope: "",
  55. phone: "",
  56. isImage: false
  57. }
  58. const dialogVisible = ref(false)
  59. const formRef = ref(null)
  60. const formData = ref(cloneDeep(DEFAULT_FORM_DATA))
  61. const formRules = {
  62. orgId: [{ required: true, trigger: "blur", message: "必填" }],
  63. theme: [{ required: true, trigger: "blur", message: "必填" }],
  64. themeDetail: [{ required: true, trigger: "blur", message: "必填" }],
  65. activityTime: [{ required: true, trigger: "blur", message: "必填" }],
  66. address: [{ required: true, trigger: "blur", message: "必填" }],
  67. describes: [{ required: true, trigger: "blur", message: "必填" }],
  68. registrationTime: [{ required: true, trigger: "blur", message: "必填" }],
  69. totalNumber: [{ required: true, trigger: "blur", message: "必填" }],
  70. scope: [{ required: true, trigger: "blur", message: "必填" }],
  71. phone: [{ required: true, trigger: "blur", message: "必填" }]
  72. }
  73. const handleCreateOrUpdate = () => {
  74. formRef.value?.validate((valid, fields) => {
  75. if (!valid) return console.error("表单校验不通过", fields)
  76. loading.value = true
  77. const api = formData.value.id === undefined ? createInsertActivity : updateActivity
  78. api({
  79. ...formData.value,
  80. startTime: formData.value.activityTime ? formData.value.activityTime[0] : undefined,
  81. endTime: formData.value.activityTime ? formData.value.activityTime[1] : undefined,
  82. lng: "101",
  83. lat: "78",
  84. signsTime: formData.value.registrationTime ? formData.value.registrationTime[0] : undefined,
  85. signeTime: formData.value.registrationTime ? formData.value.registrationTime[1] : undefined
  86. })
  87. .then(() => {
  88. ElMessage.success("操作成功")
  89. dialogVisible.value = false
  90. getTableData()
  91. })
  92. .catch(() => {
  93. loading.value = false
  94. })
  95. })
  96. }
  97. const resetForm = () => {
  98. formRef.value?.clearValidate()
  99. formData.value = cloneDeep(DEFAULT_FORM_DATA)
  100. }
  101. //#endregion
  102. //#region 删
  103. const handleDelete = (row) => {
  104. ElMessageBox.confirm("确认删除?", "提示", {
  105. confirmButtonText: "确定",
  106. cancelButtonText: "取消",
  107. type: "warning"
  108. }).then(() => {
  109. deleteActivityById(row.id).then(() => {
  110. ElMessage.success("删除成功")
  111. getTableData()
  112. })
  113. })
  114. }
  115. //#endregion
  116. //#region 改
  117. const handleUpdate = (row) => {
  118. dialogVisible.value = true
  119. formData.value = cloneDeep(row)
  120. }
  121. //#endregion
  122. //#region 查
  123. const tableData = ref([])
  124. const searchFormRef = ref(null)
  125. const searchData = reactive({
  126. orgName: undefined,
  127. userName: undefined,
  128. theme: undefined,
  129. collegeId: undefined,
  130. colleges: [],
  131. periodId: undefined,
  132. periods: [],
  133. majorId: undefined,
  134. majors: [],
  135. classId: undefined,
  136. classList: [],
  137. statuId: undefined,
  138. createTime: null,
  139. activityTime: null,
  140. registrationTime: null
  141. })
  142. const getTableData = () => {
  143. loading.value = true
  144. getActivityPages({
  145. currentPage: paginationData.currentPage,
  146. pageCount: paginationData.pageSize,
  147. orgName: searchData.orgName,
  148. userName: searchData.userName,
  149. theme: searchData.theme,
  150. collegeId: searchData.collegeId,
  151. periodId: searchData.periodId,
  152. majorId: searchData.majorId,
  153. classId: searchData.classId,
  154. statuId: searchData.statuId,
  155. createStartTime: searchData.createTime ? searchData.createTime[0] : undefined,
  156. createEndTime: searchData.createTime ? searchData.createTime[1] : undefined,
  157. startTime: searchData.activityTime ? searchData.activityTime[0] : undefined,
  158. endTime: searchData.activityTime ? searchData.activityTime[1] : undefined,
  159. signsTime: searchData.registrationTime ? searchData.registrationTime[0] : undefined,
  160. signeTime: searchData.registrationTime ? searchData.registrationTime[1] : undefined
  161. })
  162. .then(({ data }) => {
  163. console.log(data)
  164. paginationData.total = data.totalCount
  165. tableData.value = data.list
  166. })
  167. .catch(() => {
  168. tableData.value = []
  169. })
  170. .finally(() => {
  171. loading.value = false
  172. })
  173. }
  174. const handleSearch = () => {
  175. paginationData.currentPage === 1 ? getTableData() : (paginationData.currentPage = 1)
  176. }
  177. const resetSearch = () => {
  178. searchFormRef.value?.resetFields()
  179. handleSearch()
  180. }
  181. // 获取学院集合
  182. const getColleges = async () => {
  183. const res = await getQueryCollegesApi()
  184. // console.log(res)
  185. searchData.colleges = res.data
  186. }
  187. // 学院筛选框切换回调
  188. const changeCollege = async (e) => {
  189. const res = await getQueryPeriodsApi({
  190. collegeId: e
  191. })
  192. // console.log(res)
  193. searchData.periods = res.data
  194. }
  195. // 学段筛选框切换回调
  196. const changePeriod = async (e) => {
  197. const res = await getQueryMajorsApi({
  198. periodId: e
  199. })
  200. searchData.majors = res.data
  201. }
  202. // 专业筛选框切换回调
  203. const changeMajorId = async (e) => {
  204. const res = await getQueryClassesApi({
  205. majorId: e
  206. })
  207. searchData.classList = res.data
  208. }
  209. // 新增按钮回调
  210. const handleAdd = () => {
  211. dialogVisible.value = true
  212. getOrgList()
  213. }
  214. // 获取弹窗组织列表
  215. const getOrgList = async () => {
  216. const res = await getClubPage({
  217. currentPage: 1,
  218. pageCount: 999
  219. })
  220. // console.log(res)
  221. formData.value.organization = res.data.list
  222. }
  223. // 切换 弹窗组织筛选框回调
  224. const changeOrg = (e) => {
  225. // console.log(e)
  226. const v = formData.value.organization.findIndex((item) => item.id == e)
  227. formData.value.orgName = formData.value.organization[v].name
  228. }
  229. //#endregion
  230. /** 监听分页参数的变化 */
  231. watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
  232. watch([() => paginationData.currentPage, () => paginationData.pageSize], getColleges, { immediate: true })
  233. /** 导出 */
  234. const handleDownload = () => {
  235. ElMessageBox.confirm("确认导出吗?", "提示", {
  236. confirmButtonText: "确定",
  237. cancelButtonText: "取消",
  238. type: "warning"
  239. }).then(() => {
  240. downloadReq()
  241. })
  242. }
  243. // 导出请求
  244. const downloadReq = async () => {
  245. const res = await exportActivityExcel({
  246. orgName: searchData.orgName,
  247. userName: searchData.userName,
  248. theme: searchData.theme,
  249. collegeId: searchData.collegeId,
  250. periodId: searchData.periodId,
  251. majorId: searchData.majorId,
  252. classId: searchData.classId,
  253. statuId: searchData.statuId,
  254. createStartTime: searchData.createTime ? searchData.createTime[0] : undefined,
  255. createEndTime: searchData.createTime ? searchData.createTime[1] : undefined,
  256. startTime: searchData.activityTime ? searchData.activityTime[0] : undefined,
  257. endTime: searchData.activityTime ? searchData.activityTime[1] : undefined,
  258. signsTime: searchData.registrationTime ? searchData.registrationTime[0] : undefined,
  259. signeTime: searchData.registrationTime ? searchData.registrationTime[1] : undefined
  260. })
  261. // console.log(res)
  262. // 请求成功返回后,获取到Excel文件的二进制数据
  263. const blob = new Blob([res], { type: "application/vnd.ms-excel" })
  264. // 创建下载链接
  265. const downloadUrl = URL.createObjectURL(blob)
  266. // 创建一个隐藏的a标签,设置下载链接和文件名,模拟点击下载
  267. const link = document.createElement("a")
  268. link.style.display = "none"
  269. link.href = downloadUrl
  270. link.download = "活动管理.xlsx"
  271. document.body.appendChild(link)
  272. link.click()
  273. document.body.removeChild(link)
  274. }
  275. </script>
  276. <template>
  277. <div class="app-container">
  278. <el-card v-loading="loading" header="活动管理">
  279. <div class="toolbar-wrapper">
  280. <el-form ref="searchFormRef" :inline="true" :model="searchData">
  281. <el-form-item prop="orgName" label="组织名称">
  282. <el-input v-model="searchData.orgName" placeholder="请输入" />
  283. </el-form-item>
  284. <el-form-item prop="userName" label="发起人">
  285. <el-input v-model="searchData.userName" placeholder="请输入" />
  286. </el-form-item>
  287. <el-form-item prop="theme" label="活动主题">
  288. <el-input v-model="searchData.theme" placeholder="请输入" />
  289. </el-form-item>
  290. <el-form-item prop="collegeId" label="学院">
  291. <el-select v-model="searchData.collegeId" placeholder="请选择" style="width: 178px" @change="changeCollege">
  292. <el-option v-for="item in searchData.colleges" :label="item.name" :value="item.id" :key="item.id" />
  293. </el-select>
  294. </el-form-item>
  295. <el-form-item prop="periodId" label="学段">
  296. <el-select
  297. v-model="searchData.periodId"
  298. placeholder="请选择"
  299. style="width: 178px"
  300. :disabled="!searchData.collegeId"
  301. @change="changePeriod"
  302. >
  303. <el-option v-for="item in searchData.periods" :label="item.name" :value="item.id" :key="item.id" />
  304. </el-select>
  305. </el-form-item>
  306. <el-form-item prop="majorId" label="专业">
  307. <el-select
  308. v-model="searchData.majorId"
  309. placeholder="请选择"
  310. style="width: 178px"
  311. :disabled="!searchData.periodId"
  312. @change="changeMajorId"
  313. >
  314. <el-option v-for="item in searchData.majors" :label="item.name" :value="item.id" :key="item.id" />
  315. </el-select>
  316. </el-form-item>
  317. <el-form-item prop="classId" label="班级">
  318. <el-select
  319. v-model="searchData.classId"
  320. placeholder="请选择"
  321. style="width: 178px"
  322. :disabled="!searchData.majorId"
  323. >
  324. <el-option v-for="item in searchData.classList" :label="item.name" :value="item.id" :key="item.id" />
  325. </el-select>
  326. </el-form-item>
  327. <el-form-item prop="statuId" label="状态">
  328. <el-select v-model="searchData.statuId" placeholder="请选择" style="width: 178px">
  329. <el-option label="全部" value="0" />
  330. <el-option label="未开始" value="1" />
  331. <el-option label="进行中" value="2" />
  332. <el-option label="已结束" value="3" />
  333. </el-select>
  334. </el-form-item>
  335. <el-form-item prop="createTime" label="创建时间">
  336. <el-date-picker
  337. v-model="searchData.createTime"
  338. type="datetimerange"
  339. start-placeholder="开始时间"
  340. end-placeholder="结束时间"
  341. value-format="YYYY-MM-DD HH:mm:ss"
  342. />
  343. </el-form-item>
  344. <el-form-item prop="activityTime" label="活动时间">
  345. <el-date-picker
  346. v-model="searchData.activityTime"
  347. type="datetimerange"
  348. start-placeholder="开始时间"
  349. end-placeholder="结束时间"
  350. value-format="YYYY-MM-DD HH:mm:ss"
  351. />
  352. </el-form-item>
  353. <el-form-item prop="registrationTime" label="报名时间">
  354. <el-date-picker
  355. v-model="searchData.registrationTime"
  356. type="datetimerange"
  357. start-placeholder="开始时间"
  358. end-placeholder="结束时间"
  359. value-format="YYYY-MM-DD HH:mm:ss"
  360. />
  361. </el-form-item>
  362. <el-form-item>
  363. <el-button type="primary" @click="handleSearch">查询</el-button>
  364. <el-button @click="resetSearch" plain>重置</el-button>
  365. </el-form-item>
  366. </el-form>
  367. <div>
  368. <el-button type="primary" @click="setAdminDialogVisible = true">设置管理员</el-button>
  369. <el-button type="primary" @click="handleAdd">新增</el-button>
  370. <el-button plain @click="handleDownload">导出</el-button>
  371. </div>
  372. </div>
  373. <div class="table-wrapper">
  374. <el-table :data="tableData" max-height="500">
  375. <el-table-column type="index" label="序号" width="80" align="center" />
  376. <el-table-column prop="name" label="发起人" align="center" show-overflow-tooltip />
  377. <el-table-column prop="collegeName" label="学院" align="center" show-overflow-tooltip />
  378. <el-table-column prop="periodName" label="学段" align="center" />
  379. <el-table-column prop="majorName" label="专业" align="center" show-overflow-tooltip />
  380. <el-table-column prop="className" label="班级" align="center" />
  381. <el-table-column prop="orgName" label="所属组织" align="center" />
  382. <el-table-column prop="theme" label="活动主题" align="center" />
  383. <el-table-column label="活动时间" align="center" show-overflow-tooltip>
  384. <template #default="{ row }">
  385. <div>{{ row.startTime }} -- {{ row.endTime }}</div>
  386. </template>
  387. </el-table-column>
  388. <el-table-column label="报名时间" align="center" show-overflow-tooltip>
  389. <template #default="{ row }">
  390. <div>{{ row.signsTime }} -- {{ row.signeTime }}</div>
  391. </template>
  392. </el-table-column>
  393. <el-table-column label="状态" align="center">
  394. <template #default="{ row }">
  395. <div style="color: #366fff" v-if="row.activityStatuName == 1">未开始</div>
  396. <div style="color: #e6a23c" v-if="row.activityStatuName == 2">进行中</div>
  397. <div style="color: #d43030" v-if="row.activityStatuName == 3">已结束</div>
  398. </template>
  399. </el-table-column>
  400. <el-table-column label="已报名" align="center" show-overflow-tooltip>
  401. <template #default="{ row }">
  402. <div style="color: #d43030" v-if="row.reportNumber == 0">未报名</div>
  403. <div style="color: #366fff" v-else>已报名</div>
  404. </template>
  405. </el-table-column>
  406. <el-table-column label="已签到" align="center" show-overflow-tooltip>
  407. <template #default="{ row }">
  408. <div style="color: #d43030" v-if="row.signinNumber == 0">未签到</div>
  409. <div style="color: #366fff" v-else>已签到</div>
  410. </template>
  411. </el-table-column>
  412. <el-table-column prop="createTime" label="创建时间" align="center" show-overflow-tooltip />
  413. <el-table-column fixed="right" label="操作" width="200" align="center">
  414. <template #default="scope">
  415. <el-link type="primary" @click="handleUpdate(scope.row)">详情</el-link>
  416. <el-link type="danger" @click="handleDelete(scope.row)">删除</el-link>
  417. </template>
  418. </el-table-column>
  419. </el-table>
  420. </div>
  421. <div class="pager-wrapper">
  422. <el-pagination
  423. background
  424. :layout="paginationData.layout"
  425. :page-sizes="paginationData.pageSizes"
  426. :total="paginationData.total"
  427. :page-size="paginationData.pageSize"
  428. :currentPage="paginationData.currentPage"
  429. @size-change="handleSizeChange"
  430. @current-change="handleCurrentChange"
  431. />
  432. </div>
  433. </el-card>
  434. <!-- 新增/修改 -->
  435. <el-dialog
  436. v-model="dialogVisible"
  437. :title="formData.id === undefined ? '创建活动' : '编辑活动'"
  438. @closed="resetForm"
  439. width="50%"
  440. top="5vh"
  441. >
  442. <el-form ref="formRef" :model="formData" :rules="formRules" label-width="auto" size="large">
  443. <el-form-item prop="orgId" label="所属组织">
  444. <el-select v-model="formData.orgId" placeholder="请选择" @change="changeOrg">
  445. <el-option v-for="item in formData.organization" :label="item.name" :value="item.id" :key="item.id" />
  446. </el-select>
  447. </el-form-item>
  448. <el-form-item prop="theme" label="活动主题">
  449. <el-input v-model="formData.theme" placeholder="请输入" />
  450. </el-form-item>
  451. <el-form-item prop="themeDetail" label="活动详情">
  452. <el-input v-model="formData.themeDetail" type="textarea" :rows="5" placeholder="请输入" />
  453. </el-form-item>
  454. <el-form-item prop="activityTime" label="活动时间">
  455. <el-date-picker
  456. v-model="formData.activityTime"
  457. type="datetimerange"
  458. start-placeholder="开始时间"
  459. end-placeholder="结束时间"
  460. value-format="YYYY-MM-DD HH:mm:ss"
  461. />
  462. </el-form-item>
  463. <el-form-item prop="address" label="活动地址">
  464. <el-input v-model="formData.address" placeholder="请输入" />
  465. </el-form-item>
  466. <el-form-item prop="describes" label="参与说明">
  467. <el-input v-model="formData.describes" type="textarea" :rows="5" placeholder="请输入" />
  468. </el-form-item>
  469. <el-form-item prop="registrationTime" label="报名时间">
  470. <el-date-picker
  471. v-model="formData.registrationTime"
  472. type="datetimerange"
  473. start-placeholder="开始时间"
  474. end-placeholder="结束时间"
  475. value-format="YYYY-MM-DD HH:mm:ss"
  476. />
  477. </el-form-item>
  478. <el-form-item prop="totalNumber" label="报名总人数">
  479. <el-input v-model.number="formData.totalNumber" type="number" placeholder="请输入" />
  480. </el-form-item>
  481. <el-form-item prop="scope" label="报名范围">
  482. <el-select v-model="formData.scope" placeholder="请选择">
  483. <el-option label="全部" value="2" />
  484. <el-option label="组织成员" value="1" />
  485. </el-select>
  486. </el-form-item>
  487. <el-form-item prop="phone" label="咨询方式">
  488. <el-input v-model="formData.phone" placeholder="请输入" />
  489. </el-form-item>
  490. <el-form-item prop="isImage" label="活动相册">
  491. <el-switch v-model="formData.isImage" />
  492. </el-form-item>
  493. </el-form>
  494. <template #footer>
  495. <el-button @click="dialogVisible = false">取消</el-button>
  496. <el-button type="primary" @click="handleCreateOrUpdate" :loading="loading">确定</el-button>
  497. </template>
  498. </el-dialog>
  499. <!-- 设置管理员 -->
  500. <el-dialog v-model="setAdminDialogVisible" title="设置管理员" @closed="resetSetAdminForm" width="50%">
  501. <el-form
  502. ref="setAdminFormRef"
  503. :model="setAdminFormData"
  504. :rules="setAdminFormRules"
  505. label-width="auto"
  506. size="large"
  507. >
  508. <el-form-item prop="list" label="管理员">
  509. <el-select v-model="setAdminFormData.list" placeholder="请选择" multiple>
  510. <el-option label="todo" value="todo" />
  511. </el-select>
  512. </el-form-item>
  513. </el-form>
  514. <template #footer>
  515. <el-button @click="setAdminDialogVisible = false">取消</el-button>
  516. <el-button type="primary" @click="handleSetAdmin" :loading="loading">确定</el-button>
  517. </template>
  518. </el-dialog>
  519. </div>
  520. </template>
  521. <style lang="scss" scoped>
  522. .toolbar-wrapper {
  523. margin-bottom: 26px;
  524. }
  525. .table-wrapper {
  526. margin-bottom: 29px;
  527. .el-link {
  528. margin-right: 15px;
  529. }
  530. }
  531. .pager-wrapper {
  532. display: flex;
  533. justify-content: flex-end;
  534. }
  535. </style>