马宇豪
2023-07-19 0bab85897653f65df1932edb829f2af2bf58b846
修改
已修改17个文件
已添加4个文件
1104 ■■■■■ 文件已修改
package-lock.json 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/user.js 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/example.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/menu/index.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/Report.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/addressBook.vue 269 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/callRecord.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/components/addressUserMod.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/components/groupListMod.vue 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/components/msgEditMod.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/history.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/list.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/massSend.vue 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/msgRecord.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/notice.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/release.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Admin/userManage.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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": {
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"
src/App.vue
@@ -11,8 +11,8 @@
  -moz-osx-font-smoothing: grayscale;
}
* {
  margin: 0px;
  padding: 0px;
  margin: 0;
  padding: 0;
}
.inner{
    background-color: #fff;
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
    })
}
src/assets/example.xlsx
Binary files differ
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",
                }
            ],
        },
        {
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',
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 = {
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>
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 = {
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>
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>
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>
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 = {
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 = {
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 = [];
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 = {
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>
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'
            }, //设置定制化表格数据
        },
        {
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) {
vue.config.js
@@ -10,4 +10,22 @@
            ],
        },
    },
    configureWebpack: {
        module: {
            rules: [
                {
                    test: /\.(xlsx|xls)$/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[name].[ext]', // 保留原始文件名和扩展名
                                outputPath: 'assets', // 输出文件的文件夹路径,可以根据需要更改
                            },
                        },
                    ],
                },
            ],
        },
    }
};