caolin 3 weeks ago
commit 29ee948cbb
  1. 42
      src/api/kpi/index.js
  2. 41
      src/api/kpi/score.js
  3. 61
      src/views/Basic/WxRobot/Setting.vue
  4. 113
      src/views/Kpi/Appraise/Components/DialogAppraise.vue
  5. 77
      src/views/Kpi/Appraise/index.vue
  6. 167
      src/views/Kpi/Score/index.vue

@ -0,0 +1,42 @@
import request from '@/config/axios'
// 创建
export const createKpi = (data) => {
return request.post({
url: '/admin-api/oa/examine-configuration/create',
data,
isSubmitForm: true
})
}
// 修改
export const updateKpi = (data) => {
return request.put({ url: '/admin-api/oa/examine-configuration/update', data })
}
// 修改状态
export const updateKpiStatus = (data) => {
return request.put({ url: '/admin-api/oa/examine-configuration/status/update', data })
}
// 分页
export const getKpiPage = (params) => {
return request.get({ url: '/admin-api/oa/examine-configuration/page', params })
}
// 详情
export const getKpiDetail = (id) => {
return request.get({ url: '/admin-api/oa/examine-configuration/get', params: { id } })
}
// 删除
export const deleteKpi = (id) => {
return request.delete({ url: '/admin-api/oa/examine-configuration/delete', params: { id } })
}
//
export const getKpiEmployees = () => {
return request.get({
url: '/admin-api/oa/employee/examinedEmployees'
})
}

@ -0,0 +1,41 @@
import request from '@/config/axios'
// 打分数据
export const getKpiEmployees = (params) => {
return request.get({ url: '/admin-api/oa/examine-score/getExamineUserExamineScore', params })
}
// 保存考勤打分数据
export const saveKpiScore = (data) => {
return request.put({ url: '/admin-api/oa/examine-score/update', data })
}
// 删除考核项
export const deleteKpiItem = (params) => {
return request.delete({ url: '/admin-api/oa/examine-score/delete', params })
}
// 考评分页数据
export const getScorePage = (params) => {
return request.get({ url: '/admin-api/oa/examine-period/page', params })
}
// 封存
export const saveScoreRecord = (params) => {
return request.get({ url: '/admin-api/oa/examine-period/confirm', params })
}
// 查询周期是否已封存
export const getKpiPeriodStatus = (params) => {
return request.get({ url: '/admin-api/oa/examine-period/getExaminePeriodByPeriod', params })
}
// 查询考核记录
export const getScoreRecord = (data) => {
return request.post({ url: '/admin-api/oa/examine-result/periodPage', data })
}
// 查详情
export const getScoreDetail = (params) => {
return request.get({ url: '/admin-api/oa/examine-result/detail', params })
}

@ -10,23 +10,28 @@
</el-checkbox-group>
</template>
</el-table-column>
<el-table-column label="微信群名称" min-width="150">
<el-table-column label="群二维码" width="100">
<template #default="{ row }">
<el-input
v-if="row.sendType.includes('微信群')"
v-model="row.wxGroup"
size="small"
clearable
@input="rowChange(row)"
/>
<span v-else>{{ row.wxGroup }}</span>
</template>
</el-table-column>
<el-table-column label="群二维码" width="90">
<template #default="{ row }">
<img :src="row.wxCode" width="80px" height="80px" alt="" />
<el-upload
ref="rewardFile"
action="#"
:limit="1"
accept=".jpg,.jpeg,.png"
:disabled="loading"
:show-file-list="false"
:before-upload="fileBeforeUpload"
:http-request="(data) => rewardUpload(data, row.id)"
>
<el-button type="primary" size="small">
<Icon icon="ep:upload" class="mr-5px" size="small" /> 点击上传
</el-button>
</el-upload>
<!-- <UploadImg v-model="row.wxQrCode" /> -->
<!-- <img :src="row.wxCode" width="80px" height="80px" alt="" /> -->
</template>
</el-table-column>
<el-table-column label="微信群名称" prop="wxGroup" min-width="150" />
<el-table-column label="操作" width="100">
<template #default="{ row }">
<!-- <div>
@ -190,6 +195,34 @@ async function submitSendTime() {
}
}
}
const fileBeforeUpload = (file) => {
let format = '.' + file.name.split('.')[1]
if (!['.jpg', '.png', 'jpeg'].includes(format)) {
message.error(`请上传指定格式".jpg,.jpeg,.png"文件`)
return false
}
let isRightSize = file.size / 1024 / 1024 < 20
if (!isRightSize) {
message.error('文件大小超过 20MB')
}
return isRightSize
}
const fd = ref(new FormData())
async function rewardUpload(data, id) {
fd.value.append('file', data.file)
fd.value.append('id', id)
//
try {
//
setTimeout(() => {
getTableList()
}, 2000)
} finally {
}
}
</script>
<style lang="scss" scoped></style>

