123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- <template>
- <div class="p-small radius-base fill-blank statistics-personnel">
- <base-table
- ref="tableRef"
- border
- stripe
- size="small"
- :data="sortTableData"
- :columns="columns"
- :height="tableHeight"
- :row-class-name="rowClassName"
- highlight-current-row
- @current-change="onCurrentChange"
- @row-dblclick="myDbClick"
- @row-contextmenu="rowContextmenu"
- @sort-change="sortChange"
- >
- <template #column-marker="{ row }">
- <!-- <el-popover
- v-if="row.markerId"
- :ref="(popover: any) => setPopoverRefs(row.markerId,popover)"
- trigger="contextmenu"
- placement="right"
- :show-arrow="false"
- transition="none"
- @show="onPopoverShow(row.markerId)"
- >
- <template #reference>
- <el-button size="small" link type="primary">{{ row.markerName }}</el-button>
- </template>
- <el-menu @select="() => {}">
- <el-menu-item index="setting" @click="onSetWorkload(row)">
- <el-button link>设置工作量</el-button>
- </el-menu-item>
- <el-menu-item index="send" @click="onSendMessage(row)">
- <el-button link>发送消息</el-button>
- </el-menu-item>
- </el-menu>
- </el-popover> -->
- <span v-if="row.markerId">{{ row.markerName }}</span>
- <span v-else-if="row.markingGroupNumber == 0">全体</span>
- <span v-else>全组</span>
- </template>
- </base-table>
- </div>
- <!-- <div
- v-if="!!current && !!current?.markerId"
- v-loading="loading1 || loading2"
- class="flex justify-between m-t-base charts-box"
- >
- <el-button type="primary" plain size="small" class="close-panel" @click="clearCheck">关闭</el-button>
- <div class="flex-1 p-base radius-base fill-blank m-r-base chart-box">
- <vue-e-charts class="full" :option="markerSubjectiveChartsOption"></vue-e-charts>
- </div>
- <div class="flex-1 p-base radius-base fill-blank chart-box">
- <vue-e-charts class="full" :option="markerObjectiveChartsOption"></vue-e-charts>
- </div>
- </div> -->
- <set-workload v-model="setWorkloadVisible" :data="setWorkloadData" />
- <right-key-menu
- v-show="visable"
- ref="rightKeyMenu"
- @right-click="rightClick"
- @on-set-workload="onSetWorkload"
- @on-send-message="onSendMessage"
- @force-assessment="forceAssessment"
- ></right-key-menu>
- </template>
- <script setup lang="tsx" name="StatisticsPersonnel">
- /** 人员数据统计-按人员展开 */
- import { ref, inject, computed, watch, nextTick, unref } from 'vue'
- import { useRouter } from 'vue-router'
- import { ElButton, ElPopover, ElMenu, ElMenuItem } from 'element-plus'
- import VueECharts from 'vue-echarts'
- import BaseTable from '@/components/element/BaseTable.vue'
- import SetWorkload from './SetWorkload.vue'
- import useVW, { usePX } from '@/hooks/useVW'
- import useFetch from '@/hooks/useFetch'
- import useTableCheck from '@/hooks/useTableCheck'
- import RightKeyMenu from './RightKeyMenu.vue'
- import useMainStore from '@/store/main'
- import type { EChartsOption } from 'echarts'
- import type { ExtractApiResponse, ExtractApiParams } from '@/api/api'
- import type { EpTableColumn } from 'global-type'
- import type { PopoverInstance } from 'element-plus'
- const mainStore = useMainStore()
- const isChief = computed(() => {
- const arr = ['CHIEF', 'SECTION_LEADER', 'EXPERT']
- return arr.indexOf(mainStore.myUserInfo?.role || '') > -1
- })
- const rightKeyMenu = ref(null)
- const rightClick = () => {
- visable.value = false
- }
- const props = defineProps<{
- data: ExtractApiResponse<'getStatisticsByGroup'>
- params: ExtractApiParams<'getStatisticsByGroup'> & { expand: boolean }
- filterColumns?: any
- result?: any
- }>()
- const rowClassName = (obj: any) => {
- if (obj.row.markingGroupNumber === 0) {
- return 'fixed-row'
- } else if (obj.row.isGroupTotalRow) {
- return 'fixed-row2'
- }
- }
- const setMessageVisible = inject<(visible: boolean) => void>('setMessageVisible')
- const setReplyUserId = inject<(id: number) => void>('setReplyUserId')
- const { push } = useRouter()
- const columns = computed(() => {
- return [
- {
- label: '#',
- type: 'index',
- width: 50,
- align: 'center',
- fixed: 'left',
- index(index: number) {
- return index + 1
- },
- },
- {
- label: '小组',
- prop: 'markingGroupNumber',
- width: 60,
- fixed: 'left',
- formatter(row: any) {
- return row.markingGroupNumber === 0 ? '全部' : `第${row.markingGroupNumber}组`
- },
- },
- { label: '评卷员', prop: 'markerName', minWidth: 84, slotName: 'marker', fixed: 'left' },
- { align: 'center', label: '评卷份数', prop: 'markingPaperCount', minWidth: 96 },
- { align: 'center', label: '平均分', prop: 'avg', minWidth: 80 },
- {
- align: 'center',
- label: '相关系数',
- prop: 'xyRelate',
- minWidth: 96,
- formatter(row: any) {
- let r = unref(props.result) || []
- const { markingGroupNumber } = row
- let compareTarget = r.find((item: any) => item.markingGroupNumber == markingGroupNumber)
- let allTarget = r.find((item: any) => item.markingGroupNumber == 0)
- if (compareTarget && markingGroupNumber) {
- return (
- props.params.markingGroupNumber ? row.xyRelate < compareTarget.xyRelate : row.xyRelate < allTarget.xyRelate
- ) ? (
- <span style="color:red">{row.xyRelate}</span>
- ) : (
- row.xyRelate
- )
- } else {
- return row.xyRelate
- }
- },
- },
- { align: 'center', label: '标准差', prop: 'std', minWidth: 80 },
- { align: 'center', label: '综合系数', prop: 'integration', minWidth: 96 },
- // { align: 'center', label: '评卷份数', prop: 'markingPaperCount', width: 92 },
- { align: 'center', label: '当日可阅', prop: 'markDayCount', minWidth: 96 },
- { align: 'center', label: '剩余可阅', prop: 'todoMarkDayCount', minWidth: 96 },
- { align: 'center', label: '客观题0分量', prop: 'objectiveZero', minWidth: 120 },
- { align: 'center', label: '客观平均分', prop: 'objectiveAvg', minWidth: 110 },
- { align: 'center', label: '客观标准差', prop: 'objectiveStd', minWidth: 110 },
- {
- align: 'center',
- label: '重评/待确认',
- prop: 'reMarkUnConfirmCount',
- minWidth: 120,
- formatter(row: any) {
- return `${row.reMarkCount}/${row.reMarkUnConfirmCount}`
- },
- },
- { align: 'center', label: '抽查量', prop: 'checkCount', minWidth: 90 },
- { align: 'center', label: '抽查改正量', prop: 'checkCorrectCount', minWidth: 110 },
- // { align: 'center', label: '相关系数', prop: 'xyRelate', width: 90 },
- // { align: 'center', label: '平均分', prop: 'avg', width: 80 },
- // { align: 'center', label: '标准差', prop: 'std', width: 80 },
- { align: 'center', label: '近5分钟最高分', prop: 'scoreTop', minWidth: 130 },
- { align: 'center', label: '近5分钟最低分', prop: 'scoreLow', minWidth: 130 },
- { align: 'center', label: '近5分钟客主比', prop: 'objSubRate', minWidth: 130 },
- { align: 'center', label: '平均客主比', prop: 'objSubAvgRate', minWidth: 110 },
- {
- align: 'center',
- label: '在线',
- prop: 'online',
- minWidth: 72,
- formatter(row: any) {
- if (row.markingGroupNumber === 0) {
- let total = (unref(props.result) || [])
- .filter((item: any) => item.markingGroupNumber !== 0)
- .reduce((ar: any, item: any) => {
- return [...ar, ...(item.markerDetails || [])]
- }, [])
- .reduce((num: number, item: any) => {
- return num + (item.online ? 1 : 0)
- }, 0)
- return total + '人'
- } else if (row.isGroupTotalRow) {
- let total = (unref(props.result) || [])
- .filter((item: any) => item.markingGroupNumber == row.markingGroupNumber)
- .reduce((ar: any, item: any) => {
- return [...ar, ...(item.markerDetails || [])]
- }, [])
- .reduce((num: number, item: any) => {
- return num + (item.online ? 1 : 0)
- }, 0)
- return total + '人'
- } else {
- return row.online ? '在线' : '离线'
- }
- },
- },
- { align: 'center', label: '状态', prop: 'markingStatus', minWidth: 100 },
- { align: 'center', label: '速度', prop: 'markingRate', minWidth: 72 },
- // { align: 'center', label: '综合系数', prop: 'integration', width: 90 },
- ]
- .map((col: any) => {
- if (!['小组', '重评/待确认'].includes(col.label)) col.sortable = 'custom'
- return col
- })
- .filter((col: any) => {
- return props.filterColumns ? props.filterColumns.indexOf(col.prop) > -1 || col.type === 'index' : col
- })
- })
- const visable = ref(false)
- const rowContextmenu = (row: any, column: any, event: any) => {
- console.log(row, column, event)
- visable.value = false
- setTimeout(() => {
- visable.value = true
- }, 50)
- event.preventDefault()
- nextTick(() => {
- // alert(1)
- ;(rightKeyMenu.value as any).onload(row, column, event)
- })
- }
- const popovers = ref<Record<string, PopoverInstance>>({})
- const setWorkloadVisible = ref<boolean>(false)
- const setWorkloadData = ref<ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>>()
- function setPopoverRefs(id: number, popover: PopoverInstance) {
- popovers.value[`popovers-${id}`] = popover
- }
- /** on popover show */
- function onPopoverShow(id: number) {
- Object.keys(popovers.value).forEach((k) => {
- if (k !== `popovers-${id}`) {
- popovers.value[k]?.hide()
- }
- })
- }
- /** 设置工作量 */
- function onSetWorkload(data: ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>) {
- popovers.value[`popovers-${data.markerId}`]?.hide()
- setWorkloadData.value = data
- setWorkloadVisible.value = true
- }
- /** 发送消息 */
- function onSendMessage(data: ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>) {
- popovers.value[`popovers-${data.markerId}`]?.hide()
- setReplyUserId?.(data.markerId)
- setMessageVisible?.(true)
- }
- function forceAssessment(data: ExtractArrayValue<ExtractApiResponse<'getStatisticsByGroup'>>) {
- push({
- name: 'MarkingAssess',
- query: { markerId: data.markerId },
- })
- }
- const data = computed(() => {
- return JSON.parse(JSON.stringify(props.data || [])) || []
- })
- const { tableRef, tableData, current, onCurrentChange, onDbClick, currentView, elTableRef } = useTableCheck(data, false)
- const myDbClick = (row: any) => {
- onDbClick(row)
- if (currentView.value && !!currentView.value.markingGroupNumber && !!currentView.value.markerId) {
- push({
- name: 'AnalysisPersonnelStatisticsMarker',
- query: {
- markerId: currentView.value.markerId,
- markerName: currentView.value.markerName,
- source: '人员数据统计',
- subjectCode: props.params?.subjectCode,
- questionMainNumber: props.params?.questionMainNumber,
- },
- })
- }
- }
- const sortTableData = ref<any[]>([])
- const originalTableData = ref<any[]>([])
- const sortChange = (params: any) => {
- const { column, prop, order } = params
- if (order === 'ascending') {
- sortTableData.value.sort((a: any, b: any) => {
- if (a.markingGroupNumber == 0) {
- return -1
- } else if (b.markingGroupNumber == 0) {
- return 1
- } else {
- if (typeof a[prop] === 'string') {
- let aa = a[prop] || '',
- bb = b[prop] || ''
- return aa.localeCompare(bb)
- } else {
- return a[prop] - b[prop]
- }
- }
- })
- } else if (order === 'descending') {
- sortTableData.value.sort((a: any, b: any) => {
- if (a.markingGroupNumber == 0) {
- return -1
- } else if (b.markingGroupNumber == 0) {
- return 1
- } else {
- if (typeof a[prop] === 'string') {
- let aa = a[prop] || '',
- bb = b[prop] || ''
- return bb.localeCompare(aa)
- } else {
- return b[prop] - a[prop]
- }
- }
- })
- } else if (order == null) {
- sortTableData.value = JSON.parse(JSON.stringify(originalTableData.value))
- }
- }
- function initSortTableData() {
- if (tableData.value) {
- let data: any[] = JSON.parse(JSON.stringify(tableData.value)) || []
- if (data.length && data[data.length - 1].markingGroupNumber === 0) {
- let last = data.splice(data.length - 1, 1)[0]
- data.unshift(last)
- }
- sortTableData.value = JSON.parse(JSON.stringify(data))
- originalTableData.value = JSON.parse(JSON.stringify(data))
- }
- }
- initSortTableData()
- watch(tableData, () => {
- popovers.value = {}
- initSortTableData()
- })
- // watch(currentView, () => {
- // if (currentView.value && !!currentView.value.markingGroupNumber) {
- // push({
- // name: 'AnalysisPersonnelStatisticsMarker',
- // query: { markerId: currentView.value.markerId, markerName: currentView.value.markerName },
- // })
- // }
- // })
- const {
- fetch: getStatisticObjectiveByMarker,
- result: objectiveByMarker,
- loading: loading1,
- } = useFetch('getStatisticObjectiveByMarker')
- const {
- fetch: getStatisticSubjectiveByMarker,
- result: subjectiveByMarker,
- loading: loading2,
- } = useFetch('getStatisticSubjectiveByMarker')
- watch(
- [() => props.params, current],
- () => {
- const { startTime = '', endTime = '' } = props.params || {}
- // if (current.value?.markerId) {
- if (current.value?.markerId) {
- getStatisticObjectiveByMarker({
- markerId: current.value.markerId,
- startTime,
- endTime,
- })
- getStatisticSubjectiveByMarker({
- markerId: current.value.markerId,
- startTime,
- endTime,
- })
- }
- },
- { immediate: true, deep: true }
- )
- type StatisticObjectiveByMarker = ExtractApiResponse<'getStatisticObjectiveByMarker'>
- type StatisticObjectiveByMarkerValues = StatisticObjectiveByMarker['segmentsByAll']
- const getXAxisData = <K extends keyof ExtractArrayValue<StatisticObjectiveByMarkerValues>>(
- field: K,
- data?: StatisticObjectiveByMarkerValues
- ) => {
- if (!data) {
- return []
- }
- const getValue = (key: K, item: ExtractArrayValue<StatisticObjectiveByMarkerValues>) => {
- return item[key]
- }
- return data?.map((v) => getValue(field, v))
- }
- const markerSubjectiveChartsOption = computed<EChartsOption>(() => {
- return {
- legend: {
- right: 0,
- itemWidth: 14,
- data: ['评卷员主观分布', '小组主观分布', '题组主观分布'],
- },
- xAxis: {
- axisLine: { show: false },
- axisTick: { show: false },
- splitLine: { show: false },
- axisLabel: {
- align: 'right',
- },
- data: getXAxisData('scoreStart', subjectiveByMarker?.value?.segmentsByAll),
- },
- yAxis: [
- {
- type: 'value',
- },
- {
- type: 'value',
- axisLabel: {
- formatter: `{value}%`,
- },
- splitLine: { show: false },
- },
- ],
- series: [
- {
- name: '评卷员主观分布',
- type: 'line',
- itemStyle: {
- color: '#3AD500',
- },
- data: getXAxisData('rate', subjectiveByMarker?.value?.segmentsByUser),
- },
- {
- name: '小组主观分布',
- type: 'line',
- itemStyle: {
- color: '#0064FF',
- },
- data: getXAxisData('rate', subjectiveByMarker?.value?.segmentsByGroup),
- },
- {
- name: '题组主观分布',
- type: 'line',
- itemStyle: {
- color: '#008000',
- },
- data: getXAxisData('rate', subjectiveByMarker?.value?.segmentsByAll),
- },
- ],
- }
- })
- const markerObjectiveChartsOption = computed<EChartsOption>(() => {
- return {
- legend: {
- right: 0,
- itemWidth: 14,
- data: ['评卷员客观分布', '小组客观分布', '题组客观分布'],
- },
- xAxis: {
- axisLine: { show: false },
- axisTick: { show: false },
- splitLine: { show: false },
- axisLabel: {
- align: 'right',
- },
- data: getXAxisData('scoreStart', objectiveByMarker?.value?.segmentsByAll),
- },
- yAxis: [
- {
- type: 'value',
- },
- {
- type: 'value',
- axisLabel: {
- formatter: `{value}%`,
- },
- splitLine: { show: false },
- },
- ],
- series: [
- {
- name: '评卷员客观分布',
- type: 'line',
- itemStyle: {
- color: '#3AD500',
- },
- data: getXAxisData('rate', objectiveByMarker?.value?.segmentsByUser),
- },
- {
- name: '小组客观分布',
- type: 'line',
- itemStyle: {
- color: '#0064FF',
- },
- data: getXAxisData('rate', objectiveByMarker?.value?.segmentsByGroup),
- },
- {
- name: '题组客观分布',
- type: 'line',
- itemStyle: {
- color: '#008000',
- },
- data: getXAxisData('rate', objectiveByMarker?.value?.segmentsByAll),
- },
- ],
- }
- })
- const tableHeight = computed(() => {
- // return !!current.value && !!current.value?.markerId ? 'calc(100vh - 520px)' : 'calc(100vh - 219px)'
- return 'calc(100vh - 250px)'
- })
- const clearCheck = () => {
- ;(elTableRef as any).value!.setCurrentRow(undefined)
- }
- </script>
- <style scoped lang="scss">
- .statistics-personnel {
- :deep(.el-table) {
- .fixed-row {
- display: table-row;
- position: sticky;
- position: '-webkit-sticky';
- top: 0;
- width: 100%;
- z-index: 3;
- font-weight: bold;
- color: #333;
- }
- .fixed-row2 {
- display: table-row;
- position: sticky;
- position: '-webkit-sticky';
- top: 44px;
- width: 100%;
- z-index: 3;
- font-weight: bold;
- color: #333;
- }
- }
- }
- .chart-box {
- height: 293px;
- }
- .charts-box {
- position: relative;
- .close-panel {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translateX(-50%) translateY(-50%);
- z-index: 1;
- }
- }
- </style>
|