|
|
@@ -1,15 +1,19 @@
|
|
|
<script setup>
|
|
|
-import { reactive, ref, watch, onMounted } from "vue"
|
|
|
+import { reactive, ref, watch, onBeforeMount } from "vue"
|
|
|
import {
|
|
|
- createTableDataApi,
|
|
|
- deleteTableDataApi,
|
|
|
- updateTableDataApi,
|
|
|
+ clubTopupApi,
|
|
|
+ insertClubDataApi,
|
|
|
+ deleteClubDataApi,
|
|
|
+ updateClubDataApi,
|
|
|
getQueryCategoryPageApi,
|
|
|
+ getQueryOrgTreeApi,
|
|
|
getTableDataApi,
|
|
|
- insertClubDataApi
|
|
|
+ getQueryPageUserApi,
|
|
|
+ getAlumniClubExcelApi
|
|
|
} from "@/api/alumniOrganization"
|
|
|
import { ElMessage, ElMessageBox } from "element-plus"
|
|
|
import { usePagination } from "@/hooks/usePagination"
|
|
|
+import { Search } from "@element-plus/icons-vue"
|
|
|
import { cloneDeep } from "lodash-es"
|
|
|
|
|
|
const loading = ref(false)
|
|
|
@@ -18,23 +22,81 @@ const { paginationData, handleCurrentChange, handleSizeChange } = usePagination(
|
|
|
//#region 增
|
|
|
const DEFAULT_FORM_DATA = {
|
|
|
id: undefined,
|
|
|
- name: "",
|
|
|
- classification: undefined,
|
|
|
- administrator: [],
|
|
|
- introduction: "",
|
|
|
- about: ""
|
|
|
+ name: undefined,
|
|
|
+ categoryId: undefined,
|
|
|
+ categoryName: undefined,
|
|
|
+ number: 6,
|
|
|
+ description: undefined,
|
|
|
+ contact: undefined,
|
|
|
+ contacts: undefined,
|
|
|
+ phone: undefined,
|
|
|
+ email: undefined,
|
|
|
+ address: undefined,
|
|
|
+ categoryOption: undefined,
|
|
|
+ admin: undefined,
|
|
|
+ admins: undefined
|
|
|
}
|
|
|
+const isEdit = ref(false)
|
|
|
+const orgTreeData = ref([])
|
|
|
+const userData = ref([])
|
|
|
+const checkUserList = ref([])
|
|
|
+const selectAdminDialogFormVisible = ref(false)
|
|
|
const dialogVisible = ref(false)
|
|
|
const formRef = ref(null)
|
|
|
const formData = ref(cloneDeep(DEFAULT_FORM_DATA))
|
|
|
const formRules = {
|
|
|
- name: [{ required: true, trigger: "blur", message: "必填" }]
|
|
|
+ name: [{ required: true, trigger: "blur", message: "请输入组织名称" }],
|
|
|
+ categoryId: [{ required: true, trigger: "blur", message: "请选择组织分类" }],
|
|
|
+ number: [{ required: true, trigger: "blur", message: "请设置人数" }],
|
|
|
+ admin: [{ required: true, trigger: "blur", message: "请选择管理员" }],
|
|
|
+ admins: [{ required: true, trigger: "blur", message: "请选择管理员" }],
|
|
|
+ description: [{ required: true, trigger: "blur", message: "请输入描述" }],
|
|
|
+ contact: [{ required: true, trigger: "blur", message: "请输入联系人" }],
|
|
|
+ phone: [
|
|
|
+ { required: true, trigger: "blur", message: "请输入联系方式" },
|
|
|
+ {
|
|
|
+ pattern: /^(?:\+86)?1[3-9]\d{9}|(?:\+86)?\d{3,4}-?\d{7,8}$/,
|
|
|
+ trigger: "blur",
|
|
|
+ message: "请输入有效的手机号或座机号码"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ email: [
|
|
|
+ { required: false, trigger: "blur" }, // 不强制要求填写
|
|
|
+ {
|
|
|
+ pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
|
|
+ trigger: "blur",
|
|
|
+ message: "请输入有效的邮箱地址"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ address: [
|
|
|
+ { required: false, trigger: "blur" }, // 不是必填项
|
|
|
+ {
|
|
|
+ pattern: /^[\u4e00-\u9fa5a-zA-Z0-9\s,-.]+$/, // 基本地址模式
|
|
|
+ trigger: "blur",
|
|
|
+ message: "请输入有效的地址" // 无效地址时的提示信息
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 转化数据
|
|
|
+ */
|
|
|
+const transformData = (data) => {
|
|
|
+ return data.map((item) => ({
|
|
|
+ label: item.name,
|
|
|
+ id: item.id,
|
|
|
+ parentId: item.parentId ? item.parentId : null,
|
|
|
+ cardNumber: item.cardNumber ? item.cardNumber : null,
|
|
|
+ children: item.children ? transformData(item.children) : null
|
|
|
+ }))
|
|
|
}
|
|
|
+/**
|
|
|
+ * 创建 或 更新 组织
|
|
|
+ */
|
|
|
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 ? insertClubDataApi : updateClubDataApi
|
|
|
api(formData.value)
|
|
|
.then(() => {
|
|
|
ElMessage.success("操作成功")
|
|
|
@@ -46,9 +108,32 @@ const handleCreateOrUpdate = () => {
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
+const handleCategoryChange = (value) => {
|
|
|
+ const selected = searchData.categoryOption.find((item) => item.id === value)
|
|
|
+ if (selected) {
|
|
|
+ formData.value.categoryId = selected.id
|
|
|
+ formData.value.categoryName = selected.name
|
|
|
+ } else {
|
|
|
+ formData.value.categoryId = undefined
|
|
|
+ formData.value.categoryName = undefined
|
|
|
+ }
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 创建组织弹窗
|
|
|
+ */
|
|
|
+const handleCreateDialog = () => {
|
|
|
+ dialogVisible.value = true
|
|
|
+ isEdit.value = false
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 重置表单
|
|
|
+ */
|
|
|
const resetForm = () => {
|
|
|
formRef.value?.clearValidate()
|
|
|
formData.value = cloneDeep(DEFAULT_FORM_DATA)
|
|
|
+ checkUserList.value = undefined
|
|
|
+ formData.value.admin = undefined
|
|
|
+ userData.value = []
|
|
|
}
|
|
|
//#endregion
|
|
|
|
|
|
@@ -59,7 +144,9 @@ const handleDelete = (row) => {
|
|
|
cancelButtonText: "取消",
|
|
|
type: "warning"
|
|
|
}).then(() => {
|
|
|
- deleteTableDataApi(row.id).then(() => {
|
|
|
+ deleteClubDataApi({
|
|
|
+ id: row.id
|
|
|
+ }).then(() => {
|
|
|
ElMessage.success("删除成功")
|
|
|
getTableData()
|
|
|
})
|
|
|
@@ -71,10 +158,27 @@ const handleDelete = (row) => {
|
|
|
const handleUpdate = (row) => {
|
|
|
dialogVisible.value = true
|
|
|
formData.value = cloneDeep(row)
|
|
|
+ isEdit.value = true
|
|
|
+ formData.value.admins = row.admins
|
|
|
+ formData.value.admin = row.admins.map((item) => item.id)
|
|
|
+ userData.value = transformData(row.admins)
|
|
|
+ // 默认勾选
|
|
|
+ checkUserList.value = row.admins.map((item) => item.id)
|
|
|
}
|
|
|
const handleTop = (row) => {
|
|
|
- // todo 调用置顶接口
|
|
|
- console.log(row)
|
|
|
+ loading.value = true
|
|
|
+ clubTopupApi({
|
|
|
+ id: row.id,
|
|
|
+ isTop: 1
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ ElMessage.success("置顶成功")
|
|
|
+ getTableData()
|
|
|
+ })
|
|
|
+ .catch(() => ({}))
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
}
|
|
|
//#endregion
|
|
|
|
|
|
@@ -85,8 +189,8 @@ const searchData = reactive({
|
|
|
name: undefined,
|
|
|
creator: undefined,
|
|
|
createTime: null,
|
|
|
- classification: undefined,
|
|
|
- classificationOptions: undefined
|
|
|
+ categoryId: undefined,
|
|
|
+ categoryOption: undefined
|
|
|
})
|
|
|
/**
|
|
|
* 获取表格数据
|
|
|
@@ -100,7 +204,7 @@ const getTableData = () => {
|
|
|
userName: searchData.creator,
|
|
|
startTime: searchData.createTime ? searchData.createTime[0] : undefined,
|
|
|
endTime: searchData.createTime ? searchData.createTime[1] : undefined,
|
|
|
- categoryId: searchData.classification
|
|
|
+ categoryId: searchData.categoryId
|
|
|
})
|
|
|
.then(({ data }) => {
|
|
|
paginationData.total = data.totalCount
|
|
|
@@ -119,14 +223,30 @@ const getTableData = () => {
|
|
|
const getQueryCategoryPage = () => {
|
|
|
loading.value = true
|
|
|
getQueryCategoryPageApi({
|
|
|
- currentPage: paginationData.currentPage,
|
|
|
- pageCount: paginationData.pageSize
|
|
|
+ currentPage: 1,
|
|
|
+ pageCount: 1000
|
|
|
})
|
|
|
.then(({ data }) => {
|
|
|
- searchData.classificationOptions = data.list
|
|
|
+ searchData.categoryOption = data.list
|
|
|
})
|
|
|
.catch(() => {
|
|
|
- searchData.classificationOptions = []
|
|
|
+ searchData.categoryOption = []
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 获取部门树
|
|
|
+ */
|
|
|
+const getQueryOrgTree = () => {
|
|
|
+ loading.value = true
|
|
|
+ getQueryOrgTreeApi()
|
|
|
+ .then(({ data }) => {
|
|
|
+ orgTreeData.value = transformData(data)
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ orgTreeData.value = []
|
|
|
})
|
|
|
.finally(() => {
|
|
|
loading.value = false
|
|
|
@@ -135,23 +255,93 @@ const getQueryCategoryPage = () => {
|
|
|
const handleSearch = () => {
|
|
|
paginationData.currentPage === 1 ? getTableData() : (paginationData.currentPage = 1)
|
|
|
}
|
|
|
-const resetSearch = () => {
|
|
|
- searchFormRef.value?.resetFields()
|
|
|
- handleSearch()
|
|
|
-}
|
|
|
+// const resetSearch = () => {
|
|
|
+// searchFormRef.value?.resetFields()
|
|
|
+// handleSearch()
|
|
|
+// }
|
|
|
//#endregion
|
|
|
|
|
|
/** 监听分页参数的变化 */
|
|
|
watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })
|
|
|
|
|
|
/** 导出 */
|
|
|
-const handleDownload = () => {
|
|
|
- // todo 调用导出接口
|
|
|
- console.log("导出")
|
|
|
+const handleDownload = async () => {
|
|
|
+ loading.value = true
|
|
|
+ const res = await getAlumniClubExcelApi({
|
|
|
+ name: searchData.name,
|
|
|
+ userName: searchData.creator,
|
|
|
+ startTime: searchData.createTime ? searchData.createTime[0] : undefined,
|
|
|
+ endTime: searchData.createTime ? searchData.createTime[1] : undefined,
|
|
|
+ categoryId: searchData.categoryId
|
|
|
+ })
|
|
|
+ // 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 = `校友组织数据_下载时间_${getCurrentDateTime()}.xlsx`
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ loading.value = false
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 获取日期时间
|
|
|
+ */
|
|
|
+const getCurrentDateTime = () => {
|
|
|
+ const now = new Date()
|
|
|
+ const year = now.getFullYear()
|
|
|
+ const month = String(now.getMonth() + 1).padStart(2, "0") // 月份从0开始,需要加1
|
|
|
+ const day = String(now.getDate()).padStart(2, "0")
|
|
|
+ const hours = String(now.getHours()).padStart(2, "0")
|
|
|
+ const minutes = String(now.getMinutes()).padStart(2, "0")
|
|
|
+ const seconds = String(now.getSeconds()).padStart(2, "0")
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 选择管理员
|
|
|
+ */
|
|
|
+const handleNodeClick = (e) => {
|
|
|
+ loading.value = true
|
|
|
+ // 获取用户列表
|
|
|
+ getQueryPageUserApi({
|
|
|
+ currentPage: 1,
|
|
|
+ pageCount: 1000,
|
|
|
+ departmentId: e.id
|
|
|
+ })
|
|
|
+ .then(({ data }) => {
|
|
|
+ formData.value.admins = data.list.map((item) => ({
|
|
|
+ id: item.id,
|
|
|
+ name: item.name,
|
|
|
+ cardNumber: item.cardNumber
|
|
|
+ }))
|
|
|
+ userData.value = transformData(data.list)
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ userData.value = []
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 获取用户列表
|
|
|
+ */
|
|
|
+const handleCheckUser = () => {
|
|
|
+ if (isEdit.value) {
|
|
|
+ formData.value.admin = checkUserList.value
|
|
|
+ } else {
|
|
|
+ formData.value.admin = checkUserList.value
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-onMounted(() => {
|
|
|
+onBeforeMount(() => {
|
|
|
getQueryCategoryPage()
|
|
|
+ getQueryOrgTree()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
@@ -175,14 +365,9 @@ onMounted(() => {
|
|
|
value-format="x"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="classification" label="分类">
|
|
|
- <el-select v-model="searchData.classification" placeholder="选择分类" clearable style="width: 178px">
|
|
|
- <el-option
|
|
|
- v-for="item in searchData.classificationOptions"
|
|
|
- :key="item.id"
|
|
|
- :label="item.name"
|
|
|
- :value="item.id"
|
|
|
- />
|
|
|
+ <el-form-item prop="categoryId" label="分类">
|
|
|
+ <el-select v-model="searchData.categoryId" placeholder="选择分类" clearable style="width: 178px">
|
|
|
+ <el-option v-for="item in searchData.categoryOption" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
@@ -191,7 +376,7 @@ onMounted(() => {
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<div>
|
|
|
- <el-button type="primary" @click="dialogVisible = true">创建组织</el-button>
|
|
|
+ <el-button type="primary" @click="handleCreateDialog">创建组织</el-button>
|
|
|
<el-button plain @click="handleDownload">导出</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -235,32 +420,93 @@ onMounted(() => {
|
|
|
v-model="dialogVisible"
|
|
|
:title="formData.id === undefined ? '创建组织' : '编辑组织'"
|
|
|
@closed="resetForm"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
width="50%"
|
|
|
+ align-center
|
|
|
>
|
|
|
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="auto" size="large">
|
|
|
<el-form-item prop="name" label="组织名称">
|
|
|
<el-input v-model="formData.name" placeholder="请输入组织名称" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="classification" label="组织分类">
|
|
|
- <el-select v-model="formData.classification" placeholder="请选择分类">
|
|
|
- <el-option label="todo" value="todo" />
|
|
|
+ <el-form-item prop="categoryId" label="组织分类">
|
|
|
+ <el-select v-model="formData.categoryId" clearable placeholder="请选择分类" @change="handleCategoryChange">
|
|
|
+ <el-option v-for="item in searchData.categoryOption" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="administrator" label="管理员">
|
|
|
- <el-select v-model="formData.administrator" placeholder="请选择管理员" multiple>
|
|
|
- <el-option label="todo" value="todo" />
|
|
|
- </el-select>
|
|
|
+ <el-form-item prop="admin" label="管理员">
|
|
|
+ <div style="display: flex; align-items: flex-start; width: 100%">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="selectAdminDialogFormVisible = true"
|
|
|
+ style="margin-right: 8px"
|
|
|
+ :icon="Search"
|
|
|
+ />
|
|
|
+ <el-select v-model="formData.admin" placeholder="请选择管理员" disabled multiple>
|
|
|
+ <el-option v-for="item in formData.admins" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="number" label="人数">
|
|
|
+ <el-input-number v-model="formData.number" type="number" :min="1" :max="1000" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="introduction" label="本会简介">
|
|
|
- <el-input v-model="formData.introduction" type="textarea" :rows="5" placeholder="请输入" />
|
|
|
+ <el-form-item prop="description" label="本会简介">
|
|
|
+ <el-input v-model="formData.description" type="textarea" :rows="5" placeholder="请输入简介" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="about" label="联系我们">
|
|
|
- <el-input v-model="formData.about" type="textarea" :rows="5" placeholder="请输入" />
|
|
|
+ <!-- <el-form-item prop="contact" label="联系我们" /> -->
|
|
|
+ <el-form-item prop="contacts" label="联系人">
|
|
|
+ <el-input v-model="formData.contacts" type="text" placeholder="请输入联系人" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="phone" label="电话">
|
|
|
+ <el-input v-model="formData.phone" type="text" placeholder="请输入电话" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="email" label="邮箱">
|
|
|
+ <el-input v-model="formData.email" type="text" placeholder="请输入邮箱" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="address" label="地址">
|
|
|
+ <el-input v-model="formData.address" type="text" placeholder="请输入地址" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="handleCreateOrUpdate" :loading="loading">立即创建</el-button>
|
|
|
+ <el-button v-if="formData.id === undefined" type="primary" @click="handleCreateOrUpdate" :loading="loading"
|
|
|
+ >立即创建</el-button
|
|
|
+ >
|
|
|
+ <el-button v-else type="primary" @click="handleCreateOrUpdate" :loading="loading">立即修改</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ <!-- 选择管理员 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="selectAdminDialogFormVisible"
|
|
|
+ title="选择管理员"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ width="500"
|
|
|
+ draggable
|
|
|
+ style="margin-top: 10%"
|
|
|
+ >
|
|
|
+ <div class="custom-scroll" style="height: 300px; overflow: auto; display: flex; justify-content: space-between">
|
|
|
+ <el-tree
|
|
|
+ class="custom-scroll"
|
|
|
+ style="height: 300px; overflow: auto; width: 50%"
|
|
|
+ :data="orgTreeData"
|
|
|
+ default-expand-all
|
|
|
+ @node-click="handleNodeClick"
|
|
|
+ />
|
|
|
+ <el-checkbox-group v-model="checkUserList" style="height: 300px; overflow: auto; padding: 10px; width: 50%">
|
|
|
+ <el-checkbox
|
|
|
+ v-for="item in userData"
|
|
|
+ :key="item.id"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.id"
|
|
|
+ @change="handleCheckUser"
|
|
|
+ />
|
|
|
+ </el-checkbox-group>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="selectAdminDialogFormVisible = false">确定</el-button>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
@@ -282,4 +528,29 @@ onMounted(() => {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
+
|
|
|
+.custom-scroll {
|
|
|
+ overflow-y: scroll; /* 允许垂直滚动 */
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ padding: 0;
|
|
|
+ background-color: #ffffff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 自定义滚动条样式 */
|
|
|
+.custom-scroll::-webkit-scrollbar {
|
|
|
+ width: 1px; /* 滚动条的宽度 */
|
|
|
+}
|
|
|
+
|
|
|
+.custom-scroll::-webkit-scrollbar-track {
|
|
|
+ background: #f1f1f1; /* 滚动条轨道的颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+.custom-scroll::-webkit-scrollbar-thumb {
|
|
|
+ background: #0061ff; /* 滚动条的颜色 */
|
|
|
+ border-radius: 1px; /* 圆角 */
|
|
|
+}
|
|
|
+
|
|
|
+.custom-scroll::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #0061ff; /* 滚动条悬停时的颜色 */
|
|
|
+}
|
|
|
</style>
|