Bladeren bron

修改样式

lex-xin 3 maanden geleden
bovenliggende
commit
89b785cc65

+ 3 - 3
src/router/routes-teacher.ts

@@ -413,9 +413,9 @@ export default [
         }
       },
       {
-        path: '/practice-detail',
-        name: 'practice-detail',
-        component: () => import('@/teacher/statistics/practice-detail/index'),
+        path: '/practice-statistics-detail',
+        name: 'practice-statistics-detail',
+        component: () => import('@/teacher/statistics/practice-statistics-detail/index'),
         meta: {
           title: '练习统计'
         }

+ 0 - 112
src/teacher/statistics/practice-detail/index.tsx

@@ -1,112 +0,0 @@
-import { defineComponent, ref } from 'vue'
-import styles from './index.module.less'
-import iconArrow1 from '../images/icon-arrow1.png'
-import iconArrow11 from '../images/icon-arrow1-1.png'
-import icon1 from '../images/icon-1.png'
-import icon2 from '../images/icon-2.png'
-import iconDownload from '../images/icon-download.png'
-import { Popup } from 'vant'
-
-export default defineComponent({
-  name: 'PracticeDetail',
-  setup() {
-    const searchStatus = ref(false)
-    return () => (
-      <div class={styles.practiceDetail}>
-        <div class={styles.section}>
-          <div class={[styles.filter, searchStatus.value && styles.active]} onClick={() => searchStatus.value = true}>
-            <span>筛选</span>
-            <img src={searchStatus.value ? iconArrow11 : iconArrow1} />
-          </div>
-          <div class={styles.title}>
-            <span>练习详情</span>
-          </div>
-
-          <div class={styles.leaveTime}>
-            <span class={styles.num}>23</span>
-            <span class={styles.text}>时</span>
-            <span class={styles.num}>36</span>
-            <span class={styles.text}>分</span>
-            <span class={styles.num}>23</span>
-            <span class={styles.text}>秒</span>
-          </div>
-
-          <div class={styles.sList}>
-            <div class={styles.sItem}>
-              <div class={styles.sTop}>
-                <img src={icon2} />
-                <span>练习人数</span>
-              </div>
-              <div class={styles.sBottom}>
-                <span class={styles.num}>23</span>
-                <span class={styles.text}>人</span>
-              </div>
-            </div>
-            <div class={styles.sItem}>
-              <div class={styles.sTop}>
-                <img src={icon1} />
-                <span>平均练习时长</span>
-              </div>
-              <div class={styles.sBottom}>
-                <span class={styles.num}>23</span>
-                <span class={styles.text}>分钟</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <div class={styles.section}>
-          <div class={styles.title}>
-            <span>学员练习时长</span>
-            <div class={styles.download}>
-              <div>导出</div>
-              <img src={iconDownload} />
-            </div>
-          </div>
-
-          <div class={styles.scroll}>
-            <table class={styles.dataTable} style={{ width: '486px' }}>
-              <colgroup>
-                <col style="width: 88px;" />
-                <col style="width: 105px;" />
-                <col style="width: 106px;" />
-                <col style="width: 72px;" />
-                <col style="width: 106px;" />
-              </colgroup>
-              <thead>
-                <tr>
-                  <th class={styles.tdFixedLeft}>学员</th>
-                  <th>乐器</th>
-                  <th>
-                    <div>练习时长</div>
-                    {/* <div class={styles.filters}>
-                      
-                    </div> */}
-                  </th>
-                  <th>练习天数</th>
-                  <th>平均练习时长</th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr>
-                  <td class={styles.tdFixedLeft}>
-                    <img class={styles.userImg} />
-                    <span>王曼王曼王曼</span>
-                  </td>
-                  <td>长笛</td>
-                  <td>22小时56分24秒</td>
-                  <td>8</td>
-                  <td>22小时56分24秒</td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-        </div>
-
-        <Popup v-model:show={searchStatus.value}>
-
-        </Popup>
-      </div>
-    )
-  }
-})

+ 50 - 0
src/teacher/statistics/practice-statistics-detail/echats/index.module.less

@@ -0,0 +1,50 @@
+.eChartSection {
+  background-color: #fff;
+  box-shadow: 0px 2px 10px 0px rgba(229, 229, 229, 0.1);
+  border-radius: 10px;
+  padding: 12px 0 0;
+  margin: 0;
+  .eChartTitle {
+    display: flex;
+    justify-content: space-between;
+    background: #F8F8F8;
+    border-radius: 4px;
+    padding: 6px 12px;
+
+    .left {
+      display: flex;
+      align-items: center;
+    }
+
+    .item {
+      display: flex;
+      align-items: center;
+      margin-right: 12px;
+      --color: #2DC7AA;
+
+      &:last-child {
+        margin-right: 0;
+      }
+
+
+      .text {
+        font-size: 12px;
+        color: #333333;
+        line-height: 16px;
+        padding: 0 4px 0 0;
+      }
+
+      .num {
+        font-weight: 600;
+        font-size: 12px;
+        color: var(--color);
+        line-height: 16px;
+      }
+    }
+  }
+
+  .eChart {
+    height: 240px;
+    padding: 0;
+  }
+}

+ 272 - 0
src/teacher/statistics/practice-statistics-detail/echats/index.tsx

@@ -0,0 +1,272 @@
+import { defineComponent, nextTick, onMounted, ref, shallowRef } from 'vue'
+import styles from './index.module.less'
+import * as echarts from 'echarts/core'
+import {
+  LineChart
+  // LineSeriesOption
+} from 'echarts/charts'
+// import { PieChart } from 'echarts/charts'
+import {
+  TitleComponent,
+  // 组件类型的定义后缀都为 ComponentOption
+  // TitleComponentOption,
+  TooltipComponent,
+  // TooltipComponentOption,
+  GridComponent,
+  // 数据集组件
+  DatasetComponent,
+  // DatasetComponentOption,
+  // 内置数据转换器组件 (filter, sort)
+  // TransformComponent,
+  LegendComponent,
+  ToolboxComponent,
+  DataZoomComponent
+} from 'echarts/components'
+import { LabelLayout } from 'echarts/features'
+import { CanvasRenderer } from 'echarts/renderers'
+
+// 注册必须的组件
+echarts.use([
+  TitleComponent,
+  TooltipComponent,
+  GridComponent,
+  DatasetComponent,
+  // TransformComponent,
+  LabelLayout,
+  // UniversalTransition,
+  CanvasRenderer,
+  // PieChart,
+  ToolboxComponent,
+  LegendComponent,
+  DataZoomComponent,
+  LineChart
+])
+
+const lineChartOption = (params: {
+  xAxisData: any
+  seriesData: any
+  colors: {
+    lineColor?: string
+    startColor?: string
+    endColor?: string
+    unit?: string
+  }
+}) => {
+  return {
+    title: {
+      text: '单位:' + (params.colors.unit || '人'),
+      textStyle: {
+        color: '#777777',
+        fontSize: 13,
+        fontWeight: 400
+      }
+    },
+    legend: { show: false },
+    emphasis: { lineStyle: { width: 2 } },
+    xAxis: {
+      boundaryGap: false,
+      data: params.xAxisData,
+      type: 'category',
+      axisLine: { lineStyle: { color: '#8C8C8C' } },
+      lineStyle: { color: '#F2F2F2' }
+    },
+    color: [
+      params.colors.lineColor || '#2DC7AA'
+      // '#FF6079'
+      // '#2DC7AA',
+      // '#FF602C',
+      // '#91DD1C',
+      // '#FFA92C',
+      // '#BE7E2E',
+      // '#1C96DD',
+      // '#D22CFF',
+      // '#FF3C3C',
+      // '#1AEE3E',
+      // '#00c9ff'
+    ],
+    series: [
+      {
+        lineStyle: { width: 1 },
+        data: params.seriesData,
+        symbol: 'circle',
+        name: '购买次数',
+        type: 'line',
+        areaStyle: {
+          color: {
+            type: 'linear',
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [
+              {
+                offset: 0,
+                color: params.colors.startColor || 'rgba(45, 199, 170, 0.23)'
+                // 0% 处的颜色
+              },
+              {
+                offset: 1,
+                // 100% 处的颜色
+                color: params.colors.endColor || 'rgba(45, 199, 170, 0)'
+              }
+            ]
+          }
+        },
+        emphasis: { lineStyle: { width: 1 } }
+      }
+    ],
+    grid: {
+      bottom: '3%',
+      containLabel: true,
+      left: '3%',
+      right: '5%',
+      top: '40'
+    },
+    tooltip: {
+      trigger: 'axis',
+      confine: true,
+      formatter: function (params: any) {
+        return params[0].name
+      },
+      backgroundColor: '#FF6079',
+      borderWidth: 0,
+      borderRadius: 24,
+      padding: [1, 4],
+      textStyle: {
+        color: '#FFFFFF',
+        fontSize: 12
+      }
+    },
+    yAxis: {
+      type: 'value',
+      splitLine: {
+        axisLine: { lineStyle: { color: '#8C8C8C' } },
+        lineStyle: { color: ['#f2f2f2'], type: 'dashed' }
+      }
+    },
+    dataZoom: [{ type: 'inside', throttle: 100 }],
+    toolbox: { feature: { saveAsImage: { show: false } } }
+  }
+}
+export default defineComponent({
+  name: 'eChats-model',
+  props: {
+    list: {
+      type: Array,
+      default: () => []
+    },
+    type: {
+      type: String as PropType<'TIME' | 'NUM'>,
+      default: 'TIME'
+    }
+  },
+  setup(props, { emit }) {
+    const chartId = 'eChart_' + Date.now() + props.type
+    const color = props.type === 'NUM' ? '#FF955D' : '#2DC7AA'
+    const statisticCounts = ref({
+      time: '',
+      browseCount: 0,
+      buyCount: 0
+    })
+    let myChart: echarts.ECharts
+
+    nextTick(() => {
+      myChart = echarts.init(document.getElementById(chartId) as HTMLDivElement)
+    })
+
+    onMounted(() => {
+      nextTick(() => {
+        myChart.clear()
+        lineChartOption &&
+          myChart.setOption(
+            lineChartOption({
+              xAxisData: [
+                '01月',
+                '02月',
+                '03月',
+                '04月',
+                '05月',
+                '06月',
+                '07月',
+                '08月',
+                '09月',
+                '10月',
+                '11月',
+                '12月'
+              ],
+              seriesData: [
+                '0',
+                '0',
+                '0',
+                '0',
+                '0',
+                '0',
+                '0',
+                '2',
+                '0',
+                '8',
+                '10',
+                '0'
+              ],
+              colors: {
+                lineColor: color,
+                startColor:
+                  props.type === 'NUM'
+                    ? 'rgba(255, 149, 93, 0.23)'
+                    : 'rgba(45, 199, 170, 0.23)',
+                endColor:
+                  props.type === 'NUM'
+                    ? 'rgba(255, 149, 93, 0)'
+                    : 'rgba(45, 199, 170, 0)',
+                unit: props.type === 'NUM' ? '人' : '分钟'
+              }
+            })
+          )
+        myChart.on('highlight', function (params: any) {
+          const batch = params.batch || []
+          const options: any = myChart.getOption()
+          batch.forEach((item: any) => {
+            const batchIndex = item.dataIndex
+
+            const browseCount = options.series[0].data[batchIndex]
+            const buyCount = options.series[1].data[batchIndex]
+            statisticCounts.value = {
+              browseCount,
+              buyCount
+            }
+          })
+        })
+      })
+    })
+    return () => (
+      <div class={styles.eChartSection}>
+        <div class={styles.eChartTitle}>
+          <div class={styles.left}>
+            <div class={styles.item} style={{ '--color': color } as any}>
+              {/* <span class={styles.line}></span> */}
+              {props.type === 'NUM' ? (
+                <>
+                  <span class={styles.text}>{statisticCounts.value.time} 练习人数</span>
+                  <span class={styles.num}>
+                    {statisticCounts.value.browseCount}人
+                  </span>
+                </>
+              ) : (
+                <>
+                  <span class={styles.text}>{statisticCounts.value.time} 练习时长</span>
+                  <span class={styles.num}>
+                    {statisticCounts.value.browseCount}
+                  </span>
+                </>
+              )}
+            </div>
+          </div>
+        </div>
+
+        <div class={styles.eChart}>
+          <div id={chartId} style="width: 100%; height: 100%;"></div>
+        </div>
+      </div>
+    )
+  }
+})

+ 9 - 0
src/teacher/statistics/practice-detail/index.module.less → src/teacher/statistics/practice-statistics-detail/index.module.less

@@ -5,6 +5,12 @@
   background: linear-gradient(to bottom, #beffe6 0px, #f6f7f8 392px);
 }
 
+.groupContainer {
+  height: calc(100vh - var(--header-height, 0));
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
 .section {
   background: #ffffff;
   box-shadow: 0px 2px 10px 0px rgba(229, 229, 229, 0.1);
@@ -13,6 +19,9 @@
   padding: 12px;
   margin: 12px 14px 0;
   position: relative;
+  &:last-child {
+    margin-bottom: 30px;
+  }
 
   .filter {
     position: absolute;

+ 138 - 0
src/teacher/statistics/practice-statistics-detail/index.tsx

@@ -0,0 +1,138 @@
+import { defineComponent, ref } from 'vue'
+import styles from './index.module.less'
+import iconArrow1 from '../images/icon-arrow1.png'
+import iconArrow11 from '../images/icon-arrow1-1.png'
+import icon1 from '../images/icon-1.png'
+import icon2 from '../images/icon-2.png'
+import iconDownload from '../images/icon-download.png'
+import { Popup } from 'vant'
+import Echats from './echats'
+import ColHeader from '@/components/col-header'
+import TheSticky from '@/components/the-sticky'
+
+export default defineComponent({
+  name: 'PracticeDetail',
+  setup() {
+    const searchStatus = ref(false)
+    return () => (
+      <div class={styles.practiceDetail}>
+        <TheSticky position="top">
+          <ColHeader background="transparent" border={false} />
+        </TheSticky>
+        <div class={styles.groupContainer}>
+          <div class={styles.section}>
+            <div
+              class={[styles.filter, searchStatus.value && styles.active]}
+              onClick={() => (searchStatus.value = true)}
+            >
+              <span>筛选</span>
+              <img src={searchStatus.value ? iconArrow11 : iconArrow1} />
+            </div>
+            <div class={styles.title}>
+              <span>练习详情</span>
+            </div>
+
+            <div class={styles.leaveTime}>
+              <span class={styles.num}>23</span>
+              <span class={styles.text}>时</span>
+              <span class={styles.num}>36</span>
+              <span class={styles.text}>分</span>
+              <span class={styles.num}>23</span>
+              <span class={styles.text}>秒</span>
+            </div>
+
+            <div class={styles.sList}>
+              <div class={styles.sItem}>
+                <div class={styles.sTop}>
+                  <img src={icon2} />
+                  <span>练习人数</span>
+                </div>
+                <div class={styles.sBottom}>
+                  <span class={styles.num}>23</span>
+                  <span class={styles.text}>人</span>
+                </div>
+              </div>
+              <div class={styles.sItem}>
+                <div class={styles.sTop}>
+                  <img src={icon1} />
+                  <span>平均练习时长</span>
+                </div>
+                <div class={styles.sBottom}>
+                  <span class={styles.num}>23</span>
+                  <span class={styles.text}>分钟</span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <span>练习时长</span>
+            </div>
+
+            <Echats />
+          </div>
+
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <span>练习人数</span>
+            </div>
+
+            <Echats type="NUM" />
+          </div>
+
+          <div class={styles.section}>
+            <div class={styles.title}>
+              <span>学员练习时长</span>
+              <div class={styles.download}>
+                <div>导出</div>
+                <img src={iconDownload} />
+              </div>
+            </div>
+
+            <div class={styles.scroll}>
+              <table class={styles.dataTable} style={{ width: '486px' }}>
+                <colgroup>
+                  <col style="width: 88px;" />
+                  <col style="width: 105px;" />
+                  <col style="width: 106px;" />
+                  <col style="width: 72px;" />
+                  <col style="width: 106px;" />
+                </colgroup>
+                <thead>
+                  <tr>
+                    <th class={styles.tdFixedLeft}>学员</th>
+                    <th>乐器</th>
+                    <th>
+                      <div>练习时长</div>
+                      {/* <div class={styles.filters}>
+                    </div> */}
+                    </th>
+                    <th>练习天数</th>
+                    <th>平均练习时长</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  {[1, 2, 3, 4, 5, 6, 7, 78, 89].map(() => (
+                    <tr>
+                      <td class={styles.tdFixedLeft}>
+                        <img class={styles.userImg} />
+                        <span>王曼王曼王曼</span>
+                      </td>
+                      <td>长笛</td>
+                      <td>22小时56分24秒</td>
+                      <td>8</td>
+                      <td>22小时56分24秒</td>
+                    </tr>
+                  ))}
+                </tbody>
+              </table>
+            </div>
+          </div>
+        </div>
+
+        <Popup v-model:show={searchStatus.value}></Popup>
+      </div>
+    )
+  }
+})