浏览代码

no message

xiaoxin 1 年之前
父节点
当前提交
c4ea61a0b9

+ 2 - 0
package.json

@@ -14,8 +14,10 @@
     "prepare": "husky"
   },
   "dependencies": {
+    "@amap/amap-jsapi-loader": "1.0.1",
     "@element-plus/icons-vue": "2.3.1",
     "@rollup/rollup-win32-x64-msvc": "4.28.1",
+    "aieditor": "1.2.9",
     "axios": "1.7.7",
     "dayjs": "1.11.13",
     "element-plus": "2.8.8",

+ 50 - 4
src/api/activityManagement/index.js

@@ -1,6 +1,6 @@
 import { request } from "@/utils/service"
 
-/** 增 */
+/** 增 活动分页*/
 export function createInsertActivity(data) {
   return request({
     url: "/alumniActivity/insertActivity",
@@ -9,7 +9,7 @@ export function createInsertActivity(data) {
   })
 }
 
-/** 删 */
+/** 删 活动分页*/
 export function deleteActivityById(id) {
   return request({
     url: `/alumniActivity/deleteActivityById?id=${id}`,
@@ -17,7 +17,7 @@ export function deleteActivityById(id) {
   })
 }
 
-/** 改 */
+/** 改 活动分页*/
 export function updateActivity(data) {
   return request({
     url: "/alumniActivity/updateActivity",
@@ -26,7 +26,7 @@ export function updateActivity(data) {
   })
 }
 
-/** 查 */
+/** 查 活动分页*/
 export function getActivityPages(params) {
   return request({
     url: "/alumniActivity/queryActivityPages",
@@ -53,3 +53,49 @@ export function exportActivityExcel(data) {
     responseType: "blob"
   })
 }
+
+/** 设置活动管理员 */
+export function setImageAdmin(data) {
+  return request({
+    url: "/alumniAdminApply/setImageAdmin",
+    method: "post",
+    data
+  })
+}
+
+/** 查 活动审核分页*/
+export function getActivityApplyPages(params) {
+  return request({
+    url: "/alumniActivity/queryActivityApplyPages",
+    method: "get",
+    params
+  })
+}
+
+/** 导出活动审核数据 */
+export function exportActivityApplyExcel(data) {
+  return request({
+    url: "/alumniActivity/getActivityApplyExcel",
+    method: "post",
+    data,
+    responseType: "blob"
+  })
+}
+
+/** 审核活动数据*/
+export function getExamineActivity(params) {
+  return request({
+    url: "/alumniActivity/toExamineActivity",
+    method: "get",
+    params
+  })
+}
+
+/** 根据ID获取数据详情*/
+export function getDetailInfoById(params) {
+  return request({
+    url: "/alumniActivity/queryDetailInfoById",
+    method: "get",
+    params
+  })
+}

+ 80 - 0
src/api/alumniAlbum/index.js

@@ -0,0 +1,80 @@
+import { request } from "@/utils/service"
+
+/** 删 校友相册*/
+export function deleteImageById(id) {
+  return request({
+    url: `/alumniImage/deleteImageById?id=${id}`,
+    method: "get"
+  })
+}
+
+/** 查 校友相册*/
+export function getImagePage(params) {
+  return request({
+    url: "/alumniImage/queryImagePage",
+    method: "get",
+    params
+  })
+}
+
+/** 设置校友相册管理员 */
+export function setImageAdmin(data) {
+  return request({
+    url: "/alumniAdminApply/setImageAdmin",
+    method: "post",
+    data
+  })
+}
+
+/** 查 校友相册分类*/
+export function getCategoryImagePage(params) {
+  return request({
+    url: "/alumniCategoryImage/queryCategoryImagePage",
+    method: "get",
+    params
+  })
+}
+
+/** 新增相册分类 */
+export function addCategoryImage(data) {
+  return request({
+    url: "/alumniCategoryImage/insertCategoryImage",
+    method: "post",
+    data
+  })
+}
+
+/** 更新相册分类 */
+export function updateCategoryImage(data) {
+  return request({
+    url: "/alumniCategoryImage/updateCategoryImage",
+    method: "post",
+    data
+  })
+}
+
+/** 删 校友相册分类*/
+export function deleteCategoryImage(id) {
+  return request({
+    url: `/alumniCategoryImage/deleteCategoryImage?id=${id}`,
+    method: "get"
+  })
+}
+
+/** 获取校友相册审核分页数据*/
+export function getImageApplyPage(params) {
+  return request({
+    url: "/alumniImage/queryImageApplyPage",
+    method: "get",
+    params
+  })
+}
+
+/** 审核校友相册数据*/
+export function getExamineImage(params) {
+  return request({
+    url: "/alumniImage/toExamineImage",
+    method: "get",
+    params
+  })
+}

+ 10 - 0
src/api/upload/index.js

@@ -0,0 +1,10 @@
+import { request } from "@/utils/service"
+
+/** 图片上传 */
+export function uploadFile(data) {
+  return request({
+    url: "/file/uploadFile",
+    method: "post",
+    data
+  })
+}

+ 156 - 0
src/components/Map/index.vue

@@ -0,0 +1,156 @@
+<template>
+  <div class="box">
+    <el-input
+      id="searchInputId"
+      class="common_layout"
+      placeholder="输入关键词搜索位置"
+      style="width: 36%"
+      v-model="positionInput"
+      @keyup.enter="searchLocation"
+    />
+    <el-button @click="searchLocation">搜索</el-button>
+
+    <div class="list">
+      <div class="list_item" :title="item.name" v-for="item in addressList" :key="item.id" @click="clickItem(item)">
+        {{ item.name }}
+      </div>
+    </div>
+  </div>
+
+  <div class="gdmap_container">
+    <div id="container" />
+  </div>
+</template>
+
+<script setup>
+import AMapLoader from "@amap/amap-jsapi-loader"
+import { onMounted, onUnmounted, ref } from "vue"
+import { ElMessage } from "element-plus"
+
+const $emit = defineEmits(["change"])
+
+const positionInput = ref("")
+
+let placeSearch
+let map
+
+const addressList = ref([])
+
+onMounted(() => {
+  initMap()
+})
+
+function initMap() {
+  window._AMapSecurityConfig = {
+    securityJsCode: "aeecbbed516ac15400631deb4ba44c79"
+  }
+  AMapLoader.load({
+    key: "d5b00a2684bdf5b38fcfbcf1f35c30bf", // 申请好的Web端开发者Key,首次调用 load 时必填
+    version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+    plugins: ["AMap.AutoComplete", "AMap.PlaceSearch"] // 需要使用的的插件列表,如比例尺'AMap.Scale'等
+  }).then((AMap) => {
+    map = new AMap.Map("container", {
+      resizeEnable: true,
+      viewMode: "3D", // 是否为3D地图模式
+      zoom: 15 // 初始化地图级别
+    })
+
+    const autoOptions = {
+      input: "searchInputId"
+    }
+
+    const auto = new AMap.AutoComplete(autoOptions)
+
+    placeSearch = new AMap.PlaceSearch({
+      pageSize: 20,
+      map: map
+    })
+
+    auto.on("select", select)
+    function select(e) {
+      placeSearch.setCity(e.poi.adcode)
+      placeSearch.search(e.poi.name) //关键字查询查询
+    }
+  })
+}
+
+const searchLocation = () => {
+  // 判断用户是否输入了关键字
+  if (!positionInput.value.trim()) {
+    ElMessage.error("请输入搜索内容!")
+    return
+  }
+
+  // 执行地点搜索,并处理搜索结果
+  placeSearch.search(positionInput.value, (status, result) => {
+    // console.log(status, result)
+    if (status === "complete" && result.info === "OK") {
+      // 如果搜索成功且有搜索结果
+      if (result.poiList.pois.length > 0) {
+        // 取第一个搜索结果,设置地图中心和缩放级别
+        const poi = result.poiList.pois[0]
+        map.setCenter([poi.location.lng, poi.location.lat])
+        map.setZoom(15) //缩放级别
+
+        addressList.value = result.poiList.pois
+        // console.log(addressList.value)
+      } else {
+        ElMessage.error("未找到相关地点!")
+      }
+    } else {
+      ElMessage.error("搜索失败!")
+    }
+  })
+}
+
+const clickItem = (item) => {
+  $emit("change", {
+    address: item.name,
+    lat: item.location.lat,
+    lng: item.location.lng
+  })
+}
+
+onUnmounted(() => {
+  map?.destroy()
+})
+</script>
+
+<style scoped>
+.box {
+  position: relative;
+}
+.list {
+  z-index: 2;
+  position: absolute;
+  top: 32px;
+  left: 0;
+  width: 36%;
+  max-height: 260px;
+  border: 1px solid #eee;
+  overflow-y: auto;
+  background-color: #fff;
+}
+
+.list_item {
+  box-sizing: border-box;
+  line-height: 30px;
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+  text-overflow: ellipsis;
+  border-bottom: 1px solid #ccc;
+}
+.gdmap_container {
+  margin: 10px 0;
+  width: 500px;
+  height: 300px;
+}
+#container {
+  padding: 0px;
+  margin: 0px;
+  border: 1px solid gray;
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 130 - 0
src/components/customEditor/index.vue

@@ -0,0 +1,130 @@
+<template>
+  <div class="editor" ref="divRef">
+    <div class="aie-container">
+      <div class="aie-container-header" />
+      <div class="aie-container-main" />
+      <div class="aie-container-footer" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { AiEditor } from "aieditor"
+import "aieditor/dist/style.css"
+import { onMounted, onUnmounted, ref, watch } from "vue"
+import { getToken } from "@/utils/cache/cookies"
+import { ElMessage } from "element-plus"
+
+const $emit = defineEmits(["change"])
+
+const $props = defineProps(["value"])
+
+const divRef = ref()
+
+let aiEditor = null
+
+onMounted(() => {
+  init()
+})
+
+const init = () => {
+  aiEditor = new AiEditor({
+    element: divRef.value,
+    placeholder: "点击输入内容...",
+    draggable: false,
+    toolbarExcludeKeys: [
+      "brush",
+      "eraser",
+      "link",
+      "code",
+      "todo",
+      "emoji",
+      "video",
+      "ai",
+      "video",
+      "printer",
+      "source-code",
+      "attachment",
+      "quote",
+      "container",
+      "code-block",
+      "table"
+    ],
+    image: {
+      uploadUrl: "https://chtech.ncjti.edu.cn/alumnus/alumni_api/alumni/api/file/uploadFile",
+      uploadFormName: "file",
+      uploadHeaders: {
+        token: getToken() || ""
+      },
+      uploaderEvent: {
+        onSuccess: (_file, response) => {
+          if (response.code == 200) {
+            return {
+              errorCode: 0,
+              data: {
+                src: response.data.fileUrl,
+                width: "auto",
+                height: "200px",
+                class: "image_class",
+                loading: true
+              }
+            }
+          }
+        },
+        onFailed: () => {
+          ElMessage.error("图片上传失败")
+        },
+        onError: () => {
+          ElMessage.error("图片上传错误")
+        }
+      },
+      bubbleMenuItems: ["delete"]
+    },
+    onChange: (aiEditor) => {
+      // 监听到用编辑器内容发生变化了,控制台打印编辑器的 html 内容...
+      if (aiEditor.getHtml() == "<p></p>") {
+        $emit("change", "")
+      } else {
+        $emit("change", aiEditor.getHtml())
+      }
+    }
+  })
+
+  if ($props.value) {
+    aiEditor.focus().insert($props.value)
+  }
+}
+
+watch(
+  () => $props.value,
+  (v) => {
+    // console.log(v)
+    if (!v) {
+      aiEditor?.clear()
+      $emit("change", undefined)
+    }
+  },
+  {
+    immediate: true
+  }
+)
+
+onUnmounted(() => {
+  aiEditor && aiEditor.destroy()
+})
+</script>
+
+<style lang="scss" scoped>
+.editor {
+  width: 100%;
+}
+
+.aie-container-main {
+  height: 200px;
+  overflow-y: auto;
+}
+
+.aie-container-footer {
+  display: none;
+}
+</style>

+ 12 - 1
src/views/account-management/index.vue

@@ -21,10 +21,21 @@ const DEFAULT_FORM_DATA = {
 const dialogVisible = ref(false)
 const formRef = ref(null)
 const formData = ref(cloneDeep(DEFAULT_FORM_DATA))
+
+const validatePass = (_rule, value, callback) => {
+  if (value === "") {
+    callback(new Error("姓名必填"))
+  }
+  const reg = /^[\u4e00-\u9fa5]{2,4}$/
+
+  if (!reg.test(value)) {
+    callback(new Error("姓名格式有误"))
+  }
+}
 const formRules = {
   account: [{ required: true, trigger: "blur", message: "账号必填" }],
   cardNumber: [{ required: true, trigger: "blur", message: "微校卡号必填" }],
-  name: [{ required: true, trigger: "blur", message: "姓名必填" }],
+  name: [{ validator: validatePass, trigger: "blur" }],
   password: [{ required: true, trigger: "blur", message: "密码必填" }],
   roleId: [{ required: true, trigger: "blur", message: "角色必填" }]
 }

+ 238 - 91
src/views/activity-management/audit-list.vue

@@ -1,8 +1,15 @@
 <script setup>
 import { reactive, ref, watch } from "vue"
-import { getTableDataApi } from "@/api/table"
-import { ElMessageBox } from "element-plus"
+import {
+  getActivityApplyPages,
+  exportActivityApplyExcel,
+  getExamineActivity,
+  getDetailInfoById
+} from "@/api/activityManagement"
+import { getQueryCollegesApi, getQueryPeriodsApi, getQueryMajorsApi, getQueryClassesApi } from "@/api/alumniManager"
+import { ElMessageBox, ElMessage } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
+// import { cloneDeep } from "lodash-es"
 
 const loading = ref(false)
 const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
@@ -10,9 +17,14 @@ const { paginationData, handleCurrentChange, handleSizeChange } = usePagination(
 //#region 详情
 const dialogVisible = ref(false)
 const formData = ref({})
-const handleUpdate = (row) => {
+const handleUpdate = async (row) => {
   dialogVisible.value = true
-  formData.value = cloneDeep(row)
+
+  const res = await getDetailInfoById({
+    id: row.id
+  })
+  // console.log(res)
+  formData.value = res.data
 }
 //#endregion
 
@@ -20,40 +32,46 @@ const handleUpdate = (row) => {
 const tableData = ref([])
 const searchFormRef = ref(null)
 const searchData = reactive({
-  organizationName: undefined,
-  initiator: undefined,
+  orgName: undefined,
+  userName: undefined,
   theme: undefined,
-  college: undefined,
-  stage: undefined,
-  major: undefined,
-  class: undefined,
-  status: undefined,
+  collegeId: undefined,
+  colleges: [],
+  periodId: undefined,
+  periods: [],
+  majorId: undefined,
+  majors: [],
+  classId: undefined,
+  classList: [],
+  isPass: undefined,
   createTime: null,
   activityTime: null,
   registrationTime: null
 })
 const getTableData = () => {
   loading.value = true
-  getTableDataApi({
+  getActivityApplyPages({
     currentPage: paginationData.currentPage,
-    size: paginationData.pageSize,
-    organizationName: searchData.organizationName,
-    initiator: searchData.initiator,
+    pageCount: paginationData.pageSize,
+    orgName: searchData.orgName,
+    userName: searchData.userName,
     theme: searchData.theme,
-    college: searchData.college,
-    stage: searchData.stage,
-    major: searchData.major,
-    class: searchData.class,
-    status: searchData.status,
-    starTime: searchData.createTime ? searchData.createTime[0] : undefined,
-    endTime: searchData.createTime ? searchData.createTime[1] : undefined,
-    starTime2: searchData.activityTime ? searchData.activityTime[0] : undefined,
-    endTime2: searchData.activityTime ? searchData.activityTime[1] : undefined,
-    starTime3: searchData.registrationTime ? searchData.registrationTime[0] : undefined,
-    endTime3: searchData.registrationTime ? searchData.registrationTime[1] : undefined
+    collegeId: searchData.collegeId,
+    periodId: searchData.periodId,
+    majorId: searchData.majorId,
+    classId: searchData.classId,
+    isPass: searchData.isPass,
+    createStartTime: searchData.createTime ? searchData.createTime[0] : undefined,
+    createEndTime: searchData.createTime ? searchData.createTime[1] : undefined,
+    startTime: searchData.activityTime ? searchData.activityTime[0] : undefined,
+    endTime: searchData.activityTime ? searchData.activityTime[1] : undefined,
+    signsTime: searchData.registrationTime ? searchData.registrationTime[0] : undefined,
+    signeTime: searchData.registrationTime ? searchData.registrationTime[1] : undefined
   })
     .then(({ data }) => {
-      paginationData.total = data.total
+      // console.log(data)
+
+      paginationData.total = data.totalCount
       tableData.value = data.list
     })
     .catch(() => {
@@ -70,11 +88,45 @@ const resetSearch = () => {
   searchFormRef.value?.resetFields()
   handleSearch()
 }
+
+// 获取学院集合
+const getColleges = async () => {
+  const res = await getQueryCollegesApi()
+  // console.log(res)
+  searchData.colleges = res.data
+}
+
+// 学院筛选框切换回调
+const changeCollege = async (e) => {
+  const res = await getQueryPeriodsApi({
+    collegeId: e
+  })
+  // console.log(res)
+  searchData.periods = res.data
+}
+
+// 学段筛选框切换回调
+const changePeriod = async (e) => {
+  const res = await getQueryMajorsApi({
+    periodId: e
+  })
+  searchData.majors = res.data
+}
+
+// 专业筛选框切换回调
+const changeMajorId = async (e) => {
+  const res = await getQueryClassesApi({
+    majorId: e
+  })
+  searchData.classList = res.data
+}
 //#endregion
 
 /** 监听分页参数的变化 */
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 
+watch([() => paginationData.currentPage, () => paginationData.pageSize], getColleges, { immediate: true })
+
 /** 通过 */
 const handlePass = (row) => {
   ElMessageBox.confirm("确认通过?", "提示", {
@@ -82,27 +134,80 @@ const handlePass = (row) => {
     cancelButtonText: "取消",
     type: "warning"
   }).then(() => {
-    // todo 调用通过接口
-    console.log(row)
+    checkReq(2, row.id)
   })
 }
 
 /** 拒绝 */
 const handleReject = (row) => {
-  ElMessageBox.confirm("确认拒绝?", "提示", {
+  ElMessageBox.prompt("确认拒绝?", "提示", {
     confirmButtonText: "确定",
     cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    // todo 调用拒绝接口
-    console.log(row)
+    inputPlaceholder: "请输入拒绝的原因"
+  }).then(({ value }) => {
+    if (value && value.trim()) {
+      checkReq(3, row.id, value)
+    } else {
+      ElMessage.warning("拒绝原因为必填")
+      handleReject(row)
+    }
+  })
+}
+
+// 审核请求
+const checkReq = async (isPass, id, passValue = "") => {
+  await getExamineActivity({
+    id,
+    isPass,
+    passValue
   })
+
+  ElMessage.success("操作成功")
+  getTableData()
 }
 
 /** 导出 */
 const handleDownload = () => {
-  // todo 调用导出接口
-  console.log("导出")
+  ElMessageBox.confirm("确认导出吗?", "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning"
+  }).then(() => {
+    downloadReq()
+  })
+}
+
+// 导出请求
+const downloadReq = async () => {
+  const res = await exportActivityApplyExcel({
+    orgName: searchData.orgName,
+    userName: searchData.userName,
+    theme: searchData.theme,
+    collegeId: searchData.collegeId,
+    periodId: searchData.periodId,
+    majorId: searchData.majorId,
+    classId: searchData.classId,
+    isPass: searchData.isPass,
+    createStartTime: searchData.createTime ? searchData.createTime[0] : undefined,
+    createEndTime: searchData.createTime ? searchData.createTime[1] : undefined,
+    startTime: searchData.activityTime ? searchData.activityTime[0] : undefined,
+    endTime: searchData.activityTime ? searchData.activityTime[1] : undefined,
+    signsTime: searchData.registrationTime ? searchData.registrationTime[0] : undefined,
+    signeTime: searchData.registrationTime ? searchData.registrationTime[1] : undefined
+  })
+  // console.log(res)
+  // 请求成功返回后,获取到Excel文件的二进制数据
+  const blob = new Blob([res], { type: "application/vnd.ms-excel" })
+  // 创建下载链接
+  const downloadUrl = URL.createObjectURL(blob)
+  // 创建一个隐藏的a标签,设置下载链接和文件名,模拟点击下载
+  const link = document.createElement("a")
+  link.style.display = "none"
+  link.href = downloadUrl
+  link.download = "活动管理审核.xlsx"
+  document.body.appendChild(link)
+  link.click()
+  document.body.removeChild(link)
 }
 </script>
 
@@ -111,38 +216,57 @@ const handleDownload = () => {
     <el-card v-loading="loading" header="审核列表">
       <div class="toolbar-wrapper">
         <el-form ref="searchFormRef" :inline="true" :model="searchData">
-          <el-form-item prop="organizationName" label="组织名称">
-            <el-input v-model="searchData.organizationName" placeholder="请输入" />
+          <el-form-item prop="orgName" label="组织名称">
+            <el-input v-model="searchData.orgName" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="initiator" label="发起人">
-            <el-input v-model="searchData.initiator" placeholder="请输入" />
+          <el-form-item prop="userName" label="发起人">
+            <el-input v-model="searchData.userName" placeholder="请输入" />
           </el-form-item>
           <el-form-item prop="theme" label="活动主题">
             <el-input v-model="searchData.theme" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="college" label="学院">
-            <el-select v-model="searchData.college" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="collegeId" label="学院">
+            <el-select v-model="searchData.collegeId" placeholder="请选择" style="width: 178px" @change="changeCollege">
+              <el-option v-for="item in searchData.colleges" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="stage" label="学段">
-            <el-select v-model="searchData.stage" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="periodId" label="学段">
+            <el-select
+              v-model="searchData.periodId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.collegeId"
+              @change="changePeriod"
+            >
+              <el-option v-for="item in searchData.periods" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="major" label="专业">
-            <el-select v-model="searchData.major" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="majorId" label="专业">
+            <el-select
+              v-model="searchData.majorId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.periodId"
+              @change="changeMajorId"
+            >
+              <el-option v-for="item in searchData.majors" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="class" label="班级">
-            <el-select v-model="searchData.class" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="classId" label="班级">
+            <el-select
+              v-model="searchData.classId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.majorId"
+            >
+              <el-option v-for="item in searchData.classList" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="status" label="状态">
-            <el-select v-model="searchData.status" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="isPass" label="状态">
+            <el-select v-model="searchData.isPass" placeholder="请选择" style="width: 178px">
+              <el-option label="待审核" value="1" />
+              <el-option label="已通过" value="2" />
+              <el-option label="已拒绝" value="3" />
             </el-select>
           </el-form-item>
           <el-form-item prop="createTime" label="创建时间">
@@ -151,7 +275,7 @@ const handleDownload = () => {
               type="datetimerange"
               start-placeholder="开始时间"
               end-placeholder="结束时间"
-              value-format="x"
+              value-format="YYYY-MM-DD HH:mm:ss"
             />
           </el-form-item>
           <el-form-item prop="activityTime" label="活动时间">
@@ -160,7 +284,7 @@ const handleDownload = () => {
               type="datetimerange"
               start-placeholder="开始时间"
               end-placeholder="结束时间"
-              value-format="x"
+              value-format="YYYY-MM-DD HH:mm:ss"
             />
           </el-form-item>
           <el-form-item prop="registrationTime" label="报名时间">
@@ -169,7 +293,7 @@ const handleDownload = () => {
               type="datetimerange"
               start-placeholder="开始时间"
               end-placeholder="结束时间"
-              value-format="x"
+              value-format="YYYY-MM-DD HH:mm:ss"
             />
           </el-form-item>
           <el-form-item>
@@ -183,27 +307,44 @@ const handleDownload = () => {
       </div>
       <div class="table-wrapper">
         <el-table :data="tableData" max-height="500">
-          <el-table-column type="index" label="序号" width="100" align="center" />
-          <el-table-column prop="initiator" label="发起人" align="center" />
-          <el-table-column prop="college" label="学院" align="center" />
-          <el-table-column prop="stage" label="学段" align="center" />
-          <el-table-column prop="major" label="专业" align="center" />
-          <el-table-column prop="class" label="班级" align="center" />
-          <el-table-column prop="organizationName" label="所属组织" align="center" />
-          <el-table-column prop="theme" label="活动主题" align="center" />
-          <el-table-column prop="activityTime" label="活动开始时间" align="center" />
-          <el-table-column prop="registrationTime" label="报名开始时间" align="center" />
-          <el-table-column prop="todo" label="已报名" align="center" />
-          <el-table-column prop="todo" label="已签到" align="center" />
-          <el-table-column prop="status" label="状态" align="center" />
-          <el-table-column prop="todo" label="审核人" align="center" />
-          <el-table-column prop="todo" label="审核时间" align="center" />
-          <el-table-column prop="createTime" label="创建时间" align="center" />
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="name" label="发起人" align="center" show-overflow-tooltip />
+          <el-table-column prop="collegeName" label="学院" align="center" show-overflow-tooltip />
+          <el-table-column prop="periodName" label="学段" align="center" show-overflow-tooltip />
+          <el-table-column prop="majorName" label="专业" align="center" show-overflow-tooltip />
+          <el-table-column prop="className" label="班级" align="center" />
+          <el-table-column prop="orgName" label="所属组织" align="center" show-overflow-tooltip />
+          <el-table-column prop="theme" label="活动主题" align="center" show-overflow-tooltip />
+          <el-table-column prop="startTime" label="活动开始时间" align="center" show-overflow-tooltip />
+          <el-table-column prop="signsTime" label="报名开始时间" align="center" show-overflow-tooltip />
+          <el-table-column label="已报名" align="center" show-overflow-tooltip>
+            <template #default="{ row }">
+              <div style="color: #d43030" v-if="row.reportNumber == 0">未报名</div>
+              <div style="color: #366fff" v-else>已报名</div>
+            </template>
+          </el-table-column>
+          <el-table-column label="已签到" align="center" show-overflow-tooltip>
+            <template #default="{ row }">
+              <div style="color: #d43030" v-if="row.signinNumber == 0">未签到</div>
+              <div style="color: #366fff" v-else>已签到</div>
+            </template>
+          </el-table-column>
+          <el-table-column label="状态" align="center" show-overflow-tooltip>
+            <template #default="{ row }">
+              <div style="color: #366fff" v-if="row.passName == '已通过'">已通过</div>
+              <div style="color: #e6a23c" v-if="row.passName == '待审核'">待审核</div>
+              <div style="color: #d43030" v-if="row.passName == '已拒绝'">已拒绝</div>
+            </template>
+          </el-table-column>
+
+          <el-table-column prop="applyUserAdmin" label="审核人" align="center" show-overflow-tooltip />
+          <el-table-column prop="passTime" label="审核时间" align="center" show-overflow-tooltip />
+          <el-table-column prop="createTime" label="创建时间" align="center" show-overflow-tooltip />
           <el-table-column fixed="right" label="操作" width="200" align="center">
-            <template #default="scope">
-              <el-link type="primary" @click="handleUpdate(scope.row)">详情</el-link>
-              <el-link type="danger" @click="handleReject(scope.row)">拒绝</el-link>
-              <el-link type="success" @click="handlePass(scope.row)">同意</el-link>
+            <template #default="{ row }">
+              <el-link type="primary" @click="handleUpdate(row)">详情</el-link>
+              <el-link type="danger" v-if="row.passName == '待审核'" @click="handleReject(row)">拒绝</el-link>
+              <el-link type="success" v-if="row.passName == '待审核'" @click="handlePass(row)">同意</el-link>
             </template>
           </el-table-column>
         </el-table>
@@ -222,25 +363,25 @@ const handleDownload = () => {
       </div>
     </el-card>
     <!-- 详情 -->
-    <el-dialog v-model="dialogVisible" title="详情" @closed="resetForm" width="50%">
-      <div>
-        <h2>欢迎加入上海足球协会</h2>
-        <p>时间:2024-02-0216:53至2024-12-31 20:53</p>
-        <p>地点:上海市嘉定区安辰路999号</p>
-        <img src="@/assets/image.png" width="100%" />
-        <p>详情...</p>
-        <h3>已报名 (12 人)</h3>
+    <el-dialog v-model="dialogVisible" title="详情" width="50%" top="5vh">
+      <div v-if="formData">
+        <h2>{{ formData.theme }}</h2>
+        <p>时间:{{ formData.startTime }}至{{ formData.endTime }}</p>
+        <p>地点:{{ formData.address }}</p>
+        <img v-if="formData.poster" :src="formData.poster" width="100%" />
+        <div class="rich" v-html="formData.themeDetail" />
+        <h3>已报名 ({{ formData?.reportDatas?.length }} 人)</h3>
         <div class="user-list">
-          <div class="user-item" v-for="(item, index) in 10" :key="index">
+          <div class="user-item" v-for="(item, index) in formData.reportDatas" :key="index">
             <el-avatar :size="40" src="" />
-            <span>姓名</span>
+            <span>{{ item.name }}</span>
           </div>
         </div>
-        <h3>签到人员 (12 人)</h3>
+        <h3>签到人员 ({{ formData?.signinDatas?.length }} 人)</h3>
         <div class="user-list">
-          <div class="user-item" v-for="(item, index) in 10" :key="index">
+          <div class="user-item" v-for="(item, index) in formData.signinDatas" :key="index">
             <el-avatar :size="40" src="" />
-            <span>姓名</span>
+            <span>{{ item.name }}</span>
           </div>
         </div>
       </div>
@@ -265,6 +406,12 @@ const handleDownload = () => {
   justify-content: flex-end;
 }
 
+.rich {
+  ::v-deep(img) {
+    height: 200px;
+  }
+}
+
 .user-list {
   display: flex;
   flex-wrap: wrap;

+ 113 - 20
src/views/activity-management/index.vue

@@ -6,30 +6,44 @@ import {
   updateActivity,
   getClubPage,
   deleteActivityById,
-  exportActivityExcel
+  exportActivityExcel,
+  setImageAdmin,
+  getDetailInfoById
 } from "@/api/activityManagement"
+import { getPageUser } from "@/api/schoolEndorsement"
 import { getQueryCollegesApi, getQueryPeriodsApi, getQueryMajorsApi, getQueryClassesApi } from "@/api/alumniManager"
 import { ElMessage, ElMessageBox } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
 import { cloneDeep } from "lodash-es"
+import Map from "@/components/Map/index.vue"
+import CustomEditor from "@/components/customEditor/index.vue"
 
 const loading = ref(false)
 const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
 
+const mapDialogVisible = ref(false)
+
 //#region 设置管理员
 const SET_ADMIN_DEFAULT_FORM_DATA = {
-  list: []
+  list: [],
+  admins: []
 }
 const setAdminDialogVisible = ref(false)
 const setAdminFormRef = ref(null)
 const setAdminFormData = ref(cloneDeep(SET_ADMIN_DEFAULT_FORM_DATA))
 const setAdminFormRules = {
-  list: [{ required: true, trigger: "blur", message: "必填" }]
+  admins: [{ required: true, trigger: "blur", message: "必填" }]
 }
 const handleSetAdmin = () => {
-  setAdminFormRef.value?.validate((valid, fields) => {
+  setAdminFormRef.value?.validate(async (valid, fields) => {
     if (!valid) return console.error("表单校验不通过", fields)
     // todo 调用设置管理员接口
+
+    await setImageAdmin(setAdminFormData.value)
+
+    ElMessage.success("操作成功")
+    setAdminDialogVisible.value = false
+    resetSetAdminForm()
   })
 }
 const resetSetAdminForm = () => {
@@ -56,11 +70,21 @@ const DEFAULT_FORM_DATA = {
   totalNumber: undefined,
   scope: "",
   phone: "",
-  isImage: false
+  isImage: 2
 }
 const dialogVisible = ref(false)
 const formRef = ref(null)
 const formData = ref(cloneDeep(DEFAULT_FORM_DATA))
+const validatePass = (_rule, value, callback) => {
+  if (value === "") {
+    callback(new Error("咨询方式必填"))
+  }
+  const reg = /^1[3-9]\d{9}$/
+
+  if (!reg.test(value)) {
+    callback(new Error("咨询方式格式有误,请输入手机号"))
+  }
+}
 const formRules = {
   orgId: [{ required: true, trigger: "blur", message: "必填" }],
   theme: [{ required: true, trigger: "blur", message: "必填" }],
@@ -71,7 +95,7 @@ const formRules = {
   registrationTime: [{ required: true, trigger: "blur", message: "必填" }],
   totalNumber: [{ required: true, trigger: "blur", message: "必填" }],
   scope: [{ required: true, trigger: "blur", message: "必填" }],
-  phone: [{ required: true, trigger: "blur", message: "必填" }]
+  phone: [{ validator: validatePass, trigger: "blur" }]
 }
 const handleCreateOrUpdate = () => {
   formRef.value?.validate((valid, fields) => {
@@ -82,8 +106,8 @@ const handleCreateOrUpdate = () => {
       ...formData.value,
       startTime: formData.value.activityTime ? formData.value.activityTime[0] : undefined,
       endTime: formData.value.activityTime ? formData.value.activityTime[1] : undefined,
-      lng: "101",
-      lat: "78",
+      lng: "115.831653",
+      lat: "28.705019",
       signsTime: formData.value.registrationTime ? formData.value.registrationTime[0] : undefined,
       signeTime: formData.value.registrationTime ? formData.value.registrationTime[1] : undefined
     })
@@ -120,8 +144,19 @@ const handleDelete = (row) => {
 
 //#region 改
 const handleUpdate = (row) => {
+  getOrgList()
+  getDetailInfo(row.id)
+}
+
+const getDetailInfo = async (id) => {
+  const res = await getDetailInfoById({
+    id
+  })
+  // console.log(res)
+  formData.value = res.data
+  formData.value.activityTime = [res.data.startTime, res.data.endTime]
+  formData.value.registrationTime = [res.data.signsTime, res.data.signeTime]
   dialogVisible.value = true
-  formData.value = cloneDeep(row)
 }
 //#endregion
 
@@ -166,7 +201,7 @@ const getTableData = () => {
     signeTime: searchData.registrationTime ? searchData.registrationTime[1] : undefined
   })
     .then(({ data }) => {
-      console.log(data)
+      // console.log(data)
 
       paginationData.total = data.totalCount
       tableData.value = data.list
@@ -241,8 +276,34 @@ const changeOrg = (e) => {
   formData.value.orgName = formData.value.organization[v].name
 }
 
+// 设置管理员按钮回调
+const handleSet = () => {
+  setAdminDialogVisible.value = true
+  getUserData()
+}
+
+// 获取用户列表以设置管理员
+const getUserData = async () => {
+  const res = await getPageUser({
+    currentPage: 1,
+    pageCount: 999
+  })
+  // console.log(res)
+  setAdminFormData.value.list = res.data.list
+}
+
+const clickInput = () => {
+  mapDialogVisible.value = true
+}
+
 //#endregion
 
+// 富文本编辑器组件自定义事件
+const getEditorValue = (data) => {
+  // console.log(data)
+  formData.value.themeDetail = data
+}
+
 /** 监听分页参数的变化 */
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 
@@ -291,6 +352,17 @@ const downloadReq = async () => {
   link.click()
   document.body.removeChild(link)
 }
+
+// 地图弹窗自定义事件
+const getAddressInfo = (data) => {
+  // console.log(data)
+
+  const { address, lat, lng } = data
+  mapDialogVisible.value = false
+  formData.value.address = address
+  formData.value.lat = lat
+  formData.value.lng = lng
+}
 </script>
 
 <template>
@@ -385,7 +457,7 @@ const downloadReq = async () => {
           </el-form-item>
         </el-form>
         <div>
-          <el-button type="primary" @click="setAdminDialogVisible = true">设置管理员</el-button>
+          <el-button type="primary" @click="handleSet">设置管理员</el-button>
           <el-button type="primary" @click="handleAdd">新增</el-button>
           <el-button plain @click="handleDownload">导出</el-button>
         </div>
@@ -462,7 +534,7 @@ const downloadReq = async () => {
       :title="formData.id === undefined ? '创建活动' : '编辑活动'"
       @closed="resetForm"
       width="50%"
-      top="5vh"
+      top="1vh"
     >
       <el-form ref="formRef" :model="formData" :rules="formRules" label-width="auto" size="large">
         <el-form-item prop="orgId" label="所属组织">
@@ -474,7 +546,7 @@ const downloadReq = async () => {
           <el-input v-model="formData.theme" placeholder="请输入" />
         </el-form-item>
         <el-form-item prop="themeDetail" label="活动详情">
-          <el-input v-model="formData.themeDetail" type="textarea" :rows="5" placeholder="请输入" />
+          <CustomEditor v-if="dialogVisible" :value="formData.themeDetail" @change="getEditorValue" />
         </el-form-item>
         <el-form-item prop="activityTime" label="活动时间">
           <el-date-picker
@@ -486,7 +558,10 @@ const downloadReq = async () => {
           />
         </el-form-item>
         <el-form-item prop="address" label="活动地址">
-          <el-input v-model="formData.address" placeholder="请输入" />
+          <el-input v-model="formData.address" placeholder="请选择活动地址" v-show="false" />
+          <div class="input_box" :class="{ novalue: !formData.address }" @click="clickInput">
+            {{ formData.address ? formData.address : "请选择活动地址" }}
+          </div>
         </el-form-item>
         <el-form-item prop="describes" label="参与说明">
           <el-input v-model="formData.describes" type="textarea" :rows="5" placeholder="请输入" />
@@ -505,15 +580,15 @@ const downloadReq = async () => {
         </el-form-item>
         <el-form-item prop="scope" label="报名范围">
           <el-select v-model="formData.scope" placeholder="请选择">
-            <el-option label="全部" value="2" />
-            <el-option label="组织成员" value="1" />
+            <el-option label="全部" :value="2" />
+            <el-option label="组织成员" :value="1" />
           </el-select>
         </el-form-item>
         <el-form-item prop="phone" label="咨询方式">
           <el-input v-model="formData.phone" placeholder="请输入" />
         </el-form-item>
         <el-form-item prop="isImage" label="活动相册">
-          <el-switch v-model="formData.isImage" />
+          <el-switch v-model="formData.isImage" :active-value="1" :inactive-value="2" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -530,9 +605,9 @@ const downloadReq = async () => {
         label-width="auto"
         size="large"
       >
-        <el-form-item prop="list" label="管理员">
-          <el-select v-model="setAdminFormData.list" placeholder="请选择" multiple>
-            <el-option label="todo" value="todo" />
+        <el-form-item prop="admins" label="管理员">
+          <el-select v-model="setAdminFormData.admins" placeholder="请选择" multiple value-key="name">
+            <el-option v-for="item in setAdminFormData.list" :label="item.name" :value="item" :key="item.id" />
           </el-select>
         </el-form-item>
       </el-form>
@@ -541,6 +616,11 @@ const downloadReq = async () => {
         <el-button type="primary" @click="handleSetAdmin" :loading="loading">确定</el-button>
       </template>
     </el-dialog>
+
+    <!-- 地图弹窗 -->
+    <el-dialog v-model="mapDialogVisible" title="选择地址" width="50%">
+      <Map @change="getAddressInfo" />
+    </el-dialog>
   </div>
 </template>
 
@@ -560,4 +640,17 @@ const downloadReq = async () => {
   display: flex;
   justify-content: flex-end;
 }
+
+.input_box {
+  padding: 0 10px;
+  width: 100%;
+  line-height: 40px;
+  border-radius: 5px;
+  border: 1px solid #dcdfe6;
+  cursor: pointer;
+}
+
+.novalue {
+  color: #a8abb2;
+}
 </style>

+ 154 - 61
src/views/alumni-album/audit-list.vue

@@ -1,8 +1,9 @@
 <script setup>
 import { reactive, ref, watch } from "vue"
-import { getTableDataApi } from "@/api/table"
-import { ElMessageBox } from "element-plus"
+import { getImageApplyPage, getExamineImage } from "@/api/alumniAlbum"
+import { ElMessageBox, ElMessage } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
+import { getQueryCollegesApi, getQueryPeriodsApi, getQueryMajorsApi, getQueryClassesApi } from "@/api/alumniManager"
 
 const loading = ref(false)
 const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
@@ -11,32 +12,38 @@ const { paginationData, handleCurrentChange, handleSizeChange } = usePagination(
 const tableData = ref([])
 const searchFormRef = ref(null)
 const searchData = reactive({
-  organizationName: undefined,
-  applicant: undefined,
-  status: undefined,
-  college: undefined,
-  stage: undefined,
-  major: undefined,
-  class: undefined,
+  orgName: undefined,
+  userName: undefined,
+  isPass: undefined,
+  collegeId: undefined,
+  colleges: [],
+  periodId: undefined,
+  periods: [],
+  majorId: undefined,
+  majors: [],
+  classId: undefined,
+  classList: [],
   createTime: null
 })
 const getTableData = () => {
   loading.value = true
-  getTableDataApi({
+  getImageApplyPage({
     currentPage: paginationData.currentPage,
-    size: paginationData.pageSize,
-    organizationName: searchData.organizationName,
-    applicant: searchData.applicant,
-    status: searchData.applicant,
-    college: searchData.college,
-    stage: searchData.stage,
-    major: searchData.major,
-    class: searchData.class,
-    starTime: searchData.createTime ? searchData.createTime[0] : undefined,
+    pageCount: paginationData.pageSize,
+    orgName: searchData.orgName,
+    userName: searchData.userName,
+    isPass: searchData.isPass,
+    collegeId: searchData.collegeId,
+    periodId: searchData.periodId,
+    majorId: searchData.majorId,
+    classId: searchData.classId,
+    startTime: searchData.createTime ? searchData.createTime[0] : undefined,
     endTime: searchData.createTime ? searchData.createTime[1] : undefined
   })
     .then(({ data }) => {
-      paginationData.total = data.total
+      // console.log(data)
+
+      paginationData.total = data.totalCount
       tableData.value = data.list
     })
     .catch(() => {
@@ -55,9 +62,43 @@ const resetSearch = () => {
 }
 //#endregion
 
+// 获取学院集合
+const getColleges = async () => {
+  const res = await getQueryCollegesApi()
+  // console.log(res)
+  searchData.colleges = res.data
+}
+
+// 学院筛选框切换回调
+const changeCollege = async (e) => {
+  const res = await getQueryPeriodsApi({
+    collegeId: e
+  })
+  // console.log(res)
+  searchData.periods = res.data
+}
+
+// 学段筛选框切换回调
+const changePeriod = async (e) => {
+  const res = await getQueryMajorsApi({
+    periodId: e
+  })
+  searchData.majors = res.data
+}
+
+// 专业筛选框切换回调
+const changeMajorId = async (e) => {
+  const res = await getQueryClassesApi({
+    majorId: e
+  })
+  searchData.classList = res.data
+}
+
 /** 监听分页参数的变化 */
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 
+watch([() => paginationData.currentPage, () => paginationData.pageSize], getColleges, { immediate: true })
+
 /** 通过 */
 const handlePass = (row) => {
   ElMessageBox.confirm("确认通过?", "提示", {
@@ -65,22 +106,37 @@ const handlePass = (row) => {
     cancelButtonText: "取消",
     type: "warning"
   }).then(() => {
-    // todo 调用通过接口
-    console.log(row)
+    checkReq(2, row.id)
   })
 }
 
 /** 拒绝 */
 const handleReject = (row) => {
-  ElMessageBox.confirm("确认拒绝?", "提示", {
+  ElMessageBox.prompt("确认拒绝?", "提示", {
     confirmButtonText: "确定",
     cancelButtonText: "取消",
-    type: "warning"
-  }).then(() => {
-    // todo 调用拒绝接口
-    console.log(row)
+    inputPlaceholder: "请输入拒绝的原因"
+  }).then(({ value }) => {
+    if (value && value.trim()) {
+      checkReq(3, row.id, value)
+    } else {
+      ElMessage.warning("拒绝原因为必填")
+      handleReject(row)
+    }
   })
 }
+
+// 审核请求
+const checkReq = async (isPass, id, passValue = "") => {
+  await getExamineImage({
+    id,
+    isPass,
+    passValue
+  })
+
+  ElMessage.success("操作成功")
+  getTableData()
+}
 </script>
 
 <template>
@@ -88,35 +144,54 @@ const handleReject = (row) => {
     <el-card v-loading="loading" header="审核列表">
       <div class="toolbar-wrapper">
         <el-form ref="searchFormRef" :inline="true" :model="searchData">
-          <el-form-item prop="organizationName" label="组织名称">
-            <el-input v-model="searchData.organizationName" placeholder="请输入" />
+          <el-form-item prop="orgName" label="组织名称">
+            <el-input v-model="searchData.orgName" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="applicant" label="申请人">
-            <el-input v-model="searchData.applicant" placeholder="请输入" />
+          <el-form-item prop="userName" label="申请人">
+            <el-input v-model="searchData.userName" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="status" label="审核状态">
-            <el-select v-model="searchData.status" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="isPass" label="审核状态">
+            <el-select v-model="searchData.isPass" placeholder="请选择" style="width: 178px">
+              <el-option label="待审核" value="1" />
+              <el-option label="已通过" value="2" />
+              <el-option label="已拒绝" value="3" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="college" label="学院">
-            <el-select v-model="searchData.college" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="collegeId" label="学院">
+            <el-select v-model="searchData.collegeId" placeholder="请选择" style="width: 178px" @change="changeCollege">
+              <el-option v-for="item in searchData.colleges" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="stage" label="学段">
-            <el-select v-model="searchData.stage" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="periodId" label="学段">
+            <el-select
+              v-model="searchData.periodId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.collegeId"
+              @change="changePeriod"
+            >
+              <el-option v-for="item in searchData.periods" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="major" label="专业">
-            <el-select v-model="searchData.major" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="majorId" label="专业">
+            <el-select
+              v-model="searchData.majorId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.periodId"
+              @change="changeMajorId"
+            >
+              <el-option v-for="item in searchData.majors" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="class" label="班级">
-            <el-select v-model="searchData.class" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="classId" label="班级">
+            <el-select
+              v-model="searchData.classId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.majorId"
+            >
+              <el-option v-for="item in searchData.classList" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
           <el-form-item prop="createTime" label="创建时间">
@@ -125,7 +200,7 @@ const handleReject = (row) => {
               type="datetimerange"
               start-placeholder="开始时间"
               end-placeholder="结束时间"
-              value-format="x"
+              value-format="YYYY-MM-DD HH:mm:ss"
             />
           </el-form-item>
           <el-form-item>
@@ -136,21 +211,39 @@ const handleReject = (row) => {
       </div>
       <div class="table-wrapper">
         <el-table :data="tableData" max-height="500">
-          <el-table-column type="index" label="序号" width="100" align="center" />
-          <el-table-column prop="applicant" label="申请人" align="center" />
-          <el-table-column prop="college" label="学院" align="center" />
-          <el-table-column prop="stage" label="学段" align="center" />
-          <el-table-column prop="major" label="专业" align="center" />
-          <el-table-column prop="class" label="班级" align="center" />
-          <el-table-column prop="img" label="图片" align="center" />
-          <el-table-column prop="todo" label="审核人" align="center" />
-          <el-table-column prop="status" label="审核状态" align="center" />
-          <el-table-column prop="todo" label="审核时间" align="center" />
-          <el-table-column prop="createTime" label="创建时间" align="center" />
+          <el-table-column type="index" label="序号" width="80" align="center" />
+          <el-table-column prop="name" label="申请人" align="center" />
+          <el-table-column prop="collegeName" label="学院" align="center" show-overflow-tooltip />
+          <el-table-column prop="periodName" label="学段" align="center" />
+          <el-table-column prop="majorName" label="专业" align="center" show-overflow-tooltip />
+          <el-table-column prop="className" label="班级" align="center" />
+          <el-table-column label="图片" align="center">
+            <template #default="{ row }">
+              <el-image
+                style="width: 100px; height: 100px"
+                :src="row.images[0]"
+                fit="cover"
+                lazy
+                :preview-src-list="row.images"
+                hide-on-click-modal
+                preview-teleported
+              />
+            </template>
+          </el-table-column>
+          <el-table-column prop="adminName" label="审核人" align="center" />
+          <el-table-column label="审核状态" align="center" show-overflow-tooltip>
+            <template #default="{ row }">
+              <div style="color: #366fff" v-if="row.passName == '已通过'">已通过</div>
+              <div style="color: #e6a23c" v-if="row.passName == '待审核'">待审核</div>
+              <div style="color: #d43030" v-if="row.passName == '已拒绝'">已拒绝</div>
+            </template>
+          </el-table-column>
+          <el-table-column prop="passTime" label="审核时间" align="center" show-overflow-tooltip />
+          <el-table-column prop="createTime" label="创建时间" align="center" show-overflow-tooltip />
           <el-table-column fixed="right" label="操作" width="200" align="center">
-            <template #default="scope">
-              <el-link type="danger" @click="handleReject(scope.row)">拒绝</el-link>
-              <el-link type="primary" @click="handlePass(scope.row)">通过</el-link>
+            <template #default="{ row }">
+              <el-link type="danger" v-if="row.passName == '待审核'" @click="handleReject(row)">拒绝</el-link>
+              <el-link type="primary" v-if="row.passName == '待审核'" @click="handlePass(row)">通过</el-link>
             </template>
           </el-table-column>
         </el-table>

+ 11 - 9
src/views/alumni-album/classification-management.vue

@@ -1,6 +1,6 @@
 <script setup>
 import { reactive, ref, watch } from "vue"
-import { createTableDataApi, deleteTableDataApi, updateTableDataApi, getTableDataApi } from "@/api/table"
+import { getCategoryImagePage, addCategoryImage, updateCategoryImage, deleteCategoryImage } from "@/api/alumniAlbum"
 import { ElMessage, ElMessageBox } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
 import { cloneDeep } from "lodash-es"
@@ -23,7 +23,7 @@ const handleCreateOrUpdate = () => {
   formRef.value?.validate((valid, fields) => {
     if (!valid) return console.error("表单校验不通过", fields)
     loading.value = true
-    const api = formData.value.id === undefined ? createTableDataApi : updateTableDataApi
+    const api = formData.value.id === undefined ? addCategoryImage : updateCategoryImage
     api(formData.value)
       .then(() => {
         ElMessage.success("操作成功")
@@ -48,7 +48,7 @@ const handleDelete = (row) => {
     cancelButtonText: "取消",
     type: "warning"
   }).then(() => {
-    deleteTableDataApi(row.id).then(() => {
+    deleteCategoryImage(row.id).then(() => {
       ElMessage.success("删除成功")
       getTableData()
     })
@@ -71,14 +71,16 @@ const searchData = reactive({
 })
 const getTableData = () => {
   loading.value = true
-  getTableDataApi({
+  getCategoryImagePage({
     currentPage: paginationData.currentPage,
-    size: paginationData.pageSize,
-    starTime: searchData.createTime ? searchData.createTime[0] : undefined,
+    pageCount: paginationData.pageSize,
+    startTime: searchData.createTime ? searchData.createTime[0] : undefined,
     endTime: searchData.createTime ? searchData.createTime[1] : undefined
   })
     .then(({ data }) => {
-      paginationData.total = data.total
+      // console.log(data)
+
+      paginationData.total = data.totalCount
       tableData.value = data.list
     })
     .catch(() => {
@@ -112,7 +114,7 @@ watch([() => paginationData.currentPage, () => paginationData.pageSize], getTabl
               type="datetimerange"
               start-placeholder="开始时间"
               end-placeholder="结束时间"
-              value-format="x"
+              value-format="YYYY-MM-DD HH:mm:ss"
             />
           </el-form-item>
           <el-form-item>
@@ -129,7 +131,7 @@ watch([() => paginationData.currentPage, () => paginationData.pageSize], getTabl
           <el-table-column type="index" label="序号" width="100" align="center" />
           <el-table-column prop="name" label="分类名称" align="center" />
           <el-table-column prop="createTime" label="创建时间" align="center" />
-          <el-table-column prop="creator" label="创建人" align="center" />
+          <el-table-column prop="createUser" label="创建人" align="center" />
           <el-table-column fixed="right" label="操作" width="200" align="center">
             <template #default="scope">
               <el-link type="primary" @click="handleUpdate(scope.row)">编辑</el-link>

+ 177 - 46
src/views/alumni-album/index.vue

@@ -1,8 +1,11 @@
 <script setup>
 import { reactive, ref, watch } from "vue"
-import { deleteTableDataApi, getTableDataApi } from "@/api/table"
+import { getImagePage, deleteImageById, setImageAdmin } from "@/api/alumniAlbum"
 import { ElMessage, ElMessageBox } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
+import { getQueryCollegesApi, getQueryPeriodsApi, getQueryMajorsApi, getQueryClassesApi } from "@/api/alumniManager"
+import { getPageUser } from "@/api/schoolEndorsement"
+import { cloneDeep } from "lodash-es"
 
 const loading = ref(false)
 const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
@@ -14,7 +17,7 @@ const handleDelete = (row) => {
     cancelButtonText: "取消",
     type: "warning"
   }).then(() => {
-    deleteTableDataApi(row.id).then(() => {
+    deleteImageById(row.id).then(() => {
       ElMessage.success("删除成功")
       getTableData()
     })
@@ -26,29 +29,33 @@ const handleDelete = (row) => {
 const tableData = ref([])
 const searchFormRef = ref(null)
 const searchData = reactive({
-  name: undefined,
-  organizationName: undefined,
-  college: undefined,
-  stage: undefined,
-  major: undefined,
-  class: undefined
+  userName: undefined,
+  orgName: undefined,
+  collegeId: undefined,
+  colleges: [],
+  periodId: undefined,
+  periods: [],
+  majorId: undefined,
+  majors: [],
+  classId: undefined,
+  classList: []
 })
 const getTableData = () => {
   loading.value = true
-  getTableDataApi({
+  getImagePage({
     currentPage: paginationData.currentPage,
-    size: paginationData.pageSize,
-    name: searchData.name,
-    organizationName: searchData.organizationName,
-    college: searchData.college,
-    stage: searchData.stage,
-    major: searchData.major,
-    class: searchData.class,
-    starTime: searchData.createTime ? searchData.createTime[0] : undefined,
-    endTime: searchData.createTime ? searchData.createTime[1] : undefined
+    pageCount: paginationData.pageSize,
+    userName: searchData.userName,
+    orgName: searchData.orgName,
+    collegeId: searchData.collegeId,
+    periodId: searchData.periodId,
+    majorId: searchData.majorId,
+    classId: searchData.classId
   })
     .then(({ data }) => {
-      paginationData.total = data.total
+      // console.log(data)
+
+      paginationData.total = data.totalCount
       tableData.value = data.list
     })
     .catch(() => {
@@ -65,16 +72,89 @@ const resetSearch = () => {
   searchFormRef.value?.resetFields()
   handleSearch()
 }
+
+// 获取学院集合
+const getColleges = async () => {
+  const res = await getQueryCollegesApi()
+  // console.log(res)
+  searchData.colleges = res.data
+}
+
+// 学院筛选框切换回调
+const changeCollege = async (e) => {
+  const res = await getQueryPeriodsApi({
+    collegeId: e
+  })
+  // console.log(res)
+  searchData.periods = res.data
+}
+
+// 学段筛选框切换回调
+const changePeriod = async (e) => {
+  const res = await getQueryMajorsApi({
+    periodId: e
+  })
+  searchData.majors = res.data
+}
+
+// 专业筛选框切换回调
+const changeMajorId = async (e) => {
+  const res = await getQueryClassesApi({
+    majorId: e
+  })
+  searchData.classList = res.data
+}
 //#endregion
 
 /** 监听分页参数的变化 */
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 
+watch([() => paginationData.currentPage, () => paginationData.pageSize], getColleges, { immediate: true })
+
 /** 设置管理员 */
 const handleSet = () => {
-  // todo 调用设置管理员接口
-  console.log("设置管理员")
+  setAdminDialogVisible.value = true
+  getUserData()
 }
+
+// 获取用户列表以设置管理员
+const getUserData = async () => {
+  const res = await getPageUser({
+    currentPage: 1,
+    pageCount: 999
+  })
+  // console.log(res)
+  setAdminFormData.value.list = res.data.list
+}
+
+//#region 设置管理员
+const SET_ADMIN_DEFAULT_FORM_DATA = {
+  list: [],
+  admins: []
+}
+const setAdminDialogVisible = ref(false)
+const setAdminFormRef = ref(null)
+const setAdminFormData = ref(cloneDeep(SET_ADMIN_DEFAULT_FORM_DATA))
+const setAdminFormRules = {
+  admins: [{ required: true, trigger: "blur", message: "必填" }]
+}
+const handleSetAdmin = () => {
+  setAdminFormRef.value?.validate(async (valid, fields) => {
+    if (!valid) return console.error("表单校验不通过", fields)
+    // todo 调用设置管理员接口
+
+    await setImageAdmin(setAdminFormData.value)
+
+    ElMessage.success("操作成功")
+    setAdminDialogVisible.value = false
+    resetSetAdminForm()
+  })
+}
+const resetSetAdminForm = () => {
+  setAdminFormRef.value?.clearValidate()
+  setAdminFormData.value = cloneDeep(SET_ADMIN_DEFAULT_FORM_DATA)
+}
+//#endregion
 </script>
 
 <template>
@@ -82,30 +162,47 @@ const handleSet = () => {
     <el-card v-loading="loading" header="校友相册">
       <div class="toolbar-wrapper">
         <el-form ref="searchFormRef" :inline="true" :model="searchData">
-          <el-form-item prop="name" label="姓名">
-            <el-input v-model="searchData.name" placeholder="请输入" />
+          <el-form-item prop="userName" label="姓名">
+            <el-input v-model="searchData.userName" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="organizationName" label="组织名称">
-            <el-input v-model="searchData.organizationName" placeholder="请输入" />
+          <el-form-item prop="orgName" label="组织名称">
+            <el-input v-model="searchData.orgName" placeholder="请输入" />
           </el-form-item>
-          <el-form-item prop="college" label="学院">
-            <el-select v-model="searchData.college" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="collegeId" label="学院">
+            <el-select v-model="searchData.collegeId" placeholder="请选择" style="width: 178px" @change="changeCollege">
+              <el-option v-for="item in searchData.colleges" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="stage" label="学段">
-            <el-select v-model="searchData.stage" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="periodId" label="学段">
+            <el-select
+              v-model="searchData.periodId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.collegeId"
+              @change="changePeriod"
+            >
+              <el-option v-for="item in searchData.periods" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="major" label="专业">
-            <el-select v-model="searchData.major" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="majorId" label="专业">
+            <el-select
+              v-model="searchData.majorId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.periodId"
+              @change="changeMajorId"
+            >
+              <el-option v-for="item in searchData.majors" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
-          <el-form-item prop="class" label="班级">
-            <el-select v-model="searchData.class" placeholder="请选择" style="width: 178px">
-              <el-option label="todo" value="todo" />
+          <el-form-item prop="classId" label="班级">
+            <el-select
+              v-model="searchData.classId"
+              placeholder="请选择"
+              style="width: 178px"
+              :disabled="!searchData.majorId"
+            >
+              <el-option v-for="item in searchData.classList" :label="item.name" :value="item.id" :key="item.id" />
             </el-select>
           </el-form-item>
           <el-form-item>
@@ -119,16 +216,29 @@ const handleSet = () => {
       </div>
       <div class="table-wrapper">
         <el-table :data="tableData" max-height="500">
-          <el-table-column type="index" label="序号" width="100" align="center" />
+          <el-table-column type="index" label="序号" width="80" align="center" />
           <el-table-column prop="name" label="姓名" align="center" />
-          <el-table-column prop="college" label="学院" align="center" />
-          <el-table-column prop="stage" label="学段" align="center" />
-          <el-table-column prop="major" label="专业" align="center" />
-          <el-table-column prop="class" label="班级" align="center" />
-          <el-table-column prop="todo" label="分类" align="center" />
-          <el-table-column prop="organizationName" label="组织" align="center" />
-          <el-table-column prop="img" label="图片" align="center" />
-          <el-table-column prop="createTime" label="创建时间" align="center" />
+          <el-table-column prop="collegeName" label="学院" align="center" />
+          <el-table-column prop="periodName" label="学段" align="center" />
+          <el-table-column prop="majorName" label="专业" align="center" />
+          <el-table-column prop="majorName" label="班级" align="center" />
+          <el-table-column prop="categoryName" label="分类" align="center" />
+          <el-table-column prop="orgName" label="组织" align="center" />
+          <el-table-column label="图片" align="center">
+            <template #default="{ row }">
+              <el-image
+                style="width: 100px; height: 100px"
+                :src="row.images[0]"
+                fit="cover"
+                lazy
+                :preview-src-list="row.images"
+                hide-on-click-modal
+                preview-teleported
+              />
+            </template>
+          </el-table-column>
+
+          <el-table-column prop="createTime" label="创建时间" align="center" show-overflow-tooltip />
           <el-table-column fixed="right" label="操作" width="200" align="center">
             <template #default="scope">
               <el-link type="danger" @click="handleDelete(scope.row)">删除</el-link>
@@ -149,6 +259,27 @@ const handleSet = () => {
         />
       </div>
     </el-card>
+
+    <!-- 设置管理员 -->
+    <el-dialog v-model="setAdminDialogVisible" title="设置管理员" @closed="resetSetAdminForm" width="50%">
+      <el-form
+        ref="setAdminFormRef"
+        :model="setAdminFormData"
+        :rules="setAdminFormRules"
+        label-width="auto"
+        size="large"
+      >
+        <el-form-item prop="admins" label="管理员">
+          <el-select v-model="setAdminFormData.admins" placeholder="请选择" multiple value-key="name">
+            <el-option v-for="item in setAdminFormData.list" :label="item.name" :value="item" :key="item.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="setAdminDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleSetAdmin" :loading="loading">确定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 

+ 24 - 5
src/views/news-focus/index.vue

@@ -5,6 +5,7 @@ import { ElMessage, ElMessageBox } from "element-plus"
 import { usePagination } from "@/hooks/usePagination"
 import { cloneDeep } from "lodash-es"
 import { getUserId } from "@/utils/cache/cookies"
+import CustomEditor from "@/components/customEditor/index.vue"
 
 const loading = ref(false)
 const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()
@@ -92,8 +93,10 @@ const handleDelete = (row) => {
 
 //#region 改
 const handleUpdate = (row) => {
-  dialogVisible.value = true
+  // 获取新闻分类下拉列表数据
+  getNewsType()
   formData.value = cloneDeep(row)
+  dialogVisible.value = true
 }
 //#endregion
 
@@ -159,6 +162,12 @@ const changeSelect = (e) => {
 
 //#endregion
 
+// 富文本编辑器组件自定义事件
+const getEditorValue = (data) => {
+  // console.log(data)
+  formData.value.content = data
+}
+
 /** 监听分页参数的变化 */
 watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
 </script>
@@ -196,11 +205,16 @@ watch([() => paginationData.currentPage, () => paginationData.pageSize], getTabl
       <div class="table-wrapper">
         <el-table :data="tableData" max-height="500">
           <el-table-column type="index" label="序号" width="100" align="center" />
-          <el-table-column prop="theme" label="标题" align="center" />
-          <el-table-column prop="content" label="内容" align="center" />
+          <el-table-column prop="theme" label="标题" align="center" show-overflow-tooltip />
+          <el-table-column label="内容" align="center" show-overflow-tooltip>
+            <template #default="{ row }">
+              <div v-html="row.content" />
+            </template>
+          </el-table-column>
+
           <el-table-column prop="categoryName" label="分类" align="center" />
           <el-table-column prop="userName" label="发件人" align="center" />
-          <el-table-column prop="createTime" label="创建时间" align="center" />
+          <el-table-column prop="createTime" label="创建时间" align="center" show-overflow-tooltip />
           <el-table-column fixed="right" label="操作" width="200" align="center">
             <template #default="scope">
               <el-link type="primary" @click="handleUpdate(scope.row)">编辑</el-link>
@@ -242,7 +256,7 @@ watch([() => paginationData.currentPage, () => paginationData.pageSize], getTabl
           </el-select>
         </el-form-item>
         <el-form-item prop="content" label="内容">
-          <el-input v-model="formData.content" type="textarea" :rows="5" placeholder="请输入" />
+          <CustomEditor v-if="dialogVisible" :value="formData.content" @change="getEditorValue" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -287,4 +301,9 @@ watch([() => paginationData.currentPage, () => paginationData.pageSize], getTabl
   display: flex;
   justify-content: flex-end;
 }
+
+::v-deep(.image_class) {
+  width: auto;
+  height: 30px;
+}
 </style>

+ 1 - 1
src/views/news-focus/news-classification.vue

@@ -78,7 +78,7 @@ const getTableData = () => {
     endTime: searchData.createTime ? searchData.createTime[1] : undefined
   })
     .then(({ data }) => {
-      console.log(data)
+      // console.log(data)
 
       paginationData.total = data.totalCount
       tableData.value = data.list

+ 11 - 1
src/views/school-endorsement/index.vue

@@ -59,9 +59,19 @@ const elUploadRef = ref(null)
 const dialogVisible = ref(false)
 const formRef = ref(null)
 const formData = ref(cloneDeep(DEFAULT_FORM_DATA))
+const validatePass = (_rule, value, callback) => {
+  if (value === "") {
+    callback(new Error("姓名必填"))
+  }
+  const reg = /^[\u4e00-\u9fa5]{2,4}$/
+
+  if (!reg.test(value)) {
+    callback(new Error("姓名格式有误"))
+  }
+}
 const formRules = {
   cardNumber: [{ required: true, trigger: "blur", message: "微校卡号必填" }],
-  name: [{ required: true, trigger: "blur", message: "姓名必填" }],
+  name: [{ validator: validatePass, trigger: "blur" }],
   image: [{ required: true, trigger: "blur", message: "照片必填" }],
   descript: [{ required: true, trigger: "blur", message: "个人简介必填" }]
 }