123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 |
- <template>
- <div>
- <div class="mb-4 flex item-center justify-between">
- <div>
- <el-button type="primary" @click="showEditDepartment"
- >新建部门</el-button
- >
- <el-button
- @click="
- importUploadState = false;
- importDialogVisible = true;
- "
- >一键导入部门与人员信息</el-button
- >
- <el-button
- @click="
- visibleLastTimeImport = true;
- getImportProgress();
- "
- >最近1次导入信息</el-button
- >
- </div>
- <div class="text-right w-1/2 flex items-center">
- <el-select v-model="filter.type" placeholder="">
- <el-option label="搜部门" :value="1"> </el-option>
- <el-option label="搜人员" :value="2"> </el-option>
- </el-select>
- <el-input
- v-if="filter.type == 1"
- v-model="filter.key"
- class="w-full"
- placeholder="请输入关键词"
- clearable
- >
- </el-input>
- <SearchArchivesSelect
- v-else
- style="width: 100%"
- v-model="filter.archivesId"
- :channelIds="props.channelId"
- placeholder="请选择档案"
- @change="handleChangeArchivesId"
- />
- <div class="ml-2 flex">
- <el-button type="primary" @click="onSearch">搜索</el-button>
- <el-button type="primary" plain @click="onReset">重置</el-button>
- </div>
- </div>
- </div>
- <el-table :data="departments" row-key="id" default-expand-all>
- <el-table-column prop="name" label="部门名称">
- <template #default="{ row }">
- <template v-if="filter.type == 1">
- <span v-html="hightLight(filter.key, row.name)"></span>
- </template>
- <template v-if="filter.type == 2">
- <span
- v-if="filter.archivesId"
- :style="{
- color:
- currentArchivesInfo?.departmentId == row.id ? '#67C23A' : ''
- }"
- >{{ row.name }}</span
- >
- <span v-else>{{ row.name }}</span>
- </template>
- </template>
- </el-table-column>
- <el-table-column prop="archivesTotal" label="部门人数"></el-table-column>
- <el-table-column label="操作">
- <template #default="{ row }">
- <el-button type="primary" link @click="showEditDepartment(row)"
- >编辑</el-button
- >
- <el-button
- type="primary"
- link
- @click="showPersonnelManagementVisible(row)"
- >人员管理</el-button
- >
- <el-button type="danger" link @click="deleteDepartment(row)"
- >删除</el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <el-pagination
- background
- class="justify-end mt-4"
- layout="total, prev, pager, next"
- :page-size="10"
- :total="totalDepartments"
- @change="handleCurrentChange"
- ></el-pagination>
- <el-dialog v-model="editDepartmentVisible" title="编辑部门">
- <h3 class="my-4">{{ props.channelName }}</h3>
- <el-form :label-width="100">
- <el-form-item label="部门名称" required>
- <el-input
- v-model="editDepartment.name"
- placeholder="请输入部门名称"
- ></el-input>
- </el-form-item>
- <el-form-item label="部门所属" required>
- <el-select
- v-model="editDepartment.departmentId"
- filterable
- placeholder="请选择"
- >
- <el-option label="无所属部门" :value="undefined"></el-option>
- <el-option
- v-for="item in treeDepartmentData"
- :key="item.id"
- :label="item.name"
- :value="item.id"
- >
- </el-option>
- </el-select>
- </el-form-item>
- <el-form-item label="排序">
- <el-input
- v-model="editDepartment.sort"
- type="number"
- placeholder="请输入值,值越大,在同属部门下排序越靠前"
- ></el-input>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button
- type="primary"
- :disabled="!editDepartment.name"
- @click="saveDepartment"
- >保存</el-button
- >
- </template>
- </el-dialog>
- <el-dialog v-model="visibleLastTimeImport">
- <template v-if="!importProgress.errorList.length">
- <div class="flex justify-center items-center" style="height: 300px">
- <div>
- <div class="flex items-center">
- <SuccessFilled
- class="text-green-600"
- style="height: 30px; width: 30px"
- ></SuccessFilled>
- <p class="font-bold text-blue-500 ml-2">
- 共找到{{ importProgress.total }}条数据,{{
- importProgress.progressTotal -
- importProgress.errorList.length
- }}条数据已成功处理
- </p>
- </div>
- <el-button
- class="w-full mt-10"
- type="primary"
- plain
- @click="
- visibleLastTimeImport = false;
- getDepartments();
- "
- >我知道了</el-button
- >
- </div>
- </div>
- </template>
- <template v-else>
- <div class="flex justify-between items-center mb-4 space-x-4">
- <p class="font-bold text-blue-500">
- 共找到{{ importProgress.total }}条数据,{{
- importProgress.progressTotal - importProgress.errorList.length
- }}条数据已成功处理<br />
- <span>
- {{
- importProgress.errorList.length
- }}条数据处理有误,请下载错误数据,修改后再上传本部分错误数据,正确数据无需再上传
- </span>
- </p>
- <el-button
- type="danger"
- v-if="importProgress.errorList.length"
- plain
- @click="downloadImportErrorData"
- >下载错误数据</el-button
- >
- <!-- <el-button type="warning" link @click="importUploadState = false"
- >重新上传</el-button
- > -->
- </div>
- <el-table :data="importProgress.errorList">
- <el-table-column
- v-for="(col, colIdx) in importDataHeader"
- :key="colIdx"
- >
- <template #header>{{ col }}</template>
- <template #default="{ $index }">
- <span>{{ importProgress.errorList[$index][colIdx] }}</span>
- </template>
- </el-table-column>
- </el-table>
- </template>
- </el-dialog>
- <el-dialog v-model="importDialogVisible" title="导入部门信息">
- <div
- v-if="!importUploadState"
- class="p-4 py-10 flex items-center justify-center flex-col"
- >
- <p class="my-4 text-gray-600 text-sm">
- 请下载模板文件,按照模板文件数据格式填充数据
- </p>
- <el-button
- type="primary"
- link
- class="my-6 px-4 py-2 bg-blue-500 text-white rounded-md"
- download="部门导入模板"
- :href="`${VITE_APP_OSS}/archives-import-template.xlsx`"
- tag="a"
- >一键导入excel模板下载</el-button
- >
- <el-upload
- ref="uploadRef"
- action="#"
- :show-file-list="false"
- :limit="2"
- :http-request="handleUpload"
- >
- <!-- :auto-upload="false -->
- <el-button type="primary"
- >上传名单文件,请用模板文件制作名单,否则会失败</el-button
- >
- </el-upload>
- <!-- <el-image
- class="w-full mt-3"
- :src="errorRemakeImg"
- :preview-src-list="[errorRemakeImg]"
- ></el-image> -->
- </div>
- <template v-else>
- <!-- <div
- v-if="importProgress.total != importProgress.progressTotal"
- class="flex mb-4"
- >
- <span>尚有数据未处理完成...</span>
- <el-button type="primary" link @click="refreshImportData"
- >点击刷新</el-button
- >
- </div> -->
- <div
- class="flex items-center justify-center"
- v-if="importUploadStatus == 'loading'"
- >
- 数据上传中,请稍后...
- <div class="ml-4">
- <el-button type="primary" plain @click="refreshImportData"
- >点击刷新</el-button
- >
- </div>
- </div>
- <div
- v-else-if="importUploadStatus == 'error'"
- class="flex justify-center items-center flex-col"
- >
- <div>
- <div class="text-red-500">数据上传失败,请重新上传</div>
- <el-button
- class="w-full mt-4"
- type="primary"
- plain
- @click="importUploadState = false"
- >重新上传</el-button
- >
- </div>
- </div>
- </template>
- </el-dialog>
- <el-dialog v-model="personnelManagementVisible" title="人员管理">
- <div class="flex justify-between items-center">
- <span
- >部门名称:<b>
- {{ personnelManagement.row?.name }}
- (所属渠道:{{ props.channelName }})</b
- ></span
- >
- <div class="space-x-4">
- <el-select
- v-model="appendPersonnelIds"
- multiple
- filterable
- remote
- :remote-method="searchArchives"
- >
- <el-option
- v-for="(item, index) in searchArchivesList"
- :key="index"
- :label="item.name"
- :value="item.id"
- ></el-option>
- </el-select>
- <el-button
- type="primary"
- :disabled="!appendPersonnelIds.length"
- @click="appendPersonnel"
- >确定</el-button
- >
- </div>
- </div>
- <el-divider></el-divider>
- <el-table :data="personnelManagement.list">
- <el-table-column label="姓名" prop="name"></el-table-column>
- <el-table-column
- label="性别"
- prop="gender"
- :formatter="v => (v.gender == 1 ? '男' : '女')"
- ></el-table-column>
- <el-table-column label="年龄" prop="birthday">
- <template #default="{ row }">
- {{ formatAge(row.birthday) }}
- </template>
- </el-table-column>
- <el-table-column label="档案编号" prop="id"></el-table-column>
- <el-table-column label="操作">
- <template #default="{ row }">
- <el-button type="danger" link @click="removePersonnel(row)"
- >移除</el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <el-pagination
- class="justify-end mt-4"
- :current-page="personnelManagement.filter.page"
- :page-size="personnelManagement.filter.pageSize"
- :total="personnelManagement.total"
- background
- layout="total, prev, pager, next, jumper"
- @current-change="
- page => {
- personnelManagement.filter.page = page;
- getPersonnelList();
- }
- "
- ></el-pagination>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import SearchArchivesSelect from "@/components/Archives/SearchArchivesSelect.vue";
- import { SuccessFilled } from "@element-plus/icons-vue";
- import { onMounted, ref, nextTick } from "vue";
- // 导入ElMessage和ElMessageBox组件
- import {
- ElMessage,
- ElMessageBox,
- ElLoading,
- progressProps
- } from "element-plus";
- // 导入route、router并定义
- import { useRoute, useRouter } from "vue-router";
- import errorRemake from "@/assets/errorRemake.png";
- import { request, formatAge } from "@/utils";
- import dayjs from "dayjs";
- import { watch } from "vue";
- const { VITE_APP_OSS } = import.meta.env;
- const [route, router] = [useRoute(), useRouter()];
- const props = defineProps({
- channelId: {
- default: undefined
- },
- channelName: {
- type: String,
- default: ""
- }
- });
- watch(
- () => props.channelId,
- (n, o) => {
- if (n) {
- try {
- nextTick(() => {
- filter.value.channelId = n;
- onSearch();
- });
- } catch (error) {
- console.log(error);
- }
- }
- },
- {
- deep: true,
- immediate: true
- }
- );
- const filter = ref({
- page: 1,
- pageSize: 10,
- key: "",
- type: 2,
- archivesId: "",
- channelId: undefined
- });
- const currentArchivesInfo = ref({ departmentId: "" });
- const errorRemakeImg = errorRemake;
- const departments = ref([{}]);
- const treeDepartmentData = ref([]);
- const totalDepartments = ref(0);
- const mapTree = (org, parentId) => {
- const haveChildren =
- Array.isArray(org.subDepartmentList) && org.subDepartmentList.length > 0;
- return {
- parentId,
- departmentId: parentId,
- ...org,
- children: haveChildren
- ? org.subDepartmentList?.map(r => mapTree(r, org.id))
- : []
- };
- };
- let filterTree = (value, arr) => {
- let newarr = [];
- arr.forEach(element => {
- if (element.name.indexOf(value) > -1) {
- // 判断条件
- newarr.push({ ...element, children: element.children });
- } else {
- if (element.children && element.children.length > 0) {
- let result = filterTree(value, element.children);
- if (result && result.length > 0) {
- let obj = {
- ...element,
- children: result
- };
- newarr.push(obj);
- }
- }
- }
- });
- return newarr;
- };
- const getFlatArr = (arr, key = "children") => {
- return arr.reduce((val, item) => {
- let flatArr = [...val, item];
- // 可以在此处限制各种需要的条件,在展示字段前加空格,——之类的,以及其它情况
- if (item[key]) {
- flatArr = [...flatArr, ...getFlatArr(item[key])];
- }
- return flatArr;
- }, []);
- };
- const handleChangeArchivesId = (info, options) => {
- try {
- currentArchivesInfo.value = options.archivesChannels.filter(
- v => v.id == props.channelId
- )[0];
- } catch (error) {
- currentArchivesInfo.value = {
- departmentId: ""
- };
- }
- };
- const getDepartments = async () => {
- const { data } = await request.get(
- `/archivesService/mechanism/department/list`,
- {
- params: filter.value
- }
- );
- if (!data.list?.length) {
- departments.value = [];
- treeDepartmentData.value = [];
- return;
- }
- let array = [];
- array = data.list?.map(org => mapTree(org));
- console.log("array", array);
- departments.value = filterTree(
- filter.value.type == 1 ? filter.value.key : "",
- array
- );
- treeDepartmentData.value = getFlatArr(array);
- totalDepartments.value = data.total;
- };
- const hightLight = (keyWord, suggtion) => {
- // 使用 regexp 构造函数,因为这里要匹配的是一个变量
- const reg = new RegExp(keyWord, "ig");
- const newSrt = String(suggtion).replace(reg, function (p) {
- return `<span style="color: #67C23A">${p}</span>`;
- });
- return newSrt;
- };
- const onSearch = () => {
- getDepartments();
- };
- const onReset = () => {
- filter.value.page = 1;
- filter.value.pageSize = 10;
- filter.value.key = "";
- filter.value.type = 2;
- filter.value.archivesId = "";
- onSearch();
- };
- function handleCurrentChange(page) {
- filter.value.page = page;
- getDepartments();
- }
- const deleteDepartment = async row => {
- await ElMessageBox.confirm(`确认要删除这个部门吗?`, "提示");
- await request.post(`/archivesService/mechanism/department/delete`, {
- id: row.id
- });
- ElMessage.success("删除成功");
- await getDepartments();
- };
- const editDepartmentVisible = ref(false);
- const editDepartment = ref({
- id: "",
- name: "",
- departmentId: undefined,
- sort: undefined
- });
- const showEditDepartment = department => {
- if (department) {
- editDepartment.value = { ...department };
- } else {
- editDepartment.value = {
- id: "",
- name: "",
- departmentId: undefined,
- sort: undefined
- };
- }
- editDepartmentVisible.value = true;
- };
- const saveDepartment = async () => {
- await request.post(
- `/archivesService/mechanism/department/${
- editDepartment.value.id ? "/update/name" : "/create"
- }`,
- {
- ...editDepartment.value,
- sort: Number(editDepartment.value.sort),
- channelId: props.channelId
- }
- );
- ElMessage.success("保存成功");
- editDepartmentVisible.value = false;
- getDepartments();
- };
- const personnelManagementVisible = ref(false);
- const personnelManagement = ref({
- filter: {
- page: 1,
- pageSize: 10,
- needSubArchives: 1,
- departmentId: 0
- },
- row: undefined,
- list: [],
- total: 0
- });
- const showPersonnelManagementVisible = row => {
- personnelManagement.value.row = row;
- personnelManagement.value.filter.page = 1;
- personnelManagement.value.filter.departmentId = row.id;
- personnelManagementVisible.value = true;
- getPersonnelList();
- };
- const getPersonnelList = async () => {
- const { data } = await request.get(
- "/archivesService/mechanism/archives/paginate",
- {
- params: {
- ...personnelManagement.value.filter
- }
- }
- );
- personnelManagement.value.list = data.list;
- personnelManagement.value.total = data.total;
- };
- const removePersonnel = async row => {
- await ElMessageBox.confirm(`确认要移除这个人吗?`, "提示");
- await request.post(`/archivesService/mechanism/department/removeArchives`, {
- archivesId: row.id,
- departmentId: personnelManagement.value.row.id
- });
- ElMessage.success("删除成功");
- getDepartments();
- await getPersonnelList();
- };
- const appendPersonnelIds = ref([]);
- const appendPersonnel = async () => {
- await request.post(`/archivesService/mechanism/department/pushArchives`, {
- departmentId: personnelManagement.value.row.id,
- archivesIds: appendPersonnelIds.value
- });
- ElMessage.success("添加成功");
- appendPersonnelIds.value = [];
- getDepartments();
- getPersonnelList();
- };
- const searchArchivesList = ref([]);
- const searchArchives = async q => {
- if (!q) return;
- const { data } = await request.get(
- "/archivesService/mechanism/archives/paginate",
- {
- params: {
- page: 1,
- pageSize: 999,
- needSubArchives: 1,
- departmentId: 0,
- inDepartment: 2,
- keyword: q,
- channelId: props.channelId
- }
- }
- );
- searchArchivesList.value = data.list;
- };
- const importDialogVisible = ref(false);
- const importUploadState = ref(false);
- const visibleLastTimeImport = ref(false);
- const importUploadStatus = ref("");
- const importDataHeader = ref([]);
- const importProgress = ref({
- total: 0,
- progressTotal: 0,
- errorList: []
- });
- const handleUpload = async opt => {
- console.log(opt);
- importUploadStatus.value = "loading";
- const loading = ElLoading.service({
- lock: true,
- text: "开始上传",
- background: "rgba(255, 255, 255, 0.7)"
- });
- const fd = new FormData();
- fd.append("file", opt.file);
- fd.append("channelId", props.channelId);
- try {
- await request.post("/archivesService/mechanism/archives/import", fd, {
- headers: {
- "Content-Type": "multipart/form-data"
- }
- });
- ElMessage.success("上传成功");
- await getImportProgress();
- if (importProgress.value?.status > 1) {
- importDialogVisible.value = false;
- visibleLastTimeImport.value = true;
- }
- // importUploadState.value = true;
- } catch (e) {
- console.log("error", e);
- importUploadState.value = true;
- importUploadStatus.value = "error";
- } finally {
- loading.close();
- }
- };
- const getImportProgress = async () => {
- const { data } = await request.get(
- "/archivesService/mechanism/archives/import/progress"
- );
- importProgress.value = {
- total: data.progress.total,
- progressTotal: data.progress.progressTotal,
- errorList: data.progress.errorList.slice(2),
- status: data.progress.status
- };
- importDataHeader.value = data.progress.errorList[1];
- // importUploadState.value = !!importProgress.value.total;
- };
- const refreshImportData = async () => {
- await getImportProgress();
- if (importProgress.value?.importProgress > 1) {
- importDialogVisible.value = false;
- visibleLastTimeImport.value = true;
- }
- ElMessage.success("刷新成功");
- };
- const downloadImportErrorData = async () => {
- try {
- const response = await request.post(
- "/archivesService/mechanism/archives/import/error/download",
- {},
- {
- responseType: "blob"
- }
- );
- console.log(response);
- const link = document.createElement("a");
- const blob = new Blob([response], {
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- });
- const body = document.body;
- link.href = window.URL.createObjectURL(blob);
- link.download = `错误数据-${dayjs(Date.now()).format(
- "YYYYMMDDHHmmss"
- )}.xlsx`;
- body.appendChild(link);
- link.click();
- body.removeChild(link);
- window.URL.revokeObjectURL(link.href);
- ElMessage.success("下载成功!");
- } catch (error) {
- ElMessage.error("下载失败,请重试!");
- console.error("Export error:", error);
- }
- };
- // onMounted(onSearch);
- </script>
- <style lang="scss"></style>
|