|
@@ -1,70 +1,33 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="p-2">
|
|
|
|
|
- <!-- 顶部统计卡片 -->
|
|
|
|
|
- <el-row :gutter="16" class="mb-[10px]">
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <div class="flex items-center justify-between">
|
|
|
|
|
- <div>
|
|
|
|
|
- <p class="text-gray-500 text-sm">人员总数</p>
|
|
|
|
|
- <p class="text-2xl font-bold text-blue-500">{{ statsData.total }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-icon :size="40" color="#409EFF"><UserFilled /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <div class="flex items-center justify-between">
|
|
|
|
|
- <div>
|
|
|
|
|
- <p class="text-gray-500 text-sm">已投保</p>
|
|
|
|
|
- <p class="text-2xl font-bold text-green-500">{{ statsData.insured }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-icon :size="40" color="#67C23A"><CircleCheckFilled /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <div class="flex items-center justify-between">
|
|
|
|
|
- <div>
|
|
|
|
|
- <p class="text-gray-500 text-sm">未投保</p>
|
|
|
|
|
- <p class="text-2xl font-bold text-red-500">{{ statsData.uninsured }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-icon :size="40" color="#F56C6C"><CircleCloseFilled /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <div class="flex items-center justify-between">
|
|
|
|
|
- <div>
|
|
|
|
|
- <p class="text-gray-500 text-sm">保险即将到期</p>
|
|
|
|
|
- <p class="text-2xl font-bold text-orange-500">{{ statsData.expiringSoon }}</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-icon :size="40" color="#E6A23C"><WarningFilled /></el-icon>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
-
|
|
|
|
|
- <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
|
|
|
|
- <div v-show="showSearch" class="mb-[10px]">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
|
|
|
|
|
+ <div class="person-page">
|
|
|
|
|
+ <div class="page-header">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <div class="page-title">人员信息管理</div>
|
|
|
|
|
+ <div class="page-desc">管理企业所有工作人员基本信息和资质证书</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-button v-has-permi="['riskctrl:personInfo:add']" type="primary" icon="Plus" @click="handleAdd"> 新增人员 </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 搜索区域 -->
|
|
|
|
|
+ <el-card shadow="hover" class="search-card">
|
|
|
|
|
+ <el-form ref="queryFormRef" :model="queryParams" label-position="top" class="search-form">
|
|
|
|
|
+ <el-row :gutter="12">
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="姓名" prop="personName">
|
|
<el-form-item label="姓名" prop="personName">
|
|
|
- <el-input v-model="queryParams.personName" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
|
|
+ <el-input v-model="queryParams.personName" placeholder="请输入姓名搜索" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="人员类型" prop="personType">
|
|
<el-form-item label="人员类型" prop="personType">
|
|
|
- <el-select v-model="queryParams.personType" placeholder="请选择" clearable>
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="dict in person_type_options"
|
|
|
|
|
- :key="dict.value"
|
|
|
|
|
- :label="dict.label"
|
|
|
|
|
- :value="dict.value"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <el-select v-model="queryParams.personType" placeholder="全部类型" clearable class="w-full">
|
|
|
|
|
+ <el-option v-for="dict in person_type_options" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="8">
|
|
|
<el-form-item label="工作门店" prop="workStoreId">
|
|
<el-form-item label="工作门店" prop="workStoreId">
|
|
|
<el-tree-select
|
|
<el-tree-select
|
|
|
v-model="queryParams.workStoreId"
|
|
v-model="queryParams.workStoreId"
|
|
@@ -74,83 +37,61 @@
|
|
|
placeholder="请选择门店"
|
|
placeholder="请选择门店"
|
|
|
check-strictly
|
|
check-strictly
|
|
|
clearable
|
|
clearable
|
|
|
|
|
+ class="w-full"
|
|
|
/>
|
|
/>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="保险状态" prop="insuranceStatus">
|
|
|
|
|
- <el-select v-model="queryParams.insuranceStatus" placeholder="请选择" clearable>
|
|
|
|
|
- <el-option label="已投保" value="1" />
|
|
|
|
|
- <el-option label="未投保" value="0" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item>
|
|
|
|
|
- <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
|
|
- <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </div>
|
|
|
|
|
- </transition>
|
|
|
|
|
-
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <el-row :gutter="10">
|
|
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button v-has-permi="['riskctrl:personInfo:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button v-has-permi="['riskctrl:personInfo:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button v-has-permi="['riskctrl:personInfo:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">删除</el-button>
|
|
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
|
|
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- </template>
|
|
|
|
|
|
|
|
|
|
- <el-table v-loading="loading" border :data="personList" @selection-change="handleSelectionChange">
|
|
|
|
|
- <el-table-column type="selection" width="50" align="center" />
|
|
|
|
|
- <el-table-column v-if="columns[0].visible" label="人员类型" align="center" prop="personType" width="100">
|
|
|
|
|
|
|
+ <div class="search-actions">
|
|
|
|
|
+ <el-button @click="resetQuery">重置</el-button>
|
|
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 列表工具栏 -->
|
|
|
|
|
+ <div class="list-toolbar">
|
|
|
|
|
+ <div class="list-total">共 {{ total }} 条记录</div>
|
|
|
|
|
+ <el-button v-has-permi="['riskctrl:personInfo:add']" type="primary" icon="Plus" @click="handleAdd"> 新增人员 </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 列表 -->
|
|
|
|
|
+ <el-card shadow="hover" class="table-card">
|
|
|
|
|
+ <el-table v-loading="loading" border :data="personList">
|
|
|
|
|
+ <el-table-column label="人员类型" align="center" prop="personType" width="110">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<el-tag :type="getPersonTypeType(scope.row.personType)">{{ scope.row.personType }}</el-tag>
|
|
<el-tag :type="getPersonTypeType(scope.row.personType)">{{ scope.row.personType }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column v-if="columns[1].visible" label="姓名" align="center" prop="personName" />
|
|
|
|
|
- <el-table-column v-if="columns[2].visible" label="年龄" align="center" prop="age" width="80" />
|
|
|
|
|
- <el-table-column v-if="columns[3].visible" label="身份证号" align="center" prop="idCard" width="180">
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="姓名" align="center" prop="personName" width="120" />
|
|
|
|
|
+ <el-table-column label="年龄" align="center" prop="age" width="80" />
|
|
|
|
|
+ <el-table-column label="身份证号" align="center" prop="idCard" width="180">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<span>{{ maskIdCard(scope.row.idCard) }}</span>
|
|
<span>{{ maskIdCard(scope.row.idCard) }}</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column v-if="columns[4].visible" label="工作门店" align="center" prop="workStoreName" />
|
|
|
|
|
- <el-table-column v-if="columns[5].visible" label="保险状态" align="center" prop="insuranceStatus" width="100">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <el-tag :type="getInsuranceType(scope.row)">{{ getInsuranceLabel(scope.row) }}</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column v-if="columns[6].visible" label="保险有效期" align="center" prop="insuranceExpireDate" width="120">
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="岗位证书" align="center" prop="certificates" min-width="150" :show-overflow-tooltip="true">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
- <span>{{ scope.row.insuranceExpireDate ? scope.row.insuranceExpireDate.substring(0, 10) : '-' }}</span>
|
|
|
|
|
|
|
+ <span>{{ getCertNames(scope.row.certificates) }}</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column v-if="columns[7].visible" label="岗位证书" align="center" width="120">
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="工作门店" align="center" prop="workStoreName" min-width="140" :show-overflow-tooltip="true" />
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="保险状态" align="center" prop="insuranceStatus" width="110">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
- <el-button link type="primary" @click="viewCertificates(scope.row)">
|
|
|
|
|
- {{ getCertCount(scope.row.certificates) }}个证书
|
|
|
|
|
- </el-button>
|
|
|
|
|
|
|
+ <el-tag :type="getInsuranceType(scope.row)">{{ getInsuranceLabel(scope.row) }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column v-if="columns[8].visible" label="创建时间" align="center" prop="createTime" width="160" />
|
|
|
|
|
- <el-table-column label="操作" fixed="right" width="200" class-name="small-padding fixed-width">
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="操作" fixed="right" width="220" class-name="small-padding fixed-width">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
- <el-tooltip content="修改" placement="top">
|
|
|
|
|
- <el-button v-has-permi="['riskctrl:personInfo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
|
|
|
|
- </el-tooltip>
|
|
|
|
|
- <el-tooltip content="查看证书" placement="top">
|
|
|
|
|
- <el-button link type="primary" icon="Document" @click="viewCertificates(scope.row)"></el-button>
|
|
|
|
|
- </el-tooltip>
|
|
|
|
|
- <el-tooltip content="删除" placement="top">
|
|
|
|
|
- <el-button v-has-permi="['riskctrl:personInfo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
|
|
|
|
- </el-tooltip>
|
|
|
|
|
|
|
+ <el-button link type="primary" @click="viewCertificates(scope.row)">查看证书</el-button>
|
|
|
|
|
+ <el-button v-has-permi="['riskctrl:personInfo:edit']" link type="warning" @click="handleUpdate(scope.row)">编辑</el-button>
|
|
|
|
|
+ <el-button v-has-permi="['riskctrl:personInfo:remove']" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
</el-table>
|
|
</el-table>
|
|
@@ -159,108 +100,146 @@
|
|
|
</el-card>
|
|
</el-card>
|
|
|
|
|
|
|
|
<!-- 新增/修改弹窗 -->
|
|
<!-- 新增/修改弹窗 -->
|
|
|
- <el-dialog v-model="open" :title="title" width="700px" append-to-body>
|
|
|
|
|
- <el-form ref="personFormRef" :model="form" :rules="rules" label-width="100px">
|
|
|
|
|
|
|
+ <el-dialog v-model="open" :title="dialogTitle" width="980px" append-to-body :close-on-click-modal="false" destroy-on-close class="person-dialog">
|
|
|
|
|
+ <!-- <div class="dialog-banner">-->
|
|
|
|
|
+ <!-- <div class="dialog-banner-title">-->
|
|
|
|
|
+ <!-- <span class="dialog-banner-icon">+</span>-->
|
|
|
|
|
+ <!-- <span>{{ dialogTitle }}</span>-->
|
|
|
|
|
+ <!-- </div>-->
|
|
|
|
|
+ <!-- <div class="dialog-banner-desc">请填写人员详细信息,带 <span class="required-star">*</span> 为必填项</div>-->
|
|
|
|
|
+ <!-- </div>-->
|
|
|
|
|
+
|
|
|
|
|
+ <!-- <div class="section-title">人员信息</div>-->
|
|
|
|
|
+
|
|
|
|
|
+ <el-form ref="personFormRef" :model="form" :rules="rules" label-position="top" class="person-form">
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="人员类型" prop="personType">
|
|
|
|
|
- <el-select v-model="form.personType" placeholder="请选择" class="w-full">
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="dict in person_type_options"
|
|
|
|
|
- :key="dict.value"
|
|
|
|
|
- :label="dict.label"
|
|
|
|
|
- :value="dict.value"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <el-form-item label="人员类型" prop="personType" required>
|
|
|
|
|
+ <el-select v-model="form.personType" placeholder="请选择人员类型" class="w-full">
|
|
|
|
|
+ <el-option v-for="dict in person_type_options" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
+
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="姓名" prop="personName">
|
|
|
|
|
|
|
+ <el-form-item label="姓名" prop="personName" required>
|
|
|
<el-input v-model="form.personName" placeholder="请输入姓名" />
|
|
<el-input v-model="form.personName" placeholder="请输入姓名" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
|
|
+
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="身份证号" prop="idCard">
|
|
|
|
|
|
|
+ <el-form-item label="身份证号" prop="idCard" required>
|
|
|
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" @blur="calcAge" />
|
|
<el-input v-model="form.idCard" placeholder="请输入身份证号" maxlength="18" @blur="calcAge" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
+
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="年龄">
|
|
<el-form-item label="年龄">
|
|
|
<el-input v-model="form.age" placeholder="自动计算" disabled />
|
|
<el-input v-model="form.age" placeholder="自动计算" disabled />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
|
|
+
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="工作门店" prop="workStoreId">
|
|
|
|
|
|
|
+ <el-form-item label="工作门店" prop="workStoreId" required>
|
|
|
<el-tree-select
|
|
<el-tree-select
|
|
|
v-model="form.workStoreId"
|
|
v-model="form.workStoreId"
|
|
|
:data="deptOptions"
|
|
:data="deptOptions"
|
|
|
:props="{ value: 'id', label: 'label', children: 'children' }"
|
|
:props="{ value: 'id', label: 'label', children: 'children' }"
|
|
|
value-key="id"
|
|
value-key="id"
|
|
|
- placeholder="请选择门店"
|
|
|
|
|
|
|
+ placeholder="请选择工作门店"
|
|
|
check-strictly
|
|
check-strictly
|
|
|
clearable
|
|
clearable
|
|
|
class="w-full"
|
|
class="w-full"
|
|
|
/>
|
|
/>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
+
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="保险状态" prop="insuranceStatus">
|
|
|
|
|
- <el-select v-model="form.insuranceStatus" placeholder="请选择" class="w-full">
|
|
|
|
|
|
|
+ <el-form-item label="保险状态" prop="insuranceStatus" required>
|
|
|
|
|
+ <el-select v-model="form.insuranceStatus" placeholder="请选择保险状态" class="w-full">
|
|
|
<el-option label="已投保" value="1" />
|
|
<el-option label="已投保" value="1" />
|
|
|
<el-option label="未投保" value="0" />
|
|
<el-option label="未投保" value="0" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- <el-form-item v-if="form.insuranceStatus === '1'" label="保险有效期" prop="insuranceExpireDate">
|
|
|
|
|
- <el-date-picker v-model="form.insuranceExpireDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" class="w-full" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="岗位证书">
|
|
|
|
|
- <div class="w-full">
|
|
|
|
|
- <div v-for="(cert, index) in certificateList" :key="index" class="flex items-center gap-2 mb-2">
|
|
|
|
|
- <el-select v-model="cert.certType" placeholder="证书类型" style="width: 180px">
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="dict in certificate_type_options"
|
|
|
|
|
- :key="dict.value"
|
|
|
|
|
- :label="dict.label"
|
|
|
|
|
- :value="dict.value"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-upload
|
|
|
|
|
- :action="uploadAction"
|
|
|
|
|
- :headers="uploadHeaders"
|
|
|
|
|
- :show-file-list="false"
|
|
|
|
|
- :on-success="(res: any) => handleCertUploadSuccess(res, index)"
|
|
|
|
|
- accept=".jpg,.png,.pdf"
|
|
|
|
|
- >
|
|
|
|
|
- <el-button type="primary" link>
|
|
|
|
|
- {{ cert.certUrl ? '重新上传' : '上传证书' }}
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </el-upload>
|
|
|
|
|
- <el-tag v-if="cert.certUrl" type="success" size="small">已上传</el-tag>
|
|
|
|
|
- <el-button type="danger" link icon="Delete" @click="removeCertificate(index)"></el-button>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="保险有效期开始" prop="insuranceStartDate" :required="form.insuranceStatus === '1'">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="form.insuranceStartDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="年/月/日"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ :disabled-date="disabledDate"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="保险有效期结束" prop="insuranceEndDate" :required="form.insuranceStatus === '1'">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="form.insuranceEndDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="年/月/日"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ :disabled-date="disabledDate"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="岗位证书" required>
|
|
|
|
|
+ <div class="certificate-section">
|
|
|
|
|
+ <div v-for="(cert, index) in certificateList" :key="index" class="certificate-row">
|
|
|
|
|
+ <el-row :gutter="12" align="middle">
|
|
|
|
|
+ <el-col :span="8">
|
|
|
|
|
+ <el-select v-model="cert.certType" placeholder="请选择证书类型" class="w-full">
|
|
|
|
|
+ <el-option v-for="dict in certificate_type_options" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :span="14">
|
|
|
|
|
+ <div class="cert-file-group">
|
|
|
|
|
+ <el-input :model-value="cert.certName || ''" placeholder="请选择文件" readonly class="cert-file-input" />
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ :action="uploadAction"
|
|
|
|
|
+ :headers="uploadHeaders"
|
|
|
|
|
+ :show-file-list="false"
|
|
|
|
|
+ :accept="'.jpg,.jpeg,.png,.pdf'"
|
|
|
|
|
+ :before-upload="beforeCertUpload"
|
|
|
|
|
+ :on-success="(res: any, file: any) => handleCertUploadSuccess(res, file, index)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-button type="primary">选择文件</el-button>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :span="2">
|
|
|
|
|
+ <el-button link type="danger" icon="Delete" @click="removeCertificate(index)" />
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="add-cert-box" @click="addCertificate">
|
|
|
|
|
+ <span class="add-cert-plus">+</span>
|
|
|
|
|
+ <span>添加证书</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <el-button type="primary" link icon="Plus" @click="addCertificate">添加证书</el-button>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="状态" prop="status">
|
|
|
|
|
- <el-radio-group v-model="form.status">
|
|
|
|
|
- <el-radio label="0">正常</el-radio>
|
|
|
|
|
- <el-radio label="1">停用</el-radio>
|
|
|
|
|
- </el-radio-group>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="备注" prop="remark">
|
|
|
|
|
- <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
+
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
<div class="dialog-footer">
|
|
|
- <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
|
|
- <el-button @click="cancel">取 消</el-button>
|
|
|
|
|
|
|
+ <el-button @click="cancel">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="submitForm">保存</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
@@ -270,7 +249,13 @@
|
|
|
<div v-if="currentCerts.length > 0">
|
|
<div v-if="currentCerts.length > 0">
|
|
|
<div v-for="(cert, index) in currentCerts" :key="index" class="cert-item mb-4 p-4 border rounded">
|
|
<div v-for="(cert, index) in currentCerts" :key="index" class="cert-item mb-4 p-4 border rounded">
|
|
|
<p class="font-bold mb-2">证书类型:{{ cert.certTypeName || cert.certType }}</p>
|
|
<p class="font-bold mb-2">证书类型:{{ cert.certTypeName || cert.certType }}</p>
|
|
|
- <el-image v-if="isImage(cert.certUrl)" :src="cert.certUrl" style="max-width: 100%; max-height: 300px;" fit="contain" :preview-src-list="[cert.certUrl]" />
|
|
|
|
|
|
|
+ <el-image
|
|
|
|
|
+ v-if="isImage(cert.certUrl)"
|
|
|
|
|
+ :src="cert.certUrl"
|
|
|
|
|
+ style="max-width: 100%; max-height: 300px"
|
|
|
|
|
+ fit="contain"
|
|
|
|
|
+ :preview-src-list="[cert.certUrl]"
|
|
|
|
|
+ />
|
|
|
<div v-else class="text-center py-4">
|
|
<div v-else class="text-center py-4">
|
|
|
<el-icon :size="48" color="#409EFF"><Document /></el-icon>
|
|
<el-icon :size="48" color="#409EFF"><Document /></el-icon>
|
|
|
<p class="mt-2">
|
|
<p class="mt-2">
|
|
@@ -285,44 +270,65 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup name="PersonInfo" lang="ts">
|
|
<script setup name="PersonInfo" lang="ts">
|
|
|
-import { listPersonInfo, getPersonInfo, delPersonInfo, addPersonInfo, updatePersonInfo, getPersonStats } from '@/api/riskctrl/personInfo';
|
|
|
|
|
-import { PersonInfoForm, PersonInfoQuery, PersonInfoVO, Certificate } from '@/api/riskctrl/personInfo/types';
|
|
|
|
|
|
|
+import { computed, getCurrentInstance, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
|
|
|
|
+import type { ComponentInternalInstance } from 'vue';
|
|
|
|
|
+import type { FormInstance } from 'element-plus';
|
|
|
|
|
+import { Document, Delete, Plus } from '@element-plus/icons-vue';
|
|
|
|
|
+import { listPersonInfo, getPersonInfo, delPersonInfo, addPersonInfo, updatePersonInfo } from '@/api/riskctrl/personInfo';
|
|
|
|
|
+import type { PersonInfoForm, PersonInfoQuery, PersonInfoVO } from '@/api/riskctrl/personInfo/types';
|
|
|
import { listDept } from '@/api/system/dept';
|
|
import { listDept } from '@/api/system/dept';
|
|
|
import { getToken } from '@/utils/auth';
|
|
import { getToken } from '@/utils/auth';
|
|
|
|
|
+import { getDictOptions } from '@/api/riskctrl/dictData';
|
|
|
|
|
+
|
|
|
|
|
+interface CertificateItem {
|
|
|
|
|
+ certType: string;
|
|
|
|
|
+ certTypeName?: string;
|
|
|
|
|
+ certUrl: string;
|
|
|
|
|
+ certName?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type TagType = 'success' | 'warning' | 'danger' | 'info' | 'primary';
|
|
|
|
|
+
|
|
|
|
|
+type PersonForm = PersonInfoForm & {
|
|
|
|
|
+ insuranceStartDate?: string;
|
|
|
|
|
+ insuranceEndDate?: string;
|
|
|
|
|
+ certificates?: string;
|
|
|
|
|
+ workStoreName?: string;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
|
|
const personList = ref<PersonInfoVO[]>([]);
|
|
const personList = ref<PersonInfoVO[]>([]);
|
|
|
const loading = ref(true);
|
|
const loading = ref(true);
|
|
|
-const showSearch = ref(true);
|
|
|
|
|
-const ids = ref<Array<string | number>>([]);
|
|
|
|
|
-const single = ref(true);
|
|
|
|
|
-const multiple = ref(true);
|
|
|
|
|
const total = ref(0);
|
|
const total = ref(0);
|
|
|
-const title = ref('');
|
|
|
|
|
const open = ref(false);
|
|
const open = ref(false);
|
|
|
const certOpen = ref(false);
|
|
const certOpen = ref(false);
|
|
|
|
|
|
|
|
-const queryFormRef = ref<ElFormInstance>();
|
|
|
|
|
-const personFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
|
+const queryFormRef = ref<FormInstance>();
|
|
|
|
|
+const personFormRef = ref<FormInstance>();
|
|
|
|
|
+
|
|
|
|
|
+const deptOptions = ref<any[]>([]);
|
|
|
|
|
+const person_type_options = ref<any[]>([]);
|
|
|
|
|
+const certificate_type_options = ref<any[]>([]);
|
|
|
|
|
|
|
|
-const statsData = reactive({
|
|
|
|
|
- total: 0,
|
|
|
|
|
- insured: 0,
|
|
|
|
|
- uninsured: 0,
|
|
|
|
|
- expiringSoon: 0
|
|
|
|
|
|
|
+const uploadAction = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
|
|
|
|
|
+const uploadHeaders = ref({
|
|
|
|
|
+ Authorization: 'Bearer ' + getToken(),
|
|
|
|
|
+ clientid: import.meta.env.VITE_APP_CLIENT_ID
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+const dialogMode = ref<'add' | 'edit'>('add');
|
|
|
|
|
+const dialogTitle = computed(() => (dialogMode.value === 'add' ? '新增人员信息' : '编辑人员信息'));
|
|
|
|
|
+
|
|
|
const queryParams = reactive<PersonInfoQuery>({
|
|
const queryParams = reactive<PersonInfoQuery>({
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
personName: undefined,
|
|
personName: undefined,
|
|
|
personType: undefined,
|
|
personType: undefined,
|
|
|
- workStoreId: undefined,
|
|
|
|
|
- insuranceStatus: undefined
|
|
|
|
|
|
|
+ workStoreId: undefined
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const form = reactive<PersonInfoForm>({
|
|
|
|
|
|
|
+const form = reactive<PersonForm>({
|
|
|
personId: undefined,
|
|
personId: undefined,
|
|
|
personType: '',
|
|
personType: '',
|
|
|
personName: '',
|
|
personName: '',
|
|
@@ -331,74 +337,83 @@ const form = reactive<PersonInfoForm>({
|
|
|
workStoreId: undefined,
|
|
workStoreId: undefined,
|
|
|
workStoreName: '',
|
|
workStoreName: '',
|
|
|
insuranceStatus: '0',
|
|
insuranceStatus: '0',
|
|
|
- insuranceExpireDate: undefined,
|
|
|
|
|
|
|
+ insuranceStartDate: '',
|
|
|
|
|
+ insuranceEndDate: '',
|
|
|
certificates: '[]',
|
|
certificates: '[]',
|
|
|
status: '0',
|
|
status: '0',
|
|
|
remark: ''
|
|
remark: ''
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const certificateList = ref<Certificate[]>([]);
|
|
|
|
|
-const currentCerts = ref<Certificate[]>([]);
|
|
|
|
|
-
|
|
|
|
|
-const deptOptions = ref<any[]>([]);
|
|
|
|
|
-const person_type_options = ref<any[]>([]);
|
|
|
|
|
-const certificate_type_options = ref<any[]>([]);
|
|
|
|
|
-
|
|
|
|
|
-const uploadAction = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
|
|
|
|
|
-const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken(), clientid: import.meta.env.VITE_APP_CLIENT_ID });
|
|
|
|
|
|
|
+const certificateList = ref<CertificateItem[]>([]);
|
|
|
|
|
+const currentCerts = ref<CertificateItem[]>([]);
|
|
|
|
|
|
|
|
-const rules = {
|
|
|
|
|
|
|
+const rules = computed(() => ({
|
|
|
personType: [{ required: true, message: '人员类型不能为空', trigger: 'change' }],
|
|
personType: [{ required: true, message: '人员类型不能为空', trigger: 'change' }],
|
|
|
personName: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
|
personName: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
|
|
idCard: [
|
|
idCard: [
|
|
|
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
|
|
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
|
|
|
- { pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, message: '身份证号格式不正确', trigger: 'blur' }
|
|
|
|
|
|
|
+ {
|
|
|
|
|
+ pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
|
|
|
|
|
+ message: '身份证号格式不正确',
|
|
|
|
|
+ trigger: 'blur'
|
|
|
|
|
+ }
|
|
|
],
|
|
],
|
|
|
- insuranceStatus: [{ required: true, message: '保险状态不能为空', trigger: 'change' }]
|
|
|
|
|
|
|
+ workStoreId: [{ required: true, message: '工作门店不能为空', trigger: 'change' }],
|
|
|
|
|
+ insuranceStatus: [{ required: true, message: '保险状态不能为空', trigger: 'change' }],
|
|
|
|
|
+ insuranceStartDate: form.insuranceStatus === '1' ? [{ required: true, message: '请选择保险有效期开始', trigger: 'change' }] : [],
|
|
|
|
|
+ insuranceEndDate:
|
|
|
|
|
+ form.insuranceStatus === '1'
|
|
|
|
|
+ ? [
|
|
|
|
|
+ { required: true, message: '请选择保险有效期结束', trigger: 'change' },
|
|
|
|
|
+ {
|
|
|
|
|
+ validator: (_rule: any, value: string, callback: any) => {
|
|
|
|
|
+ if (value && form.insuranceStartDate && value < form.insuranceStartDate) {
|
|
|
|
|
+ callback(new Error('保险有效期结束不能早于开始日期'));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'change'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ : []
|
|
|
|
|
+}));
|
|
|
|
|
+
|
|
|
|
|
+const disabledDate = (time: Date) => {
|
|
|
|
|
+ return time.getTime() > Date.now();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const columns = ref([
|
|
|
|
|
- { key: 0, label: '人员类型', visible: true },
|
|
|
|
|
- { key: 1, label: '姓名', visible: true },
|
|
|
|
|
- { key: 2, label: '年龄', visible: true },
|
|
|
|
|
- { key: 3, label: '身份证号', visible: true },
|
|
|
|
|
- { key: 4, label: '工作门店', visible: true },
|
|
|
|
|
- { key: 5, label: '保险状态', visible: true },
|
|
|
|
|
- { key: 6, label: '保险有效期', visible: true },
|
|
|
|
|
- { key: 7, label: '岗位证书', visible: true },
|
|
|
|
|
- { key: 8, label: '创建时间', visible: true }
|
|
|
|
|
-]);
|
|
|
|
|
-
|
|
|
|
|
-const loadStats = async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await getPersonStats();
|
|
|
|
|
- Object.assign(statsData, res.data);
|
|
|
|
|
- } catch { /* ignore */ }
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const createEmptyCertificate = (): CertificateItem => ({
|
|
|
|
|
+ certType: '',
|
|
|
|
|
+ certUrl: '',
|
|
|
|
|
+ certName: ''
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
- const res = await listPersonInfo(queryParams);
|
|
|
|
|
- personList.value = res.rows;
|
|
|
|
|
- total.value = res.total;
|
|
|
|
|
- loading.value = false;
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res: any = await listPersonInfo(queryParams as any);
|
|
|
|
|
+ personList.value = res.rows || [];
|
|
|
|
|
+ total.value = res.total || 0;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载人员列表失败', error);
|
|
|
|
|
+ personList.value = [];
|
|
|
|
|
+ total.value = 0;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const getDeptTree = async () => {
|
|
const getDeptTree = async () => {
|
|
|
- const res = await listDept();
|
|
|
|
|
- deptOptions.value = res.data;
|
|
|
|
|
|
|
+ const res: any = await listDept();
|
|
|
|
|
+ deptOptions.value = res.data || [];
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const getDicts = async () => {
|
|
const getDicts = async () => {
|
|
|
- const ptRes = await proxy?.getDicts('person_type');
|
|
|
|
|
- person_type_options.value = ptRes.data || [];
|
|
|
|
|
- const ctRes = await proxy?.getDicts('certificate_type');
|
|
|
|
|
- certificate_type_options.value = ctRes.data || [];
|
|
|
|
|
-};
|
|
|
|
|
|
|
+ const [personRes, certRes] = await Promise.all([getDictOptions('person_type'), getDictOptions('certificate_type')]);
|
|
|
|
|
|
|
|
-const cancel = () => {
|
|
|
|
|
- open.value = false;
|
|
|
|
|
- reset();
|
|
|
|
|
|
|
+ person_type_options.value = personRes;
|
|
|
|
|
+ certificate_type_options.value = certRes;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const reset = () => {
|
|
const reset = () => {
|
|
@@ -410,16 +425,27 @@ const reset = () => {
|
|
|
form.workStoreId = undefined;
|
|
form.workStoreId = undefined;
|
|
|
form.workStoreName = '';
|
|
form.workStoreName = '';
|
|
|
form.insuranceStatus = '0';
|
|
form.insuranceStatus = '0';
|
|
|
- form.insuranceExpireDate = undefined;
|
|
|
|
|
|
|
+ form.insuranceStartDate = '';
|
|
|
|
|
+ form.insuranceEndDate = '';
|
|
|
form.certificates = '[]';
|
|
form.certificates = '[]';
|
|
|
form.status = '0';
|
|
form.status = '0';
|
|
|
form.remark = '';
|
|
form.remark = '';
|
|
|
- certificateList.value = [];
|
|
|
|
|
|
|
+ certificateList.value = [createEmptyCertificate()];
|
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
|
- personFormRef.value?.resetFields();
|
|
|
|
|
|
|
+ personFormRef.value?.clearValidate();
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => form.insuranceStatus,
|
|
|
|
|
+ (val) => {
|
|
|
|
|
+ if (val === '0') {
|
|
|
|
|
+ form.insuranceStartDate = '';
|
|
|
|
|
+ form.insuranceEndDate = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
const handleQuery = () => {
|
|
const handleQuery = () => {
|
|
|
queryParams.pageNum = 1;
|
|
queryParams.pageNum = 1;
|
|
|
getList();
|
|
getList();
|
|
@@ -427,96 +453,164 @@ const handleQuery = () => {
|
|
|
|
|
|
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
|
queryFormRef.value?.resetFields();
|
|
queryFormRef.value?.resetFields();
|
|
|
- handleQuery();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const handleSelectionChange = (selection: PersonInfoVO[]) => {
|
|
|
|
|
- ids.value = selection.map((item) => item.personId);
|
|
|
|
|
- single.value = selection.length !== 1;
|
|
|
|
|
- multiple.value = !selection.length;
|
|
|
|
|
|
|
+ queryParams.pageNum = 1;
|
|
|
|
|
+ queryParams.personName = undefined;
|
|
|
|
|
+ queryParams.personType = undefined;
|
|
|
|
|
+ queryParams.workStoreId = undefined;
|
|
|
|
|
+ getList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleAdd = () => {
|
|
const handleAdd = () => {
|
|
|
|
|
+ dialogMode.value = 'add';
|
|
|
reset();
|
|
reset();
|
|
|
open.value = true;
|
|
open.value = true;
|
|
|
- title.value = '新增人员';
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleUpdate = async (row?: PersonInfoVO) => {
|
|
|
|
|
|
|
+const handleUpdate = async (row: PersonInfoVO) => {
|
|
|
|
|
+ dialogMode.value = 'edit';
|
|
|
reset();
|
|
reset();
|
|
|
- const personId = row?.personId || ids.value[0];
|
|
|
|
|
- const res = await getPersonInfo(personId);
|
|
|
|
|
- Object.assign(form, res.data);
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- certificateList.value = JSON.parse(form.certificates || '[]');
|
|
|
|
|
- } catch {
|
|
|
|
|
- certificateList.value = [];
|
|
|
|
|
|
|
+ const res: any = await getPersonInfo(row.personId);
|
|
|
|
|
+ const data: any = res.data || res;
|
|
|
|
|
+ Object.assign(form, data);
|
|
|
|
|
+
|
|
|
|
|
+ form.insuranceStartDate = data.insuranceStartDate || '';
|
|
|
|
|
+ form.insuranceEndDate = data.insuranceEndDate || data.insuranceExpireDate || '';
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const parsed = typeof data.certificates === 'string' ? JSON.parse(data.certificates || '[]') : data.certificates || [];
|
|
|
|
|
+ certificateList.value = Array.isArray(parsed) && parsed.length > 0 ? parsed : [createEmptyCertificate()];
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ certificateList.value = [createEmptyCertificate()];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ open.value = true;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取人员详情失败', error);
|
|
|
}
|
|
}
|
|
|
- open.value = true;
|
|
|
|
|
- title.value = '修改人员';
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const submitForm = () => {
|
|
const submitForm = () => {
|
|
|
personFormRef.value?.validate(async (valid: boolean) => {
|
|
personFormRef.value?.validate(async (valid: boolean) => {
|
|
|
- if (valid) {
|
|
|
|
|
- form.certificates = JSON.stringify(certificateList.value);
|
|
|
|
|
- if (form.workStoreId && deptOptions.value.length > 0) {
|
|
|
|
|
- const dept = findDeptById(deptOptions.value, String(form.workStoreId));
|
|
|
|
|
- if (dept) form.workStoreName = dept.label;
|
|
|
|
|
|
|
+ if (!valid) return;
|
|
|
|
|
+
|
|
|
|
|
+ const certValid = certificateList.value.length > 0 && certificateList.value.every((item) => item.certType && item.certUrl);
|
|
|
|
|
+ if (!certValid) {
|
|
|
|
|
+ proxy?.$modal.msgError('请完善岗位证书信息');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const dept = form.workStoreId ? findDeptById(deptOptions.value, String(form.workStoreId)) : null;
|
|
|
|
|
+ form.workStoreName = dept?.label || '';
|
|
|
|
|
+
|
|
|
|
|
+ const payload: any = {
|
|
|
|
|
+ ...form,
|
|
|
|
|
+ insuranceExpireDate: form.insuranceEndDate || '',
|
|
|
|
|
+ certificates: JSON.stringify(
|
|
|
|
|
+ certificateList.value.map((item) => ({
|
|
|
|
|
+ certType: item.certType,
|
|
|
|
|
+ certTypeName: getCertTypeLabel(item.certType),
|
|
|
|
|
+ certUrl: item.certUrl,
|
|
|
|
|
+ certName: item.certName || getFileNameFromUrl(item.certUrl)
|
|
|
|
|
+ }))
|
|
|
|
|
+ )
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (payload.insuranceStatus === '0') {
|
|
|
|
|
+ payload.insuranceStartDate = '';
|
|
|
|
|
+ payload.insuranceEndDate = '';
|
|
|
|
|
+ payload.insuranceExpireDate = '';
|
|
|
}
|
|
}
|
|
|
- if (form.personId !== undefined) {
|
|
|
|
|
- await updatePersonInfo(form);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (payload.personId !== undefined) {
|
|
|
|
|
+ await updatePersonInfo(payload);
|
|
|
proxy?.$modal.msgSuccess('修改成功');
|
|
proxy?.$modal.msgSuccess('修改成功');
|
|
|
} else {
|
|
} else {
|
|
|
- await addPersonInfo(form);
|
|
|
|
|
|
|
+ await addPersonInfo(payload);
|
|
|
proxy?.$modal.msgSuccess('新增成功');
|
|
proxy?.$modal.msgSuccess('新增成功');
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
open.value = false;
|
|
open.value = false;
|
|
|
await getList();
|
|
await getList();
|
|
|
- await loadStats();
|
|
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('提交失败', error);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleDelete = async (row?: PersonInfoVO) => {
|
|
|
|
|
- const personIds = row?.personId || ids.value;
|
|
|
|
|
- await proxy?.$modal.confirm('是否确认删除姓名为"' + (row?.personName || '') + '"的数据项?');
|
|
|
|
|
- await delPersonInfo(personIds);
|
|
|
|
|
- await getList();
|
|
|
|
|
- await loadStats();
|
|
|
|
|
- proxy?.$modal.msgSuccess('删除成功');
|
|
|
|
|
|
|
+const cancel = () => {
|
|
|
|
|
+ open.value = false;
|
|
|
|
|
+ reset();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleDelete = async (row: PersonInfoVO) => {
|
|
|
|
|
+ await proxy?.$modal.confirm('是否确认删除姓名为"' + (row.personName || '') + '"的数据项?');
|
|
|
|
|
+ try {
|
|
|
|
|
+ await delPersonInfo(row.personId);
|
|
|
|
|
+ proxy?.$modal.msgSuccess('删除成功');
|
|
|
|
|
+ await getList();
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('删除失败', error);
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const calcAge = () => {
|
|
const calcAge = () => {
|
|
|
if (!form.idCard || form.idCard.length !== 18) return;
|
|
if (!form.idCard || form.idCard.length !== 18) return;
|
|
|
- const year = parseInt(form.idCard.substring(6, 10));
|
|
|
|
|
|
|
+ const year = parseInt(form.idCard.substring(6, 10), 10);
|
|
|
const currentYear = new Date().getFullYear();
|
|
const currentYear = new Date().getFullYear();
|
|
|
form.age = currentYear - year;
|
|
form.age = currentYear - year;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const addCertificate = () => {
|
|
const addCertificate = () => {
|
|
|
- certificateList.value.push({ certType: '', certUrl: '' });
|
|
|
|
|
|
|
+ certificateList.value.push(createEmptyCertificate());
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const removeCertificate = (index: number) => {
|
|
const removeCertificate = (index: number) => {
|
|
|
certificateList.value.splice(index, 1);
|
|
certificateList.value.splice(index, 1);
|
|
|
|
|
+ if (certificateList.value.length === 0) {
|
|
|
|
|
+ certificateList.value.push(createEmptyCertificate());
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getUploadUrl = (res: any) => {
|
|
|
|
|
+ return res?.data?.url || res?.url || res?.fileName || '';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const beforeCertUpload = (file: File) => {
|
|
|
|
|
+ const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png'];
|
|
|
|
|
+ if (!allowedTypes.includes(file.type)) {
|
|
|
|
|
+ proxy?.$modal.msgError('仅支持 PDF、JPG、PNG 格式的文件');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ const isLt10M = file.size / 1024 / 1024 < 10;
|
|
|
|
|
+ if (!isLt10M) {
|
|
|
|
|
+ proxy?.$modal.msgError('文件大小不能超过 10MB');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const handleCertUploadSuccess = (res: any, index: number) => {
|
|
|
|
|
- if (res.code === 200) {
|
|
|
|
|
- certificateList.value[index].certUrl = res.url || res.fileName;
|
|
|
|
|
- proxy?.$modal.msgSuccess('上传成功');
|
|
|
|
|
- } else {
|
|
|
|
|
- proxy?.$modal.msgError(res.msg || '上传失败');
|
|
|
|
|
|
|
+const handleCertUploadSuccess = (res: any, file: any, index: number) => {
|
|
|
|
|
+ const url = getUploadUrl(res);
|
|
|
|
|
+ if (!url) {
|
|
|
|
|
+ proxy?.$modal.msgError(res?.msg || '上传失败');
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if (!certificateList.value[index]) return;
|
|
|
|
|
+ certificateList.value[index].certUrl = url;
|
|
|
|
|
+ certificateList.value[index].certName = file?.name || getFileNameFromUrl(url);
|
|
|
|
|
+ proxy?.$modal.msgSuccess('上传成功');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const viewCertificates = (row: PersonInfoVO) => {
|
|
const viewCertificates = (row: PersonInfoVO) => {
|
|
|
try {
|
|
try {
|
|
|
- currentCerts.value = JSON.parse(row.certificates || '[]');
|
|
|
|
|
|
|
+ const parsed = typeof row.certificates === 'string' ? JSON.parse(row.certificates || '[]') : row.certificates || [];
|
|
|
|
|
+ currentCerts.value = Array.isArray(parsed) ? parsed : [];
|
|
|
currentCerts.value.forEach((cert: any) => {
|
|
currentCerts.value.forEach((cert: any) => {
|
|
|
- const dict = certificate_type_options.value.find((d: any) => d.value === cert.certType);
|
|
|
|
|
- if (dict) cert.certTypeName = dict.label;
|
|
|
|
|
|
|
+ cert.certTypeName = getCertTypeLabel(cert.certType);
|
|
|
});
|
|
});
|
|
|
} catch {
|
|
} catch {
|
|
|
currentCerts.value = [];
|
|
currentCerts.value = [];
|
|
@@ -536,13 +630,35 @@ const getCertCount = (certs: string) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const getCertNames = (certs: string) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const list = JSON.parse(certs || '[]');
|
|
|
|
|
+ if (!Array.isArray(list) || !list.length) return '-';
|
|
|
|
|
+ return list
|
|
|
|
|
+ .map((item: any) => getCertTypeLabel(item.certType) || item.certType || '')
|
|
|
|
|
+ .filter(Boolean)
|
|
|
|
|
+ .join('、');
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ return '-';
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getFileNameFromUrl = (url: string) => {
|
|
|
|
|
+ if (!url) return '未知文件';
|
|
|
|
|
+ const parts = url.split('/');
|
|
|
|
|
+ return parts[parts.length - 1] || '未知文件';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getCertTypeLabel = (type: string) => {
|
|
|
|
|
+ const dict = certificate_type_options.value.find((d: any) => d.value === type);
|
|
|
|
|
+ return dict?.label || type || '';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const maskIdCard = (idCard: string) => {
|
|
const maskIdCard = (idCard: string) => {
|
|
|
- if (!idCard || idCard.length < 8) return idCard;
|
|
|
|
|
|
|
+ if (!idCard || idCard.length < 8) return idCard || '-';
|
|
|
return idCard.substring(0, 4) + '**********' + idCard.substring(idCard.length - 4);
|
|
return idCard.substring(0, 4) + '**********' + idCard.substring(idCard.length - 4);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-type TagType = 'success' | 'warning' | 'danger' | 'info' | 'primary';
|
|
|
|
|
-
|
|
|
|
|
const getPersonTypeType = (type: string): TagType => {
|
|
const getPersonTypeType = (type: string): TagType => {
|
|
|
const map: Record<string, TagType> = {
|
|
const map: Record<string, TagType> = {
|
|
|
'防损人员': 'danger',
|
|
'防损人员': 'danger',
|
|
@@ -552,26 +668,29 @@ const getPersonTypeType = (type: string): TagType => {
|
|
|
return map[type] || 'info';
|
|
return map[type] || 'info';
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const getInsuranceType = (row: PersonInfoVO): TagType => {
|
|
|
|
|
- if (row.insuranceStatus === '0') return 'danger';
|
|
|
|
|
- if (isExpiringSoon(row.insuranceExpireDate)) return 'warning';
|
|
|
|
|
- return 'success';
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-const getInsuranceLabel = (row: PersonInfoVO): string => {
|
|
|
|
|
- if (row.insuranceStatus === '0') return '未投保';
|
|
|
|
|
- if (isExpiringSoon(row.insuranceExpireDate)) return '即将到期';
|
|
|
|
|
- return '已投保';
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
const isExpiringSoon = (dateStr?: string) => {
|
|
const isExpiringSoon = (dateStr?: string) => {
|
|
|
if (!dateStr) return false;
|
|
if (!dateStr) return false;
|
|
|
const expire = new Date(dateStr);
|
|
const expire = new Date(dateStr);
|
|
|
|
|
+ if (Number.isNaN(expire.getTime())) return false;
|
|
|
const now = new Date();
|
|
const now = new Date();
|
|
|
const diff = expire.getTime() - now.getTime();
|
|
const diff = expire.getTime() - now.getTime();
|
|
|
return diff > 0 && diff <= 30 * 24 * 60 * 60 * 1000;
|
|
return diff > 0 && diff <= 30 * 24 * 60 * 60 * 1000;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const getInsuranceType = (row: any): TagType => {
|
|
|
|
|
+ if (row.insuranceStatus === '0') return 'danger';
|
|
|
|
|
+ const endDate = row.insuranceEndDate || row.insuranceExpireDate;
|
|
|
|
|
+ if (isExpiringSoon(endDate)) return 'warning';
|
|
|
|
|
+ return 'success';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getInsuranceLabel = (row: any): string => {
|
|
|
|
|
+ if (row.insuranceStatus === '0') return '未投保';
|
|
|
|
|
+ const endDate = row.insuranceEndDate || row.insuranceExpireDate;
|
|
|
|
|
+ if (isExpiringSoon(endDate)) return '即将到期';
|
|
|
|
|
+ return '已投保';
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const findDeptById = (depts: any[], id: string): any => {
|
|
const findDeptById = (depts: any[], id: string): any => {
|
|
|
for (const dept of depts) {
|
|
for (const dept of depts) {
|
|
|
if (String(dept.id) === id) return dept;
|
|
if (String(dept.id) === id) return dept;
|
|
@@ -585,8 +704,226 @@ const findDeptById = (depts: any[], id: string): any => {
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
getList();
|
|
getList();
|
|
|
- loadStats();
|
|
|
|
|
getDeptTree();
|
|
getDeptTree();
|
|
|
getDicts();
|
|
getDicts();
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.person-page {
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ background: #f5f7fb;
|
|
|
|
|
+ min-height: calc(100vh - 50px);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.page-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.page-title {
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #1f2937;
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.page-desc {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #6b7280;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-card,
|
|
|
|
|
+.table-card {
|
|
|
|
|
+ border-radius: 14px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-form {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-actions {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.list-toolbar {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin: 6px 0 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.list-total {
|
|
|
|
|
+ color: #64748b;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.person-dialog {
|
|
|
|
|
+ :deep(.el-dialog__header) {
|
|
|
|
|
+ margin-right: 0;
|
|
|
|
|
+ padding: 16px 20px 10px;
|
|
|
|
|
+ border-bottom: 1px solid #eef2f7;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-dialog__body) {
|
|
|
|
|
+ padding: 18px 20px 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-dialog__footer) {
|
|
|
|
|
+ padding: 14px 20px 20px;
|
|
|
|
|
+ border-top: 1px solid #eef2f7;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialog-banner {
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialog-banner-title {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ color: #2563eb;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialog-banner-icon {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ width: 18px;
|
|
|
|
|
+ height: 18px;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialog-banner-desc {
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+ color: #64748b;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.required-star {
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #1f2937;
|
|
|
|
|
+ padding: 10px 0 12px;
|
|
|
|
|
+ border-bottom: 1px solid #eef2f7;
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.person-form {
|
|
|
|
|
+ :deep(.el-form-item) {
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-form-item__label) {
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #374151;
|
|
|
|
|
+ padding-bottom: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-input__wrapper),
|
|
|
|
|
+ :deep(.el-select__wrapper),
|
|
|
|
|
+ :deep(.el-textarea__inner) {
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-form-item.is-required > .el-form-item__label:before) {
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.certificate-section {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.certificate-row {
|
|
|
|
|
+ padding: 12px 0;
|
|
|
|
|
+ border-bottom: 1px dashed #e5e7eb;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.certificate-row:last-child {
|
|
|
|
|
+ border-bottom: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cert-file-group {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cert-file-input {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cert-file-group :deep(.el-input__wrapper) {
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cert-file-group .el-upload {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.add-cert-box {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+ width: 30%;
|
|
|
|
|
+ min-width: 220px;
|
|
|
|
|
+ height: 44px;
|
|
|
|
|
+ border: 1px dashed #cbd5e1;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ color: #2563eb;
|
|
|
|
|
+ background: #f8fbff;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.add-cert-box:hover {
|
|
|
|
|
+ border-color: #93c5fd;
|
|
|
|
|
+ background: #eff6ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.add-cert-plus {
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.dialog-footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.cert-item {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-button.is-link) {
|
|
|
|
|
+ padding-left: 0;
|
|
|
|
|
+ padding-right: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-input-group__append) {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|