hejianhao
2025-04-02 f425ef0d689a312d0600a55097ad260cadfa87fc
系统管理
5个文件已修改
13个文件已添加
2375 ■■■■■ 已修改文件
src/App.vue 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SelectMember/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/TableSlot/index.vue 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 111 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/projectList/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/operation-log/index.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/add.vue 403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/detail.vue 352 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/edit.vue 398 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/index.vue 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/role/service.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/add-edit.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/disb.vue 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/inherit.vue 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/reset-password.vue 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/components/view-data.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/service.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue
@@ -94,24 +94,54 @@
  }
}
.select-member {
  .el-dialog {
    border-radius: 16px 16px 6px 6px;
.el-dialog {
  border-radius: 16px 16px 6px 6px;
    .el-dialog__header {
      font-family: 'Source Han Sans CN Bold Bold';
      font-weight: bold;
      font-size: 20px;
      line-height: 20px;
      color: #222222;
      padding: 39px 0 36px 30px;
      border-bottom: 1px solid rgba(238, 238, 238, 1);
    }
    .el-dialog__body {
      padding: 15px 24px 33px 14px;
      overflow: hidden;
    }
  .el-dialog__header {
    font-family: 'Source Han Sans CN Bold Bold';
    font-weight: bold;
    font-size: 20px;
    line-height: 20px;
    color: #222222;
    padding: 39px 0 36px 30px;
    border-bottom: 1px solid rgba(238, 238, 238, 1);
  }
  .el-dialog__body {
    padding: 15px 24px 33px 14px;
    overflow: hidden;
  }
}
.el-input__inner:focus,
.el-select .el-input__inner:focus,
.el-select .el-input.is-focus .el-input__inner {
  border-color: #009688;
}
.el-switch.is-checked .el-switch__core {
  background-color: #009688;
  border-color: #009688;
}
.select-member-footer {
  display: flex;
  justify-content: center;
  button {
    width: 150px;
  }
}
.el-tabs__item.is-active {
  color: #009688;
}
.el-tabs__item:hover {
  color: #009688;
}
.el-tabs__active-bar {
  background-color: #009688;
}
</style>
src/components/SelectMember/index.vue
@@ -227,13 +227,4 @@
        }
    }
}
.select-member-footer {
    display: flex;
    justify-content: center;
    button {
        width: 150px;
    }
}
</style>
src/components/TableSlot/index.vue
@@ -1,13 +1,22 @@
<template>
    <div class="table-slot">
        <div class="search">
            <slot name="search"></slot>
        </div>
        <template v-if="$slots.setting">
            <div class="search">
                <slot name="search"></slot>
            </div>
        </template>
        <div class="table">
            <slot name="setting"></slot>
            <Table :data="tableData" :total="total" :queryForm="queryForm" @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
                <slot name="table"></slot>
            </Table>
            <template v-if="$slots.setting">
                <div style="margin-bottom: 20px;">
                    <slot name="setting"></slot>
                </div>
            </template>
            <template v-if="$slots.table">
                <Table :data="tableData" :total="total" :queryForm="queryForm" @currentChange="handleCurrentChange"
                    @sizeChange="handleSizeChange">
                    <slot name="table"></slot>
                </Table>
            </template>
        </div>
    </div>
