lizhenhao %!s(int64=2) %!d(string=hai) anos
pai
achega
3839d7cf8e

+ 8 - 1
src/api/sys/UserService.js

@@ -9,7 +9,14 @@ export default class UserService {
       data: inputForm
     })
   }
-
+  saveCertList (inputForm) {
+    return request({
+      url: `/sys/user/saveCertList`,
+      method: 'post',
+      headers: {arrayFormat: 'repeat'},
+      data: inputForm
+    })
+  }
   saveInfo (inputForm) {
     return request({
       url: `/sys/user/saveInfo`,

+ 388 - 0
src/views/modules/sys/cert/CertForm.vue

@@ -0,0 +1,388 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+  <el-dialog
+    :title="title"
+    :close-on-click-modal="false"
+     v-dialogDrag
+    width="1400px"
+    :visible.sync="visible">
+    <el-form size="small" :model="inputForm" ref="inputForm" @keyup.enter.native="doSubmit()"
+             label-width="120px" v-loading="loading" :class="method==='view'?'readonly':''" :disabled="method==='view'" @submit.native.prevent>
+     <el-divider content-position="left"><i class="el-icon-user"></i> 人员信息</el-divider>
+      <el-row :gutter="15">
+        <el-col :span="12">
+            <el-form-item label="姓名" :rules="[{required: true, message: '姓名不能为空', trigger: 'blur'}]" prop="name">
+              <el-input :disabled="true" v-model="inputForm.name" maxlength="50" placeholder=""></el-input>
+            </el-form-item>
+        </el-col>
+         <el-col :span="12">
+           <el-form-item label="手机" prop="mobile" :rules="[{validator:validator.isMobile, trigger:'blur'}]">
+             <el-input :disabled="true" v-model="inputForm.mobile" maxlength="50" placeholder=""></el-input>
+           </el-form-item>
+         </el-col>
+         <el-col :span="12">
+           <el-form-item prop="officeDTO.id" :rules="[{required: true, message: '部门不能为空', trigger: 'blur'}]" label="部门">
+             <SelectTree
+               ref="officeTree"
+               :props="{
+                value: 'id',             // ID字段名
+                label: 'name',         // 显示名称
+                children: 'children'    // 子级字段名
+              }"
+
+               :url="`/sys/office/treeData?type=2`"
+               :value="inputForm.officeDTO.id"
+               :clearable="true"
+               :accordion="true"
+               :disabled="true"
+               @getValue="(value) => {inputForm.officeDTO.id=value}"/>
+           </el-form-item>
+         </el-col>
+         <el-col :span="12">
+           <el-form-item label="岗位" prop="postIdList">
+             <el-select :disabled="true" v-model="inputForm.postIdList" style="width:100%" multiple placeholder="请选择">
+               <el-option
+                 v-for="item in postList"
+                 :key="item.id"
+                 :label="item.name"
+                 :value="item.id">
+               </el-option>
+             </el-select>
+           </el-form-item>
+         </el-col>
+         <el-col :span="12">
+           <el-form-item label="角色" prop="roleIdList" :rules="[{required: true, message: '角色不能为空', trigger: 'blur'}]">
+             <el-select :disabled="true" v-model="inputForm.roleIdList" style="width:100%" multiple placeholder="请选择">
+               <el-option
+                 v-for="role in roleList"
+                 :key="role.id"
+                 :label="role.name"
+                 :value="role.id">
+               </el-option>
+             </el-select>
+           </el-form-item>
+         </el-col>
+    </el-row>
+      <el-divider content-position="left"><i class="el-icon-document"></i>
+        执业资格证书
+        <el-button style="margin-left: 20px" type="primary" :disabled="method==='view'" size="mini" @click="insertEvent()" plain>
+          新增
+        </el-button>
+      </el-divider>
+      <el-row  :gutter="15">
+        <vxe-table
+          border
+          show-overflow
+          show-footer
+          ref="certTable"
+          class="vxe-table-element"
+          :data="inputForm.certDTOList"
+          style="margin-left: 5em"
+          @cell-click=""
+          @edit-closed=""
+          :key="tableKey"
+          highlight-current-row
+          :edit-config="{trigger: 'click', mode: 'row', showStatus: true, autoClear: true}"
+        >
+          <vxe-table-column field="type"align="center" title="证书类型" :edit-render="{name: '$select', options: $dictUtils.getDictList('sys_cert_type')}">
+            <template v-slot:edit="scope">
+              <vxe-select v-model="scope.row.type" @change="changeType(scope.$rowIndex)" placeholder="请选择证书类型" clearable style="width: 100%;" transfer>
+                <vxe-option
+                  v-for="item in $dictUtils.getDictList('sys_cert_type')"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value">
+                </vxe-option>
+              </vxe-select>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="no"align="center" title="证书编号" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <el-input v-model="scope.row.no" ></el-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="authorities"align="center" title="发证机关" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <el-input v-model="scope.row.authorities" ></el-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="issuedDate"align="center" title="发证日期" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <vxe-input v-model="scope.row.issuedDate" placeholder="发证日期" type="date" transfer></vxe-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="enrollDate"align="center" title="注册日期" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <vxe-input v-model="scope.row.enrollDate" placeholder="注册日期" type="date" transfer></vxe-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="expireDate"align="center" title="到期日期" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <vxe-input v-model="scope.row.expireDate" placeholder="到期日期" type="date" transfer></vxe-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="enrollCertNo"align="center" title="注册证书编号" :edit-render="{}">
+            <template v-slot:edit="scope">
+              <el-input v-model="scope.row.enrollCertNo" ></el-input>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column field="profession"align="center" title="专业" :edit-render="{name: '$select'}">
+            <template v-slot:edit="scope">
+              <vxe-select v-model="scope.row.profession" @change="changeProfession" placeholder="请选择专业" clearable style="width: 100%;" transfer>
+                <vxe-option
+                  v-for="item in scope.row.professionList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.label">
+                </vxe-option>
+              </vxe-select>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column min-width="50px" field="fileUrl" align="center" title="文件" :edit-render="{}">
+            <template v-slot="{row}">
+              <img style="height: 43px" class="img" :src="row.fileUrl"/>
+            </template>
+            <template v-slot:edit="scope" >
+                <el-upload slot="reference"
+                           class="avatar"
+                           style="height: 43px;line-height: 43px"
+                           :action="`${$http.BASE_URL}/sys/file/webupload/upload?uploadPath=fileUrl`"
+                           :on-success="(res,file)=> handleAvatarSuccess(res,file,scope)"
+                           :before-upload="beforeAvatarUpload"
+                           :show-file-list="false">
+                  <el-button type="info" slot="trigger" size="mini" style="margin-right: 10px" class="avatar-uploader-table">上传</el-button>
+                  <img style="max-height: 43px;max-width: 56px" slot="tip" :readonly="true" @click="showPhoto(scope.row.fileUrl)" v-if="scope.row.fileUrl" :src="scope.row.fileUrl" class="el-upload-list__item-thumbnail">
+                </el-upload>
+            </template>
+          </vxe-table-column>
+          <vxe-table-column title="操作" width="100">
+            <template v-slot="scope">
+              <el-button size="mini" type="danger" @click="removeEvent(scope.row,scope.$rowIndex)">删除</el-button>
+            </template>
+          </vxe-table-column>
+        </vxe-table>
+      </el-row>
+    </el-form>
+    <el-image-viewer
+      v-if="showViewer"
+      :on-close="closeViewer"
+      :url-list="[url]"
+      :zIndex="99999"
+    />
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small" @click="visible = false" icon="el-icon-circle-close">关闭</el-button>
+      <el-button size="small" type="primary" v-if="method != 'view'" @click="doSubmit()" icon="el-icon-circle-check" v-noMoreClick>确定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import SelectTree from '@/components/treeSelect/treeSelect.vue'
+import UserService from '@/api/sys/UserService'
+import PostService from '@/api/sys/PostService'
+import RoleService from '@/api/sys/RoleService'
+import ElImageViewer from 'element-ui/packages/image/src/image-viewer'
+export default {
+  data () {
+    return {
+      visible: false,
+      loading: false,
+      title: '',
+      method: '',
+      roleList: [],
+      postList: [],
+      inputForm: {
+        id: '',
+        companyDTO: { // 归属公司
+          id: '',
+          name: ''
+        },
+        officeDTO: {
+          id: '',
+          name: ''
+        },
+        roleIdList: [],
+        postIdList: [],
+        loginName: '', // 登录名
+        no: '', // 工号
+        name: '', // 姓名
+        email: '', // 邮箱
+        phone: '', // 电话
+        mobile: '', // 手机
+        loginFlag: '1', // 是否允许登陆
+        photo: '', // 头像
+        qrCode: '', // 二维码
+        oldLoginName: '', // 原登录名
+        newPassword: '', // 新密码
+        confirmNewPassword: '',
+        sign: '', // 签名
+        remarks: '', // 备注
+        certDTOList: [] // 资格证书列表
+      },
+      professionList: [],
+      tableKey: '',
+      url: '',
+      showViewer: false
+    }
+  },
+  userService: null,
+  roleService: null,
+  postService: null,
+  created () {
+    this.userService = new UserService()
+    this.roleService = new RoleService()
+    this.postService = new PostService()
+  },
+  mounted () {
+  },
+  components: {
+    SelectTree,
+    ElImageViewer
+  },
+  methods: {
+    init (method, id) {
+      this.method = method
+      this.inputForm.id = id
+      if (method === 'add') {
+        this.title = `新建用户`
+      } else if (method === 'edit') {
+        this.title = '修改执业资格证书'
+      } else if (method === 'view') {
+        this.title = '查看执业资格证书'
+      }
+      this.visible = true
+      this.$nextTick(() => {
+        this.$refs.inputForm.resetFields()
+        this.inputForm.oldLoinName = ''
+        this.roleService.list({current: 1, size: -1}).then(({data}) => {
+          this.roleList = data.records
+        })
+        this.postService.list({current: 1, size: -1}).then(({data}) => {
+          this.postList = data.records
+        })
+
+        if (method === 'edit' || method === 'view') { // 修改或者查看
+          this.loading = true
+          this.userService.queryById(this.inputForm.id).then(({data}) => {
+            this.inputForm = this.recover(this.inputForm, data)
+            this.inputForm.oldLoginName = this.inputForm.loginName
+            if (this.commonJS.isEmpty(this.inputForm.certDTOList)) {
+              this.inputForm.certDTOList = []
+            }
+            this.inputForm.certDTOList.forEach(item => {
+              if (item.type === '1' || item.type === '2') {
+                item.professionList = this.$dictUtils.getDictList('sys_cert_profession_build')
+              } else if (item.type === '3' || item.type === '4') {
+                item.professionList = this.$dictUtils.getDictList('sys_cert_profession_cost')
+              } else if (item.type === '5') {
+                item.professionList = this.$dictUtils.getDictList('sys_cert_profession_supervision')
+              }
+            })
+            this.loading = false
+          })
+        }
+      })
+    },
+    beforeAvatarUpload (file) {
+      const isJPG = file.type.indexOf('image/') >= 0
+      const isLt2M = file.size / 1024 / 100 <= 1
+
+      if (!isJPG) {
+        this.$message.error('上传文件只能是图片格式')
+        return false
+      }
+      if (!isLt2M) {
+        this.$message.error('上传文件大小不能超过 100KB')
+        return false
+      }
+      return true
+    },
+      // 表单提交
+    doSubmit () {
+      this.$refs['inputForm'].validate((valid) => {
+        if (valid) {
+          this.loading = true
+          this.inputForm.certDTOList.forEach((item, index) => {
+            if (this.commonJS.isEmpty(item.type)) {
+              this.$message.error('执业资格证书中第' + (index + 1) + '条数据的“证书类型”未填写')
+              this.loading = false
+              throw new Error()
+            }
+            if (this.commonJS.isEmpty(item.no)) {
+              this.$message.error('执业资格证书中第' + (index + 1) + '条数据的“证书编号”未填写')
+              this.loading = false
+              throw new Error()
+            }
+            if (this.commonJS.isNotEmpty(item.issuedDate)) {
+              item.issuedDate = this.moment(item.issuedDate).format('YYYY-MM-DD')
+            }
+            if (this.commonJS.isNotEmpty(item.enrollDate)) {
+              item.enrollDate = this.moment(item.enrollDate).format('YYYY-MM-DD')
+            }
+            if (this.commonJS.isNotEmpty(item.expireDate)) {
+              item.expireDate = this.moment(item.expireDate).format('YYYY-MM-DD')
+            } else {
+              this.$message.error('执业资格证书中第' + (index + 1) + '条数据的“到期日期”未填写')
+              this.loading = false
+              throw new Error()
+            }
+          })
+          this.userService.saveCertList(this.inputForm).then(({data}) => {
+            this.loading = false
+            this.visible = false
+            this.$message.success(data)
+            this.$emit('refreshDataList')
+          }).catch(() => {
+            this.loading = false
+          })
+        }
+      })
+    },
+    // 新增
+    insertEvent () {
+      this.$refs.certTable.insert().then((data) => {
+        this.inputForm.certDTOList.push(data)
+      })
+    },
+    // 删除
+    removeEvent (row, rowIndex) {
+      this.$refs.certTable.remove(row)
+      this.inputForm.certDTOList.splice(rowIndex, 1)
+    },
+    changeType (index) {
+      this.inputForm.certDTOList[index].profession = ''
+      if (this.inputForm.certDTOList[index].type === '1' || this.inputForm.certDTOList[index].type === '2') {
+        this.inputForm.certDTOList[index].professionList = this.$dictUtils.getDictList('sys_cert_profession_build')
+      } else if (this.inputForm.certDTOList[index].type === '3' || this.inputForm.certDTOList[index].type === '4') {
+        this.inputForm.certDTOList[index].professionList = this.$dictUtils.getDictList('sys_cert_profession_cost')
+      } else if (this.inputForm.certDTOList[index].type === '5') {
+        this.inputForm.certDTOList[index].professionList = this.$dictUtils.getDictList('sys_cert_profession_supervision')
+      }
+    },
+    changeProfession () {
+      this.$forceUpdate()
+    },
+    handleAvatarSuccess (res, file, scope) {
+      this.inputForm.certDTOList[scope.$rowIndex].fileUrl = res.url
+      this.$forceUpdate()
+    },
+    showPhoto (fileUrl) {
+      this.url = fileUrl
+      this.showViewer = true
+    },
+    // 关闭查看器
+    closeViewer () {
+      this.url = ''
+      this.showViewer = false
+    }
+  }
+}
+</script>
+<style scoped>
+.avatar{
+  height: 100px;
+}
+.el-divider__text {
+    font-weight: bold;
+    font-size: 16px;
+}
+</style>

+ 202 - 0
src/views/modules/sys/cert/CertList.vue

@@ -0,0 +1,202 @@
+<template>
+      <div class="page">
+        <el-form size="small" :inline="true" class="query-form" ref="searchForm" :model="searchForm" @keyup.enter.native="refreshList()" @submit.native.prevent>
+          <el-form-item prop="loginName">
+            <el-input size="small" v-model="searchForm.loginName" placeholder="登录名" clearable></el-input>
+          </el-form-item>
+          <el-form-item prop="name">
+            <el-input size="small" v-model="searchForm.name" placeholder="姓名" clearable></el-input>
+          </el-form-item>
+        <el-form-item>
+          <el-button  type="primary" @click="refreshList()" size="small" icon="el-icon-search">查询</el-button>
+          <el-button @click="resetSearch()" size="small" icon="el-icon-refresh-right">重置</el-button>
+        </el-form-item>
+        </el-form>
+      <div class="bg-white top" style="padding-top: 0">
+          <vxe-toolbar   :refresh="{query: refreshList}" >
+              <template #buttons>
+              </template>
+          </vxe-toolbar>
+          <div style="height: calc(100% - 80px);">
+              <vxe-table
+                  border="inner"
+                  auto-resize
+                  resizable
+                  height="auto"
+                  :loading="loading"
+                  size="small"
+                  ref="certTable"
+                  show-header-overflow
+                  show-overflow
+                  highlight-hover-row
+                  :print-config="{}"
+                  :import-config="{}"
+                  :export-config="{}"
+                  @sort-change="sortChangeHandle"
+                  :sort-config="{remote:true}"
+                  :data="dataList"
+                  >
+                <vxe-column type="seq" width="60" title="序号"></vxe-column>
+                <vxe-column type="checkbox"  width="40px"></vxe-column>
+                <vxe-column  title="头像" field="photo">
+                    <template slot-scope="scope">
+                      <img :src="scope.row.photo === ''?'/static/img/avatar.png':scope.row.photo" style="height:35px"/>
+                    </template>
+                </vxe-column>
+                <vxe-column  title="登录名" field="loginName" sortable>
+                  <template slot-scope="scope">
+                    <el-link  type="primary" :underline="false" v-if="hasPermission('sys:user:edit')" @click="edit(scope.row.id)">{{scope.row.loginName}}</el-link>
+                    <el-link  type="primary" :underline="false" v-else-if="hasPermission('sys:user:view')"  @click="view(scope.row.id,)">{{scope.row.loginName}}</el-link>
+                    <span v-else>{{scope.row.loginName}}</span>
+                  </template>
+                </vxe-column>
+
+                <vxe-column  title="姓名" field="name" sortable></vxe-column>
+                <vxe-column  title="手机号" field="mobile" sortable></vxe-column>
+                <vxe-column  title="集团" field="companyDTO.name" sortable>
+                  <template slot-scope="scope">
+                    <el-tag>{{scope.row.companyDTO && scope.row.companyDTO.name}}</el-tag>
+                  </template>
+                </vxe-column>
+                <vxe-column  title="部门" field="officeDTO.name" sortable>
+                  <template slot-scope="scope">
+                    <el-tag>{{scope.row.officeDTO && scope.row.officeDTO.name}}</el-tag>
+                  </template>
+                </vxe-column>
+                <vxe-column  title="状态" field="loginFlag" sortable>
+                  <template slot-scope="scope">
+                    <el-tag v-if="scope.row.loginFlag === '1'" size="small" type="success">正常</el-tag>
+                    <el-tag v-else-if="scope.row.loginFlag === '0'" size="small" type="danger">禁用</el-tag>
+                  </template>
+                </vxe-column>
+                <vxe-column title="操作" width="100" fixed="right" align="center">
+                  <template slot-scope="scope">
+                    <el-button v-if="hasPermission('sys:user:edit')" type="text" size="small" icon="el-icon-edit" @click="edit(scope.row.id)">编辑</el-button>
+                  </template>
+                </vxe-column>
+              </vxe-table>
+              <vxe-pager
+                background
+                size="small"
+                :current-page="tablePage.currentPage"
+                :page-size="tablePage.pageSize"
+                :total="tablePage.total"
+                :page-sizes="[10, 20, 100, 1000, {label: '全量数据', value: 1000000}]"
+                :layouts="['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes', 'Total']"
+                @page-change="currentChangeHandle">
+              </vxe-pager>
+          </div>
+      <!-- 弹窗, 新增 / 修改 -->
+      <cert-form  ref="certForm" @refreshDataList="refreshList"></cert-form>
+      </div>
+    </div>
+</template>
+
+<script>
+  import CertForm from './CertForm'
+  import UserService from '@/api/sys/UserService'
+  import OfficeService from '@/api/sys/OfficeService'
+
+  // import XEAjax from 'xe-ajax'
+  // import XEUtils from 'xe-utils'
+
+  export default {
+    data () {
+      return {
+        searchForm: {
+          loginName: '',
+          name: '',
+          companyDTO: {
+            id: ''
+          },
+          officeDTO: {
+            id: ''
+          }
+        },
+        dataList: [],
+        tablePage: {
+          total: 0,
+          currentPage: 1,
+          pageSize: 10,
+          orders: []
+        },
+        loading: false
+      }
+    },
+    components: {
+      CertForm
+    },
+    userService: null,
+    officeService: null,
+    created () {
+      this.userService = new UserService()
+      this.officeService = new OfficeService()
+    },
+    activated () {
+      this.refreshList()
+    },
+    watch: {
+    },
+    methods: {
+      // 获取数据列表
+      refreshList () {
+        this.loading = true
+        this.userService.list({
+          'current': this.tablePage.currentPage,
+          'size': this.tablePage.pageSize,
+          'orders': this.tablePage.orders,
+          ...this.searchForm
+        }).then(({data}) => {
+          this.dataList = data.records
+          this.tablePage.total = data.total
+          this.loading = false
+        })
+      },
+      // 当前页
+      currentChangeHandle ({ currentPage, pageSize }) {
+        this.tablePage.currentPage = currentPage
+        this.tablePage.pageSize = pageSize
+        this.refreshList()
+      },
+      // 排序
+      sortChangeHandle (column) {
+        this.tablePage.orders = []
+        if (column.order != null) {
+          if (column.property === 'officeDTO.name') {
+            column.property = 'o.name'
+          }
+          if (column.property === 'companyDTO.name') {
+            column.property = 'c.name'
+          }
+          if (column.property === 'postDTO.name') {
+            column.property = 'p.name'
+          }
+          this.tablePage.orders.push({column: this.$utils.toLine(column.property), asc: column.order === 'asc'})
+        }
+        this.refreshList()
+      },
+      // 修改
+      edit (id) {
+        id = id || this.$refs.certTable.getCheckboxRecords().map(item => {
+          return item.id
+        })[0]
+        this.$refs.certForm.init('edit', id)
+      },
+      // 查看
+      view (id) {
+        this.$refs.certForm.init('view', id)
+      },
+      resetSearch () {
+        this.searchForm.companyDTO.id = ''
+        this.searchForm.officeDTO.id = ''
+        this.$refs.searchForm.resetFields()
+        this.refreshList()
+      }
+    }
+  }
+</script>
+<style lang="scss">
+.el-card__body {
+    overflow: auto;
+}
+</style>