健康同学微信公众号h5项目
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

952 líneas
21KB

  1. <!-- -->
  2. <template>
  3. <div class="pageContent insight">
  4. <div class="periodNav">
  5. <div class="periodItem week" :class="{ active: active == 0 }" @click="active = 0">周</div>
  6. <div class="periodItem month" :class="{ active: active == 1 }" @click="active = 1">月</div>
  7. </div>
  8. <div class="periodSwitch">
  9. <div class="arrow arrowLeft" @click="active = active == 0 ? 1 : 0">
  10. <van-icon name="arrow-left"></van-icon>
  11. </div>
  12. <div class="timearea">kdfdfkjhsojldkfho</div>
  13. <div class="arrow arrowRight" @click="active = active == 1 ? 0 : 1">
  14. <van-icon name="arrow"></van-icon>
  15. </div>
  16. </div>
  17. <div class="label">情绪感知</div>
  18. <div class="periodNav">
  19. <div
  20. class="periodItem minPeriodItem"
  21. :class="{ active: emotionActive == index }"
  22. v-for="(item, index) in emotionList"
  23. :key="index"
  24. @click="onEmotionClick(index)"
  25. >
  26. <span>{{ item.name }}</span>
  27. </div>
  28. </div>
  29. <div class="ChatBox">
  30. <div class="legendBox">
  31. <div class="legend">
  32. <div class="legendItem" :class="item.type" v-for="(item, index) in degreeList" :key="index">
  33. <span>{{ item.name }}{{ emotionList[emotionActive].name }}</span>
  34. <span class="number">{{ 120 }}次({{ 120 }}%)</span>
  35. </div>
  36. </div>
  37. </div>
  38. <div class="Chat emotionChat" id="emotionChart"></div>
  39. </div>
  40. <div class="grid">
  41. <div class="gridItem" :style="`background:${item.color};`" v-for="(item, index) in degreeList" :key="index">
  42. <div class="time">
  43. <span class="number">5</span>
  44. <span>天</span>
  45. </div>
  46. <div class="title">{{ item.name }}{{ emotionList[emotionActive].name }}倾向</div>
  47. </div>
  48. </div>
  49. <div class="label">体征感知</div>
  50. <div class="periodNav">
  51. <div
  52. class="periodItem minPeriodItem"
  53. :class="{ active: signActive == index }"
  54. v-for="(item, index) in signList"
  55. :key="index"
  56. @click="onSignClick(index)"
  57. >
  58. <span>{{ item.name }}</span>
  59. </div>
  60. </div>
  61. <div class="ChatBox" :class="{ hiddenChart: signActive == 2 }">
  62. <div class="legendBox minLegendBox">
  63. <div class="legend">
  64. <div class="legendItem" :class="item.type" v-for="(item, index) in signGrid" :key="index">
  65. <span>{{ item.name }}</span>
  66. </div>
  67. </div>
  68. </div>
  69. <div class="Chat signChart" id="signChart"></div>
  70. </div>
  71. <div class="grid" v-if="signList[signActive].grid">
  72. <div
  73. class="gridItem"
  74. :class="{ minItem: signGrid.length % 3 == 0 }"
  75. :style="`background:${item.color};`"
  76. v-for="(item, index) in signGrid"
  77. :key="index"
  78. >
  79. <div class="time">
  80. <span class="number">5</span>
  81. <span>天</span>
  82. </div>
  83. <div class="title">{{ item.name }}</div>
  84. </div>
  85. </div>
  86. <div class="ChatBox minChartBox" :class="{ hiddenChart: signActive != 2 }">
  87. <div class="Chat stepChart" id="stepChart"></div>
  88. </div>
  89. <div class="ChatBox minChartBox" :class="{ hiddenChart: signActive != 2 }">
  90. <div class="Chat calorieChart" id="calorieChart"></div>
  91. </div>
  92. </div>
  93. </template>
  94. <script>
  95. export default {
  96. data() {
  97. return {
  98. active: 0,
  99. emotionActive: 0,
  100. signActive: 0,
  101. emotionChart: null,
  102. signChart: null,
  103. stepChart: null,
  104. calorieChart: null,
  105. emotionList: [
  106. {
  107. name: '疲劳'
  108. },
  109. {
  110. name: '压力'
  111. },
  112. {
  113. name: '焦虑'
  114. }
  115. ],
  116. degreeList: [
  117. {
  118. name: '无',
  119. type: 'not',
  120. color: '#179B3B'
  121. },
  122. {
  123. name: '轻度',
  124. type: 'mild',
  125. color: '#2EA7E0'
  126. },
  127. {
  128. name: '中度',
  129. type: 'moderate',
  130. color: '#8DC21F'
  131. },
  132. {
  133. name: '重度',
  134. type: 'severe',
  135. color: '#FF5F8B'
  136. }
  137. ],
  138. signList: [
  139. {
  140. grid: 'heartRateList',
  141. name: '心率'
  142. },
  143. {
  144. grid: 'temperatureList',
  145. name: '体温'
  146. },
  147. {
  148. grid: null,
  149. name: '运动'
  150. }
  151. ],
  152. heartRateList: [
  153. {
  154. name: '正常',
  155. type: 'moderate',
  156. color: '#179B3B'
  157. },
  158. {
  159. name: '偏高',
  160. type: 'severe',
  161. color: '#FF5F8B'
  162. },
  163. {
  164. name: '偏低',
  165. type: 'mild',
  166. color: '#2EA7E0'
  167. }
  168. ],
  169. temperatureList: [
  170. {
  171. name: '正常',
  172. type: 'moderate',
  173. color: '#179B3B'
  174. },
  175. {
  176. name: '发烧',
  177. type: 'severe',
  178. color: '#FF5F8B'
  179. }
  180. ]
  181. };
  182. },
  183. created() {},
  184. mounted() {
  185. this.initData();
  186. },
  187. watch: {
  188. active(val) {
  189. console.log(val);
  190. this.emotionActive = 0;
  191. this.signActive = 0;
  192. this.$nextTick(() => {
  193. this.initData();
  194. });
  195. },
  196. emotionActive(val) {
  197. console.log(val);
  198. this.$nextTick(() => {
  199. this.initEmotionChart();
  200. });
  201. },
  202. signActive(val) {
  203. console.log(val);
  204. this.$nextTick(() => {
  205. this.initSignChart();
  206. });
  207. }
  208. },
  209. computed: {
  210. degreeMap() {
  211. let map = new Map();
  212. this.degreeList.forEach(item => {
  213. map.set(item.name, item.color);
  214. });
  215. return map;
  216. },
  217. heartRateMap() {
  218. let map = new Map();
  219. this.heartRateList.forEach(item => {
  220. map.set(item.name, item.color);
  221. });
  222. return map;
  223. },
  224. temperatureMap() {
  225. let map = new Map();
  226. this.temperatureList.forEach(item => {
  227. map.set(item.name, item.color);
  228. });
  229. return map;
  230. },
  231. signGrid() {
  232. let list = [];
  233. if (this.signList[this.signActive].grid) {
  234. let temp = this[`${this.signList[this.signActive].grid}`];
  235. if (temp) {
  236. list = temp;
  237. }
  238. }
  239. return list;
  240. }
  241. },
  242. methods: {
  243. initData() {
  244. //初始化图表
  245. if (!this.emotionChart) {
  246. this.emotionChart = this.$echarts.init(document.getElementById('emotionChart'));
  247. }
  248. if (!this.signChart) {
  249. this.signChart = this.$echarts.init(document.getElementById('signChart'));
  250. }
  251. if (!this.stepChart) {
  252. this.stepChart = this.$echarts.init(document.getElementById('stepChart'));
  253. }
  254. if (!this.calorieChart) {
  255. this.calorieChart = this.$echarts.init(document.getElementById('calorieChart'));
  256. }
  257. //渲染图表
  258. this.initEmotionChart();
  259. this.initSignChart();
  260. },
  261. initEmotionChart() {
  262. let option = this.getOption();
  263. //测试
  264. let data = [
  265. {
  266. time: '5.11',
  267. value: 10
  268. },
  269. {
  270. time: '5.12',
  271. value: 20
  272. },
  273. {
  274. time: '5.13',
  275. value: 40
  276. },
  277. {
  278. time: '5.14',
  279. value: 50
  280. },
  281. {
  282. time: '5.15',
  283. value: 60
  284. },
  285. {
  286. time: '5.16',
  287. value: 70
  288. },
  289. {
  290. time: '5.17',
  291. value: 90
  292. }
  293. ];
  294. this.formateEmotionData(option, data);
  295. this.emotionChart.setOption(option);
  296. },
  297. initSignChart() {
  298. let option = this.getOption();
  299. if (this.signActive == 0) {
  300. //测试
  301. let data = [
  302. {
  303. time: '5.11',
  304. value: 50
  305. },
  306. {
  307. time: '5.12',
  308. value: 60
  309. },
  310. {
  311. time: '5.13',
  312. value: 70
  313. },
  314. {
  315. time: '5.14',
  316. value: 80
  317. },
  318. {
  319. time: '5.15',
  320. value: 120
  321. },
  322. {
  323. time: '5.16',
  324. value: 130
  325. },
  326. {
  327. time: '5.17',
  328. value: 150
  329. }
  330. ];
  331. this.formateHeartRateData(option, data);
  332. this.signChart.setOption(option);
  333. } else if (this.signActive == 1) {
  334. //测试
  335. let data = [
  336. {
  337. time: '5.11',
  338. value: 36
  339. },
  340. {
  341. time: '5.12',
  342. value: 38
  343. },
  344. {
  345. time: '5.13',
  346. value: 37
  347. },
  348. {
  349. time: '5.14',
  350. value: 36
  351. },
  352. {
  353. time: '5.15',
  354. value: 38
  355. },
  356. {
  357. time: '5.16',
  358. value: 37
  359. },
  360. {
  361. time: '5.17',
  362. value: 39
  363. }
  364. ];
  365. this.formateTemperatureData(option, data);
  366. this.signChart.setOption(option);
  367. } else if (this.signActive == 2) {
  368. option = this.getOption(false);
  369. let option2 = this.getOption(false);
  370. //测试
  371. let data = [
  372. {
  373. time: '5.11',
  374. value: 36
  375. },
  376. {
  377. time: '5.12',
  378. value: 38
  379. },
  380. {
  381. time: '5.13',
  382. value: 37
  383. },
  384. {
  385. time: '5.14',
  386. value: 36
  387. },
  388. {
  389. time: '5.15',
  390. value: 38
  391. },
  392. {
  393. time: '5.16',
  394. value: 37
  395. },
  396. {
  397. time: '5.17',
  398. value: 39
  399. }
  400. ];
  401. //测试
  402. let data2 = [
  403. {
  404. time: '5.11',
  405. value: 36
  406. },
  407. {
  408. time: '5.12',
  409. value: 38
  410. },
  411. {
  412. time: '5.13',
  413. value: 37
  414. },
  415. {
  416. time: '5.14',
  417. value: 36
  418. },
  419. {
  420. time: '5.15',
  421. value: 38
  422. },
  423. {
  424. time: '5.16',
  425. value: 37
  426. },
  427. {
  428. time: '5.17',
  429. value: 39
  430. }
  431. ];
  432. this.formateStepData(option, data);
  433. this.formateCalorieData(option2, data2);
  434. this.stepChart.setOption(option);
  435. this.calorieChart.setOption(option2);
  436. }
  437. },
  438. formateEmotionData(option, data) {
  439. let xdata = [];
  440. let ydata = [];
  441. let fill = [];
  442. let max = 0;
  443. data.forEach(item => {
  444. if (item.value > max) {
  445. max = item.value;
  446. }
  447. });
  448. data.forEach(item => {
  449. xdata.push(item.time);
  450. fill.push({
  451. value: max - item.value,
  452. label: {
  453. show: false
  454. },
  455. itemStyle: {
  456. color: this.formateEmotionColor(item.value)
  457. }
  458. });
  459. ydata.push({
  460. value: item.value,
  461. label: {
  462. show: true
  463. },
  464. itemStyle: {
  465. color: this.formateEmotionColor(item.value)
  466. }
  467. });
  468. });
  469. option.xAxis.data = xdata;
  470. option.series[0].data = ydata;
  471. option.series[1].data = fill;
  472. },
  473. formateEmotionColor(value) {
  474. if (value > 75) {
  475. return this.degreeMap.get('重度');
  476. } else if (value > 50) {
  477. return this.degreeMap.get('中度');
  478. } else if (value > 25) {
  479. return this.degreeMap.get('轻度');
  480. } else {
  481. return this.degreeMap.get('无');
  482. }
  483. },
  484. formateHeartRateData(option, data) {
  485. let xdata = [];
  486. let ydata = [];
  487. let fill = [];
  488. let max = 0;
  489. data.forEach(item => {
  490. if (item.value > max) {
  491. max = item.value;
  492. }
  493. });
  494. data.forEach(item => {
  495. xdata.push(item.time);
  496. fill.push({
  497. value: max - item.value,
  498. label: {
  499. show: false
  500. },
  501. itemStyle: {
  502. color: this.formateHeartRateColor(item.value)
  503. }
  504. });
  505. ydata.push({
  506. value: item.value,
  507. label: {
  508. show: true
  509. },
  510. itemStyle: {
  511. color: this.formateHeartRateColor(item.value)
  512. }
  513. });
  514. });
  515. option.xAxis.data = xdata;
  516. option.series[0].data = ydata;
  517. option.series[1].data = fill;
  518. // 规定最小值
  519. option.yAxis.min = 40;
  520. // 规定最大值
  521. option.yAxis.max = 150;
  522. //设置高压标线
  523. option.series[1].markLine = {
  524. data: [
  525. {
  526. symbol: 'pin',
  527. silent: true,
  528. yAxis: 120,
  529. lineStyle: {
  530. color: '#C1272D'
  531. },
  532. label: {
  533. color: '#C1272D'
  534. }
  535. }
  536. ]
  537. };
  538. },
  539. formateHeartRateColor(value) {
  540. if (value > 120) {
  541. return this.heartRateMap.get('偏高');
  542. } else if (value >= 60) {
  543. return this.heartRateMap.get('正常');
  544. } else {
  545. return this.heartRateMap.get('偏低');
  546. }
  547. },
  548. formateTemperatureData(option, data) {
  549. let xdata = [];
  550. let ydata = [];
  551. let fill = [];
  552. let max = 0;
  553. data.forEach(item => {
  554. if (item.value > max) {
  555. max = item.value;
  556. }
  557. });
  558. data.forEach(item => {
  559. xdata.push(item.time);
  560. fill.push({
  561. value: max - item.value,
  562. label: {
  563. show: false
  564. },
  565. itemStyle: {
  566. color: this.formateTemperatureColor(item.value)
  567. }
  568. });
  569. ydata.push({
  570. value: item.value,
  571. label: {
  572. show: true
  573. },
  574. itemStyle: {
  575. color: this.formateTemperatureColor(item.value)
  576. }
  577. });
  578. });
  579. option.xAxis.data = xdata;
  580. option.series[0].data = ydata;
  581. option.series[1].data = fill;
  582. // 规定最小值
  583. option.yAxis.min = 35;
  584. // 规定最大值
  585. option.yAxis.max = max;
  586. },
  587. formateTemperatureColor(value) {
  588. if (value > 38) {
  589. return this.temperatureMap.get('发烧');
  590. } else {
  591. return this.temperatureMap.get('正常');
  592. }
  593. },
  594. formateStepData(option, data) {
  595. let xdata = [];
  596. let ydata = [];
  597. data.forEach(item => {
  598. xdata.push(item.time);
  599. ydata.push({
  600. value: item.value,
  601. label: {
  602. show: true
  603. },
  604. itemStyle: {
  605. color: this.formateTemperatureColor(item.value)
  606. }
  607. });
  608. });
  609. option.xAxis.data = xdata;
  610. option.series[0].data = ydata;
  611. // 隐藏y轴
  612. option.yAxis.show = false;
  613. },
  614. formateCalorieData(option, data) {
  615. let xdata = [];
  616. let ydata = [];
  617. data.forEach(item => {
  618. xdata.push(item.time);
  619. ydata.push({
  620. value: item.value,
  621. label: {
  622. show: true
  623. },
  624. itemStyle: {
  625. color: this.formateTemperatureColor(item.value)
  626. }
  627. });
  628. });
  629. option.xAxis.data = xdata;
  630. option.series[0].data = ydata;
  631. // 隐藏y轴
  632. option.yAxis.show = false;
  633. },
  634. getOption(point = true) {
  635. let option = {
  636. grid: {
  637. top: '10%',
  638. left: '10%',
  639. right: '10%',
  640. bottom: '20%'
  641. },
  642. xAxis: {
  643. type: 'category',
  644. axisLine: {
  645. show: false
  646. },
  647. axisTick: {
  648. show: false
  649. }
  650. },
  651. yAxis: {
  652. type: 'value'
  653. },
  654. series: [
  655. {
  656. type: 'bar',
  657. barMaxWidth: 15,
  658. stack: 'value',
  659. stackStrategy: 'all',
  660. animation: false,
  661. label: {
  662. show: true,
  663. width: 15,
  664. height: 15,
  665. backgroundColor: '#fff',
  666. borderRadius: 50,
  667. borderWidth: 3,
  668. borderColor: 'inherit',
  669. verticalAlign: 'middle',
  670. position: 'insideTop',
  671. formatter: function () {
  672. return '';
  673. }
  674. },
  675. itemStyle: {
  676. borderRadius: [0, 0, 12, 12]
  677. }
  678. },
  679. {
  680. type: 'bar',
  681. barMaxWidth: 15,
  682. stack: 'value',
  683. stackStrategy: 'all',
  684. animation: false,
  685. itemStyle: {
  686. borderRadius: [12, 12, 0, 0]
  687. }
  688. }
  689. ]
  690. };
  691. if (!point) {
  692. option.series = [
  693. {
  694. type: 'bar',
  695. barMaxWidth: 15,
  696. label: {
  697. show: true,
  698. color: '#000',
  699. position: 'outside',
  700. },
  701. itemStyle: {
  702. borderRadius: 12
  703. }
  704. },
  705. ]
  706. }
  707. return option;
  708. },
  709. onEmotionClick(index) {
  710. this.emotionActive = index;
  711. // 情绪选项变化重新渲染图表
  712. },
  713. onSignClick(index) {
  714. this.signActive = index;
  715. // 体征选项变化重新渲染图表
  716. }
  717. }
  718. };
  719. </script>
  720. <style scoped lang="scss">
  721. @import './include.scss';
  722. .insight {
  723. padding-top: 20px;
  724. .periodNav {
  725. display: flex;
  726. justify-content: space-around;
  727. align-items: center;
  728. .periodItem {
  729. width: 48%;
  730. font-size: 28px;
  731. color: black;
  732. text-align: center;
  733. padding: 12px;
  734. border-radius: 30px;
  735. background: #e6e6e6;
  736. &.active {
  737. color: white;
  738. background: #179b3b;
  739. }
  740. }
  741. .minPeriodItem {
  742. width: 32%;
  743. padding: 8px;
  744. }
  745. }
  746. .periodSwitch {
  747. display: flex;
  748. width: 100%;
  749. justify-content: space-between;
  750. align-items: center;
  751. padding: 40px 10px;
  752. padding-bottom: 10px;
  753. .timearea {
  754. font-size: 28px;
  755. justify-self: center;
  756. color: gray;
  757. }
  758. .arrow {
  759. font-size: 45px;
  760. }
  761. }
  762. .label {
  763. font-size: 40px;
  764. font-weight: bold;
  765. padding: 30px 0;
  766. }
  767. .ChatBox {
  768. overflow: hidden;
  769. width: calc(100vw - 60px);
  770. height: 600px;
  771. border: 2px solid #cdf348;
  772. border-radius: 70px;
  773. margin-top: 30px;
  774. .legendBox {
  775. width: 100%;
  776. height: 150px;
  777. display: flex;
  778. justify-content: flex-end;
  779. align-items: center;
  780. padding: 0 30px;
  781. .legend {
  782. display: flex;
  783. flex-wrap: wrap;
  784. justify-content: flex-end;
  785. align-items: center;
  786. font-size: 20px;
  787. .legendItem {
  788. position: relative;
  789. width: 205px;
  790. margin-top: 10px;
  791. margin-right: 20px;
  792. &:nth-child(2n) {
  793. margin-right: 0;
  794. }
  795. }
  796. .not {
  797. .number {
  798. color: #179b3b;
  799. }
  800. &::before {
  801. content: '';
  802. position: absolute;
  803. top: 0;
  804. bottom: 0;
  805. left: -20px;
  806. width: 15px;
  807. height: 15px;
  808. background: #179b3b;
  809. border-radius: 50%;
  810. margin: auto;
  811. }
  812. }
  813. .mild {
  814. .number {
  815. color: #2ea7e0;
  816. }
  817. &::before {
  818. content: '';
  819. position: absolute;
  820. top: 0;
  821. bottom: 0;
  822. left: -20px;
  823. width: 15px;
  824. height: 15px;
  825. background: #2ea7e0;
  826. border-radius: 50%;
  827. margin: auto;
  828. }
  829. }
  830. .moderate {
  831. .number {
  832. color: #8dc21f;
  833. }
  834. &::before {
  835. content: '';
  836. position: absolute;
  837. top: 0;
  838. bottom: 0;
  839. left: -20px;
  840. width: 15px;
  841. height: 15px;
  842. background: #8dc21f;
  843. border-radius: 50%;
  844. margin: auto;
  845. }
  846. }
  847. .severe {
  848. .number {
  849. color: #ff5f8b;
  850. }
  851. &::before {
  852. content: '';
  853. position: absolute;
  854. top: 0;
  855. bottom: 0;
  856. left: -20px;
  857. width: 15px;
  858. height: 15px;
  859. background: #ff5f8b;
  860. border-radius: 50%;
  861. margin: auto;
  862. }
  863. }
  864. }
  865. }
  866. .minLegendBox {
  867. .legend {
  868. .legendItem {
  869. width: auto;
  870. margin-right: 50px;
  871. &:nth-child(2n) {
  872. margin-right: 50px;
  873. }
  874. &:last-child {
  875. margin-right: 0;
  876. }
  877. }
  878. }
  879. }
  880. .Chat {
  881. width: calc(100vw - 60px);
  882. height: 450px;
  883. }
  884. }
  885. .minChartBox {
  886. height: 450px;
  887. }
  888. .hiddenChart {
  889. width: 0;
  890. height: 0;
  891. }
  892. .grid {
  893. display: flex;
  894. flex-wrap: wrap;
  895. justify-content: space-between;
  896. color: white;
  897. font-size: 30px;
  898. .gridItem {
  899. width: calc((100vw - 60px) * 0.48);
  900. height: calc((100vw - 60px) * 0.48);
  901. border-radius: 60px;
  902. display: flex;
  903. flex-direction: column;
  904. justify-content: center;
  905. align-items: center;
  906. margin-top: 30px;
  907. .time {
  908. .number {
  909. font-size: 90px;
  910. font-weight: bold;
  911. }
  912. }
  913. }
  914. .minItem {
  915. width: calc((100vw - 60px) * 0.32);
  916. height: calc((100vw - 60px) * 0.32);
  917. font-size: 28px;
  918. .time {
  919. .number {
  920. font-size: 70px;
  921. font-weight: bold;
  922. }
  923. }
  924. }
  925. }
  926. }
  927. </style>