</template>
@@ -54,6 +63,7 @@
    display: flex;
    flex-direction: column;
}
.search {
    padding: 34px 30px 15px 30px;
    box-shadow: 0px 10px 19px 0px rgba(0, 0, 0, 0.06);
src/router/index.js
@@ -38,6 +38,65 @@
        component: () => import("../views/login"),
    },
    {
        path: "/system",
        meta: {
            title: "系统管理",
        },
        component: Layouts,
        children: [
            {
                path: "user",
                name: "User",
                meta: {
                    title: "人员管理",
                },
                component: () => import("../views/system/user"),
            },
            {
                path: "role",
                name: "Role",
                meta: {
                    title: "角色管理",
                },
                component: () => import("../views/system/role"),
            },
            {
                path: "add-role",
                name: "AddRole",
                meta: {
                    title: "新增角色",
                    hide: true,
                },
                component: () => import("../views/system/role/add"),
            },
            {
                path: "edit-role",
                name: "EditRole",
                meta: {
                    title: "编辑角色",
                    hide: true,
                },
                component: () => import("../views/system/role/edit"),
            },
            {
                path: "detail-role",
                name: "DetailRole",
                meta: {
                    title: "角色详情",
                    hide: true,
                },
                component: () => import("../views/system/role/detail"),
            },
            {
                path: "operation-log",
                meta: {
                    title: "操作日志",
                },
                component: () => import("../views/system/operation-log"),
            },
        ]
    },
    {
        path: "/projectList",
        meta: {
            title: "项目组管理",
@@ -182,37 +241,37 @@
    //         return
    //     }
        // 设置标签列表
        if (!to.meta.hide || !to.meta.oneself) {
            let tagList = JSON.parse(localStorage.getItem('tagList') || '[]')
            // 判断是否存在
            let isExist = tagList.some(item => item.path === to.path)
            if (!isExist) {
                // 只保存必要的信息
                const tagInfo = {
                    path: to.path,
                    name: to.name,
                    meta: to.meta
                }
                tagList.push(tagInfo)
                localStorage.setItem('tagList', JSON.stringify(tagList))
                store.commit('SET_TAGLIST', tagList)
    // 设置标签列表
    if (!to.meta.hide || !to.meta.oneself) {
        let tagList = JSON.parse(localStorage.getItem('tagList') || '[]')
        // 判断是否存在
        let isExist = tagList.some(item => item.path === to.path)
        if (!isExist) {
            // 只保存必要的信息
            const tagInfo = {
                path: to.path,
                name: to.name,
                meta: to.meta
            }
            tagList.push(tagInfo)
            localStorage.setItem('tagList', JSON.stringify(tagList))
            store.commit('SET_TAGLIST', tagList)
        }
    }
        // 判断是否需要缓存
        if (to.meta.keepAlive) {
            let keepAliveList = JSON.parse(localStorage.getItem('keepAliveList') || '[]')
            // 判断是否已经缓存
            let isExist = keepAliveList.includes(to.name)
            if (!isExist) {
                keepAliveList.push(to.name)
                localStorage.setItem('keepAliveList', JSON.stringify(keepAliveList))
                store.commit('SET_KEEPALIVELIST', keepAliveList)
            }
    // 判断是否需要缓存
    if (to.meta.keepAlive) {
        let keepAliveList = JSON.parse(localStorage.getItem('keepAliveList') || '[]')
        // 判断是否已经缓存
        let isExist = keepAliveList.includes(to.name)
        if (!isExist) {
            keepAliveList.push(to.name)
            localStorage.setItem('keepAliveList', JSON.stringify(keepAliveList))
            store.commit('SET_KEEPALIVELIST', keepAliveList)
        }
    }
        next()
    next()
    // }
});
src/views/projectList/index.vue
@@ -13,14 +13,14 @@
                    <el-form-item label="通创建日期:">
                        <el-input v-model="form.name"></el-input>
                    </el-form-item>
                    <el-form-item label="" style="margin-left: 63px;">
                        <el-button type="default">重置</el-button>
                    <el-form-item style="margin-left: 63px;">
                        <el-button>重置</el-button>
                        <el-button type="primary">查询</el-button>
                    </el-form-item>
                </el-form>
            </template>
            <template #setting>
                <el-button @click="handleAddProject" class="el-icon-plus" style="margin-bottom: 20px;" type="primary">
                <el-button @click="handleAddProject" class="el-icon-plus" type="primary">
                    新增项目组</el-button>
            </template>
            <template #table>
src/views/system/operation-log/index.vue
New file
@@ -0,0 +1,59 @@
<template>
  <div class="operationLog">
    <TableCustom :queryForm="queryForm" :tableData="tableData" :total="total"
      @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
      <template #table>
        <el-table-column label="序号" type="index" show-overflow-tooltip />
        <el-table-column label="操作时间" prop="operTime" show-overflow-tooltip />
        <el-table-column label="操作人" prop="operName" show-overflow-tooltip />
        <el-table-column label="操作内容" prop="title" show-overflow-tooltip />
      </template>
    </TableCustom>
  </div>
</template>
<script>
// import { getOperlogList } from '@/api/systemSettings'
export default {
  name: 'operationLog',
  data() {
    return {
      list: null,
      total: 0,
      queryForm: {
        pageNum: 1,
        pageSize: 10,
      },
    }
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  created() {
    // this.fetchData()
  },
  methods: {
    handleSizeChange(val) {
      this.queryForm.pageSize = val
      this.fetchData()
    },
    handleCurrentChange(val) {
      this.queryForm.pageNum = val
      this.fetchData()
    },
    async fetchData() {
      this.listLoading = true
      const { data } = await getOperlogList(this.queryForm)
      this.list = data.data.records
      this.total = data.data.total
    },
  },
}
</script>
<style scoped lang="less">
.operationLog {
  height: 100%;
}
</style>
src/views/system/role/add.vue
New file
@@ -0,0 +1,403 @@
<template>
  <Card>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>角色信息</div>
    </div>
    <el-form ref="form" :inline="true" :model="form" :rules="rules" label-width="80px" class="demo-form-inline">
      <el-form-item label="角色名称" prop="roleName">
        <el-input v-model="form.roleName" placeholder="请输入角色名称"></el-input>
      </el-form-item>
      <el-form-item label="备注" prop="remark">
        <el-input v-model="form.remark" placeholder="请输入备注"></el-input>
      </el-form-item>
    </el-form>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>操作权限</div>
    </div>
    <div>
      <div class="header">
        <div class="w20">模块名称</div>
        <div class="sconed">
          <div class="subpage">
            <div class="title">页面名称</div>
            <div class="btns">权限</div>
          </div>
        </div>
      </div>
      <div v-for="item in menu" :key="item.menuId">
        <div class="row">
          <div class="w20">
            <el-checkbox v-model="item.selected" @change="(e) => {
              setCheckStatus1(item.menuId, e)
            }" :checked="item.selected">
              {{ item.menuName }}
            </el-checkbox>
          </div>
          <div class="sconed">
            <div class="subpage"
              v-if="(item.children.length > 0 && item.children[0].children.length > 0) || item.children[0].children.menuType != 'F'">
              <div v-for="item1 in item.children" :key="item1.menuId" class="two">
                <div class="left">
                  <el-checkbox v-model="item1.selected" @change="(e) => {
                    setCheckStatus2(item1.menuId, e, item.menuId)
                  }" :checked="item1.selected">
                    {{ item1.menuName }}
                  </el-checkbox>
                </div>
                <div class="right">
                  <div v-for="item2 in item1.children" :key="item2.menuId">
                    <el-checkbox v-model="item2.selected" @change="(e) => {
                      setCheckStatus3(item2.menuId, e, item1.menuId, item.menuId)
                    }" :checked="item2.selected">
                      {{ item2.menuName }}
                    </el-checkbox>
                  </div>
                </div>
              </div>
            </div>
            <div class="subpage" v-else>
              <div class="two">
                <!-- <div class="left">
                  </div> -->
                <div class="right">
                  <div v-for="item1 in item.children" :key="item1.menuId">
                    <el-checkbox v-model="item1.selected" @change="(e) => {
                      setCheckStatus2(item1.menuId, e, item.menuId,)
                    }" :checked="item1.selected">
                      {{ item1.menuName }}
                    </el-checkbox>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div v-if="menu.length == 0" :style="{ height: height + 'px' }">
        <div class="no-data">暂无数据</div>
      </div>
    </div>
    <div class="btn_box">
      <el-button @click="$router.go(-1)">返回</el-button>
      <el-button type="primary" @click="onSubmit">保存</el-button>
    </div>
  </Card>
