index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <template>
  2. <home-container ref="homeContainer" :customStyle="{ background: 'none' }" @scroll="handleScroll"
  3. @scroll-end="handleScrollEnd">
  4. <div class="eikon-statistics-container">
  5. <!-- 个人模式显示选项卡 -->
  6. <h-tabs v-if="hasTabs && !hasOldLevel" v-model="currentTab" :tabs="tabList" value-key="type" label-key="title"
  7. @change="handleTabChange" class="h-tabs-fixed" />
  8. <topInfo @department-change="handleDepartmentChange"
  9. :class="['top-info-fixed', (hasTabs && !hasOldLevel) ? 'with-tabs' : 'without-tabs']"
  10. :currentTab="currentTab" />
  11. <!-- 总体概览和详细内容区域 -->
  12. <div class="content-section" :class="(hasTabs && !hasOldLevel) ? 'with-tabs' : 'without-tabs'">
  13. <!-- 总体概览 -->
  14. <div class="overview-section">
  15. <div class="section-title">总体概览</div>
  16. <general-overview :overview-data="overviewData" />
  17. </div>
  18. <!-- 详细内容 -->
  19. <div class="detail-section">
  20. <div class="section-title">详细内容</div>
  21. <!-- 资质能力 -->
  22. <qualification />
  23. <!-- 工作履历 -->
  24. <work-experience />
  25. <!-- 出勤投入 -->
  26. <attendance />
  27. <!-- 工作产出 -->
  28. <work-output />
  29. <!-- 标准执行 -->
  30. <standard-execution />
  31. <!-- 学习成长 -->
  32. <learning-growth />
  33. <!-- 协同配合 -->
  34. <collaboration />
  35. <!-- 主观印象 -->
  36. <subjective-impression />
  37. <!-- 成员明细 -->
  38. <member-details v-if="!isPersonal" @nameClick="handleNameClick" />
  39. </div>
  40. </div>
  41. </div>
  42. <!-- 返回上一级按钮 -->
  43. <div v-if="hasOldLevel" class="back-button" :class="{ 'back-button-hidden': isScrolling }">
  44. <div class="back-circle" @click="goBack">
  45. <div class="back-icon">
  46. <uni-icons type="back" size="40rpx" color="#ffffff"></uni-icons>
  47. </div>
  48. <div class="back-text">返回上一级</div>
  49. </div>
  50. </div>
  51. </home-container>
  52. </template>
  53. <script>
  54. import { mapActions, mapState, mapGetters } from 'vuex'
  55. import HomeContainer from "@/components/HomeContainer.vue";
  56. import HTabs from "@/components/h-tabs/h-tabs.vue";
  57. import { getInfo } from "@/api/login"
  58. import topInfo from "@/pages/eikonStatistics/components/topInfo.vue"
  59. import GeneralOverview from "@/pages/eikonStatistics/components/general-overview.vue"
  60. import StandardExecution from "@/pages/eikonStatistics/components/standard-execution.vue"
  61. import Collaboration from "@/pages/eikonStatistics/components/collaboration.vue"
  62. import Qualification from "@/pages/eikonStatistics/components/Qualification.vue"
  63. import WorkExperience from "@/pages/eikonStatistics/components/WorkExperience.vue"
  64. import Attendance from "@/pages/eikonStatistics/components/Attendance.vue"
  65. import WorkOutput from "@/pages/eikonStatistics/components/WorkOutput.vue"
  66. import LearningGrowth from "@/pages/eikonStatistics/components/LearningGrowth.vue"
  67. import SubjectiveImpression from "@/pages/eikonStatistics/components/SubjectiveImpression.vue"
  68. import MemberDetails from "@/pages/eikonStatistics/components/MemberDetails.vue"
  69. import { getModuleMetrics } from "@/api/eikonStatistics/eikonStatistics"
  70. export default {
  71. components: { HomeContainer, HTabs, topInfo, GeneralOverview, StandardExecution, Collaboration, Qualification, WorkExperience, Attendance, WorkOutput, LearningGrowth, SubjectiveImpression, MemberDetails },
  72. data() {
  73. return {
  74. currentTab: 'personal', // 当前选中的选项卡
  75. isScrolling: false, // 是否正在滚动
  76. // 选项卡列表
  77. tabList: [
  78. {
  79. type: 'personal',
  80. title: '个人'
  81. },
  82. {
  83. type: 'team',
  84. title: '班组'
  85. }
  86. ],
  87. // 概览数据
  88. overviewData: {
  89. // 左侧数据
  90. qualificationAbility: 0,
  91. attendanceInput: 0,
  92. standardExecution: 0,
  93. coordination: 0,
  94. // 右侧数据
  95. workHistory: 0,
  96. workOutput: 0,
  97. learningGrowth: 0,
  98. subjectiveImpression: 0
  99. },
  100. };
  101. },
  102. computed: {
  103. ...mapState({
  104. userInfo: state => state.user.userInfo,
  105. userRoles: state => state.user.roles || [],
  106. currentLevel: state => state.eikonLevel.currentLevel,
  107. currentLevelId: state => state.eikonLevel.currentLevelId,
  108. oldLevel: state => state.eikonLevel.oldLevel,
  109. oldLevelId: state => state.eikonLevel.oldLevelId,
  110. allData: state => state.eikonLevel.allData,
  111. systemData: state => state.eikonLevel.systemData
  112. }),
  113. ...mapGetters('eikonLevel', ['isPersonal', 'isZhanZhang', 'isKeZhang', 'isBanZuZhang']),
  114. hasOldLevel() {
  115. return this.oldLevel && this.oldLevelId
  116. },
  117. hasTabs() {
  118. return this.userRoles.includes('SecurityCheck') || this.userRoles.includes('xiaozuzhang')
  119. }
  120. },
  121. async onLoad() {
  122. // 设置初始级别
  123. await this.setInitialLevel();
  124. // 加载初始数据
  125. await this.loadPageData();
  126. console.log(this.userInfo, "userInfo")
  127. },
  128. methods: {
  129. ...mapActions('eikonLevel', ['changeLevel', 'initLevel', 'saveAllData', 'loadData']),
  130. //根据不同的登录角色,赋予初始级别
  131. async setInitialLevel() {
  132. let isZhanZhang = this.userRoles && this.userRoles.includes('test')
  133. let isKeZhang = this.userRoles && this.userRoles.includes('banzuzhang')
  134. let isZhiJian = this.userRoles && this.userRoles.includes('zhijianke')
  135. let isJingLi = this.userRoles && this.userRoles.includes('bumenjingli')
  136. let level = isKeZhang ? 'department' : isJingLi ? 'brigade' : 'station';
  137. let levelId = isKeZhang ? this.userInfo.departmentId : isJingLi ? this.userInfo.brigadeId : this.userInfo.stationId;
  138. if (isKeZhang || isZhanZhang || isZhiJian || isJingLi) {
  139. this.initLevel({ level: level, levelId: levelId })
  140. } else {
  141. this.initLevel({ level: 'personal', levelId: this.userInfo.userId })
  142. }
  143. },
  144. // 选项卡切换处理
  145. async handleTabChange(tabType, tabItem) {
  146. this.currentTab = tabType;
  147. if (tabType === 'team') {
  148. this.initLevel({ level: 'team', levelId: this.userInfo.teamsId })
  149. } else {
  150. this.initLevel({ level: 'personal', levelId: this.userInfo.userId })
  151. }
  152. // 根据选项卡类型加载不同的数据
  153. await this.loadPageData();
  154. // 滚动到顶部
  155. this.scrollToTop()
  156. },
  157. handleNameClick(row) {
  158. // 滚动到顶部
  159. this.scrollToTop()
  160. },
  161. scrollToTop() {
  162. this.$nextTick(() => {
  163. if (this.$refs.homeContainer && this.$refs.homeContainer.scrollToTop) {
  164. this.$refs.homeContainer.scrollToTop();
  165. }
  166. });
  167. },
  168. // 部门选择变化处理
  169. async handleDepartmentChange(selectedDepartment) {
  170. console.log('部门选择变化:', selectedDepartment);
  171. const { deptType } = selectedDepartment
  172. if (deptType == 'STATION') {
  173. this.initLevel({ level: 'station', levelId: selectedDepartment.id })
  174. }
  175. if (deptType == 'MANAGER') {
  176. this.initLevel({ level: 'department', levelId: selectedDepartment.id })
  177. }
  178. if (deptType == 'TEAMS') {
  179. this.initLevel({ level: 'team', levelId: selectedDepartment.id })
  180. }
  181. if (deptType == 'BRIGADE') {
  182. this.initLevel({ level: 'brigade', levelId: selectedDepartment.id })
  183. }
  184. if (!deptType) {
  185. this.initLevel({ level: 'personal', levelId: selectedDepartment.id })
  186. }
  187. // 重新加载数据
  188. await this.loadPageData();
  189. },
  190. // 加载数据方法
  191. async loadPageData() {
  192. try {
  193. await this.loadData()
  194. } catch (error) {
  195. console.error('加载数据失败:', error);
  196. uni.showToast({
  197. title: '数据加载失败',
  198. icon: 'none'
  199. });
  200. }
  201. },
  202. // 滚动处理
  203. handleScroll() {
  204. this.isScrolling = true
  205. },
  206. // 滚动停止处理
  207. handleScrollEnd() {
  208. this.isScrolling = false
  209. },
  210. // 返回上一级
  211. async goBack() {
  212. this.initLevel({
  213. level: this.oldLevel,
  214. levelId: this.oldLevelId
  215. })
  216. // 重新加载数据
  217. await this.loadPageData();
  218. }
  219. }
  220. }</script>
  221. <style lang="scss" scoped>
  222. .eikon-statistics-container {
  223. padding: 20rpx;
  224. }
  225. // 固定选项卡样式
  226. .h-tabs-fixed {
  227. padding: 22rpx 44rpx;
  228. position: fixed;
  229. top: 78rpx;
  230. left: 0;
  231. right: 0;
  232. z-index: 998;
  233. background: #ffffff;
  234. }
  235. // 固定顶部信息样式
  236. .top-info-fixed {
  237. position: fixed;
  238. left: 0;
  239. right: 0;
  240. z-index: 998;
  241. background: #ffffff;
  242. }
  243. // 当h-tabs显示时,topInfo的top位置
  244. .top-info-fixed.with-tabs {
  245. top: 160rpx;
  246. }
  247. // 当h-tabs不显示时,topInfo的top位置
  248. .top-info-fixed.without-tabs {
  249. top: 78rpx;
  250. }
  251. // 总体概览和详细内容区域样式
  252. .content-section {
  253. /* 为固定的h-tabs和topInfo预留空间 */
  254. }
  255. // 当h-tabs显示时,content-section的margin-top
  256. .content-section.with-tabs {
  257. margin-top: 320rpx;
  258. }
  259. // 当h-tabs不显示时,content-section的margin-top
  260. .content-section.without-tabs {
  261. margin-top: 210rpx;
  262. }
  263. .overview-section,
  264. .detail-section {
  265. margin-bottom: 40rpx;
  266. }
  267. .section-title {
  268. font-size: 32rpx;
  269. font-weight: bold;
  270. color: #333333;
  271. text-align: left;
  272. margin-bottom: 24rpx;
  273. }
  274. .overview-content {
  275. display: flex;
  276. justify-content: space-between;
  277. background: #ffffff;
  278. border-radius: 16rpx;
  279. padding: 32rpx;
  280. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
  281. }
  282. .overview-item {
  283. text-align: center;
  284. flex: 1;
  285. }
  286. .overview-number {
  287. font-size: 48rpx;
  288. font-weight: bold;
  289. color: #1890ff;
  290. margin-bottom: 8rpx;
  291. }
  292. .overview-label {
  293. font-size: 24rpx;
  294. color: #666666;
  295. }
  296. .detail-item {
  297. padding: 24rpx 0;
  298. border-bottom: 1rpx solid #f0f0f0;
  299. &:last-child {
  300. border-bottom: none;
  301. }
  302. }
  303. .detail-header {
  304. display: flex;
  305. justify-content: space-between;
  306. align-items: center;
  307. margin-bottom: 16rpx;
  308. }
  309. .detail-title {
  310. font-size: 28rpx;
  311. font-weight: 500;
  312. color: #333333;
  313. }
  314. .detail-time {
  315. font-size: 24rpx;
  316. color: #999999;
  317. }
  318. .detail-description {
  319. font-size: 26rpx;
  320. color: #666666;
  321. line-height: 1.5;
  322. }
  323. /* 折叠面板内容样式 */
  324. .collapse-content {
  325. padding: 20rpx;
  326. }
  327. .content-item {
  328. display: flex;
  329. align-items: flex-start;
  330. margin-bottom: 16rpx;
  331. &:last-child {
  332. margin-bottom: 0;
  333. }
  334. }
  335. .item-label {
  336. font-size: 26rpx;
  337. color: #333333;
  338. font-weight: 500;
  339. min-width: 120rpx;
  340. margin-right: 16rpx;
  341. }
  342. .item-value {
  343. font-size: 26rpx;
  344. color: #999999;
  345. flex: 1;
  346. line-height: 1.5;
  347. }
  348. /* 查获排名样式 */
  349. .seize-ranking-section {
  350. margin-top: 30rpx;
  351. border-top: 1rpx solid #f0f0f0;
  352. padding-top: 20rpx;
  353. }
  354. .ranking-header {
  355. display: flex;
  356. justify-content: space-between;
  357. align-items: center;
  358. margin-bottom: 20rpx;
  359. }
  360. .ranking-title {
  361. font-size: 28rpx;
  362. font-weight: bold;
  363. color: #333333;
  364. }
  365. .ranking-content {
  366. background: #f8f9fa;
  367. border-radius: 12rpx;
  368. padding: 20rpx;
  369. }
  370. .ranking-list {
  371. display: flex;
  372. flex-direction: column;
  373. gap: 16rpx;
  374. }
  375. .ranking-item {
  376. display: flex;
  377. align-items: center;
  378. padding: 12rpx 16rpx;
  379. background: #ffffff;
  380. border-radius: 8rpx;
  381. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
  382. }
  383. .rank-number {
  384. width: 40rpx;
  385. height: 40rpx;
  386. background: #4873E3;
  387. color: #ffffff;
  388. border-radius: 50%;
  389. display: flex;
  390. align-items: center;
  391. justify-content: center;
  392. font-size: 24rpx;
  393. font-weight: bold;
  394. margin-right: 16rpx;
  395. }
  396. .rank-name {
  397. flex: 1;
  398. font-size: 26rpx;
  399. color: #333333;
  400. font-weight: 500;
  401. }
  402. .rank-count {
  403. font-size: 26rpx;
  404. color: #4873E3;
  405. font-weight: bold;
  406. }
  407. /* 查获排名进度条样式 */
  408. .ranking-progress {
  409. margin-top: 20rpx;
  410. padding: 20rpx;
  411. background: #ffffff;
  412. border-radius: 12rpx;
  413. .progress-text {
  414. font-size: 28rpx;
  415. color: #333;
  416. font-weight: 500;
  417. }
  418. }
  419. /* 学习成长样式 */
  420. .learning-ranking-section {}
  421. /* 返回按钮样式 */
  422. .back-button {
  423. position: fixed;
  424. right: 30rpx;
  425. bottom: 120rpx;
  426. z-index: 999;
  427. transition: transform 0.3s ease-in-out;
  428. }
  429. .back-button-hidden {
  430. transform: translateX(120rpx);
  431. }
  432. .back-circle {
  433. width: 120rpx;
  434. height: 120rpx;
  435. background: linear-gradient(135deg, #4873E3 0%, #6A8EE8 100%);
  436. border-radius: 50%;
  437. display: flex;
  438. flex-direction: column;
  439. align-items: center;
  440. justify-content: center;
  441. box-shadow: 0 8rpx 24rpx rgba(72, 115, 227, 0.3);
  442. cursor: pointer;
  443. transition: all 0.3s ease;
  444. }
  445. .back-circle:active {
  446. transform: scale(0.95);
  447. box-shadow: 0 4rpx 12rpx rgba(72, 115, 227, 0.4);
  448. }
  449. .back-icon {
  450. margin-bottom: 8rpx;
  451. }
  452. .back-text {
  453. font-size: 20rpx;
  454. color: #ffffff;
  455. font-weight: 500;
  456. text-align: center;
  457. margin-bottom: 20rpx;
  458. line-height: 1.2;
  459. }
  460. </style>