comprehensiveStatistics.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <template>
  2. <div class="app-container">
  3. <div class="app-content">
  4. <div class="title">综合统计看板</div>
  5. <el-form class="search-form" :inline="true" label-width="90px">
  6. <!-- 时间范围选择 -->
  7. <el-form-item label="时间范围">
  8. <el-date-picker
  9. v-model="dateRange"
  10. type="daterange"
  11. range-separator="至"
  12. start-placeholder="开始日期"
  13. end-placeholder="结束日期"
  14. value-format="yyyy-MM-dd"
  15. :picker-options="pickerOptions">
  16. </el-date-picker>
  17. </el-form-item>
  18. <!-- 统计维度选择 -->
  19. <el-form-item label="统计维度">
  20. <el-select v-model="queryParams.dimension" placeholder="请选择统计维度" @change="handleDimensionChange" clearable>
  21. <el-option label="个人" :value="1"></el-option>
  22. <el-option label="公司" :value="2"></el-option>
  23. <el-option label="部门" :value="3"></el-option>
  24. </el-select>
  25. </el-form-item>
  26. <!-- 公司选择 -->
  27. <el-form-item label="选择公司" v-if="queryParams.dimension">
  28. <el-select
  29. v-model="queryParams.companyId"
  30. placeholder="请选择公司"
  31. @change="handleCompanyChange"
  32. clearable
  33. :disabled="!queryParams.dimension">
  34. <el-option
  35. v-for="company in companyList"
  36. :key="company.companyId"
  37. :label="company.companyName"
  38. :value="company.companyId">
  39. </el-option>
  40. </el-select>
  41. </el-form-item>
  42. <!-- 部门选择 -->
  43. <el-form-item label="选择部门" v-if="showDepartmentSelect">
  44. <el-select
  45. v-model="queryParams.deptId"
  46. placeholder="请选择部门"
  47. @change="handleDeptChange"
  48. clearable
  49. :disabled="!queryParams.companyId">
  50. <el-option
  51. v-for="dept in deptList"
  52. :key="dept.deptId"
  53. :label="dept.deptName"
  54. :value="dept.deptId">
  55. </el-option>
  56. </el-select>
  57. </el-form-item>
  58. <!-- 人员选择 -->
  59. <el-form-item label="选择人员" v-if="showUserSelect">
  60. <el-select
  61. v-model="queryParams.userId"
  62. placeholder="请选择人员"
  63. clearable
  64. :disabled="!queryParams.deptId">
  65. <el-option
  66. v-for="user in userList"
  67. :key="user.userId"
  68. :label="user.userName"
  69. :value="user.userId">
  70. </el-option>
  71. </el-select>
  72. </el-form-item>
  73. <el-form-item>
  74. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  75. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  76. </el-form-item>
  77. </el-form>
  78. </div>
  79. <!-- 数据表格 -->
  80. <div class="table-section">
  81. <el-table :data="paginatedTableData" border style="width: 100%" height="400">
  82. <el-table-column prop="companyName" label="公司名称" />
  83. <el-table-column prop="deptName" label="部门名称" />
  84. <el-table-column prop="userName" label="人员姓名" />
  85. <el-table-column prop="lineNum" label="进线数" />
  86. <el-table-column prop="activeNum" label="激活数" />
  87. <el-table-column prop="completeNum" label="完课数" />
  88. <el-table-column prop="answerNum" label="答题数" />
  89. <el-table-column prop="redPacketNum" label="红包领取数" />
  90. </el-table>
  91. <el-pagination
  92. @size-change="handleSizeChange"
  93. @current-change="handleCurrentChange"
  94. :current-page="currentPage"
  95. :page-sizes="[10, 20, 50, 100]"
  96. :page-size="pageSize"
  97. layout="total, sizes, prev, pager, next, jumper"
  98. :total="tableData.length"
  99. style="margin-top: 20px; text-align: right;">
  100. </el-pagination>
  101. </div>
  102. </div>
  103. </template>
  104. <script>
  105. import { getStatisticsData, getSearchUserInfo, getSearchCompanyInfo, getSearchDeptInfo } from "@/api/statistics/statistics";
  106. export default {
  107. data() {
  108. return {
  109. companyList: [],
  110. deptList: [],
  111. userList: [],
  112. rawData: [],
  113. tableData: [],
  114. currentPage: 1,
  115. pageSize: 20,
  116. dateRange: [],
  117. pickerOptions: {
  118. shortcuts: [{
  119. text: '最近一周',
  120. onClick(picker) {
  121. const end = new Date();
  122. const start = new Date();
  123. start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
  124. picker.$emit('pick', [start, end]);
  125. }
  126. }, {
  127. text: '最近一个月',
  128. onClick(picker) {
  129. const end = new Date();
  130. const start = new Date();
  131. start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
  132. picker.$emit('pick', [start, end]);
  133. }
  134. }, {
  135. text: '最近三个月',
  136. onClick(picker) {
  137. const end = new Date();
  138. const start = new Date();
  139. start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
  140. picker.$emit('pick', [start, end]);
  141. }
  142. }]
  143. },
  144. queryParams: {
  145. dimension: null,
  146. companyId: null,
  147. deptId: null,
  148. userId: null
  149. }
  150. };
  151. },
  152. computed: {
  153. paginatedTableData() {
  154. const start = (this.currentPage - 1) * this.pageSize;
  155. const end = start + this.pageSize;
  156. return this.tableData.slice(start, end);
  157. },
  158. showDepartmentSelect() {
  159. return this.queryParams.dimension &&
  160. (this.queryParams.dimension === 3 || this.queryParams.dimension === 1) &&
  161. this.queryParams.companyId;
  162. },
  163. showUserSelect() {
  164. return this.queryParams.dimension === 1 &&
  165. this.queryParams.companyId &&
  166. this.queryParams.deptId;
  167. }
  168. },
  169. mounted() {
  170. // 设置默认时间为当天
  171. const today = new Date();
  172. this.dateRange = [today, today];
  173. // 默认设置统计维度为公司
  174. this.queryParams.dimension = 2;
  175. this.loadData();
  176. },
  177. methods: {
  178. loadData() {
  179. // 使用 getSearchCompanyInfo 获取公司下拉数据
  180. getSearchCompanyInfo().then(response => {
  181. this.companyList = response.data || [];
  182. // 默认选择第一个公司(如果存在)
  183. if (this.companyList.length > 0) {
  184. this.queryParams.companyId = this.companyList[0].companyId;
  185. // 加载该公司的部门数据
  186. return this.loadDeptData(this.queryParams.companyId);
  187. }
  188. }).then(() => {
  189. // 在公司和部门默认选择完成后,请求统计数据
  190. return this.fetchStatisticsData();
  191. }).catch(error => {
  192. console.error('数据加载失败:', error);
  193. this.$message.error('数据加载失败');
  194. });
  195. },
  196. loadDeptData(companyId) {
  197. return getSearchDeptInfo({ id: companyId }).then(response => {
  198. this.deptList = response.data || [];
  199. // 默认选择第一个部门(如果存在)
  200. if (this.deptList.length > 0) {
  201. this.queryParams.deptId = this.deptList[0].deptId;
  202. }
  203. }).catch(error => {
  204. console.error('部门数据加载失败:', error);
  205. this.$message.error('部门数据加载失败');
  206. });
  207. },
  208. fetchStatisticsData() {
  209. // 构造请求参数对象
  210. const params = {
  211. dimension: this.queryParams.dimension,
  212. startTime: this.formatDate(this.dateRange[0]),
  213. endTime: this.formatDate(this.dateRange[1])
  214. };
  215. // 根据维度设置id参数
  216. if (this.queryParams.dimension === 1 && this.queryParams.userId) {
  217. params.id = this.queryParams.userId;
  218. } else if (this.queryParams.dimension === 2 && this.queryParams.companyId) {
  219. params.id = this.queryParams.companyId;
  220. } else if (this.queryParams.dimension === 3 && this.queryParams.deptId) {
  221. params.id = this.queryParams.deptId;
  222. }
  223. // 以POST方式发送请求体
  224. return getStatisticsData(params).then(response => {
  225. this.rawData = response.data || [];
  226. this.tableData = [...this.rawData];
  227. this.currentPage = 1;
  228. }).catch(error => {
  229. console.error('统计数据加载失败:', error);
  230. this.$message.error('统计数据加载失败');
  231. });
  232. },
  233. formatDate(date) {
  234. if (!date) return '';
  235. const d = new Date(date);
  236. const year = d.getFullYear();
  237. const month = String(d.getMonth() + 1).padStart(2, '0');
  238. const day = String(d.getDate()).padStart(2, '0');
  239. return `${year}-${month}-${day}`;
  240. },
  241. handleCompanyChange(companyId) {
  242. this.queryParams.deptId = null;
  243. this.queryParams.userId = null;
  244. this.deptList = [];
  245. this.userList = [];
  246. if (!companyId) return;
  247. // 使用 getSearchDeptInfo 获取部门下拉数据
  248. getSearchDeptInfo({ id: companyId }).then(response => {
  249. this.deptList = response.data || [];
  250. // 默认选择第一个部门(如果存在)
  251. // 仅在部门维度或个人维度下默认选择第一个部门
  252. if (this.deptList.length > 0 &&
  253. (this.queryParams.dimension === 3 || this.queryParams.dimension === 1)) {
  254. this.queryParams.deptId = this.deptList[0].deptId;
  255. // 如果是个人维度,还需要加载用户数据
  256. if (this.queryParams.dimension === 1 && this.queryParams.deptId) {
  257. this.loadUserData(this.queryParams.deptId);
  258. }
  259. }
  260. }).catch(error => {
  261. console.error('部门数据加载失败:', error);
  262. this.$message.error('部门数据加载失败');
  263. });
  264. },
  265. // 新增加载用户数据的方法
  266. loadUserData(deptId) {
  267. if (!deptId) return;
  268. getSearchUserInfo({ id: deptId }).then(response => {
  269. this.userList = response.data || [];
  270. // 可以选择默认选中第一个用户
  271. if (this.userList.length > 0) {
  272. // this.queryParams.userId = this.userList[0].userId;
  273. }
  274. }).catch(error => {
  275. console.error('人员数据加载失败:', error);
  276. this.$message.error('人员数据加载失败');
  277. });
  278. },
  279. handleDeptChange(deptId) {
  280. this.queryParams.userId = null;
  281. this.userList = [];
  282. if (!deptId) return;
  283. // 使用 getSearchUserInfo 获取人员下拉数据
  284. getSearchUserInfo({ id: deptId }).then(response => {
  285. this.userList = response.data || [];
  286. // 在个人维度下,默认选择第一个用户(可选)
  287. if (this.userList.length > 0 && this.queryParams.dimension === 1) {
  288. // this.queryParams.userId = this.userList[0].userId;
  289. }
  290. }).catch(error => {
  291. console.error('人员数据加载失败:', error);
  292. this.$message.error('人员数据加载失败');
  293. });
  294. },
  295. handleQuery() {
  296. // 触发统计数据请求
  297. this.fetchStatisticsData();
  298. },
  299. handleDimensionChange() {
  300. // 重置后续选择项
  301. this.queryParams.companyId = null;
  302. this.queryParams.deptId = null;
  303. this.queryParams.userId = null;
  304. this.deptList = [];
  305. this.userList = [];
  306. },
  307. resetQuery() {
  308. this.queryParams = {
  309. dimension: null,
  310. companyId: null,
  311. deptId: null,
  312. userId: null
  313. };
  314. // 重置时间为当天
  315. const today = new Date();
  316. this.dateRange = [today, today];
  317. this.deptList = [];
  318. this.userList = [];
  319. this.tableData = [...this.rawData];
  320. this.currentPage = 1;
  321. },
  322. handleSizeChange(val) {
  323. this.pageSize = val;
  324. this.currentPage = 1;
  325. },
  326. handleCurrentChange(val) {
  327. this.currentPage = val;
  328. }
  329. }
  330. };
  331. </script>
  332. <style scoped>
  333. .app-container {
  334. border: 1px solid #e6e6e6;
  335. padding: 12px;
  336. background-color: #f5f7fa;
  337. }
  338. .app-content {
  339. background-color: white;
  340. padding: 20px;
  341. border-radius: 4px;
  342. margin-bottom: 20px;
  343. }
  344. .title {
  345. text-align: center;
  346. font-size: 24px;
  347. font-weight: bold;
  348. color: #333;
  349. margin-bottom: 20px;
  350. }
  351. .search-form {
  352. display: flex;
  353. justify-content: center;
  354. flex-wrap: wrap;
  355. }
  356. .search-form .el-form-item {
  357. margin-bottom: 10px;
  358. }
  359. .table-section {
  360. background-color: white;
  361. padding: 20px;
  362. border-radius: 4px;
  363. }
  364. .table-section .el-table {
  365. width: 100% !important;
  366. }
  367. @media (min-width: 1200px) {
  368. .table-section {
  369. padding: 20px 50px;
  370. }
  371. }
  372. </style>