</template>
<script>
import { roleInfoFromUserId, getRoleInfo, add, edit } from './service.js'
export default {
  name: 'add-role',
  components: {},
  props: {},
  data() {
    return {
      form: {
        roleName: "",
        remark: "",
      },
      rules: {
        roleName: [
          { required: true, message: "请输入角色名称", trigger: "blur" },
        ],
      },
      menu: [],
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  watch: {},
  created() {
    roleInfoFromUserId({ userId: 1 }).then(res => {
      if (this.$route.query.roleId) {
        getRoleInfo({ roleId: this.$route.query.roleId }).then(resp => {
          this.menu = this.setSelectedIds(res.data.data, resp.data.data.menus || []);
          this.form = {
            roleName: resp.data.data.roleName,
            remark: resp.data.data.remark,
            roleId: resp.data.data.roleId
          }
        })
      } else {
        this.menu = res.data.data
      }
    })
  },
  mounted() { },
  methods: {
    setSelectedIds(arr, selectKeyList) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      arr.forEach(traverse);
      return arr;
    },
    onSubmit() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          if (this.getSelectedIds(this.menu).length == 0) {
            this.msgwarning('请勾选操作权限')
            return
          }
          let obj = {
            ...this.form,
            menuIds: this.getSelectedIds(this.menu)
          }
          if (this.$route.query && this.$route.query.roleId) {
            obj.roleId = this.$route.query.roleId
            edit(obj).then(() => {
              this.msgsuccess('保存成功')
              this.$router.go(-1)
            })
          } else {
            add(obj).then(() => {
              this.msgsuccess('保存成功')
              this.form = {
                roleName: "",
                remark: "",
              }
              this.menu = []
              this.$router.go(-1)
            })
          }
        }
      })
    },
    getSelectedIds(arr) {
      let result = [];
      function traverse(item) {
        if (item.selected) {
          result.push(item.menuId);
        }
        if (item.children && item.children.length > 0) {
          for (let children of item.children) {
            traverse(children);
          }
        }
      }
      for (let item of arr) {
        traverse(item);
      }
      return result;
    },
    setCheckStatus1(id, status) { //点击第1级
      if (!status) {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = status
            if (item.children.length > 0) {
              item.children = item.children.map(item1 => {
                item1.selected = status
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    item2.selected = status
                    return { ...item2 }
                  })
                }
                return { ...item1 }
              })
            }
          }
          return { ...item }
        })
      } else {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = true
          }
          return { ...item }
        })
      }
    },
    setCheckStatus2(id, status, aId) { //点击第2级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == id) {
                item1.selected = status
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    },
    setCheckStatus3(id, status, bId, aId) {//点击第3级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == bId) {
                item1.selected = true
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    if (item2.menuId == id) {
                      item2.selected = status
                    }
                    return { ...item2 }
                  })
                }
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    }
  },
};
</script>
<style lang="less" scoped>
.title_box {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  font-weight: bold;
  div:first-child {
    width: 4px;
    height: 16px;
    background: #598DEC;
    margin-right: 8px;
  }
}
.no-data {
  height: 100%;
  background-color: #fff;
  border-radius: 0 0 6px 6px;
  border-left: 1px solid #e8e8e8;
  border-right: 1px solid #e8e8e8;
  border-bottom: 1px solid #e8e8e8;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #909399;
  font-size: 14px;
}
.btn_box {
  margin-top: 40px;
  text-align: center;
  button {
    width: 220px;
  }
}
.el-checkbox {
  display: flex;
  align-items: center;
}
.row,
.header {
  display: flex;
  align-items: center;
  border: 1px solid #e8e8e8;
  .w20 {
    width: 15%;
    padding: 8px 20px;
  }
  .sconed {
    flex: 1;
    .subpage {
      .title {
        border: 1px solid #e8e8e8;
        border-top: none;
        border-bottom: none;
      }
      .two {
        display: flex;
        align-items: center;
        border: 1px solid #e8e8e8;
        border-top: none;
        border-right: none;
        .left {
          width: 200px;
          padding: 13px 20px;
          border-right: 1px solid #e8e8e8;
        }
        .right {
          display: flex;
          flex: 1;
          div {
            padding: 13px 0 13px 20px;
          }
        }
      }
      .two:last-child {
        border-bottom: none;
      }
      .btns {
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
    }
  }
}
.header {
  border-radius: 16px 16px 0 0;
  background-color: #FAFAFA;
  color: #909399;
  .subpage {
    display: flex;
  }
  .title {
    width: 200px;
    padding: 8px 20px;
  }
}
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  img {
    width: 12px;
    height: 19px;
  }
  div {
    flex-shrink: 0;
    font-weight: bold;
    font-size: 18px;
    color: #222222;
    line-height: 27px;
    font-family: 'Source Han Sans CN Bold Bold';
  }
}
</style>
src/views/system/role/detail.vue
New file
@@ -0,0 +1,352 @@
<template>
  <Card>
    <div class="header-title">{{ form.roleName }}</div>
    <div class="header-remark">备注:{{ form.remark }}</div>
    <el-tabs v-model="activeName">
      <el-tab-pane label="操作权限" name="first">
        <div class="header">
          <div class="w20">模块名称</div>
          <div class="sconed">
            <div class="subpage">
              <div class="title">页面名称</div>
              <div class="btns">权限</div>
            </div>
          </div>
        </div>
        <div v-for="item in menu" :key="item.menuId">
          <div class="row">
            <div class="w20">
              <el-checkbox disabled :checked="item.selected">
                {{ item.menuName }}
              </el-checkbox>
            </div>
            <div class="sconed">
              <div class="subpage"
                v-if="(item.children.length > 0 && item.children[0].children.length > 0) || item.children[0].children.menuType != 'F'">
                <div v-for="item1 in item.children" :key="item1.menuId" class="two">
                  <div class="left">
                    <el-checkbox disabled :checked="item1.selected">
                      {{ item1.menuName }}
                    </el-checkbox>
                  </div>
                  <div class="right">
                    <div v-for="item2 in item1.children" :key="item2.menuId">
                      <el-checkbox disabled :checked="item2.selected">
                        {{ item2.menuName }}
                      </el-checkbox>
                    </div>
                  </div>
                </div>
              </div>
              <div class="subpage" v-else>
                <div class="two">
                  <!-- <div class="left">
                    </div> -->
                  <div class="right">
                    <div v-for="item1 in item.children" :key="item1.menuId">
                      <el-checkbox disabled :checked="item1.selected">
                        {{ item1.menuName }}
                      </el-checkbox>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div v-if="menu.length == 0" :style="{ height: height + 'px' }">
          <div class="no-data">暂无数据</div>
        </div>
      </el-tab-pane>
      <el-tab-pane label="人员列表" name="second">
        <el-form :inline="true" class="demo-form-inline">
          <div style="display: flex;justify-content: space-between;">
            <el-form-item label="人员搜索">
              <el-input v-model="nickNameOrPhone" placeholder="请输入姓名/联系电话"></el-input>
            </el-form-item>
            <el-form-item label="所属部门">
              <el-select v-model="deptId" placeholder="请选择(多选)" multiple>
                <el-option v-for="item in deptList" :key="item.id" :label="item.deptName" :value="item.id">
                </el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="登陆状态">
              <el-select v-model="status" placeholder="请选择">
                <el-option
                  v-for="item in [{ label: '全部', value: '' }, { label: '启用', value: 0 }, { label: '禁用', value: 1 }]"
                  :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-button @click="reset">重置</el-button>
              <el-button type="primary" @click="onSubmit">查询</el-button>
            </el-form-item>
          </div>
        </el-form>
        <Table :data="data" :total="pagination.total" :queryForm="pagination" @currentChange="handleCurrentChange"
          @sizeChange="handleSizeChange">
          <el-table-column label="序号" type="index" width="50">
          </el-table-column>
          <el-table-column prop="nickName" label="姓名"></el-table-column>
          <el-table-column prop="phonenumber" label="联系电话">
          </el-table-column>
          <el-table-column prop="deptList" label="所属部门">
            <template slot-scope="{ row }">
              <el-tag v-for="(item, index) in row.deptList" :key="index">{{ item }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="roleName" label="角色">
          </el-table-column>
          <el-table-column prop="userName" label="登陆账号">
          </el-table-column>
          <el-table-column prop="remark" label="备注">
          </el-table-column>
          <el-table-column prop="status" label="登录状态">
            <template slot-scope="{row}">
              <div class="status_class">
                <div :class="row.status == 0 ? 'green' : 'red'"></div>
                <div>{{ row.status == 0 ? '正常' : '停用' }}</div>
                <div v-if="row.status == 1" style="cursor: pointer;" @click="dialogVisibleView = true, rowView = row">
                  <i class="el-icon-warning"></i>
                </div>
              </div>
            </template>
          </el-table-column>
          <el-table-column prop="createTime" label="创建时间"></el-table-column>
        </Table>
      </el-tab-pane>
    </el-tabs>
    <ViewData v-if="dialogVisibleView" :row="rowView" :dialogVisible="dialogVisibleView"
      @close="dialogVisibleView = false, rowView = {}" />
  </Card>
</template>
<script>
import { getRoleInfo, roleInfoFromUserId, getUserList, getDeptList } from './service.js'
import ViewData from '../user/components/view-data'
export default {
  name: 'DetailRole',
  components: {
    ViewData
  },
  props: {},
  data() {
    return {
      form: {},//表单
      activeName: 'first',//当前选中的tab
      menu: [],//菜单
      data: [],//列表数据
      nickNameOrPhone: '',//人员搜索
      deptId: [],//部门
      deptList: [],//部门列表
      status: '',//状态
      pagination: {
        total: 0,//总条数
        pageNum: 1,//当前页
        pageSize: 10,//每页条数
      },
      rowView: {},//查看
      dialogVisibleView: false,//查看
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  watch: {},
  created() {
    roleInfoFromUserId({ userId: 1 }).then(res => {
      getRoleInfo({ roleId: this.$route.query.roleId }).then(resp => {
        this.menu = this.setSelectedIds(res.data.data, resp.data.data.menus);
        this.form = {
          roleName: resp.data.data.roleName,
          remark: resp.data.data.remark,
        }
      })
    })
    this.getListData()
    getDeptList().then((res) => {
      this.deptList = res.data.data
    })
  },
  mounted() { },
  methods: {
    async getListData() {
      let obj = {
        ...this.pagination,
        nickNameOrPhone: this.nickNameOrPhone,
        deptIds: this.deptId,
        status: this.status,
        roleIds: [this.$route.query.roleId]
      }
      this.listLoading = true
      const { data: { data: { records, total } } } = await getUserList(obj)
      this.data = records
      this.pagination.total = total
    },
    setSelectedIds(arr, selectKeyList) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      arr.forEach(traverse);
      return arr;
    },
    reset() {
      this.nickNameOrPhone = ''
      this.deptId = []
      this.status = ''
      this.pagination.pageNum = 1
      this.getListData()
    },
    onSubmit() {
      this.pagination.pageNum = 1
      this.getListData()
    },
    handleCurrentChange(e) {
      this.pagination.pageNum = e;
      this.getListData()
    },
    handleSizeChange(e) {
      this.pagination.pageSize = e
      this.getListData()
    },
  },
};
</script>
<style lang="less" scoped>
.header-title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 15px;
}
.header-remark {
  font-size: 16px;
  color: rgba(0, 0, 0, 0.65);
  margin-bottom: 30px;
}
.row,
.header {
  display: flex;
  align-items: center;
  border: 1px solid #e8e8e8;
  .w20 {
    width: 15%;
    padding: 8px 20px;
  }
  .sconed {
    flex: 1;
    .subpage {
      .title {
        border: 1px solid #e8e8e8;
        border-top: none;
        border-bottom: none;
      }
      .two {
        display: flex;
        align-items: center;
        border: 1px solid #e8e8e8;
        border-top: none;
        border-right: none;
        .left {
          width: 200px;
          padding: 13px 20px;
          border-right: 1px solid #e8e8e8;
        }
        .right {
          display: flex;
          flex: 1;
          div {
            padding: 13px 0 13px 20px;
          }
        }
      }
      .two:last-child {
        border-bottom: none;
      }
      .btns {
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
    }
  }
}
.header {
  border-radius: 16px 16px 0 0;
  background-color: #FAFAFA;
  color: #909399;
  font-weight: 500;
  .subpage {
    display: flex;
  }
  .title {
    width: 200px;
    padding: 8px 20px;
  }
}
.status_class {
  display: flex;
  align-items: center;
  div:nth-child(1) {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: 5px;
  }
  div:nth-child(2) {
    margin-right: 8px;
  }
}
.no-data {
  height: 100%;
  background-color: #fff;
  border-radius: 0 0 6px 6px;
  border-left: 1px solid #e8e8e8;
  border-right: 1px solid #e8e8e8;
  border-bottom: 1px solid #e8e8e8;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #909399;
  font-size: 14px;
}
/* 当复选框禁用时覆盖默认样式 */
::v-deep .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
  background-color: #409eff;
  border-color: #409eff;
}
::v-deep .el-checkbox.is-disabled .el-checkbox__inner::after {
  border-color: #fff;
}
</style>
src/views/system/role/edit.vue
New file
@@ -0,0 +1,398 @@
<template>
  <Card>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>角色信息</div>
    </div>
    <el-form ref="form" :inline="true" :model="form" :rules="rules" label-width="80px" class="demo-form-inline">
      <el-form-item label="角色名称" prop="roleName">
        <el-input v-model="form.roleName" placeholder="请输入角色名称"></el-input>
      </el-form-item>
      <el-form-item label="备注" prop="remark">
        <el-input v-model="form.remark" placeholder="请输入备注"></el-input>
      </el-form-item>
    </el-form>
    <div class="header-title-left">
      <img src="@/assets/public/headercard.png" />
      <div>操作权限</div>
    </div>
    <div>
      <div class="header">
        <div class="w20">模块名称</div>
        <div class="sconed">
          <div class="subpage">
            <div class="title">页面名称</div>
            <div class="btns">权限</div>
          </div>
        </div>
      </div>
      <div v-for="item in menu" :key="item.menuId">
        <div class="row">
          <div class="w20">
            <el-checkbox v-model="item.selected" @change="(e) => {
              setCheckStatus1(item.menuId, e)
            }" :checked="item.selected">
              {{ item.menuName }}
            </el-checkbox>
          </div>
          <div class="sconed">
            <div class="subpage"
              v-if="(item.children.length > 0 && item.children[0].children.length > 0) || item.children[0].children.menuType != 'F'">
              <div v-for="item1 in item.children" :key="item1.menuId" class="two">
                <div class="left">
                  <el-checkbox v-model="item1.selected" @change="(e) => {
                    setCheckStatus2(item1.menuId, e, item.menuId)
                  }" :checked="item1.selected">
                    {{ item1.menuName }}
                  </el-checkbox>
                </div>
                <div class="right">
                  <div v-for="item2 in item1.children" :key="item2.menuId">
                    <el-checkbox v-model="item2.selected" @change="(e) => {
                      setCheckStatus3(item2.menuId, e, item1.menuId, item.menuId)
                    }" :checked="item2.selected">
                      {{ item2.menuName }}
                    </el-checkbox>
                  </div>
                </div>
              </div>
            </div>
            <div class="subpage" v-else>
              <div class="two">
                <!-- <div class="left">
                  </div> -->
                <div class="right">
                  <div v-for="item1 in item.children" :key="item1.menuId">
                    <el-checkbox v-model="item1.selected" @change="(e) => {
                      setCheckStatus2(item1.menuId, e, item.menuId,)
                    }" :checked="item1.selected">
                      {{ item1.menuName }}
                    </el-checkbox>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div v-if="menu.length == 0" :style="{ height: height + 'px' }">
        <div class="no-data">暂无数据</div>
      </div>
    </div>
    <div class="btn_box">
      <el-button @click="$router.go(-1)">返回</el-button>
      <el-button type="primary" @click="onSubmit">保存</el-button>
    </div>
  </Card>
