Newer
Older
smartwell_front / src / views / system / user / listUser.vue
liyaguang 6 days ago 18 KB 云台安装位置
<script lang="ts" setup name="SystemDictListDict">
import { ElMessage, ElMessageBox } from 'element-plus'
import { Delete, EditPen, Key, MagicStick, Plus } from '@element-plus/icons-vue'
import type { Ref } from 'vue'
import type {
  IlistQueryType,
  TreeStructure,
  changePageType,
  userType
} from './user-interface'
import AddUser from './userAdd.vue'
import RoleAssignment from './roleAssignment.vue'
import { getDeptTreeList } from '@/api/system/dept'
import {
  delUser,
  freezeUserApi,
  getUserList,
  resetPwdApi,
  unfreezeUser,
  updateUser
} from '@/api/system/user'
import { toTreeList } from '@/utils/structure'
import { RSAencrypt } from '@/utils/security'
import type { TableColumn } from '@/components/NormalTable/table_interface'
import {
  encryptNoCodedString,
  encrypt,
  decrypt,
  isEncrypt
} from '@/utils/security1'
import { result } from 'lodash-es'
// 逻辑代码
interface Tree {
  name: string
  children?: Tree[]
  id: string
}
const select = ref()
const { proxy } = getCurrentInstance() as any
const tagNames = { 已冻结: 'danger', 启用: 'success' }
const btnNames = { 已冻结: '解冻', 启用: '冻结' }
const btnStatus = { 已冻结: '', 启用: 'primary' }
const columns: Ref<TableColumn[]> = ref([])
columns.value = [
  { text: '账号', value: 'account', align: 'center' },
  { text: '所在组织机构', value: 'deptName', align: 'center' },
  { text: '真实姓名', value: 'name', align: 'center' },
  { text: '角色', value: 'roleName', align: 'center' },
  { text: '手机号', value: 'phone', align: 'center' },
  { text: '注册时间', value: 'createtime', align: 'center' }
]
const list = ref([])
const total = ref(0)
const loadingTable = ref<boolean>(false)
const loadingTree = ref<boolean>(false)
const resetPwdFlag = ref<string>('111111')
const defaultProps = {
  children: 'children',
  label: 'name'
}
// 查询参数
const listQuery: IlistQueryType = reactive({
  keywords: '',
  beginTime: '',
  endTime: '',
  deptId: '',
  offset: 1,
  limit: 20,
  sort: 'id',
  deptType: ''
})
const treeData = ref<TreeStructure[]>([])
// 获取列表数据
const fetchData = (flag: string) => {
  loadingTable.value = true
  select.value = ''
  var reg = new RegExp('[\\u4E00-\\u9FFF]+', 'g')
  getUserList({
    ...listQuery,
    keywords: reg.test(listQuery.keywords)
      ? encryptNoCodedString(listQuery.keywords)
      : listQuery.keywords
  })
    .then(res => {
      if (reg.test(listQuery.keywords) && !res.data.rows.length) {
        // 从新获取
        getUserList({
          ...listQuery,
          keywords: listQuery.keywords
        }).then(res => {
          list.value = JSON.parse(JSON.stringify(res.data.rows || [])).map(
            (item: any) => ({
              ...item,
              name: isEncrypt(item.name) ? decrypt(item.name) : item.name,
              phone: isEncrypt(item.phone) ? decrypt(item.phone) : item.phone,
              email: isEncrypt(item.email) ? decrypt(item.email) : item.email,
              isEncrypt:
                isEncrypt(item.name) &&
                isEncrypt(item.email) &&
                isEncrypt(item.phone)
            })
          )
          total.value = res.data.total
          loadingTable.value = false
          calcTableHeight()
        })
        return
      }
      list.value = JSON.parse(JSON.stringify(res.data.rows || [])).map(
        (item: any) => ({
          ...item,
          name: isEncrypt(item.name) ? decrypt(item.name) : item.name,
          phone: isEncrypt(item.phone) ? decrypt(item.phone) : item.phone,
          email: isEncrypt(item.email) ? decrypt(item.email) : item.email,
          isEncrypt:
            isEncrypt(item.name) &&
            isEncrypt(item.email) &&
            isEncrypt(item.phone)
        })
      )
      total.value = res.data.total
      loadingTable.value = false
      calcTableHeight()

      // if (JSON.parse(JSON.stringify(res.data.rows || [])).some((item: any) => !isEncrypt(item.name))) {
      //   // console.log('需要加密')
      //   list.value.forEach((item: any) => {
      //     // if (!isEncrypt(item.name) && !isEncrypt(item.phone) && !isEncrypt(item.email)) {
      //     updateUser({
      //       ...item,
      //       name: encrypt(item.name),
      //       phone: encrypt(item.phone),
      //       email: encrypt(item.email)
      //     }).then(res => {
      //       // console.log('更新成功')
      //     })
      //     // }
      //   })
      // }
      // else {
      //   // console.log('不需要加密')
      // }
    })
    .catch(() => {
      loadingTable.value = false
    })
}
// 点击树形结构
const handleNodeClick = (data: Tree) => {
  listQuery.deptId = data.id as string
  fetchData('click')
}

