zjk
2026-03-31 b21a6fc6acb52160f26b394eff8a75b3f4d066e2
feat: 修改样式
2个文件已修改
243 ■■■■■ 已修改文件
src/views/operations/personnel/personnelManagement/detail.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/personnel/personnelManagement/index.vue 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/personnel/personnelManagement/detail.vue
@@ -80,24 +80,28 @@
</script>
<template>
  <div class="h-full flex flex-col p-12px overflow-y-auto" v-if="personnelInfo?.nickName">
    <n-card :bordered="false" class="rounded-6px" content-style="padding: 24px">
  <div v-if="personnelInfo?.nickName" class="h-full flex flex-col overflow-y-auto">
    <n-card :bordered="false" class="rounded-6px h-full" content-style="padding: 24px">
      <div class="flex items-center justify-between mb-24px">
        <div class="head">员工详情</div>
        <n-button type="primary" ghost class="w-84px h-32px" @click="handleEdit">编辑</n-button>
        <div class="head">
          员工详情
        </div>
        <n-button type="primary" ghost class="w-84px h-32px" @click="handleEdit">
          编辑
        </n-button>
      </div>
      <div class="flex items-center gap-8px mb-16px">
        <div class="relative w-40px h-40px">
          <n-avatar
            v-if="personnelInfo.avatarUrl"
            round
            :size="40"
            v-if="personnelInfo.avatarUrl"
            :src="personnelInfo.avatarUrl"
          />
          <img src="/src/assets/element-img/avatar.png" style="width:40px;height:40px" v-else-if="!personnelInfo.avatarUrl && personnelInfo.sex === 1">
          <img src="/src/assets/element-img/acatar-woman.png" style="width:40px;height:40px" v-else>
          <img src="/src/assets/element-img/man.png" v-if="personnelInfo.sex === 1" class="sex-icon">
          <img src="/src/assets/element-img/woman.png" v-else class="sex-icon">
          <img v-else-if="!personnelInfo.avatarUrl && personnelInfo.sex === 1" src="/src/assets/element-img/avatar.png" style="width:40px;height:40px">
          <img v-else src="/src/assets/element-img/acatar-woman.png" style="width:40px;height:40px">
          <img v-if="personnelInfo.sex === 1" src="/src/assets/element-img/man.png" class="sex-icon">
          <img v-else src="/src/assets/element-img/woman.png" class="sex-icon">
        </div>
        <div class="flex flex-col gap-4px">
          <span class="text-16px text-[#171F26]">{{ personnelInfo.nickName }}</span>
@@ -124,12 +128,14 @@
        </div>
      </div>
      <div class="mb-16px">
        <div class="text-14px font-600 text-[#171F26] mb-12px">身份证:</div>
        <div class="flex gap-16px" v-if="personnelInfo.idCardFrontUrl || personnelInfo.idCardBackUrl">
        <div class="text-14px font-600 text-[#171F26] mb-12px">
          身份证:
        </div>
        <div v-if="personnelInfo.idCardFrontUrl || personnelInfo.idCardBackUrl" class="flex gap-16px">
          <n-image
            v-if="personnelInfo.idCardFrontUrl"
            width="213"
            height="120"
            v-if="personnelInfo.idCardFrontUrl"
            :src="personnelInfo.idCardFrontUrl"
            object-fit="cover"
            class="rounded-4px border border-[#E5E5E5]"
@@ -147,7 +153,9 @@
      </div>
      <div class="mb-16px">
        <div class="text-14px font-600 text-[#171F26] mb-12px">驾驶证:</div>
        <div class="text-14px font-600 text-[#171F26] mb-12px">
          驾驶证:
        </div>
        <n-image
          width="213"
          height="120"
@@ -159,7 +167,9 @@
      </div>
      <div>
        <div class="text-14px font-600 text-[#171F26] mb-12px">无犯罪记录证明:</div>
        <div class="text-14px font-600 text-[#171F26] mb-12px">
          无犯罪记录证明:
        </div>
        <n-image
          width="213"
          height="120"
src/views/operations/personnel/personnelManagement/index.vue
@@ -1,12 +1,10 @@
<script setup lang="tsx">
import { ref, h, computed } from 'vue'
import { useBoolean } from '@/hooks'
import { computed, h, ref } from 'vue'
import { useBoolean, usePermission } from '@/hooks'
import { useRouter } from 'vue-router'
import type { DataTableColumns } from 'naive-ui'
import { NButton, NTag, NDropdown, NTooltip, NSwitch } from 'naive-ui'
import { usePermission } from '@/hooks'
import { getOperationsUserList, exportOperationsUserTemplate, exportOperationsUser, importOperationsUser,
  addOperationsUser, editOperationsUser, updateOperationsUserStatus, deleteOperationsUser } from '@/service/api/attendance'
