From 0bab85897653f65df1932edb829f2af2bf58b846 Mon Sep 17 00:00:00 2001 From: 马宇豪 <978517621@qq.com> Date: 星期三, 19 七月 2023 10:37:14 +0800 Subject: [PATCH] 修改 --- src/views/Admin/Report.vue | 7 vue.config.js | 18 package-lock.json | 151 +++++++ src/views/Admin/callRecord.vue | 7 src/api/user.js | 88 ++++ src/views/Admin/release.vue | 10 src/views/Admin/msgRecord.vue | 7 src/router/index.js | 6 src/views/Admin/components/msgEditMod.vue | 3 src/views/Admin/addressBook.vue | 269 ++++++++++++++ src/views/Admin/list.vue | 7 src/layout/menu/index.js | 10 src/views/Admin/massSend.vue | 136 ++++++ src/views/Admin/components/groupListMod.vue | 208 ++++++++++ src/views/Admin/userManage.vue | 1 package.json | 1 src/views/Admin/history.vue | 19 src/views/Admin/components/addressUserMod.vue | 149 +++++++ src/App.vue | 4 src/assets/example.xlsx | 0 src/views/Admin/notice.vue | 3 21 files changed, 1,062 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 31888de..fbb87fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-service": "~4.5.0", "babel-plugin-import": "^1.13.3", + "file-loader": "^6.2.0", "less": "^3.0.4", "less-loader": "^5.0.0", "vue-template-compiler": "^2.6.11" @@ -2412,6 +2413,48 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "optional": true + }, + "node_modules/@vue/cli-service/node_modules/file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "dependencies": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/@vue/cli-service/node_modules/file-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/@vue/cli-service/node_modules/file-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } }, "node_modules/@vue/cli-service/node_modules/has-flag": { "version": "4.0.0", @@ -6321,19 +6364,47 @@ "integrity": "sha1-tO7oFIq7Adzx0aw0Nn1Z4S+mHW4=" }, "node_modules/file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npm.taobao.org/file-loader/download/file-loader-4.3.0.tgz", - "integrity": "sha1-eA8ED3KbPRgBnyBgX3I+hEuKWK8=", + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, "dependencies": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 10.13.0" }, "peerDependencies": { - "webpack": "^4.0.0" + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" } }, "node_modules/file-uri-to-path": { @@ -18527,6 +18598,38 @@ "dev": true, "optional": true }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -21852,13 +21955,37 @@ "integrity": "sha1-tO7oFIq7Adzx0aw0Nn1Z4S+mHW4=" }, "file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npm.taobao.org/file-loader/download/file-loader-4.3.0.tgz", - "integrity": "sha1-eA8ED3KbPRgBnyBgX3I+hEuKWK8=", + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, "file-uri-to-path": { diff --git a/package.json b/package.json index 484ca2f..27b03ee 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@vue/cli-plugin-vuex": "~4.5.0", "@vue/cli-service": "~4.5.0", "babel-plugin-import": "^1.13.3", + "file-loader": "^6.2.0", "less": "^3.0.4", "less-loader": "^5.0.0", "vue-template-compiler": "^2.6.11" diff --git a/src/App.vue b/src/App.vue index 487663f..1f80fff 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,8 +11,8 @@ -moz-osx-font-smoothing: grayscale; } * { - margin: 0px; - padding: 0px; + margin: 0; + padding: 0; } .inner{ background-color: #fff; diff --git a/src/api/user.js b/src/api/user.js index 3ed0cc0..a8df775 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -75,7 +75,7 @@ return request({ url:'/mesmanager/recipient/add', method:'post', - data:data, + data:data }) } @@ -83,7 +83,7 @@ return request({ url:'/mesmanager/recipient/update', method:'post', - data:data, + data:data }) } @@ -102,9 +102,93 @@ }) } +// 获取原通讯录用户 +export function getAddressBook(data){ + return request({ + url: '/addressbook/user/page', + method: 'post', + data:data + }) +} +// 新增原通讯录用户 +export function addGroupUser(data){ + return request({ + url: '/addressbook/user/add', + method: 'post', + data:data + }) +} +// 修改原通讯录用户 +export function updateGroupUser(data){ + return request({ + url: '/addressbook/user/update', + method: 'post', + data:data + }) +} +// 删除原通讯录用户 +export function delGroupUser(data){ + return request({ + url: '/addressbook/user/delete', + method: 'post', + data:data + }) +} +// 获取通讯录分组 +export function getGroupList(){ + return request({ + url: '/addressbook/group/listAll', + method: 'get' + }) +} +// 新增通讯录分组 +export function addGroup(data){ + return request({ + url: '/addressbook/group/add', + method: 'post', + data:data + }) +} +// 修改通讯录分组 +export function updateGroup(data){ + return request({ + url: '/addressbook/group/update', + method: 'post', + data:data + }) +} + +// 删除通讯录分组 +export function delGroup(data){ + return request({ + url: '/addressbook/group/delete', + method: 'post', + data:data + }) +} + +// 获取分组和组员 +export function getUserByGroup(){ + return request({ + url: '/addressbook/group/listUserByGroup', + method: 'get' + }) +} + +// 导入通讯录 +export function importFile(data){ + return request({ + headers: { + "Content-Type": "multipart/form-data", + }, + url: '/addressbook/user/import', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/src/assets/example.xlsx b/src/assets/example.xlsx new file mode 100644 index 0000000..1800858 --- /dev/null +++ b/src/assets/example.xlsx Binary files differ diff --git a/src/layout/menu/index.js b/src/layout/menu/index.js index f1e46de..5d1f033 100644 --- a/src/layout/menu/index.js +++ b/src/layout/menu/index.js @@ -80,6 +80,11 @@ MenuTitle: "平级接收人管理", MenuPath: "/samelevel", }, + { + MenuID: "34", + MenuTitle: "原通讯录", + MenuPath: "/addressBook", + } ], }, { @@ -214,6 +219,11 @@ MenuTitle: "平级接收人管理", MenuPath: "/samelevel", }, + { + MenuID: "34", + MenuTitle: "原通讯录", + MenuPath: "/addressBook", + } ], }, { diff --git a/src/router/index.js b/src/router/index.js index d03f95e..dbbe9ea 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -94,6 +94,12 @@ meta: { title: '平级接收人管理' }, component: () => import('@/views/Admin/sameLevel'), }, + { + path: '/addressBook', + name: 'addressBook', + meta: { title: '原通讯录' }, + component: () => import('@/views/Admin/addressBook'), + }, { path: '/user', name: 'user', diff --git a/src/views/Admin/Report.vue b/src/views/Admin/Report.vue index 79369d2..5588b32 100644 --- a/src/views/Admin/Report.vue +++ b/src/views/Admin/Report.vue @@ -30,7 +30,7 @@ /> </a-col> <a-col :span="4"> - <a-button type="primary" @click="getData">查询</a-button> + <a-button type="primary" @click="searchData()">查询</a-button> <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> </a-col> </a-row> @@ -190,6 +190,11 @@ } }, + searchData(){ + this.search.pageIndex = 1 + this.getData() + }, + resetSearch(){ const t = this t.search = { diff --git a/src/views/Admin/addressBook.vue b/src/views/Admin/addressBook.vue new file mode 100644 index 0000000..7618f47 --- /dev/null +++ b/src/views/Admin/addressBook.vue @@ -0,0 +1,269 @@ +<template> + <div class="inner"> + <a-row type="flex" justify="space-between" style="margin-bottom: 20px"> + <a-col :span="8"> + <a-button v-if="unittype && unittype !== null" type="primary" @click="editData('add',{})">新增</a-button> + <a-button v-if="unittype && unittype !== null" type="primary" @click="visible = true" style="margin: 0 12px">导入通讯录表</a-button> + <a-button v-if="unittype && unittype !== null" type="primary" @click="openGroup()">分组管理</a-button> + </a-col> + <a-col :span="16"> + <a-row type="flex" justify="end" :gutter="14"> + <a-col :span="6"> + <a-input v-model="search.searchParams.company" placeholder="单位名称" style="width: 100%"/> + </a-col> + <a-col :span="6"> + <a-select v-model="search.searchParams.addressBookGroupId" placeholder="选择分组" style="width: 100%" @change="handleChange"> + <a-select-option v-for="item in groupData" :value="item.id" :key="item.id">{{item.name}}</a-select-option> + </a-select> + </a-col> + <a-col :span="4"> + <a-button type="primary" @click="searchData()">查询</a-button> + <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> + </a-col> + </a-row> + </a-col> + </a-row> + <div class="table-cont"> + <a-table :columns="columns" :data-source="tableData" :pagination="pagination" :rowKey="record=>record.id" bordered> + <template #group="groupId"> + {{ getGroupName(groupId) }} + </template> + <template #action="action,row"> + <a-button type="link" @click="editData('edit',row)">编辑</a-button> + <a-button type="link" class="delBtn" @click="delData(row)">删除</a-button> + </template> + </a-table> + </div> + <address-user-mod ref="addressRef" :groupList="groupData" @refresh="getAddressBook"></address-user-mod> + <group-list-mod ref="groupRef" @refresh="getGroupList"></group-list-mod> + <a-modal v-model="visible" title="导入通讯录" ok-text="导入通讯录" :confirmLoading="uploadLoading" cancel-text="取消" @ok="uploadFile" centered :afterClose="clearMod"> + <a-form-model ref="ruleForm" :label-col="labelCol" :wrapper-col="wrapperCol" :colon="false"> + <a-form-model-item label="通讯录表格模板" extra="导入通讯录须依据此模板"> + <a-button type="primary" @click="downloadFile">下载模板</a-button> + </a-form-model-item> + <a-form-model-item label="通讯录表格文件"> + <a-upload :file-list="fileList" :remove="handleRemove" :before-upload="beforeUpload" accept=".xlsx,.xls"> + <a-button> <a-icon type="upload"/> 点击上传 </a-button> + </a-upload> + </a-form-model-item> + </a-form-model> + </a-modal> + </div> +</template> +<script> +import {delGroupUser, getAddressBook, getGroupList, importFile} from '@/api/user' +import {getUserInfo} from "@/util/storage" +import addressUserMod from "@/views/Admin/components/addressUserMod" +import groupListMod from "@/views/Admin/components/groupListMod"; +import exampleFile from '@/assets/example.xlsx' +export default { + name: 'addressBook', + components: { addressUserMod,groupListMod }, + data () { + return { + groupData: [], + unittype: null, + districtId: null, + search:{ + pageIndex: 1, + pageSize: 20, + searchParams:{ + company: '', + addressBookGroupId: null + } + }, + columns:[ + { + title: '分组', + dataIndex: 'addressBookGroupId', + key: 'addressBookGroupId', + scopedSlots: { customRender: 'group' } + }, + { + title: '单位', + dataIndex: 'company', + key: 'company' + }, + { + title: '姓名', + dataIndex: 'name', + key: 'name' + }, + { + title: '手机号码', + dataIndex: 'phone', + key: 'phone' + }, + { + title: '操作', + key: 'action', + scopedSlots: { customRender: 'action' } + }, + ], + tableData: [], + pagination: { + current: 1, + defaultCurrent: 1, + defaultPageSize: 20, + total: 0, + onChange: ( page, pageSize ) => this.onPageChange(page,pageSize), + showTotal: total => `共 ${total} 条` + }, + visible: false, + labelCol: { span: 8 }, + wrapperCol: { span: 14 }, + exampleFile, + fileList: [], + delList: [], + uploadLoading: false + } + }, + created() { + const t = this + t.unittype = getUserInfo().unittype + t.districtId = getUserInfo().districtId + t.getAddressBook() + t.getGroupList() + }, + methods:{ + async getAddressBook(){ + const t = this + const res = await getAddressBook(t.search) + if(res.data.code == 100){ + t.tableData = res.data.data + t.pagination.total = res.data.total + }else{ + t.$message.warning(res.data.msg); + } + }, + + async getGroupList(){ + let res = await getGroupList() + if(res.data.code == 100){ + this.groupData = res.data.data + } else { + this.$message.warning(res.data.msg); + } + }, + + handleChange(value) { + console.log(`selected ${value}`); + }, + + searchData(){ + this.search.pageIndex = 1 + this.getAddressBook() + }, + + resetSearch(){ + const t = this + t.search = { + pageIndex: 1, + pageSize: 20, + searchParams:{ + company: '', + addressBookGroupId: null + } + } + t.getAddressBook() + }, + + editData(type,data){ + const t = this + t.$refs.addressRef.openDialog(type,data) + }, + openGroup(){ + this.$refs.groupRef.openDialog() + }, + + async delData(row){ + const t = this + this.$confirm({ + title: '提示', + content: h => <div>是否删除该条用户信息?</div>, + cancelText: '取消', + okText: '确认', + centered: true, + onOk() { + delGroupUser({id: row.id}).then(res=>{ + if(res.data.code == 100){ + t.$message.success('删除用户信息成功'); + t.getAddressBook() + }else{ + t.$message.warning(res.data.msg); + } + }) + }, + onCancel() { + console.log('Cancel'); + }, + }); + }, + + onPageChange(page, pageSize) { + const t= this + t.pagination.current = page + t.search.pageIndex = page + t.getAddressBook() + }, + getGroupName(id){ + return this.groupData.find(i => i.id === id)?.name; + }, + + downloadFile(){ + const link = document.createElement('a') + link.href = exampleFile + link.target = '_blank' + link.download = '通讯录导入模板.xlsx' + link.click() + }, + + handleRemove(file) { + const index = this.fileList.indexOf(file) + const newFileList = this.fileList.slice() + newFileList.splice(index, 1) + this.fileList = newFileList; + }, + beforeUpload(file) { + this.fileList = [...this.fileList, file] + this.fileList = this.fileList.slice(-1) + return false; + }, + + async uploadFile(){ + if(this.fileList.length == 0){ + this.$message.warning('请先上传通讯录表格'); + return + }else{ + this.uploadLoading = true + const { fileList } = this; + const formData = new FormData(); + fileList.forEach((file) => { + formData.append('file', file) + }) + const res = await importFile(formData) + if(res.data.code == 100){ + this.$message.success(res.data.msg,2); + this.fileList = [] + this.uploadLoading = false + this.visible = false + this.getAddressBook() + }else{ + this.$message.warning(res.data.msg); + this.uploadLoading = false + } + } + }, + + clearMod(){ + this.fileList = [] + } + } +} +</script> + +<style lang="less" scoped> +.delBtn{ + color: @danger +} +</style> diff --git a/src/views/Admin/callRecord.vue b/src/views/Admin/callRecord.vue index 7c40087..0b887e5 100644 --- a/src/views/Admin/callRecord.vue +++ b/src/views/Admin/callRecord.vue @@ -27,7 +27,7 @@ /> </a-col> <a-col :span="4"> - <a-button type="primary" @click="getData">查询</a-button> + <a-button type="primary" @click="searchData">查询</a-button> <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> </a-col> </a-row> @@ -216,6 +216,11 @@ console.log('onOk: ', value); }, + searchData(){ + this.search.pageIndex = 1 + this.getData() + }, + resetSearch(){ const t = this t.search = { diff --git a/src/views/Admin/components/addressUserMod.vue b/src/views/Admin/components/addressUserMod.vue new file mode 100644 index 0000000..9d0cc3f --- /dev/null +++ b/src/views/Admin/components/addressUserMod.vue @@ -0,0 +1,149 @@ +<template> + <a-modal + :title="title" + :visible="visible" + centered + :confirm-loading="confirmLoading" + width="50%" + cancelText="取消" + okText="确认" + @ok="onSubmit" + @cancel="handleCancel" + :afterClose="clearMod" + > + <a-form-model ref="ruleForm" :rules="rules" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :colon="false"> + <a-form-model-item label="姓名" prop="name"> + <a-input v-model="form.name"/> + </a-form-model-item> + <a-form-model-item label="手机号码" prop="phone"> + <a-input v-model="form.phone"/> + </a-form-model-item> + <a-form-model-item label="单位名称" prop="company"> + <a-input v-model="form.company"/> + </a-form-model-item> + <a-form-model-item label="分组" prop="addressBookGroupId"> + <a-select v-model="form.addressBookGroupId" placeholder="选择分组"> + <a-select-option v-for="item in groupList" :value="item.id" :key="item.id">{{item.name}}</a-select-option> + </a-select> + </a-form-model-item> + </a-form-model> + </a-modal> +</template> + +<script> +import {addGroupUser, addUser, updateGroupUser, updateUser} from '@/api/user' +import {verifySimplePhone} from "@/util/validate"; +export default { + name: 'addressUserMod', + props: ['groupList'], + data () { + let validatePhone = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入手机号')) + }else{ + if(!verifySimplePhone(value)){ + callback(new Error('手机号格式有误')) + }else{ + callback() + } + } + } + + return { + title: '新增用户', + visible: false, + confirmLoading: false, + labelCol: { span: 4 }, + wrapperCol: { span: 14 }, + form: { + id: null, + name: '', + phone: '', + company: '', + addressBookGroupId: null + }, + rules: { + name: [{ required: true, message: '请输入姓名', trigger: 'blur'}], + phone: [{ required: true, validator: validatePhone, trigger: 'blur'}], + company: [{ required: true, message: '请输入单位名称', trigger: 'blur'}], + addressBookGroupId: [{ required: true, message: '请选择分组', trigger: 'change'}], + } + } + }, + created() { + const t = this + }, + methods:{ + openDialog(type,data){ + const t = this + if(type == 'add'){ + t.title = '新增用户' + t.form = { + id: null, + name: '', + phone: '', + company: '', + addressBookGroupId: null + } + }else{ + t.title = '编辑用户' + for(let i in data){ + if(t.isValidKey(i,t.form)){ + t.form[i] = data[i] + } + } + } + t.visible = true + }, + isValidKey(key, object){ + return key in object; + }, + + clearMod(){ + this.$refs.ruleForm.clearValidate() + this.$refs.ruleForm.resetFields() + }, + + onSubmit() { + this.$refs.ruleForm.validate(valid => { + if (valid) { + if(this.title == '新增用户'){ + const { id,...data } = this.form + addGroupUser(data).then((res)=>{ + if(res.data.code == 100){ + this.$message.success('新增用户成功') + this.$emit('refresh') + this.visible = false + }else{ + this.$message.error(res.data.msg) + } + }) + }else{ + updateGroupUser(this.form).then((res)=>{ + if(res.data.code == 100){ + this.$message.success('修改用户成功') + this.$emit('refresh') + this.visible = false + }else{ + this.$message.error(res.data.msg) + } + }) + } + } else { + console.log('error submit!!'); + return false; + } + }); + }, + + handleCancel(e) { + const t = this + t.visible = false; + } + } +} +</script> + +<style lang="less" scoped> + +</style> diff --git a/src/views/Admin/components/groupListMod.vue b/src/views/Admin/components/groupListMod.vue new file mode 100644 index 0000000..3a900be --- /dev/null +++ b/src/views/Admin/components/groupListMod.vue @@ -0,0 +1,208 @@ +<template> + <a-modal + title="分组管理" + :visible="visible" + centered + :confirm-loading="confirmLoading" + width="50%" + cancelText="取消" + okText="确认" + @ok="handleOk" + @cancel="handleCancel" + :afterClose="refreshGroup" + > + <a-button type="primary" @click="editData('add',{})" style="margin-bottom: 12px">新增分组</a-button> + <div class="table-cont"> + <a-table :columns="columns" :data-source="tableData" :pagination="false" :rowKey="record=>record.id" bordered> + <template #action="action,row"> + <a-button type="link" @click="editData('edit',row)">编辑</a-button> + <a-button type="link" class="delBtn" @click="delData(row)">删除</a-button> + </template> + </a-table> + </div> + <a-modal + :title="editTitle" + :visible="editVisible" + centered + :confirm-loading="confirmLoading" + width="30%" + cancelText="取消" + okText="确认" + @ok="onSubmit" + @cancel="editVisible = false" + :afterClose="clearMod" + > + <a-form-model ref="ruleForm" :rules="rules" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :colon="false"> + <a-form-model-item label="分组名称" prop="name"> + <a-input v-model="form.name"/> + </a-form-model-item> + </a-form-model> + </a-modal> + </a-modal> +</template> + +<script> +import {addGroup, delGroup, getGroupList, updateGroup, updateUser} from '@/api/user' +export default { + name: 'groupListMod', + props: [], + data () { + return { + editTitle: '新增分组', + visible: false, + editVisible: false, + confirmLoading: false, + labelCol: { span: 4 }, + wrapperCol: { span: 14 }, + columns:[ + { + title: '分组名称', + dataIndex: 'name', + key: 'name' + }, + { + title: 'id', + dataIndex: 'id', + key: 'id' + }, + { + title: '操作', + key: 'action', + scopedSlots: { customRender: 'action' } + }, + ], + tableData: [], + form: { + id: null, + name: '', + }, + rules: { + name: [{ required: true, message: '请输入组名', trigger: 'blur'}] + } + } + }, + created() { + const t = this + }, + methods:{ + openDialog(){ + const t = this + t.getGroupList() + t.visible = true + }, + + async getGroupList(){ + let res = await getGroupList() + if(res.data.code == 100){ + this.tableData = res.data.data + } else { + this.$message.warning(res.data.msg); + } + }, + + editData(type,data){ + const t = this + if(type == 'add'){ + t.editTitle = '新增分组' + t.form = { + id: null, + name: '', + } + }else{ + t.editTitle = '编辑分组' + for(let i in data){ + if(t.isValidKey(i,t.form)){ + t.form[i] = data[i] + } + } + } + t.editVisible = true + }, + + isValidKey(key, object){ + return key in object; + }, + + onSubmit() { + this.$refs.ruleForm.validate(valid => { + if (valid) { + if(this.editTitle == '新增分组'){ + const { id,...data } = this.form + addGroup(data).then((res)=>{ + if(res.data.code == 100){ + this.$message.success('新增分组成功') + this.getGroupList() + this.editVisible = false + }else{ + this.$message.error(res.data.msg) + } + }) + }else{ + updateGroup(this.form).then((res)=>{ + if(res.data.code == 100){ + this.$message.success('修改分组成功') + this.getGroupList() + this.editVisible = false + }else{ + this.$message.error(res.data.msg) + } + }) + } + } else { + console.log('error submit!!'); + return false; + } + }); + }, + + async delData(row){ + const t = this + this.$confirm({ + title: '提示', + content: h => <div>是否删除该条分组?</div>, + cancelText: '取消', + okText: '确认', + centered: true, + onOk() { + delGroup({id: row.id}).then(res=>{ + if(res.data.code == 100){ + t.$message.success('删除分组成功'); + t.getGroupList() + }else{ + t.$message.warning(res.data.msg); + } + }) + }, + onCancel() { + console.log('Cancel'); + }, + }); + }, + + clearMod(){ + this.$refs.ruleForm.clearValidate() + this.$refs.ruleForm.resetFields() + }, + + refreshGroup(){ + this.$emit('refresh') + }, + + handleOk(e) { + const t = this + t.visible = false; + }, + + handleCancel(e) { + const t = this + t.visible = false; + } + } +} +</script> + +<style lang="less" scoped> +.delBtn{ + color: @danger +} +</style> diff --git a/src/views/Admin/components/msgEditMod.vue b/src/views/Admin/components/msgEditMod.vue index cefb82c..ac30da2 100644 --- a/src/views/Admin/components/msgEditMod.vue +++ b/src/views/Admin/components/msgEditMod.vue @@ -97,6 +97,7 @@ </div> <a-form-model-item prop="receiver" style="margin-bottom: 6px"> <a-tree-select + :maxTagCount="3" show-search tree-checkable treeCheckStrictly @@ -127,7 +128,7 @@ </a-checkbox> </div> <a-form-model-item> - <a-select mode="multiple" placeholder="选择平级接收单位" v-model="form.recipient" @change="handle" :disabled="disable"> + <a-select mode="multiple" placeholder="选择平级接收单位" v-model="form.recipient" @change="handle" :disabled="disable" :maxTagCount="3"> <a-select-option v-for="item in filteredOptions" :key="item.id" :value="item.id"> {{ item.recipientName }}({{item.company}} {{item.phone}}) </a-select-option> diff --git a/src/views/Admin/history.vue b/src/views/Admin/history.vue index 0556fae..dd5ace3 100644 --- a/src/views/Admin/history.vue +++ b/src/views/Admin/history.vue @@ -30,7 +30,7 @@ <a-input v-model="search.searchParams.publishingUnit" placeholder="单位名称" style="width: 100%"/> </a-col> <a-col :span="4"> - <a-button type="primary" @click="getData">查询</a-button> + <a-button type="primary" @click="searchData">查询</a-button> <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> </a-col> </a-row> @@ -63,9 +63,9 @@ <a-button @click="viewFile(item)" type="link" v-for="(item,index) in attachment" :key="index"><a-icon type="paper-clip"/>{{item.attachmentName}}</a-button> </div> </template > - <template #responseSituation="text"> - <a-tag :color="text === 3 ? 'red' :text === 2? 'green':text === 1?'orange':'blue'"> - {{text == 1 ? '待叫应' : text == 2 ?'已叫应':text == 3 ?'超时未叫应' : ''}} + <template #responsesRate="text"> + <a-tag :color="Number(text.split('%')[0]) == 100 ? 'green' :Number(text.split('%')[0]) == 0? 'red':'orange'"> + {{ text }} </a-tag> </template> <template #operation="text, record, index"> @@ -131,10 +131,10 @@ }, }, { - title: '叫应情况', - dataIndex: 'responseSituation', + title: '叫应率', + dataIndex: 'responsesRate', scopedSlots: { - customRender: 'responseSituation' + customRender: 'responsesRate' }, //设置定制化表格数据 }, { @@ -260,6 +260,11 @@ console.log('onOk: ', value); }, + searchData(){ + this.search.pageIndex = 1 + this.getData() + }, + resetSearch(){ const t = this t.search = { diff --git a/src/views/Admin/list.vue b/src/views/Admin/list.vue index 008c24f..bbad73a 100644 --- a/src/views/Admin/list.vue +++ b/src/views/Admin/list.vue @@ -30,7 +30,7 @@ /> </a-col> <a-col :span="4"> - <a-button type="primary" @click="getData">查询</a-button> + <a-button type="primary" @click="searchData">查询</a-button> <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> </a-col> </a-row> @@ -272,6 +272,11 @@ }) }, + searchData(){ + this.search.pageIndex = 1 + this.getData() + }, + resetSearch(){ const t = this t.search = { diff --git a/src/views/Admin/massSend.vue b/src/views/Admin/massSend.vue index bea7eff..6bebbd2 100644 --- a/src/views/Admin/massSend.vue +++ b/src/views/Admin/massSend.vue @@ -75,6 +75,35 @@ <a-row :gutter="24"> <a-col :span="12"> <div style="display:flex;justify-content: space-between;align-items: center;"> + <b>原通讯录接收人:</b> + <a-checkbox :checked="checkTxlAll" @change="checkTxlChange"> + 全选 + </a-checkbox> + </div> + <a-form-model-item prop="txlUsers"> + <a-tree-select + show-search + tree-checkable + style="width: 100%" + v-model="form.txlUsers" + :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" + placeholder="选择原通讯录接收人" + allow-clear + multiple + :maxTagCount="3" + @change="onTxlChanges" + @search="onTxlSearch" + @select="onTxlSelect" + :tree-data="addressBook" + :replaceFields="replaceTxlFields" + > + </a-tree-select> + </a-form-model-item> + </a-col> + </a-row> + <a-row :gutter="24"> + <a-col :span="12"> + <div style="display:flex;justify-content: space-between;align-items: center;"> <b>选择接收单位:</b> <a-checkbox :checked="checkAll" @change="checkChange"> 全选 @@ -91,6 +120,7 @@ placeholder="选择工作通知接收单位" allow-clear multiple + :maxTagCount="3" @change="onChanges" @search="onSearch" @select="onSelect" @@ -109,7 +139,7 @@ </div> <a-form-model-item> <a-select mode="multiple" placeholder="选择平级接收单位" v-model="form.recipient" @change="handle"> - <a-select-option v-for="item in filteredOptions" :key="item.id" :value="item.id"> + <a-select-option v-for="item in filteredOptions" :key="item.id" :value="item.id" :maxTagCount="3"> {{ item.recipientName }}({{item.company}} {{item.phone}}) </a-select-option> </a-select> @@ -153,7 +183,8 @@ </template> <script> -import { getPeerRecipient, getAreaWithUserIfo } from '@/api/user' +import {getPeerRecipient, getAreaWithUserIfo, getUserByGroup} from '@/api/user' +import { TreeSelect } from 'ant-design-vue'; import { massSend } from "@/api/send"; import {getUserInfo} from "@/util/storage"; export default { @@ -171,13 +202,23 @@ warningLevel: undefined, content: '', publishingUnit: '', + txlUsers: [], receiver: [], recipient: [], + addressBookRecipient: [], verticalRecipient: [], horizontalRecipient: [] }, + checkTxlAll: false, checkAll: false, checkSlAll: false, + addressBook: [], + replaceTxlFields: { + children:'userInfos', + title:'name', + key:'id', + value: 'id' + }, areaUsers: [], replaceFields: { children:'children', @@ -215,12 +256,42 @@ created() { const t = this t.form.publishingUnit = t.userInfo.company + t.getUserByGroup() t.getSameLevel() t.getAreaUsers() }, computed: { }, methods: { + // 获取原通讯录 + async getUserByGroup(){ + let t = this + let res = await getUserByGroup() + if(res.data.code == 100){ + if(res.data.data){ + let bookData = [] + bookData = res.data.data + for(let i in bookData){ + if(!bookData[i].userInfos || bookData[i].userInfos.length == 0){ + bookData.splice(i, 1) + } + } + for(let j of bookData){ + j.id = j.id.toString() + '-' + '1' + j.userInfos.map((item)=>{ + item.name = item.name + '('+ item.company + ' ' + item.phone + ')' + return item + }) + } + t.addressBook = bookData + }else{ + console.log('暂无数据') + } + }else{ + this.$message.warning(res.data.msg); + } + }, + // 获取同级接收人 async getSameLevel(){ let t = this @@ -254,16 +325,34 @@ } }, + //选择子部门部分 + onTxlChanges(value,label,extra) { + const t = this + if(t.form.txlUsers.length == 0){ + t.checkTxlAll = false + } + }, + checkTxlChange(e) { + const t = this + this.checkTxlAll = !this.checkTxlAll + if(t.checkTxlAll == true){ + let res = [] + for(let i of t.addressBook){ + if(i.userInfos && i.userInfos.length>0) + res = res.concat(...i.userInfos) + } + t.form.txlUsers = res.map(i=>i.id) + }else{ + t.form.txlUsers = [] + } + }, + //选择子部门部分 onChanges(value,label,extra) { const t = this if(t.form.receiver.length == 0){ t.checkAll = false } - - // for(let i of value){ - // t.form.verticalRecipient = [...t.form.verticalRecipient,...t.findNodeById(t.areaUsers,i.value).users] - // } }, checkChange(e) { @@ -299,8 +388,20 @@ confirmSend(){ this.$refs.ruleForm.validate(valid => { if (valid) { + this.form.addressBookRecipient = [] this.form.verticalRecipient = [] this.form.horizontalRecipient = [] + + const address = this.form.txlUsers.map((item)=> + { + this.findUserById(item).recipientType = 3 + const {addressBookGroupId,...data} = this.findUserById(item) + data.name = data.name.split('(')[0] + return data + } + ) + this.form.addressBookRecipient = address + const aList = this.form.receiver.map(item=>this.findNodeById(this.areaUsers,item.value)?.users) if(aList.includes(null)){ this.$message.error('选择接收单位时存在无用户的单位') @@ -313,6 +414,7 @@ const obj = {recipientUnit,recipientType:1,...rest} this.form.verticalRecipient.push(obj) } + if(this.form.recipient.length>0){ const bList = this.form.recipient.map(item => this.filteredOptions.find(i=>i.id == item)) for(let i of bList){ @@ -322,13 +424,14 @@ this.form.horizontalRecipient.push(noId) } } - const {receiver,recipient,...data} = this.form + const {txlUsers,receiver,recipient,...data} = this.form massSend(data).then( res =>{ if(res.data.code == 100){ this.$message.success('信息群发成功') this.$refs.ruleForm.clearValidate() this.$refs.ruleForm.resetFields() this.form.recipient = [] + this.checkTxlAll = false this.checkAll = false this.checkSlAll = false }else{ @@ -348,6 +451,12 @@ // console.log(...arguments); }, onSelect() { + // console.log(...arguments); + }, + onTxlSearch() { + // console.log(...arguments); + }, + onTxlSelect() { // console.log(...arguments); }, //选择平级部门部分 @@ -391,6 +500,19 @@ return null; }, + findUserById(id){ + for(let i of this.addressBook){ + if(i.userInfos && i.userInfos.length>0){ + for(let j of i.userInfos){ + if(j.id == id){ + return j + } + } + } + } + return null + }, + // 将树状数据所有id和name放入对象数组 traverseTree(treeData) { let result = []; diff --git a/src/views/Admin/msgRecord.vue b/src/views/Admin/msgRecord.vue index 3faef87..9969882 100644 --- a/src/views/Admin/msgRecord.vue +++ b/src/views/Admin/msgRecord.vue @@ -12,7 +12,7 @@ /> </a-col> <a-col :span="4"> - <a-button type="primary" @click="getData">查询</a-button> + <a-button type="primary" @click="searchData()">查询</a-button> <a-button style="margin-left: 12px" @click="resetSearch">重置</a-button> </a-col> </a-row> @@ -102,6 +102,11 @@ } }, + searchData(){ + this.search.pageIndex = 1 + this.getData() + }, + resetSearch(){ const t = this t.search = { diff --git a/src/views/Admin/notice.vue b/src/views/Admin/notice.vue index 09c6b7c..4fdcbc7 100644 --- a/src/views/Admin/notice.vue +++ b/src/views/Admin/notice.vue @@ -93,6 +93,7 @@ <a-form-model-item prop="receiver" style="margin-bottom: 6px"> <a-tree-select show-search + :maxTagCount="3" tree-checkable treeCheckStrictly style="width: 100%" @@ -121,7 +122,7 @@ </a-checkbox> </div> <a-form-model-item prop="recipient"> - <a-select mode="multiple" placeholder="选择平级接收单位" v-model="form.recipient" @change="handle"> + <a-select mode="multiple" placeholder="选择平级接收单位" v-model="form.recipient" @change="handle" :maxTagCount="3"> <a-select-option v-for="item in filteredOptions" :key="item.id" :value="item.id"> {{ item.recipientName }}({{item.company}} {{item.phone}}) </a-select-option> diff --git a/src/views/Admin/release.vue b/src/views/Admin/release.vue index 6adf908..00707b5 100644 --- a/src/views/Admin/release.vue +++ b/src/views/Admin/release.vue @@ -60,9 +60,9 @@ <a-button @click="viewFile(item)" type="link" v-for="(item,index) in attachment" :key="index"><a-icon type="paper-clip"/>{{item.attachmentName}}</a-button> </div> </template > - <template #responseSituation="text"> - <a-tag :color="text === 3 ? 'green' :text === 2? 'blue':text === 1?'orange':'red'"> - {{text == 1 ? '均未叫应' : text == 2 ?'部分叫应':text == 3 ?'全部叫应' : ''}} + <template #responsesRate="text"> + <a-tag :color="Number(text.split('%')[0]) == 100 ? 'green' :Number(text.split('%')[0]) == 0? 'red':'orange'"> + {{ text }} </a-tag> </template> <template #operation="text, record, index"> @@ -141,10 +141,10 @@ }, { title: '叫应情况', - dataIndex: 'responseSituation', + dataIndex: 'responsesRate', width: '10%', scopedSlots: { - customRender: 'responseSituation' + customRender: 'responsesRate' }, //设置定制化表格数据 }, { diff --git a/src/views/Admin/userManage.vue b/src/views/Admin/userManage.vue index 585a1d8..340998f 100644 --- a/src/views/Admin/userManage.vue +++ b/src/views/Admin/userManage.vue @@ -268,7 +268,6 @@ onChange(value) { const t = this t.search.searchParams.districtId = value[value.length - 1] - console.log(value,'val') }, findAreaById(data,value) { for (const node of data) { diff --git a/vue.config.js b/vue.config.js index dc354e9..1048b70 100644 --- a/vue.config.js +++ b/vue.config.js @@ -10,4 +10,22 @@ ], }, }, + configureWebpack: { + module: { + rules: [ + { + test: /\.(xlsx|xls)$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', // 保留原始文件名和扩展名 + outputPath: 'assets', // 输出文件的文件夹路径,可以根据需要更改 + }, + }, + ], + }, + ], + }, + } }; -- Gitblit v1.9.2