// 获取组织列表树数据
const fetchTreeData = () => {
  loadingTree.value = true
  getDeptTreeList(listQuery)
    .then(res => {
      treeData.value = toTreeList<TreeStructure>(res.data, '0', true)
      loadingTree.value = false
    })
    .catch(() => {
      loadingTree.value = false
    })
}
// 查询数据
const search = () => {
  fetchData('search')
}
const addUserRef = ref()
// 新增用户
const add = () => {
  addUserRef.value.initDialog('create')
}
// 编辑用户
const edit = () => {
  if (select.value) {
    const user: userType = list.value[select.value - 1]
    addUserRef.value.initDialog('update', user)
  } else {
    ElMessage.error('必须选中用户')
  }
}
// 重置密码
const resetPwd = () => {
  if (select.value) {
    const user: userType = list.value[select.value - 1]
    ElMessageBox.confirm(
      `确定要重置${user.name}的密码为${resetPwdFlag.value}吗?`,
      '确认重置密码',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
      .then(async () => {
        const params = {
          id: String(user.id),
          newPwd: await RSAencrypt(resetPwdFlag.value)
        }
        resetPwdApi(params).then(() => {
          ElMessage.success('操作成功')
        })
      })
      .catch(() => {})
  } else {
    ElMessage.error('必须选中用户')
  }
}
// 批量加密
// const index = ref(0)
// const batchEncrypt = () => {
//   if (total.value === 0) {
//     ElMessage.warning('暂无用户')
//     return
//   }
//   for (let i = index.value * 20; i < ((index.value + 1) * 20); i++) {
//     getUserList({ offset: i, limit: 1 }).then(res => {
//       if (res.data.rows.length) {
//         const data = res.data.rows[0]
//         if (!isEncrypt(data.name) && !isEncrypt(data.phone) && !isEncrypt(data.email)) {
//           updateUser({
//             ...data,
//             name: encrypt(data.name),
//             phone: encrypt(data.phone),
//             email: encrypt(data.email)
//           }).then(res => {
//             console.log('更新成功')
//           })
//         }
//       }
//     })
//   }
//   index.value += 1

// }
// 删除用户
const deleteUser = () => {
  if (select.value) {
    const user: userType = list.value[select.value - 1]
    ElMessageBox.confirm(`确定要删除${user.name}吗?`, 'Warning', {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning'
    })
      .then(() => {
        delUser({ id: user.id }).then(() => {
          ElMessage.success('操作成功')
          fetchData('')
        })
      })
      .catch(() => {})
  } else {
    ElMessage.error('必须选中用户')
  }
}
// 冻结/解冻用户
const frozenUser = (row: userType) => {
  ElMessageBox.confirm(
    `确定要${(btnNames as { [key: string]: string })[row.statusName]}${
      row.name
    }吗?`,
    'Warning',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning'
    }
  )
    .then(() => {
      if ((btnNames as any)[row.statusName] === '冻结') {
        // 发送请求
        freezeUserApi({ id: row.id }).then(res => {
          ElMessage.success('操作成功')
          fetchData('')
        })
      } else {
        unfreezeUser({ id: row.id }).then(res => {
          ElMessage.success('操作成功')
          fetchData('')
        })
      }
    })
    .catch(() => {})
}
const roleAssignmentRef = ref() // 角色分配弹窗
// 角色分配
const assignmentRole = () => {
  if (select.value) {
    const user: userType = list.value[select.value - 1]
    roleAssignmentRef.value.initDialog(user)
  } else {
    ElMessage.error('必须选中用户')
  }
}
// 页数发生变化后的操作,可能是页码变化,可能是每页容量变化,此函数必写
const changePage = (val: changePageType) => {
  if (val && val.size) {
    listQuery.limit = val.size
  }
  if (val && val.page) {
    listQuery.offset = val.page
  }
  fetchData('')
}
// 重置页面
const resetData = () => {
  fetchData('')
}
// 挂载
onMounted(() => {
  // 获取树形结构数据
  fetchTreeData()
  // 获取列表数据
  fetchData('click')
  window.addEventListener('resize', calcTableHeight)
  calcTableHeight()
})

