|
@@ -1,625 +1,364 @@
|
|
<template>
|
|
<template>
|
|
<div class="quality-analysis">
|
|
<div class="quality-analysis">
|
|
- <report-header
|
|
|
|
- title="质量监控分析"
|
|
|
|
- v-model:dateRange="curTimeRange"
|
|
|
|
- @timeChange="timeChange"
|
|
|
|
- >
|
|
|
|
- <t-select
|
|
|
|
- style="width: 200px"
|
|
|
|
- :options="serviceOptions"
|
|
|
|
|
|
+ <report-header title="质量监控分析" hideTimePicker>
|
|
|
|
+ <select-service-unit
|
|
v-model="serviceId"
|
|
v-model="serviceId"
|
|
- :keys="{ label: 'name', value: 'id' }"
|
|
|
|
- filterable
|
|
|
|
- ></t-select>
|
|
|
|
|
|
+ clearable
|
|
|
|
+ defaultSelect
|
|
|
|
+ style="width: 220px"
|
|
|
|
+ ></select-service-unit>
|
|
</report-header>
|
|
</report-header>
|
|
<div class="page-main">
|
|
<div class="page-main">
|
|
- <div class="col1">
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">质量问题总体盘点</span>
|
|
|
|
- <t-select
|
|
|
|
- style="width: 100px"
|
|
|
|
- :options="groupOptions"
|
|
|
|
- v-model="group"
|
|
|
|
- ></t-select>
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap">
|
|
|
|
- <div class="chart-part">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="overallPieData"
|
|
|
|
- :options="overallPieOptions"
|
|
|
|
- @chartClick="chart0Click"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-part">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="overallRadarData"
|
|
|
|
- :options="overallRadarOptions"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="scroll-content">
|
|
|
|
+ <div class="col1">
|
|
|
|
+ <div class="module-title">按大区质量问题统计</div>
|
|
|
|
+ <t-table
|
|
|
|
+ size="small"
|
|
|
|
+ row-key="areaManagerId"
|
|
|
|
+ :columns="columns1"
|
|
|
|
+ :data="data1 || []"
|
|
|
|
+ bordered
|
|
|
|
+ :max-height="tableMaxHeightHalf"
|
|
|
|
+ :loading="loading1"
|
|
|
|
+ >
|
|
|
|
+ <template #name="{ col, row }">
|
|
|
|
+ <span v-if="row[col.colKey]">{{ row[col.colKey] }}</span>
|
|
|
|
+ <span v-else style="font-weight: bold">合计</span>
|
|
|
|
+ </template>
|
|
|
|
+ <template #sum="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ areaManagerId: row.areaManagerId,
|
|
|
|
+ influenceDegree: '',
|
|
|
|
+ supplierId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.sum }}</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeA="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'A',
|
|
|
|
+ areaManagerId: row.areaManagerId,
|
|
|
|
+ supplierId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeA }}({{
|
|
|
|
+ row.influenceDegreeARate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeB="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'B',
|
|
|
|
+ areaManagerId: row.areaManagerId,
|
|
|
|
+ supplierId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeB }}({{
|
|
|
|
+ row.influenceDegreeBRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeC="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'C',
|
|
|
|
+ areaManagerId: row.areaManagerId,
|
|
|
|
+ supplierId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeC }}({{
|
|
|
|
+ row.influenceDegreeCRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeD="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'D',
|
|
|
|
+ areaManagerId: row.areaManagerId,
|
|
|
|
+ supplierId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeD }}({{
|
|
|
|
+ row.influenceDegreeDRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ </t-table>
|
|
|
|
+ <div class="module-title m-t-15px">按人力商质量问题统计</div>
|
|
|
|
+ <t-table
|
|
|
|
+ size="small"
|
|
|
|
+ row-key="supplierId"
|
|
|
|
+ :columns="columns2"
|
|
|
|
+ :data="data2 || []"
|
|
|
|
+ bordered
|
|
|
|
+ :max-height="tableMaxHeightHalf"
|
|
|
|
+ :loading="loading2"
|
|
|
|
+ >
|
|
|
|
+ <template #name="{ col, row }">
|
|
|
|
+ <span v-if="row[col.colKey]">{{ row[col.colKey] }}</span>
|
|
|
|
+ <span v-else style="font-weight: bold">合计</span>
|
|
|
|
+ </template>
|
|
|
|
+ <template #sum="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ supplierId: row.supplierId,
|
|
|
|
+ influenceDegree: '',
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.sum }}</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeA="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'A',
|
|
|
|
+ supplierId: row.supplierId,
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeA }}({{
|
|
|
|
+ row.influenceDegreeARate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeB="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'B',
|
|
|
|
+ supplierId: row.supplierId,
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeB }}({{
|
|
|
|
+ row.influenceDegreeBRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeC="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'C',
|
|
|
|
+ supplierId: row.supplierId,
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeC }}({{
|
|
|
|
+ row.influenceDegreeCRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ <template #influenceDegreeD="{ col, row }">
|
|
|
|
+ <t-link
|
|
|
|
+ theme="primary"
|
|
|
|
+ @click="
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: 'D',
|
|
|
|
+ supplierId: row.supplierId,
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ })
|
|
|
|
+ "
|
|
|
|
+ >{{ row.influenceDegreeD }}({{
|
|
|
|
+ row.influenceDegreeDRate
|
|
|
|
+ }})</t-link
|
|
|
|
+ >
|
|
|
|
+ </template>
|
|
|
|
+ </t-table>
|
|
</div>
|
|
</div>
|
|
- </div>
|
|
|
|
- <div class="col2">
|
|
|
|
- <div class="col2-head">
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">质量问题审核进度</span>
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap p-t-7px">
|
|
|
|
- <t-progress :percentage="auditProgress || 0" />
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="col2-body">
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">影响度供应商分布及对比</span>
|
|
|
|
- <!-- <t-button variant="outline">
|
|
|
|
- <template #icon><FullscreenIcon /></template>
|
|
|
|
- </t-button> -->
|
|
|
|
- <FullscreenIcon
|
|
|
|
- class="cursor-pointer"
|
|
|
|
- @click="chart11?.maximize"
|
|
|
|
- color="#595959"
|
|
|
|
- />
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="result11"
|
|
|
|
- :options="options11"
|
|
|
|
- ref="chart11"
|
|
|
|
- @chartClick="chart11Click"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">执行协调类归因供应商分布及对比</span>
|
|
|
|
- <!-- <t-button variant="outline">
|
|
|
|
- <template #icon><FullscreenIcon /></template>
|
|
|
|
- </t-button> -->
|
|
|
|
- <FullscreenIcon
|
|
|
|
- class="cursor-pointer"
|
|
|
|
- @click="chart12?.maximize"
|
|
|
|
- color="#595959"
|
|
|
|
- />
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="result12"
|
|
|
|
- :options="options12"
|
|
|
|
- ref="chart12"
|
|
|
|
- @chartClick="chart12Click"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">影响度大区分布及对比TOP5</span>
|
|
|
|
- <!-- <t-button variant="outline">
|
|
|
|
- <template #icon><FullscreenIcon /></template>
|
|
|
|
- </t-button> -->
|
|
|
|
- <FullscreenIcon
|
|
|
|
- class="cursor-pointer"
|
|
|
|
- @click="chart21?.maximize"
|
|
|
|
- color="#595959"
|
|
|
|
- />
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="result21"
|
|
|
|
- :options="options21"
|
|
|
|
- ref="chart21"
|
|
|
|
- @chartClick="chart21Click"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="card">
|
|
|
|
- <div class="title">
|
|
|
|
- <span class="label">执行协调类归因大区分布及对比TOP5</span>
|
|
|
|
- <!-- <t-button variant="outline">
|
|
|
|
- <template #icon><FullscreenIcon /></template>
|
|
|
|
- </t-button> -->
|
|
|
|
- <FullscreenIcon
|
|
|
|
- class="cursor-pointer"
|
|
|
|
- @click="chart22?.maximize"
|
|
|
|
- color="#595959"
|
|
|
|
- />
|
|
|
|
- </div>
|
|
|
|
- <div class="chart-wrap">
|
|
|
|
- <my-chart
|
|
|
|
- v-if="result22"
|
|
|
|
- :options="options22"
|
|
|
|
- ref="chart22"
|
|
|
|
- @chartClick="chart22Click"
|
|
|
|
- ></my-chart>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="col2">
|
|
|
|
+ <div class="module-title">质量问题原因分类</div>
|
|
|
|
+
|
|
|
|
+ <t-table
|
|
|
|
+ size="small"
|
|
|
|
+ row-key="leadId"
|
|
|
|
+ :columns="columns3"
|
|
|
|
+ :data="data3 || []"
|
|
|
|
+ bordered
|
|
|
|
+ :max-height="tableMaxHeight"
|
|
|
|
+ :loading="loading3"
|
|
|
|
+ >
|
|
|
|
+ <template #name="{ col, row }">
|
|
|
|
+ <span v-if="row[col.colKey]">{{ row[col.colKey] }}</span>
|
|
|
|
+ <span v-else style="font-weight: bold">合计</span>
|
|
|
|
+ </template>
|
|
|
|
+ <template #reasonRate="{ col, row }">
|
|
|
|
+ {{ row.reasonRate + '%' }}
|
|
|
|
+ </template>
|
|
|
|
+ <template #operate="{ row, col }">
|
|
|
|
+ <t-link theme="primary" @click="toDetail(row)">详情</t-link>
|
|
|
|
+ </template>
|
|
|
|
+ </t-table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
-
|
|
|
|
- <InventoryDrillDialog
|
|
|
|
- v-model:visible="showInventoryDrillData"
|
|
|
|
- :data="inventoryDrillData"
|
|
|
|
- :footer="false"
|
|
|
|
- ></InventoryDrillDialog>
|
|
|
|
- <DegreeDrillDialog
|
|
|
|
- v-model:visible="showDegreeDrillData"
|
|
|
|
- :data="degreeDrillData"
|
|
|
|
- :title="degreeDrillTitle"
|
|
|
|
- :footer="false"
|
|
|
|
- ></DegreeDrillDialog> </div
|
|
|
|
-></template>
|
|
|
|
|
|
+ <DetailDrawer v-model="showDetailDrawer" ref="DetailDrawerRef" />
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
|
|
<script setup name="QualityAnalysis">
|
|
<script setup name="QualityAnalysis">
|
|
-import { ref, computed, watch } from 'vue';
|
|
|
|
-import {
|
|
|
|
- createRingPieOption,
|
|
|
|
- createStackingBarOption,
|
|
|
|
- createRadarOption,
|
|
|
|
-} from '@/utils/chart';
|
|
|
|
|
|
+import { ref, computed, watch, onMounted } from 'vue';
|
|
|
|
+import ChinaPointChart from '@/components/common/china-point-chart/index.vue';
|
|
import { useRequest } from 'vue-request';
|
|
import { useRequest } from 'vue-request';
|
|
-import {
|
|
|
|
- qualityServiceList,
|
|
|
|
- qualityAnalysisPie,
|
|
|
|
- qualityAnalysisRadar,
|
|
|
|
- qualityAnalysisProgress,
|
|
|
|
- qualityAnalysisInfluence,
|
|
|
|
-} from '@/api/report';
|
|
|
|
import { FullscreenIcon } from 'tdesign-icons-vue-next';
|
|
import { FullscreenIcon } from 'tdesign-icons-vue-next';
|
|
-import { ISSUES_REASON_TYPE } from '@/config/constants';
|
|
|
|
-import InventoryDrillDialog from './inventory-drill-dialog.vue';
|
|
|
|
-import DegreeDrillDialog from './degree-drill-dialog.vue';
|
|
|
|
import { cloneDeep } from 'lodash-es';
|
|
import { cloneDeep } from 'lodash-es';
|
|
-const chart11 = ref();
|
|
|
|
-const chart12 = ref();
|
|
|
|
-const chart21 = ref();
|
|
|
|
-const chart22 = ref();
|
|
|
|
-const degreeDrillTitle = ref('');
|
|
|
|
-const inventoryDrillData = ref({
|
|
|
|
- serviceUnitId: '',
|
|
|
|
- degree: '',
|
|
|
|
- serviceName: '',
|
|
|
|
-});
|
|
|
|
-const showInventoryDrillData = ref(false);
|
|
|
|
-const chart0Click = (params) => {
|
|
|
|
- inventoryDrillData.value.serviceUnitId = serviceId.value;
|
|
|
|
- inventoryDrillData.value.degree = params.name;
|
|
|
|
- inventoryDrillData.value.serviceName = serviceOptions.value.find(
|
|
|
|
- (item) => item.id == serviceId.value
|
|
|
|
- )?.name;
|
|
|
|
- showInventoryDrillData.value = true;
|
|
|
|
-};
|
|
|
|
-const degreeDrillData = ref({
|
|
|
|
- Influence_degree: '',
|
|
|
|
- region_id: '',
|
|
|
|
- regionName: '',
|
|
|
|
- reason: '',
|
|
|
|
- supplierId: '',
|
|
|
|
- supplierName: '',
|
|
|
|
- serviceUnitId: '',
|
|
|
|
- serviceName: '',
|
|
|
|
|
|
+import DetailDrawer from './detail-drawer.vue';
|
|
|
|
+import {
|
|
|
|
+ qualityByRegion,
|
|
|
|
+ qualityBySupplier,
|
|
|
|
+ qualityReasonTypes,
|
|
|
|
+} from '@/api/report';
|
|
|
|
+const tableMaxHeightHalf = ref(0);
|
|
|
|
+const tableMaxHeight = ref(0);
|
|
|
|
+onMounted(() => {
|
|
|
|
+ tableMaxHeight.value = parseInt(window.innerHeight) - 117;
|
|
|
|
+ tableMaxHeightHalf.value = parseInt((parseInt(window.innerHeight) - 147) / 2);
|
|
});
|
|
});
|
|
|
|
|
|
-const showDegreeDrillData = ref(false);
|
|
|
|
|
|
+const showDetailDrawer = ref(false);
|
|
|
|
+const data1 = ref([]);
|
|
|
|
+const data2 = ref([]);
|
|
|
|
+const data3 = ref([]);
|
|
|
|
|
|
-const getReasonValue = (name) => {
|
|
|
|
- for (let key in ISSUES_REASON_TYPE) {
|
|
|
|
- if (ISSUES_REASON_TYPE[key] == name) {
|
|
|
|
- return key;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return '';
|
|
|
|
-};
|
|
|
|
-const chart11Click = (params) => {
|
|
|
|
- let map = getSupplierIdsMap(result11.value);
|
|
|
|
- degreeDrillTitle.value = '影响度供应商分布';
|
|
|
|
- degreeDrillData.value = {
|
|
|
|
- serviceUnitId: serviceId.value,
|
|
|
|
- serviceName: serviceOptions.value.find((item) => item.id == serviceId.value)
|
|
|
|
- ?.name,
|
|
|
|
- Influence_degree: params.name,
|
|
|
|
- supplierId: map[params.seriesName],
|
|
|
|
- supplierName: params.seriesName,
|
|
|
|
- };
|
|
|
|
- showDegreeDrillData.value = true;
|
|
|
|
-};
|
|
|
|
-const chart12Click = (params) => {
|
|
|
|
- let map = getSupplierIdsMap(result12.value);
|
|
|
|
- degreeDrillTitle.value = '执行协调类归因供应商分布';
|
|
|
|
- degreeDrillData.value = {
|
|
|
|
- serviceUnitId: serviceId.value,
|
|
|
|
- serviceName: serviceOptions.value.find((item) => item.id == serviceId.value)
|
|
|
|
- ?.name,
|
|
|
|
- supplierId: map[params.seriesName],
|
|
|
|
- supplierName: params.seriesName,
|
|
|
|
- reason: getReasonValue(params.name),
|
|
|
|
- reasonName: params.name,
|
|
|
|
- };
|
|
|
|
- showDegreeDrillData.value = true;
|
|
|
|
-};
|
|
|
|
-const chart21Click = (params) => {
|
|
|
|
- let map = getRegionIdsMap(result21.value);
|
|
|
|
- degreeDrillTitle.value = '影响度大区分布';
|
|
|
|
- degreeDrillData.value = {
|
|
|
|
- serviceUnitId: serviceId.value,
|
|
|
|
- serviceName: serviceOptions.value.find((item) => item.id == serviceId.value)
|
|
|
|
- ?.name,
|
|
|
|
- region_id: map[params.name],
|
|
|
|
- Influence_degree: params.seriesName,
|
|
|
|
- regionName: params.name,
|
|
|
|
- };
|
|
|
|
- showDegreeDrillData.value = true;
|
|
|
|
-};
|
|
|
|
-const chart22Click = (params) => {
|
|
|
|
- let map = getRegionIdsMap(result22.value);
|
|
|
|
- degreeDrillTitle.value = '执行协调类归因大区分布';
|
|
|
|
- degreeDrillData.value = {
|
|
|
|
- serviceUnitId: serviceId.value,
|
|
|
|
- serviceName: serviceOptions.value.find((item) => item.id == serviceId.value)
|
|
|
|
- ?.name,
|
|
|
|
- // region_id: map[params.name],
|
|
|
|
- // reason: getReasonValue(params.seriesName),
|
|
|
|
- // regionName: params.name,
|
|
|
|
- // reasonName: params.seriesName,
|
|
|
|
- region_id: map[params.name],
|
|
|
|
- Influence_degree: params.seriesName,
|
|
|
|
- regionName: params.name,
|
|
|
|
- };
|
|
|
|
- showDegreeDrillData.value = true;
|
|
|
|
-};
|
|
|
|
-const curTimeRange = ref([]);
|
|
|
|
-const timeParams = computed(() => {
|
|
|
|
- return {
|
|
|
|
- startTime: new Date(curTimeRange.value[0] + ' 00:00:00').getTime(),
|
|
|
|
- endTime: new Date(curTimeRange.value[1] + ' 23:59:59').getTime(),
|
|
|
|
- };
|
|
|
|
-});
|
|
|
|
-const groupOptions = ref([
|
|
|
|
- { label: '按供应商', value: 'REASON_SUPPLIER' },
|
|
|
|
- { label: '按大区', value: 'REASON_REGION' },
|
|
|
|
-]);
|
|
|
|
-let group = ref('REASON_SUPPLIER');
|
|
|
|
-const serviceOptions = ref([]);
|
|
|
|
|
|
+const loading1 = ref(false);
|
|
|
|
+const loading2 = ref(false);
|
|
|
|
+const loading3 = ref(false);
|
|
const serviceId = ref('');
|
|
const serviceId = ref('');
|
|
-const timeChange = (time) => {
|
|
|
|
- qualityServiceList(timeParams.value).then((res) => {
|
|
|
|
- serviceOptions.value = res || [];
|
|
|
|
- res?.length && (serviceId.value = res[0].id);
|
|
|
|
|
|
+watch(serviceId, (serviceId) => {
|
|
|
|
+ loading1.value = true;
|
|
|
|
+ loading2.value = true;
|
|
|
|
+ loading3.value = true;
|
|
|
|
+ qualityByRegion({ serviceId }).then((res) => {
|
|
|
|
+ loading1.value = false;
|
|
|
|
+ data1.value = res?.records || [];
|
|
|
|
+ let push = {
|
|
|
|
+ name: '',
|
|
|
|
+ sum: res?.total, //总数
|
|
|
|
+ influenceDegreeA: res?.influenceDegreeATotal, //数量
|
|
|
|
+ influenceDegreeARate: res?.influenceDegreeARateTotal, //比例(直接+'%'号展示)
|
|
|
|
+ influenceDegreeB: res?.influenceDegreeBTotal,
|
|
|
|
+ influenceDegreeBRate: res?.influenceDegreeBRateTotal,
|
|
|
|
+ influenceDegreeC: res?.influenceDegreeCTotal,
|
|
|
|
+ influenceDegreeCRate: res?.influenceDegreeCRateTotal,
|
|
|
|
+ influenceDegreeD: res?.influenceDegreeDTotal,
|
|
|
|
+ influenceDegreeDRate: res?.influenceDegreeDRateTotal,
|
|
|
|
+ };
|
|
|
|
+ data1.value.push(push);
|
|
});
|
|
});
|
|
-};
|
|
|
|
-// 总体盘点饼图
|
|
|
|
-const { data: overallPieData, run: overallPieRun } =
|
|
|
|
- useRequest(qualityAnalysisPie);
|
|
|
|
-// 总体盘点归因雷达图
|
|
|
|
-const { data: overallRadarData, run: overallRadarRun } =
|
|
|
|
- useRequest(qualityAnalysisRadar);
|
|
|
|
-// 总体盘点饼图
|
|
|
|
-const { data: auditProgress, run: progressRun } = useRequest(
|
|
|
|
- qualityAnalysisProgress
|
|
|
|
-);
|
|
|
|
-// 影响度/归因柱状图
|
|
|
|
-const { data: result11, run: run11 } = useRequest(qualityAnalysisInfluence);
|
|
|
|
-const { data: result12, run: run12 } = useRequest(qualityAnalysisInfluence);
|
|
|
|
-const { data: result21, run: run21 } = useRequest(qualityAnalysisInfluence);
|
|
|
|
-const { data: result22, run: run22 } = useRequest(qualityAnalysisInfluence);
|
|
|
|
-
|
|
|
|
-watch(serviceId, (serviceUnitId) => {
|
|
|
|
- const param = { serviceUnitId };
|
|
|
|
- overallPieRun(param);
|
|
|
|
- overallRadarRun({ ...param, group: group.value });
|
|
|
|
- progressRun(param);
|
|
|
|
- run11({ ...param, group: 'INFLUENCE_SUPPLIER' });
|
|
|
|
- run12({ ...param, group: 'REASON_SUPPLIER' });
|
|
|
|
- run21({ ...param, group: 'INFLUENCE_REGION' });
|
|
|
|
- run22({ ...param, group: 'REASON_REGION' });
|
|
|
|
-});
|
|
|
|
-watch(group, () => {
|
|
|
|
- overallRadarRun({ serviceUnitId: serviceId.value, group: group.value });
|
|
|
|
-});
|
|
|
|
-
|
|
|
|
-const buildBarData1 = (result = {}) => {
|
|
|
|
- let xData = Object.keys(result);
|
|
|
|
- let names = Object.values(result)
|
|
|
|
- ? Object.keys(Object.values(result)[0] || {})
|
|
|
|
- : [];
|
|
|
|
- let sData = [];
|
|
|
|
- for (let i = 0; i < names.length; i++) {
|
|
|
|
- let data = [];
|
|
|
|
- for (let j = 0; j < xData.length; j++) {
|
|
|
|
- data.push(result[xData[j]][names[i]] || 0);
|
|
|
|
- }
|
|
|
|
- sData.push({
|
|
|
|
- name: names[i],
|
|
|
|
- data: data,
|
|
|
|
- barWidth: '16px',
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- return { xData, seriesData: sData };
|
|
|
|
-};
|
|
|
|
-const buildBarData2 = (result = {}) => {
|
|
|
|
- let xData = Object.keys(result);
|
|
|
|
- let names = Object.values(result)
|
|
|
|
- ? Object.keys(Object.values(result)[0] || {}).filter((key) => key !== 'id')
|
|
|
|
- : [];
|
|
|
|
- let sData = [];
|
|
|
|
- for (let i = 0; i < names.length; i++) {
|
|
|
|
- let data = [];
|
|
|
|
- for (let j = 0; j < xData.length; j++) {
|
|
|
|
- data.push(result[xData[j]][names[i]] || 0);
|
|
|
|
- }
|
|
|
|
- sData.push({
|
|
|
|
- name: names[i],
|
|
|
|
- data: data,
|
|
|
|
- barWidth: '16px',
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- return { xData, seriesData: sData };
|
|
|
|
-};
|
|
|
|
-// 总体盘点饼图
|
|
|
|
-const overallPieOptions = computed(() => {
|
|
|
|
- const result = Object.entries(overallPieData.value || {});
|
|
|
|
- return createRingPieOption(
|
|
|
|
- {
|
|
|
|
- data: result.map(([name, value]) => {
|
|
|
|
- return {
|
|
|
|
- name,
|
|
|
|
- value,
|
|
|
|
- };
|
|
|
|
- }),
|
|
|
|
- center: ['50%', '40%'],
|
|
|
|
- radius: [50, 80],
|
|
|
|
- title: '质量问题累计',
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- legend: {
|
|
|
|
- right: 'auto',
|
|
|
|
- top: 'auto',
|
|
|
|
- bottom: 30,
|
|
|
|
- orient: 'horizontal',
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
- );
|
|
|
|
-});
|
|
|
|
-// 总体盘点归因雷达图
|
|
|
|
-const overallRadarOptions = computed(() => {
|
|
|
|
- const result = overallRadarData.value || {};
|
|
|
|
- // const result = {
|
|
|
|
- // all: {
|
|
|
|
- // EXEC: 4,
|
|
|
|
- // EXEC1: 3,
|
|
|
|
- // EXEC2: 5,
|
|
|
|
- // EXEC3: 6,
|
|
|
|
- // },
|
|
|
|
- // 小熊U: {
|
|
|
|
- // EXEC: 2,
|
|
|
|
- // EXEC1: 4,
|
|
|
|
- // EXEC2: 7,
|
|
|
|
- // EXEC3: 4,
|
|
|
|
- // },
|
|
|
|
- // };
|
|
|
|
- // let xData = Object.keys(result);
|
|
|
|
- // let names = Object.values(result)
|
|
|
|
- // ? Object.keys(Object.values(result)[0] || {})
|
|
|
|
- // : [];
|
|
|
|
- // let sData = [];
|
|
|
|
- // for (let i = 0; i < xData.length; i++) {
|
|
|
|
- // let data = [];
|
|
|
|
- // for (let j = 0; j <script names.length; j++) {
|
|
|
|
- // data.push(result[xData[i]][names[j]] || 0);
|
|
|
|
- // }
|
|
|
|
- // sData.push({
|
|
|
|
- // name: xData[i] === 'all' ? '全部' : xData[i],
|
|
|
|
- // value: data,
|
|
|
|
- // });
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- const sData = Object.entries(result).map((arr) => {
|
|
|
|
- return {
|
|
|
|
- name: arr[0],
|
|
|
|
- data: Object.values(
|
|
|
|
- Object.keys(ISSUES_REASON_TYPE).reduce((obj, key) => {
|
|
|
|
- obj[key] = arr[1][key] || 0;
|
|
|
|
- return obj;
|
|
|
|
- }, {})
|
|
|
|
- ),
|
|
|
|
|
|
+ qualityBySupplier({ serviceId }).then((res) => {
|
|
|
|
+ loading2.value = false;
|
|
|
|
+ data2.value = res?.records || [];
|
|
|
|
+ let push = {
|
|
|
|
+ name: '',
|
|
|
|
+ sum: res?.total,
|
|
|
|
+ influenceDegreeA: res?.influenceDegreeATotal,
|
|
|
|
+ influenceDegreeARate: res?.influenceDegreeARateTotal,
|
|
|
|
+ influenceDegreeB: res?.influenceDegreeBTotal,
|
|
|
|
+ influenceDegreeBRate: res?.influenceDegreeBRateTotal,
|
|
|
|
+ influenceDegreeC: res?.influenceDegreeCTotal,
|
|
|
|
+ influenceDegreeCRate: res?.influenceDegreeCRateTotal,
|
|
|
|
+ influenceDegreeD: res?.influenceDegreeDTotal,
|
|
|
|
+ influenceDegreeDRate: res?.influenceDegreeDRateTotal,
|
|
};
|
|
};
|
|
|
|
+ data2.value.push(push);
|
|
});
|
|
});
|
|
- return createRadarOption({
|
|
|
|
- names: Object.values(ISSUES_REASON_TYPE),
|
|
|
|
- sData,
|
|
|
|
|
|
+ qualityReasonTypes({ serviceId }).then((res) => {
|
|
|
|
+ loading3.value = false;
|
|
|
|
+ data3.value = res?.records || [];
|
|
|
|
+ let push = {
|
|
|
|
+ name: '',
|
|
|
|
+ reasonSum: res?.total,
|
|
|
|
+ reasonRate: res?.reasonRateTotal,
|
|
|
|
+ };
|
|
|
|
+ data3.value.push(push);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
-
|
|
|
|
-// 影响度/归因柱状图
|
|
|
|
-const barExtendOption = {
|
|
|
|
- grid: {
|
|
|
|
- top: 50,
|
|
|
|
- left: 15,
|
|
|
|
|
|
+const baseColumns = [
|
|
|
|
+ { colKey: 'sum', title: '质量问题总数' },
|
|
|
|
+ { colKey: 'influenceDegreeA', title: 'A类' },
|
|
|
|
+ { colKey: 'influenceDegreeB', title: 'B类' },
|
|
|
|
+ { colKey: 'influenceDegreeC', title: 'C类' },
|
|
|
|
+ { colKey: 'influenceDegreeD', title: 'D类' },
|
|
|
|
+];
|
|
|
|
+const columns1 = [{ colKey: 'name', title: '大区经理' }, ...baseColumns];
|
|
|
|
+const columns2 = [{ colKey: 'name', title: '人力商' }, ...baseColumns];
|
|
|
|
+const columns3 = [
|
|
|
|
+ { colKey: 'name', title: '问题原因' },
|
|
|
|
+ { colKey: 'reasonSum', title: '数量' },
|
|
|
|
+ { colKey: 'reasonRate', title: '占比' },
|
|
|
|
+ {
|
|
|
|
+ title: '操作',
|
|
|
|
+ colKey: 'operate',
|
|
|
|
+ width: 100,
|
|
},
|
|
},
|
|
- yAxis: {
|
|
|
|
- name: '件',
|
|
|
|
- nameTextStyle: {
|
|
|
|
- color: '#595959',
|
|
|
|
- },
|
|
|
|
- nameGap: 28,
|
|
|
|
- },
|
|
|
|
-};
|
|
|
|
-const options11 = computed(() => {
|
|
|
|
- return createStackingBarOption(
|
|
|
|
- buildBarData1(result11.value || {}),
|
|
|
|
- barExtendOption
|
|
|
|
- );
|
|
|
|
-});
|
|
|
|
-const options12 = computed(() => {
|
|
|
|
- let res = result12.value || {};
|
|
|
|
- let obj = Object.keys(res).reduce((o, k) => {
|
|
|
|
- if (ISSUES_REASON_TYPE[k]) {
|
|
|
|
- o[ISSUES_REASON_TYPE[k]] = res[k];
|
|
|
|
- } else {
|
|
|
|
- o[k] = res[k];
|
|
|
|
- }
|
|
|
|
- return o;
|
|
|
|
- }, {});
|
|
|
|
|
|
+];
|
|
|
|
|
|
- return createStackingBarOption(buildBarData1(obj), barExtendOption);
|
|
|
|
-});
|
|
|
|
-const getSupplierIdsMap = (result) => {
|
|
|
|
- let map = {};
|
|
|
|
- let res = cloneDeep(result || {});
|
|
|
|
- let objs = Object.values(res);
|
|
|
|
- for (let i = 0; i < objs.length; i++) {
|
|
|
|
- let obj = objs[i];
|
|
|
|
- for (let k in obj) {
|
|
|
|
- map[k] = obj[k].id;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return map;
|
|
|
|
|
|
+const DetailDrawerRef = ref();
|
|
|
|
+const searchByX = (obj) => {
|
|
|
|
+ DetailDrawerRef.value.searchByOther(obj);
|
|
};
|
|
};
|
|
-const getRegionIdsMap = (result) => {
|
|
|
|
- let map = {};
|
|
|
|
- let res = cloneDeep(result || {});
|
|
|
|
- for (let k in res) {
|
|
|
|
- map[k] = res[k].id;
|
|
|
|
- }
|
|
|
|
- return map;
|
|
|
|
|
|
+
|
|
|
|
+const toDetail = (row) => {
|
|
|
|
+ searchByX({
|
|
|
|
+ influenceDegree: '',
|
|
|
|
+ supplierId: '',
|
|
|
|
+ areaManagerId: '',
|
|
|
|
+ reason: row.reason,
|
|
|
|
+ });
|
|
};
|
|
};
|
|
-const options21 = computed(() => {
|
|
|
|
- return createStackingBarOption(
|
|
|
|
- buildBarData2(result21.value || {}),
|
|
|
|
- barExtendOption
|
|
|
|
- );
|
|
|
|
-});
|
|
|
|
-const options22 = computed(() => {
|
|
|
|
- let res = result22.value || {};
|
|
|
|
- for (let key in res) {
|
|
|
|
- let oldObj = res[key];
|
|
|
|
- let objNew = Object.keys(oldObj).reduce((o, k) => {
|
|
|
|
- if (ISSUES_REASON_TYPE[k]) {
|
|
|
|
- o[ISSUES_REASON_TYPE[k]] = oldObj[k];
|
|
|
|
- } else {
|
|
|
|
- o[k] = res[k];
|
|
|
|
- }
|
|
|
|
- return o;
|
|
|
|
- }, {});
|
|
|
|
- // res[key] = objNew;
|
|
|
|
- }
|
|
|
|
- return createStackingBarOption(buildBarData2(res), barExtendOption);
|
|
|
|
-});
|
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
<style lang="less" scoped>
|
|
.quality-analysis {
|
|
.quality-analysis {
|
|
.page-main {
|
|
.page-main {
|
|
height: calc(100vh - 57px);
|
|
height: calc(100vh - 57px);
|
|
- min-width: 1000px;
|
|
|
|
- min-height: 600px;
|
|
|
|
- padding: 16px;
|
|
|
|
|
|
+ padding: 15px;
|
|
overflow: auto;
|
|
overflow: auto;
|
|
- display: flex;
|
|
|
|
- justify-content: stretch;
|
|
|
|
-
|
|
|
|
- .col1 {
|
|
|
|
- width: 360px;
|
|
|
|
- margin-right: 16px;
|
|
|
|
- flex-grow: 0;
|
|
|
|
- flex-shrink: 0;
|
|
|
|
- .card {
|
|
|
|
- height: 100%;
|
|
|
|
- }
|
|
|
|
- .chart-wrap {
|
|
|
|
- position: relative;
|
|
|
|
-
|
|
|
|
- &::after {
|
|
|
|
- content: '';
|
|
|
|
- display: block;
|
|
|
|
- position: absolute;
|
|
|
|
- top: 50%;
|
|
|
|
- border-bottom: 1px solid #e5e5e5;
|
|
|
|
- left: 14px;
|
|
|
|
- right: 14px;
|
|
|
|
- z-index: 9;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- .chart-part {
|
|
|
|
- height: 50%;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- .col2 {
|
|
|
|
- flex-grow: 2;
|
|
|
|
- .col2-head {
|
|
|
|
- height: 85px;
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
- }
|
|
|
|
- .col2-body {
|
|
|
|
- height: calc(100% - 85px - 16px);
|
|
|
|
- position: relative;
|
|
|
|
- .card {
|
|
|
|
- position: absolute;
|
|
|
|
- width: calc(50% - 8px);
|
|
|
|
- height: calc(50% - 8px);
|
|
|
|
- &:nth-of-type(1) {
|
|
|
|
- top: 0;
|
|
|
|
- left: 0;
|
|
|
|
- }
|
|
|
|
- &:nth-of-type(2) {
|
|
|
|
- top: 0;
|
|
|
|
- right: 0;
|
|
|
|
- }
|
|
|
|
- &:nth-of-type(3) {
|
|
|
|
- bottom: 0;
|
|
|
|
- left: 0;
|
|
|
|
- }
|
|
|
|
- &:nth-of-type(4) {
|
|
|
|
- bottom: 0;
|
|
|
|
- right: 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- .card {
|
|
|
|
- padding: 16px;
|
|
|
|
- background-color: #fff;
|
|
|
|
- border: 1px solid #e5e5e5;
|
|
|
|
- border-radius: 4px;
|
|
|
|
- display: flex;
|
|
|
|
- flex-direction: column;
|
|
|
|
- justify-content: space-between;
|
|
|
|
-
|
|
|
|
- .title {
|
|
|
|
|
|
+ .scroll-content {
|
|
|
|
+ height: 100%;
|
|
|
|
+ min-width: 1000px;
|
|
display: flex;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
- align-items: center;
|
|
|
|
- flex-grow: 0;
|
|
|
|
- flex-shrink: 0;
|
|
|
|
- .label {
|
|
|
|
- height: 22px;
|
|
|
|
- font-size: 14px;
|
|
|
|
- font-weight: 500;
|
|
|
|
- color: #262626;
|
|
|
|
- line-height: 22px;
|
|
|
|
- }
|
|
|
|
- .t-button {
|
|
|
|
- padding: 0;
|
|
|
|
- border: none !important;
|
|
|
|
- outline: none;
|
|
|
|
- background-color: transparent !important;
|
|
|
|
- height: auto;
|
|
|
|
- color: #595959;
|
|
|
|
- :deep(div) {
|
|
|
|
- display: none;
|
|
|
|
|
|
+ .col1,
|
|
|
|
+ .col2 {
|
|
|
|
+ width: calc(50% - 8px);
|
|
|
|
+ :deep(.t-table__body) {
|
|
|
|
+ font-size: 13px !important;
|
|
|
|
+ .t-link {
|
|
|
|
+ font-size: 13px !important;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ .module-title {
|
|
|
|
+ height: 30px;
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ color: #000;
|
|
|
|
+ text-align: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- .chart-wrap {
|
|
|
|
- flex-grow: 2;
|
|
|
|
- overflow: hidden;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
</style>
|