satisfied.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <template>
  2. <div class="statistics-department-satisfied-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd20">
  4. <el-form :model="state.queryParams" ref="ruleFormRef" @submit.native.prevent inline>
  5. <el-form-item prop="crTime">
  6. <statistical-time v-model="state.queryParams.crTime" @change="handleQuery" ref="statisticalTimeRef" :disabled="state.loading"/>
  7. </el-form-item>
  8. <el-form-item label="部门名称" prop="OrgName">
  9. <el-input v-model="state.queryParams.OrgName" placeholder="部门名称" clearable @keyup.enter="handleQuery" class="keyword-input" />
  10. </el-form-item>
  11. <el-form-item>
  12. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  13. <el-button @click="drawer = true" class="default-button"> <SvgIcon name="ele-Search" class="mr5" />更多查询</el-button>
  14. <el-popover :width="500" trigger="click">
  15. <template #reference>
  16. <el-button type="primary" title="口径说明"><SvgIcon name="ele-QuestionFilled" class="mr5"/>口径说明</el-button>
  17. </template>
  18. <el-descriptions title="" :column="1" border style="max-height: 400px; overflow: auto">
  19. <el-descriptions-item label="部门名称">工单新建时用户所在的部门</el-descriptions-item>
  20. <el-descriptions-item label="部门类别">部门类型</el-descriptions-item>
  21. <el-descriptions-item label="小计">部门发起工单的总数量</el-descriptions-item>
  22. <el-descriptions-item label="总满意率">(小计-不满意)/小计</el-descriptions-item>
  23. <el-descriptions-item label="非常满意">已回访-部门评价-办件结果为非常满意</el-descriptions-item>
  24. <el-descriptions-item label="非常满意率">非常满意/小计</el-descriptions-item>
  25. <el-descriptions-item label="满意">已回访-部门评价-办件结果为满意</el-descriptions-item>
  26. <el-descriptions-item label="满意率">满意/小计</el-descriptions-item>
  27. <el-descriptions-item label="视为满意">甄别申请-不满意件-修改为满意件</el-descriptions-item>
  28. <el-descriptions-item label="视为满意率">视为满意/小计</el-descriptions-item>
  29. <el-descriptions-item label="一般">已回访-部门评价-办件结果为一般</el-descriptions-item>
  30. <el-descriptions-item label="一般率">一般/小计</el-descriptions-item>
  31. <el-descriptions-item label="不满意">已回访-部门评价-办件结果为不满意</el-descriptions-item>
  32. <el-descriptions-item label="不满意率">不满意/小计</el-descriptions-item>
  33. <el-descriptions-item label="非常不满意">已回访-部门评价-办件结果为非常不满意</el-descriptions-item>
  34. <el-descriptions-item label="非常不满意率">非常不满意/小计</el-descriptions-item>
  35. <el-descriptions-item label="未做评价">已回访-部门评价-办件结果为未做评价</el-descriptions-item>
  36. <el-descriptions-item label="未做评价率">未做评价/小计</el-descriptions-item>
  37. <el-descriptions-item label="未接通">已回访-部门评价-办件结果为未接通</el-descriptions-item>
  38. <el-descriptions-item label="未接通率">未接通/小计</el-descriptions-item>
  39. </el-descriptions>
  40. </el-popover>
  41. <el-button type="primary" @click="onDetailList" :loading="state.loading"> <SvgIcon name="ele-List" class="mr5" />列表明细 </el-button>
  42. </el-form-item>
  43. </el-form>
  44. <vxe-toolbar
  45. ref="toolbarRef"
  46. :loading="state.loading"
  47. custom
  48. :refresh="{
  49. queryMethod: handleQuery,
  50. }"
  51. :tools="[{ toolRender: { name: 'exportAll' } }]"
  52. >
  53. </vxe-toolbar>
  54. <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
  55. <vxe-table
  56. border
  57. :loading="state.loading"
  58. :data="state.tableData"
  59. :column-config="{ resizable: true }"
  60. :row-config="{ isCurrent: true, isHover: true, height: 30, useKey: true }"
  61. ref="tableRef"
  62. height="auto"
  63. auto-resize
  64. show-overflow
  65. :scrollY="{ enabled: true, gt: 100 }"
  66. id="statisticsDepartmentSatisfied"
  67. :custom-config="{
  68. storage: true,
  69. }"
  70. showHeaderOverflow
  71. :params="{ exportMethod: departmentSatisfactionExport, exportParams: requestParams }"
  72. show-footer
  73. :footer-method="footerMethod"
  74. >
  75. <vxe-column field="orgName" title="部门名称" min-width="200" fixed="left">
  76. <template #default="scope">
  77. <el-button type="primary" link @click="onDetailOrg(scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  78. {{ scope.row.orgName }}
  79. </el-button>
  80. <span v-else>{{ scope.row.orgName }}</span>
  81. </template>
  82. </vxe-column>
  83. <vxe-column field="orgTypeText" title="部门类别" min-width="100" fixed="left"> </vxe-column>
  84. <vxe-column field="totalSumRateText" title="总满意率" min-width="120"> </vxe-column>
  85. <vxe-column field="totalSumCount" title="小计" min-width="90" fixed="left"> </vxe-column>
  86. <vxe-column field="verySatisfiedCount" title="非常满意" min-width="100">
  87. <template #default="scope">
  88. <el-button type="primary" link @click="linkDetail(scope.row.verySatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  89. {{ scope.row.verySatisfiedCount }}
  90. </el-button>
  91. <span v-else>{{ scope.row.verySatisfiedCount }}</span>
  92. </template>
  93. </vxe-column>
  94. <vxe-column field="verySatisfiedRateText" title="非常满意率" min-width="120"> </vxe-column>
  95. <vxe-column field="satisfiedCount" title="满意" min-width="100">
  96. <template #default="scope">
  97. <el-button type="primary" link @click="linkDetail(scope.row.satisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  98. {{ scope.row.satisfiedCount }}
  99. </el-button>
  100. <span v-else>{{ scope.row.satisfiedCount }}</span>
  101. </template>
  102. </vxe-column>
  103. <vxe-column field="satisfiedRateText" title="满意率" min-width="120"> </vxe-column>
  104. <!-- 自贡字段 -->
  105. <template v-if="['ZiGong'].includes(themeConfig.appScope)">
  106. <vxe-column field="normalCount" title="一般" min-width="100">
  107. <template #default="scope">
  108. <el-button type="primary" link @click="linkDetail(scope.row.normalKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  109. {{ scope.row.normalCount }}
  110. </el-button>
  111. <span v-else>{{ scope.row.normalCount }}</span>
  112. </template>
  113. </vxe-column>
  114. <vxe-column field="normalRateText" title="一般率" min-width="120"> </vxe-column>
  115. </template>
  116. <template v-if="['ZiGong'].includes(themeConfig.appScope)">
  117. <vxe-column field="regardedAsSatisfiedCount" title="甄别为满意" min-width="100">
  118. <template #default="scope">
  119. <el-button type="primary" link @click="linkDetail(scope.row.regardedAsSatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  120. {{ scope.row.regardedAsSatisfiedCount }}
  121. </el-button>
  122. <span v-else>{{ scope.row.regardedAsSatisfiedCount }}</span>
  123. </template>
  124. </vxe-column>
  125. <vxe-column field="regardedAsSatisfiedRateText" title="甄别为满意率" min-width="120"> </vxe-column>
  126. </template>
  127. <template v-else>
  128. <vxe-column field="regardedAsSatisfiedCount" title="视为满意" min-width="100">
  129. <template #default="scope">
  130. <el-button type="primary" link @click="linkDetail(scope.row.regardedAsSatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  131. {{ scope.row.regardedAsSatisfiedCount }}
  132. </el-button>
  133. <span v-else>{{ scope.row.regardedAsSatisfiedCount }}</span>
  134. </template>
  135. </vxe-column>
  136. <vxe-column field="regardedAsSatisfiedRateText" title="视为满意率" min-width="120"> </vxe-column>
  137. </template>
  138. <vxe-column field="defaultSatisfiedCount" title="默认满意" min-width="100">
  139. <template #default="scope">
  140. <el-button type="primary" link @click="linkDetail(scope.row.defaultSatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  141. {{ scope.row.defaultSatisfiedCount }}
  142. </el-button>
  143. <span v-else>{{ scope.row.defaultSatisfiedCount }}</span>
  144. </template>
  145. </vxe-column>
  146. <vxe-column field="defaultSatisfiedRateText" title="默认满意率" min-width="120"> </vxe-column>
  147. <vxe-column field="noSatisfiedCount" title="不满意" min-width="100">
  148. <template #default="scope">
  149. <el-button type="primary" link @click="linkDetail(scope.row.noSatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  150. {{ scope.row.noSatisfiedCount }}
  151. </el-button>
  152. <span v-else>{{ scope.row.noSatisfiedCount }}</span>
  153. </template>
  154. </vxe-column>
  155. <vxe-column field="noSatisfiedRateText" title="不满意率" min-width="120"> </vxe-column>
  156. <!-- 自贡字段 -->
  157. <template v-if="['ZiGong'].includes(themeConfig.appScope)">
  158. <vxe-column field="veryNoSatisfiedCount" title="非常不满意" min-width="100">
  159. <template #default="scope">
  160. <el-button type="primary" link @click="linkDetail(scope.row.veryNoSatisfiedKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  161. {{ scope.row.veryNoSatisfiedCount }}
  162. </el-button>
  163. <span v-else>{{ scope.row.veryNoSatisfiedCount }}</span>
  164. </template>
  165. </vxe-column>
  166. <vxe-column field="veryNoSatisfiedRateText" title="非常不满意率" min-width="120"> </vxe-column>
  167. </template>
  168. <vxe-column field="noEvaluateCount" title="未作评价" min-width="100">
  169. <template #default="scope">
  170. <el-button type="primary" link @click="linkDetail(scope.row.noEvaluateKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  171. {{ scope.row.noEvaluateCount }}
  172. </el-button>
  173. <span v-else>{{ scope.row.noEvaluateCount }}</span>
  174. </template>
  175. </vxe-column>
  176. <vxe-column field="noEvaluateRateText" title="未作评价率" min-width="120"> </vxe-column>
  177. <vxe-column field="noPutThroughCount" title="未接通" min-width="100">
  178. <template #default="scope">
  179. <el-button type="primary" link @click="linkDetail(scope.row.noPutThroughKey, scope.row)" v-if="!['市直合计', '区县合计'].includes(scope.row.orgName)">
  180. {{ scope.row.noPutThroughCount }}
  181. </el-button>
  182. <span v-else>{{ scope.row.noPutThroughCount }}</span>
  183. </template>
  184. </vxe-column>
  185. <vxe-column field="noPutThroughRateText" title="未接通率" min-width="120"> </vxe-column>
  186. </vxe-table>
  187. </div>
  188. </div>
  189. <!-- 更多查询 -->
  190. <el-drawer v-model="drawer" title="更多查询" size="500px">
  191. <el-form :model="state.queryParams" ref="drawerRuleFormRef" @submit.native.prevent label-width="100px">
  192. <el-form-item label="归档类型" prop="TypeId" v-if="['ZiGong','LuZhou'].includes(themeConfig.appScope)">
  193. <el-select v-model="state.queryParams.TypeId" placeholder="归档类型" @change="handleQuery">
  194. <el-option label="办件结果" value="1" />
  195. <el-option label="办件态度" value="2" />
  196. </el-select>
  197. </el-form-item>
  198. <el-form-item label="热线号码" prop="LineNum">
  199. <el-select v-model="state.queryParams.LineNum" placeholder="请选择热线号码" clearable @change="handleQuery">
  200. <el-option v-for="item in state.callForwardingSource" :value="item.dicDataValue" :key="item.dicDataValue" :label="item.dicDataName" />
  201. </el-select>
  202. </el-form-item>
  203. <el-form-item label="回访方式" prop="VisitType">
  204. <el-select v-model="state.queryParams.VisitType" placeholder="请选择回访方式" clearable @change="handleQuery">
  205. <el-option v-for="item in state.visitType" :value="item.key" :key="item.key" :label="item.value" />
  206. </el-select>
  207. </el-form-item>
  208. <el-form-item label="来电主体" prop="TypeCode">
  209. <el-select v-model="state.queryParams.TypeCode" placeholder="请选择来电主体" @change="handleQuery">
  210. <el-option :value="0" label="全部" />
  211. <el-option :value="1" label="市民" />
  212. <el-option :value="2" label="企业" />
  213. </el-select>
  214. </el-form-item>
  215. <!-- <el-form-item label="类型" prop="TypeId">
  216. <el-select v-model="state.queryParams.TypeId" placeholder="类型" @change="handleQuery">
  217. <el-option label="办件结果" value="1" />
  218. <el-option label="办件态度" value="2" />
  219. </el-select>
  220. </el-form-item>-->
  221. </el-form>
  222. <template #footer>
  223. <el-button type="primary" @click="handleQuery" :loading="state.loading"> <SvgIcon name="ele-Search" class="mr5" />查询 </el-button>
  224. <el-button @click="resetQuery(drawerRuleFormRef)" class="default-button"> <SvgIcon name="ele-Refresh" class="mr5" />重置 </el-button>
  225. </template>
  226. </el-drawer>
  227. </div>
  228. </template>
  229. <script setup lang="tsx" name="statisticsDepartmentSatisfied">
  230. import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
  231. import { FormInstance } from 'element-plus';
  232. import { departmentSatisfaction, departmentSatisfactionBase, departmentSatisfactionExport } from '@/api/statistics/department';
  233. import { defaultDate } from '@/utils/constants';
  234. import { useRouter } from 'vue-router';
  235. import { callPeriodBase } from '@/api/statistics/call';
  236. import Other from '@/utils/other';
  237. import { useThemeConfig } from '@/stores/themeConfig';
  238. import { storeToRefs } from 'pinia';
  239. import XEUtils from 'xe-utils';
  240. const StatisticalTime = defineAsyncComponent(() => import('@/components/StatisticalTime/index.vue')); // 日期类型选择组件
  241. // 定义变量内容
  242. const state = reactive<any>({
  243. queryParams: {
  244. // 查询条件
  245. PageIndex: 1,
  246. PageSize: 10,
  247. OrgName: null,
  248. LineNum: null,
  249. crTime: defaultDate, // 时间默认今天开始到今天结束
  250. StartTime: null,
  251. EndTime: null,
  252. TypeId: '1',
  253. TypeCode: 0,
  254. VisitType:null,
  255. },
  256. tableData: [], //表单
  257. loading: false, // 加载
  258. total: 0, // 总数
  259. totalCount: {},
  260. callForwardingSource: [],
  261. visitType:[],
  262. });
  263. const storesThemeConfig = useThemeConfig();
  264. const { themeConfig } = storeToRefs(storesThemeConfig);
  265. /** 搜索按钮操作 */
  266. const handleQuery = () => {
  267. // state.queryParams.PageIndex = 1;
  268. queryList();
  269. };
  270. /** 获取列表 */
  271. const requestParams = ref<EmptyObjectType>({});
  272. const queryList = () => {
  273. state.loading = true;
  274. requestParams.value = Other.deepClone(state.queryParams);
  275. requestParams.value.StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
  276. requestParams.value.EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
  277. Reflect.deleteProperty(requestParams.value, 'crTime');
  278. departmentSatisfaction(requestParams.value)
  279. .then((res: any) => {
  280. state.tableData = res.result?.dataList ?? [];
  281. if (res.result.dataList.length) {
  282. state.tableData.push(res.result.citySumModel);
  283. state.tableData.push(res.result.countySumModel);
  284. }
  285. state.totalCount = res.result.sumModel;
  286. state.loading = false;
  287. })
  288. .catch((err: any) => {
  289. state.loading = false;
  290. });
  291. };
  292. /** 重置按钮操作 */
  293. const drawerRuleFormRef = ref();
  294. const ruleFormRef = ref<RefType>(); // 表单ref
  295. const drawer = ref(false);
  296. const statisticalTimeRef = ref<RefType>();
  297. const resetQuery = (formEl: FormInstance | undefined) => {
  298. if (!formEl) return;
  299. formEl.resetFields();
  300. ruleFormRef.value?.resetFields();
  301. statisticalTimeRef.value.reset();
  302. queryList();
  303. };
  304. // 计算合计
  305. const footerMethod = ({ columns, data }) => {
  306. return [
  307. columns.map((column: any, columnIndex: number) => {
  308. if (columnIndex === 0) {
  309. return '合计';
  310. }
  311. // 后端返回了数据集合 state.totalCount 所以不需要计算 直接进行赋值
  312. return XEUtils.get(state.totalCount, column.property);
  313. }),
  314. ];
  315. };
  316. const router = useRouter();
  317. // 点击部门名称
  318. const onDetailOrg = (row: any) => {
  319. const StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
  320. const EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
  321. router.push({
  322. name: 'statisticsDepartmentSatisfiedOrg',
  323. query: {
  324. StartTime,
  325. EndTime,
  326. OrgCode: row.orgCode,
  327. TypeId: state.queryParams.TypeId,
  328. LineNum: state.queryParams.LineNum,
  329. },
  330. });
  331. };
  332. // 点击数字
  333. const linkDetail = (key: string, row: any) => {
  334. const StartTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[0];
  335. const EndTime = state.queryParams.crTime === null ? null : state.queryParams.crTime[1];
  336. router.push({
  337. name: 'statisticsDepartmentSatisfiedDetail',
  338. query: {
  339. StartTime,
  340. EndTime,
  341. OrgCode: row.orgCode,
  342. TypeId: state.queryParams.TypeId,
  343. LineNum: state.queryParams.LineNum,
  344. DateValue: key,
  345. },
  346. });
  347. };
  348. // 获取基础信息
  349. const getBaseInfo = async () => {
  350. try {
  351. const { result } = await callPeriodBase();
  352. state.callForwardingSource = result.callForwardingSource ?? [];
  353. } catch (e) {
  354. console.log(e);
  355. }
  356. };
  357. // 获取查询基础数据
  358. const getBaseData = async () => {
  359. try {
  360. const { result } = await departmentSatisfactionBase();
  361. state.visitType = result.visitType;
  362. } catch (e) {
  363. console.log(e);
  364. }
  365. }
  366. // 跳转列表明细
  367. const onDetailList = () => {
  368. router.push({
  369. name: 'statisticsDepartmentSatisfiedDetailList',
  370. });
  371. };
  372. const toolbarRef = ref<RefType>();
  373. const tableRef = ref<RefType>();
  374. onMounted(() => {
  375. queryList();
  376. if (tableRef.value && toolbarRef.value) {
  377. tableRef.value.connect(toolbarRef.value);
  378. }
  379. getBaseInfo();
  380. getBaseData();
  381. });
  382. </script>