const tableHeight = ref(400)
// const
const calcTableHeight = () => {
  // 顶部高度
  const topHeight = 50 + 60
  // app-container的 padding距离
  const appPadding = 20
  // 查询组件的高度
  const searchDiv = document.getElementById('search-div-id')
  const searchHeight = searchDiv ? searchDiv.clientHeight : 0
  // 表格顶部的文字提示高度
  const tableTopHeight = 32 + 10
  // 表格表头
  const tableHeaderHeight = 40
  // 分页器的高度
  const tablePaginationHeight = 40
  // 判断数据长度
  const height =
    window.innerHeight -
    topHeight -
    appPadding -
    searchHeight -
    tableTopHeight -
    tableHeaderHeight -
    tablePaginationHeight
  if (list.value.length * 50 >= height) {
    tableHeight.value = height
  } else {
    tableHeight.value = list.value.length ? (list.value.length + 1) * 50 : 100
  }
}
onBeforeUnmount(() => {
  window.removeEventListener('resize', calcTableHeight)
})

// 批量加密用户
const tableRef = ref()
const multiSelectData = ref([])
const clearSelect = () => {
  multiSelectData.value = []
  tableRef.value?.clearMulti()
}
const multiSelect = (data: any) => {
  multiSelectData.value = data
}
const loadingBtn = ref(false)
const encryptUser = () => {
  if (!multiSelectData.value.length) {
    ElMessage.warning('请选择要加密的用户')
    return
  }
  loadingBtn.value = true
  async function updateArrayItems(arr) {
    const results = await Promise.allSettled(
      arr.map((item, index) =>
        updateUser({
          ...item,
          name: isEncrypt(item.name) ? item.name : encrypt(item.name),
          phone: isEncrypt(item.phone) ? item.phone : encrypt(item.phone),
          email: isEncrypt(item.email) ? item.email : encrypt(item.email),
          isEncrypt: undefined
        })
          .then(() => ({ index, success: true }))
          .catch(error => ({ index, success: false, error }))
      )
    )

    const failedRequests = results
      .filter(r => r.status === 'rejected' || !r.value.success)
      .map(r => ({
        index: r.value?.index ?? results.indexOf(r),
        error: r.reason || r.value.error
      }))

    if (failedRequests.length > 0) {
      console.error('以下请求失败:')
      failedRequests.forEach(req => {
        const item = arr[req.index]
        console.error(
          `- 数据 ${item.id} (索引 ${req.index}) 失败:${req.error.message}`
        )
      })
      return { success: false, failedRequests }
    } else {
      console.log('所有请求成功!')
      return { success: true }
    }
  }
  updateArrayItems(multiSelectData.value)
    .then(result => {
      loadingBtn.value = false
      if (result.success) {
        // 显示成功提示
        ElMessage.success('加密成功')
        clearSelect()
        fetchData('')
      } else {
        console.log(result.failedRequests, '加密失败')
        const fail = (result.failedRequests || []).map(
          item => multiSelectData.value[item.index]?.name || ''
        )
        // 显示失败详情
        ElMessage.warning(
          `${(result.failedRequests || []).length}个加密失败,分别是:${fail.join(
            ','
          )}`
        )
        clearSelect()
        fetchData('')
      }
    })
    .catch(() => {
      loadingBtn.value = false
    })
}
</script>