</template>
<script>
import { roleInfoFromUserId, getRoleInfo, add, edit } from './service.js'
export default {
  name: 'EditRole',
  components: {},
  props: {},
  data() {
    return {
      form: {
        roleName: "",
        remark: "",
      },
      rules: {
        roleName: [
          { required: true, message: "请输入角色名称", trigger: "blur" },
        ],
      },
      menu: [],
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  watch: {},
  created() {
    roleInfoFromUserId({ userId: 1 }).then(res => {
      if (this.$route.query.roleId) {
        getRoleInfo({ roleId: this.$route.query.roleId }).then(resp => {
          this.menu = this.setSelectedIds(res.data.data, resp.data.data.menus || []);
          this.form = {
            roleName: resp.data.data.roleName,
            remark: resp.data.data.remark,
            roleId: resp.data.data.roleId
          }
        })
      } else {
        this.menu = res.data.data
      }
    })
  },
  mounted() { },
  methods: {
    setSelectedIds(arr, selectKeyList) {
      function traverse(item) {
        item.selected = selectKeyList.includes(item.menuId);
        if (item.children && item.children.length > 0) {
          item.children.forEach(traverse);
        }
      }
      arr.forEach(traverse);
      return arr;
    },
    onSubmit() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          if (this.getSelectedIds(this.menu).length == 0) {
            this.msgwarning('请勾选操作权限')
            return
          }
          let obj = {
            ...this.form,
            menuIds: this.getSelectedIds(this.menu)
          }
          if (this.$route.query && this.$route.query.roleId) {
            obj.roleId = this.$route.query.roleId
            edit(obj).then(() => {
              this.msgsuccess('保存成功')
              this.$router.go(-1)
            })
          } else {
            add(obj).then(() => {
              this.msgsuccess('保存成功')
              this.$router.go(-1)
            })
          }
        }
      })
    },
    getSelectedIds(arr) {
      let result = [];
      function traverse(item) {
        if (item.selected) {
          result.push(item.menuId);
        }
        if (item.children && item.children.length > 0) {
          for (let children of item.children) {
            traverse(children);
          }
        }
      }
      for (let item of arr) {
        traverse(item);
      }
      return result;
    },
    setCheckStatus1(id, status) { //点击第1级
      if (!status) {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = status
            if (item.children.length > 0) {
              item.children = item.children.map(item1 => {
                item1.selected = status
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    item2.selected = status
                    return { ...item2 }
                  })
                }
                return { ...item1 }
              })
            }
          }
          return { ...item }
        })
      } else {
        this.menu = this.menu.map(item => {
          if (item.menuId == id) {
            item.selected = true
          }
          return { ...item }
        })
      }
    },
    setCheckStatus2(id, status, aId) { //点击第2级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == id) {
                item1.selected = status
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    },
    setCheckStatus3(id, status, bId, aId) {//点击第3级
      this.menu = this.menu.map(item => {
        if (item.menuId == aId) {
          item.selected = true
          if (item.children.length > 0) {
            item.children = item.children.map(item1 => {
              if (item1.menuId == bId) {
                item1.selected = true
                if (item1.children.length > 0) {
                  item1.children = item1.children.map(item2 => {
                    if (item2.menuId == id) {
                      item2.selected = status
                    }
                    return { ...item2 }
                  })
                }
              }
              return { ...item1 }
            })
          }
        }
        return { ...item }
      })
    }
  },
};
</script>
<style lang="less" scoped>
.title_box {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  font-weight: bold;
  div:first-child {
    width: 4px;
    height: 16px;
    background: #598DEC;
    margin-right: 8px;
  }
}
.no-data {
  height: 100%;
  background-color: #fff;
  border-radius: 0 0 6px 6px;
  border-left: 1px solid #e8e8e8;
  border-right: 1px solid #e8e8e8;
  border-bottom: 1px solid #e8e8e8;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #909399;
  font-size: 14px;
}
.btn_box {
  margin-top: 40px;
  text-align: center;
  button {
    width: 220px;
  }
}
.el-checkbox {
  display: flex;
  align-items: center;
}
.row,
.header {
  display: flex;
  align-items: center;
  border: 1px solid #e8e8e8;
  .w20 {
    width: 15%;
    padding: 8px 20px;
  }
  .sconed {
    flex: 1;
    .subpage {
      .title {
        border: 1px solid #e8e8e8;
        border-top: none;
        border-bottom: none;
      }
      .two {
        display: flex;
        align-items: center;
        border: 1px solid #e8e8e8;
        border-top: none;
        border-right: none;
        .left {
          width: 200px;
          padding: 13px 20px;
          border-right: 1px solid #e8e8e8;
        }
        .right {
          display: flex;
          flex: 1;
          div {
            padding: 13px 0 13px 20px;
          }
        }
      }
      .two:last-child {
        border-bottom: none;
      }
      .btns {
        display: flex;
        align-items: center;
        padding: 0 20px;
      }
    }
  }
}
.header {
  border-radius: 16px 16px 0 0;
  background-color: #FAFAFA;
  color: #909399;
  .subpage {
    display: flex;
  }
  .title {
    width: 200px;
    padding: 8px 20px;
  }
}
.header-title-left {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-bottom: 20px;
  img {
    width: 12px;
    height: 19px;
  }
  div {
    flex-shrink: 0;
    font-weight: bold;
    font-size: 18px;
    color: #222222;
    line-height: 27px;
    font-family: 'Source Han Sans CN Bold Bold';
  }
}
</style>
src/views/system/role/index.vue
New file
@@ -0,0 +1,160 @@
<template>
  <div class="list">
    <TableCustom :queryForm="pagination" :tableData="tableData" :total="pagination.total" @currentChange="handleCurrentChange"
      @sizeChange="handleSizeChange">
      <template #search>
        <el-form :model="form" inline>
          <el-form-item label="角色名称">
            <el-input v-model="roleName" placeholder="请输入角色名称"></el-input>
          </el-form-item>
          <el-form-item style="margin-left: 63px;">
            <el-button @click="reset">重置</el-button>
            <el-button type="primary" @click="onSubmit">查询</el-button>
          </el-form-item>
        </el-form>
      </template>
      <template #setting>
        <el-button icon="el-icon-plus" @click="add" type="primary">添加角色</el-button>
      </template>
      <template #table>
        <el-table-column type="index" width="55" label="序号"></el-table-column>
        <el-table-column prop="roleName" label="角色名称"></el-table-column>
        <el-table-column prop="roleNum" label="角色人数"></el-table-column>
        <el-table-column prop="remark" label="备注"></el-table-column>
        <el-table-column prop="createTime" label="创建时间"></el-table-column>
        <el-table-column label="操作" width="300">
          <template slot-scope="{row}">
            <div>
              <el-button type="text"
                @click="$router.push(`/system/detail-role?roleId=${row.roleId}`)">详情</el-button>
              <el-button v-if="row.roleId != 1" type="text"
                @click="$router.push(`/system/edit-role?roleId=${row.roleId}`)">编辑</el-button>
              <el-button v-if="row.roleId != 1" type="text" @click="del(row)">删除</el-button>
            </div>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
    <ShowDelConfirm :show="delShow" @close="delShow = false" @confirm="delConfirm" />
  </div>
