瀏覽代碼

管理端图表

liushengqiang 2 年之前
父節點
當前提交
dd073c1c59

+ 9 - 1
src/router/routes-common.ts

@@ -270,5 +270,13 @@ export const rootRouter = [
       title: '404',
       platform: paymentType
     }
-  }
+  },
+  {
+    path: '/subject-echarts',
+    name: 'subject-echarts',
+    component: () => import('@/views/subject-echarts/index'),
+    meta: {
+      title: '声部图表'
+    }
+  },
 ]

+ 36 - 0
src/views/subject-echarts/component/CircleProgress.module.less

@@ -0,0 +1,36 @@
+.circle {
+    transform: rotate(-90deg);
+}
+
+.circle-main-box {
+    position: relative;
+    display: block;
+    margin: 0 auto;
+}
+
+.count-num {
+    position: absolute;
+    left: 0;
+    top: 21Px;
+    align-items: center;
+    justify-content: center;
+    display: flex;
+    font-size: 22Px;
+    color: #333;
+    user-select: none;
+    font-weight: bold;
+    width: 100%;
+    text-align: center;
+}
+.countType{
+    transform: scaleX(0.7);
+}
+.des{
+    position: absolute;
+    left: 0;
+    top: 47Px;
+    font-size: 12px;
+    color: #777;
+    width: 100%;
+    text-align: center;
+}

+ 122 - 0
src/views/subject-echarts/component/CircleProgress.tsx

@@ -0,0 +1,122 @@
+import { defineComponent } from 'vue'
+import styles from './CircleProgress.module.less'
+
+export default defineComponent({
+  name: 'CircleProgress',
+  data() {
+    return {
+      now: 0,
+      timer: null as any
+    }
+  },
+  props: {
+    // 进度值
+    value: {
+      type: Number,
+      default: 0
+    },
+    // 尺寸
+    size: {
+      type: Number,
+      default: 120
+    },
+    // 边框粗细
+    strokeWidth: {
+      type: Number,
+      default: 10
+    },
+    // 进度条颜色
+    color: {
+      type: String,
+      default: 'rgba(153,202,251,1)'
+    },
+    // 动画执行时间
+    duration: {
+      type: Number,
+      default: 1000
+    },
+    des:{
+        type: String,
+        default: '总达标率'
+    }
+  },
+  computed: {
+    percentage() {
+      return this.value
+    },
+    countDown() {
+      return this.now
+    },
+    // 圆心x轴坐标
+    cx() {
+      return this.size / 2
+    },
+    // 圆心y轴坐标
+    cy() {
+      return this.size / 2
+    },
+    // 半径
+    radius() {
+      return (this.size - this.strokeWidth) / 2
+    },
+    // 圆周长
+    circumference() {
+      return 2 * Math.PI * this.radius
+    },
+    // 进度长度
+    progress() {
+      return (1 - this.now / 100) * this.circumference
+    }
+  },
+  mounted() {
+    this.run()
+  },
+  methods: {
+    run() {
+      if (this.value == 0) return
+      let t = this.duration / this.value
+      this.timer = setInterval(() => {
+        if (this.now >= this.value) {
+          return clearInterval(this.timer)
+        }
+        this.now++
+      }, t)
+    }
+  },
+  render() {
+    return (
+      <div class={styles['circle-main']}>
+        <div
+          class={styles['circle-main-box']}
+          style={[{ width: this.size + 'px', height: this.size + 'px' }]}
+        >
+          <svg width={this.size} height={this.size} class={styles['circle']}>
+            <circle
+              r={this.radius}
+              cx={this.cx}
+              cy={this.cy}
+              fill="transparent"
+              stroke="#FFE7DF"
+              stroke-width={this.strokeWidth}
+            />
+            <circle
+              r={this.radius}
+              cx={this.cx}
+              cy={this.cy}
+              fill="transparent"
+              stroke={this.color}
+              stroke-width={this.strokeWidth}
+              stroke-linecap="round"
+              stroke-dasharray={this.circumference}
+              stroke-dashoffset={this.progress}
+            />
+          </svg>
+          <span class={styles['count-num']}>
+            {this.countDown}<span class={styles.countType}>%</span>
+          </span>
+          <span class={styles.des}>{this.des}</span>
+        </div>
+      </div>
+    )
+  }
+})

二進制
src/views/subject-echarts/image/icon-backup.png


二進制
src/views/subject-echarts/image/icon-ensemble.png


+ 183 - 0
src/views/subject-echarts/index.module.less