<template>
  <!-- 布局 -->
  <app-container>
    <div class="container">
      <!-- 左侧组织机构 -->
      <div class="left-container">
        <div class="dept-div">
          <el-card class="box-card" shadow="always">
            <template #header>
              <div class="clearfix">
                <span>组织机构</span>
              </div>
            </template>
            <el-scrollbar height="100%" class="user-dept-scroll">
              <el-tree
                v-loading="loadingTree"
                :data="treeData"
                :props="defaultProps"
                default-expand-all
                :expand-on-click-node="false"
                @node-click="handleNodeClick"
              />
            </el-scrollbar>
          </el-card>
        </div>
      </div>
      <!-- 右侧表格 -->
      <div ref="tableContainer" class="table">
        <!-- 筛选条件 -->
        <search-area @search="search">
          <search-item>
            <el-input
              v-model.trim="listQuery.keywords"
              placeholder="账户/真实姓名/手机号"
              clearable
              style="width: 162px"
            />
          </search-item>
          <search-item>
            <el-date-picker
              v-model="listQuery.beginTime"
              type="datetime"
              format="YYYY-MM-DD HH:mm:ss"
              value-format="YYYY-MM-DD HH:mm:ss"
              placeholder="选择开始时间"
            />
          </search-item>
          <search-item>
            <el-date-picker
              v-model="listQuery.endTime"
              type="datetime"
              format="YYYY-MM-DD HH:mm:ss"
              value-format="YYYY-MM-DD HH:mm:ss"
              placeholder="选择结束时间"
            />
          </search-item>
        </search-area>
        <!-- 表头标题 -->
        <table-container :title-show="false">
          <template #btns-right>
            <!-- 操作 -->
            <div>
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/add')"
                type="primary"
                :icon="Plus"
                @click="add"
              >
                新增
              </el-button>
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/update')"
                type="primary"
                :icon="EditPen"
                @click="edit"
              >
                修改
              </el-button>
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/delete')"
                type="primary"
                :icon="Delete"
                @click="deleteUser"
              >
                删除
              </el-button>
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/reset')"
                type="primary"
                :icon="Key"
                @click="resetPwd"
              >
                重置密码
              </el-button>
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/roleAssign')"
                type="primary"
                :icon="MagicStick"
                @click="assignmentRole"
              >
                角色分配
              </el-button>
              <!-- v-if="proxy.hasPerm('/sys/mgr/encrypt')" -->
              <el-button
                v-if="proxy.hasPerm('/sys/mgr/encrypt')"
                type="primary"
                :icon="Key"
                @click="encryptUser"
                :disabled="loadingBtn"
              >
                加密
              </el-button>
            </div>
          </template>
          <!-- 查询结果Table显示 -->
          <normal-table
            ref="tableRef"
            :data="list"
            :total="total"
            :columns="columns"
            :query="listQuery"
            :list-loading="loadingTable"
            :height="tableHeight"
            @change="changePage"
            :is-showmulti-select="proxy.hasPerm('/sys/mgr/encrypt')"
            @multi-select="multiSelect"
          >
            <template #preColumns>
              <el-table-column label="选择" width="55" align="center">
                <template #default="scope">
                  <el-radio
                    v-model="select"
                    :label="scope.$index + 1"
                    class="radio"
                  />
                </template>
              </el-table-column>
              <el-table-column label="#" width="80" align="center">
                <template #default="scope">
                  {{
                    (listQuery.offset - 1) * listQuery.limit + scope.$index + 1
                  }}
                </template>
              </el-table-column>
            </template>
            <template #columns>
              <el-table-column
                v-if="proxy.hasPerm('/sys/mgr/encrypt')"
                label="是否加密"
                align="center"
                width="85"
              >
                <template #default="scope">
                  {{ scope.row.isEncrypt ? '是' : '否' }}
                </template>
              </el-table-column>
              <el-table-column label="状态" align="center" width="80">
                <template #default="scope">
                  <el-tag :type="(tagNames as any)[scope.row.statusName]">
                    {{ scope.row.statusName }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" width="60">
                <template #default="scope">
                  <el-button
                    :type="(btnStatus as any)[scope.row.statusName]"
                    link
                    size="small"
                    @click="frozenUser(scope.row)"
                  >
                    {{ (btnNames as any)[scope.row.statusName] }}
                  </el-button>
                </template>
              </el-table-column>
            </template>
          </normal-table>
        </table-container>
      </div>
    </div>
    <!-- 角色分配 -->
    <role-assignment ref="roleAssignmentRef" @reset-data="resetData" />
    <!-- 添加或修改用户 -->
    <add-user ref="addUserRef" @reset-data="resetData" />
  </app-container>
</template>

<style lang="scss" scoped>
// 样式
.container {
  width: 100%;
  display: flex;

  .left-container {
    width: 22%;
  }

  :deep(.el-radio__label) {
    display: none;
  }

  .table {
    width: 78%;
  }
}

.dept-div {
  padding-right: 12px;

  :deep(.el-card) {
    height: calc(100vh - 50px - 25px - 60px);
    background-color: #fff;
  }

  .box-card {
    width: 100%;

    .user-dept-scroll {
      width: 100%;
      height: calc(100vh - 50px - 60px - 20px - 40px - 60px);
    }
  }
}
</style>