</template>
<script>
import { getList, delRole } from './service'
export default {
  name: 'Role',
  data() {
    return {
      delShow: false,
      data: [],
      roleName: '',
      pagination: {
        total: 10,
        pageNum: 1,
        pageSize: 10,
      },
      delId: '',
    };
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  watch: {},
  created() {
    this.getListData()
  },
  mounted() { },
  methods: {
    add() {
      this.$router.push('/system/add-role')
    },
    delConfirm() {
      delRole(this.delId).then(() => {
        this.delShow = false
        this.disbRow = {}
        this.getListData()
        this.msgsuccess('删除成功')
      })
    },
    del(row) {
      this.delShow = true
      this.delId = row.roleId
    },
    async getListData() {
      let obj = {
        roleName: this.roleName,
        pageNum: this.pagination.pageNum,
        pageSize: this.pagination.pageSize
      }
      this.listLoading = true
      const { data: { data: { records, total } } } = await getList(obj)
      this.data = records
      this.pagination.total = total
    },
    reset() {
      this.roleName = ''
      this.pagination.pageNum = 1
      this.getListData()
    },
    onSubmit() {
      this.pagination.pageNum = 1
      this.getListData()
    },
    handleCurrentChange(e) {
      this.pagination.pageNum = e;
      this.getListData()
    },
    handleSizeChange(e) {
      this.pagination.pageSize = e
      this.getListData()
    },
  },
};
</script>
<style lang="less" scoped>
.list {
  height: 100%;
}
.green {
  background-color: green;
}
.red {
  background-color: red;
}
.demo-form-inline {
  display: flex;
  justify-content: space-between;
}
.add_btn {
  margin-bottom: 20px;
}
.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 20px;
}
.status_class {
  display: flex;
  align-items: center;
  div:nth-child(1) {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: 5px;
  }
  div:nth-child(2) {
    margin-right: 8px;
  }
}
</style>
src/views/system/role/service.js
New file
@@ -0,0 +1,41 @@
import axios from '@/utils/request';
// 列表
export const getList = (data) => {
  return axios.post('/system/role/list', { ...data })
}
// 添加
export const add = (data) => {
  return axios.post('/system/role/add', { ...data })
}
// 编辑
export const edit = (data) => {
  return axios.put('/system/role', { ...data })
}
// 删除
export const delRole = (id) => {
  return axios.delete(`/system/role/deleteById/${id}`)
}
// 获取菜单树
export const roleInfoFromUserId = (params) => {
  return axios.get(`/system/role/roleInfoFromUserId`, { params })
}
// 获取角色详情
export const getRoleInfo = (params) => {
  return axios.get(`/system/role/roleInfo`, { params })
}
// 列表
export const getUserList = (data) => {
  return axios.post('/system/user/list', { ...data })
}
export const getDeptList = (params) => {
  return axios.post('/t-dept/listAll')
}
src/views/system/user/components/add-edit.vue
New file
@@ -0,0 +1,104 @@
<template>
  <div>
    <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" :title="row.userId ? '编辑人员' : '添加人员'" width="30%">
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="姓名" prop="nickName">
          <el-input v-model="form.nickName" placeholder="请输入" style="width: 50%"></el-input>
        </el-form-item>
        <el-form-item label="联系电话" prop="phonenumber">
          <el-input v-model="form.phonenumber" placeholder="请输入" style="width: 50%"></el-input>
        </el-form-item>
        <el-form-item label="登陆账号" prop="userName">
          <div style="display: flex; align-items: center">
            <el-input v-model="form.userName" :disabled="form.userId" placeholder="请输入" style="width: 50%"></el-input>
            <div style="margin-left: 10px; color: red">登录账号不可重复</div>
          </div>
        </el-form-item>
        <el-form-item label="角色" prop="roleId">
          <el-select v-model="form.roleId" placeholder="请选择(单选)">
            <el-option v-for="item in roleList" :key="item.roleId" :label="item.roleName"
              :value="item.roleId"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="启动状态" prop="status">
          <el-switch v-model="form.status"></el-switch>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input type="textarea" v-model="form.remark" placeholder="请输入" style="width: 50%"></el-input>
        </el-form-item>
      </el-form>
      <div class="select-member-footer">
        <el-button @click="$emit('close')">关闭</el-button>
        <el-button type="primary" @click="submit">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  props: {
    dialogVisible: {
      type: Boolean,
      default: false,
    },
    row: {
      type: Object,
      default: () => { },
    },
    roleList: {
      type: Array,
      default: () => [],
    },
    deptType: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      form: { status: true },
      userDeptId: '',
      rules: {
        nickName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
        phonenumber: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
        roleId: [{ required: true, message: '请选择角色', trigger: 'blur' }],
        userName: [{ required: true, message: '请输入登陆账号', trigger: 'blur' }],
        status: [{ required: true, message: '请选择启动状态', trigger: 'blur' }],
      },
    }
  },
  created() {
    this.form = { status: true }
    if (Object.keys(this.row).length) {
      this.form = {
        userId: this.row.userId,
        nickName: this.row.nickName,
        phonenumber: this.row.phonenumber,
        roleId: this.row.roleId,
        userName: this.row.userName,
        remark: this.row.remark,
        status: this.row.status == 0 ? true : false,
      }
    } else {
      this.form = {}
    }
  },
  mounted() { },
  methods: {
    submit() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          let obj = {
            ...this.form,
            status: this.form.status ? 0 : 1,
          }
          this.$emit('confirm', obj)
        }
      })
    },
  },
}
</script>
<style scoped></style>
src/views/system/user/components/disb.vue
New file
@@ -0,0 +1,68 @@
<template>
  <div>
    <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" title="禁用人员" width="30%">
      <el-form ref="form" :model="form" label-width="80px">
        <el-form-item label="姓名" prop="nickName">
          {{ form.nickName }}
        </el-form-item>
        <el-form-item label="登陆账号" prop="userName">
          {{ form.userName }}
        </el-form-item>
        <el-form-item label="禁用备注" prop="disableRemark">
          <el-input type="textarea" v-model="form.disableRemark" placeholder="请输入备注"></el-input>
        </el-form-item>
      </el-form>
      <div class="select-member-footer">
        <el-button @click="$emit('close')">关闭</el-button>
        <el-button type="primary" @click="submit">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  props: {
    dialogVisible: {
      type: Boolean,
      default: false
    },
    row: {
      type: Object,
      default: () => { }
    }
  },
  data() {
    return {
      form: {},
    };
  },
  created() {
    if (Object.keys(this.row).length) {
      this.form = {
        ...this.row,
        userId: this.row.userId,
        nickName: this.row.nickName,
        userName: this.row.userName,
        disableRemark: ''
      }
    }
  },
  mounted() { },
  methods: {
    submit() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          this.form.deptId = this.row.deptId
          let obj = {
            remark: this.form.disableRemark,
            userId: this.form.userId
          }
          this.$emit('confirm', obj)
        }
      })
    }
  },
};
</script>
<style scoped></style>
src/views/system/user/components/inherit.vue
New file
@@ -0,0 +1,97 @@
<template>
    <div>
        <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" title="账号继承" :show-close="false">
            <div class="inherit-title">当前账号</div>
            <div class="inherit-content">
                <div>姓名:张三</div>
                <div>电话:102929292929</div>
                <div>角色:工艺工程师</div>
                <div>登录账号:328746378</div>
            </div>
            <div class="inherit-table-title">
                <div class="inherit-title">继承账号</div>
                <el-button type="primary" @click="selectMember">选择人员</el-button>
            </div>
            <Table :data="tableData" :total="0">
                <el-table-column label="姓名" prop="role" />
                <el-table-column label="联系电话" prop="name" />
                <el-table-column label="角色" prop="createTime" />
                <el-table-column label="登陆账号" prop="createTime" />
                <el-table-column label="登录状态" prop="createTime" />
                <el-table-column label="创建时间" prop="createTime" />
            </Table>
            <div class="select-member-footer">
                <el-button @click="$emit('close')">关闭</el-button>
                <el-button type="primary" @click="submit">保存</el-button>
            </div>
        </el-dialog>
        <SelectMember ref="selectMember" />
    </div>