@ -1,31 +1,58 @@
<template>
<Dialog :title="title" v-model="show" width="800px">
<el-form v-loading="formLoading" :model="form" ref="formRef" :rules="rules" label-width="80px">
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="考核指标" prop="examineTarget">
<el-input v-model="form.examineTarget" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="生效日期" prop="effectiveDate">
<el-date-picker
v-model="form.effectiveDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="选择日期时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="考核指标" prop="name">
<el-input v-model="form.name" placeholder="请输入" />
<el-form-item label="模式" prop="type">
<el-radio-group v-model="form.type">
<el-radio
v-for="(item, index) in prop.kpiModeOoptions"
:key="index"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="权重" prop="rate">
<el-input-number v-model="form.rate" :min="0" :step="1" :controls="false" />
<el-form-item label="权重" prop="weight">
<el-input-number v-model="form.weight" :min="0" :step="1" :controls="false" />
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="评分上限" prop="maxScore">
<el-input-number v-model="form.maxScore" :min="0" :step="1" :controls="false" />
<el-form-item label="评分上限" prop="examineScore">
<el-input-number v-model="form.examineScore" :min="0" :step="1" :controls="false" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="考核内容" prop="kaoheneirong">
<el-form-item label="考核内容" prop="examineContent">
<Editor
v-model:modelValue="form.kaoheneirong"
v-model:modelValue="form.examineContent"
height="150px"
:toolbarConfig="{
toolbarKeys: []
@ -37,9 +64,9 @@
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="考核规则" prop="kaoheguize">
<el-form-item label="考核规则" prop="examineRule">
<Editor
v-model:modelValue="form.kaoheguize"
v-model:modelValue="form.examineRule"
height="150px"
:toolbarConfig="{ toolbarKeys: [] }"
style="width: 100%"
@ -49,7 +76,7 @@
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="考核员工" prop="checkEmployees">
<el-form-item label="考核员工" prop="examinedUserIdList">
<div>
<el-checkbox
v-model="checkAll"
@ -58,10 +85,11 @@
>
全选
</el-checkbox>
<el-checkbox-group v-model="form.checkEmployees" @change="handleCheckedChange">
<el-checkbox-group v-model="form.examinedUserIdList" @change="handleCheckedChange">
<el-checkbox
v-for="item in employeeOptions"
:key="item.id"
:disabled="item.status == 1"
:label="item.id"
:value="item.id"
>
@ -83,7 +111,14 @@
</template>
<script setup name="DialogAppraise">
import * as ResourceApi from '@/api/system/library/resource'
import * as KpiApi from '@/api/kpi/index.js'
const prop = defineProps({
kpiModeOoptions: {
type: Array,
default: () => []
}
})
const { t } = useI18n() //
const message = useMessage() //
@ -95,9 +130,10 @@ const formType = ref('create')
const form = ref({})
const formLoading = ref(false)
const rules = ref({
name: { required: true, message: '标题不可为空', trigger: 'blur' },
kaoheneirong: { required: true, message: '标题不可为空', trigger: 'blur' },
kaoheguize: { required: true, message: '标题不可为空', trigger: 'blur' }
examineTarget: { required: true, message: '标题不可为空', trigger: 'blur' },
effectiveDate: { required: true, message: '生效日期不可为空', trigger: 'blur,change' },
examineContent: { required: true, message: '标题不可为空', trigger: 'blur' },
examineRule: { required: true, message: '标题不可为空', trigger: 'blur' }
})
async function open(type, val) {
@ -105,28 +141,36 @@ async function open(type, val) {
title.value = type == 'update' ? '修改考核项' : '新增考核项'
formType.value = type
resetForm()
if (val?.id) {
if (val) {
formLoading.value = true
try {
form.value = await ResourceApi.getResource(id)
form.value = await KpiApi.getKpiDetail(val)
} finally {
formLoading.value = false
}
} else if (val.name) {
form.value = { ...val }
}
const checkedCount = form.value.checkEmployees?.length || 0
getOptions()
const checkedCount = form.value.examinedUserIdList?.length || 0
isIndeterminate.value = checkedCount > 0 && checkedCount < employeeOptions.value.length
}
function getOptions() {
KpiApi.getKpiEmployees().then((data) => {
employeeOptions.value = data
handleCheckAllChange(true)
checkAll.value = true
})
}
function resetForm() {
form.value = {
name: '',
rate: 0,
kaoheneirong: ``,
kaoheguize: ``,
maxScore: 5,
checkEmployees: []
examineTarget: '',
type: '3',
weight: 0,
examineContent: ``,
examineRule: ``,
examineScore: 5,
examinedUserIdList: []
}
}
@ -145,15 +189,15 @@ async function handleSave() {
formLoading.value = true
try {
if (formType.value === 'create') {
// await ResourceApi.createResource(form.value)
await KpiApi.createKpi(form.value)
message.success(t('common.createSuccess'))
} else {
// await ResourceApi.updateResource(form.value)
await KpiApi.updateKpi(form.value)
message.success(t('common.updateSuccess'))
}
show.value = false
//
emit('success', form.value)
emit('success')
} finally {
formLoading.value = false
}
@ -161,15 +205,10 @@ async function handleSave() {
const checkAll = ref(false)
const isIndeterminate = ref(false)
const employeeOptions = ref([
{ id: 1, name: '武大郎' },
{ id: 2, name: '李二郎' },
{ id: 3, name: '拼命三郎' },
{ id: 4, name: '杨四郎' }
])
const employeeOptions = ref([])
function handleCheckAllChange(val) {
form.value.checkEmployees = val ? employeeOptions.value.map((it) => it.id) : []
form.value.examinedUserIdList = val ? employeeOptions.value.map((it) => it.id) : []
isIndeterminate.value = false
}

@ -3,7 +3,7 @@
<!-- 搜索工作栏 -->
<el-form :model="searchForm" ref="queryFormRef" inline label-width="0">
<el-form-item>
<el-input v-model="searchForm.name" placeholder="请输入考核指标" clearable />
<el-input v-model="searchForm.examineTarget" placeholder="请输入考核指标" clearable />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery" v-hasPermi="['kpi:appraise:search']"> 搜索</el-button>
@ -21,30 +21,32 @@
<!-- 列表 -->
<el-table v-loading="loading" :data="list" border>
<el-table-column prop="name" label="考核指标" width="180" />
<el-table-column prop="rate" label="权重%" width="90" />
<el-table-column prop="examineTarget" label="考核指标" width="180" />
<el-table-column prop="typeName" label="分值模式" width="90" :formatter="getKpiModeLabel" />
<el-table-column prop="weight" label="权重%" width="90" />
<el-table-column label="考核内容">
<template #default="{ row }">
<div v-dompurify-html="row.kaoheneirong"></div>
<div v-dompurify-html="row.examineContent"></div>
</template>
</el-table-column>
<el-table-column label="考核规则" prop="kaoheguize">
<el-table-column label="考核规则" prop="examineRule">
<template #default="{ row }">
<div v-dompurify-html="row.kaoheguize"></div>
<div v-dompurify-html="row.examineRule"></div>
</template>
</el-table-column>
<el-table-column label="评分上限" prop="maxScore" width="90" />
<el-table-column label="评分上限" prop="examineScore" width="90" />
<el-table-column label="考核人数" width="90">
<template #default="{ row }">
{{ row.checkEmployees?.length || 0 }}
{{ row.examinedUserIdList?.length || 0 }}
</template>
</el-table-column>
<el-table-column label="生效日期" prop="effectiveDate" width="120" />
<el-table-column label="操作" class-name="fixed-width" width="120">
<template #default="{ row }">
<el-button
link
type="primary"
@click="openForm('update', row)"
@click="openForm('update', row.id)"
v-hasPermi="['kpi:appraise:update']"
>
修改
@ -67,18 +69,22 @@
@pagination="getList"
/>
<DialogAppraise ref="formRef" @success="handleQuery" />
<DialogAppraise ref="formRef" :kpiModeOoptions="kpiModeOoptions" @success="handleQuery" />
</div>
</template>
<script setup name="KpiContent">
import { removeNullField } from '@/utils'
import DialogAppraise from './Components/DialogAppraise.vue'
import * as KpiApi from '@/api/kpi/index.js'
import { getGeneralSysDictData } from '@/api/system/dict/dict.data'
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref() //
const list = ref([]) //
const searchForm = ref({
name: undefined,
examineTarget: undefined,
pageNo: 1,
pageSize: 20
})
@ -86,48 +92,51 @@ const total = ref(0)
/** 初始化 **/
onMounted(() => {
getOptions()
handleQuery()
})
const getList = async (info) => {
const kpiModeOoptions = ref([])
function getOptions() {
getGeneralSysDictData('examine_type').then((data) => {
kpiModeOoptions.value = data
})
}
function getKpiModeLabel(row) {
return kpiModeOoptions.value.find((it) => it.value == row.type)?.label
}
const getList = async () => {
loading.value = true
try {
const data = [
{
name: '纳税申报',
rate: 35,
kaoheneirong:
'<p>1、每个月13号之前在柠檬云财税把上个月的财务数据录入系统出具财务报表;</p><p>2、社保和公积金及时增减人员,做好核定工作,及时申报缴纳;</p><p>3、按照税务局规定时间把自己所负责的公司增指税、附加税、所得税以及其他税种申报完成</p>',
kaoheguize: `当天处理完今日线索,连续2天未处理完,一次扣2分,最多扣5分`,
maxScore: 5,
checkEmployees: [1, 3, 4]
}
]
if (info) {
data.push(info)
}
list.value = data
const data = await KpiApi.getKpiPage(removeNullField(searchForm.value))
list.value = data.list
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = (info) => {
const handleQuery = () => {
searchForm.value.pageNo = 1
getList(info)
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
searchForm.value = {
examineTarget: undefined,
pageNo: 1,
pageSize: 20
}
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type, row) => {
formRef.value.open(type, row)
const openForm = (type, id) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
@ -136,7 +145,7 @@ const handleDelete = async (id) => {
//
await message.delConfirm()
//
await DeptApi.deleteDept(id)
await KpiApi.deleteKpi(id)
message.success(t('common.delSuccess'))
//
await getList()

@ -4,7 +4,7 @@
<el-form :model="searchForm" inline label-width="0">
<el-form-item>
<el-date-picker
v-model="searchForm.peroid"
v-model="searchForm.period"
type="month"
format="YYYY-MM"
value-format="YYYY-MM"
@ -16,15 +16,20 @@
<el-button @click="getList" v-hasPermi="['kpi:score:search']"> 搜索</el-button>
</el-form-item>
</el-form>
<el-tabs v-model="searchForm.employeeId" @tab-click="getList">
<el-tabs v-model="employeeId" @tab-click="getList">
<el-tab-pane
v-for="item in employeeOptions"
:key="item.id"
:label="item.name"
:name="item.id"
:key="item.examinedUser"
:name="item.examinedUser"
>
<template #label>
<span>{{ item.examinedUserName }}</span>
<el-badge :is-dot="item.notExaminedCount > 0" type="danger" />
</template>
<div class="mb-10px">
<el-tag v-if="peroidSaved" type="danger"> 当前周期已封存不可修改当月绩效考核 </el-tag>
<el-tag v-if="periodSaved == 1" type="danger">
当前周期已封存不可修改当月绩效考核
</el-tag>
<el-tag v-else type="warning">
当前周期未封存可选择修改当月绩效考核两秒内不操作将自动保存考核结果
</el-tag>
@ -37,27 +42,28 @@
show-summary
:summary-method="getSummaries"
>
<el-table-column prop="name" label="考核指标" width="180" />
<el-table-column prop="rate" label="权重%" width="90" />
<el-table-column prop="examineTarget" label="考核指标" width="180" />
<el-table-column prop="typeName" label="分值模式" width="90" />
<el-table-column prop="weight" label="权重%" width="90" />
<el-table-column label="考核内容">
<template #default="{ row }">
<div v-dompurify-html="row.kaoheneirong"></div>
<div v-dompurify-html="row.examineContent"></div>
</template>
</el-table-column>
<el-table-column label="考核规则" prop="kaoheguize">
<el-table-column label="考核规则" prop="examineRule">
<template #default="{ row }">
<div v-dompurify-html="row.kaoheguize"></div>
<div v-dompurify-html="row.examineRule"></div>
</template>
</el-table-column>
<el-table-column label="评分上限" prop="maxScore" width="90" />
<el-table-column label="评分上限" prop="examineScore" width="90" />
<el-table-column label="考核评分" width="120">
<template #default="{ row }">
<div v-if="peroidSaved">{{ row.score }}</div>
<div v-if="periodSaved == 1">{{ row.score }}</div>
<el-input-number
v-else
v-model="row.score"
:min="0"
:max="row.maxScore"
:max="row.examineScore"
:step="1"
:controls="false"
size="small"
@ -68,7 +74,7 @@
</el-table-column>
<el-table-column label="评分备注" width="200">
<template #default="{ row }">
<div v-if="peroidSaved">{{ row.remark }}</div>
<div v-if="periodSaved == 1">{{ row.remark }}</div>
<el-input
v-else
v-model="row.remark"
@ -80,7 +86,14 @@
/>
</template>
</el-table-column>
<el-table-column label="加权得分" prop="jiaquandefen" width="90" />
<el-table-column label="加权得分" prop="weightSocre" width="90" />
<el-table-column label="操作" fixed="right" width="100">
<template #default="{ row }">
<el-button style="padding: 0" type="danger" text @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
@ -89,84 +102,52 @@
<script setup name="KpiScore">
import { formatDate } from '@/utils/formatTime'
import * as KpiApi from '@/api/kpi/score.js'
const { t } = useI18n() //
const message = useMessage() //
const loading = ref(false) //
const list = ref() //
const list = ref([]) //
const searchForm = ref({
peroid: undefined,
employeeId: undefined
period: undefined
})
const peroidSaved = ref(true)
const periodSaved = ref(0)
const employeeOptions = ref([
{ id: 1, name: '武大郎' },
{ id: 2, name: '李二郎' },
{ id: 3, name: '拼命三郎' },
{ id: 4, name: '杨四郎' }
// { id: 11, name: '' },
// { id: 21, name: '' },
// { id: 31, name: '' },
// { id: 41, name: '' },
// { id: 12, name: '' },
// { id: 22, name: '' },
// { id: 32, name: '' },
// { id: 42, name: '' },
// { id: 13, name: '' },
// { id: 23, name: '' },
// { id: 33, name: '' },
// { id: 43, name: '' }
])
const employeeOptions = ref([])
const employeeId = ref('')
onMounted(() => {
//
//
const today = new Date()
searchForm.value.peroid = formatDate(
searchForm.value.period = formatDate(
new Date(today.getFullYear(), today.getMonth() - 1),
'YYYY-MM'
)
if (employeeOptions.value.length > 0) {
searchForm.value.employeeId = employeeOptions.value[0].id
}
KpiApi.getKpiPeriodStatus({ period: searchForm.value.period }).then((data) => {
periodSaved.value = data.status
})
getList()
})
async function getList() {
if (!searchForm.value.employeeId) {
message.error('当月无待考核员工!!!')
return
}
loading.value = true
try {
const data = [
{
name: '纳税申报',
rate: 40,
kaoheneirong:
'<p>1、每个月13号之前在柠檬云财税把上个月的财务数据录入系统出具财务报表;</p><p>2、社保和公积金及时增减人员,做好核定工作,及时申报缴纳;</p><p>3、按照税务局规定时间把自己所负责的公司增指税、附加税、所得税以及其他税种申报完成</p>',
kaoheguize: `当天处理完今日线索,连续2天未处理完,一次扣2分,最多扣5分`,
maxScore: 20,
score: 10,
checkEmployees: [1, 3, 4],
jiaquandefen: 4
},
{
name: '数据统计',
rate: 20,
kaoheneirong:
'<p>1、每个月13号之前在柠檬云财税把上个月的财务数据录入系统出具财务报表;</p><p>2、社保和公积金及时增减人员,做好核定工作,及时申报缴纳;</p><p>3、按照税务局规定时间把自己所负责的公司增指税、附加税、所得税以及其他税种申报完成</p>',
kaoheguize: `当天处理完今日线索,连续2天未处理完,一次扣2分,最多扣5分`,
maxScore: 10,
score: 8,
checkEmployees: [1, 3, 4],
jiaquandefen: 1.6
//
const arr = await KpiApi.getKpiEmployees(searchForm.value)
employeeOptions.value = arr || []
if (employeeOptions.value.length > 0) {
if (!employeeId.value) {
employeeId.value = employeeOptions.value[0].examinedUser
}
]
list.value = data
peroidSaved.value = !peroidSaved.value
} else {
message.error('当月无待考核员工!!!')
return
}
list.value =
employeeOptions.value.find((it) => it.examinedUser == employeeId.value)
?.examineScoreRespVOList || []
} finally {
loading.value = false
}
@ -174,11 +155,37 @@ async function getList() {
let timer = ref(null)
const lastId = ref('')
function handleSave() {
lastId.value == row.id && clearTimeout(timer.value)
timer.value = setTimeout(() => {
message.success('正在保存数据,请稍后')
}, 2000)
function handleSave(row) {
try {
lastId.value == row.id && clearTimeout(timer.value)
timer.value = setTimeout(async () => {
lastId.value = row.id
// message.success('')
await KpiApi.saveKpiScore({
id: row.id,
score: row.score,
remark: row.remark
})
message.success('修改成功!')
getList()
}, 2000)
} catch (err) {
console.log(err)
}
}
async function handleDelete(row) {
try {
//
await message.delConfirm(
`删除后,该员工于${searchForm.value.period}将不再考核此项,是否确认删除?`
)
//
await KpiApi.deleteKpiItem({ id: row.id })
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
function getSummaries({ columns, data }) {
@ -190,7 +197,7 @@ function getSummaries({ columns, data }) {
}
const values = data.map((item) => Number(item[column.property]))
if (!values.every((value) => Number.isNaN(value))) {
if (column.property == 'jiaquandefen') {
if (column.property == 'weightSocre') {
sums[index] = 5.6
} else {
sums[index] = ''

Loading…
Cancel
Save