@@ -0,0 +1,183 @@
+.subjectEcharts {
+    padding: 12px;
+}
+
+.container {
+    padding: 14px 12px;
+    border-radius: 10px;
+    background-color: #fff;
+    margin-bottom: 12px;
+}
+
+.head {
+    display: flex;
+    font-size: 15px;
+    color: #333;
+    padding-bottom: 14px;
+
+    .headLeft {
+        display: flex;
+        align-items: center;
+
+        .icon {
+            display: block;
+            width: 18px;
+            height: 18px;
+            margin-right: 8px;
+        }
+    }
+
+    .headRight {
+        margin-left: auto;
+        display: flex;
+        align-items: center;
+
+        &>div {
+            font-size: 10px;
+            border-radius: 12px;
+            border: 1px solid;
+            padding: 0 4px;
+            margin: 0 3px;
+            line-height: 18px;
+        }
+    }
+}
+
+.content {
+    display: flex;
+    align-items: center;
+    padding: 8px 2px;
+}
+
+.echartsMain {
+    margin: 0 -12px;
+    height: 202px;
+}
+
+.items {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    margin-left: 17px;
+    justify-content: space-between;
+
+    .item {
+        .itemNum {
+            display: flex;
+            justify-content: flex-end;
+            align-items: center;
+            font-weight: bold;
+            font-size: 22px;
+            color: #333;
+            margin-bottom: 3px;
+            white-space: nowrap;
+
+            .rect {
+                width: 8px;
+                height: 8px;
+                background: #FF8057;
+                border-radius: 2px;
+                margin-right: 6px;
+            }
+        }
+
+        .itemTitle {
+            font-size: 12px;
+            color: #777;
+            white-space: nowrap;
+        }
+    }
+
+    .line {
+        padding: 0 10px;
+        border-left: 1px solid;
+        border-right: 1px solid;
+        border-image: linear-gradient(to bottom, #EAEAEA, #979797, #EAEAEA) 1;
+    }
+}
+
+.subjectWrap {
+    padding: 0;
+}
+
+.listItem {
+    position: relative;
+    display: flex;
+    align-items: center;
+    padding: 10px 15px 10px 27px;
+
+    &:not(:last-child) {
+        border-bottom: 1px dashed #EAEAEA;
+    }
+
+    .itemLeft {
+        border-right: 1px solid;
+        border-image: linear-gradient(to bottom, #EAEAEA, #979797, #EAEAEA) 1;
+
+        .subjectName {
+            width: 54px;
+            font-size: 14px;
+            font-weight: 600;
+            color: #333;
+            line-height: 18px;
+            margin-bottom: 3px;
+        }
+
+        .subjectType {
+            font-size: 10px;
+            font-weight: 400;
+            color: #666;
+            line-height: 14px;
+        }
+    }
+
+    .dot {
+        position: absolute;
+        left: 15px;
+        top: 15px;
+        width: 4px;
+        height: 4px;
+        background: #AAAAAA;
+        border-radius: 50%;
+    }
+}
+
+.listItemActive {
+    background-color: rgba(255,128,87,0.09);
+    border: 1px solid #FF8057 !important;
+    border-radius: 10px;
+}
+
+.listitems {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    margin-left: 17px;
+    justify-content: space-between;
+
+    .item {
+
+        .itemNum {
+            display: flex;
+            justify-content: flex-end;
+            align-items: center;
+            font-weight: bold;
+            font-size: 16px;
+            color: #333;
+            margin-bottom: 3px;
+            white-space: nowrap;
+        }
+
+        .itemTitle {
+            font-size: 10px;
+            color: #777;
+            white-space: nowrap;
+        }
+    }
+
+    .line {
+        border-left: 1px solid;
+        border-right: 1px solid;
+        border-image: linear-gradient(to bottom, #EAEAEA, #979797, #EAEAEA) 1;
+    }
+}

+ 250 - 0
src/views/subject-echarts/index.tsx

@@ -0,0 +1,250 @@
+import { defineComponent, onMounted, ref } from 'vue'
+import styles from './index.module.less'
+import CircleProgress from './component/CircleProgress'
+import iconBackup from './image/icon-backup.png'
+import iconEnsemble from './image/icon-ensemble.png'
+
+import * as echarts from 'echarts'
+import { postMessage } from '@/helpers/native-message'
+
+type EChartsOption = echarts.EChartsOption
+
+export default defineComponent({
+  name: 'subject-echarts',
+  setup() {
+    const colors = [
+      {
+        color: '#FF8057',
+        borderColor: 'rgba(255,128,87,0.5)',
+        text: '达标率'
+      },
+      {
+        color: '#2FC58D',
+        borderColor: 'rgba(47,197,141,0.5)',
+        text: '达标'
+      },
+      {
+        color: '#4A99FF',
+        borderColor: 'rgba(74,153,255,0.5)',
+        text: '未达标'
+      },
+      {
+        color: '#9884BA',
+        borderColor: 'rgba(152,132,186,0.5)',
+        text: '非会员'
+      }
+    ]
+    const myChart = ref<echarts.ECharts>()
+    const handleInit = () => {
+      var chartDom = document.getElementById('subjectEcharts')!
+      myChart.value = echarts.init(chartDom, undefined, {
+        renderer: 'svg'
+      })
+      var option: EChartsOption
+
+      option = {
+        tooltip: {
+          trigger: 'axis',
+        },
+        dataset: {
+          source: [['Email']]
+        },
+        grid: {
+          left: '3%',
+          right: '3%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          data: ['长笛', '单簧管', '萨克斯', '小号', '圆号', '上低音号', '打击乐'],
+          axisPointer:{
+            type: 'shadow'
+          }
+        },
+        yAxis: [
+          {
+            type: 'value',
+            position: 'left',
+            alignTicks: true,
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: colors[2].color
+              }
+            },
+            axisLabel: {
+              formatter: '{value}'
+            }
+          },
+          {
+            type: 'value',
+            position: 'right',
+            alignTicks: true,
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: colors[0].color
+              }
+            },
+            axisLabel: {
+              formatter: '{value} %'
+            }
+          }
+        ],
+        series: [
+          {
+            name: '达标率',
+            type: 'line',
+            data: [120, 132, 101, 134, 90, 230, 210]
+          },
+          {
+            name: '达标',
+            type: 'line',
+            data: [150, 232, 201, 154, 190, 330, 410]
+          },
+          {
+            name: '未达标',
+            type: 'line',
+            data: [320, 332, 301, 334, 390, 330, 320]
+          },
+          {
+            name: '非会员',
+            type: 'line',
+            yAxisIndex: 1,
+            data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
+          }
+        ]
+      }
+
+      option && myChart.value.setOption(option)
+      console.log('🚀 ~ myChart:', myChart.value)
+    }
+    const getThisWeek = async () => {
+      postMessage(
+        {
+          api: 'setAccomanyEcharts'
+        },
+        (evt) => {
+          console.log(evt?.content)
+        }
+      )
+    }
+    const action = () => {
+      // myChart.value?.dispatchAction({
+      //   type: 'showTip',
+      //   name: '单簧管'
+      // })
+      myChart.value?.dispatchAction({
+          type:'downplay',
+          seriesIndex:[0,1],//两个图标同时展示
+          dataIndex:0
+    })
+    }
+    onMounted(() => {
+      getThisWeek()
+      handleInit()
+    })
+    return () => (
+      <div class={styles.subjectEcharts}>
+        <div class={[styles.container, styles.ensemble]}>
+          <div class={styles.head}>
+            <div class={styles.headLeft}>
+              <img class={styles.icon} src={iconEnsemble} />
+              <div>总体情况</div>
+            </div>
+          </div>
+
+          <div class={styles.content}>
+            <CircleProgress value={80} size={80} color="#FF8057" strokeWidth={6} duration={3000} />
+            <div class={styles.items}>
+              <div class={styles.item}>
+                <div class={styles.itemNum}>
+                  <span class={styles.rect}></span>
+                  <span style={{ color: '#FF8057' }}>348</span>
+                </div>
+                <div class={styles.itemTitle}>达标人数</div>
+              </div>
+              <div class={[styles.item, styles.line]}>
+                <div class={styles.itemNum}>
+                  <span class={styles.rect} style={{ background: '#FFE7DF' }}></span>
+                  <span>348</span>
+                </div>
+                <div class={styles.itemTitle}>未达标人数</div>
+              </div>
+              <div class={styles.item}>
+                <div class={styles.itemNum}>
+                  <span>348</span>
+                </div>
+                <div class={styles.itemTitle}>非会员人数</div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class={[styles.container, styles.ensemble]}>
+          <div class={styles.head}>
+            <div class={styles.headLeft}>
+              <img class={styles.icon} src={iconBackup} />
+              <div>声部情况</div>
+            </div>
+            <div class={styles.headRight}>
+              {colors.map((c) => (
+                <div
+                  style={{ color: c.color, borderColor: c.borderColor }}
+                  onClick={() => {
+                    console.log(myChart.value)
+                    action()
+                  }}
+                >
+                  {c.text}
+                </div>
+              ))}
+            </div>
+          </div>
+
+          <div id="subjectEcharts" class={styles.echartsMain}></div>
+        </div>
+
+        <div class={[styles.container, styles.subjectWrap]}>
+          {new Array(8).fill(1).map((item: any, index: number) => (
+            <div class={[styles.listItem, index == 3 ? styles.listItemActive : '']}>
+              <div class={styles.dot}></div>
+              <div class={styles.itemLeft}>
+                <div class={styles.subjectName}>长笛</div>
+                <div class={styles.subjectType}>声部</div>
+              </div>
+              <div class={styles.listitems}>
+                <div class={styles.item}>
+                  <div class={styles.itemNum}>
+                    <span style={{ color: colors[0].color }}>348</span>
+                  </div>
+                  <div class={styles.itemTitle}>达标率</div>
+                </div>
+                <div class={styles.item}>
+                  <div class={styles.itemNum}>
+                    <span style={{ color: colors[1].color }}>348</span>
+                  </div>
+                  <div class={styles.itemTitle}>达标人数</div>
+                </div>
+                <div class={styles.item}>
+                  <div class={styles.itemNum}>
+                    <span style={{ color: colors[2].color }}>348</span>
+                  </div>
+                  <div class={styles.itemTitle}>未达标人数</div>
+                </div>
+                <div class={styles.item}>
+                  <div class={styles.itemNum}>
+                    <span style={{ color: colors[3].color }}>348</span>
+                  </div>
+                  <div class={styles.itemTitle}>非会员人数</div>
+                </div>
+              </div>
+            </div>
+          ))}
+        </div>
+      </div>
+    )
+  }
+})