</template>
<script>
export default {
    props: {
        dialogVisible: {
            type: Boolean,
            default: false
        },
        row: {
            type: Object,
            default: () => { }
        }
    },
    data() {
        return {
        };
    },
    created() {
        if (Object.keys(this.row).length) {
            this.form = {
                ...this.row
            }
        }
    },
    mounted() { },
    methods: {
        selectMember() {
            this.$refs.selectMember.open()
        },
        submit() {
        }
    },
};
</script>
<style scoped lang="less">
.inherit-title {
    font-weight: 500;
    font-size: 16px;
    line-height: 16px;
    color: #222222;
    font-family: 'SourceHanSansCN-Medium';
    margin-bottom: 8px;
}
.inherit-content {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    gap: 10px;
    border: 1px solid #E6E6E6;
    border-radius: 6px;
    padding: 10px;
}
.inherit-table-title {
    display: flex;
    align-items: center;
    margin: 20px 0;
    gap: 10px;
    .inherit-title {
        margin-bottom: 0;
    }
}
.select-member-footer{
    margin-top: 40px;
}
</style>
src/views/system/user/components/reset-password.vue
New file
@@ -0,0 +1,78 @@
<template>
  <div>
    <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" title="重置密码" width="30%">
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="姓名" prop="nickName">
          <el-input :disabled="true" v-model="form.nickName" placeholder="请输入" style="width: 50%;"></el-input>
        </el-form-item>
        <el-form-item label="登陆账号" prop="account">
          <el-input :disabled="true" v-model="form.account" placeholder="请输入" style="width: 50%;"></el-input>
        </el-form-item>
        <el-form-item label="新密码" prop="password">
          <el-input v-model="form.password" placeholder="请输入" style="width: 50%;"></el-input>
        </el-form-item>
        <el-form-item label="确认密码" prop="confirmPassword">
          <el-input v-model="form.confirmPassword" placeholder="请输入" style="width: 50%;"></el-input>
        </el-form-item>
      </el-form>
      <div class="select-member-footer">
        <el-button @click="$emit('close')">关闭</el-button>
        <el-button type="primary" @click="submit">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  props: {
    dialogVisible: {
      type: Boolean,
      default: false
    },
    row: {
      type: Object,
      default: () => { }
    }
  },
  data() {
    return {
      form: {},
      rules: {
        password: [{ required: true, message: '请新密码', trigger: 'blur' }],
        confirmPassword: [{ required: true, message: '请确认密码', trigger: 'blur' }, {
          validator: (rule, value, callback) => {
            if (value !== this.form.password) {
              callback(new Error('两次输入密码不一致!'));
            } else {
              callback();
            }
          }
        }],
      }
    };
  },
  created() {
    this.form = { status: true }
    if (Object.keys(this.row).length) {
      this.form = {
        userId: this.row.userId,
        nickName: this.row.nickName,
        account: this.row.userName,
      }
    }
  },
  mounted() { },
  methods: {
    submit() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          this.$emit('confirm', this.form)
        }
      })
    }
  },
};
</script>
<style scoped></style>
src/views/system/user/components/view-data.vue
New file
@@ -0,0 +1,48 @@
<template>
  <div>
    <el-dialog :visible.sync="dialogVisible" @close="$emit('close')" title="禁用信息" width="30%">
      <el-form ref="form" label-width="80px">
        <el-form-item label="禁用原因" prop="disableRemark">
          {{ form.disableRemark }}
        </el-form-item>
        <el-form-item label="操作时间" prop="operatingTime">
          {{ form.operatingTime }}
        </el-form-item>
        <el-form-item label="操作人" prop="operatingPerson">
          {{ form.operatingPerson }}
        </el-form-item>
      </el-form>
      <div class="select-member-footer">
        <el-button @click="$emit('close')">关闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
