|
@@ -1,51 +1,80 @@
|
|
|
<template>
|
|
|
<div class="statistics-call-telephonist-container layout-padding">
|
|
|
<div class="layout-padding-auto layout-padding-view pd20">
|
|
|
- <ProTable
|
|
|
- ref="proTableRef"
|
|
|
- :columns="columns"
|
|
|
- :data="state.tableData"
|
|
|
- @updateTable="queryList"
|
|
|
- :loading="state.loading"
|
|
|
- :pagination="false"
|
|
|
- show-summary
|
|
|
- border
|
|
|
- row-key="orgId"
|
|
|
- @sort-change="sortChange"
|
|
|
- :summary-method="getSummaries"
|
|
|
- :toolButton="['refresh', 'setting', 'exportAll']"
|
|
|
- :exportMethod="callAgentExport"
|
|
|
- :exportParams="requestParams"
|
|
|
- >
|
|
|
- <template #table-search>
|
|
|
- <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
|
|
|
- <el-form-item prop="crTime">
|
|
|
- <statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
|
|
|
- <el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
|
|
|
- <SvgIcon name="ele-Refresh" class="mr5" />重置
|
|
|
- </el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </template>
|
|
|
- <template #description>
|
|
|
+ <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
|
|
|
+ <el-form-item prop="crTime">
|
|
|
+ <statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
|
|
|
+ <el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="state.loading">
|
|
|
+ <SvgIcon name="ele-Refresh" class="mr5" />重置
|
|
|
+ </el-button>
|
|
|
<el-popover :width="500" trigger="click">
|
|
|
<template #reference>
|
|
|
- <el-button circle title="口径说明"><SvgIcon name="ele-QuestionFilled" /></el-button>
|
|
|
+ <el-button type="primary" title="口径说明"><SvgIcon name="ele-QuestionFilled" class="mr5"/>口径说明</el-button>
|
|
|
</template>
|
|
|
<el-descriptions title="" :column="1" border style="max-height: 400px; overflow: auto">
|
|
|
<el-descriptions-item label="工作效率">(登录时长-小休摘机时长)/登录时长</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
</el-popover>
|
|
|
- </template>
|
|
|
- <template #name="{ row }">
|
|
|
- <span
|
|
|
- >{{ row.name }} <span v-if="row.staffNo">({{ row.staffNo }})</span></span
|
|
|
- >
|
|
|
- </template>
|
|
|
- </ProTable>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <vxe-toolbar
|
|
|
+ ref="toolbarRef"
|
|
|
+ :loading="state.loading"
|
|
|
+ custom
|
|
|
+ :refresh="{
|
|
|
+ queryMethod: handleQuery,
|
|
|
+ }"
|
|
|
+ :tools="[{ toolRender: { name: 'exportAll' } }]"
|
|
|
+ >
|
|
|
+ </vxe-toolbar>
|
|
|
+ <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
|
|
|
+ <vxe-table
|
|
|
+ border
|
|
|
+ :loading="state.loading"
|
|
|
+ :data="state.tableData"
|
|
|
+ :column-config="{ resizable: true }"
|
|
|
+ :row-config="{ isCurrent: true, isHover: true, height: 30 }"
|
|
|
+ ref="tableRef"
|
|
|
+ height="auto"
|
|
|
+ auto-resize
|
|
|
+ :scrollY="{ enabled: true, gt: 0 }"
|
|
|
+ show-overflow
|
|
|
+ id="statisticsCallTelephonist"
|
|
|
+ :custom-config="{ storage: true }"
|
|
|
+ show-footer
|
|
|
+ :footer-method="footerMethod"
|
|
|
+ :params="{ exportMethod: callAgentExport, exportParams: requestParams }"
|
|
|
+ >
|
|
|
+ <vxe-column field="name" title="坐席工号" width="120"></vxe-column>
|
|
|
+ <vxe-column field="inTotal" title="呼入总量" sortable width="120"> </vxe-column>
|
|
|
+ <vxe-column field="inAnswered" title="接通总量" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="inHangupImmediate" title="呼入秒挂" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="inHanguped" title="呼入超时未接" sortable width="130"></vxe-column>
|
|
|
+ <vxe-column field="inAnsweredRateString" title="接通率" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="inDurationAvg" title="呼入平均时长" sortable width="130"></vxe-column>
|
|
|
+ <vxe-column field="inAvailableAnswer" title="有效接通量" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="inHangupImmediateWhenAnswered" title="接通秒挂" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="availableAnswerRateString" title="有效接通率" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="outTotal" title="呼出总量" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="outAnswered" title="呼出接通量" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="outAnsweredRateString" title="呼出接通率" sortable width="120"></vxe-column>
|
|
|
+ <vxe-column field="outDurationAvg" title="呼出平均时长" width="130" sortable></vxe-column>
|
|
|
+ <vxe-column field="loginDuration" title="登录时长" width="120" sortable>
|
|
|
+ <template #default="scope">
|
|
|
+ {{ formatDurationDay(scope.row.loginDuration) }}
|
|
|
+ </template>
|
|
|
+ </vxe-column>
|
|
|
+ <vxe-column field="restDuration" title="小休时长" width="120" sortable>
|
|
|
+ <template #default="scope">
|
|
|
+ {{ formatDurationDay(scope.row.restDuration) }}
|
|
|
+ </template>
|
|
|
+ </vxe-column>
|
|
|
+ <vxe-column field="workRateString" title="工作效率" width="120" sortable></vxe-column>
|
|
|
+ </vxe-table>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -56,74 +85,10 @@ import { callAgent, callAgentExport } from '@/api/statistics/call';
|
|
|
import { defaultDate } from "@/utils/constants";
|
|
|
import { formatDurationDay } from '@/utils/formatTime';
|
|
|
import Other from "@/utils/other";
|
|
|
+import XEUtils from 'xe-utils';
|
|
|
|
|
|
const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
|
|
|
|
|
|
-const proTableRef = ref<RefType>(); // 表格ref
|
|
|
-// 表格配置项
|
|
|
-const columns = ref<any[]>([
|
|
|
- {
|
|
|
- prop: 'name',
|
|
|
- label: '坐席工号',
|
|
|
- align: 'center',
|
|
|
- minWidth: 110,
|
|
|
- render: (scope) => (
|
|
|
- <span>
|
|
|
- {scope.row.name} {scope.row.staffNo ? <span>({scope.row.telNo})</span> : ''}{' '}
|
|
|
- </span>
|
|
|
- ),
|
|
|
- },
|
|
|
- { prop: 'inTotal', label: '呼入总量', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- { prop: 'inAnswered', label: '接通总量', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- { prop: 'inHangupImmediate', label: '呼入秒挂', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- { prop: 'inHanguped', label: '呼入超时未接', align: 'center', sortable: 'custom', minWidth: 140 },
|
|
|
- {
|
|
|
- prop: 'inAnsweredRateString',
|
|
|
- label: '接通率',
|
|
|
- align: 'center',
|
|
|
- sortable: 'custom',
|
|
|
- minWidth: 120,
|
|
|
- },
|
|
|
- { prop: 'inDurationAvg', label: '呼入平均时长', align: 'center', sortable: 'custom', minWidth: 140 },
|
|
|
- { prop: 'inAvailableAnswer', label: '有效接通量', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- { prop: 'inHangupImmediateWhenAnswered', label: '接通秒挂', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- {
|
|
|
- prop: 'availableAnswerRateString',
|
|
|
- label: '有效接通率',
|
|
|
- align: 'center',
|
|
|
- sortable: 'custom',
|
|
|
- minWidth: 120,
|
|
|
- },
|
|
|
- { prop: 'outTotal', label: '呼出总量', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- { prop: 'outAnswered', label: '呼出接通量', align: 'center', sortable: 'custom', minWidth: 120 },
|
|
|
- {
|
|
|
- prop: 'outAnsweredRateString',
|
|
|
- label: '呼出接通率',
|
|
|
- align: 'center',
|
|
|
- minWidth: 120,
|
|
|
- },
|
|
|
- {
|
|
|
- prop: 'outDurationAvg',
|
|
|
- label: '呼出平均时长',
|
|
|
- align: 'center',
|
|
|
- minWidth: 110,
|
|
|
- },
|
|
|
- {
|
|
|
- prop: 'loginDuration',
|
|
|
- label: '登录时长',
|
|
|
- align: 'center',
|
|
|
- minWidth: 150,
|
|
|
- render: (scope) => <span>{formatDurationDay(scope.row.loginDuration)}</span>,
|
|
|
- },
|
|
|
- {
|
|
|
- prop: 'restDuration',
|
|
|
- label: '小休时长',
|
|
|
- align: 'center',
|
|
|
- minWidth: 100,
|
|
|
- render: (scope) => <span>{formatDurationDay(scope.row.restDuration)}</span>,
|
|
|
- },
|
|
|
- { prop: 'workRateString', label: '工作效率', align: 'center', minWidth: 100 },
|
|
|
-]);
|
|
|
// 定义变量内容
|
|
|
const ruleFormRef = ref<RefType>(); // 表单ref
|
|
|
const state = reactive<any>({
|
|
@@ -160,13 +125,6 @@ const queryList = () => {
|
|
|
state.loading = false;
|
|
|
});
|
|
|
};
|
|
|
-// 排序
|
|
|
-const sortChange = (val: any) => {
|
|
|
- state.queryParams.SortField = val.prop;
|
|
|
- // 0 升序 1 降序
|
|
|
- state.queryParams.SortRule = val.order ? (val.order == 'descending' ? 1 : 0) : null;
|
|
|
- queryList();
|
|
|
-};
|
|
|
/** 重置按钮操作 */
|
|
|
const statisticalTimeRef = ref<RefType>();
|
|
|
const resetQuery = (formEl: FormInstance | undefined) => {
|
|
@@ -175,69 +133,28 @@ const resetQuery = (formEl: FormInstance | undefined) => {
|
|
|
statisticalTimeRef.value.reset();
|
|
|
queryList();
|
|
|
};
|
|
|
-// 合计
|
|
|
-const getSummaries = (param: any) => {
|
|
|
- const { columns, data } = param;
|
|
|
- const sums: string[] = [];
|
|
|
- columns.forEach((column: { property: string }, index: number) => {
|
|
|
- if (index === 0) {
|
|
|
- sums[index] = '合计';
|
|
|
- return;
|
|
|
- }
|
|
|
- const values = data.map((item: { [x: string]: any }) => Number(item[column.property]));
|
|
|
- if (['inAnsweredRate', 'outAnsweredRate', 'workRate'].includes(column.property)) {
|
|
|
- //百分比不能计算
|
|
|
- sums[index] = '';
|
|
|
- return '';
|
|
|
- } else if (['loginDuration', 'restDuration'].includes(column.property)) {
|
|
|
- sums[index] = formatDurationDay(
|
|
|
- values.reduce((prev: any, curr: any) => {
|
|
|
- const value = Number(curr);
|
|
|
- if (!Number.isNaN(value)) {
|
|
|
- return prev + curr;
|
|
|
- } else {
|
|
|
- return prev;
|
|
|
- }
|
|
|
- }, 0)
|
|
|
- );
|
|
|
- } else if (!values.every((value: unknown) => Number.isNaN(value))) {
|
|
|
- sums[index] = `${values.reduce((prev: any, curr: any) => {
|
|
|
- const value = Number(curr);
|
|
|
- if (!Number.isNaN(value)) {
|
|
|
- return prev + curr;
|
|
|
- } else {
|
|
|
- return prev;
|
|
|
- }
|
|
|
- }, 0)}`;
|
|
|
- } else {
|
|
|
- sums[index] = ' ';
|
|
|
- }
|
|
|
- });
|
|
|
- return sums;
|
|
|
- /*const { columns } = param;
|
|
|
- const sums: string[] = [];
|
|
|
- columns.forEach((column: { property: string }, index: number) => {
|
|
|
- switch (column.property) {
|
|
|
- case 'name':
|
|
|
- sums[index] = state.totalCount?.name;
|
|
|
- break;
|
|
|
- case 'num':
|
|
|
- sums[index] = state.totalCount?.num;
|
|
|
- break;
|
|
|
- case 'chainNum':
|
|
|
- sums[index] = state.totalCount?.chainNum;
|
|
|
- break;
|
|
|
- case 'chainRate':
|
|
|
- sums[index] = state.totalCount?.chainRate;
|
|
|
- break;
|
|
|
- default:
|
|
|
- sums[index] = '';
|
|
|
- break;
|
|
|
- }
|
|
|
- });
|
|
|
- return sums;*/
|
|
|
+// 计算合计
|
|
|
+const footerMethod = ({ columns, data }) => {
|
|
|
+ return [
|
|
|
+ columns.map((column: any, columnIndex: number) => {
|
|
|
+ if (columnIndex === 0) {
|
|
|
+ return '合计';
|
|
|
+ }
|
|
|
+ if (['inTotal', 'inAnswered', 'inHangupImmediate','inHanguped','inDurationAvg','inAvailableAnswer','inHangupImmediateWhenAnswered','outTotal','outAnswered'].includes(column.property)) {
|
|
|
+ return XEUtils.sum(data, column.property);
|
|
|
+ }
|
|
|
+ if(['loginDuration', 'restDuration'].includes(column.property)){
|
|
|
+ return formatDurationDay(XEUtils.sum(data, column.property))
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ ];
|
|
|
};
|
|
|
+const toolbarRef = ref<RefType>();
|
|
|
+const tableRef = ref<RefType>();
|
|
|
onMounted(() => {
|
|
|
queryList();
|
|
|
+ if (tableRef.value && toolbarRef.value) {
|
|
|
+ tableRef.value.connect(toolbarRef.value);
|
|
|
+ }
|
|
|
});
|
|
|
</script>
|