import { NButton, NDropdown, NSwitch, NTag, NTooltip } from 'naive-ui'
import { addOperationsUser, deleteOperationsUser, editOperationsUser, exportOperationsUser, exportOperationsUserTemplate, getOperationsUserList, importOperationsUser, updateOperationsUserStatus } from '@/service/api/attendance'
import { fetchRoleList } from '@/service/api/fleet'
import { downloadFile } from '@/utils'
import SearchPanel from '@/components/custom/SearchPanel.vue'
@@ -23,7 +21,7 @@
  nickName: '',
  phoneNumber: '',
  roleId: null as string | null,
  status: null as string | null
  status: null as string | null,
})
const showFormModal = ref(false)
@@ -37,7 +35,7 @@
const statusOptions = [
  { label: '启用', value: true },
  { label: '禁用', value: false }
  { label: '禁用', value: false },
]
const personnelList = ref<any[]>([])
@@ -52,17 +50,17 @@
  pageParams.value.pageNum = 1
  getList()
}
const handleReset = () => {
function handleReset() {
  searchModel.value = {
    nickName: '',
    phoneNumber: '',
    roleId: null,
    status: null
    status: null,
  }
  handleSearch()
}
const handleExport = () => {
function handleExport() {
  const params = {
    ...pageParams.value,
    nickName: searchModel.value.nickName,
@@ -70,7 +68,7 @@
    roleId: searchModel.value.roleId,
    status: searchModel.value.status,
  }
  exportOperationsUser(params).then(res => {
  exportOperationsUser(params).then((res) => {
    if (res instanceof Blob) {
      downloadFile(res, `运维人员列表_${new Date().getTime()}.xlsx`)
      window.$message.success('导出成功')
@@ -117,33 +115,33 @@
  showResetPasswordModal.value = true
}
const handleAdd = () => {
function handleAdd() {
  formMode.value = 'add'
  showFormModal.value = true
}
const handleEdit = (row: any) => {
function handleEdit(row: any) {
  formMode.value = 'edit'
  showFormModal.value = true
  modalRef.value?.openModal(row)
}
const handleDelete = (row: any) => {
function handleDelete(row: any) {
  dialog.create({
    title: '是否确认删除数据?',
    content: `确认删除该人员?此操作不可恢复。`,
    positiveText: '确认',
    negativeText: '取消',
    maskClosable: false,
                autoFocus: false,
    autoFocus: false,
    onPositiveClick: () => {
      deleteOperationsUser(row.id).then(res => {
      deleteOperationsUser(row.id).then((res) => {
        if (res.code === 200) {
          window.$message.success('删除成功')
          getList()
        }
      })
    }
    },
  })
}
@@ -157,11 +155,11 @@
    positiveText: '确认',
    negativeText: '取消',
    maskClosable: false,
                autoFocus: false,
    autoFocus: false,
    onPositiveClick: async () => {
      const res = await updateOperationsUserStatus({
        id: row.id,
        status: newStatus
        status: newStatus,
      })
      if (res.code === 200) {
        window.$message.success('操作成功')
@@ -174,17 +172,18 @@
  })
}
const handleConfirmForm = (data: any) => {
function handleConfirmForm(data: any) {
  if (data.id) {
    editOperationsUser(data).then(res => {
    editOperationsUser(data).then((res) => {
      if (res.code === 200) {
        showFormModal.value = false
        window.$message.success('编辑成功')
        getList()
      }
    })
  } else {
    addOperationsUser(data).then(res => {
  }
  else {
    addOperationsUser(data).then((res) => {
      if (res.code === 200) {
        showFormModal.value = false
        window.$message.success('新增成功')
@@ -194,17 +193,16 @@
  }
}
const filterItems = computed(() => [
  {
    label: '员工姓名',
    path: 'nickName',
    render: () => <n-input v-model:value={searchModel.value.nickName} placeholder="请输入" clearable />
    render: () => <n-input v-model:value={searchModel.value.nickName} placeholder="请输入" clearable />,
  },
  {
    label: '联系电话',
    path: 'phoneNumber',
    render: () => <n-input v-model:value={searchModel.value.phoneNumber} placeholder="请输入" clearable />
    render: () => <n-input v-model:value={searchModel.value.phoneNumber} placeholder="请输入" clearable />,
  },
  {
    label: '角色',
@@ -228,7 +226,7 @@
          ),
        }}
      </n-select>
    )
    ),
  },
  {
    label: '账号状态',
@@ -252,7 +250,7 @@
          ),
        }}
      </n-select>
    )
    ),
  },
])
@@ -265,16 +263,20 @@
    resizable: true,
    align: 'left',
    render: (row) => {
      const avatarUrl = row.avatar ? JSON.parse(row.avatar).url : null;
      const avatarUrl = row.avatar ? JSON.parse(row.avatar).url : null
      return (
        <div class="flex items-center gap-2">
          {avatarUrl ? (
            <img src={avatarUrl} alt="头像" class="w-24px h-24px rounded-full object-cover" onError={(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/ab6c7ff4-403a-4a2c-9ebf-310c023dc20f.png' }} />
          ) : row.sex === 1 ? (
            <img src="/src/assets/element-img/avatar.png" class="w-24px h-24px rounded-full" alt="默认男头像" />
          ) : (
            <img src="/src/assets/element-img/acatar-woman.png" class="w-24px h-24px rounded-full" alt="默认女头像" />
          )}
          {avatarUrl
            ? (
                <img src={avatarUrl} alt="头像" class="w-24px h-24px rounded-full object-cover" onError={(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/ab6c7ff4-403a-4a2c-9ebf-310c023dc20f.png' }} />
              )
            : row.sex === 1
              ? (
                  <img src="/src/assets/element-img/avatar.png" class="w-24px h-24px rounded-full" alt="默认男头像" />
                )
              : (
                  <img src="/src/assets/element-img/acatar-woman.png" class="w-24px h-24px rounded-full" alt="默认女头像" />
                )}
          <NTooltip trigger="hover" placement="top" max-width="500px">
            {{
              trigger: () => <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>{row.nickName}</div>,
@@ -285,24 +287,24 @@
      )
    },
  },
  { title: '编号', key: 'uniqueNumber', width: 100, resizable: true, align: 'left'  },
  { title: '联系电话', key: 'phoneNumber', width: 120, resizable: true, align: 'left'  },
  { title: '编号', key: 'uniqueNumber', width: 100, resizable: true, align: 'left', ellipsis: { tooltip: true } },
  { title: '联系电话', key: 'phoneNumber', width: 120, resizable: true, align: 'left' },
  {
    title: '角色',
    key: 'sysRoles',
    width: 360,
    resizable: true,
    align: 'left',
    ellipsis:  true ,
    render: (row) => (
    ellipsis: true,
    render: row => (
      <div class="flex items-center gap-1 flex-wrap">
        {
          row.sysRoles?.map((role: any) => (
            <NTag type="primary" size="small" style={{ borderRadius: '4px' }} >{role.roleName}</NTag>
            <NTag type="primary" bordered={false} size="small" style={{ borderRadius: '4px' }}>{role.roleName}</NTag>
          ))
        }
      </div>
    )
    ),
  },
  {
    title: '状态',
@@ -310,48 +312,50 @@
    width: 80,
    resizable: true,
    align: 'left',
    render: (row) => (
      row.status  ? (
        <NTag
          bordered={false}
          style={{
            borderRadius: '4px',
            color: '#0BB52D',
            backgroundColor: '#DCF5DD',
          }}
          size="small"
        >
          启用
        </NTag>
      ) : (
        <NTag
          bordered={false}
          style={{
            borderRadius: '4px',
            color: '#666666',
            backgroundColor: 'rgba(23, 31, 38, 0.05)',
          }}
          size="small"
        >
          禁用
        </NTag>
      )
    )
    render: row => (
      row.status
        ? (
            <NTag
              bordered={false}
              style={{
                borderRadius: '4px',
                color: '#0BB52D',
                backgroundColor: '#DCF5DD',
              }}
              size="small"
            >
              启用
            </NTag>
          )
        : (
            <NTag
              bordered={false}
              style={{
                borderRadius: '4px',
                color: '#666666',
                backgroundColor: 'rgba(23, 31, 38, 0.05)',
              }}
              size="small"
            >
              禁用
            </NTag>
          )
    ),
  },
  { title: '创建时间', key: 'createTime', width: 160 , resizable: true, align: 'left', render: (row) => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' },
  { title: '备注', key: 'remarks', width: 150, resizable: true, align: 'left', ellipsis: { tooltip: true }, render: (row) => row.remarks || '-' },
  { title: '创建时间', key: 'createTime', width: 160, resizable: true, align: 'left', render: row => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' },
  { title: '备注', key: 'remarks', width: 150, resizable: true, align: 'left', ellipsis: { tooltip: true }, render: row => row.remarks || '-' },
  {
    title: '操作',
    key: 'actions',
    width: 220,
    width: 250,
    fixed: 'right',
    align: 'left',
    render: (row) => (
      <div class="flex items-center gap-3">
    render: row => (
      <div class="flex items-center gap-16px">
        {/* <a v-permission="70115" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleEdit(row)}>编辑</a> */}
        <a v-permission="70117" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleResetPassword(row)}>重置密码</a>
         <a v-permission="70111" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>详情</a>
        <a v-permission="70111" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>详情</a>
        <a v-permission="70118" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleDelete(row)}>删除</a>
        <NSwitch
          v-permission="70116"
@@ -364,8 +368,8 @@
          }}
        </NSwitch>
      </div>
    )
  }
    ),
  },
])
async function getList() {
@@ -379,7 +383,7 @@
  }
  getOperationsUserList(params).then((res) => {
    personnelList.value = res.data.records || []
    personnelList.value.forEach((item: any, index: number)  => {
    personnelList.value.forEach((item: any, index: number) => {
      item.serialNumber = (pageParams.value.pageNum - 1) * pageParams.value.pageSize + index + 1
    })
    total.value = res.data.total || res.data.records?.length
@@ -409,7 +413,7 @@
  getList()
}
const handleDownloadTemplate = async () => {
async function handleDownloadTemplate() {
  const response = await exportOperationsUserTemplate()
  if (response instanceof Blob) {
    downloadFile(response, `运维人员导入模板.xlsx`)
@@ -429,8 +433,12 @@
            </n-form-item>
          </n-gi>
          <template #action>
            <n-button style="width: 84px; height: 32px" @click="handleReset">重置</n-button>
            <n-button type="primary" style="width: 84px; height: 32px" @click="handleSearch">查询</n-button>
            <NButton style="width: 84px; height: 32px" @click="handleReset">
              重置
            </NButton>
            <NButton type="primary" style="width: 84px; height: 32px" @click="handleSearch">
              查询
            </NButton>
          </template>
        </SearchPanel>
      </n-form>
@@ -439,11 +447,22 @@
    <n-card class="flex-1 robotaxi-table-card">
      <template #header>
        <n-flex justify="flex-end">
          <n-button v-permission="70112" @click="handleExport" style="width: 84px; height: 32px">导出</n-button>
          <n-button v-permission="70113" @click="handleImport" style="width: 84px; height: 32px">导入</n-button>
          <n-button v-permission="70112" @click="handleDownloadTemplate" style="width: 84px; height: 32px">下载</n-button>
          <n-button v-permission="70114" style="width: 84px;height: 32px;background-color: #E6F4FF;color: #0068FF;border: 1px solid #7ABDFF;"
            @click="handleAdd">新增</n-button>
          <NButton v-permission="70112" style="width: 84px; height: 32px" @click="handleExport">
            导出
          </NButton>
          <NButton v-permission="70113" style="width: 84px; height: 32px" @click="handleImport">
            导入
          </NButton>
          <NButton v-permission="70112" style="width: 84px; height: 32px" @click="handleDownloadTemplate">
            下载
          </NButton>
          <NButton
            v-permission="70114"
            secondary style="width: 84px;height: 32px;background-color: #E6F4FF;color: #0068FF;"
            @click="handleAdd"
          >
            新增
          </NButton>
        </n-flex>
      </template>
      <div class="flex-1 flex flex-col h-full">
@@ -467,9 +486,11 @@
      </div>
    </n-card>
    <PersonnelFormModal v-model:show="showFormModal" ref="modalRef" :mode="formMode" :roleOptions="roleOptions"
      @confirm="handleConfirmForm" />
      <!-- 重置密码弹窗 -->
    <PersonnelFormModal
      ref="modalRef" v-model:show="showFormModal" :mode="formMode" :role-options="roleOptions"
      @confirm="handleConfirmForm"
    />
    <!-- 重置密码弹窗 -->
    <ResetPasswordModal
      v-model:visible="showResetPasswordModal"
      :row-data="resetPasswordRow"
@@ -481,7 +502,7 @@
      style="display: none"
      accept=".xlsx, .xls"
      @change="handleFileChange"
    />
    >
  </div>
</template>