export default {
  props: {
    dialogVisible: {
      type: Boolean,
      default: false,
    },
    row: {
      type: Object,
      default: () => { },
    },
  },
  data() {
    return {
      form: {},
    }
  },
  created() {
    if (Object.keys(this.row).length) {
      this.form = { ...this.row }
    }
  },
  mounted() { },
  methods: {},
}
</script>
<style scoped></style>
src/views/system/user/index.vue
New file
@@ -0,0 +1,312 @@
<template>
  <div class="list">
    <TableCustom :queryForm="pagination" :tableData="tableData" :total="pagination.total"
      @currentChange="handleCurrentChange" @sizeChange="handleSizeChange">
      <template #search>
        <el-form label-width="100px" inline>
          <el-form-item label="人员搜索">
            <el-input v-model="nickNameOrPhone" placeholder="请输入姓名/联系电话"></el-input>
          </el-form-item>
          <el-form-item label="人员角色">
            <el-select v-model="roleId" placeholder="请选择(多选)" multiple>
              <el-option v-for="item in roleList" :key="item.roleId" :label="item.roleName"
                :value="item.roleId"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="登陆状态">
            <el-select v-model="status" placeholder="请选择(多选)">
              <el-option v-for="item in [
                { label: '全部', value: '' },
                { label: '启用', value: 0 },
                { label: '禁用', value: 1 },
              ]" :key="item.value" :label="item.label" :value="item.value"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item style="margin-left: 63px;">
            <el-button @click="reset">重置</el-button>
            <el-button type="primary" @click="onSubmit">查询</el-button>
          </el-form-item>
        </el-form>
      </template>
      <template #setting>
        <el-button icon="el-icon-plus" @click="dialogVisible = true" type="primary">
          添加人员
        </el-button>
      </template>
      <template #table>
        <el-table-column label="序号" type="index" width="50"></el-table-column>
        <el-table-column prop="nickName" label="姓名"></el-table-column>
        <el-table-column prop="phonenumber" label="联系电话"></el-table-column>
        <el-table-column prop="roleName" label="角色"></el-table-column>
        <el-table-column prop="userName" label="登陆账号"></el-table-column>
        <el-table-column prop="remark" label="备注"></el-table-column>
        <el-table-column prop="status" label="登录状态">
          <template #default="{ row }">
            <div class="status_class">
              <div :class="row.status == 0 ? 'green' : 'red'"></div>
              <div>{{ row.status == 0 ? '正常' : '禁用' }}</div>
              <div v-if="row.status == 1" style="cursor: pointer"
                @click="; (dialogVisibleView = true), (rowView = row), $forceUpdate()">
                <i class="el-icon-warning"></i>
              </div>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间"></el-table-column>
        <el-table-column label="操作" width="300">
          <template slot-scope="{ row }">
            <div>
              <el-button type="text" @click="edit(row)">编辑</el-button>
              <el-button type="text" @click="edit(row)">账号继承</el-button>
              <el-button v-if="row.status != 0" type="text" @click="updateStatus(row, true)">
                启用
              </el-button>
              <el-button v-if="row.status == 0" type="text" @click="updateStatus(row, false)">
                禁用
              </el-button>
              <el-button type="text" @click="detail(row)">重置密码</el-button>
              <el-button type="text" @click="del(row)">删除</el-button>
            </div>
          </template>
        </el-table-column>
      </template>
    </TableCustom>
    <AddEdit v-if="dialogVisible" :row="row" :deptList="deptList" :deptType="deptTypeList" :roleList="roleList"
      :dialogVisible="dialogVisible" @close="dialogVisible = false, row = {}" @confirm="confirm" />
    <ResetPassword v-if="passwordVisible" :row="row" :dialogVisible="passwordVisible"
      @close="passwordVisible = false, row = {}" @confirm="passwordConfirm" />
    <ViewData v-if="dialogVisibleView" :row="rowView" :dialogVisible="dialogVisibleView"
      @close="dialogVisibleView = false, rowView = {}" />
    <Disb v-if="disbDialogVisible" :row="disbRow" :dialogVisible="disbDialogVisible"
      @close="disbDialogVisible = false, disbRow = {}" @confirm="disbConfirm" />
    <ShowDelConfirm :show="delShow" @close="delShow = false" @confirm="delConfirm" title="确认要删除该人员吗?"
      tip="删除人员后,该人员不可登录。如非必要,系统建议您使用禁用功能。" okText="确定删除" />
    <Inherit v-if="inheritDialogVisible" :row="inheritRow" :dialogVisible="inheritDialogVisible"
      @close="inheritDialogVisible = false, inheritRow = {}" @confirm="inheritConfirm" />
  </div>
