work.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <template>
  2. <div class="quality-seats-container layout-pd">
  3. <el-row :gutter="10">
  4. <el-col class="mb10">
  5. <el-card shadow="never">
  6. <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
  7. <el-form-item prop="crTime">
  8. <statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="gridOptions.loading" />
  9. </el-form-item>
  10. <el-form-item>
  11. <el-button type="primary" @click="handleQuery" :loading="gridOptions.loading">
  12. <SvgIcon name="ele-Search" class="mr5" />查询
  13. </el-button>
  14. <el-button @click="resetQuery(ruleFormRef)" class="default-button" :loading="gridOptions.loading">
  15. <SvgIcon name="ele-Refresh" class="mr5" />重置
  16. </el-button>
  17. </el-form-item>
  18. </el-form>
  19. </el-card>
  20. </el-col>
  21. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mb10">
  22. <el-card shadow="never" style="height: 400px">
  23. <vxe-grid v-bind="gridOptions" v-on="gridEvents" ref="gridRef"> </vxe-grid>
  24. </el-card>
  25. </el-col>
  26. <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mb10">
  27. <el-card shadow="never" style="height: 400px">
  28. <div class="flex-center-between">
  29. <h3></h3>
  30. <div>
  31. <el-select-v2
  32. v-model="AcceptorId"
  33. filterable
  34. :options="seatUser"
  35. :props="{
  36. label: 'value',
  37. value: 'key',
  38. }"
  39. placeholder="请选择坐席"
  40. clearable
  41. @change="getSeatsData"
  42. style="width: 240px"
  43. />
  44. </div>
  45. </div>
  46. <v-chart :option="option" :loading="seatsLoading" autoresize style="height: 320px" />
  47. </el-card>
  48. </el-col>
  49. <el-col>
  50. <el-card shadow="never" style="height: 400px">
  51. <div class="flex-center-between">
  52. <h3></h3>
  53. <div class="flex">
  54. <input-number-range v-model="state.queryParams.value" @change="getYearData" />
  55. <el-select-v2
  56. v-model="AcceptorId1"
  57. filterable
  58. :options="seatUser"
  59. :props="{
  60. label: 'value',
  61. value: 'key',
  62. }"
  63. placeholder="请选择坐席"
  64. clearable
  65. @change="getYearData"
  66. style="width: 240px"
  67. />
  68. </div>
  69. </div>
  70. <v-chart :option="option1" :loading="yearLoading" autoresize style="height: 320px" />
  71. </el-card>
  72. </el-col>
  73. </el-row>
  74. </div>
  75. </template>
  76. <script setup lang="tsx" name="qualityWork">
  77. import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
  78. import { FormInstance } from 'element-plus';
  79. import { defaultDate } from '@/utils/constants';
  80. import Other from '@/utils/other';
  81. import { qualityOverview, qualityOverviewExport, qualityScore, qualityScoreBase, qualityScoreGrade } from '@/api/quality';
  82. const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
  83. const InputNumberRange = defineAsyncComponent(() => import('@/components/NumberRange/index.vue')); // 数字区间
  84. // 定义变量内容
  85. const state = reactive<any>({
  86. queryParams: {
  87. // 查询条件
  88. crTime: defaultDate,
  89. SortField: null,
  90. SortRule: null,
  91. value: [0, 100],
  92. AcceptorId: null,
  93. },
  94. loading: false, // 加载
  95. total: 0, // 总数
  96. });
  97. const requestParams = ref<EmptyObjectType>({});
  98. const gridOptions = reactive<any>({
  99. loading: false,
  100. border: true,
  101. showOverflow: true,
  102. columnConfig: {
  103. resizable: true,
  104. },
  105. scrollY: {
  106. enabled: true,
  107. gt: 100,
  108. },
  109. toolbarConfig: {
  110. zoom: true,
  111. custom: true,
  112. refresh: {
  113. queryMethod: () => {
  114. handleQuery();
  115. },
  116. },
  117. tools: [{ toolRender: { name: 'exportCurrent' } }, { toolRender: { name: 'exportAll' } }],
  118. },
  119. customConfig: {
  120. storage: true,
  121. },
  122. height: 360,
  123. id: 'qualityWork',
  124. rowConfig: { isHover: true, height: 30, isCurrent: true, useKey: true },
  125. columns: [
  126. { field: 'qualityItem', title: '质检项' },
  127. { field: 'orderNum', title: '质检工单数', sortable: true },
  128. { field: 'rate', title: '占比' },
  129. ],
  130. data: [],
  131. params: {
  132. exportMethod: qualityOverviewExport,
  133. exportParams: requestParams,
  134. },
  135. sortConfig: {
  136. remote: true,
  137. },
  138. });
  139. /** 搜索按钮操作 */
  140. const handleQuery = () => {
  141. queryList();
  142. };
  143. /** 获取列表 */
  144. const queryList = async () => {
  145. gridOptions.loading = true;
  146. requestParams.value = Other.deepClone(state.queryParams);
  147. requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
  148. requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
  149. Reflect.deleteProperty(requestParams.value, 'crTime');
  150. requestParams.value.MinGrade = state.queryParams.value[0];
  151. requestParams.value.MaxGrade = state.queryParams.value[1];
  152. Reflect.deleteProperty(requestParams.value, 'value');
  153. try {
  154. const { result } = await qualityOverview(requestParams.value);
  155. gridOptions.data = result ?? [];
  156. gridOptions.loading = false;
  157. } catch (e) {
  158. gridOptions.loading = false;
  159. console.log(e);
  160. }
  161. };
  162. const gridEvents = {
  163. sortChange(val: any) {
  164. state.queryParams.SortField = val.order ? val.field : null;
  165. // 0 升序 1 降序
  166. state.queryParams.SortRule = val.order ? (val.order == 'desc' ? 1 : 0) : null;
  167. handleQuery();
  168. },
  169. };
  170. // 获取坐席质检分值分析
  171. const seatsLoading = ref(false);
  172. const AcceptorId = ref<string | number | null>(null);
  173. const getSeatsData = async () => {
  174. seatsLoading.value = true;
  175. try {
  176. const { result } = await qualityScore({ ...requestParams.value, AcceptorId: AcceptorId.value });
  177. const legendData = ['90-100分', '80-90分', '70-80分', '60-70分', '60分以下'];
  178. const data = [
  179. {
  180. value: result.ninetyGrade,
  181. name: '90-100分',
  182. },
  183. {
  184. value: result.eightyGrade,
  185. name: '80-90分',
  186. },
  187. {
  188. value: result.seventyGrade,
  189. name: '70-80分',
  190. },
  191. {
  192. value: result.sixtyGrade,
  193. name: '60-70分',
  194. },
  195. {
  196. value: result.fiftyGrade,
  197. name: '60分以下',
  198. },
  199. ];
  200. setOption(legendData, data);
  201. seatsLoading.value = false;
  202. } catch (e) {
  203. console.log(e);
  204. seatsLoading.value = false;
  205. }
  206. };
  207. const option = ref<any>({});
  208. // 坐席质检分值分析
  209. const setOption = (legendData: string[], data: any) => {
  210. option.value = {
  211. title: {
  212. text: '坐席质检分值分析',
  213. left: '0',
  214. },
  215. tooltip: {
  216. formatter: '{b0}: {c0} ({d}%)',
  217. },
  218. toolbox: {
  219. show: true,
  220. orient: 'horizontal',
  221. showTitle: true,
  222. feature: {
  223. saveAsImage: {
  224. type: 'png',
  225. show: true,
  226. title: '保存为图片',
  227. },
  228. },
  229. },
  230. legend: [
  231. {
  232. top: '20%',
  233. right: '20',
  234. orient: 'vertical',
  235. data: legendData,
  236. },
  237. ],
  238. series: [
  239. {
  240. type: 'pie',
  241. radius: ['0%', '80%'],
  242. roseType: 'area',
  243. label: {
  244. show: true,
  245. overflow: 'none',
  246. formatter: function (params: any) {
  247. if (params.name !== '') {
  248. return `${params.name}:${params.data.value}(${params.percent}%)`;
  249. }
  250. },
  251. },
  252. data: data,
  253. },
  254. ],
  255. };
  256. };
  257. const AcceptorId1 = ref<string | number | null>(null);
  258. const yearLoading = ref(false);
  259. // 获取近一年质检分值分析
  260. const getYearData = async () => {
  261. yearLoading.value = true;
  262. try {
  263. const { result } = await qualityScoreGrade({ ...requestParams.value, AcceptorId: AcceptorId1.value });
  264. console.log(result, 'result');
  265. const xData = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
  266. const data = result.map((item: any) => item.num);
  267. // const data = [1,2,45,213,45,65,7,8,1,23]
  268. setOption1(data, xData);
  269. yearLoading.value = false;
  270. } catch (e) {
  271. console.log(e);
  272. yearLoading.value = false;
  273. }
  274. };
  275. const option1 = ref<any>({});
  276. // 近一年质检分值分析
  277. const setOption1 = (data: any, xData: any) => {
  278. option1.value = {
  279. title: {
  280. text: '近一年质检分值分析',
  281. left: '0',
  282. },
  283. legend: {
  284. show: false,
  285. },
  286. tooltip: {
  287. show: true,
  288. trigger: 'item',
  289. },
  290. grid: {
  291. top: '20%',
  292. left: '5%',
  293. right: '1%',
  294. bottom: '12%',
  295. },
  296. toolbox: {
  297. show: true,
  298. orient: 'horizontal',
  299. showTitle: true,
  300. feature: {
  301. saveAsImage: {
  302. type: 'png',
  303. show: true,
  304. title: '保存为图片',
  305. },
  306. },
  307. },
  308. xAxis: [
  309. {
  310. type: 'category',
  311. data: xData,
  312. axisTick: {
  313. show: false, // 是否显示坐标轴轴线
  314. },
  315. axisLabel: {
  316. color: '#000',
  317. fontSize: 14,
  318. },
  319. splitLine: {
  320. show: false,
  321. },
  322. boundaryGap: true,
  323. axisLine: {
  324. //坐标轴轴线相关设置。
  325. show: true,
  326. inside: false,
  327. },
  328. },
  329. ],
  330. yAxis: [
  331. {
  332. type: 'value',
  333. name: '单位:件',
  334. nameGap: 15,
  335. nameTextStyle: {
  336. fontSize: 14,
  337. color: '#aaa',
  338. align: 'center',
  339. padding: [10, 0, 0, -5],
  340. },
  341. axisLabel: {
  342. //坐标轴刻度标签的相关设置。
  343. show: true,
  344. },
  345. axisLine: {
  346. show: true,
  347. },
  348. axisTick: {
  349. show: false,
  350. },
  351. splitLine: {
  352. show: true,
  353. },
  354. show: true,
  355. },
  356. ],
  357. series: [
  358. {
  359. name: '',
  360. type: 'bar',
  361. barMaxWidth: 30,
  362. data: data,
  363. itemStyle: {
  364. color: {
  365. type: 'linear',
  366. x: 0,
  367. y: 0,
  368. x2: 0,
  369. y2: 1,
  370. colorStops: [
  371. {
  372. offset: 0,
  373. color: 'rgba(31, 129, 255, 1)',
  374. },
  375. {
  376. offset: 1,
  377. color: 'rgba(31, 134, 255, .5)',
  378. },
  379. ],
  380. },
  381. },
  382. label: {
  383. show: true,
  384. position: 'top',
  385. fontSize: 14,
  386. color: '#333',
  387. },
  388. },
  389. {
  390. name: '',
  391. type: 'line',
  392. barMaxWidth: 30,
  393. data: data,
  394. itemStyle: {
  395. color: {
  396. type: 'linear',
  397. x: 0,
  398. y: 0,
  399. x2: 0,
  400. y2: 1,
  401. colorStops: [
  402. {
  403. offset: 0,
  404. color: 'rgba(31, 129, 255, 1)',
  405. },
  406. {
  407. offset: 1,
  408. color: 'rgba(31, 134, 255, .5)',
  409. },
  410. ],
  411. },
  412. },
  413. },
  414. ],
  415. };
  416. };
  417. const ruleFormRef = ref<RefType>();
  418. /** 重置按钮操作 */
  419. const statisticalTimeRef = ref<RefType>();
  420. const resetQuery = (formEl: FormInstance | undefined) => {
  421. if (!formEl) return;
  422. formEl.resetFields();
  423. statisticalTimeRef.value.reset();
  424. queryList();
  425. };
  426. // 获取基础信息
  427. const seatUser = ref<EmptyArrayType>([]);
  428. const getBaseData = async () => {
  429. try {
  430. const { result } = await qualityScoreBase();
  431. seatUser.value = result.seats ?? [];
  432. } catch (e) {
  433. console.log(e);
  434. }
  435. };
  436. onMounted(() => {
  437. queryList();
  438. getSeatsData();
  439. getYearData();
  440. getBaseData();
  441. });
  442. </script>