</template>
<script>
import { getList, add, edit, delDept, roleList, updatePwd, changeStatus, typeList } from './service'
import AddEdit from './components/add-edit.vue'
import ViewData from './components/view-data.vue'
import Disb from './components/disb.vue'
import ResetPassword from './components/reset-password.vue'
import Inherit from './components/inherit.vue'
export default {
  name: 'User',
  components: {
    AddEdit,
    ViewData,
    Disb,
    ResetPassword,
    Inherit,
  },
  data() {
    return {
      dialogVisible: false,//新增
      passwordVisible: false,//重置密码
      dialogVisibleView: false,//查看
      disbDialogVisible: false,//禁用
      delShow: false,//删除
      inheritDialogVisible: false,//账号继承
      data: [],//列表数据
      nickNameOrPhone: '',//人员搜索
      deptId: [],//部门
      roleId: [],//角色
      status: '',//状态
      roleList: [],//角色列表
      deptTypeList: [],//类型列表
      pagination: {
        total: 10,//总条数
        pageNum: 1,//当前页
        pageSize: 10,//每页条数
      },
      row: {},//新增
      rowView: {},//查看
      disbRow: {},//禁用
      delId: '',//删除
      inheritRow: {},//账号继承
    }
  },
  computed: {
    height() {
      return this.$baseTableHeight()
    },
  },
  watch: {},
  created() {
    if (this.$route.query.deptId) {
      this.deptId = [this.$route.query.deptId]
    }
    this.getRoleList()
    this.getListData()
    this.getTypeList()
  },
  mounted() { },
  methods: {
    getTypeList() {
      typeList().then((res) => {
        this.deptTypeList = res.data.data
      })
    },
    getRoleList() {
      roleList().then((res) => {
        this.roleList = res.data.data
      })
    },
    delConfirm() {
      delDept(this.delId).then(() => {
        this.delShow = false
        this.msgsuccess('删除成功')
        this.getListData()
      })
    },
    disbConfirm(form) {
      changeStatus({ ...form, status: 1 }).then(() => {
        this.disbDialogVisible = false
        this.disbRow = {}
        this.msgsuccess('禁用成功')
        this.getListData()
      })
    },
    confirm(form) {
      if (form.userId) {
        edit(form).then(() => {
          this.row = {}
          this.dialogVisible = false
          this.msgsuccess('修改成功')
          this.getListData()
        })
      } else {
        add(form).then(() => {
          this.row = {}
          this.dialogVisible = false
          this.msgsuccess('添加成功')
          this.getListData()
        })
      }
    },
    passwordConfirm(form) {
      updatePwd(form).then(() => {
        this.row = {}
        this.passwordVisible = false
        this.msgsuccess('修改密码成功')
        this.getListData()
      })
    },
    detail(row) {
      this.passwordVisible = true
      this.row = row
    },
    edit(row) {
      this.row = row
      this.dialogVisible = true
    },
    updateStatus(row, type) {
      if (type) {
        changeStatus({ ...row, status: 0 }).then(() => {
          this.msgsuccess('启用成功')
          this.getListData()
        })
      } else {
        this.disbRow = row
        this.disbDialogVisible = true
      }
    },
    inheritConfirm() {
    },
    del(row) {
      this.delShow = true
      this.delId = row.userId
    },
    async getListData() {
      let obj = {
        ...this.pagination,
        nickNameOrPhone: this.nickNameOrPhone,
        deptIds: this.deptId,
        roleIds: this.roleId,
        status: this.status,
      }
      this.listLoading = true
      const {
        data: {
          data: { records, total },
        },
      } = await getList(obj)
      this.data = records
      this.pagination.total = total
      this.timeOutID = setTimeout(() => {
        this.listLoading = false
      }, 500)
    },
    reset() {
      this.nickNameOrPhone = ''
      this.roleId = []
      this.deptId = []
      this.status = ''
      this.pagination.pageNum = 1
      this.getListData()
    },
    onSubmit() {
      this.pagination.pageNum = 1
      this.getListData()
    },
    handleCurrentChange(e) {
      this.pagination.pageNum = e
      this.getListData()
    },
    handleSizeChange(e) {
      this.pagination.pageSize = e
      this.getListData()
    },
  },
}
</script>
<style scoped lang="less">
.list {
  height: 100%;
}
.green {
  background-color: green;
}
.red {
  background-color: red;
}
.demo-form-inline {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.add_btn {
  margin-bottom: 20px;
}
.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 20px;
}
.status_class {
  display: flex;
  align-items: center;
  div:nth-child(1) {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: 5px;
  }
  div:nth-child(2) {
    margin-right: 8px;
  }
}
</style>
src/views/system/user/service.js
New file
@@ -0,0 +1,41 @@
import axios from '@/utils/request';
// 列表
export const getList = (data) => {
  return axios.post('/system/user/list', { ...data })
}
// 添加
export const add = (data) => {
  return axios.post('/system/user/add', { ...data })
}
// 编辑
export const edit = (data) => {
  return axios.post('/system/user/edit', { ...data })
}
// 删除
export const delDept = (id) => {
  return axios.delete(`/system/user/deleteById/${id}`)
}
// 部门
export const deptList = (data) => {
  return axios.post(`/t-dept/listAll`)
}
export const roleList = () => {
  return axios.post(`/system/role/listNotPage`)
}
export const changeStatus = (data) => {
  return axios.put(`/system/user/changeStatus`, { ...data })
}
export const updatePwd = (data) => {
  return axios.post(`/system/user/resetPwd`, { ...data })
}
export const typeList = (data) => {
  return axios.get(`/t-business-dept/list/type?type=1`,)
}